From 18db7628acdd7cc5ec64733f6347acacc4059e45 Mon Sep 17 00:00:00 2001 From: tartaric_acid Date: Sun, 27 Oct 2024 02:01:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20molang=20=E5=92=8C?= =?UTF-8?q?=E5=8A=A8=E7=94=BB=E7=B3=BB=E7=BB=9F=20-=20=E7=8E=B0=E5=9C=A8?= =?UTF-8?q?=E6=96=B0=E7=9A=84=E5=8A=A8=E7=94=BB=E7=B3=BB=E7=BB=9F=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=9B=B4=E5=8A=A0=E5=AE=8C=E5=A4=87=E7=9A=84=E5=9F=BA?= =?UTF-8?q?=E5=B2=A9=E7=89=88=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../animation/gecko/AnimationRegister.java | 235 +---------- .../animation/gecko/molang/TLMBinding.java | 13 + .../animation/gecko/molang/YSMBinding.java | 131 ++++++ .../molang/functions/BoneParamFunction.java | 34 ++ .../gecko/molang/functions/BonePosition.java | 35 ++ .../gecko/molang/functions/BoneRotation.java | 35 ++ .../gecko/molang/functions/BoneScale.java | 35 ++ .../gecko/molang/functions/EffectLevel.java | 43 ++ .../functions/EquippedEnchantmentLevel.java | 45 ++ .../gecko/molang/functions/ModVersion.java | 28 ++ .../molang/functions/RelativeBlockName.java | 40 ++ .../gecko/molang/struct/Vec3fStruct.java | 44 ++ .../molang/variable/LadderFacingVariable.java | 27 ++ .../client/entity/GeckoMaidEntity.java | 9 +- .../client/init/ClientSetupEvent.java | 1 - .../geckolib3/core/AnimatableEntity.java | 15 +- .../geckolib3/core/ConstantValue.java | 37 -- .../geckolib3/core/builder/Animation.java | 17 +- .../core/controller/AnimationController.java | 325 ++++++--------- .../AnimationControllerContext.java | 13 + .../core/easing/EasingFunctionArgs.java | 18 - .../geckolib3/core/easing/EasingManager.java | 107 ----- .../geckolib3/core/easing/EasingType.java | 17 - .../event/CustomInstructionKeyframeEvent.java | 23 -- .../event/InstructionKeyFrameExecutor.java | 48 +++ .../core/event/predicate/AnimationEvent.java | 9 +- .../core/keyframe/AnimationPoint.java | 46 +-- .../core/keyframe/AnimationPointQueue.java | 6 - .../core/keyframe/BoneAnimation.java | 12 +- .../core/keyframe/BoneAnimationQueue.java | 70 +--- .../geckolib3/core/keyframe/KeyFrame.java | 66 --- .../core/keyframe/KeyFrameLocation.java | 26 -- .../core/keyframe/KeyFramePoint.java | 22 + .../core/keyframe/TransitionPoint.java | 24 ++ .../core/keyframe/VectorKeyFrameList.java | 49 --- .../core/keyframe/bone/BoneKeyFrame.java | 45 ++ .../keyframe/bone/BoneKeyFrameProcessor.java | 26 ++ .../keyframe/bone/CatmullRomKeyFrame.java | 35 ++ .../core/keyframe/bone/EasingType.java | 37 ++ .../core/keyframe/bone/LinearKeyFrame.java | 29 ++ .../core/keyframe/bone/RawBoneKeyFrame.java | 87 ++++ .../keyframe/bone/TransitionKeyFrame.java | 22 + .../core/keyframe/bone/Vector3v.java | 23 ++ .../keyframe/{ => event}/EventKeyFrame.java | 3 +- .../{ => event}/ParticleEventKeyFrame.java | 10 +- .../geckolib3/core/manager/AnimationData.java | 30 +- .../geckolib3/core/molang/LazyVariable.java | 41 -- .../core/molang/MolangException.java | 12 - .../geckolib3/core/molang/MolangParser.java | 219 ++-------- .../core/molang/binding/ContextBinding.java | 85 ++++ .../core/molang/binding/PrimaryBinding.java | 55 +++ .../variable/ForeignVariableBinding.java | 37 ++ .../variable/ScopedVariableBinding.java | 37 ++ .../binding/variable/TempVariableBinding.java | 38 ++ .../core/molang/builtin/MathBinding.java | 59 +++ .../core/molang/builtin/QueryBinding.java | 123 ++++++ .../core/molang/builtin/math/ACos.java | 16 + .../core/molang/builtin/math/ASin.java | 16 + .../core/molang/builtin/math/ATan2.java | 17 + .../core/molang/builtin/math/Abs.java | 16 + .../core/molang/builtin/math/Atan.java | 16 + .../core/molang/builtin/math/Ceil.java | 16 + .../core/molang/builtin/math/Clamp.java | 19 + .../core/molang/builtin/math/Cos.java | 16 + .../core/molang/builtin/math/DieRoll.java | 34 ++ .../molang/builtin/math/DieRollInteger.java | 34 ++ .../core/molang/builtin/math/Exp.java | 16 + .../core/molang/builtin/math/Floor.java | 16 + .../core/molang/builtin/math/HermitBlend.java | 17 + .../core/molang/builtin/math/Lerp.java | 19 + .../core/molang/builtin/math/LerpRotate.java | 19 + .../core/molang/builtin/math/Ln.java | 16 + .../core/molang/builtin/math/Max.java | 17 + .../core/molang/builtin/math/Min.java | 17 + .../core/molang/builtin/math/MinAngle.java | 23 ++ .../core/molang/builtin/math/Mod.java | 16 + .../core/molang/builtin/math/Pow.java | 17 + .../core/molang/builtin/math/Random.java | 26 ++ .../molang/builtin/math/RandomInteger.java | 26 ++ .../core/molang/builtin/math/Round.java | 16 + .../core/molang/builtin/math/Sin.java | 16 + .../core/molang/builtin/math/Sqrt.java | 16 + .../core/molang/builtin/math/Trunc.java | 17 + .../molang/builtin/query/BiomeHasAllTags.java | 38 ++ .../molang/builtin/query/BiomeHasAnyTag.java | 38 ++ .../molang/builtin/query/EmptyFunction.java | 12 + .../builtin/query/EquippedItemAllTags.java | 47 +++ .../builtin/query/EquippedItemAnyTags.java | 47 +++ .../builtin/query/ItemMaxDurability.java | 25 ++ .../molang/builtin/query/ItemNameAny.java | 49 +++ .../query/ItemRemainingDurability.java | 25 ++ .../core/molang/builtin/query/Position.java | 27 ++ .../molang/builtin/query/PositionDelta.java | 27 ++ .../query/RelativeBlockHasAllTags.java | 46 +++ .../builtin/query/RelativeBlockHasAnyTag.java | 46 +++ .../core/molang/context/AnimationContext.java | 127 ++++++ .../core/molang/context/IContext.java | 39 ++ .../molang/expressions/MolangAssignment.java | 29 -- .../molang/expressions/MolangExpression.java | 43 -- .../expressions/MolangMultiStatement.java | 40 -- .../core/molang/expressions/MolangValue.java | 40 -- .../core/molang/function/ContextFunction.java | 27 ++ .../blocks/AbstractBlockFunction.java | 12 + .../molang/function/blocks/BlockFunction.java | 12 + .../entity/AbstractArrowEntityFunction.java | 12 + .../function/entity/ArrowEntityFunction.java | 12 + .../function/entity/EntityFunction.java | 12 + .../function/entity/LivingEntityFunction.java | 12 + .../entity/LocalPlayerEntityFunction.java | 12 + .../function/entity/MobEntityFunction.java | 12 + .../function/entity/PlayerEntityFunction.java | 12 + .../entity/TamableEntityFunction.java | 12 + .../molang/function/item/ItemFunction.java | 12 + .../function/item/ItemStackFunction.java | 12 + .../core/molang/functions/CosDegrees.java | 21 - .../core/molang/functions/SinDegrees.java | 21 - .../storage/IForeignVariableStorage.java | 5 + .../storage/IScopedVariableStorage.java | 7 + .../molang/storage/ITempVariableStorage.java | 7 + .../core/molang/storage/VariableStorage.java | 85 ++++ .../core/molang/util/PooledStringHashMap.java | 29 ++ .../core/molang/util/PooledStringHashSet.java | 36 ++ .../core/molang/util/StringPool.java | 28 ++ .../core/molang/value/DoubleValue.java | 33 ++ .../geckolib3/core/molang/value/IValue.java | 41 ++ .../core/molang/value/MolangValue.java | 30 ++ .../core/molang/value/RotationValue.java | 31 ++ .../core/molang/variable/ContextVariable.java | 24 ++ .../core/molang/variable/IValueEvaluator.java | 5 + .../core/molang/variable/LambdaVariable.java | 16 + .../variable/block/AbstractBlockVariable.java | 17 + .../variable/block/BlockStateVariable.java | 17 + .../molang/variable/block/BlockVariable.java | 17 + .../variable/entity/EntityVariable.java | 17 + .../variable/entity/LivingEntityVariable.java | 17 + .../variable/entity/MaidEntityVariable.java | 17 + .../variable/entity/MobEntityVariable.java | 17 + .../entity/TamableEntityVariable.java | 17 + .../variable/item/ItemStackVariable.java | 17 + .../molang/variable/item/ItemVariable.java | 17 + .../core/processor/AnimationProcessor.java | 186 ++++++--- .../geckolib3/core/util/MathUtil.java | 48 +-- .../geo/GeoReplacedEntityRenderer.java | 7 +- .../geo/exception/GeckoLibException.java | 18 - .../geckolib3/resource/GeckoLibCache.java | 13 +- .../geckolib3/util/MolangUtils.java | 31 +- .../util/json/JsonAnimationUtils.java | 215 +++------- .../util/json/JsonKeyFrameUtils.java | 298 +++++++++----- .../touhoulittlemaid/mclib/math/Constant.java | 24 -- .../touhoulittlemaid/mclib/math/Group.java | 20 - .../touhoulittlemaid/mclib/math/IValue.java | 10 - .../mclib/math/MathBuilder.java | 388 ------------------ .../touhoulittlemaid/mclib/math/Negate.java | 20 - .../touhoulittlemaid/mclib/math/Negative.java | 20 - .../mclib/math/Operation.java | 125 ------ .../touhoulittlemaid/mclib/math/Operator.java | 24 -- .../touhoulittlemaid/mclib/math/Ternary.java | 24 -- .../touhoulittlemaid/mclib/math/Variable.java | 30 -- .../mclib/math/functions/Function.java | 55 --- .../mclib/math/functions/classic/ACos.java | 21 - .../mclib/math/functions/classic/ASin.java | 21 - .../mclib/math/functions/classic/ATan.java | 21 - .../mclib/math/functions/classic/ATan2.java | 21 - .../mclib/math/functions/classic/Abs.java | 21 - .../mclib/math/functions/classic/Cos.java | 21 - .../mclib/math/functions/classic/Exp.java | 21 - .../mclib/math/functions/classic/Ln.java | 21 - .../mclib/math/functions/classic/Mod.java | 21 - .../mclib/math/functions/classic/Pi.java | 16 - .../mclib/math/functions/classic/Pow.java | 21 - .../mclib/math/functions/classic/Sin.java | 21 - .../mclib/math/functions/classic/Sqrt.java | 21 - .../mclib/math/functions/limit/Clamp.java | 21 - .../mclib/math/functions/limit/Max.java | 21 - .../mclib/math/functions/limit/Min.java | 21 - .../mclib/math/functions/rounding/Ceil.java | 21 - .../mclib/math/functions/rounding/Floor.java | 21 - .../mclib/math/functions/rounding/Round.java | 21 - .../mclib/math/functions/rounding/Trunc.java | 23 -- .../mclib/math/functions/utility/DieRoll.java | 29 -- .../functions/utility/DieRollInteger.java | 29 -- .../math/functions/utility/HermiteBlend.java | 25 -- .../mclib/math/functions/utility/Lerp.java | 22 - .../math/functions/utility/LerpRotate.java | 22 - .../mclib/math/functions/utility/Random.java | 35 -- .../math/functions/utility/RandomInteger.java | 26 -- .../mclib/utils/Interpolation.java | 100 ----- .../mclib/utils/Interpolations.java | 14 + .../touhoulittlemaid/molang/MolangEngine.java | 82 ++++ .../molang/MolangEngineImpl.java | 46 +++ .../molang/lexer/Characters.java | 50 +++ .../touhoulittlemaid/molang/lexer/Cursor.java | 101 +++++ .../molang/lexer/MolangLexer.java | 188 +++++++++ .../molang/lexer/MolangLexerImpl.java | 301 ++++++++++++++ .../touhoulittlemaid/molang/lexer/Token.java | 136 ++++++ .../molang/lexer/TokenKind.java | 201 +++++++++ .../molang/parser/MolangParser.java | 198 +++++++++ .../molang/parser/MolangParserImpl.java | 356 ++++++++++++++++ .../molang/parser/ParseException.java | 72 ++++ .../ast/AssignableVariableExpression.java | 42 ++ .../molang/parser/ast/BinaryExpression.java | 151 +++++++ .../molang/parser/ast/CallExpression.java | 98 +++++ .../molang/parser/ast/DoubleExpression.java | 82 ++++ .../parser/ast/ExecutionScopeExpression.java | 86 ++++ .../molang/parser/ast/Expression.java | 53 +++ .../molang/parser/ast/ExpressionVisitor.java | 174 ++++++++ .../parser/ast/IdentifierExpression.java | 106 +++++ .../parser/ast/StatementExpression.java | 86 ++++ .../molang/parser/ast/StringExpression.java | 79 ++++ .../parser/ast/StructAccessExpression.java | 27 ++ .../ast/TernaryConditionalExpression.java | 119 ++++++ .../molang/parser/ast/UnaryExpression.java | 118 ++++++ .../molang/parser/ast/VariableExpression.java | 42 ++ .../molang/runtime/AssignableVariable.java | 7 + .../molang/runtime/ExecutionContext.java | 35 ++ .../molang/runtime/ExpressionEvaluator.java | 107 +++++ .../runtime/ExpressionEvaluatorImpl.java | 335 +++++++++++++++ .../molang/runtime/Function.java | 103 +++++ .../molang/runtime/HashMapStruct.java | 59 +++ .../molang/runtime/Struct.java | 10 + .../molang/runtime/Variable.java | 8 + .../molang/runtime/binding/ObjectBinding.java | 41 ++ .../runtime/binding/StandardBindings.java | 119 ++++++ .../runtime/binding/ValueConversions.java | 91 ++++ .../touhoulittlemaid/util/EquipmentUtil.java | 21 + 225 files changed, 7793 insertions(+), 3185 deletions(-) create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/TLMBinding.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/YSMBinding.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/BoneParamFunction.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/BonePosition.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/BoneRotation.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/BoneScale.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/EffectLevel.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/EquippedEnchantmentLevel.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/ModVersion.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/RelativeBlockName.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/struct/Vec3fStruct.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/variable/LadderFacingVariable.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/ConstantValue.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/controller/AnimationControllerContext.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/easing/EasingFunctionArgs.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/easing/EasingManager.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/easing/EasingType.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/event/CustomInstructionKeyframeEvent.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/event/InstructionKeyFrameExecutor.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/KeyFrame.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/KeyFrameLocation.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/KeyFramePoint.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/TransitionPoint.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/VectorKeyFrameList.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/BoneKeyFrame.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/BoneKeyFrameProcessor.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/CatmullRomKeyFrame.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/EasingType.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/LinearKeyFrame.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/RawBoneKeyFrame.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/TransitionKeyFrame.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/Vector3v.java rename src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/{ => event}/EventKeyFrame.java (96%) rename src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/{ => event}/ParticleEventKeyFrame.java (65%) delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/LazyVariable.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/MolangException.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/ContextBinding.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/PrimaryBinding.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/variable/ForeignVariableBinding.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/variable/ScopedVariableBinding.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/variable/TempVariableBinding.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/MathBinding.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/QueryBinding.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/ACos.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/ASin.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/ATan2.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Abs.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Atan.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Ceil.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Clamp.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Cos.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/DieRoll.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/DieRollInteger.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Exp.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Floor.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/HermitBlend.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Lerp.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/LerpRotate.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Ln.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Max.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Min.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/MinAngle.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Mod.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Pow.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Random.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/RandomInteger.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Round.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Sin.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Sqrt.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Trunc.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/BiomeHasAllTags.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/BiomeHasAnyTag.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/EmptyFunction.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/EquippedItemAllTags.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/EquippedItemAnyTags.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/ItemMaxDurability.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/ItemNameAny.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/ItemRemainingDurability.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/Position.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/PositionDelta.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/RelativeBlockHasAllTags.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/RelativeBlockHasAnyTag.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/context/AnimationContext.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/context/IContext.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/expressions/MolangAssignment.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/expressions/MolangExpression.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/expressions/MolangMultiStatement.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/expressions/MolangValue.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/ContextFunction.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/blocks/AbstractBlockFunction.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/blocks/BlockFunction.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/AbstractArrowEntityFunction.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/ArrowEntityFunction.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/EntityFunction.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/LivingEntityFunction.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/LocalPlayerEntityFunction.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/MobEntityFunction.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/PlayerEntityFunction.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/TamableEntityFunction.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/item/ItemFunction.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/item/ItemStackFunction.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/functions/CosDegrees.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/functions/SinDegrees.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/storage/IForeignVariableStorage.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/storage/IScopedVariableStorage.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/storage/ITempVariableStorage.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/storage/VariableStorage.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/util/PooledStringHashMap.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/util/PooledStringHashSet.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/util/StringPool.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/value/DoubleValue.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/value/IValue.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/value/MolangValue.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/value/RotationValue.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/ContextVariable.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/IValueEvaluator.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/LambdaVariable.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/block/AbstractBlockVariable.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/block/BlockStateVariable.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/block/BlockVariable.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/EntityVariable.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/LivingEntityVariable.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/MaidEntityVariable.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/MobEntityVariable.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/TamableEntityVariable.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/item/ItemStackVariable.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/item/ItemVariable.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/geo/exception/GeckoLibException.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Constant.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Group.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/IValue.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/MathBuilder.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Negate.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Negative.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Operation.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Operator.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Ternary.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Variable.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/Function.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/ACos.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/ASin.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/ATan.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/ATan2.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Abs.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Cos.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Exp.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Ln.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Mod.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Pi.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Pow.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Sin.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Sqrt.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/limit/Clamp.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/limit/Max.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/limit/Min.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/rounding/Ceil.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/rounding/Floor.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/rounding/Round.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/rounding/Trunc.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/DieRoll.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/DieRollInteger.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/HermiteBlend.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/Lerp.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/LerpRotate.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/Random.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/RandomInteger.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/utils/Interpolation.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/MolangEngine.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/MolangEngineImpl.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/Characters.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/Cursor.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/MolangLexer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/MolangLexerImpl.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/Token.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/TokenKind.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/MolangParser.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/MolangParserImpl.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ParseException.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/AssignableVariableExpression.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/BinaryExpression.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/CallExpression.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/DoubleExpression.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/ExecutionScopeExpression.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/Expression.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/ExpressionVisitor.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/IdentifierExpression.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/StatementExpression.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/StringExpression.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/StructAccessExpression.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/TernaryConditionalExpression.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/UnaryExpression.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/VariableExpression.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/AssignableVariable.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/ExecutionContext.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/ExpressionEvaluator.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/ExpressionEvaluatorImpl.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/Function.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/HashMapStruct.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/Struct.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/Variable.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/binding/ObjectBinding.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/binding/StandardBindings.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/binding/ValueConversions.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/util/EquipmentUtil.java diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/AnimationRegister.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/AnimationRegister.java index 4645d74ca..069f5e0b6 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/AnimationRegister.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/AnimationRegister.java @@ -5,22 +5,8 @@ import com.github.tartaricacid.touhoulittlemaid.entity.item.EntitySit; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.builder.ILoopType; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.event.predicate.AnimationEvent; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.LazyVariable; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.MolangParser; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.model.provider.data.EntityModelData; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.resource.GeckoLibCache; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.util.MolangUtils; -import net.minecraft.client.CameraType; -import net.minecraft.client.Minecraft; -import net.minecraft.util.Mth; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.Pose; import net.minecraft.world.entity.vehicle.Boat; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraft.world.item.UseAnim; -import net.minecraft.world.phys.Vec3; import java.util.function.BiPredicate; @@ -32,14 +18,15 @@ public static void registerAnimationState() { register("sleep", Priority.HIGHEST, (maid, event) -> maid.asEntity().getPose() == Pose.SLEEPING); register("swim", Priority.HIGHEST, (maid, event) -> maid.asEntity().isSwimming()); + register("gomoku", Priority.HIGH, (maid, event) -> sitInJoy(maid, Type.GOMOKU)); + register("bookshelf", Priority.HIGH, (maid, event) -> sitInJoy(maid, Type.BOOKSHELF)); + register("computer", Priority.HIGH, (maid, event) -> sitInJoy(maid, Type.COMPUTER)); + register("keyboard", Priority.HIGH, (maid, event) -> sitInJoy(maid, Type.KEYBOARD)); + register("picnic", Priority.HIGH, (maid, event) -> sitInJoy(maid, Type.ON_HOME_MEAL)); + register("boat", Priority.HIGH, (maid, event) -> maid.asEntity().getVehicle() instanceof Boat); - register("gomoku", Priority.HIGH, (maid, event) -> maid.asEntity().getVehicle() instanceof EntitySit sit && sit.getJoyType().equals(Type.GOMOKU.getTypeName())); - register("bookshelf", Priority.HIGH, (maid, event) -> maid.asEntity().getVehicle() instanceof EntitySit sit && sit.getJoyType().equals(Type.BOOKSHELF.getTypeName())); - register("computer", Priority.HIGH, (maid, event) -> maid.asEntity().getVehicle() instanceof EntitySit sit && sit.getJoyType().equals(Type.COMPUTER.getTypeName())); - register("keyboard", Priority.HIGH, (maid, event) -> maid.asEntity().getVehicle() instanceof EntitySit sit && sit.getJoyType().equals(Type.KEYBOARD.getTypeName())); - register("picnic", Priority.HIGH, (maid, event) -> maid.asEntity().getVehicle() instanceof EntitySit sit && sit.getJoyType().equals(Type.ON_HOME_MEAL.getTypeName())); - register("sit", Priority.HIGH, (maid, event) -> maid.isMaidInSittingPose()); register("chair", Priority.HIGH, (maid, event) -> maid.asEntity().isPassenger()); + register("sit", Priority.HIGH, (maid, event) -> maid.isMaidInSittingPose()); register("swim_stand", Priority.NORMAL, (maid, event) -> maid.asEntity().isInWater()); register("attacked", ILoopType.EDefaultLoopTypes.PLAY_ONCE, Priority.NORMAL, (maid, event) -> maid.asEntity().hurtTime > 0); @@ -51,169 +38,8 @@ public static void registerAnimationState() { register("idle", Priority.LOWEST, (maid, event) -> true); } - @SuppressWarnings("all") - public static void registerVariables() { - MolangParser parser = GeckoLibCache.getInstance().parser; - - parser.register(new LazyVariable("query.actor_count", 0)); - parser.register(new LazyVariable("query.anim_time", 0)); - - parser.register(new LazyVariable("query.body_x_rotation", 0)); - parser.register(new LazyVariable("query.body_y_rotation", 0)); - parser.register(new LazyVariable("query.cardinal_facing_2d", 0)); - parser.register(new LazyVariable("query.distance_from_camera", 0)); - parser.register(new LazyVariable("query.equipment_count", 0)); - parser.register(new LazyVariable("query.eye_target_x_rotation", 0)); - parser.register(new LazyVariable("query.eye_target_y_rotation", 0)); - parser.register(new LazyVariable("query.ground_speed", 0)); - - parser.register(new LazyVariable("query.has_cape", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.has_rider", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.head_x_rotation", 0)); - parser.register(new LazyVariable("query.head_y_rotation", 0)); - parser.register(new LazyVariable("query.health", 0)); - parser.register(new LazyVariable("query.hurt_time", 0)); - - parser.register(new LazyVariable("query.is_eating", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.is_first_person", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.is_in_water", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.is_in_water_or_rain", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.is_jumping", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.is_on_fire", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.is_on_ground", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.is_playing_dead", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.is_riding", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.is_sleeping", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.is_sneaking", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.is_spectator", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.is_sprinting", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.is_swimming", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.is_using_item", MolangUtils.FALSE)); - parser.register(new LazyVariable("query.item_in_use_duration", 0)); - parser.register(new LazyVariable("query.item_max_use_duration", 0)); - parser.register(new LazyVariable("query.item_remaining_use_duration", 0)); - - parser.register(new LazyVariable("query.life_time", 0)); - parser.register(new LazyVariable("query.max_health", 0)); - parser.register(new LazyVariable("query.modified_distance_moved", 0)); - parser.register(new LazyVariable("query.moon_phase", 0)); - - parser.register(new LazyVariable("query.player_level", 0)); - parser.register(new LazyVariable("query.time_of_day", 0)); - parser.register(new LazyVariable("query.time_stamp", 0)); - parser.register(new LazyVariable("query.vertical_speed", 0)); - parser.register(new LazyVariable("query.walk_distance", 0)); - parser.register(new LazyVariable("query.yaw_speed", 0)); - - parser.register(new LazyVariable("ysm.head_yaw", 0)); - parser.register(new LazyVariable("ysm.head_pitch", 0)); - parser.register(new LazyVariable("ysm.has_helmet", MolangUtils.FALSE)); - parser.register(new LazyVariable("ysm.has_chest_plate", MolangUtils.FALSE)); - parser.register(new LazyVariable("ysm.has_leggings", MolangUtils.FALSE)); - parser.register(new LazyVariable("ysm.has_boots", MolangUtils.FALSE)); - parser.register(new LazyVariable("ysm.has_mainhand", MolangUtils.FALSE)); - parser.register(new LazyVariable("ysm.has_offhand", MolangUtils.FALSE)); - - parser.register(new LazyVariable("ysm.has_elytra", MolangUtils.FALSE)); - parser.register(new LazyVariable("ysm.elytra_rot_x", 0)); - parser.register(new LazyVariable("ysm.elytra_rot_y", 0)); - parser.register(new LazyVariable("ysm.elytra_rot_z", 0)); - - parser.register(new LazyVariable("ysm.is_close_eyes", MolangUtils.FALSE)); - parser.register(new LazyVariable("ysm.is_passenger", MolangUtils.FALSE)); - parser.register(new LazyVariable("ysm.is_sleep", MolangUtils.FALSE)); - parser.register(new LazyVariable("ysm.is_sneak", MolangUtils.FALSE)); - parser.register(new LazyVariable("ysm.is_riptide", MolangUtils.FALSE)); - - parser.register(new LazyVariable("ysm.armor_value", 0)); - parser.register(new LazyVariable("ysm.hurt_time", 0)); - parser.register(new LazyVariable("ysm.food_level", 20)); - - parser.register(new LazyVariable("ysm.first_person_mod_hide", MolangUtils.FALSE)); - - parser.register(new LazyVariable("tlm.is_begging", MolangUtils.FALSE)); - parser.register(new LazyVariable("tlm.is_sitting", MolangUtils.FALSE)); - parser.register(new LazyVariable("tlm.has_backpack", MolangUtils.FALSE)); - } - - public static void setParserValue(AnimationEvent animationEvent, MolangParser parser, EntityModelData data, IMaid maid) { - Minecraft mc = Minecraft.getInstance(); - if (mc.level == null) { - return; - } - - Mob mob = maid.asEntity(); - - parser.setValue("query.actor_count", () -> mc.level.getEntityCount()); - parser.setValue("query.body_x_rotation", mob::getXRot); - parser.setValue("query.body_y_rotation", () -> Mth.wrapDegrees(mob.getYRot())); - parser.setValue("query.cardinal_facing_2d", () -> mob.getDirection().get3DDataValue()); - parser.setValue("query.distance_from_camera", () -> mc.gameRenderer.getMainCamera().getPosition().distanceTo(mob.position())); - parser.setValue("query.equipment_count", () -> getEquipmentCount(mob)); - parser.setValue("query.eye_target_x_rotation", () -> mob.getViewXRot(0)); - parser.setValue("query.eye_target_y_rotation", () -> mob.getViewYRot(0)); - parser.setValue("query.ground_speed", () -> getGroundSpeed(mob)); - - parser.setValue("query.has_rider", () -> MolangUtils.booleanToFloat(mob.isVehicle())); - parser.setValue("query.head_x_rotation", () -> data.netHeadYaw); - parser.setValue("query.head_y_rotation", () -> data.headPitch); - parser.setValue("query.health", mob::getHealth); - parser.setValue("query.hurt_time", () -> mob.hurtTime); - - parser.setValue("query.is_eating", () -> MolangUtils.booleanToFloat(mob.getUseItem().getUseAnimation() == UseAnim.EAT)); - parser.setValue("query.is_first_person", () -> MolangUtils.booleanToFloat(mc.options.getCameraType() == CameraType.FIRST_PERSON)); - parser.setValue("query.is_in_water", () -> MolangUtils.booleanToFloat(mob.isInWater())); - parser.setValue("query.is_in_water_or_rain", () -> MolangUtils.booleanToFloat(mob.isInWaterRainOrBubble())); - parser.setValue("query.is_jumping", () -> MolangUtils.booleanToFloat(!mob.isPassenger() && !mob.onGround() && !mob.isInWater())); - parser.setValue("query.is_on_fire", () -> MolangUtils.booleanToFloat(mob.isOnFire())); - parser.setValue("query.is_on_ground", () -> MolangUtils.booleanToFloat(mob.onGround())); - parser.setValue("query.is_playing_dead", () -> MolangUtils.booleanToFloat(mob.isDeadOrDying())); - parser.setValue("query.is_riding", () -> MolangUtils.booleanToFloat(mob.isPassenger())); - parser.setValue("query.is_sleeping", () -> MolangUtils.booleanToFloat(mob.isSleeping())); - parser.setValue("query.is_sneaking", () -> MolangUtils.booleanToFloat(mob.onGround() && mob.getPose() == Pose.CROUCHING)); - parser.setValue("query.is_spectator", () -> MolangUtils.booleanToFloat(mob.isSpectator())); - parser.setValue("query.is_sprinting", () -> MolangUtils.booleanToFloat(mob.isSprinting())); - parser.setValue("query.is_swimming", () -> MolangUtils.booleanToFloat(mob.isSwimming())); - parser.setValue("query.is_using_item", () -> MolangUtils.booleanToFloat(mob.isUsingItem())); - parser.setValue("query.item_in_use_duration", () -> mob.getTicksUsingItem() / 20.0); - parser.setValue("query.item_max_use_duration", () -> getMaxUseDuration(mob) / 20.0); - parser.setValue("query.item_remaining_use_duration", () -> mob.getUseItemRemainingTicks() / 20.0); - - parser.setValue("query.max_health", mob::getMaxHealth); - parser.setValue("query.modified_distance_moved", () -> mob.walkDist); - parser.setValue("query.moon_phase", () -> mc.level.getMoonPhase()); - - parser.setValue("query.player_level", () -> maid.getExperience() / 120d); - parser.setValue("query.time_of_day", () -> MolangUtils.normalizeTime(mc.level.getDayTime())); - parser.setValue("query.time_stamp", () -> mc.level.getDayTime()); - parser.setValue("query.vertical_speed", () -> getVerticalSpeed(mob)); - parser.setValue("query.walk_distance", () -> mob.moveDist); - parser.setValue("query.yaw_speed", () -> getYawSpeed(animationEvent, mob)); - - parser.setValue("ysm.head_yaw", () -> data.netHeadYaw); - parser.setValue("ysm.head_pitch", () -> data.headPitch); - - parser.setValue("ysm.has_helmet", () -> getSlotValue(mob, EquipmentSlot.HEAD)); - parser.setValue("ysm.has_chest_plate", () -> getSlotValue(mob, EquipmentSlot.CHEST)); - parser.setValue("ysm.has_leggings", () -> getSlotValue(mob, EquipmentSlot.LEGS)); - parser.setValue("ysm.has_boots", () -> getSlotValue(mob, EquipmentSlot.FEET)); - parser.setValue("ysm.has_mainhand", () -> getSlotValue(mob, EquipmentSlot.MAINHAND)); - parser.setValue("ysm.has_offhand", () -> getSlotValue(mob, EquipmentSlot.OFFHAND)); - - parser.setValue("ysm.has_elytra", () -> MolangUtils.booleanToFloat(mob.getItemBySlot(EquipmentSlot.CHEST).getItem() == Items.ELYTRA)); - - parser.setValue("ysm.is_close_eyes", () -> getEyeCloseState(animationEvent, mob)); - parser.setValue("ysm.is_passenger", () -> MolangUtils.booleanToFloat(mob.isPassenger())); - parser.setValue("ysm.is_sleep", () -> MolangUtils.booleanToFloat(mob.getPose() == Pose.SLEEPING)); - parser.setValue("ysm.is_sneak", () -> MolangUtils.booleanToFloat(mob.onGround() && mob.getPose() == Pose.CROUCHING)); - parser.setValue("ysm.is_riptide", () -> MolangUtils.booleanToFloat(mob.isAutoSpinAttack())); - - parser.setValue("ysm.armor_value", mob::getArmorValue); - parser.setValue("ysm.hurt_time", () -> mob.hurtTime); - - parser.setValue("tlm.is_begging", () -> MolangUtils.booleanToFloat(maid.isBegging())); - parser.setValue("tlm.is_sitting", () -> MolangUtils.booleanToFloat(maid.isMaidInSittingPose())); - parser.setValue("tlm.has_backpack", () -> MolangUtils.booleanToFloat(maid.hasBackpack())); + private static boolean sitInJoy(IMaid maid, Type type) { + return maid.asEntity().getVehicle() instanceof EntitySit sit && sit.getJoyType().equals(type.getTypeName()); } private static void register(String animationName, ILoopType loopType, int priority, BiPredicate> predicate) { @@ -224,47 +50,4 @@ private static void register(String animationName, ILoopType loopType, int prior private static void register(String animationName, int priority, BiPredicate> predicate) { register(animationName, ILoopType.EDefaultLoopTypes.LOOP, priority, predicate); } - - private static int getEquipmentCount(Mob maid) { - int count = 0; - for (ItemStack s : maid.getArmorSlots()) { - if (!s.isEmpty()) { - count += 1; - } - } - return count; - } - - private static double getMaxUseDuration(Mob maid) { - ItemStack useItem = maid.getUseItem(); - if (useItem.isEmpty()) { - return 0.0; - } else { - return useItem.getUseDuration(); - } - } - - private static float getYawSpeed(AnimationEvent animationEvent, Mob maid) { - double seekTime = animationEvent.getAnimationTick(); - return maid.getViewYRot((float) seekTime - maid.getViewYRot((float) seekTime - 0.1f)); - } - - private static float getGroundSpeed(Mob maid) { - Vec3 velocity = maid.getDeltaMovement(); - return 20 * Mth.sqrt((float) ((velocity.x * velocity.x) + (velocity.z * velocity.z))); - } - - private static float getVerticalSpeed(Mob maid) { - return 20 * (float) (maid.position().y - maid.yo); - } - - private static double getEyeCloseState(AnimationEvent animationEvent, Mob maid) { - double remainder = (animationEvent.getAnimationTick() + Math.abs(maid.getUUID().getLeastSignificantBits()) % 10) % 90; - boolean isBlinkTime = 85 < remainder && remainder < 90; - return MolangUtils.booleanToFloat(maid.isSleeping() || isBlinkTime); - } - - private static double getSlotValue(Mob maid, EquipmentSlot slot) { - return MolangUtils.booleanToFloat(!maid.getItemBySlot(slot).isEmpty()); - } } \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/TLMBinding.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/TLMBinding.java new file mode 100644 index 000000000..2052d7b91 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/TLMBinding.java @@ -0,0 +1,13 @@ +package com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.binding.ContextBinding; + +public class TLMBinding extends ContextBinding { + public static final TLMBinding INSTANCE = new TLMBinding(); + + private TLMBinding() { + maidEntityVar("is_begging", ctx -> ctx.entity().isBegging()); + maidEntityVar("is_sitting", ctx -> ctx.entity().isMaidInSittingPose()); + maidEntityVar("has_backpack", ctx -> ctx.entity().hasBackpack()); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/YSMBinding.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/YSMBinding.java new file mode 100644 index 000000000..83322f7c8 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/YSMBinding.java @@ -0,0 +1,131 @@ +package com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang; + +import com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.functions.*; +import com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.variable.LadderFacingVariable; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.event.predicate.AnimationEvent; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.binding.ContextBinding; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.query.EmptyFunction; +import com.github.tartaricacid.touhoulittlemaid.util.EquipmentUtil; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Pose; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraftforge.common.ForgeMod; +import org.apache.commons.lang3.StringUtils; + +public class YSMBinding extends ContextBinding { + public static final YSMBinding INSTANCE = new YSMBinding(); + + @SuppressWarnings("resource") + private YSMBinding() { + function("mod_version", new ModVersion()); + function("equipped_enchantment_level", new EquippedEnchantmentLevel()); + function("effect_level", new EffectLevel()); + function("relative_block_name", new RelativeBlockName()); + + function("bone_rot", new BoneRotation()); + function("bone_pos", new BonePosition()); + function("bone_scale", new BoneScale()); + + var("head_yaw", ctx -> ctx.data().netHeadYaw); + var("head_pitch", ctx -> ctx.data().headPitch); + var("weather", ctx -> getWeather(ctx.level())); + var("dimension_name", ctx -> ctx.level().dimension().location().toString()); + var("fps", ctx -> Minecraft.getInstance().getFps()); + + entityVar("is_passenger", ctx -> ctx.entity().isPassenger()); + entityVar("is_sleep", ctx -> ctx.entity().getPose() == Pose.SLEEPING); + entityVar("is_sneak", ctx -> ctx.entity().onGround() && ctx.entity().getPose() == Pose.CROUCHING); + entityVar("is_open_air", ctx -> isOpenAir(ctx.entity())); + entityVar("eye_in_water", ctx -> ctx.entity().isUnderWater()); + entityVar("frozen_ticks", ctx -> ctx.entity().getTicksFrozen()); + entityVar("air_supply", ctx -> ctx.entity().getAirSupply()); + + livingEntityVar("has_helmet", ctx -> getSlotValue(ctx.entity(), EquipmentSlot.HEAD)); + livingEntityVar("has_chest_plate", ctx -> getSlotValue(ctx.entity(), EquipmentSlot.CHEST)); + livingEntityVar("has_leggings", ctx -> getSlotValue(ctx.entity(), EquipmentSlot.LEGS)); + livingEntityVar("has_boots", ctx -> getSlotValue(ctx.entity(), EquipmentSlot.FEET)); + livingEntityVar("has_mainhand", ctx -> getSlotValue(ctx.entity(), EquipmentSlot.MAINHAND)); + livingEntityVar("has_offhand", ctx -> getSlotValue(ctx.entity(), EquipmentSlot.OFFHAND)); + livingEntityVar("has_elytra", ctx -> !EquipmentUtil.getEquippedElytraItem(ctx.entity()).isEmpty()); + livingEntityVar("is_riptide", ctx -> ctx.entity().isAutoSpinAttack()); + livingEntityVar("armor_value", ctx -> ctx.entity().getArmorValue()); + livingEntityVar("hurt_time", ctx -> ctx.entity().hurtTime); + livingEntityVar("is_close_eyes", ctx -> getEyeCloseState(ctx.animationEvent(), ctx.entity())); + livingEntityVar("on_ladder", ctx -> ctx.entity().onClimbable()); + livingEntityVar("ladder_facing", new LadderFacingVariable()); + livingEntityVar("arrow_count", ctx -> ctx.entity().getArrowCount()); + livingEntityVar("stinger_count", ctx -> ctx.entity().getStingerCount()); + + livingEntityVar("attack_damage", ctx -> ctx.entity().getAttributeValue(Attributes.ATTACK_DAMAGE)); + livingEntityVar("attack_speed", ctx -> ctx.entity().getAttributeValue(Attributes.ATTACK_SPEED)); + livingEntityVar("attack_knockback", ctx -> ctx.entity().getAttributeValue(Attributes.ATTACK_KNOCKBACK)); + livingEntityVar("movement_speed", ctx -> ctx.entity().getAttributeValue(Attributes.MOVEMENT_SPEED)); + livingEntityVar("knockback_resistance", ctx -> ctx.entity().getAttributeValue(Attributes.KNOCKBACK_RESISTANCE)); + livingEntityVar("luck", ctx -> ctx.entity().getAttributeValue(Attributes.LUCK)); + + livingEntityVar("block_reach", ctx -> ctx.entity().getAttributeValue(ForgeMod.BLOCK_REACH.get())); + livingEntityVar("entity_reach", ctx -> ctx.entity().getAttributeValue(ForgeMod.ENTITY_REACH.get())); + livingEntityVar("swim_speed", ctx -> ctx.entity().getAttributeValue(ForgeMod.SWIM_SPEED.get())); + livingEntityVar("entity_gravity", ctx -> ctx.entity().getAttributeValue(ForgeMod.ENTITY_GRAVITY.get())); + livingEntityVar("step_height_addition", ctx -> ctx.entity().getAttributeValue(ForgeMod.STEP_HEIGHT_ADDITION.get())); + livingEntityVar("nametag_distance", ctx -> ctx.entity().getAttributeValue(ForgeMod.NAMETAG_DISTANCE.get())); + + + // 女仆和 YSM 之间不一致的 molang,仅保留防止报错 + function("dump_equipped_item", new EmptyFunction()); + function("dump_relative_block", new EmptyFunction()); + function("bone_pivot_abs", new EmptyFunction()); + + var("dump_mods", ctx -> 0); + var("texture_name", ctx -> StringUtils.EMPTY); + var("elytra_rot_x", ctx -> 0); + var("elytra_rot_y", ctx -> 0); + var("elytra_rot_z", ctx -> 0); + + entityVar("dump_effects", ctx -> 0); + entityVar("dump_biome", ctx -> 0); + entityVar("biome_category", ctx -> 0); + + livingEntityVar("rendering_in_inventory", ctx -> false); + maidEntityVar("food_level", ctx -> 20); + + var("first_person_mod_hide", ctx -> false); + var("has_left_shoulder_parrot", ctx -> false); + var("has_right_shoulder_parrot", ctx -> false); + var("left_shoulder_parrot_variant", ctx -> 0); + var("right_shoulder_parrot_variant", ctx -> 0); + } + + private static boolean getEyeCloseState(AnimationEvent animationEvent, LivingEntity player) { + double remainder = (animationEvent.getAnimationTick() + Math.abs(player.getUUID().getLeastSignificantBits()) % 10) % 90; + boolean isBlinkTime = 85 < remainder && remainder < 90; + return player.isSleeping() || isBlinkTime; + } + + private static boolean getSlotValue(LivingEntity entity, EquipmentSlot slot) { + return !EquipmentUtil.getEquippedItem(entity, slot).isEmpty(); + } + + private static int getWeather(ClientLevel world) { + if (world.isThundering()) { + return 2; + } else if (world.isRaining()) { + return 1; + } + return 0; + } + + private static boolean isOpenAir(Entity entity) { + BlockPos blockpos = entity.blockPosition(); + if (!entity.level.canSeeSky(blockpos)) { + return false; + } + return entity.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, blockpos).getY() <= blockpos.getY(); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/BoneParamFunction.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/BoneParamFunction.java new file mode 100644 index 000000000..475e8ea8f --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/BoneParamFunction.java @@ -0,0 +1,34 @@ +package com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.functions; + +import com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.struct.Vec3fStruct; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity.EntityFunction; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.processor.IBone; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import net.minecraft.util.StringUtil; +import net.minecraft.world.entity.Entity; +import org.jetbrains.annotations.NotNull; + +public abstract class BoneParamFunction extends EntityFunction { + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } + + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + var str = arguments.getAsString(context, 0); + if (StringUtil.isNullOrEmpty(str)) { + return null; + } + + var bone = context.entity().geoInstance().getAnimationProcessor().getBone(str); + if (bone == null) { + return null; + } + + return getParam(bone); + } + + protected abstract Vec3fStruct getParam(@NotNull IBone bone); +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/BonePosition.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/BonePosition.java new file mode 100644 index 000000000..9d4941244 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/BonePosition.java @@ -0,0 +1,35 @@ +package com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.functions; + +import com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.struct.Vec3fStruct; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.processor.IBone; +import org.jetbrains.annotations.NotNull; + +public final class BonePosition extends BoneParamFunction { + @Override + protected Vec3fStruct getParam(@NotNull IBone bone) { + return new BonePositionStruct(bone); + } + + private static final class BonePositionStruct extends Vec3fStruct { + private final IBone bone; + + public BonePositionStruct(IBone bone) { + this.bone = bone; + } + + @Override + protected float getX() { + return bone.getPositionX(); + } + + @Override + protected float getY() { + return bone.getPositionY(); + } + + @Override + protected float getZ() { + return bone.getPositionZ(); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/BoneRotation.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/BoneRotation.java new file mode 100644 index 000000000..d12ca475e --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/BoneRotation.java @@ -0,0 +1,35 @@ +package com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.functions; + +import com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.struct.Vec3fStruct; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.processor.IBone; +import org.jetbrains.annotations.NotNull; + +public final class BoneRotation extends BoneParamFunction { + @Override + protected Vec3fStruct getParam(@NotNull IBone bone) { + return new BoneRotationStruct(bone); + } + + private static final class BoneRotationStruct extends Vec3fStruct { + private final IBone bone; + + public BoneRotationStruct(IBone bone) { + this.bone = bone; + } + + @Override + protected float getX() { + return -(float) Math.toDegrees(bone.getRotationX()); + } + + @Override + protected float getY() { + return -(float) Math.toDegrees(bone.getRotationY()); + } + + @Override + protected float getZ() { + return (float) Math.toDegrees(bone.getRotationZ()); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/BoneScale.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/BoneScale.java new file mode 100644 index 000000000..628f6ed99 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/BoneScale.java @@ -0,0 +1,35 @@ +package com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.functions; + +import com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.struct.Vec3fStruct; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.processor.IBone; +import org.jetbrains.annotations.NotNull; + +public final class BoneScale extends BoneParamFunction { + @Override + protected Vec3fStruct getParam(@NotNull IBone bone) { + return new BoneScaleStruct(bone); + } + + private static final class BoneScaleStruct extends Vec3fStruct { + private final IBone bone; + + public BoneScaleStruct(IBone bone) { + this.bone = bone; + } + + @Override + protected float getX() { + return bone.getScaleX(); + } + + @Override + protected float getY() { + return bone.getScaleY(); + } + + @Override + protected float getZ() { + return bone.getScaleZ(); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/EffectLevel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/EffectLevel.java new file mode 100644 index 000000000..c02158600 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/EffectLevel.java @@ -0,0 +1,43 @@ +package com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.functions; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.util.MolangUtils; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraftforge.registries.ForgeRegistries; + +public class EffectLevel extends ContextFunction { + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } + + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + ResourceLocation effectId = MolangUtils.parseResourceLocation(context.entity(), arguments.getAsString(context, 0)); + if (effectId == null) { + return null; + } + + MobEffect effect = ForgeRegistries.MOB_EFFECTS.getValue(effectId); + if (effect == null) { + return 0; + } + + if (context.entity().entity() instanceof LivingEntity) { + MobEffectInstance instance = ((LivingEntity) context.entity().entity()).getEffect(effect); + if (instance != null) { + return instance.getAmplifier() + 1; + } + } else { + return null; + } + + return 0; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/EquippedEnchantmentLevel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/EquippedEnchantmentLevel.java new file mode 100644 index 000000000..332d45e60 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/EquippedEnchantmentLevel.java @@ -0,0 +1,45 @@ +package com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.functions; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity.LivingEntityFunction; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.util.MolangUtils; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.util.EquipmentUtil; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.Enchantment; +import net.minecraftforge.registries.ForgeRegistries; + +public class EquippedEnchantmentLevel extends LivingEntityFunction { + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + EquipmentSlot slotType = MolangUtils.parseSlotType(context.entity(), arguments.getAsString(context, 0)); + if (slotType == null) { + return null; + } + + ResourceLocation id = MolangUtils.parseResourceLocation(context.entity(), arguments.getAsString(context, 1)); + if (id == null) { + return null; + } + + Enchantment enchantment = ForgeRegistries.ENCHANTMENTS.getValue(id); + if (enchantment == null) { + return 0; + } + + ItemStack itemStack = EquipmentUtil.getEquippedItem(context.entity().entity(), slotType); + if (itemStack.isEmpty()) { + return 0; + } + + return itemStack.getEnchantmentLevel(enchantment); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 2; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/ModVersion.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/ModVersion.java new file mode 100644 index 000000000..9ce5ebc7b --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/ModVersion.java @@ -0,0 +1,28 @@ +package com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.functions; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; +import net.minecraftforge.common.util.MavenVersionStringHelper; +import net.minecraftforge.fml.ModList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ModVersion implements Function { + @Nullable + @Override + public Object evaluate(@NotNull ExecutionContext context, @NotNull ArgumentCollection arguments) { + String modId = arguments.getAsString(context, 0); + if (modId == null) { + return null; + } + + return ModList.get().getModContainerById(modId) + .map(modContainer -> MavenVersionStringHelper.artifactVersionToString((modContainer.getModInfo().getVersion()))) + .orElse(null); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/RelativeBlockName.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/RelativeBlockName.java new file mode 100644 index 000000000..c8f950469 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/functions/RelativeBlockName.java @@ -0,0 +1,40 @@ +package com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.functions; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity.EntityFunction; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.registries.ForgeRegistries; + +public class RelativeBlockName extends EntityFunction { + @Override + protected Object eval(ExecutionContext> ctx, ArgumentCollection arguments) { + Entity entity = ctx.entity().entity(); + + int offsetX = arguments.getAsInt(ctx, 0); + int offsetY = arguments.getAsInt(ctx, 1); + int offsetZ = arguments.getAsInt(ctx, 2); + if (Math.abs(offsetX) > 8 || Math.abs(offsetY) > 8 || Math.abs(offsetZ) > 8) { + return false; + } + + BlockPos pos = new BlockPos((int) entity.getX() + offsetX, + (int) entity.getY() + offsetY, + (int) entity.getZ() + offsetZ); + BlockState block = ctx.entity().entity().level().getBlockState(pos); + ResourceLocation blockId = ForgeRegistries.BLOCKS.getKey(block.getBlock()); + if (blockId == null) { + return null; + } + + return blockId.toString(); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 3; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/struct/Vec3fStruct.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/struct/Vec3fStruct.java new file mode 100644 index 000000000..b5f1c966e --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/struct/Vec3fStruct.java @@ -0,0 +1,44 @@ +package com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.struct; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.util.StringPool; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Struct; + +public abstract class Vec3fStruct implements Struct { + private static final int NAME_X = StringPool.computeIfAbsent("x"); + private static final int NAME_Y = StringPool.computeIfAbsent("y"); + private static final int NAME_Z = StringPool.computeIfAbsent("z"); + + @Override + public Object getProperty(int name) { + if (name == NAME_X) { + return getX(); + } + if (name == NAME_Y) { + return getY(); + } + if (name == NAME_Z) { + return getZ(); + } + return null; + } + + protected abstract float getX(); + + protected abstract float getY(); + + protected abstract float getZ(); + + @Override + public void putProperty(int name, Object value) { + } + + @Override + public String toString() { + return String.format("vec3{x=%.2f, y=%.2f, z=%.2f}", getX(), getY(), getZ()); + } + + @Override + public Struct copy() { + return this; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/variable/LadderFacingVariable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/variable/LadderFacingVariable.java new file mode 100644 index 000000000..c7b76f221 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/molang/variable/LadderFacingVariable.java @@ -0,0 +1,27 @@ +package com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.variable; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.IValueEvaluator; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.level.block.HorizontalDirectionalBlock; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.Optional; + +public class LadderFacingVariable implements IValueEvaluator> { + @Override + public Integer eval(IContext ctx) { + Optional climbablePos = ctx.entity().getLastClimbablePos(); + if (climbablePos.isPresent()) { + BlockState blockState = ctx.entity().level().getBlockState(climbablePos.get()); + Optional optionalValue = blockState.getOptionalValue(HorizontalDirectionalBlock.FACING); + if (optionalValue.isPresent()) { + // 输出数字 0-3,分别对应:南-西-北-东 + return optionalValue.get().get2DDataValue(); + } + } + return 0; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/entity/GeckoMaidEntity.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/entity/GeckoMaidEntity.java index 8455a81ab..9bca45042 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/entity/GeckoMaidEntity.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/entity/GeckoMaidEntity.java @@ -9,6 +9,7 @@ import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.controller.AnimationController; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.event.predicate.AnimationEvent; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.MolangParser; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.AnimationContext; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.processor.IBone; import com.github.tartaricacid.touhoulittlemaid.geckolib3.geo.animated.AnimatedGeoModel; import com.github.tartaricacid.touhoulittlemaid.geckolib3.model.provider.data.EntityModelData; @@ -71,12 +72,12 @@ public void registerControllers() { @Override @SuppressWarnings("all") - public boolean setCustomAnimations(@NotNull AnimationEvent animationEvent) { + public boolean setCustomAnimations(AnimationContext context, @NotNull AnimationEvent animationEvent) { List extraData = animationEvent.getExtraData(); MolangParser parser = GeckoLibCache.getInstance().parser; if (!Minecraft.getInstance().isPaused() && extraData.size() == 1 && extraData.get(0) instanceof EntityModelData data) { - AnimationRegister.setParserValue(animationEvent, parser, data, this.maid); - var update = super.setCustomAnimations(animationEvent); + //AnimationRegister.setParserValue(animationEvent, parser, data, this.maid); + var update = super.setCustomAnimations(context, animationEvent); AnimatedGeoModel currentModel = this.getCurrentModel(); if (currentModel != null && currentModel.head() != null) { IBone head = currentModel.head(); @@ -88,7 +89,7 @@ public boolean setCustomAnimations(@NotNull AnimationEvent animationEvent) { } return update; } else { - return super.setCustomAnimations(animationEvent); + return super.setCustomAnimations(context, animationEvent); } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/init/ClientSetupEvent.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/init/ClientSetupEvent.java index 2880ea519..6cdf379bb 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/init/ClientSetupEvent.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/init/ClientSetupEvent.java @@ -19,7 +19,6 @@ public class ClientSetupEvent { @SubscribeEvent public static void onClientSetup(FMLClientSetupEvent event) { event.enqueueWork(AnimationRegister::registerAnimationState); - event.enqueueWork(AnimationRegister::registerVariables); event.enqueueWork(MaidTipsOverlay::init); event.enqueueWork(ShowOptifineScreen::checkOptifineIsLoaded); diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/AnimatableEntity.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/AnimatableEntity.java index 4fe8a4ee2..8143c0ba9 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/AnimatableEntity.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/AnimatableEntity.java @@ -5,6 +5,7 @@ import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.controller.AnimationController; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.event.predicate.AnimationEvent; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.manager.AnimationData; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.AnimationContext; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.processor.AnimationProcessor; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.util.RateLimiter; import com.github.tartaricacid.touhoulittlemaid.geckolib3.file.AnimationFile; @@ -48,7 +49,7 @@ public AnimationData getAnimationData() { public abstract ResourceLocation getTextureLocation(); - public boolean setCustomAnimations(@NotNull AnimationEvent animationEvent) { + public boolean setCustomAnimations(AnimationContext ctx, @NotNull AnimationEvent animationEvent) { if (!updateModel()) { return false; } @@ -69,15 +70,15 @@ public boolean setCustomAnimations(@NotNull AnimationEvent animationEvent) { } animationEvent.animationTick = this.seekTime; - this.animationProcessor.preAnimationSetup(this.seekTime); - if (this.animationProcessor.isModelEmpty()) { + this.animationProcessor.preAnimationSetup(this, this.seekTime); + if (this.animationProcessor.isModelRendererEmpty()) { return false; } if (!forceUpdate(animationEvent) && !this.rateLimiter.request((float) this.seekTime * 20)) { return false; } - this.animationProcessor.tickAnimation(this.seekTime, animationEvent, GeckoLibCache.getInstance().parser); + this.animationProcessor.tickAnimation(this.seekTime, animationEvent, ctx); return true; } @@ -107,7 +108,7 @@ public boolean updateModel() { } if (this.currentModel == null || model != this.currentModel.geoModel()) { this.currentModel = new AnimatedGeoModel(model); - this.animationProcessor.updateModel(this.currentModel.bones().values()); + this.animationProcessor.registerModelRenderer(this.currentModel.bones()); } return true; } @@ -138,4 +139,8 @@ protected boolean forceUpdate(AnimationEvent animationEvent) { public E getEntity() { return entity; } + + public double getSeekTime() { + return seekTime; + } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/ConstantValue.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/ConstantValue.java deleted file mode 100644 index 42b831d62..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/ConstantValue.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; - - -public class ConstantValue implements IValue { - private final double value; - - public ConstantValue(double value) { - this.value = value; - } - - public static ConstantValue fromDouble(double d) { - return new ConstantValue(d); - } - - public static ConstantValue fromFloat(float d) { - return new ConstantValue(d); - } - - public static ConstantValue parseDouble(String s) { - return new ConstantValue(Double.parseDouble(s)); - } - - public static ConstantValue parseFloat(String s) { - return new ConstantValue(Float.parseFloat(s)); - } - - public static ConstantValue subtract(IValue first, IValue second) { - return ConstantValue.fromDouble(first.get() - second.get()); - } - - @Override - public double get() { - return value; - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/builder/Animation.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/builder/Animation.java index 1505de295..7a370b63a 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/builder/Animation.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/builder/Animation.java @@ -5,8 +5,9 @@ package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.builder; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.BoneAnimation; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.EventKeyFrame; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.ParticleEventKeyFrame; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.event.EventKeyFrame; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.value.IValue; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.List; @@ -15,17 +16,15 @@ public class Animation { public final double animationLength; public final ILoopType loop; public final List boneAnimations; - public final List> soundKeyFrames; - public final List particleKeyFrames; - public final List> customInstructionKeyframes; + public final List> customInstructionKeyframes; - public Animation(String animationName, double animationLength, ILoopType loop, List boneAnimations, List> soundKeyFrames, List particleKeyFrames, List> customInstructionKeyframes) { + public Animation(String animationName, double animationLength, ILoopType loop, + ReferenceArrayList boneAnimations, + ReferenceArrayList> customInstructionKeyframes) { this.animationName = animationName; this.animationLength = animationLength; this.loop = loop; this.boneAnimations = boneAnimations; - this.soundKeyFrames = soundKeyFrames; - this.particleKeyFrames = particleKeyFrames; this.customInstructionKeyframes = customInstructionKeyframes; } -} +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/controller/AnimationController.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/controller/AnimationController.java index fb4160864..9cd7b34be 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/controller/AnimationController.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/controller/AnimationController.java @@ -6,29 +6,32 @@ package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.controller; import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.*; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.AnimatableEntity; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.AnimationState; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.PlayState; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.builder.Animation; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.builder.AnimationBuilder; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.builder.ILoopType; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.easing.EasingType; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.event.CustomInstructionKeyframeEvent; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.event.InstructionKeyFrameExecutor; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.event.ParticleKeyFrameEvent; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.event.SoundKeyframeEvent; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.event.predicate.AnimationEvent; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.*; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.MolangParser; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone.BoneKeyFrame; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone.EasingType; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.AnimationContext; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.snapshot.BoneSnapshot; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.snapshot.BoneTopLevelSnapshot; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.util.Axis; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExpressionEvaluator; +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; import it.unimi.dsi.fastutil.objects.ReferenceArrayList; -import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; -import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; -import javax.annotation.Nullable; -import java.util.*; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; @@ -37,27 +40,25 @@ public class AnimationController> { * 动画控制器名称 */ private final String name; - private final Object2ObjectOpenHashMap boneAnimationQueues = new Object2ObjectOpenHashMap<>(); + private final Object2ReferenceOpenHashMap boneAnimationQueues = new Object2ReferenceOpenHashMap<>(); private final ReferenceArrayList activeBoneAnimationQueues = new ReferenceArrayList<>(); - private final Set> executedKeyFrames = new ReferenceOpenHashSet<>(); + private InstructionKeyFrameExecutor instructionKeyFrameExecutor; /** * 在动画之间过渡需要多长时间 */ public double transitionLengthTicks; public boolean isJustStarting = false; public double tickOffset; - public Double2DoubleFunction customEasingMethod; public double animationSpeed = 1D; /** * 默认情况下,动画将使用关键帧的 EasingType
* 复写此数值将用于全局 */ - public EasingType easingType = EasingType.NONE; - public boolean shouldResetTick = false; + public EasingType easingType = EasingType.LINEAR; /** * 实体对象 */ - protected T animatable; + protected final T animatable; /** * 动画谓词,每次触发前都会调用一次 */ @@ -67,6 +68,7 @@ public class AnimationController> { protected Animation currentAnimation; protected ILoopType currentAnimationLoop; protected AnimationBuilder currentAnimationBuilder = new AnimationBuilder(); + public boolean shouldResetTick = false; protected boolean justStartedTransition = false; protected boolean needsAnimationReload = false; /** @@ -77,10 +79,6 @@ public class AnimationController> { * 播放粒子关键帧时触发的 Particle Listener */ private IParticleListener particleListener; - /** - * 播放指令关键帧时触发的 Instruction Listener - */ - private ICustomInstructionListener customInstructionListener; private boolean justStopped = false; /** @@ -121,28 +119,6 @@ public AnimationController(T animatable, String name, float transitionLengthTick this.tickOffset = 0.0d; } - /** - * 实例化动画控制器,每个控制器同一时间只能播放一个动画
- * 你可以为一个实体附加多个动画控制器
- * 比如一个控制器控制实体大小,另一个控制移动,攻击等等 - * - * @param animatable 实体 - * @param name 动画控制器名称 - * @param transitionLengthTicks 动画过渡时间(tick) - * @param customEasingMethod 自定义过渡插值类型,参数输入和输出均为 0-1 - * {@link com.github.tartaricacid.touhoulittlemaid.geckolib3.core.easing.EasingManager} - */ - public AnimationController(T animatable, String name, float transitionLengthTicks, - Double2DoubleFunction customEasingMethod, IAnimationPredicate animationPredicate) { - this.animatable = animatable; - this.name = name; - this.transitionLengthTicks = transitionLengthTicks; - this.customEasingMethod = customEasingMethod; - this.easingType = EasingType.CUSTOM; - this.animationPredicate = animationPredicate; - this.tickOffset = 0.0d; - } - /** * 此方法使用 AnimationBuilder 设置当前动画 * 你可以每帧运行此方法,如果每次都传入相同的 AnimationBuilder,它将不会重新启动。 @@ -162,7 +138,7 @@ public void setAnimation(AnimationBuilder builder) { return null; } else { ILoopType loopType = animation.loop; - if(rawAnimation.loopType != null) { + if (rawAnimation.loopType != null) { loopType = rawAnimation.loopType; } return Pair.of(loopType, animation); @@ -224,31 +200,25 @@ public void registerParticleListener(IParticleListener particleListener) { this.particleListener = particleListener; } - /** - * 注册 Custom Instruction Listener. - */ - public void registerCustomInstructionListener(ICustomInstructionListener customInstructionListener) { - this.customInstructionListener = customInstructionListener; - } - /** * 此方法每帧调用一次,以便填充动画点队列并处理动画状态逻辑。 * - * @param tick 当前 tick + 插值 tick - * @param event 动画测试事件 - * @param modelRendererList 所有的 AnimatedModelRender 列表 + * @param tick 当前 tick + 插值 tick + * @param event 动画测试事件 + * @param modelRendererList 所有的 AnimatedModelRender 列表 */ - public void process(final double tick, AnimationEvent event, Map modelRendererList, - MolangParser parser, boolean modelDirty) { - parser.setValue("query.life_time", () -> tick / 20); + public void process(final double tick, AnimationEvent event, ExpressionEvaluator> evaluator, List modelRendererList, + boolean crashWhenCantFindBone, boolean isRendererDirty, boolean scheduledUpdate) { + AnimationControllerContext context = new AnimationControllerContext(); if (this.currentAnimation != null) { Animation animation = this.animatable.getAnimation(this.currentAnimation.animationName); - if (animation != null) { + if (animation != null && this.currentAnimation != animation) { this.currentAnimation = animation; + this.instructionKeyFrameExecutor = new InstructionKeyFrameExecutor(animation.customInstructionKeyframes); } } - if (modelDirty) { + if (isRendererDirty) { switchRenderer(modelRendererList); if (this.currentAnimation != null) { switchAnimation(); @@ -293,87 +263,76 @@ public void process(final double tick, AnimationEvent event, Map current = this.animationQueue.poll(); if (current != null) { - this.currentAnimationLoop = current.getLeft(); - this.currentAnimation = current.getRight(); - resetEventKeyFrames(); + this.currentAnimationLoop = current.getFirst(); + this.currentAnimation = current.getSecond(); + this.instructionKeyFrameExecutor = new InstructionKeyFrameExecutor(current.getSecond().customInstructionKeyframes); + resetEventKeyFrames(false, null); switchAnimation(); } else { this.currentAnimation = null; + this.instructionKeyFrameExecutor = null; } } if (this.currentAnimation != null) { - setAnimTime(parser, 0); - for (BoneAnimationQueue boneAnimationQueue : this.activeBoneAnimationQueues) { + context.setAnimTime(0); + for (BoneAnimationQueue boneAnimationQueue : activeBoneAnimationQueues) { BoneAnimation boneAnimation = boneAnimationQueue.animation; - if(boneAnimation == null) { + if (boneAnimation == null) { continue; } BoneSnapshot boneSnapshot = boneAnimationQueue.snapshot(); BoneSnapshot initialSnapshot = boneAnimationQueue.topLevelSnapshot.bone.getInitialSnapshot(); // 添加即将出现的动画的初始位置,以便模型转换到新动画的初始状态 - VectorKeyFrameList> rotationKeyFrames = boneAnimation.rotationKeyFrames; - if (!rotationKeyFrames.xKeyFrames.isEmpty()) { - AnimationPoint xPoint = getAnimationPointAtTick(rotationKeyFrames.xKeyFrames, 0, true, Axis.X); - AnimationPoint yPoint = getAnimationPointAtTick(rotationKeyFrames.yKeyFrames, 0, true, Axis.Y); - AnimationPoint zPoint = getAnimationPointAtTick(rotationKeyFrames.zKeyFrames, 0, true, Axis.Z); - boneAnimationQueue.rotationXQueue().add(new AnimationPoint(null, adjustedTick, this.transitionLengthTicks, - boneSnapshot.rotationValueX - initialSnapshot.rotationValueX, - xPoint.animationStartValue)); - boneAnimationQueue.rotationYQueue().add(new AnimationPoint(null, adjustedTick, this.transitionLengthTicks, - boneSnapshot.rotationValueY - initialSnapshot.rotationValueY, - yPoint.animationStartValue)); - boneAnimationQueue.rotationZQueue().add(new AnimationPoint(null, adjustedTick, this.transitionLengthTicks, - boneSnapshot.rotationValueZ - initialSnapshot.rotationValueZ, - zPoint.animationStartValue)); + List rotationKeyFrames = boneAnimation.rotationKeyFrames; + if (!rotationKeyFrames.isEmpty()) { + AnimationPoint point = getTransitionPointAtTick(rotationKeyFrames, adjustedTick, + new Vector3f(boneSnapshot.rotationValueX - initialSnapshot.rotationValueX, + boneSnapshot.rotationValueY - initialSnapshot.rotationValueY, + boneSnapshot.rotationValueZ - initialSnapshot.rotationValueZ), + context); + boneAnimationQueue.rotationQueue().add(point); } - VectorKeyFrameList> positionKeyFrames = boneAnimation.positionKeyFrames; - if (!positionKeyFrames.xKeyFrames.isEmpty()) { - AnimationPoint xPoint = getAnimationPointAtTick(positionKeyFrames.xKeyFrames, 0, false, Axis.X); - AnimationPoint yPoint = getAnimationPointAtTick(positionKeyFrames.yKeyFrames, 0, false, Axis.Y); - AnimationPoint zPoint = getAnimationPointAtTick(positionKeyFrames.zKeyFrames, 0, false, Axis.Z); - boneAnimationQueue.positionXQueue().add(new AnimationPoint(null, adjustedTick, this.transitionLengthTicks, - boneSnapshot.positionOffsetX, xPoint.animationStartValue)); - boneAnimationQueue.positionYQueue().add(new AnimationPoint(null, adjustedTick, this.transitionLengthTicks, - boneSnapshot.positionOffsetY, yPoint.animationStartValue)); - boneAnimationQueue.positionZQueue().add(new AnimationPoint(null, adjustedTick, this.transitionLengthTicks, - boneSnapshot.positionOffsetZ, zPoint.animationStartValue)); + List positionKeyFrames = boneAnimation.positionKeyFrames; + if (!positionKeyFrames.isEmpty()) { + AnimationPoint point = getTransitionPointAtTick(positionKeyFrames, adjustedTick, + new Vector3f(boneSnapshot.positionOffsetX, + boneSnapshot.positionOffsetY, + boneSnapshot.positionOffsetZ), + context); + boneAnimationQueue.positionQueue().add(point); } - VectorKeyFrameList> scaleKeyFrames = boneAnimation.scaleKeyFrames; - if (!scaleKeyFrames.xKeyFrames.isEmpty()) { - AnimationPoint xPoint = getAnimationPointAtTick(scaleKeyFrames.xKeyFrames, 0, false, Axis.X); - AnimationPoint yPoint = getAnimationPointAtTick(scaleKeyFrames.yKeyFrames, 0, false, Axis.Y); - AnimationPoint zPoint = getAnimationPointAtTick(scaleKeyFrames.zKeyFrames, 0, false, Axis.Z); - boneAnimationQueue.scaleXQueue().add(new AnimationPoint(null, adjustedTick, this.transitionLengthTicks, - boneSnapshot.scaleValueX, xPoint.animationStartValue)); - boneAnimationQueue.scaleYQueue().add(new AnimationPoint(null, adjustedTick, this.transitionLengthTicks, - boneSnapshot.scaleValueY, yPoint.animationStartValue)); - boneAnimationQueue.scaleZQueue().add(new AnimationPoint(null, adjustedTick, this.transitionLengthTicks, - boneSnapshot.scaleValueZ, zPoint.animationStartValue)); + List scaleKeyFrames = boneAnimation.scaleKeyFrames; + if (!scaleKeyFrames.isEmpty()) { + AnimationPoint point = getTransitionPointAtTick(scaleKeyFrames, adjustedTick, + new Vector3f(boneSnapshot.scaleValueX, + boneSnapshot.scaleValueY, + boneSnapshot.scaleValueZ), + context); + boneAnimationQueue.scaleQueue().add(point); } } } } else if (getAnimationState() == AnimationState.RUNNING) { resetQueues(); // 开始运行动画 - processCurrentAnimation(adjustedTick, tick, parser); + processCurrentAnimation(context, evaluator, adjustedTick, tick, crashWhenCantFindBone, scheduledUpdate); } } - private void setAnimTime(MolangParser parser, final double tick) { - parser.setValue("query.anim_time", () -> tick / 20); - } - protected PlayState testAnimationPredicate(AnimationEvent event) { return this.animationPredicate.test(event); } - private void processCurrentAnimation(double tick, double actualTick, MolangParser parser) { + private void processCurrentAnimation(AnimationControllerContext context, ExpressionEvaluator> evaluator, double tick, double actualTick, boolean crashWhenCantFindBone, boolean scheduledUpdate) { assert currentAnimation != null; + evaluator.entity().setAnimationControllerContext(context); + // 如果动画已经结束了 if (tick >= this.currentAnimation.animationLength) { + context.setAnimTime(this.currentAnimation.animationLength / 20.0f); // 如果动画为循环播放,继续重头播放 if (!this.currentAnimationLoop.isRepeatingAfterEnd()) { // 从队列中提取下一个动画 @@ -386,86 +345,55 @@ private void processCurrentAnimation(double tick, double actualTick, MolangParse // 否则,将状态设置为过渡并开始过渡到下一个动画为下一帧 this.animationState = AnimationState.TRANSITIONING; this.shouldResetTick = true; - this.currentAnimation = peek.getRight(); - this.currentAnimationLoop = peek.getLeft(); + this.currentAnimation = peek.getSecond(); + this.instructionKeyFrameExecutor = new InstructionKeyFrameExecutor(peek.getSecond().customInstructionKeyframes); + this.currentAnimationLoop = peek.getFirst(); } } else { // 重置 tick,以便下一个动画从刻度 0 开始 this.shouldResetTick = true; tick = adjustTick(actualTick); - resetEventKeyFrames(); + resetEventKeyFrames(true, evaluator); } } - setAnimTime(parser, tick); + context.setAnimTime(tick / 20.0f); // 循环遍历当前动画中的每个骨骼动画并处理值 - for (BoneAnimationQueue boneAnimationQueue : this.activeBoneAnimationQueues) { + for (BoneAnimationQueue boneAnimationQueue : activeBoneAnimationQueues) { BoneAnimation boneAnimation = boneAnimationQueue.animation; - VectorKeyFrameList> rotationKeyFrames = boneAnimation.rotationKeyFrames; - VectorKeyFrameList> positionKeyFrames = boneAnimation.positionKeyFrames; - VectorKeyFrameList> scaleKeyFrames = boneAnimation.scaleKeyFrames; - - if (!rotationKeyFrames.xKeyFrames.isEmpty()) { - boneAnimationQueue.rotationXQueue() - .add(getAnimationPointAtTick(rotationKeyFrames.xKeyFrames, tick, true, Axis.X)); - boneAnimationQueue.rotationYQueue() - .add(getAnimationPointAtTick(rotationKeyFrames.yKeyFrames, tick, true, Axis.Y)); - boneAnimationQueue.rotationZQueue() - .add(getAnimationPointAtTick(rotationKeyFrames.zKeyFrames, tick, true, Axis.Z)); + List rotationKeyFrames = boneAnimation.rotationKeyFrames; + if (!rotationKeyFrames.isEmpty()) { + boneAnimationQueue.rotationQueue().add(getKeyFramePointAtTick(rotationKeyFrames, tick, context)); } - if (!positionKeyFrames.xKeyFrames.isEmpty()) { - boneAnimationQueue.positionXQueue() - .add(getAnimationPointAtTick(positionKeyFrames.xKeyFrames, tick, false, Axis.X)); - boneAnimationQueue.positionYQueue() - .add(getAnimationPointAtTick(positionKeyFrames.yKeyFrames, tick, false, Axis.Y)); - boneAnimationQueue.positionZQueue() - .add(getAnimationPointAtTick(positionKeyFrames.zKeyFrames, tick, false, Axis.Z)); + List positionKeyFrames = boneAnimation.positionKeyFrames; + if (!positionKeyFrames.isEmpty()) { + boneAnimationQueue.positionQueue().add(getKeyFramePointAtTick(positionKeyFrames, tick, context)); } - if (!scaleKeyFrames.xKeyFrames.isEmpty()) { - boneAnimationQueue.scaleXQueue() - .add(getAnimationPointAtTick(scaleKeyFrames.xKeyFrames, tick, false, Axis.X)); - boneAnimationQueue.scaleYQueue() - .add(getAnimationPointAtTick(scaleKeyFrames.yKeyFrames, tick, false, Axis.Y)); - boneAnimationQueue.scaleZQueue() - .add(getAnimationPointAtTick(scaleKeyFrames.zKeyFrames, tick, false, Axis.Z)); + List scaleKeyFrames = boneAnimation.scaleKeyFrames; + if (!scaleKeyFrames.isEmpty()) { + boneAnimationQueue.scaleQueue().add(getKeyFramePointAtTick(scaleKeyFrames, tick, context)); } } - if (this.soundListener != null) { - for (EventKeyFrame soundKeyFrame : this.currentAnimation.soundKeyFrames) { - if (tick >= soundKeyFrame.getStartTick() && this.executedKeyFrames.add(soundKeyFrame)) { - SoundKeyframeEvent event = new SoundKeyframeEvent<>(this.animatable, tick, soundKeyFrame.getEventData(), this); - this.soundListener.playSound(event); - } - } - } - if (this.particleListener != null) { - for (ParticleEventKeyFrame particleEventKeyFrame : this.currentAnimation.particleKeyFrames) { - if (tick >= particleEventKeyFrame.getStartTick() && this.executedKeyFrames.add(particleEventKeyFrame)) { - ParticleKeyFrameEvent event = new ParticleKeyFrameEvent<>(this.animatable, tick, particleEventKeyFrame.effect, particleEventKeyFrame.locator, particleEventKeyFrame.script, this); - this.particleListener.summonParticle(event); - } - } - } - if (this.customInstructionListener != null) { - for (EventKeyFrame customInstructionKeyFrame : currentAnimation.customInstructionKeyframes) { - if (tick >= customInstructionKeyFrame.getStartTick() && this.executedKeyFrames.add(customInstructionKeyFrame)) { - CustomInstructionKeyframeEvent event = new CustomInstructionKeyframeEvent<>(this.animatable, tick, customInstructionKeyFrame.getEventData(), this); - this.customInstructionListener.executeInstruction(event); - } - } + // TODO: 声音关键帧和粒子关键帧暂不支持 + + // 计划外更新不执行指令关键帧 + if (instructionKeyFrameExecutor != null && scheduledUpdate) { + instructionKeyFrameExecutor.executeTo(evaluator, tick); } if (this.transitionLengthTicks == 0 && shouldResetTick && this.animationState == AnimationState.TRANSITIONING) { - Pair current = this.animationQueue.poll(); - if(current != null) { - this.currentAnimation = current.getRight(); - this.currentAnimationLoop = current.getLeft(); + Pair current = animationQueue.poll(); + if (current != null) { + this.currentAnimation = current.getSecond(); + this.currentAnimationLoop = current.getFirst(); + this.instructionKeyFrameExecutor = new InstructionKeyFrameExecutor(current.getSecond().customInstructionKeyframes); } else { this.currentAnimation = null; + this.instructionKeyFrameExecutor = null; } } } @@ -475,7 +403,7 @@ private void switchAnimation() { this.activeBoneAnimationQueues.clear(); for (BoneAnimation animation : this.currentAnimation.boneAnimations) { BoneAnimationQueue queue = this.boneAnimationQueues.get(animation.boneName); - if(queue == null) { + if (queue == null) { continue; } queue.animation = animation; @@ -486,9 +414,9 @@ private void switchAnimation() { } // 切换模型,重新填充所有初始动画点队列 - private void switchRenderer(Map modelRendererList) { + private void switchRenderer(List modelRendererList) { this.boneAnimationQueues.clear(); - for (BoneTopLevelSnapshot modelRenderer : modelRendererList.values()) { + for (BoneTopLevelSnapshot modelRenderer : modelRendererList) { this.boneAnimationQueues.put(modelRenderer.name, new BoneAnimationQueue(modelRenderer)); } this.activeBoneAnimationQueues.clear(); @@ -496,7 +424,7 @@ private void switchRenderer(Map modelRendererList) } private void resetQueues() { - for(BoneAnimationQueue queue : this.activeBoneAnimationQueues) { + for (BoneAnimationQueue queue : this.activeBoneAnimationQueues) { queue.resetQueues(); } } @@ -516,47 +444,35 @@ public double adjustTick(double tick) { } } - // 将关键帧位置转换为动画点 - private AnimationPoint getAnimationPointAtTick(List> frames, double tick, boolean isRotation, Axis axis) { - KeyFrameLocation> location = getCurrentKeyFrameLocation(frames, tick); - KeyFrame currentFrame = location.currentFrame; - double startValue = currentFrame.getStartValue().get(); - double endValue = currentFrame.getEndValue().get(); - - if (isRotation) { - if (!(currentFrame.getStartValue() instanceof ConstantValue)) { - startValue = Math.toRadians(startValue); - if (axis == Axis.X || axis == Axis.Y) { - startValue *= -1; - } - } - if (!(currentFrame.getEndValue() instanceof ConstantValue)) { - endValue = Math.toRadians(endValue); - if (axis == Axis.X || axis == Axis.Y) { - endValue *= -1; - } + /** + * 返回当前关键帧播放进度 + **/ + private AnimationPoint getKeyFramePointAtTick(List frames, double tick, AnimationControllerContext context) { + for (int i = 0; i < frames.size(); i++) { + if (frames.get(i).getStartTick() > tick) { + BoneKeyFrame frame = frames.get(i - 1); + return new KeyFramePoint(tick - frame.getStartTick(), frame, context); } } - return new AnimationPoint(currentFrame, location.currentTick, currentFrame.getLength(), startValue, endValue); + BoneKeyFrame frame = frames.get(frames.size() - 1); + return new KeyFramePoint(tick - frame.getStartTick(), frame, context); } /** - * 返回当前关键帧,以及上一个关键帧耗时 + * 返回过渡进度 **/ - private KeyFrameLocation> getCurrentKeyFrameLocation(List> frames, double ageInTicks) { - double totalTimeTracker = 0; - for (KeyFrame frame : frames) { - totalTimeTracker += frame.getLength(); - if (totalTimeTracker > ageInTicks) { - double tick = (ageInTicks - (totalTimeTracker - frame.getLength())); - return new KeyFrameLocation<>(frame, tick); - } - } - return new KeyFrameLocation<>(frames.get(frames.size() - 1), ageInTicks); + private TransitionPoint getTransitionPointAtTick(List frames, double tick, Vector3f offsetPoint, AnimationControllerContext context) { + BoneKeyFrame dstFrame = frames.get(0); + return new TransitionPoint(tick, this.transitionLengthTicks, offsetPoint, dstFrame, context); } - private void resetEventKeyFrames() { - this.executedKeyFrames.clear(); + private void resetEventKeyFrames(boolean reachEnd, ExpressionEvaluator> evaluator) { + if (instructionKeyFrameExecutor != null) { + if (reachEnd) { + instructionKeyFrameExecutor.executeRemaining(evaluator); + } + instructionKeyFrameExecutor.reset(); + } } public void markNeedsReload() { @@ -593,9 +509,4 @@ public interface ISoundListener> { public interface IParticleListener> { void summonParticle(ParticleKeyFrameEvent event); } - - @FunctionalInterface - public interface ICustomInstructionListener> { - void executeInstruction(CustomInstructionKeyframeEvent event); - } } \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/controller/AnimationControllerContext.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/controller/AnimationControllerContext.java new file mode 100644 index 000000000..988d23c53 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/controller/AnimationControllerContext.java @@ -0,0 +1,13 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.controller; + +public class AnimationControllerContext { + private double animTime; + + public void setAnimTime(double animTime) { + this.animTime = animTime; + } + + public double animTime() { + return animTime; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/easing/EasingFunctionArgs.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/easing/EasingFunctionArgs.java deleted file mode 100644 index bbb500744..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/easing/EasingFunctionArgs.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.easing; - - -import java.util.Objects; - -public record EasingFunctionArgs(EasingType easingType, Double arg0) { - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - EasingFunctionArgs that = (EasingFunctionArgs) o; - return easingType == that.easingType && Objects.equals(arg0, that.arg0); - } -} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/easing/EasingManager.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/easing/EasingManager.java deleted file mode 100644 index 2d024e78d..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/easing/EasingManager.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.easing; - -import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; - -import java.util.List; - -public class EasingManager { - public static double ease(double number, EasingType easingType, List easingArgs) { - Double firstArg = easingArgs == null || easingArgs.isEmpty() ? null : easingArgs.get(0); - return ease(number, easingType, firstArg); - } - - private static double ease(double number, EasingType easingType, Double arg0) { - return switch (easingType) { - default -> number; - case STEP -> step(arg0).apply(number); - }; - } - - private static Double2DoubleFunction getEasingFuncImpl(EasingFunctionArgs args) { - return switch (args.easingType()) { - default -> in(EasingManager::linear); - case STEP -> in(step(args.arg0())); - }; - } - - private static Double2DoubleFunction in(Double2DoubleFunction easing) { - return easing; - } - - private static double linear(double t) { - return t; - } - - - private static Double2DoubleFunction step(Double stepArg) { - int steps = stepArg != null ? stepArg.intValue() : 2; - double[] intervals = stepRange(steps); - return t -> intervals[findIntervalBorderIndex(t, intervals, false)]; - } - - /** - * The MIT License (MIT) - *

- * Copyright (c) 2015 Boris Chumichev - *

- * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - *

- * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - *

- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - *

- * /** - *

- * Utilizes bisection method to search an interval to which point belongs to, - * then returns an index of left or right border of the interval - */ - private static int findIntervalBorderIndex(double point, double[] intervals, boolean useRightBorder) { - // 如果点超出给定间隔 - if (point < intervals[0]) { - return 0; - } - if (point > intervals[intervals.length - 1]) { - return intervals.length - 1; - } - // 如果点在区间内 - // 开始搜索全区间 - int indexOfNumberToCompare = 0; - int leftBorderIndex = 0; - int rightBorderIndex = intervals.length - 1; - // 使用二分搜索,缩小搜索范围 - while (rightBorderIndex - leftBorderIndex != 1) { - indexOfNumberToCompare = leftBorderIndex + (rightBorderIndex - leftBorderIndex) / 2; - if (point >= intervals[indexOfNumberToCompare]) { - leftBorderIndex = indexOfNumberToCompare; - } else { - rightBorderIndex = indexOfNumberToCompare; - } - } - return useRightBorder ? rightBorderIndex : leftBorderIndex; - } - - private static double[] stepRange(int steps) { - final double stop = 1; - if (steps < 2) { - throw new IllegalArgumentException("steps must be > 2, got:" + steps); - } - double stepLength = stop / (double) steps; - double[] stepArray = new double[steps]; - for (int i = 0; i < steps; i++) { - stepArray[i] = i * stepLength; - } - return stepArray; - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/easing/EasingType.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/easing/EasingType.java deleted file mode 100644 index 703bb867e..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/easing/EasingType.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.easing; - -import java.util.Locale; - -public enum EasingType { - // 插值类型 - NONE, CUSTOM, LINEAR, STEP; - - public static EasingType getEasingTypeFromString(String search) { - return switch (search.toLowerCase(Locale.ROOT)) { - default -> NONE; - case "custom" -> CUSTOM; - case "linear" -> LINEAR; - case "step" -> STEP; - }; - } -} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/event/CustomInstructionKeyframeEvent.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/event/CustomInstructionKeyframeEvent.java deleted file mode 100644 index d1dc4aefc..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/event/CustomInstructionKeyframeEvent.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2020. - * Author: Bernie G. (Gecko) - */ - -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.event; - -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.controller.AnimationController; - -public class CustomInstructionKeyframeEvent extends KeyframeEvent { - public final String instructions; - - /** - * KeyframeEvent 所需的所有数据 - * - * @param instructions 指令列表,在 blockbench 中,自定义指令框中的每一行都是单独的指令 - */ - @SuppressWarnings("rawtypes") - public CustomInstructionKeyframeEvent(T entity, double animationTick, String instructions, AnimationController controller) { - super(entity, animationTick, controller); - this.instructions = instructions; - } -} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/event/InstructionKeyFrameExecutor.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/event/InstructionKeyFrameExecutor.java new file mode 100644 index 000000000..a45f70fdb --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/event/InstructionKeyFrameExecutor.java @@ -0,0 +1,48 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.event; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.event.EventKeyFrame; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.value.IValue; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExpressionEvaluator; + +import java.util.List; + +public class InstructionKeyFrameExecutor { + private final List> list; + private int nextIndex = 0; + + public InstructionKeyFrameExecutor(List> list) { + this.list = list; + } + + private void evalValues(ExpressionEvaluator evaluator, IValue[] values) { + for (IValue value : values) { + value.evalAsDouble(evaluator); + } + } + + public void executeTo(ExpressionEvaluator evaluator, double currentTick) { + while (!reachEnd()) { + EventKeyFrame keyFrame = list.get(nextIndex); + if (keyFrame.getStartTick() > currentTick) { + return; + } + evalValues(evaluator, keyFrame.getEventData()); + nextIndex++; + } + } + + public void executeRemaining(ExpressionEvaluator evaluator) { + for (int i = nextIndex; i < list.size(); i++) { + evalValues(evaluator, list.get(i).getEventData()); + } + nextIndex = list.size(); + } + + public boolean reachEnd() { + return nextIndex >= list.size(); + } + + public void reset() { + nextIndex = 0; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/event/predicate/AnimationEvent.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/event/predicate/AnimationEvent.java index 4127d0d7f..e7d4fde8c 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/event/predicate/AnimationEvent.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/event/predicate/AnimationEvent.java @@ -7,7 +7,7 @@ import java.util.List; public class AnimationEvent> { - private final T animatableEntity; + private final T animatable; private final float limbSwing; private final float limbSwingAmount; private final float partialTick; @@ -16,9 +16,9 @@ public class AnimationEvent> { public double animationTick; protected AnimationController controller; - public AnimationEvent(T animatableEntity, float limbSwing, float limbSwingAmount, float partialTick, boolean isMoving, + public AnimationEvent(T animatable, float limbSwing, float limbSwingAmount, float partialTick, boolean isMoving, List extraData) { - this.animatableEntity = animatableEntity; + this.animatable = animatable; this.limbSwing = limbSwing; this.limbSwingAmount = limbSwingAmount; this.partialTick = partialTick; @@ -34,7 +34,7 @@ public double getAnimationTick() { } public T getAnimatableEntity() { - return animatableEntity; + return animatable; } public float getLimbSwing() { @@ -65,6 +65,7 @@ public List getExtraData() { return extraData; } + @SuppressWarnings("unchecked") public List getExtraDataOfType(Class type) { ObjectArrayList matches = new ObjectArrayList<>(); for (Object obj : this.extraData) { diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/AnimationPoint.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/AnimationPoint.java index c10af0dca..254ea7de5 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/AnimationPoint.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/AnimationPoint.java @@ -5,42 +5,38 @@ package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.controller.AnimationControllerContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.AnimationContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExpressionEvaluator; +import org.joml.Vector3f; -@SuppressWarnings("rawtypes") -public class AnimationPoint { +public abstract class AnimationPoint { /** - * 动画插值中要从中获取的当前 tick + * 当前关键帧播放进度 */ public final double currentTick; /** - * 当前动画结束的 tick + * 当前关键帧总长度 */ - public final double animationEndTick; + public final double totalTick; /** - * 动画起始值 + * 与动画控制器相关的 molang 上下文 */ - public final double animationStartValue; - /** - * 动画结束值 - */ - public final double animationEndValue; + private final AnimationControllerContext context; - /** - * 当前关键帧 - */ - public final KeyFrame keyframe; + public AnimationPoint(double currentTick, double totalTick, AnimationControllerContext context) { + this.currentTick = currentTick; + this.totalTick = totalTick; + this.context = context; + } - public AnimationPoint(KeyFrame keyframe, double tick, double animationEndTick, double animationStartValue, double animationEndValue) { - this.keyframe = keyframe; - this.currentTick = tick; - this.animationEndTick = animationEndTick; - this.animationStartValue = animationStartValue; - this.animationEndValue = animationEndValue; + protected double getPercentCompleted() { + return totalTick == 0 ? 1 : (currentTick / totalTick); } - @Override - public String toString() { - return "Tick: " + currentTick + " | End Tick: " + animationEndTick + " | Start Value: " + animationStartValue - + " | End Value: " + animationEndValue; + protected void setupControllerContext(ExpressionEvaluator> evaluator) { + evaluator.entity().setAnimationControllerContext(context); } + + public abstract Vector3f getLerpPoint(ExpressionEvaluator> evaluator); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/AnimationPointQueue.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/AnimationPointQueue.java index e198362a4..f5318946f 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/AnimationPointQueue.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/AnimationPointQueue.java @@ -5,16 +5,10 @@ package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.processor.IBone; - -import java.io.Serial; import java.util.LinkedList; /** * 动画点队列,这些动画点用于插值计算 */ public class AnimationPointQueue extends LinkedList { - @Serial - private static final long serialVersionUID = 5472797438476621193L; - public IBone model; } \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/BoneAnimation.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/BoneAnimation.java index 64ab34c5e..d5dfb4798 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/BoneAnimation.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/BoneAnimation.java @@ -5,15 +5,17 @@ package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone.BoneKeyFrame; + +import java.util.List; public class BoneAnimation { public final String boneName; - public final VectorKeyFrameList> rotationKeyFrames; - public final VectorKeyFrameList> positionKeyFrames; - public final VectorKeyFrameList> scaleKeyFrames; + public final List rotationKeyFrames; + public final List positionKeyFrames; + public final List scaleKeyFrames; - public BoneAnimation(String boneName, VectorKeyFrameList> rotationKeyFrames, VectorKeyFrameList> positionKeyFrames, VectorKeyFrameList> scaleKeyFrames) { + public BoneAnimation(String boneName, List rotationKeyFrames, List positionKeyFrames, List scaleKeyFrames) { this.boneName = boneName; this.rotationKeyFrames = rotationKeyFrames; this.positionKeyFrames = positionKeyFrames; diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/BoneAnimationQueue.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/BoneAnimationQueue.java index 2a282e5dc..b6b57b7de 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/BoneAnimationQueue.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/BoneAnimationQueue.java @@ -7,8 +7,7 @@ import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.snapshot.BoneSnapshot; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.snapshot.BoneTopLevelSnapshot; - -import javax.annotation.Nullable; +import org.jetbrains.annotations.Nullable; public class BoneAnimationQueue { public final BoneTopLevelSnapshot topLevelSnapshot; @@ -16,76 +15,39 @@ public class BoneAnimationQueue { @Nullable public BoneAnimation animation; - // TODO: 待改进 - public AnimationPointQueue rotationXQueue = new AnimationPointQueue(); - public AnimationPointQueue rotationYQueue = new AnimationPointQueue(); - public AnimationPointQueue rotationZQueue = new AnimationPointQueue(); - public AnimationPointQueue positionXQueue = new AnimationPointQueue(); - public AnimationPointQueue positionYQueue = new AnimationPointQueue(); - public AnimationPointQueue positionZQueue = new AnimationPointQueue(); - public AnimationPointQueue scaleXQueue = new AnimationPointQueue(); - public AnimationPointQueue scaleYQueue = new AnimationPointQueue(); - public AnimationPointQueue scaleZQueue = new AnimationPointQueue(); + public AnimationPointQueue rotationQueue = new AnimationPointQueue(); + public AnimationPointQueue positionQueue = new AnimationPointQueue(); + public AnimationPointQueue scaleQueue = new AnimationPointQueue(); public BoneAnimationQueue(BoneTopLevelSnapshot snapshot) { - this.topLevelSnapshot = snapshot; - this.controllerSnapshot = new BoneSnapshot(snapshot); + topLevelSnapshot = snapshot; + controllerSnapshot = new BoneSnapshot(snapshot); } public BoneSnapshot snapshot() { return controllerSnapshot; } - public AnimationPointQueue rotationXQueue() { - return rotationXQueue; - } - - public AnimationPointQueue rotationYQueue() { - return rotationYQueue; - } - - public AnimationPointQueue rotationZQueue() { - return rotationZQueue; - } - - public AnimationPointQueue positionXQueue() { - return positionXQueue; - } - - public AnimationPointQueue positionYQueue() { - return positionYQueue; - } - - public AnimationPointQueue positionZQueue() { - return positionZQueue; - } - - public AnimationPointQueue scaleXQueue() { - return scaleXQueue; + public AnimationPointQueue rotationQueue() { + return rotationQueue; } - public AnimationPointQueue scaleYQueue() { - return scaleYQueue; + public AnimationPointQueue positionQueue() { + return positionQueue; } - public AnimationPointQueue scaleZQueue() { - return scaleZQueue; + public AnimationPointQueue scaleQueue() { + return scaleQueue; } public void updateSnapshot() { - this.controllerSnapshot.copyFrom(this.topLevelSnapshot); + controllerSnapshot.copyFrom(topLevelSnapshot); } // 链表重开比 clear() 快 public void resetQueues() { - this.rotationXQueue = new AnimationPointQueue(); - this.rotationYQueue = new AnimationPointQueue(); - this.rotationZQueue = new AnimationPointQueue(); - this.positionXQueue = new AnimationPointQueue(); - this.positionYQueue = new AnimationPointQueue(); - this.positionZQueue = new AnimationPointQueue(); - this.scaleXQueue = new AnimationPointQueue(); - this.scaleYQueue = new AnimationPointQueue(); - this.scaleZQueue = new AnimationPointQueue(); + rotationQueue = new AnimationPointQueue(); + positionQueue = new AnimationPointQueue(); + scaleQueue = new AnimationPointQueue(); } } \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/KeyFrame.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/KeyFrame.java deleted file mode 100644 index 611aca46b..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/KeyFrame.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2020. - * Author: Bernie G. (Gecko) - */ - -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe; - -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.easing.EasingType; -import it.unimi.dsi.fastutil.doubles.DoubleLists; - -import java.util.List; -import java.util.Objects; - -public class KeyFrame { - public final EasingType easingType; - public final List easingArgs; - private final Double length; - private final T startValue; - private final T endValue; - - public KeyFrame(Double length, T startValue, T endValue) { - this.length = length; - this.startValue = startValue; - this.endValue = endValue; - this.easingType = EasingType.LINEAR; - this.easingArgs = DoubleLists.emptyList(); - } - - public KeyFrame(Double length, T startValue, T endValue, EasingType easingType) { - this.length = length; - this.startValue = startValue; - this.endValue = endValue; - this.easingType = easingType; - this.easingArgs = DoubleLists.emptyList(); - } - - public KeyFrame(Double length, T startValue, T endValue, EasingType easingType, List easingArgs) { - this.length = length; - this.startValue = startValue; - this.endValue = endValue; - this.easingType = easingType; - this.easingArgs = easingArgs; - } - - public Double getLength() { - return length; - } - - public T getStartValue() { - return startValue; - } - - public T getEndValue() { - return endValue; - } - - @Override - public int hashCode() { - return Objects.hash(length, startValue, endValue); - } - - @Override - public boolean equals(Object obj) { - return obj instanceof KeyFrame && hashCode() == obj.hashCode(); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/KeyFrameLocation.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/KeyFrameLocation.java deleted file mode 100644 index 579155ffe..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/KeyFrameLocation.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2020. - * Author: Bernie G. (Gecko) - */ - -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe; - -/** - * 为动画存储一个时间,并返回需要执行的关键帧 - */ -@SuppressWarnings("rawtypes") -public class KeyFrameLocation { - /** - * 当前关键帧 - */ - public T currentFrame; - /** - * 这是之前所有关键帧的总时间 - */ - public double currentTick; - - public KeyFrameLocation(T currentFrame, double currentTick) { - this.currentFrame = currentFrame; - this.currentTick = currentTick; - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/KeyFramePoint.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/KeyFramePoint.java new file mode 100644 index 000000000..1fa969834 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/KeyFramePoint.java @@ -0,0 +1,22 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.controller.AnimationControllerContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone.BoneKeyFrame; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.AnimationContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExpressionEvaluator; +import org.joml.Vector3f; + +public class KeyFramePoint extends AnimationPoint { + public final BoneKeyFrame keyframe; + + public KeyFramePoint(double currentTick, BoneKeyFrame keyframe, AnimationControllerContext context) { + super(currentTick, keyframe.getTotalTick(), context); + this.keyframe = keyframe; + } + + @Override + public Vector3f getLerpPoint(ExpressionEvaluator> evaluator) { + setupControllerContext(evaluator); + return keyframe.getLerpPoint(evaluator, getPercentCompleted()); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/TransitionPoint.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/TransitionPoint.java new file mode 100644 index 000000000..79f2b5160 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/TransitionPoint.java @@ -0,0 +1,24 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.controller.AnimationControllerContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone.BoneKeyFrame; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.AnimationContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExpressionEvaluator; +import org.joml.Vector3f; + +public class TransitionPoint extends AnimationPoint { + private final Vector3f offsetPoint; + private final BoneKeyFrame dstKeyframe; + + public TransitionPoint(double currentTick, double totalTick, Vector3f offsetPoint, BoneKeyFrame dstKeyframe, AnimationControllerContext context) { + super(currentTick, totalTick, context); + this.offsetPoint = offsetPoint; + this.dstKeyframe = dstKeyframe; + } + + @Override + public Vector3f getLerpPoint(ExpressionEvaluator> evaluator) { + setupControllerContext(evaluator); + return dstKeyframe.getTransitionPoint(evaluator, offsetPoint, getPercentCompleted()); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/VectorKeyFrameList.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/VectorKeyFrameList.java deleted file mode 100644 index dc74b7fdc..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/VectorKeyFrameList.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020. - * Author: Bernie G. (Gecko) - */ - -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe; - -import it.unimi.dsi.fastutil.objects.ObjectArrayList; - -import java.util.List; - -/** - * 矢量关键帧列表是一个方便的类,用于存储 3 个关键帧列表:X、Y 和 Z 关键帧。 - * 关键帧可以是旋转、缩放或位置。 - */ -@SuppressWarnings("rawtypes") -public class VectorKeyFrameList { - public final List xKeyFrames; - public final List yKeyFrames; - public final List zKeyFrames; - - public VectorKeyFrameList(List XKeyFrames, List YKeyFrames, List ZKeyFrames) { - this.xKeyFrames = XKeyFrames; - this.yKeyFrames = YKeyFrames; - this.zKeyFrames = ZKeyFrames; - } - - public VectorKeyFrameList() { - this.xKeyFrames = new ObjectArrayList<>(); - this.yKeyFrames = new ObjectArrayList<>(); - this.zKeyFrames = new ObjectArrayList<>(); - } - - public double getLastKeyframeTime() { - double xTime = 0; - for (T frame : this.xKeyFrames) { - xTime += frame.getLength(); - } - double yTime = 0; - for (T frame : this.yKeyFrames) { - yTime += frame.getLength(); - } - double zTime = 0; - for (T frame : this.zKeyFrames) { - zTime += frame.getLength(); - } - return Math.max(xTime, Math.max(yTime, zTime)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/BoneKeyFrame.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/BoneKeyFrame.java new file mode 100644 index 000000000..5b44e9435 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/BoneKeyFrame.java @@ -0,0 +1,45 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.util.MathUtil; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExpressionEvaluator; +import org.joml.Vector3f; + +public abstract class BoneKeyFrame { + protected final double startTick; + protected final double totalTick; + protected final Vector3v beginPoint; + + public BoneKeyFrame(double startTick, double totalTick, Vector3v beginPoint) { + this.startTick = startTick; + this.totalTick = totalTick; + this.beginPoint = beginPoint; + } + + public double getStartTick() { + return startTick; + } + + public double getTotalTick() { + return totalTick; + } + + public abstract Vector3f getLerpPoint(ExpressionEvaluator evaluator, double percentCompleted); + + public Vector3f getTransitionPoint(ExpressionEvaluator evaluator, Vector3f offsetPoint, double percentCompleted) { + if (isBegin(percentCompleted)) { + return offsetPoint; + } + if (isEnd(percentCompleted)) { + return beginPoint.eval(evaluator); + } + return MathUtil.lerpValues(percentCompleted, offsetPoint, beginPoint.eval(evaluator)); + } + + protected static boolean isBegin(double percentCompleted) { + return percentCompleted < 0.00001; + } + + protected static boolean isEnd(double percentCompleted) { + return percentCompleted > 0.99999; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/BoneKeyFrameProcessor.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/BoneKeyFrameProcessor.java new file mode 100644 index 000000000..fa658c50c --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/BoneKeyFrameProcessor.java @@ -0,0 +1,26 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone; + +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; + +import java.util.List; + +public class BoneKeyFrameProcessor { + public static List process(List frames, boolean isRotation) { + for (RawBoneKeyFrame frame : frames) { + frame.init(isRotation); + } + BoneKeyFrame[] list = new BoneKeyFrame[frames.size()]; + for (int i = 0; i < frames.size(); i++) { + RawBoneKeyFrame end = frames.get(i); + // 虽然感觉不太合理,但还是和 BlockBench 保持一致比较好 + EasingType easingType; + if (end.easingType() == EasingType.CATMULLROM || i == 0) { + easingType = end.easingType(); + } else { + easingType = frames.get(i - 1).easingType(); + } + list[i] = easingType.buildKeyFrame(frames, i); + } + return ReferenceArrayList.wrap(list); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/CatmullRomKeyFrame.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/CatmullRomKeyFrame.java new file mode 100644 index 000000000..3e7f1cc4d --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/CatmullRomKeyFrame.java @@ -0,0 +1,35 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.util.MathUtil; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExpressionEvaluator; +import org.joml.Vector3f; + +public class CatmullRomKeyFrame extends BoneKeyFrame { + private final Vector3v leftPoint; + private final Vector3v endPoint; + private final Vector3v rightPoint; + private final Vector3v postPoint; + + public CatmullRomKeyFrame(double startTick, double totalTick, Vector3v leftPoint, Vector3v current, Vector3v endPoint, Vector3v postRight, Vector3v postPoint) { + super(startTick, totalTick, current); + this.leftPoint = leftPoint; + this.endPoint = endPoint; + this.rightPoint = postRight; + this.postPoint = postPoint; + } + + @Override + public Vector3f getLerpPoint(ExpressionEvaluator evaluator, double percentCompleted) { + if (isBegin(percentCompleted)) { + return beginPoint.eval(evaluator); + } + if (isEnd(percentCompleted)) { + return postPoint.eval(evaluator); + } + Vector3f left = leftPoint.eval(evaluator); + Vector3f begin = beginPoint.eval(evaluator); + Vector3f end = endPoint.eval(evaluator); + Vector3f right = rightPoint.eval(evaluator); + return MathUtil.catmullRom(percentCompleted, left, begin, end, right); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/EasingType.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/EasingType.java new file mode 100644 index 000000000..fee42faf2 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/EasingType.java @@ -0,0 +1,37 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone; + +import java.util.List; + +// Native Access:所有字段都有读取 +public interface EasingType { + EasingType LINEAR = EasingType::buildLinearKeyFrame; + EasingType CATMULLROM = EasingType::buildCatmullRomKeyFrame; + + BoneKeyFrame buildKeyFrame(List keyFrames, int index); + + static TransitionKeyFrame buildTransitionKeyFrame(List keyFrames) { + RawBoneKeyFrame transitionDst = keyFrames.get(0); + return new TransitionKeyFrame(transitionDst.startTick(), transitionDst.preValue(), transitionDst.postValue()); + } + + static BoneKeyFrame buildLinearKeyFrame(List keyFrames, int index) { + if (index == 0) { + return buildTransitionKeyFrame(keyFrames); + } + RawBoneKeyFrame begin = keyFrames.get(index - 1); + RawBoneKeyFrame end = keyFrames.get(index); + return new LinearKeyFrame(begin.startTick(), end.startTick() - begin.startTick(), begin.postValue(), end.preValue(), end.postValue()); + } + + static BoneKeyFrame buildCatmullRomKeyFrame(List keyFrames, int index) { + if (index == 0) { + return buildTransitionKeyFrame(keyFrames); + } + RawBoneKeyFrame left = keyFrames.get(Math.max(0, index - 2)); + RawBoneKeyFrame begin = keyFrames.get(index - 1); + RawBoneKeyFrame end = keyFrames.get(index); + RawBoneKeyFrame right = keyFrames.get(Math.min(keyFrames.size() - 1, index + 1)); + + return new CatmullRomKeyFrame(begin.startTick(), end.startTick() - begin.startTick(), left.postValue(), begin.postValue(), end.preValue(), right.preValue(), end.postValue()); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/LinearKeyFrame.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/LinearKeyFrame.java new file mode 100644 index 000000000..2e703f462 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/LinearKeyFrame.java @@ -0,0 +1,29 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.util.MathUtil; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExpressionEvaluator; +import org.joml.Vector3f; + +public class LinearKeyFrame extends BoneKeyFrame { + private final Vector3v endPoint; + private final Vector3v postPoint; + + public LinearKeyFrame(double startTick, double totalTick, Vector3v beginPoint, Vector3v endPoint, Vector3v postPoint) { + super(startTick, totalTick, beginPoint); + this.endPoint = endPoint; + this.postPoint = postPoint; + } + + @Override + public Vector3f getLerpPoint(ExpressionEvaluator evaluator, double percentCompleted) { + if (isBegin(percentCompleted)) { + return beginPoint.eval(evaluator); + } + if (isEnd(percentCompleted)) { + return postPoint.eval(evaluator); + } + Vector3f begin = beginPoint.eval(evaluator); + Vector3f end = endPoint.eval(evaluator); + return MathUtil.lerpValues(percentCompleted, begin, end); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/RawBoneKeyFrame.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/RawBoneKeyFrame.java new file mode 100644 index 000000000..26ecca457 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/RawBoneKeyFrame.java @@ -0,0 +1,87 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone; + + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.value.DoubleValue; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.value.IValue; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.value.RotationValue; + +@SuppressWarnings("FieldMayBeFinal,unused") +public class RawBoneKeyFrame { + public double startTick; + public EasingType easingType; + + public double preX; + public IValue preXValue; + public double preY; + public IValue preYValue; + public double preZ; + public IValue preZValue; + + public double postX; + public IValue postXValue; + public double postY; + public IValue postYValue; + public double postZ; + public IValue postZValue; + + public boolean contiguous = true; + + public Vector3v preValue; + public Vector3v postValue; + + public RawBoneKeyFrame() { + } + + private IValue getValue(IValue value, double primitive, boolean isRotation, boolean flip) { + if (value == null) { + if (isRotation) { + return new DoubleValue(RotationValue.processValue(primitive, flip)); + } else { + return new DoubleValue(primitive); + } + } + if (isRotation) { + return new RotationValue(value, flip); + } else { + return value; + } + } + + public void init(boolean isRotation) { + if (preValue != null) { + return; + } + + preValue = new Vector3v( + getValue(this.preXValue, this.preX, isRotation, true), + getValue(this.preYValue, this.preY, isRotation, true), + getValue(this.preZValue, this.preZ, isRotation, false)); + if (contiguous) { + postValue = preValue; + } else { + postValue = new Vector3v( + getValue(this.postXValue, this.postX, isRotation, true), + getValue(this.postYValue, this.postY, isRotation, true), + getValue(this.postZValue, this.postZ, isRotation, false)); + } + if (easingType == null) { + easingType = EasingType.LINEAR; + } + } + + public double startTick() { + return startTick; + } + + public EasingType easingType() { + return easingType; + } + + public Vector3v preValue() { + return preValue; + } + + public Vector3v postValue() { + return postValue; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/TransitionKeyFrame.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/TransitionKeyFrame.java new file mode 100644 index 000000000..bf4434404 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/TransitionKeyFrame.java @@ -0,0 +1,22 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExpressionEvaluator; +import org.joml.Vector3f; + +public class TransitionKeyFrame extends BoneKeyFrame { + private final Vector3v postPoint; + + public TransitionKeyFrame(double firstStartTick, Vector3v firstPoint, Vector3v postPoint) { + super(0, firstStartTick, firstPoint); + this.postPoint = postPoint; + } + + @Override + public Vector3f getLerpPoint(ExpressionEvaluator evaluator, double percentCompleted) { + if (!isEnd(percentCompleted)) { + return beginPoint.eval(evaluator); + } else { + return postPoint.eval(evaluator); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/Vector3v.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/Vector3v.java new file mode 100644 index 000000000..2f0a4e0ca --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/bone/Vector3v.java @@ -0,0 +1,23 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.value.IValue; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExpressionEvaluator; +import org.joml.Vector3f; + +public class Vector3v { + private final IValue x; + private final IValue y; + private final IValue z; + + public Vector3v(IValue x, IValue y, IValue z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Vector3f eval(ExpressionEvaluator evaluator) { + return new Vector3f((float) x.evalAsDouble(evaluator), + (float) y.evalAsDouble(evaluator), + (float) z.evalAsDouble(evaluator)); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/EventKeyFrame.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/event/EventKeyFrame.java similarity index 96% rename from src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/EventKeyFrame.java rename to src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/event/EventKeyFrame.java index e012faad9..be61a4f9e 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/EventKeyFrame.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/event/EventKeyFrame.java @@ -3,7 +3,8 @@ * Author: Bernie G. (Gecko) */ -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe; +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.event; + public class EventKeyFrame { private final T eventData; diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/ParticleEventKeyFrame.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/event/ParticleEventKeyFrame.java similarity index 65% rename from src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/ParticleEventKeyFrame.java rename to src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/event/ParticleEventKeyFrame.java index 06566effe..b18b30269 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/ParticleEventKeyFrame.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/keyframe/event/ParticleEventKeyFrame.java @@ -1,14 +1,14 @@ -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe; +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.event; public class ParticleEventKeyFrame extends EventKeyFrame { public final String effect; public final String locator; public final String script; - public ParticleEventKeyFrame(Double startTick, String effect, String locator, String script) { - super(startTick, effect + "\n" + locator + "\n" + script); - this.script = script; - this.locator = locator; + public ParticleEventKeyFrame(double startTick, String eventData, String effect, String locator, String script) { + super(startTick, eventData); this.effect = effect; + this.locator = locator; + this.script = script; } } \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/manager/AnimationData.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/manager/AnimationData.java index 53fb2c62a..2dfcbe086 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/manager/AnimationData.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/manager/AnimationData.java @@ -6,43 +6,25 @@ package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.manager; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.controller.AnimationController; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.processor.IBone; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.snapshot.BoneSnapshot; -import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import org.apache.commons.lang3.tuple.Pair; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; -import java.util.HashMap; -import java.util.Map; +import java.util.List; @SuppressWarnings("rawtypes") public class AnimationData { - private final Map animationControllers = new Object2ObjectLinkedOpenHashMap<>(); + private final List animationControllers = new ReferenceArrayList<>(32); public double tick; public boolean isFirstTick = true; public double startTick = -1; public boolean shouldPlayWhilePaused = false; - private Map> boneSnapshotCollection; private double resetTickLength = 1; public AnimationData() { - boneSnapshotCollection = new Object2ObjectOpenHashMap<>(); } public AnimationController addAnimationController(AnimationController value) { - return this.animationControllers.put(value.getName(), value); - } - - public Map> getBoneSnapshotCollection() { - return boneSnapshotCollection; - } - - public void setBoneSnapshotCollection(HashMap> boneSnapshotCollection) { - this.boneSnapshotCollection = boneSnapshotCollection; - } - - public void clearSnapshotCache() { - this.boneSnapshotCollection = new HashMap<>(); + animationControllers.add(value); + return value; } public double getResetSpeed() { @@ -58,7 +40,7 @@ public void setResetSpeedInTicks(double resetTickLength) { this.resetTickLength = resetTickLength < 0 ? 0 : resetTickLength; } - public Map getAnimationControllers() { + public List getAnimationControllers() { return animationControllers; } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/LazyVariable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/LazyVariable.java deleted file mode 100644 index 9f9764c7b..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/LazyVariable.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.Variable; - -import java.util.function.DoubleSupplier; - -/** - * 变量延迟计算,在需要时才会进行计算
- * 这样能优化渲染,只会在需要这个数值时才会进行计算 - */ -public class LazyVariable extends Variable { - private DoubleSupplier valueSupplier; - - @Deprecated - public LazyVariable(String name, double value) { - this(name, () -> value); - } - - public LazyVariable(String name, DoubleSupplier valueSupplier) { - super(name, 0); - this.valueSupplier = valueSupplier; - } - - public static LazyVariable from(Variable variable) { - return new LazyVariable(variable.getName(), variable.get()); - } - - @Override - public void set(double value) { - this.valueSupplier = () -> value; - } - - public void set(DoubleSupplier valueSupplier) { - this.valueSupplier = valueSupplier; - } - - @Override - public double get() { - return this.valueSupplier.getAsDouble(); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/MolangException.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/MolangException.java deleted file mode 100644 index 51e24812f..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/MolangException.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang; - -import java.io.Serial; - -public class MolangException extends Exception { - @Serial - private static final long serialVersionUID = 1470247726869768015L; - - public MolangException(String message) { - super(message); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/MolangParser.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/MolangParser.java index e76a17617..173c0b35b 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/MolangParser.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/MolangParser.java @@ -1,206 +1,47 @@ package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.expressions.MolangAssignment; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.expressions.MolangExpression; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.expressions.MolangMultiStatement; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.expressions.MolangValue; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.functions.CosDegrees; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.functions.SinDegrees; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.Constant; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.MathBuilder; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.Variable; -import com.google.gson.JsonElement; -import com.google.gson.JsonPrimitive; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.binding.PrimaryBinding; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.value.DoubleValue; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.value.IValue; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.value.MolangValue; +import com.github.tartaricacid.touhoulittlemaid.molang.MolangEngine; +import com.github.tartaricacid.touhoulittlemaid.molang.parser.ParseException; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding.ObjectBinding; -import java.util.List; import java.util.Map; -import java.util.function.DoubleSupplier; -/** - * MoLang 解析器 - * Wiki - */ -public class MolangParser extends MathBuilder { - public static final Map VARIABLES = new Object2ObjectOpenHashMap<>(); - public static final MolangExpression ZERO = new MolangValue(null, new Constant(0)); - public static final MolangExpression ONE = new MolangValue(null, new Constant(1)); - public static final String RETURN = "return "; +public class MolangParser { + private final MolangEngine engine; + private final PrimaryBinding primaryBinding; - public MolangParser() { - super(); - // 将函数重新映射为 MoLang 标准名 - doCoreRemaps(); + public MolangParser(Map extraBindings) { + primaryBinding = new PrimaryBinding(extraBindings); + engine = MolangEngine.fromCustomBinding(primaryBinding); } - private void doCoreRemaps() { - // 将 sin 和 cos 改成角度参数 - this.functions.put("cos", CosDegrees.class); - this.functions.put("sin", SinDegrees.class); - - remap("abs", "math.abs"); - remap("acos", "math.acos"); - remap("asin", "math.asin"); - remap("atan", "math.atan"); - remap("atan2", "math.atan2"); - remap("ceil", "math.ceil"); - remap("clamp", "math.clamp"); - remap("cos", "math.cos"); - remap("die_roll", "math.die_roll"); - remap("die_roll_integer", "math.die_roll_integer"); - remap("exp", "math.exp"); - remap("floor", "math.floor"); - remap("hermite_blend", "math.hermite_blend"); - remap("lerp", "math.lerp"); - remap("lerprotate", "math.lerprotate"); - remap("ln", "math.ln"); - remap("max", "math.max"); - remap("min", "math.min"); - remap("mod", "math.mod"); - remap("pi", "math.pi"); - remap("pow", "math.pow"); - remap("random", "math.random"); - remap("random_integer", "math.random_integer"); - remap("round", "math.round"); - remap("sin", "math.sin"); - remap("sqrt", "math.sqrt"); - remap("trunc", "math.trunc"); - } - - @Override - public void register(Variable variable) { - if (!(variable instanceof LazyVariable)) { - variable = LazyVariable.from(variable); - } - VARIABLES.put(variable.getName(), (LazyVariable) variable); - } - - /** - * 重映射方法 - */ - public void remap(String old, String newName) { - this.functions.put(newName, this.functions.remove(old)); - } - - @Deprecated - public void setValue(String name, double value) { - setValue(name, () -> value); - } - - public void setValue(String name, DoubleSupplier value) { - LazyVariable variable = getVariable(name); - if (variable != null) { - variable.set(value); - } - } - - @Override - protected LazyVariable getVariable(String name) { - return VARIABLES.computeIfAbsent(name, key -> new LazyVariable(key, 0)); - } - - public LazyVariable getVariable(String name, MolangMultiStatement currentStatement) { - LazyVariable variable; - if (currentStatement != null) { - variable = currentStatement.locals.get(name); - if (variable != null) { - return variable; - } - } - return getVariable(name); - } - - public MolangExpression parseJson(JsonElement element) throws MolangException { - if (!element.isJsonPrimitive()) { - return ZERO; - } - JsonPrimitive primitive = element.getAsJsonPrimitive(); - if (primitive.isNumber()) { - return new MolangValue(this, new Constant(primitive.getAsDouble())); - } - if (primitive.isString()) { - String string = primitive.getAsString(); - try { - return new MolangValue(this, new Constant(Double.parseDouble(string))); - } catch (NumberFormatException ex) { - return parseExpression(string); - } - } - return ZERO; - } - - /** - * 解析一个 MoLang 表达式 - */ - public MolangExpression parseExpression(String expression) throws MolangException { - MolangMultiStatement result = null; - for (String split : expression.toLowerCase().trim().split(";")) { - String trimmed = split.trim(); - if (!trimmed.isEmpty()) { - if (result == null) { - result = new MolangMultiStatement(this); - } - result.expressions.add(parseOneLine(trimmed, result)); - } - } - if (result == null) { - throw new MolangException("Molang expression cannot be blank!"); - } - return result; - } - - /** - * 解析单个 MoLang 表达式 - */ - protected MolangExpression parseOneLine(String expression, MolangMultiStatement currentStatement) throws MolangException { - if (expression.startsWith(RETURN)) { - try { - return new MolangValue(this, parse(expression.substring(RETURN.length()))).addReturn(); - } catch (Exception e) { - throw new MolangException("Couldn't parse return '" + expression + "' expression!"); - } - } - + @SuppressWarnings("unused") + public IValue parseExpression(String molangExpression) { try { - // 将表达式拆分 - List symbols = breakdownChars(this.breakdown(expression)); - // 如果是赋值表达式 - if (symbols.size() >= 3 && (symbols.get(0) instanceof String name) && isVariable(symbols.get(0)) && symbols.get(1).equals("=")) { - symbols = symbols.subList(2, symbols.size()); - LazyVariable variable; - if (!VARIABLES.containsKey(name) && !currentStatement.locals.containsKey(name)) { - currentStatement.locals.put(name, (variable = new LazyVariable(name, 0))); - } else { - variable = getVariable(name, currentStatement); - } - return new MolangAssignment(this, variable, parseSymbolsMolang(symbols)); - } - // 如果是其他表达式 - return new MolangValue(this, parseSymbolsMolang(symbols)); + return parseExpressionUnsafe(molangExpression); } catch (Exception e) { - throw new MolangException("Couldn't parse '" + expression + "' expression!"); + TouhouLittleMaid.LOGGER.error("Failed to parse value \"{}\": {}", molangExpression, e.getMessage()); + return DoubleValue.ZERO; } } - /** - * 将 parseSymbols 方法包装,并抛出 MolangException - */ - private IValue parseSymbolsMolang(List symbols) throws MolangException { - try { - return this.parseSymbols(symbols); - } catch (Exception e) { - e.printStackTrace(); - throw new MolangException("Couldn't parse an expression!"); - } + public IValue parseExpressionUnsafe(String molangExpression) throws ParseException { + MolangValue value = new MolangValue(engine.parse(molangExpression)); + primaryBinding.popStackFrame(); + return value; + } + + @SuppressWarnings("unused") + public IValue getConstant(double value) { + return new DoubleValue(value); } - /** - * 拓展此方法,从而让 {@link #breakdownChars(String[])} 能够解析等号 - * 这样就能更加轻松解析赋值表达式 - */ - @Override - protected boolean isOperator(String s) { - return super.isOperator(s) || s.equals("="); + public void reset() { + primaryBinding.reset(); } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/ContextBinding.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/ContextBinding.java new file mode 100644 index 000000000..cbc929eaf --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/ContextBinding.java @@ -0,0 +1,85 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.binding; + +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.IValueEvaluator; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.LambdaVariable; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.block.AbstractBlockVariable; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.block.BlockStateVariable; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.block.BlockVariable; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.entity.*; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.item.ItemStackVariable; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.item.ItemVariable; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding.ObjectBinding; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.TamableAnimal; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; + +public class ContextBinding implements ObjectBinding { + private final Object2ReferenceOpenHashMap bindings = new Object2ReferenceOpenHashMap<>(); + + @Override + public Object getProperty(String name) { + return bindings.get(name); + } + + public void function(String name, Function function) { + bindings.put(name, function); + } + + public void constValue(String name, Object value) { + bindings.put(name, value); + } + + public void var(String name, IValueEvaluator> evaluator) { + bindings.put(name, new LambdaVariable<>(evaluator)); + } + + public void entityVar(String name, IValueEvaluator> evaluator) { + bindings.put(name, new EntityVariable(evaluator)); + } + + public void livingEntityVar(String name, IValueEvaluator> evaluator) { + bindings.put(name, new LivingEntityVariable(evaluator)); + } + + public void mobEntityVar(String name, IValueEvaluator> evaluator) { + bindings.put(name, new MobEntityVariable(evaluator)); + } + + public void tamableEntityVar(String name, IValueEvaluator> evaluator) { + bindings.put(name, new TamableEntityVariable(evaluator)); + } + + public void maidEntityVar(String name, IValueEvaluator> evaluator) { + bindings.put(name, new MaidEntityVariable(evaluator)); + } + + public void itemVar(String name, IValueEvaluator> evaluator) { + bindings.put(name, new ItemVariable(evaluator)); + } + + public void itemStackVar(String name, IValueEvaluator> evaluator) { + bindings.put(name, new ItemStackVariable(evaluator)); + } + + public void blockStateVar(String name, IValueEvaluator> evaluator) { + bindings.put(name, new BlockStateVariable(evaluator)); + } + + public void blockVar(String name, IValueEvaluator> evaluator) { + bindings.put(name, new BlockVariable(evaluator)); + } + + public void abstractBlockVar(String name, IValueEvaluator> evaluator) { + bindings.put(name, new AbstractBlockVariable(evaluator)); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/PrimaryBinding.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/PrimaryBinding.java new file mode 100644 index 000000000..39aa4161b --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/PrimaryBinding.java @@ -0,0 +1,55 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.binding; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.binding.variable.ForeignVariableBinding; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.binding.variable.ScopedVariableBinding; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.binding.variable.TempVariableBinding; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.MathBinding; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.QueryBinding; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding.ObjectBinding; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding.StandardBindings; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +public class PrimaryBinding implements ObjectBinding { + protected final Object2ReferenceOpenHashMap bindings = new Object2ReferenceOpenHashMap<>(); + protected final ScopedVariableBinding scopedBinding = new ScopedVariableBinding(); + protected final ForeignVariableBinding foreignBinding = new ForeignVariableBinding(); + protected final TempVariableBinding tempBinding = new TempVariableBinding(); + + public PrimaryBinding(@Nullable Map extraBindings) { + if (extraBindings != null) { + bindings.putAll(extraBindings); + } + bindings.put("math", MathBinding.INSTANCE); + bindings.put("query", QueryBinding.INSTANCE); + bindings.put("q", QueryBinding.INSTANCE); + bindings.put("loop", StandardBindings.LOOP_FUNC); + bindings.put("for_each", StandardBindings.FOR_EACH_FUNC); + + bindings.put("variable", scopedBinding); + bindings.put("v", scopedBinding); + + bindings.put("context", foreignBinding); + bindings.put("c", foreignBinding); + + bindings.put("temp", tempBinding); + bindings.put("t", tempBinding); + } + + @Override + public Object getProperty(String name) { + return bindings.get(name); + } + + public void reset() { + scopedBinding.reset(); + foreignBinding.reset(); + tempBinding.reset(); + } + + public void popStackFrame() { + tempBinding.reset(); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/variable/ForeignVariableBinding.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/variable/ForeignVariableBinding.java new file mode 100644 index 000000000..826fcf6d3 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/variable/ForeignVariableBinding.java @@ -0,0 +1,37 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.binding.variable; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.storage.IForeignVariableStorage; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.util.StringPool; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Variable; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding.ObjectBinding; +import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; +import org.jetbrains.annotations.NotNull; + +@SuppressWarnings("MapOrSetKeyShouldOverrideHashCodeEquals") +public class ForeignVariableBinding implements ObjectBinding { + private final Int2ReferenceOpenHashMap variableMap = new Int2ReferenceOpenHashMap<>(); + + @Override + public Object getProperty(String name) { + return variableMap.computeIfAbsent(StringPool.computeIfAbsent(name), ForeignVariable::new); + } + + public void reset() { + variableMap.clear(); + } + + private record ForeignVariable(int name) implements Variable { + @Override + @SuppressWarnings("unchecked") + public Object evaluate(final @NotNull ExecutionContext context) { + IForeignVariableStorage storage = ((IContext) context.entity()).foreignStorage(); + if (storage != null) { + return storage.getPublic(name); + } else { + return null; + } + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/variable/ScopedVariableBinding.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/variable/ScopedVariableBinding.java new file mode 100644 index 000000000..2998d0915 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/variable/ScopedVariableBinding.java @@ -0,0 +1,37 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.binding.variable; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.util.StringPool; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.AssignableVariable; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding.ObjectBinding; +import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; +import org.jetbrains.annotations.NotNull; + +@SuppressWarnings("MapOrSetKeyShouldOverrideHashCodeEquals") +public class ScopedVariableBinding implements ObjectBinding { + private final Int2ReferenceOpenHashMap variableMap = new Int2ReferenceOpenHashMap<>(); + + @Override + public Object getProperty(String name) { + return variableMap.computeIfAbsent(StringPool.computeIfAbsent(name), ScopedVariable::new); + } + + public void reset() { + variableMap.clear(); + } + + private record ScopedVariable(int name) implements AssignableVariable { + @Override + @SuppressWarnings("unchecked") + public Object evaluate(final @NotNull ExecutionContext context) { + return ((IContext) context.entity()).scopedStorage().getScoped(name); + } + + @Override + @SuppressWarnings("unchecked") + public void assign(@NotNull ExecutionContext context, Object value) { + ((IContext) context.entity()).scopedStorage().setScoped(name, value); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/variable/TempVariableBinding.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/variable/TempVariableBinding.java new file mode 100644 index 000000000..8d110aa54 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/binding/variable/TempVariableBinding.java @@ -0,0 +1,38 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.binding.variable; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.AssignableVariable; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding.ObjectBinding; +import it.unimi.dsi.fastutil.objects.Object2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; +import org.jetbrains.annotations.NotNull; + +public class TempVariableBinding implements ObjectBinding { + private final Object2ReferenceMap variableMap = new Object2ReferenceOpenHashMap<>(); + private int topPointer = 0; + + @Override + public Object getProperty(String name) { + return variableMap.computeIfAbsent(name, k -> new TempVariable(topPointer++)); + } + + public void reset() { + variableMap.clear(); + topPointer = 0; + } + + private record TempVariable(int address) implements AssignableVariable { + @Override + @SuppressWarnings("unchecked") + public Object evaluate(final @NotNull ExecutionContext context) { + return ((IContext) context.entity()).tempStorage().getTemp(address); + } + + @Override + @SuppressWarnings("unchecked") + public void assign(@NotNull ExecutionContext context, Object value) { + ((IContext) context.entity()).tempStorage().setTemp(address, value); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/MathBinding.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/MathBinding.java new file mode 100644 index 000000000..94a4ee5d5 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/MathBinding.java @@ -0,0 +1,59 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.binding.ContextBinding; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math.*; + +public class MathBinding extends ContextBinding { + public static final MathBinding INSTANCE = new MathBinding(); + + private MathBinding() { + /* 常量 */ + constValue("pi", Math.PI); + constValue("e", Math.E); + + /* 取整函数 */ + function("floor", new Floor()); + function("round", new Round()); + function("ceil", new Ceil()); + function("trunc", new Trunc()); + + /* 比较函数 */ + function("clamp", new Clamp()); + function("max", new Max()); + function("min", new Min()); + + /* 经典数学函数 */ + function("abs", new Abs()); + function("exp", new Exp()); + function("ln", new Ln()); + function("sqrt", new Sqrt()); + function("mod", new Mod()); + function("pow", new Pow()); + + /* 三角函数 */ + function("sin", new Sin()); // degree + function("cos", new Cos()); // degree + function("acos", new ACos()); + function("asin", new ASin()); + function("atan", new Atan()); + function("atan2", new ATan2()); + + /* 实用工具 */ + function("lerp", new Lerp()); + function("lerprotate", new LerpRotate()); + function("random", new Random()); + function("random_integer", new RandomInteger()); + function("die_roll", new DieRoll()); + function("die_roll_integer", new DieRollInteger()); + function("hermite_blend", new HermitBlend()); + + /* 其它 */ + function("min_angle", new MinAngle()); + + /* 非标准命名,兼容原 geckolib */ + function("randomi", new RandomInteger()); + function("roll", new DieRoll()); + function("rolli", new DieRollInteger()); + function("hermite", new HermitBlend()); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/QueryBinding.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/QueryBinding.java new file mode 100644 index 000000000..0e219d691 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/QueryBinding.java @@ -0,0 +1,123 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.binding.ContextBinding; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.query.*; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.util.MolangUtils; +import com.github.tartaricacid.touhoulittlemaid.util.EquipmentUtil; +import net.minecraft.client.CameraType; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Pose; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.UseAnim; +import net.minecraft.world.phys.Vec3; + +public class QueryBinding extends ContextBinding { + public static final QueryBinding INSTANCE = new QueryBinding(); + + @SuppressWarnings("resource") + private QueryBinding() { + function("biome_has_all_tags", new BiomeHasAllTags()); + function("biome_has_any_tag", new BiomeHasAnyTag()); + function("relative_block_has_all_tags", new RelativeBlockHasAllTags()); + function("relative_block_has_any_tag", new RelativeBlockHasAnyTag()); + function("is_item_name_any", new ItemNameAny()); + function("equipped_item_all_tags", new EquippedItemAllTags()); + function("equipped_item_any_tag", new EquippedItemAnyTags()); + function("position", new Position()); + function("position_delta", new PositionDelta()); + + function("max_durability", new ItemMaxDurability()); + function("remaining_durability", new ItemRemainingDurability()); + + var("actor_count", ctx -> ctx.level().getEntityCount()); + var("anim_time", ctx -> ctx.animationControllerContext().animTime()); + var("life_time", ctx -> ctx.geoInstance().getSeekTime() / 20.0); + var("head_x_rotation", ctx -> ctx.data().netHeadYaw); + var("head_y_rotation", ctx -> ctx.data().headPitch); + var("moon_phase", ctx -> ctx.level().getMoonPhase()); + var("time_of_day", ctx -> MolangUtils.normalizeTime(ctx.level().getDayTime())); + var("time_stamp", ctx -> ctx.level().getDayTime()); + + entityVar("yaw_speed", ctx -> getYawSpeed(ctx.entity())); + entityVar("cardinal_facing_2d", ctx -> ctx.entity().getDirection().get3DDataValue()); + entityVar("distance_from_camera", ctx -> ctx.mc().gameRenderer.getMainCamera().getPosition().distanceTo(ctx.entity().position())); + entityVar("eye_target_x_rotation", ctx -> ctx.entity().getViewXRot(ctx.animationEvent().getPartialTick())); + entityVar("eye_target_y_rotation", ctx -> ctx.entity().getViewYRot(ctx.animationEvent().getPartialTick())); + entityVar("ground_speed", ctx -> getGroundSpeed(ctx.entity())); + entityVar("modified_distance_moved", ctx -> ctx.entity().walkDist); + entityVar("vertical_speed", ctx -> getVerticalSpeed(ctx.entity())); + entityVar("walk_distance", ctx -> ctx.entity().moveDist); + entityVar("has_rider", ctx -> ctx.entity().isVehicle()); + entityVar("is_first_person", ctx -> ctx.mc().options.getCameraType() == CameraType.FIRST_PERSON); + entityVar("is_in_water", ctx -> ctx.entity().isInWater()); + entityVar("is_in_water_or_rain", ctx -> ctx.entity().isInWaterRainOrBubble()); + entityVar("is_on_fire", ctx -> ctx.entity().isOnFire()); + entityVar("is_on_ground", ctx -> ctx.entity().onGround()); + entityVar("is_riding", ctx -> ctx.entity().isPassenger()); + entityVar("is_sneaking", ctx -> ctx.entity().onGround() && ctx.entity().getPose() == Pose.CROUCHING); + entityVar("is_spectator", ctx -> ctx.entity().isSpectator()); + entityVar("is_sprinting", ctx -> ctx.entity().isSprinting()); + entityVar("is_swimming", ctx -> ctx.entity().isSwimming()); + + livingEntityVar("body_x_rotation", ctx -> Mth.lerp(ctx.animationEvent().getPartialTick(), ctx.entity().xRotO, ctx.entity().getXRot())); + livingEntityVar("body_y_rotation", ctx -> Mth.wrapDegrees(Mth.lerp(ctx.animationEvent().getPartialTick(), ctx.entity().yBodyRotO, ctx.entity().yBodyRot))); + livingEntityVar("health", ctx -> ctx.entity().getHealth()); + livingEntityVar("max_health", ctx -> ctx.entity().getMaxHealth()); + livingEntityVar("hurt_time", ctx -> ctx.entity().hurtTime); + livingEntityVar("is_eating", ctx -> ctx.entity().getUseItem().getUseAnimation() == UseAnim.EAT); + livingEntityVar("is_playing_dead", ctx -> ctx.entity().isDeadOrDying()); + livingEntityVar("is_sleeping", ctx -> ctx.entity().isSleeping()); + livingEntityVar("is_using_item", ctx -> ctx.entity().isUsingItem()); + livingEntityVar("item_in_use_duration", ctx -> ctx.entity().getTicksUsingItem() / 20.0); + livingEntityVar("item_max_use_duration", ctx -> getMaxUseDuration(ctx.entity()) / 20.0); + livingEntityVar("item_remaining_use_duration", ctx -> ctx.entity().getUseItemRemainingTicks() / 20.0); + livingEntityVar("equipment_count", ctx -> getEquipmentCount(ctx.entity())); + + // 为了兼容 ysm 添加的变量 + function("debug_output", new EmptyFunction()); + var("has_cape", ctx -> false); + var("cape_flap_amount", ctx -> 0); + maidEntityVar("player_level", ctx -> ctx.entity().getExperience()); + mobEntityVar("is_jumping", ctx -> !ctx.entity().isPassenger() && !ctx.entity().onGround() && !ctx.entity().isInWater()); + } + + + private static int getEquipmentCount(LivingEntity entity) { + int count = 0; + for (var slot : EquipmentSlot.values()) { + if (!slot.isArmor()) { + continue; + } + var stack = EquipmentUtil.getEquippedItem(entity, slot); + if (!stack.isEmpty()) { + count++; + } + } + return count; + } + + private static double getMaxUseDuration(LivingEntity player) { + ItemStack useItem = player.getUseItem(); + if (useItem.isEmpty()) { + return 0.0; + } else { + return useItem.getUseDuration(); + } + } + + private static float getYawSpeed(Entity entity) { + return 20 * (entity.getYRot() - entity.yRotO); + } + + private static float getGroundSpeed(Entity player) { + Vec3 velocity = player.getDeltaMovement(); + return 20 * Mth.sqrt((float) ((velocity.x * velocity.x) + (velocity.z * velocity.z))); + } + + private static float getVerticalSpeed(Entity entity) { + return 20 * (float) (entity.position().y - entity.yo); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/ACos.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/ACos.java new file mode 100644 index 000000000..29265420e --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/ACos.java @@ -0,0 +1,16 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class ACos implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Math.acos(arguments.getAsDouble(context, 0)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/ASin.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/ASin.java new file mode 100644 index 000000000..e5643c1f5 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/ASin.java @@ -0,0 +1,16 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class ASin implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Math.asin(arguments.getAsDouble(context, 0)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/ATan2.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/ATan2.java new file mode 100644 index 000000000..6240a8bec --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/ATan2.java @@ -0,0 +1,17 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class ATan2 implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Math.atan2(arguments.getAsDouble(context, 0), + arguments.getAsDouble(context, 1)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 2; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Abs.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Abs.java new file mode 100644 index 000000000..6871c3bee --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Abs.java @@ -0,0 +1,16 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class Abs implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Math.abs(arguments.getAsDouble(context, 0)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Atan.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Atan.java new file mode 100644 index 000000000..0056c5317 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Atan.java @@ -0,0 +1,16 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class Atan implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Math.atan(arguments.getAsDouble(context, 0)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Ceil.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Ceil.java new file mode 100644 index 000000000..8003f3d1a --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Ceil.java @@ -0,0 +1,16 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class Ceil implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Math.ceil(arguments.getAsDouble(context, 0)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Clamp.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Clamp.java new file mode 100644 index 000000000..232299ba6 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Clamp.java @@ -0,0 +1,19 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; +import net.minecraft.util.Mth; + +public class Clamp implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Mth.clamp(arguments.getAsDouble(context, 0), + arguments.getAsDouble(context, 1), + arguments.getAsDouble(context, 2)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 3; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Cos.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Cos.java new file mode 100644 index 000000000..6a0016fb3 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Cos.java @@ -0,0 +1,16 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class Cos implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Math.cos(arguments.getAsDouble(context, 0) / 180 * Math.PI); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/DieRoll.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/DieRoll.java new file mode 100644 index 000000000..be54a243e --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/DieRoll.java @@ -0,0 +1,34 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; + +import java.util.Random; + +public class DieRoll extends ContextFunction { + @Override + public boolean validateArgumentSize(int size) { + return size == 3; + } + + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + int i = arguments.getAsInt(context, 0); + double min = arguments.getAsDouble(context, 1); + double range = arguments.getAsDouble(context, 2); + if(min > range) { + double temp = min; + min = range; + range = temp - range; + } else { + range -= min; + } + double total = 0; + Random rnd = context.entity().random(); + while (i-- > 0) { + total += min + rnd.nextDouble() * range; + } + return total; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/DieRollInteger.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/DieRollInteger.java new file mode 100644 index 000000000..abb6d3fa5 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/DieRollInteger.java @@ -0,0 +1,34 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; + +import java.util.Random; + +public class DieRollInteger extends ContextFunction { + @Override + public boolean validateArgumentSize(int size) { + return size == 3; + } + + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + int i = Math.round(arguments.getAsFloat(context, 0)); + int min = arguments.getAsInt(context, 1); + int range = arguments.getAsInt(context, 2); + if(min > range) { + int temp = min; + min = range; + range = temp - range; + } else { + range -= min; + } + int total = 0; + Random rnd = context.entity().random(); + while (i-- > 0) { + total += min + rnd.nextInt(range); + } + return total; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Exp.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Exp.java new file mode 100644 index 000000000..97c85b776 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Exp.java @@ -0,0 +1,16 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class Exp implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Math.exp(arguments.getAsDouble(context, 0)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Floor.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Floor.java new file mode 100644 index 000000000..d2c49b362 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Floor.java @@ -0,0 +1,16 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class Floor implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Math.floor(arguments.getAsDouble(context, 0)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/HermitBlend.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/HermitBlend.java new file mode 100644 index 000000000..89fc5f000 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/HermitBlend.java @@ -0,0 +1,17 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class HermitBlend implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + double min = Math.ceil(arguments.getAsDouble(context, 0)); + return Math.floor(3.0 * Math.pow(min, 2.0) - 2.0 * Math.pow(min, 3.0)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Lerp.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Lerp.java new file mode 100644 index 000000000..479c691cd --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Lerp.java @@ -0,0 +1,19 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.mclib.utils.Interpolations; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class Lerp implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Interpolations.lerp(arguments.getAsDouble(context, 0), + arguments.getAsDouble(context, 1), + arguments.getAsDouble(context, 2)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 3; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/LerpRotate.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/LerpRotate.java new file mode 100644 index 000000000..d9d2596c3 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/LerpRotate.java @@ -0,0 +1,19 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.mclib.utils.Interpolations; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class LerpRotate implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Interpolations.lerpYaw(arguments.getAsDouble(context, 0), + arguments.getAsDouble(context, 1), + arguments.getAsDouble(context, 2)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 3; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Ln.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Ln.java new file mode 100644 index 000000000..abf4bd8a1 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Ln.java @@ -0,0 +1,16 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class Ln implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Math.log(arguments.getAsDouble(context, 0)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Max.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Max.java new file mode 100644 index 000000000..dba75d794 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Max.java @@ -0,0 +1,17 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class Max implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Math.max(arguments.getAsDouble(context, 0), + arguments.getAsDouble(context, 1)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 2; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Min.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Min.java new file mode 100644 index 000000000..e1561ad05 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Min.java @@ -0,0 +1,17 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class Min implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Math.min(arguments.getAsDouble(context, 0), + arguments.getAsDouble(context, 1)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 2; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/MinAngle.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/MinAngle.java new file mode 100644 index 000000000..16376f959 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/MinAngle.java @@ -0,0 +1,23 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class MinAngle implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + double angle = arguments.getAsDouble(context, 0) % 360; + if (angle >= 180) { + return angle - 360; + } else if (angle < -180) { + return angle + 360; + } else { + return angle; + } + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Mod.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Mod.java new file mode 100644 index 000000000..6e389909c --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Mod.java @@ -0,0 +1,16 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class Mod implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return arguments.getAsDouble(context, 0) % arguments.getAsDouble(context, 1); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 2; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Pow.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Pow.java new file mode 100644 index 000000000..d21852c56 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Pow.java @@ -0,0 +1,17 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class Pow implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Math.pow(arguments.getAsDouble(context, 0), + arguments.getAsDouble(context, 1)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 2; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Random.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Random.java new file mode 100644 index 000000000..350a0b676 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Random.java @@ -0,0 +1,26 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; + +public class Random extends ContextFunction { + @Override + public boolean validateArgumentSize(int size) { + return size == 2 || size == 3; // 出于兼容性考虑,允许 3 参数 + } + + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + double min = arguments.getAsDouble(context, 0); + double range = arguments.getAsDouble(context, 1); + if(min > range) { + double temp = min; + min = range; + range = temp - range; + } else { + range -= min; + } + return min + context.entity().random().nextDouble() * range; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/RandomInteger.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/RandomInteger.java new file mode 100644 index 000000000..8e613e2f7 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/RandomInteger.java @@ -0,0 +1,26 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; + +public class RandomInteger extends ContextFunction { + @Override + public boolean validateArgumentSize(int size) { + return size == 2; + } + + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + int min = arguments.getAsInt(context, 0); + int range = arguments.getAsInt(context, 1); + if(min > range) { + int temp = min; + min = range; + range = temp - range; + } else { + range -= min; + } + return min + context.entity().random().nextInt(range); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Round.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Round.java new file mode 100644 index 000000000..7faf5eb41 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Round.java @@ -0,0 +1,16 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class Round implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Math.round(arguments.getAsDouble(context, 0)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Sin.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Sin.java new file mode 100644 index 000000000..b6b36b354 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Sin.java @@ -0,0 +1,16 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class Sin implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Math.sin(arguments.getAsDouble(context, 0) / 180 * Math.PI); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Sqrt.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Sqrt.java new file mode 100644 index 000000000..dac01eee4 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Sqrt.java @@ -0,0 +1,16 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class Sqrt implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + return Math.sqrt(arguments.getAsDouble(context, 0)); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Trunc.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Trunc.java new file mode 100644 index 000000000..8bc2fc4f1 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/math/Trunc.java @@ -0,0 +1,17 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.math; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +public class Trunc implements Function { + @Override + public Object evaluate(ExecutionContext context, ArgumentCollection arguments) { + double value = arguments.getAsDouble(context, 0); + return value < 0 ? Math.ceil(value) : Math.floor(value); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/BiomeHasAllTags.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/BiomeHasAllTags.java new file mode 100644 index 000000000..66fea35ec --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/BiomeHasAllTags.java @@ -0,0 +1,38 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.query; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity.EntityFunction; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.util.MolangUtils; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import net.minecraft.core.Holder; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.biome.Biome; +import net.minecraftforge.registries.ForgeRegistries; + +public class BiomeHasAllTags extends EntityFunction { + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + Entity entity = context.entity().entity(); + Holder biome = entity.level().getBiome(entity.blockPosition()); + + for (int i = 0; i < arguments.size(); i++) { + ResourceLocation id = MolangUtils.parseResourceLocation(context.entity(), arguments.getAsString(context, i)); + if (id == null) { + return null; + } + TagKey tag = ForgeRegistries.BIOMES.tags().createTagKey(id); + if (!biome.is(tag)) { + return false; + } + } + + return true; + } + + @Override + public boolean validateArgumentSize(int size) { + return size >= 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/BiomeHasAnyTag.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/BiomeHasAnyTag.java new file mode 100644 index 000000000..1ae6d6e18 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/BiomeHasAnyTag.java @@ -0,0 +1,38 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.query; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity.EntityFunction; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.util.MolangUtils; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import net.minecraft.core.Holder; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.biome.Biome; +import net.minecraftforge.registries.ForgeRegistries; + +public class BiomeHasAnyTag extends EntityFunction { + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + Entity entity = context.entity().entity(); + Holder biome = entity.level().getBiome(entity.blockPosition()); + + for (int i = 0; i < arguments.size(); i++) { + ResourceLocation id = MolangUtils.parseResourceLocation(context.entity(), arguments.getAsString(context, i)); + if (id == null) { + return null; + } + TagKey tag = ForgeRegistries.BIOMES.tags().createTagKey(id); + if (biome.is(tag)) { + return true; + } + } + + return false; + } + + @Override + public boolean validateArgumentSize(int size) { + return size >= 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/EmptyFunction.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/EmptyFunction.java new file mode 100644 index 000000000..04e0a8811 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/EmptyFunction.java @@ -0,0 +1,12 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.query; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; + +public class EmptyFunction extends ContextFunction { + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + return null; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/EquippedItemAllTags.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/EquippedItemAllTags.java new file mode 100644 index 000000000..f65dd13f8 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/EquippedItemAllTags.java @@ -0,0 +1,47 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.query; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity.LivingEntityFunction; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.util.MolangUtils; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.util.EquipmentUtil; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.registries.ForgeRegistries; + +public class EquippedItemAllTags extends LivingEntityFunction { + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + EquipmentSlot slotType = MolangUtils.parseSlotType(context.entity(), arguments.getAsString(context, 0)); + if (slotType == null) { + return null; + } + + ItemStack itemStack = EquipmentUtil.getEquippedItem(context.entity().entity(), slotType); + if(itemStack.isEmpty()) { + return false; + } + + for (int i = 1; i < arguments.size(); i++) { + ResourceLocation id = MolangUtils.parseResourceLocation(context.entity(), arguments.getAsString(context, i)); + if (id == null) { + return null; + } + TagKey tag = ForgeRegistries.ITEMS.tags().createTagKey(id); + if (!itemStack.is(tag)) { + return false; + } + } + + return true; + } + + @Override + public boolean validateArgumentSize(int size) { + return size >= 2; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/EquippedItemAnyTags.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/EquippedItemAnyTags.java new file mode 100644 index 000000000..0dcfca2ca --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/EquippedItemAnyTags.java @@ -0,0 +1,47 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.query; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity.LivingEntityFunction; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.util.MolangUtils; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.util.EquipmentUtil; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.registries.ForgeRegistries; + +public class EquippedItemAnyTags extends LivingEntityFunction { + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + EquipmentSlot slotType = MolangUtils.parseSlotType(context.entity(), arguments.getAsString(context, 0)); + if (slotType == null) { + return null; + } + + ItemStack itemStack = EquipmentUtil.getEquippedItem(context.entity().entity(), slotType); + if(itemStack.isEmpty()) { + return false; + } + + for (int i = 1; i < arguments.size(); i++) { + ResourceLocation id = MolangUtils.parseResourceLocation(context.entity(), arguments.getAsString(context, i)); + if (id == null) { + return null; + } + TagKey tag = ForgeRegistries.ITEMS.tags().createTagKey(id); + if (itemStack.is(tag)) { + return true; + } + } + + return false; + } + + @Override + public boolean validateArgumentSize(int size) { + return size >= 2; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/ItemMaxDurability.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/ItemMaxDurability.java new file mode 100644 index 000000000..3050eed64 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/ItemMaxDurability.java @@ -0,0 +1,25 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.query; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity.LivingEntityFunction; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.util.MolangUtils; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.util.EquipmentUtil; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; + +public class ItemMaxDurability extends LivingEntityFunction { + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + EquipmentSlot equipmentSlot = MolangUtils.parseSlotType(context.entity(), arguments.getAsString(context, 0)); + LivingEntity entity = context.entity().entity(); + ItemStack itemBySlot = EquipmentUtil.getEquippedItem(entity, equipmentSlot); + return itemBySlot.getMaxDamage(); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/ItemNameAny.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/ItemNameAny.java new file mode 100644 index 000000000..eee40d414 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/ItemNameAny.java @@ -0,0 +1,49 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.query; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity.LivingEntityFunction; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.util.MolangUtils; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.util.EquipmentUtil; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.registries.ForgeRegistries; + +public class ItemNameAny extends LivingEntityFunction { + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + EquipmentSlot slotType = MolangUtils.parseSlotType(context.entity(), arguments.getAsString(context, 0)); + if (slotType == null) { + return null; + } + + ItemStack itemStack = EquipmentUtil.getEquippedItem(context.entity().entity(), slotType); + if(itemStack.isEmpty()) { + return false; + } + + ResourceLocation actualId = ForgeRegistries.ITEMS.getKey(itemStack.getItem()); + if(actualId == null) { + return false; + } + + for (int i = 1; i < arguments.size(); i++) { + ResourceLocation id = MolangUtils.parseResourceLocation(context.entity(), arguments.getAsString(context, i)); + if (id == null) { + return null; + } + if(id.equals(actualId)) { + return true; + } + } + + return false; + } + + @Override + public boolean validateArgumentSize(int size) { + return size >= 2; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/ItemRemainingDurability.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/ItemRemainingDurability.java new file mode 100644 index 000000000..c70091983 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/ItemRemainingDurability.java @@ -0,0 +1,25 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.query; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity.LivingEntityFunction; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.util.MolangUtils; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.util.EquipmentUtil; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; + +public class ItemRemainingDurability extends LivingEntityFunction { + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + EquipmentSlot equipmentSlot = MolangUtils.parseSlotType(context.entity(), arguments.getAsString(context, 0)); + LivingEntity entity = context.entity().entity(); + ItemStack itemBySlot = EquipmentUtil.getEquippedItem(entity, equipmentSlot); + return itemBySlot.getMaxDamage() - itemBySlot.getDamageValue(); + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/Position.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/Position.java new file mode 100644 index 000000000..5bf8065e0 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/Position.java @@ -0,0 +1,27 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.query; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity.EntityFunction; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; + +public class Position extends EntityFunction { + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + int axis = arguments.getAsInt(context, 0); + float partialTicks = context.entity().animationEvent().getPartialTick(); + Entity entity = context.entity().entity(); + switch (axis) { + case 0: return Mth.lerp(partialTicks, entity.xo, entity.getX()); + case 1: return Mth.lerp(partialTicks, entity.yo, entity.getY()); + case 2: return Mth.lerp(partialTicks, entity.zo, entity.getZ()); + default: return null; + } + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/PositionDelta.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/PositionDelta.java new file mode 100644 index 000000000..de01bb2d3 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/PositionDelta.java @@ -0,0 +1,27 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.query; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity.EntityFunction; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; + +public class PositionDelta extends EntityFunction { + @Override + protected Object eval(ExecutionContext> context, ArgumentCollection arguments) { + int axis = arguments.getAsInt(context, 0); + float partialTicks = context.entity().animationEvent().getPartialTick(); + Entity entity = context.entity().entity(); + switch (axis) { + case 0: return Mth.lerp(partialTicks, entity.xo, entity.getX()) - entity.xo; + case 1: return Mth.lerp(partialTicks, entity.yo, entity.getY()) - entity.yo; + case 2: return Mth.lerp(partialTicks, entity.zo, entity.getZ()) - entity.zo; + default: return null; + } + } + + @Override + public boolean validateArgumentSize(int size) { + return size == 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/RelativeBlockHasAllTags.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/RelativeBlockHasAllTags.java new file mode 100644 index 000000000..0552d6be5 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/RelativeBlockHasAllTags.java @@ -0,0 +1,46 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.query; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity.EntityFunction; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.util.MolangUtils; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.registries.ForgeRegistries; + +public class RelativeBlockHasAllTags extends EntityFunction { + @Override + protected Object eval(ExecutionContext> ctx, ArgumentCollection arguments) { + Entity entity = ctx.entity().entity(); + + int offsetX = arguments.getAsInt(ctx, 0); + int offsetY = arguments.getAsInt(ctx, 1); + int offsetZ = arguments.getAsInt(ctx, 2); + if (Math.abs(offsetX) > 8 || Math.abs(offsetY) > 8 || Math.abs(offsetZ) > 8) { + return false; + } + + BlockState block = ctx.entity().entity().level().getBlockState(entity.blockPosition()); + + for (int i = 3; i < arguments.size(); i++) { + ResourceLocation tagId = MolangUtils.parseResourceLocation(ctx.entity(), arguments.getAsString(ctx, i)); + if (tagId == null) { + return null; + } + + TagKey tag = ForgeRegistries.BLOCKS.tags().createTagKey(tagId); + if (!block.is(tag)) { + return false; + } + } + return true; + } + + @Override + public boolean validateArgumentSize(int size) { + return size >= 4; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/RelativeBlockHasAnyTag.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/RelativeBlockHasAnyTag.java new file mode 100644 index 000000000..4bfac9304 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/builtin/query/RelativeBlockHasAnyTag.java @@ -0,0 +1,46 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.builtin.query; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity.EntityFunction; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.util.MolangUtils; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.registries.ForgeRegistries; + +public class RelativeBlockHasAnyTag extends EntityFunction { + @Override + protected Object eval(ExecutionContext> ctx, ArgumentCollection arguments) { + Entity entity = ctx.entity().entity(); + + int offsetX = arguments.getAsInt(ctx, 0); + int offsetY = arguments.getAsInt(ctx, 1); + int offsetZ = arguments.getAsInt(ctx, 2); + if(Math.abs(offsetX) > 8 || Math.abs(offsetY) > 8 || Math.abs(offsetZ) > 8) { + return false; + } + + BlockState block = ctx.entity().entity().level().getBlockState(entity.blockPosition()); + + for (int i = 3; i < arguments.size(); i++) { + ResourceLocation tagId = MolangUtils.parseResourceLocation(ctx.entity(), arguments.getAsString(ctx, i)); + if(tagId == null) { + return null; + } + + TagKey tag = ForgeRegistries.BLOCKS.tags().createTagKey(tagId); + if (block.is(tag)) { + return true; + } + } + return false; + } + + @Override + public boolean validateArgumentSize(int size) { + return size >= 4; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/context/AnimationContext.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/context/AnimationContext.java new file mode 100644 index 000000000..43e129502 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/context/AnimationContext.java @@ -0,0 +1,127 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context; + +import com.github.tartaricacid.touhoulittlemaid.capability.GeckoMaidEntityCapabilityProvider; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.AnimatableEntity; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.controller.AnimationControllerContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.event.predicate.AnimationEvent; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.storage.IForeignVariableStorage; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.storage.IScopedVariableStorage; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.storage.ITempVariableStorage; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.storage.VariableStorage; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.model.provider.data.EntityModelData; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.entity.Mob; + +import java.util.Random; + +public class AnimationContext implements IContext { + protected final TEntity entity; + protected final AnimatableEntity instance; + protected final AnimationEvent animationEvent; + protected final EntityModelData data; + + protected AnimationControllerContext animationControllerContext; + protected Random random; + protected VariableStorage storage; + protected IForeignVariableStorage foreignStorage; + + public AnimationContext(TEntity entity, AnimatableEntity instance, AnimationEvent animationEvent, EntityModelData data) { + this.entity = entity; + this.instance = instance; + this.animationEvent = animationEvent; + this.data = data; + } + + private AnimationContext(TEntity entity, AnimatableEntity instance, AnimationEvent animationEvent, EntityModelData data, AnimationControllerContext animationControllerContext, Random random, VariableStorage storage) { + this.entity = entity; + this.instance = instance; + this.animationEvent = animationEvent; + this.data = data; + this.animationControllerContext = animationControllerContext; + this.random = random; + this.storage = storage; + if (entity instanceof Mob mob) { + mob.getCapability(GeckoMaidEntityCapabilityProvider.CAP) + .ifPresent(cap -> foreignStorage = cap.getAnimationProcessor().getPublicVariableStorage()); + } + } + + @Override + public AnimationEvent animationEvent() { + return animationEvent; + } + + @Override + public AnimatableEntity geoInstance() { + return instance; + } + + @Override + public EntityModelData data() { + return data; + } + + @Override + public AnimationControllerContext animationControllerContext() { + return animationControllerContext; + } + + @Override + public Random random() { + return random; + } + + @Override + public TEntity entity() { + return entity; + } + + @Override + public Minecraft mc() { + return Minecraft.getInstance(); + } + + @Override + public ClientLevel level() { + Minecraft mc = mc(); + if (mc != null) { + return mc.level; + } else { + return null; + } + } + + @Override + public IContext createChild(TChild child) { + return new AnimationContext<>(child, instance, animationEvent, data, animationControllerContext, random, storage); + } + + @Override + public ITempVariableStorage tempStorage() { + return storage; + } + + @Override + public IScopedVariableStorage scopedStorage() { + return storage; + } + + @Override + public IForeignVariableStorage foreignStorage() { + return foreignStorage; + } + + public void setAnimationControllerContext(AnimationControllerContext animationControllerContext) { + this.animationControllerContext = animationControllerContext; + } + + public void setStorage(VariableStorage storage) { + this.storage = storage; + this.foreignStorage = storage; + } + + public void setRandom(Random random) { + this.random = random; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/context/IContext.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/context/IContext.java new file mode 100644 index 000000000..44d82ac52 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/context/IContext.java @@ -0,0 +1,39 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.AnimatableEntity; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.controller.AnimationControllerContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.event.predicate.AnimationEvent; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.storage.IForeignVariableStorage; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.storage.IScopedVariableStorage; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.storage.ITempVariableStorage; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.model.provider.data.EntityModelData; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; + +import java.util.Random; + +public interface IContext { + TEntity entity(); + + AnimatableEntity geoInstance(); + + Minecraft mc(); + + ClientLevel level(); + + AnimationEvent animationEvent(); + + EntityModelData data(); + + AnimationControllerContext animationControllerContext(); + + Random random(); + + IContext createChild(TChild child); + + ITempVariableStorage tempStorage(); + + IScopedVariableStorage scopedStorage(); + + IForeignVariableStorage foreignStorage(); +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/expressions/MolangAssignment.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/expressions/MolangAssignment.java deleted file mode 100644 index 73e3851e6..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/expressions/MolangAssignment.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.expressions; - -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.MolangParser; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.Variable; - - -public class MolangAssignment extends MolangExpression { - public Variable variable; - public IValue expression; - - public MolangAssignment(MolangParser context, Variable variable, IValue expression) { - super(context); - this.variable = variable; - this.expression = expression; - } - - @Override - public double get() { - double value = this.expression.get(); - this.variable.set(value); - return value; - } - - @Override - public String toString() { - return this.variable.getName() + " = " + this.expression.toString(); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/expressions/MolangExpression.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/expressions/MolangExpression.java deleted file mode 100644 index 0dd973695..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/expressions/MolangExpression.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.expressions; - -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.MolangParser; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.Constant; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.Operation; -import com.google.gson.JsonElement; -import com.google.gson.JsonPrimitive; - -public abstract class MolangExpression implements IValue { - public MolangParser context; - - public MolangExpression(MolangParser context) { - this.context = context; - } - - public static boolean isZero(MolangExpression expression) { - return isConstant(expression, 0); - } - - public static boolean isOne(MolangExpression expression) { - return isConstant(expression, 1); - } - - public static boolean isConstant(MolangExpression expression, double x) { - if (expression instanceof MolangValue value) { - return value.value instanceof Constant && Operation.equals(value.value.get(), x); - } - return false; - } - - public static boolean isExpressionConstant(MolangExpression expression) { - if (expression instanceof MolangValue value) { - return value.value instanceof Constant; - } - return false; - } - - - public JsonElement toJson() { - return new JsonPrimitive(this.toString()); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/expressions/MolangMultiStatement.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/expressions/MolangMultiStatement.java deleted file mode 100644 index fec569a32..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/expressions/MolangMultiStatement.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.expressions; - -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.LazyVariable; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.MolangParser; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; - -import java.util.List; -import java.util.Map; -import java.util.StringJoiner; - -public class MolangMultiStatement extends MolangExpression { - public final List expressions = new ObjectArrayList<>(); - public final Map locals = new Object2ObjectOpenHashMap<>(); - - public MolangMultiStatement(MolangParser context) { - super(context); - } - - @Override - public double get() { - double value = 0; - for (MolangExpression expression : this.expressions) { - value = expression.get(); - } - return value; - } - - @Override - public String toString() { - StringJoiner builder = new StringJoiner("; "); - for (MolangExpression expression : this.expressions) { - builder.add(expression.toString()); - if (expression instanceof MolangValue && ((MolangValue) expression).returns) { - break; - } - } - return builder.toString(); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/expressions/MolangValue.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/expressions/MolangValue.java deleted file mode 100644 index 7c78e22c1..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/expressions/MolangValue.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.expressions; - -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.MolangParser; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.Constant; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.google.gson.JsonElement; -import com.google.gson.JsonPrimitive; - -public class MolangValue extends MolangExpression { - public IValue value; - public boolean returns; - - public MolangValue(MolangParser context, IValue value) { - super(context); - this.value = value; - } - - public MolangExpression addReturn() { - this.returns = true; - return this; - } - - @Override - public double get() { - return this.value.get(); - } - - @Override - public String toString() { - return (this.returns ? MolangParser.RETURN : "") + this.value.toString(); - } - - @Override - public JsonElement toJson() { - if (this.value instanceof Constant) { - return new JsonPrimitive(this.value.get()); - } - return super.toJson(); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/ContextFunction.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/ContextFunction.java new file mode 100644 index 000000000..e552f10e7 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/ContextFunction.java @@ -0,0 +1,27 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class ContextFunction implements Function { + protected boolean validateContext(IContext context) { + return true; + } + + @Nullable + @Override + @SuppressWarnings("unchecked") + public final Object evaluate(@NotNull ExecutionContext context, @NotNull ArgumentCollection arguments) { + Object entity = context.entity(); + if (entity instanceof IContext && validateContext((IContext) entity)) { + return eval((ExecutionContext>) context, arguments); + } else { + return null; + } + } + + protected abstract Object eval(ExecutionContext> context, ArgumentCollection arguments); +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/blocks/AbstractBlockFunction.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/blocks/AbstractBlockFunction.java new file mode 100644 index 000000000..460810442 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/blocks/AbstractBlockFunction.java @@ -0,0 +1,12 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.blocks; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import net.minecraft.world.level.block.state.BlockBehaviour; + +public abstract class AbstractBlockFunction extends ContextFunction { + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof BlockBehaviour; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/blocks/BlockFunction.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/blocks/BlockFunction.java new file mode 100644 index 000000000..ccf7cbd24 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/blocks/BlockFunction.java @@ -0,0 +1,12 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.blocks; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import net.minecraft.world.level.block.Block; + +public abstract class BlockFunction extends ContextFunction { + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof Block; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/AbstractArrowEntityFunction.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/AbstractArrowEntityFunction.java new file mode 100644 index 000000000..bbbf80dfb --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/AbstractArrowEntityFunction.java @@ -0,0 +1,12 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import net.minecraft.world.entity.projectile.AbstractArrow; + +public abstract class AbstractArrowEntityFunction extends ContextFunction { + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof AbstractArrow; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/ArrowEntityFunction.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/ArrowEntityFunction.java new file mode 100644 index 000000000..4c64ceded --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/ArrowEntityFunction.java @@ -0,0 +1,12 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import net.minecraft.world.entity.projectile.Arrow; + +public abstract class ArrowEntityFunction extends ContextFunction { + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof Arrow; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/EntityFunction.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/EntityFunction.java new file mode 100644 index 000000000..1629c3e5d --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/EntityFunction.java @@ -0,0 +1,12 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import net.minecraft.world.entity.Entity; + +public abstract class EntityFunction extends ContextFunction { + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof Entity; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/LivingEntityFunction.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/LivingEntityFunction.java new file mode 100644 index 000000000..bb4294a1e --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/LivingEntityFunction.java @@ -0,0 +1,12 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import net.minecraft.world.entity.LivingEntity; + +public abstract class LivingEntityFunction extends ContextFunction { + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof LivingEntity; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/LocalPlayerEntityFunction.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/LocalPlayerEntityFunction.java new file mode 100644 index 000000000..e6f3143d9 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/LocalPlayerEntityFunction.java @@ -0,0 +1,12 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import net.minecraft.client.player.LocalPlayer; + +public abstract class LocalPlayerEntityFunction extends ContextFunction { + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof LocalPlayer; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/MobEntityFunction.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/MobEntityFunction.java new file mode 100644 index 000000000..a1d92275b --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/MobEntityFunction.java @@ -0,0 +1,12 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import net.minecraft.world.entity.Mob; + +public abstract class MobEntityFunction extends ContextFunction { + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof Mob; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/PlayerEntityFunction.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/PlayerEntityFunction.java new file mode 100644 index 000000000..3d48221e9 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/PlayerEntityFunction.java @@ -0,0 +1,12 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import net.minecraft.client.player.AbstractClientPlayer; + +public abstract class PlayerEntityFunction extends ContextFunction { + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof AbstractClientPlayer; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/TamableEntityFunction.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/TamableEntityFunction.java new file mode 100644 index 000000000..e7a398c13 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/entity/TamableEntityFunction.java @@ -0,0 +1,12 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.entity; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import net.minecraft.world.entity.TamableAnimal; + +public abstract class TamableEntityFunction extends ContextFunction { + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof TamableAnimal; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/item/ItemFunction.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/item/ItemFunction.java new file mode 100644 index 000000000..887658cf9 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/item/ItemFunction.java @@ -0,0 +1,12 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.item; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import net.minecraft.world.item.Item; + +public abstract class ItemFunction extends ContextFunction { + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof Item; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/item/ItemStackFunction.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/item/ItemStackFunction.java new file mode 100644 index 000000000..8945eba5c --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/function/item/ItemStackFunction.java @@ -0,0 +1,12 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.item; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.function.ContextFunction; +import net.minecraft.world.item.ItemStack; + +public abstract class ItemStackFunction extends ContextFunction { + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof ItemStack; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/functions/CosDegrees.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/functions/CosDegrees.java deleted file mode 100644 index 0a19c998a..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/functions/CosDegrees.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.functions; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class CosDegrees extends Function { - public CosDegrees(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 1; - } - - @Override - public double get() { - return Math.cos(this.getArg(0) / 180 * Math.PI); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/functions/SinDegrees.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/functions/SinDegrees.java deleted file mode 100644 index d9914d759..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/functions/SinDegrees.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.functions; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class SinDegrees extends Function { - public SinDegrees(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 1; - } - - @Override - public double get() { - return Math.sin(this.getArg(0) / 180 * Math.PI); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/storage/IForeignVariableStorage.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/storage/IForeignVariableStorage.java new file mode 100644 index 000000000..f6c0da312 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/storage/IForeignVariableStorage.java @@ -0,0 +1,5 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.storage; + +public interface IForeignVariableStorage { + Object getPublic(int name); +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/storage/IScopedVariableStorage.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/storage/IScopedVariableStorage.java new file mode 100644 index 000000000..2747ff001 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/storage/IScopedVariableStorage.java @@ -0,0 +1,7 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.storage; + +public interface IScopedVariableStorage { + Object getScoped(int name); + + void setScoped(int name, Object value); +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/storage/ITempVariableStorage.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/storage/ITempVariableStorage.java new file mode 100644 index 000000000..7de2ef55a --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/storage/ITempVariableStorage.java @@ -0,0 +1,7 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.storage; + +public interface ITempVariableStorage { + Object getTemp(int address); + + void setTemp(int address, Object value); +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/storage/VariableStorage.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/storage/VariableStorage.java new file mode 100644 index 000000000..6fef0b20b --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/storage/VariableStorage.java @@ -0,0 +1,85 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.storage; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.util.PooledStringHashMap; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.util.PooledStringHashSet; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; + +@SuppressWarnings("MapOrSetKeyShouldOverrideHashCodeEquals") +public class VariableStorage implements ITempVariableStorage, IScopedVariableStorage, IForeignVariableStorage { + private static final int TEMP_INIT_CAPACITY = 16; + private static final int SCOPED_INIT_CAPACITY = 16; + + private Object[] stackFrame = new Object[TEMP_INIT_CAPACITY]; + private final PooledStringHashMap scopedMap = new PooledStringHashMap<>(SCOPED_INIT_CAPACITY); + private PooledStringHashMap publicMap = new PooledStringHashMap<>(); + + public VariableStorage() { + } + + private void ensureStackFrameSize(int size) { + if (stackFrame.length >= size) { + return; + } + if (size < stackFrame.length * 2) { + size = stackFrame.length * 2; + } + stackFrame = Arrays.copyOf(stackFrame, size); + } + + @Override + public Object getTemp(int index) { + ensureStackFrameSize(index + 1); + return stackFrame[index]; + } + + @Override + public void setTemp(int index, Object value) { + ensureStackFrameSize(index + 1); + stackFrame[index] = value; + } + + @Override + public Object getScoped(int name) { + VariableValueHolder valueHolder = scopedMap.computeIfAbsent(name, n -> new VariableValueHolder()); + return valueHolder.value; + } + + @Override + public void setScoped(int name, Object value) { + VariableValueHolder valueHolder = scopedMap.computeIfAbsent(name, n -> new VariableValueHolder()); + valueHolder.value = value; + } + + @Override + public Object getPublic(int name) { + VariableValueHolder valueHolder = publicMap.get(name); + if (valueHolder != null) { + return valueHolder.value; + } else { + return null; + } + } + + // 注意 this.publicMap 线程安全 + public void initialize(@Nullable PooledStringHashSet publicVariableNames) { + Arrays.fill(stackFrame, null); + scopedMap.clear(); + + PooledStringHashMap newPublicMap = new PooledStringHashMap<>(); + if (publicVariableNames != null) { + for (int publicVariableName : publicVariableNames) { + VariableValueHolder value = new VariableValueHolder(); + scopedMap.put(publicVariableName, value); + newPublicMap.put(publicVariableName, value); + } + } + newPublicMap.trim(); + this.publicMap = newPublicMap; + } + + private static class VariableValueHolder { + public Object value = null; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/util/PooledStringHashMap.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/util/PooledStringHashMap.java new file mode 100644 index 000000000..11a740dcf --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/util/PooledStringHashMap.java @@ -0,0 +1,29 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.util; + +import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; + +public class PooledStringHashMap extends Int2ReferenceOpenHashMap { + public PooledStringHashMap() { + } + + public PooledStringHashMap(int initialCapacity) { + super(initialCapacity); + } + + public PooledStringHashMap(Int2ReferenceOpenHashMap m) { + super(m); + } + + public V get(String key) { + int intKey = StringPool.getName(key); + if (intKey == StringPool.NONE) { + return null; + } else { + return super.get(intKey); + } + } + + public void put(String key, V value) { + super.put(StringPool.computeIfAbsent(key), value); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/util/PooledStringHashSet.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/util/PooledStringHashSet.java new file mode 100644 index 000000000..a33752619 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/util/PooledStringHashSet.java @@ -0,0 +1,36 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.util; + +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; + +import java.util.Collection; +import java.util.stream.Collectors; + +public class PooledStringHashSet extends IntOpenHashSet { + public PooledStringHashSet() { + } + + public PooledStringHashSet(int initialCapacity) { + super(initialCapacity); + } + + public PooledStringHashSet(Collection m) { + super(m.stream().map(StringPool::computeIfAbsent).collect(Collectors.toSet())); + } + + public PooledStringHashSet(IntOpenHashSet m) { + super(m); + } + + public boolean contains(String key) { + int intKey = StringPool.getName(key); + if (intKey == StringPool.NONE) { + return false; + } else { + return super.contains(intKey); + } + } + + public boolean add(String key) { + return super.add(StringPool.computeIfAbsent(key)); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/util/StringPool.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/util/StringPool.java new file mode 100644 index 000000000..fd6d0a649 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/util/StringPool.java @@ -0,0 +1,28 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.util; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +public class StringPool { + public static final int NONE = Integer.MIN_VALUE; + public static final String EMPTY = ""; + private static final AtomicInteger COUNTER = new AtomicInteger(0); + private static final ConcurrentHashMap POOL = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap MAP = new ConcurrentHashMap<>(); + + public static int computeIfAbsent(String str) { + return POOL.computeIfAbsent(str, k -> { + int name = COUNTER.incrementAndGet(); + MAP.put(name, k); + return name; + }); + } + + public static int getName(String str) { + return POOL.getOrDefault(str, NONE); + } + + public static String getString(int name) { + return MAP.getOrDefault(name, EMPTY); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/value/DoubleValue.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/value/DoubleValue.java new file mode 100644 index 000000000..65fc07fb0 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/value/DoubleValue.java @@ -0,0 +1,33 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.value; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExpressionEvaluator; + +public class DoubleValue implements IValue { + public static final DoubleValue ONE = new DoubleValue(1); + public static final DoubleValue ZERO = new DoubleValue(0); + + private final double value; + + public DoubleValue(double value) { + this.value = value; + } + + @Override + public double evalAsDouble(ExpressionEvaluator evaluator) { + return value; + } + + @Override + public boolean evalAsBoolean(ExpressionEvaluator evaluator) { + return value != 0; + } + + @Override + public Object evalUnsafe(ExpressionEvaluator evaluator) { + return value; + } + + public double value() { + return value; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/value/IValue.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/value/IValue.java new file mode 100644 index 000000000..730706a2a --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/value/IValue.java @@ -0,0 +1,41 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.value; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExpressionEvaluator; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding.ValueConversions; + +public interface IValue { + /** + * 依次执行表达式,返回最后一个表达式的值,或第一个 return 语句的值,并转换为 double 类型。 + */ + default double evalAsDouble(ExpressionEvaluator evaluator) { + try { + Object result = evalUnsafe(evaluator); + double value = ValueConversions.asDouble(result); + if (!Double.isNaN(value)) { + return value; + } + } catch (Exception e) { + TouhouLittleMaid.LOGGER.error("Failed to evaluate molang value.", e); + } + return 0; + } + + /** + * 依次执行表达式,返回最后一个表达式的值,或第一个 return 语句的值,并转换为 boolean 类型。 + */ + default boolean evalAsBoolean(ExpressionEvaluator evaluator) { + try { + Object result = evalUnsafe(evaluator); + return ValueConversions.asBoolean(result); + } catch (Exception e) { + TouhouLittleMaid.LOGGER.error("Failed to evaluate molang value.", e); + } + return false; + } + + /** + * 依次执行表达式,返回最后一个表达式的值,或第一个 return 语句的值,返回值的类型不确定,并且可能抛出异常。 + */ + Object evalUnsafe(ExpressionEvaluator evaluator) throws Exception; +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/value/MolangValue.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/value/MolangValue.java new file mode 100644 index 000000000..513c0397d --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/value/MolangValue.java @@ -0,0 +1,30 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.value; + +import com.github.tartaricacid.touhoulittlemaid.molang.parser.ast.Expression; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExpressionEvaluator; + +import java.util.List; + +public class MolangValue implements IValue { + private final Expression[] expressions; + + public MolangValue(List expressions) { + this.expressions = expressions.toArray(new Expression[0]); + } + + @Override + public Object evalUnsafe(ExpressionEvaluator evaluator) throws Exception { + Object lastResult = 0d; + + for (Expression expression : expressions) { + lastResult = evaluator.eval(expression); + Object returnValue = evaluator.popReturnValue(); + if (returnValue != null) { + lastResult = returnValue; + break; + } + } + + return lastResult; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/value/RotationValue.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/value/RotationValue.java new file mode 100644 index 000000000..9d695eaf5 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/value/RotationValue.java @@ -0,0 +1,31 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.value; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExpressionEvaluator; + +public class RotationValue implements IValue { + private final IValue value; + private final boolean flip; + + public RotationValue(IValue value, boolean flip) { + this.value = value; + this.flip = flip; + } + + public static float processValue(double value, boolean flip) { + float ret = (float) Math.toRadians(value); + if(flip) { + ret = -ret; + } + return ret; + } + + @Override + public double evalAsDouble(ExpressionEvaluator evaluator) { + return processValue(this.value.evalAsDouble(evaluator), this.flip); + } + + @Override + public Object evalUnsafe(ExpressionEvaluator evaluator) { + return evalAsDouble(evaluator); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/ContextVariable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/ContextVariable.java new file mode 100644 index 000000000..813c70f66 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/ContextVariable.java @@ -0,0 +1,24 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExecutionContext; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Variable; + +public abstract class ContextVariable implements Variable { + protected boolean validateContext(IContext context) { + return true; + } + + @Override + @SuppressWarnings("unchecked") + public final Object evaluate(ExecutionContext context) { + Object entity = context.entity(); + if (entity instanceof IContext && validateContext((IContext) entity)) { + return evaluate((IContext) entity); + } else { + return null; + } + } + + public abstract Object evaluate(IContext context); +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/IValueEvaluator.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/IValueEvaluator.java new file mode 100644 index 000000000..83dc11289 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/IValueEvaluator.java @@ -0,0 +1,5 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable; + +public interface IValueEvaluator { + TValue eval(TContext ctx); +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/LambdaVariable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/LambdaVariable.java new file mode 100644 index 000000000..a80467341 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/LambdaVariable.java @@ -0,0 +1,16 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; + +public class LambdaVariable extends ContextVariable { + private final IValueEvaluator> evaluator; + + public LambdaVariable(IValueEvaluator> evaluator) { + this.evaluator = evaluator; + } + + @Override + public final Object evaluate(IContext entityContext) { + return evaluator.eval(entityContext); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/block/AbstractBlockVariable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/block/AbstractBlockVariable.java new file mode 100644 index 000000000..3ddb4d619 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/block/AbstractBlockVariable.java @@ -0,0 +1,17 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.block; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.IValueEvaluator; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.LambdaVariable; +import net.minecraft.world.level.block.state.BlockBehaviour; + +public class AbstractBlockVariable extends LambdaVariable { + public AbstractBlockVariable(IValueEvaluator> evaluator) { + super(evaluator); + } + + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof BlockBehaviour; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/block/BlockStateVariable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/block/BlockStateVariable.java new file mode 100644 index 000000000..343d0f868 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/block/BlockStateVariable.java @@ -0,0 +1,17 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.block; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.IValueEvaluator; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.LambdaVariable; +import net.minecraft.world.level.block.state.BlockState; + +public class BlockStateVariable extends LambdaVariable { + public BlockStateVariable(IValueEvaluator> evaluator) { + super(evaluator); + } + + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof BlockState; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/block/BlockVariable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/block/BlockVariable.java new file mode 100644 index 000000000..b1c22b141 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/block/BlockVariable.java @@ -0,0 +1,17 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.block; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.IValueEvaluator; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.LambdaVariable; +import net.minecraft.world.level.block.Block; + +public class BlockVariable extends LambdaVariable { + public BlockVariable(IValueEvaluator> evaluator) { + super(evaluator); + } + + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof Block; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/EntityVariable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/EntityVariable.java new file mode 100644 index 000000000..d9f882ccd --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/EntityVariable.java @@ -0,0 +1,17 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.entity; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.IValueEvaluator; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.LambdaVariable; +import net.minecraft.world.entity.Entity; + +public class EntityVariable extends LambdaVariable { + public EntityVariable(IValueEvaluator> evaluator) { + super(evaluator); + } + + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof Entity; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/LivingEntityVariable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/LivingEntityVariable.java new file mode 100644 index 000000000..444b5f8a9 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/LivingEntityVariable.java @@ -0,0 +1,17 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.entity; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.IValueEvaluator; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.LambdaVariable; +import net.minecraft.world.entity.LivingEntity; + +public class LivingEntityVariable extends LambdaVariable { + public LivingEntityVariable(IValueEvaluator> evaluator) { + super(evaluator); + } + + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof LivingEntity; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/MaidEntityVariable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/MaidEntityVariable.java new file mode 100644 index 000000000..2ba5b8333 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/MaidEntityVariable.java @@ -0,0 +1,17 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.entity; + +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.IValueEvaluator; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.LambdaVariable; + +public class MaidEntityVariable extends LambdaVariable { + public MaidEntityVariable(IValueEvaluator> evaluator) { + super(evaluator); + } + + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof EntityMaid; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/MobEntityVariable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/MobEntityVariable.java new file mode 100644 index 000000000..48473b889 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/MobEntityVariable.java @@ -0,0 +1,17 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.entity; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.IValueEvaluator; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.LambdaVariable; +import net.minecraft.world.entity.Mob; + +public class MobEntityVariable extends LambdaVariable { + public MobEntityVariable(IValueEvaluator> evaluator) { + super(evaluator); + } + + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof Mob; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/TamableEntityVariable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/TamableEntityVariable.java new file mode 100644 index 000000000..0082e7a54 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/entity/TamableEntityVariable.java @@ -0,0 +1,17 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.entity; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.IValueEvaluator; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.LambdaVariable; +import net.minecraft.world.entity.TamableAnimal; + +public class TamableEntityVariable extends LambdaVariable { + public TamableEntityVariable(IValueEvaluator> evaluator) { + super(evaluator); + } + + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof TamableAnimal; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/item/ItemStackVariable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/item/ItemStackVariable.java new file mode 100644 index 000000000..6d7c68d26 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/item/ItemStackVariable.java @@ -0,0 +1,17 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.item; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.IValueEvaluator; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.LambdaVariable; +import net.minecraft.world.item.ItemStack; + +public class ItemStackVariable extends LambdaVariable { + public ItemStackVariable(IValueEvaluator> evaluator) { + super(evaluator); + } + + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof ItemStack; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/item/ItemVariable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/item/ItemVariable.java new file mode 100644 index 000000000..ded44305a --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/molang/variable/item/ItemVariable.java @@ -0,0 +1,17 @@ +package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.item; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.IValueEvaluator; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.variable.LambdaVariable; +import net.minecraft.world.item.Item; + +public class ItemVariable extends LambdaVariable { + public ItemVariable(IValueEvaluator> evaluator) { + super(evaluator); + } + + @Override + protected boolean validateContext(IContext context) { + return context.entity() instanceof Item; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/processor/AnimationProcessor.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/processor/AnimationProcessor.java index 12a168977..6dac40274 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/processor/AnimationProcessor.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/processor/AnimationProcessor.java @@ -3,34 +3,65 @@ import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.AnimatableEntity; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.controller.AnimationController; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.event.predicate.AnimationEvent; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.AnimationPoint; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.BoneAnimationQueue; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.manager.AnimationData; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.MolangParser; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.AnimationContext; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.storage.IForeignVariableStorage; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.storage.VariableStorage; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.util.StringPool; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.value.IValue; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.snapshot.BoneSnapshot; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.snapshot.BoneTopLevelSnapshot; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.util.MathUtil; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; - -import java.util.*; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.util.RateLimiter; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.ExpressionEvaluator; +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import net.minecraft.client.Minecraft; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; + +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.function.Consumer; @SuppressWarnings({"rawtypes", "unchecked"}) -public class AnimationProcessor { - private final Map boneMap = new Object2ObjectOpenHashMap(); +public class AnimationProcessor> { + private static final int ROAMING_STRUCT_NAME = StringPool.computeIfAbsent("roaming"); + + private final ReferenceArrayList modelRendererList = new ReferenceArrayList<>(); + private final Object2ReferenceOpenHashMap modelRendererMap = new Object2ReferenceOpenHashMap<>(); + private final VariableStorage animationStorage = new VariableStorage(); + private final Random random = new Random(); + private final ConcurrentLinkedQueue>> pendingValues = new ConcurrentLinkedQueue<>(); + private final RateLimiter rateLimiter = new RateLimiter(Minecraft.getInstance().getWindow().getRefreshRate()); private final T animatable; - private boolean reloadAnimations = false; - private boolean modelDirty = false; + private List initializationValues; + private List preAnimationValues; + + private boolean rendererDirty = false; + public boolean reloadAnimations = false; public AnimationProcessor(T animatable) { this.animatable = animatable; } - public void tickAnimation(double seekTime, AnimationEvent event, MolangParser parser) { - // 每一个动画都有自己的动画数据(AnimationData) - // 这样多个动画就能互相独立了 + @SuppressWarnings("DataFlowIssue") + public boolean tickAnimation(double seekTime, AnimationEvent event, AnimationContext ctx) { + var shouldUpdate = rateLimiter.request((float) (seekTime / 20)); + + ctx.setStorage(this.animationStorage); + ctx.setRandom(this.random); + ExpressionEvaluator> evaluator = ExpressionEvaluator.evaluator(ctx); + preProcess(evaluator); + + // InstancedAnimationFactory 仅保有一个 AnimationData 实例,与传入的 uniqueID 无关 AnimationData manager = this.animatable.getAnimationData(); - for (AnimationController controller : manager.getAnimationControllers().values()) { + for (AnimationController controller : manager.getAnimationControllers()) { if (reloadAnimations) { controller.markNeedsReload(); controller.getBoneAnimationQueues().clear(); @@ -39,7 +70,7 @@ public void tickAnimation(double seekTime, AnimationEvent event, MolangParser pa // 将当前控制器设置为动画测试事件 event.setController(controller); // 处理动画并向点队列添加新值 - controller.process(seekTime, event, this.boneMap, parser, this.modelDirty); + controller.process(seekTime, event, evaluator, modelRendererList, false, rendererDirty, shouldUpdate); boolean isParallelController = controller.getName().startsWith("parallel_"); // 遍历每个骨骼,并对属性进行插值计算 for (BoneAnimationQueue boneAnimation : controller.getBoneAnimationQueues()) { @@ -47,60 +78,50 @@ public void tickAnimation(double seekTime, AnimationEvent event, MolangParser pa BoneSnapshot initialSnapshot = snapshot.bone.getInitialSnapshot(); PointData pointData = snapshot.cachedPointData; - AnimationPoint rXPoint = boneAnimation.rotationXQueue().poll(); - AnimationPoint rYPoint = boneAnimation.rotationYQueue().poll(); - AnimationPoint rZPoint = boneAnimation.rotationZQueue().poll(); // 如果此骨骼有任何旋转值 - if (rXPoint != null && rYPoint != null && rZPoint != null) { - float valueX = MathUtil.lerpValues(rXPoint, controller.easingType, controller.customEasingMethod); - float valueY = MathUtil.lerpValues(rYPoint, controller.easingType, controller.customEasingMethod); - float valueZ = MathUtil.lerpValues(rZPoint, controller.easingType, controller.customEasingMethod); - pointData.rotationValueX += valueX; - pointData.rotationValueY += valueY; - pointData.rotationValueZ += valueZ; + if (!boneAnimation.rotationQueue().isEmpty()) { + Vector3f scale = boneAnimation.rotationQueue().poll().getLerpPoint(evaluator); + pointData.rotationValueX += scale.x(); + pointData.rotationValueY += scale.y(); + pointData.rotationValueZ += scale.z(); if (isParallelController) { snapshot.rotationValueX = pointData.rotationValueX + initialSnapshot.rotationValueX; snapshot.rotationValueY = pointData.rotationValueY + initialSnapshot.rotationValueY; snapshot.rotationValueZ = pointData.rotationValueZ + initialSnapshot.rotationValueZ; } else { - snapshot.rotationValueX = valueX + initialSnapshot.rotationValueX; - snapshot.rotationValueY = valueY + initialSnapshot.rotationValueY; - snapshot.rotationValueZ = valueZ + initialSnapshot.rotationValueZ; + snapshot.rotationValueX = scale.x() + initialSnapshot.rotationValueX; + snapshot.rotationValueY = scale.y() + initialSnapshot.rotationValueY; + snapshot.rotationValueZ = scale.z() + initialSnapshot.rotationValueZ; } snapshot.isCurrentlyRunningRotationAnimation = true; } - AnimationPoint pXPoint = boneAnimation.positionXQueue().poll(); - AnimationPoint pYPoint = boneAnimation.positionYQueue().poll(); - AnimationPoint pZPoint = boneAnimation.positionZQueue().poll(); // 如果此骨骼有任何位置值 - if (pXPoint != null && pYPoint != null && pZPoint != null) { - snapshot.positionOffsetX = MathUtil.lerpValues(pXPoint, controller.easingType, controller.customEasingMethod); - snapshot.positionOffsetY = MathUtil.lerpValues(pYPoint, controller.easingType, controller.customEasingMethod); - snapshot.positionOffsetZ = MathUtil.lerpValues(pZPoint, controller.easingType, controller.customEasingMethod); + if (!boneAnimation.positionQueue().isEmpty()) { + Vector3f position = boneAnimation.positionQueue().poll().getLerpPoint(evaluator); + snapshot.positionOffsetX = position.x(); + snapshot.positionOffsetY = position.y(); + snapshot.positionOffsetZ = position.z(); snapshot.isCurrentlyRunningPositionAnimation = true; } - - AnimationPoint sXPoint = boneAnimation.scaleXQueue().poll(); - AnimationPoint sYPoint = boneAnimation.scaleYQueue().poll(); - AnimationPoint sZPoint = boneAnimation.scaleZQueue().poll(); // 如果此骨骼有任何缩放点 - if (sXPoint != null && sYPoint != null && sZPoint != null) { - snapshot.scaleValueX = MathUtil.lerpValues(sXPoint, controller.easingType, controller.customEasingMethod); - snapshot.scaleValueY = MathUtil.lerpValues(sYPoint, controller.easingType, controller.customEasingMethod); - snapshot.scaleValueZ = MathUtil.lerpValues(sZPoint, controller.easingType, controller.customEasingMethod); + if (!boneAnimation.scaleQueue().isEmpty()) { + Vector3f scale = boneAnimation.scaleQueue().poll().getLerpPoint(evaluator); + snapshot.scaleValueX = scale.x(); + snapshot.scaleValueY = scale.y(); + snapshot.scaleValueZ = scale.z(); snapshot.isCurrentlyRunningScaleAnimation = true; } } } - this.modelDirty = false; + this.rendererDirty = false; this.reloadAnimations = false; // 追踪哪些骨骼应用了动画,并最终将没有动画的骨骼设置为默认值 final double resetTickLength = manager.getResetSpeed(); - for (BoneTopLevelSnapshot topLevelSnapshot : this.boneMap.values()) { + for (BoneTopLevelSnapshot topLevelSnapshot : modelRendererList) { BoneSnapshot initialSnapshot = topLevelSnapshot.bone.getInitialSnapshot(); if (!topLevelSnapshot.isCurrentlyRunningRotationAnimation) { @@ -149,21 +170,78 @@ public void tickAnimation(double seekTime, AnimationEvent event, MolangParser pa topLevelSnapshot.commit(); } manager.isFirstTick = false; + + postProcess(evaluator); + return true; + } + + @Nullable + public IBone getBone(String boneName) { + BoneTopLevelSnapshot renderer = modelRendererMap.get(boneName); + return renderer != null ? renderer.bone : null; + } + + public void registerModelRenderer(Map boneMap) { + this.modelRendererMap.clear(); + this.modelRendererList.clear(); + this.modelRendererList.ensureCapacity(boneMap.size()); + for (Map.Entry entry : boneMap.entrySet()) { + BoneTopLevelSnapshot renderer = new BoneTopLevelSnapshot(entry.getValue()); + this.modelRendererMap.put(entry.getKey(), renderer); + this.modelRendererList.add(renderer); + } + this.animationStorage.initialize(null); + this.rendererDirty = true; + } + + public boolean isModelRendererEmpty() { + return modelRendererList.isEmpty(); + } + + public void preAnimationSetup(AnimatableEntity animatable, double seekTime) { + } + + private void preProcess(ExpressionEvaluator> evaluator) { + if (rendererDirty && initializationValues != null) { + for (IValue value : initializationValues) { + value.evalAsDouble(evaluator); + } + initializationValues = null; + } + if (preAnimationValues != null) { + for (IValue value : preAnimationValues) { + value.evalAsDouble(evaluator); + } + } } - public void updateModel(Collection bones) { - this.boneMap.clear(); - for (var bone : bones) { - this.boneMap.put(bone.getName(), new BoneTopLevelSnapshot(bone)); + private void postProcess(ExpressionEvaluator> evaluator) { + while (!pendingValues.isEmpty()) { + Pair> pair = pendingValues.poll(); + String result; + try { + var ret = pair.getFirst().evalUnsafe(evaluator); + if (ret == null) { + result = "null"; + } else if (ret instanceof String) { + result = "'" + ret + "'"; + } else { + result = ret.toString(); + } + } catch (Exception e) { + result = "Error: " + e.getMessage(); + } + if (pair.getSecond() != null) { + pair.getSecond().accept(result); + } } - modelDirty = true; } - public boolean isModelEmpty() { - return this.boneMap.isEmpty(); + public void execute(IValue value, @Nullable Consumer resultConsumer) { + pendingValues.add(Pair.of(value, resultConsumer)); } - public void preAnimationSetup(double seekTime) { - this.animatable.setMolangQueries(seekTime); + public IForeignVariableStorage getPublicVariableStorage() { + return this.animationStorage; } -} +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/util/MathUtil.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/util/MathUtil.java index 172a504ec..90272091a 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/util/MathUtil.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/core/util/MathUtil.java @@ -1,42 +1,30 @@ package com.github.tartaricacid.touhoulittlemaid.geckolib3.core.util; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.easing.EasingManager; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.easing.EasingType; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.AnimationPoint; - -import java.util.function.Function; +import org.joml.Vector3f; @SuppressWarnings("all") public class MathUtil { - /** - * 对一个 AnimationPoint 进行线性插值计算 - * - * @param animationPoint 动画信息 - * @return 线性插值 - */ - public static float lerpValues(AnimationPoint animationPoint, EasingType easingType, Function customEasingMethod) { - if (animationPoint.currentTick >= animationPoint.animationEndTick) { - return (float) animationPoint.animationEndValue; - } - if (animationPoint.currentTick == 0 && animationPoint.animationEndTick == 0) { - return (float) animationPoint.animationEndValue; - } - if (easingType == EasingType.CUSTOM && customEasingMethod != null) { - return lerpValues(customEasingMethod.apply(animationPoint.currentTick / animationPoint.animationEndTick), - animationPoint.animationStartValue, animationPoint.animationEndValue); - } else if (easingType == EasingType.NONE && animationPoint.keyframe != null) { - easingType = animationPoint.keyframe.easingType; - } - double ease = EasingManager.ease(animationPoint.currentTick / animationPoint.animationEndTick, easingType, - animationPoint.keyframe == null ? null : animationPoint.keyframe.easingArgs); - return lerpValues(ease, animationPoint.animationStartValue, animationPoint.animationEndValue); + public static Vector3f lerpValues(double percentCompleted, Vector3f begin, Vector3f end) { + return new Vector3f(lerpValues(percentCompleted, begin.x(), end.x()), + lerpValues(percentCompleted, begin.y(), end.y()), + lerpValues(percentCompleted, begin.z(), end.z())); } public static float lerpValues(double percentCompleted, double startValue, double endValue) { - return (float) lerp(percentCompleted, startValue, endValue); + return (float) (startValue + percentCompleted * (endValue - startValue)); + } + + public static Vector3f catmullRom(double percentCompleted, Vector3f left, Vector3f begin, Vector3f end, Vector3f right) { + return new Vector3f(catmullRom(percentCompleted, left.x(), begin.x(), end.x(), right.x()), + catmullRom(percentCompleted, left.y(), begin.y(), end.y(), right.y()), + catmullRom(percentCompleted, left.z(), begin.z(), end.z(), right.z())); } - public static double lerp(double pct, double start, double end) { - return start + pct * (end - start); + public static float catmullRom(double percent, double left, double begin, double end, double right) { + double v0 = (end - left) * 0.5; + double v1 = (right - begin) * 0.5; + double t2 = percent * percent; + double t3 = percent * t2; + return (float) ((2 * begin - 2 * end + v0 + v1) * t3 + (-3 * begin + 3 * end - 2 * v0 - v1) * t2 + v0 * percent + begin); } } \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/geo/GeoReplacedEntityRenderer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/geo/GeoReplacedEntityRenderer.java index bdd327943..d32232b10 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/geo/GeoReplacedEntityRenderer.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/geo/GeoReplacedEntityRenderer.java @@ -3,6 +3,7 @@ import com.github.tartaricacid.touhoulittlemaid.extended.LivingEntityRendererAccessor; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.AnimatableEntity; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.event.predicate.AnimationEvent; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.AnimationContext; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.util.Color; import com.github.tartaricacid.touhoulittlemaid.geckolib3.geo.animated.AnimatedGeoModel; import com.github.tartaricacid.touhoulittlemaid.geckolib3.model.provider.data.EntityModelData; @@ -189,8 +190,8 @@ public void render(T entity, E animatable, float entityYaw, float partialTick, P entityModelData.netHeadYaw = -Mth.clamp(Mth.wrapDegrees(netHeadYaw), -85, 85); AnimationEvent predicate = new AnimationEvent(animatable, limbSwing, limbSwingAmount, partialTick, (limbSwingAmount <= -getSwingMotionAnimThreshold() || limbSwingAmount <= getSwingMotionAnimThreshold()), Collections.singletonList(entityModelData)); - - animatable.setCustomAnimations(predicate); + AnimationContext ctx = new AnimationContext<>(entity, this.currentAnimatable, predicate, entityModelData); + animatable.setCustomAnimations(ctx, predicate); AnimatedGeoModel model = animatable.getCurrentModel(); if (model != null) { poseStack.translate(0, 0.01f, 0); @@ -257,7 +258,7 @@ public boolean shouldShowName(T entity) { return false; } return entity.shouldShowName() || (entity == this.entityRenderDispatcher.crosshairPickEntity && entity.hasCustomName()) - && Minecraft.renderNames(); + && Minecraft.renderNames(); } protected float getSwingProgress(LivingEntity entity, float partialTick) { diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/geo/exception/GeckoLibException.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/geo/exception/GeckoLibException.java deleted file mode 100644 index 5c4e963de..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/geo/exception/GeckoLibException.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.geckolib3.geo.exception; - -import net.minecraft.resources.ResourceLocation; - -import java.io.Serial; - -public class GeckoLibException extends RuntimeException { - @Serial - private static final long serialVersionUID = 1L; - - public GeckoLibException(ResourceLocation fileLocation, String message) { - super(fileLocation + ": " + message); - } - - public GeckoLibException(ResourceLocation fileLocation, String message, Throwable cause) { - super(fileLocation + ": " + message, cause); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/resource/GeckoLibCache.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/resource/GeckoLibCache.java index 7bf1abac7..bbfbb356e 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/resource/GeckoLibCache.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/resource/GeckoLibCache.java @@ -1,16 +1,21 @@ package com.github.tartaricacid.touhoulittlemaid.geckolib3.resource; +import com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.TLMBinding; +import com.github.tartaricacid.touhoulittlemaid.client.animation.gecko.molang.YSMBinding; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.MolangParser; import com.github.tartaricacid.touhoulittlemaid.geckolib3.file.AnimationFile; import com.github.tartaricacid.touhoulittlemaid.geckolib3.geo.render.built.GeoModel; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding.ObjectBinding; import com.google.common.collect.Maps; import net.minecraft.resources.ResourceLocation; +import java.util.HashMap; import java.util.Map; public class GeckoLibCache { + private static final Map EXTRA_BINDING = new HashMap<>(); private static GeckoLibCache INSTANCE; - public final MolangParser parser = new MolangParser(); + public final MolangParser parser = createMolangParser(); private final Map animations = Maps.newHashMap(); private final Map geoModels = Maps.newHashMap(); @@ -29,4 +34,10 @@ public Map getAnimations() { public Map getGeoModels() { return geoModels; } + + private static MolangParser createMolangParser() { + EXTRA_BINDING.put("ysm", YSMBinding.INSTANCE); + EXTRA_BINDING.put("tlm", TLMBinding.INSTANCE); + return new MolangParser(EXTRA_BINDING); + } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/util/MolangUtils.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/util/MolangUtils.java index 7596df994..d17cdeb1f 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/util/MolangUtils.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/util/MolangUtils.java @@ -1,14 +1,37 @@ package com.github.tartaricacid.touhoulittlemaid.geckolib3.util; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.context.IContext; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EquipmentSlot; + +import java.util.HashMap; +import java.util.Locale; + public class MolangUtils { - public static final float TRUE = 1; - public static final float FALSE = 0; + private static final HashMap SLOT_MAP; + + static { + SLOT_MAP = new HashMap<>(); + SLOT_MAP.put("chest", EquipmentSlot.CHEST); + SLOT_MAP.put("feet", EquipmentSlot.FEET); + SLOT_MAP.put("head", EquipmentSlot.HEAD); + SLOT_MAP.put("legs", EquipmentSlot.LEGS); + SLOT_MAP.put("mainhand", EquipmentSlot.MAINHAND); + SLOT_MAP.put("offhand", EquipmentSlot.OFFHAND); + } public static float normalizeTime(long timestamp) { return ((float) (timestamp + 6000L) / 24000) % 1; } - public static float booleanToFloat(boolean input) { - return input ? TRUE : FALSE; + public static ResourceLocation parseResourceLocation(IContext context, String value) { + return ResourceLocation.tryParse(value); + } + + public static EquipmentSlot parseSlotType(IContext context, String value) { + if (value == null) { + return null; + } + return SLOT_MAP.get(value.toLowerCase(Locale.ENGLISH)); } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/util/json/JsonAnimationUtils.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/util/json/JsonAnimationUtils.java index 7059e250f..61e4ef41f 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/util/json/JsonAnimationUtils.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/util/json/JsonAnimationUtils.java @@ -7,23 +7,26 @@ import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.builder.Animation; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.builder.ILoopType; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.*; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.BoneAnimation; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone.BoneKeyFrame; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone.BoneKeyFrameProcessor; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone.RawBoneKeyFrame; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.event.EventKeyFrame; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.MolangParser; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.value.IValue; import com.github.tartaricacid.touhoulittlemaid.geckolib3.util.AnimationUtils; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; -import com.google.gson.*; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectLists; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import net.minecraft.server.ChainedJsonException; +import org.apache.commons.compress.utils.Lists; import java.util.*; @SuppressWarnings({"unchecked", "rawtypes"}) public class JsonAnimationUtils { - private static Gson GSON = null; - public static Set> getAnimations(JsonObject json) { if (json.has("animations")) { return json.getAsJsonObject("animations").entrySet(); @@ -36,97 +39,6 @@ public static List> getBones(JsonObject json) { return bones == null ? List.of() : new ArrayList<>(bones.entrySet()); } - public static Set> getRotationKeyFrames(JsonObject json) { - JsonElement rotationObject = json.get("rotation"); - if (rotationObject.isJsonArray()) { - return ImmutableSet.of(new AbstractMap.SimpleEntry("0", rotationObject.getAsJsonArray())); - } - if (rotationObject.isJsonPrimitive()) { - JsonPrimitive primitive = rotationObject.getAsJsonPrimitive(); - JsonElement jsonElement = getGson().toJsonTree(Arrays.asList(primitive, primitive, primitive)); - return ImmutableSet.of(new AbstractMap.SimpleEntry("0", jsonElement)); - } - Set> output = Sets.newLinkedHashSet(); - if (rotationObject.isJsonObject()) { - JsonObject jsonObject = rotationObject.getAsJsonObject(); - for (Map.Entry entrySet : jsonObject.entrySet()) { - if (entrySet.getValue().isJsonObject()) { - JsonObject valueObject = entrySet.getValue().getAsJsonObject(); - if (valueObject.has("post")) { - output.add(Map.entry(entrySet.getKey(), valueObject.get("post"))); - } - } else { - output.add(Map.entry(entrySet.getKey(), entrySet.getValue())); - } - } - } - return output; - } - - public static Set> getPositionKeyFrames(JsonObject json) { - JsonElement positionObject = json.get("position"); - if (positionObject.isJsonArray()) { - return ImmutableSet.of(new AbstractMap.SimpleEntry("0", positionObject.getAsJsonArray())); - } - if (positionObject.isJsonPrimitive()) { - JsonPrimitive primitive = positionObject.getAsJsonPrimitive(); - JsonElement jsonElement = getGson().toJsonTree(Arrays.asList(primitive, primitive, primitive)); - return ImmutableSet.of(new AbstractMap.SimpleEntry("0", jsonElement)); - } - Set> output = Sets.newLinkedHashSet(); - if (positionObject.isJsonObject()) { - JsonObject jsonObject = positionObject.getAsJsonObject(); - for (Map.Entry entrySet : jsonObject.entrySet()) { - if (entrySet.getValue().isJsonObject()) { - JsonObject valueObject = entrySet.getValue().getAsJsonObject(); - if (valueObject.has("post")) { - output.add(Map.entry(entrySet.getKey(), valueObject.get("post"))); - } - } else { - output.add(Map.entry(entrySet.getKey(), entrySet.getValue())); - } - } - } - return output; - } - - public static Set> getScaleKeyFrames(JsonObject json) { - JsonElement scaleObject = json.get("scale"); - if (scaleObject.isJsonArray()) { - return ImmutableSet.of(new AbstractMap.SimpleEntry("0", scaleObject.getAsJsonArray())); - } - if (scaleObject.isJsonPrimitive()) { - JsonPrimitive primitive = scaleObject.getAsJsonPrimitive(); - JsonElement jsonElement = getGson().toJsonTree(Arrays.asList(primitive, primitive, primitive)); - return ImmutableSet.of(new AbstractMap.SimpleEntry("0", jsonElement)); - } - Set> output = Sets.newLinkedHashSet(); - if (scaleObject.isJsonObject()) { - JsonObject jsonObject = scaleObject.getAsJsonObject(); - for (Map.Entry entrySet : jsonObject.entrySet()) { - if (entrySet.getValue().isJsonObject()) { - JsonObject valueObject = entrySet.getValue().getAsJsonObject(); - if (valueObject.has("post")) { - output.add(Map.entry(entrySet.getKey(), valueObject.get("post"))); - } - } else { - output.add(Map.entry(entrySet.getKey(), entrySet.getValue())); - } - } - } - return output; - } - - public static List> getSoundEffectFrames(JsonObject json) { - JsonObject soundEffects = json.getAsJsonObject("sound_effects"); - return soundEffects == null ? List.of() : new ObjectArrayList<>(soundEffects.entrySet()); - } - - public static List> getParticleEffectFrames(JsonObject json) { - JsonObject particleEffects = json.getAsJsonObject("particle_effects"); - return particleEffects == null ? List.of() : new ObjectArrayList<>(particleEffects.entrySet()); - } - public static List> getCustomInstructionKeyFrames(JsonObject json) { JsonObject customInstructions = json.getAsJsonObject("timeline"); return customInstructions == null ? List.of() : new ArrayList<>(customInstructions.entrySet()); @@ -147,10 +59,6 @@ public static Map.Entry getAnimation(JsonObject animationFi return new AbstractMap.SimpleEntry(animationName, getObjectByKey(getAnimations(animationFile), animationName)); } - public static Set> getObjectListAsArray(JsonObject json) { - return json.entrySet(); - } - public static Animation deserializeJsonToAnimation(Map.Entry element, MolangParser parser) throws ClassCastException, IllegalStateException { JsonObject animationJsonObject = element.getValue().getAsJsonObject(); @@ -162,95 +70,70 @@ public static Animation deserializeJsonToAnimation(Map.Entry(); - var soundKeyFrames = new ObjectArrayList>(); - var particleKeyFrames = new ObjectArrayList(); - var customInstructionKeyframes = new ObjectArrayList>(); - - // 处理声音关键帧 - for (Map.Entry keyFrame : getSoundEffectFrames(animationJsonObject)) { - soundKeyFrames.add(new EventKeyFrame<>(Double.parseDouble(keyFrame.getKey()) * 20, - keyFrame.getValue().getAsJsonObject().get("effect").getAsString())); - } - - // 处理粒子关键帧 - for (Map.Entry keyFrame : getParticleEffectFrames(animationJsonObject)) { - JsonObject object = keyFrame.getValue().getAsJsonObject(); - JsonElement effect = object.get("effect"); - JsonElement locator = object.get("locator"); - JsonElement preEffectScript = object.get("pre_effect_script"); - particleKeyFrames.add(new ParticleEventKeyFrame(Double.parseDouble(keyFrame.getKey()) * 20, - effect == null ? "" : effect.getAsString(), locator == null ? "" : locator.getAsString(), - preEffectScript == null ? "" : preEffectScript.getAsString())); - } + var boneAnimations = new ReferenceArrayList(); + var customInstructionKeyframes = new ReferenceArrayList>(); // 处理自定义指令关键帧 for (Map.Entry keyFrame : getCustomInstructionKeyFrames(animationJsonObject)) { - customInstructionKeyframes.add(new EventKeyFrame(Double.parseDouble(keyFrame.getKey()) * 20, - keyFrame.getValue() instanceof JsonArray - ? convertJsonArrayToList(keyFrame.getValue().getAsJsonArray()).toString() - : keyFrame.getValue().getAsString())); + double startTick = Double.parseDouble(keyFrame.getKey()) * 20; + JsonElement value = keyFrame.getValue(); + if (value.isJsonArray()) { + JsonArray array = value.getAsJsonArray(); + IValue[] values = new IValue[array.size()]; + for (int i = 0; i < array.size(); i++) { + String parserText = array.get(i).getAsString(); + values[i] = parser.parseExpression(parserText); + } + customInstructionKeyframes.add(new EventKeyFrame<>(startTick, values)); + } else if (value.isJsonPrimitive() && value.getAsJsonPrimitive().isString()) { + IValue[] values = new IValue[]{parser.parseExpression(value.getAsString())}; + customInstructionKeyframes.add(new EventKeyFrame<>(startTick, values)); + } } + // 排序,因为 json 读取是乱序的 + customInstructionKeyframes.sort(Comparator.comparingDouble(EventKeyFrame::getStartTick)); + // 此动画中使用的所有骨骼的列表 for (Map.Entry bone : getBones(animationJsonObject)) { - VectorKeyFrameList> rotationKeyFrames; - VectorKeyFrameList> positionKeyFrames; - VectorKeyFrameList> scaleKeyFrames; + + List rotationKeyFrames = Lists.newArrayList(); + List positionKeyFrames = Lists.newArrayList(); + List scaleKeyFrames = Lists.newArrayList(); JsonObject boneJsonObj = bone.getValue().getAsJsonObject(); - try { - scaleKeyFrames = JsonKeyFrameUtils.convertJsonToKeyFrames(new ObjectArrayList<>(getScaleKeyFrames(boneJsonObj)), parser); - } catch (Exception e) { - // 没有缩放关键帧 - scaleKeyFrames = new VectorKeyFrameList<>(); - } - try { - positionKeyFrames = JsonKeyFrameUtils.convertJsonToKeyFrames(new ObjectArrayList<>(getPositionKeyFrames(boneJsonObj)), parser); - } catch (Exception e) { - // 没有位置关键帧 - positionKeyFrames = new VectorKeyFrameList<>(); - } + JsonKeyFrameUtils.getKeyFrames(scaleKeyFrames, boneJsonObj.get("scale"), parser); + JsonKeyFrameUtils.getKeyFrames(positionKeyFrames, boneJsonObj.get("position"), parser); + JsonKeyFrameUtils.getKeyFrames(rotationKeyFrames, boneJsonObj.get("rotation"), parser); - try { - rotationKeyFrames = JsonKeyFrameUtils.convertJsonToRotationKeyFrames(new ObjectArrayList<>(getRotationKeyFrames(boneJsonObj)), parser); - } catch (Exception e) { - // 没有旋转关键帧 - rotationKeyFrames = new VectorKeyFrameList<>(); - } - boneAnimations.add(new BoneAnimation(bone.getKey(), rotationKeyFrames, positionKeyFrames, scaleKeyFrames)); + boneAnimations.add(new BoneAnimation(bone.getKey(), + BoneKeyFrameProcessor.process(rotationKeyFrames, true), + BoneKeyFrameProcessor.process(positionKeyFrames, false), + BoneKeyFrameProcessor.process(scaleKeyFrames, false))); } if (animationLengthTicks == -1) { animationLengthTicks = calculateLength(boneAnimations); } - return new Animation(animationName, animationLengthTicks, loop, - ObjectLists.unmodifiable(boneAnimations), - ObjectLists.unmodifiable(soundKeyFrames), - ObjectLists.unmodifiable(particleKeyFrames), - ObjectLists.unmodifiable(customInstructionKeyframes)); + return new Animation(animationName, animationLengthTicks, loop, boneAnimations, customInstructionKeyframes); } private static double calculateLength(List boneAnimations) { double longestLength = 0; for (BoneAnimation animation : boneAnimations) { - double xKeyframeTime = animation.rotationKeyFrames.getLastKeyframeTime(); - double yKeyframeTime = animation.positionKeyFrames.getLastKeyframeTime(); - double zKeyframeTime = animation.scaleKeyFrames.getLastKeyframeTime(); + double xKeyframeTime = calculateKeyFrameListLength(animation.rotationKeyFrames); + double yKeyframeTime = calculateKeyFrameListLength(animation.positionKeyFrames); + double zKeyframeTime = calculateKeyFrameListLength(animation.scaleKeyFrames); longestLength = maxAll(longestLength, xKeyframeTime, yKeyframeTime, zKeyframeTime); } return longestLength == 0 ? Double.MAX_VALUE : longestLength; } - static List convertJsonArrayToList(JsonArray array) { - return getGson().fromJson(array, ArrayList.class); - } - - private static Gson getGson() { - if (GSON == null) { - GSON = new Gson(); + private static double calculateKeyFrameListLength(List boneKeyFrames) { + if (boneKeyFrames.isEmpty()) { + return 0; } - return GSON; + return boneKeyFrames.get(boneKeyFrames.size() - 1).getStartTick(); } public static double maxAll(double... values) { diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/util/json/JsonKeyFrameUtils.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/util/json/JsonKeyFrameUtils.java index b4879599f..2f14b0aae 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/util/json/JsonKeyFrameUtils.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/geckolib3/util/json/JsonKeyFrameUtils.java @@ -5,145 +5,225 @@ package com.github.tartaricacid.touhoulittlemaid.geckolib3.util.json; -import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.ConstantValue; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.easing.EasingType; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.KeyFrame; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.VectorKeyFrameList; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.MolangException; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone.EasingType; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.keyframe.bone.RawBoneKeyFrame; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.MolangParser; -import com.github.tartaricacid.touhoulittlemaid.geckolib3.util.AnimationUtils; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.value.IValue; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectLists; -import org.apache.commons.lang3.math.NumberUtils; +import com.google.gson.JsonPrimitive; +import javax.annotation.Nullable; +import java.util.Comparator; import java.util.List; -import java.util.Map; +import java.util.Locale; + +import static com.github.tartaricacid.touhoulittlemaid.geckolib3.util.AnimationUtils.convertSecondsToTicks; /** * 用于将 json 转换成关键帧的工具类 */ -@SuppressWarnings({"unchecked", "rawtypes", "ReassignedVariable"}) public class JsonKeyFrameUtils { - private static VectorKeyFrameList> convertJson(List> element, boolean isRotation, - MolangParser parser) throws NumberFormatException, MolangException { - IValue previousXValue = null; - IValue previousYValue = null; - IValue previousZValue = null; - - var xKeyFrames = new ObjectArrayList>(); - var yKeyFrames = new ObjectArrayList>(); - var zKeyFrames = new ObjectArrayList>(); - - for (int i = 0; i < element.size(); i++) { - Map.Entry keyframe = element.get(i); - // TODO: 改成标准基岩版的插值类型 - if ("easing".equals(keyframe.getKey()) || "easingArgs".equals(keyframe.getKey())) { - continue; - } - Map.Entry previousKeyFrame = i == 0 ? null : element.get(i - 1); - - double previousKeyFrameLocation = previousKeyFrame == null ? 0 : Double.parseDouble(previousKeyFrame.getKey()); - double currentKeyFrameLocation = NumberUtils.isCreatable(keyframe.getKey()) ? Double.parseDouble(keyframe.getKey()) : 0; - double animationTimeDifference = currentKeyFrameLocation - previousKeyFrameLocation; - - JsonArray vectorJsonArray = getKeyFrameVector(keyframe.getValue()); - IValue xValue = parseExpression(parser, vectorJsonArray.get(0)); - IValue yValue = parseExpression(parser, vectorJsonArray.get(1)); - IValue zValue = parseExpression(parser, vectorJsonArray.get(2)); - - IValue currentXValue = isRotation && xValue instanceof ConstantValue ? ConstantValue.fromDouble(Math.toRadians(-xValue.get())) : xValue; - IValue currentYValue = isRotation && yValue instanceof ConstantValue ? ConstantValue.fromDouble(Math.toRadians(-yValue.get())) : yValue; - IValue currentZValue = isRotation && zValue instanceof ConstantValue ? ConstantValue.fromDouble(Math.toRadians(zValue.get())) : zValue; - KeyFrame xKeyFrame; - KeyFrame yKeyFrame; - KeyFrame zKeyFrame; - - if (keyframe.getValue().isJsonObject() && hasEasingType(keyframe.getValue())) { - EasingType easingType = getEasingType(keyframe.getValue()); - if (hasEasingArgs(keyframe.getValue())) { - List easingArgs = getEasingArgs(keyframe.getValue()); - xKeyFrame = new KeyFrame(AnimationUtils.convertSecondsToTicks(animationTimeDifference), i == 0 ? currentXValue : previousXValue, currentXValue, easingType, easingArgs); - yKeyFrame = new KeyFrame(AnimationUtils.convertSecondsToTicks(animationTimeDifference), i == 0 ? currentYValue : previousYValue, currentYValue, easingType, easingArgs); - zKeyFrame = new KeyFrame(AnimationUtils.convertSecondsToTicks(animationTimeDifference), i == 0 ? currentZValue : previousZValue, currentZValue, easingType, easingArgs); + public static void getKeyFrames(List boneKeyFrames, @Nullable JsonElement element, MolangParser parser) throws NumberFormatException { + if (element == null) { + return; + } + + if (element.isJsonPrimitive() || element.isJsonArray()) { + RawBoneKeyFrame keyframe = new RawBoneKeyFrame(); + keyframe.startTick = 0; + readPreKeyFrame(element, keyframe, parser); + boneKeyFrames.add(keyframe); + return; + } + + if (!element.isJsonObject()) { + return; + } + + JsonObject obj = element.getAsJsonObject(); + for (String time : obj.keySet()) { + RawBoneKeyFrame keyframe = new RawBoneKeyFrame(); + keyframe.startTick = convertSecondsToTicks(Double.parseDouble(time)); + + JsonElement item = obj.get(time); + if (item.isJsonPrimitive() || item.isJsonArray()) { + readPreKeyFrame(item, keyframe, parser); + } else if (item.isJsonObject()) { + JsonObject jsonObject = item.getAsJsonObject(); + + if (jsonObject.has("vector")) { + JsonElement vector = jsonObject.get("vector"); + JsonElement easing = jsonObject.get("easing"); + + readPreKeyFrame(vector, keyframe, parser); + tryGetEasingType(easing, keyframe); } else { - xKeyFrame = new KeyFrame(AnimationUtils.convertSecondsToTicks(animationTimeDifference), i == 0 ? currentXValue : previousXValue, currentXValue, easingType); - yKeyFrame = new KeyFrame(AnimationUtils.convertSecondsToTicks(animationTimeDifference), i == 0 ? currentYValue : previousYValue, currentYValue, easingType); - zKeyFrame = new KeyFrame(AnimationUtils.convertSecondsToTicks(animationTimeDifference), i == 0 ? currentZValue : previousZValue, currentZValue, easingType); + JsonElement pre = jsonObject.get("pre"); + JsonElement post = jsonObject.get("post"); + JsonElement easing = jsonObject.get("lerp_mode"); + + if (pre != null && post != null) { + readPreKeyFrame(pre, keyframe, parser); + readPostKeyFrame(post, keyframe, parser); + keyframe.contiguous = false; + } else { + if (pre != null) { + readPreKeyFrame(pre, keyframe, parser); + } else if (post != null) { + // 没错,post 赋给 pre + readPreKeyFrame(post, keyframe, parser); + } + } + tryGetEasingType(easing, keyframe); } - } else { - xKeyFrame = new KeyFrame(AnimationUtils.convertSecondsToTicks(animationTimeDifference), i == 0 ? currentXValue : previousXValue, currentXValue); - yKeyFrame = new KeyFrame(AnimationUtils.convertSecondsToTicks(animationTimeDifference), i == 0 ? currentYValue : previousYValue, currentYValue); - zKeyFrame = new KeyFrame(AnimationUtils.convertSecondsToTicks(animationTimeDifference), i == 0 ? currentZValue : previousZValue, currentZValue); } - previousXValue = currentXValue; - previousYValue = currentYValue; - previousZValue = currentZValue; - - xKeyFrames.add(xKeyFrame); - yKeyFrames.add(yKeyFrame); - zKeyFrames.add(zKeyFrame); + boneKeyFrames.add(keyframe); } - return new VectorKeyFrameList<>( - ObjectLists.unmodifiable(xKeyFrames), - ObjectLists.unmodifiable(yKeyFrames), - ObjectLists.unmodifiable(zKeyFrames)); + // 排序,因为 json 读取是乱序的 + boneKeyFrames.sort(Comparator.comparingDouble(k -> k.startTick)); } - private static JsonArray getKeyFrameVector(JsonElement element) { - if (element.isJsonArray()) { - return element.getAsJsonArray(); - } else { - return element.getAsJsonObject().get("vector").getAsJsonArray(); + private static void tryGetEasingType(JsonElement element, RawBoneKeyFrame keyframe) { + if (element == null || !element.isJsonPrimitive() || !element.getAsJsonPrimitive().isString()) { + return; + } + String easingTypeText = element.getAsJsonPrimitive().getAsString().toLowerCase(Locale.ENGLISH); + if ("linear".equals(easingTypeText)) { + keyframe.easingType = EasingType.LINEAR; + } else if ("catmullrom".equals(easingTypeText)) { + keyframe.easingType = EasingType.CATMULLROM; } } - private static boolean hasEasingType(JsonElement element) { - return element.getAsJsonObject().has("easing"); - } + private static void readPreKeyFrame(JsonElement element, RawBoneKeyFrame keyframe, MolangParser parser) { + if (element.isJsonPrimitive()) { + JsonPrimitive primitive = element.getAsJsonPrimitive(); + if (primitive.isString()) { + IValue value = parser.parseExpression(primitive.getAsString()); + keyframe.preXValue = value; + keyframe.preYValue = value; + keyframe.preZValue = value; + } else if (primitive.isNumber()) { + double value = primitive.getAsDouble(); + keyframe.preX = value; + keyframe.preY = value; + keyframe.preZ = value; + } + return; + } - private static boolean hasEasingArgs(JsonElement element) { - return element.getAsJsonObject().has("easingArgs"); - } + if (element.isJsonArray()) { + JsonArray array = element.getAsJsonArray(); + if (array.size() <= 0) { + return; + } + + if (array.size() >= 3) { + JsonPrimitive xPri = array.get(0).getAsJsonPrimitive(); + JsonPrimitive yPri = array.get(1).getAsJsonPrimitive(); + JsonPrimitive zPri = array.get(2).getAsJsonPrimitive(); + + if (xPri.isString()) { + keyframe.preXValue = parser.parseExpression(xPri.getAsString()); + } else if (xPri.isNumber()) { + keyframe.preX = xPri.getAsDouble(); + } - private static EasingType getEasingType(JsonElement element) { - final String easingString = element.getAsJsonObject().get("easing").getAsString(); - try { - // TODO: 改成标准基岩版的插值类型 - return EasingType.getEasingTypeFromString(easingString); - } catch (Exception e) { - TouhouLittleMaid.LOGGER.fatal("Unknown easing type: {}", easingString); - throw new RuntimeException(e); + if (yPri.isString()) { + keyframe.preYValue = parser.parseExpression(yPri.getAsString()); + } else if (yPri.isNumber()) { + keyframe.preY = yPri.getAsDouble(); + } + + if (zPri.isString()) { + keyframe.preZValue = parser.parseExpression(zPri.getAsString()); + } else if (zPri.isNumber()) { + keyframe.preZ = zPri.getAsDouble(); + } + + return; + } + + JsonPrimitive primitive = array.get(0).getAsJsonPrimitive(); + if (primitive.isString()) { + IValue value = parser.parseExpression(primitive.getAsString()); + keyframe.preXValue = value; + keyframe.preYValue = value; + keyframe.preZValue = value; + } else if (primitive.isNumber()) { + double value = primitive.getAsDouble(); + keyframe.preX = value; + keyframe.preY = value; + keyframe.preZ = value; + } } } - private static List getEasingArgs(JsonElement element) { - JsonObject asJsonObject = element.getAsJsonObject(); - JsonElement easingArgs = asJsonObject.get("easingArgs"); - JsonArray asJsonArray = easingArgs.getAsJsonArray(); - return JsonAnimationUtils.convertJsonArrayToList(asJsonArray); - } + private static void readPostKeyFrame(JsonElement element, RawBoneKeyFrame keyframe, MolangParser parser) { + if (element.isJsonPrimitive()) { + JsonPrimitive primitive = element.getAsJsonPrimitive(); + if (primitive.isString()) { + IValue value = parser.parseExpression(primitive.getAsString()); + keyframe.postXValue = value; + keyframe.postYValue = value; + keyframe.postZValue = value; + } else if (primitive.isNumber()) { + double value = primitive.getAsDouble(); + keyframe.postX = value; + keyframe.postY = value; + keyframe.postZ = value; + } + return; + } - public static VectorKeyFrameList> convertJsonToKeyFrames(List> element, MolangParser parser) throws NumberFormatException, MolangException { - return convertJson(element, false, parser); - } + if (element.isJsonArray()) { + JsonArray array = element.getAsJsonArray(); + if (array.size() <= 0) { + return; + } - public static VectorKeyFrameList> convertJsonToRotationKeyFrames(List> element, MolangParser parser) throws NumberFormatException, MolangException { - VectorKeyFrameList> frameList = convertJson(element, true, parser); - return new VectorKeyFrameList(frameList.xKeyFrames, frameList.yKeyFrames, frameList.zKeyFrames); - } + if (array.size() >= 3) { + JsonPrimitive xPri = array.get(0).getAsJsonPrimitive(); + JsonPrimitive yPri = array.get(1).getAsJsonPrimitive(); + JsonPrimitive zPri = array.get(2).getAsJsonPrimitive(); + + if (xPri.isString()) { + keyframe.postXValue = parser.parseExpression(xPri.getAsString()); + } else if (xPri.isNumber()) { + keyframe.postX = xPri.getAsDouble(); + } + + if (yPri.isString()) { + keyframe.postYValue = parser.parseExpression(yPri.getAsString()); + } else if (yPri.isNumber()) { + keyframe.postY = yPri.getAsDouble(); + } - public static IValue parseExpression(MolangParser parser, JsonElement element) throws MolangException { - if (element.getAsJsonPrimitive().isString()) { - return parser.parseJson(element); - } else { - return ConstantValue.fromDouble(element.getAsDouble()); + if (zPri.isString()) { + keyframe.postZValue = parser.parseExpression(zPri.getAsString()); + } else if (zPri.isNumber()) { + keyframe.postZ = zPri.getAsDouble(); + } + + return; + } + + JsonPrimitive primitive = array.get(0).getAsJsonPrimitive(); + if (primitive.isString()) { + IValue value = parser.parseExpression(primitive.getAsString()); + keyframe.postXValue = value; + keyframe.postYValue = value; + keyframe.postZValue = value; + } else if (primitive.isNumber()) { + double value = primitive.getAsDouble(); + keyframe.postX = value; + keyframe.postY = value; + keyframe.postZ = value; + } } } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Constant.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Constant.java deleted file mode 100644 index ad4a39b27..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Constant.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math; - - -public class Constant implements IValue { - private double value; - - public Constant(double value) { - this.value = value; - } - - @Override - public double get() { - return this.value; - } - - public void set(double value) { - this.value = value; - } - - @Override - public String toString() { - return String.valueOf(this.value); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Group.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Group.java deleted file mode 100644 index e00c32cbf..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Group.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math; - - -public class Group implements IValue { - private IValue value; - - public Group(IValue value) { - this.value = value; - } - - @Override - public double get() { - return this.value.get(); - } - - @Override - public String toString() { - return "(" + this.value.toString() + ")"; - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/IValue.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/IValue.java deleted file mode 100644 index a5128dc05..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/IValue.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math; - - -public interface IValue { - /** - * 获取计算值或存储值 - */ - - double get(); -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/MathBuilder.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/MathBuilder.java deleted file mode 100644 index 60b7865ef..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/MathBuilder.java +++ /dev/null @@ -1,388 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.classic.*; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.limit.Clamp; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.limit.Max; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.limit.Min; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.rounding.Ceil; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.rounding.Floor; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.rounding.Round; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.rounding.Trunc; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.utility.*; - -import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class MathBuilder { - public Map variables = new HashMap(); - - public Map> functions = new HashMap>(); - - public MathBuilder() { - /* 默认值 */ - this.register(new Variable("PI", Math.PI)); - this.register(new Variable("E", Math.E)); - - /* 取整函数 */ - this.functions.put("floor", Floor.class); - this.functions.put("round", Round.class); - this.functions.put("ceil", Ceil.class); - this.functions.put("trunc", Trunc.class); - - /* 比较函数 */ - this.functions.put("clamp", Clamp.class); - this.functions.put("max", Max.class); - this.functions.put("min", Min.class); - - /* 经典数学函数 */ - this.functions.put("abs", Abs.class); - this.functions.put("exp", Exp.class); - this.functions.put("ln", Ln.class); - this.functions.put("sqrt", Sqrt.class); - this.functions.put("mod", Mod.class); - this.functions.put("pow", Pow.class); - - /* 三角函数 */ - this.functions.put("cos", Cos.class); - this.functions.put("sin", Sin.class); - this.functions.put("acos", ACos.class); - this.functions.put("asin", ASin.class); - this.functions.put("atan", ATan.class); - this.functions.put("atan2", ATan2.class); - - /* 实用工具 */ - this.functions.put("lerp", Lerp.class); - this.functions.put("lerprotate", LerpRotate.class); - this.functions.put("random", Random.class); - this.functions.put("randomi", RandomInteger.class); - this.functions.put("roll", DieRoll.class); - this.functions.put("rolli", DieRollInteger.class); - this.functions.put("hermite", HermiteBlend.class); - } - - /** - * 注册变量 - */ - public void register(Variable variable) { - this.variables.put(variable.getName(), variable); - } - - /** - * 将给定的数学表达式解析为可用于执行数学运算的 {@link IValue} - */ - public IValue parse(String expression) throws Exception { - return this.parseSymbols(this.breakdownChars(this.breakdown(expression))); - } - - /** - * 分解表达式 - */ - public String[] breakdown(String expression) throws Exception { - // 如果给定的字符串包含非法字符,则无法解析 - if (!expression.matches("^[\\w\\d\\s_+-/*%^&|<>=!?:.,()]+$")) { - throw new Exception("Given expression '" + expression + "' contains illegal characters!"); - } - // 删除所有空格以及前导和尾随括号 - expression = expression.replaceAll("\\s+", ""); - String[] chars = expression.split("(?!^)"); - int left = 0; - int right = 0; - for (String s : chars) { - if ("(".equals(s)) { - left++; - } else if (")".equals(s)) { - right++; - } - } - // 左括号和右括号的数量应相同 - if (left != right) { - throw new Exception("Given expression '" + expression + "' has more uneven amount of parenthesis, there are " + left + " open and " + right + " closed!"); - } - return chars; - } - - /** - * 将分解字符转换为数学表达式符号列表 - */ - public List breakdownChars(String[] chars) { - List symbols = new ArrayList<>(); - String buffer = ""; - int len = chars.length; - for (int i = 0; i < len; i++) { - String s = chars[i]; - boolean longOperator = i > 0 && this.isOperator(chars[i - 1] + s); - if (this.isOperator(s) || longOperator || ",".equals(s)) { - // 使用负号翻转数值的用法判定 - if ("-".equals(s)) { - int size = symbols.size(); - boolean isFirst = size == 0 && buffer.isEmpty(); - boolean isOperatorBehind = size > 0 && (this.isOperator(symbols.get(size - 1)) || ",".equals(symbols.get(size - 1))) && buffer.isEmpty(); - if (isFirst || isOperatorBehind) { - buffer += s; - continue; - } - } - if (longOperator) { - s = chars[i - 1] + s; - buffer = buffer.substring(0, buffer.length() - 1); - } - // 推送 buffer 和操作符 - if (!buffer.isEmpty()) { - symbols.add(buffer); - buffer = ""; - } - symbols.add(s); - } else if ("(".equals(s)) { - if (!buffer.isEmpty()) { - symbols.add(buffer); - buffer = ""; - } - int counter = 1; - for (int j = i + 1; j < len; j++) { - String c = chars[j]; - if ("(".equals(c)) { - counter++; - } else if (")".equals(c)) { - counter--; - } - if (counter == 0) { - symbols.add(this.breakdownChars(buffer.split("(?!^)"))); - i = j; - buffer = ""; - break; - } else { - buffer += c; - } - } - } else { - buffer += s; - } - } - if (!buffer.isEmpty()) { - symbols.add(buffer); - } - return symbols; - } - - @SuppressWarnings("unchecked") - public IValue parseSymbols(List symbols) throws Exception { - IValue ternary = this.tryTernary(symbols); - if (ternary != null) { - return ternary; - } - int size = symbols.size(); - // 常量、变量或组(括号) - if (size == 1) { - return this.valueFromObject(symbols.get(0)); - } - // 函数 - if (size == 2) { - Object first = symbols.get(0); - Object second = symbols.get(1); - if ((this.isVariable(first) || "-".equals(first)) && second instanceof List) { - return this.createFunction((String) first, (List) second); - } - } - // 其他数学表达式 - int lastOp = this.seekLastOperator(symbols); - int op = lastOp; - - while (op != -1) { - int leftOp = this.seekLastOperator(symbols, op - 1); - if (leftOp != -1) { - Operation left = this.operationForOperator((String) symbols.get(leftOp)); - Operation right = this.operationForOperator((String) symbols.get(op)); - if (right.value > left.value) { - IValue leftValue = this.parseSymbols(symbols.subList(0, leftOp)); - IValue rightValue = this.parseSymbols(symbols.subList(leftOp + 1, size)); - return new Operator(left, leftValue, rightValue); - } else if (left.value > right.value) { - Operation initial = this.operationForOperator((String) symbols.get(lastOp)); - if (initial.value < left.value) { - IValue leftValue = this.parseSymbols(symbols.subList(0, lastOp)); - IValue rightValue = this.parseSymbols(symbols.subList(lastOp + 1, size)); - return new Operator(initial, leftValue, rightValue); - } - IValue leftValue = this.parseSymbols(symbols.subList(0, op)); - IValue rightValue = this.parseSymbols(symbols.subList(op + 1, size)); - return new Operator(right, leftValue, rightValue); - } - } - op = leftOp; - } - Operation operation = this.operationForOperator((String) symbols.get(lastOp)); - return new Operator(operation, this.parseSymbols(symbols.subList(0, lastOp)), this.parseSymbols(symbols.subList(lastOp + 1, size))); - } - - protected int seekLastOperator(List symbols) { - return this.seekLastOperator(symbols, symbols.size() - 1); - } - - protected int seekLastOperator(List symbols, int offset) { - for (int i = offset; i >= 0; i--) { - Object o = symbols.get(i); - if (this.isOperator(o)) { - return i; - } - } - return -1; - } - - protected int seekFirstOperator(List symbols) { - return this.seekFirstOperator(symbols, 0); - } - - protected int seekFirstOperator(List symbols, int offset) { - for (int i = offset, size = symbols.size(); i < size; i++) { - Object o = symbols.get(i); - if (this.isOperator(o)) { - return i; - } - } - return -1; - } - - /** - * 尝试解析三元表达式 - */ - protected IValue tryTernary(List symbols) throws Exception { - int question = -1; - int questions = 0; - int colon = -1; - int colons = 0; - int size = symbols.size(); - for (int i = 0; i < size; i++) { - Object object = symbols.get(i); - if (object instanceof String) { - if ("?".equals(object)) { - if (question == -1) { - question = i; - } - questions++; - } else if (":".equals(object)) { - if (colons + 1 == questions && colon == -1) { - colon = i; - } - colons++; - } - } - } - if (questions == colons && question > 0 && question + 1 < colon && colon < size - 1) { - return new Ternary( - this.parseSymbols(symbols.subList(0, question)), - this.parseSymbols(symbols.subList(question + 1, colon)), - this.parseSymbols(symbols.subList(colon + 1, size)) - ); - } - - return null; - } - - /** - * 创建函数值 - */ - protected IValue createFunction(String first, List args) throws Exception { - // 取非 - if ("!".equals(first)) { - return new Negate(this.parseSymbols(args)); - } - if (first.startsWith("!") && first.length() > 1) { - return new Negate(this.createFunction(first.substring(1), args)); - } - // 取负 - if ("-".equals(first)) { - return new Negative(new Group(this.parseSymbols(args))); - } - if (first.startsWith("-") && first.length() > 1) { - return new Negative(this.createFunction(first.substring(1), args)); - } - if (!this.functions.containsKey(first)) { - throw new Exception("Function '" + first + "' couldn't be found!"); - } - List values = new ArrayList<>(); - List buffer = new ArrayList<>(); - for (Object o : args) { - if (",".equals(o)) { - values.add(this.parseSymbols(buffer)); - buffer.clear(); - } else { - buffer.add(o); - } - } - if (!buffer.isEmpty()) { - values.add(this.parseSymbols(buffer)); - } - Class function = this.functions.get(first); - Constructor constructor = function.getConstructor(IValue[].class, String.class); - return constructor.newInstance(values.toArray(new IValue[0]), first); - } - - /** - * 从对象中获取值 - */ - @SuppressWarnings("unchecked") - public IValue valueFromObject(Object object) throws Exception { - if (object instanceof String) { - String symbol = (String) object; - // 取非 - if (symbol.startsWith("!")) { - return new Negate(this.valueFromObject(symbol.substring(1))); - } - if (this.isDecimal(symbol)) { - return new Constant(Double.parseDouble(symbol)); - } else if (this.isVariable(symbol)) { - // 取负 - if (symbol.startsWith("-")) { - symbol = symbol.substring(1); - Variable value = this.getVariable(symbol); - - if (value != null) { - return new Negative(value); - } - } else { - IValue value = this.getVariable(symbol); - // 避免 NPE - if (value != null) { - return value; - } - } - } - } else if (object instanceof List) { - return new Group(this.parseSymbols((List) object)); - } - throw new Exception("Given object couldn't be converted to value! " + object); - } - - protected Variable getVariable(String name) { - return this.variables.get(name); - } - - protected Operation operationForOperator(String op) throws Exception { - for (Operation operation : Operation.values()) { - if (operation.sign.equals(op)) { - return operation; - } - } - throw new Exception("There is no such operator '" + op + "'!"); - } - - protected boolean isVariable(Object o) { - return o instanceof String && !this.isDecimal((String) o) && !this.isOperator((String) o); - } - - protected boolean isOperator(Object o) { - return o instanceof String && this.isOperator((String) o); - } - - protected boolean isOperator(String s) { - return Operation.OPERATORS.contains(s) || "?".equals(s) || ":".equals(s); - } - - protected boolean isDecimal(String s) { - return s.matches("^-?\\d+(\\.\\d+)?$"); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Negate.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Negate.java deleted file mode 100644 index edb9658d2..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Negate.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math; - - -public class Negate implements IValue { - public IValue value; - - public Negate(IValue value) { - this.value = value; - } - - @Override - public double get() { - return this.value.get() == 0 ? 1 : 0; - } - - @Override - public String toString() { - return "!" + this.value.toString(); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Negative.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Negative.java deleted file mode 100644 index d714a2800..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Negative.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math; - - -public class Negative implements IValue { - public IValue value; - - public Negative(IValue value) { - this.value = value; - } - - @Override - public double get() { - return -this.value.get(); - } - - @Override - public String toString() { - return "-" + this.value.toString(); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Operation.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Operation.java deleted file mode 100644 index 4d1f791a2..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Operation.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math; - - -import java.util.HashSet; -import java.util.Set; - -public enum Operation { - // 操作符 - ADD("+", 1) { - @Override - public double calculate(double a, double b) { - return a + b; - } - }, - SUB("-", 1) { - @Override - public double calculate(double a, double b) { - return a - b; - } - }, - MUL("*", 2) { - @Override - public double calculate(double a, double b) { - return a * b; - } - }, - DIV("/", 2) { - @Override - public double calculate(double a, double b) { - /* To avoid any exceptions */ - return a / (b == 0 ? 1 : b); - } - }, - MOD("%", 2) { - @Override - public double calculate(double a, double b) { - return a % b; - } - }, - POW("^", 3) { - @Override - public double calculate(double a, double b) { - return Math.pow(a, b); - } - }, - AND("&&", 5) { - @Override - public double calculate(double a, double b) { - return a != 0 && b != 0 ? 1 : 0; - } - }, - OR("||", 5) { - @Override - public double calculate(double a, double b) { - return a != 0 || b != 0 ? 1 : 0; - } - }, - LESS("<", 5) { - @Override - public double calculate(double a, double b) { - return a < b ? 1 : 0; - } - }, - LESS_THAN("<=", 5) { - @Override - public double calculate(double a, double b) { - return a <= b ? 1 : 0; - } - }, - GREATER_THAN(">=", 5) { - @Override - public double calculate(double a, double b) { - return a >= b ? 1 : 0; - } - }, - GREATER(">", 5) { - @Override - public double calculate(double a, double b) { - return a > b ? 1 : 0; - } - }, - EQUALS("==", 5) { - @Override - public double calculate(double a, double b) { - return equals(a, b) ? 1 : 0; - } - }, - NOT_EQUALS("!=", 5) { - @Override - public double calculate(double a, double b) { - return !equals(a, b) ? 1 : 0; - } - }; - - public final static Set OPERATORS = new HashSet(); - - static { - for (Operation op : values()) { - OPERATORS.add(op.sign); - } - } - - /** - * 此操作的字符串化名称 - */ - public final String sign; - /** - * 此操作相对于其他操作的优先级 - */ - public final int value; - - Operation(String sign, int value) { - this.sign = sign; - this.value = value; - } - - public static boolean equals(double a, double b) { - return Math.abs(a - b) < 0.00001; - } - - /** - * 根据给定的两个 double 计算值 - */ - public abstract double calculate(double a, double b); -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Operator.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Operator.java deleted file mode 100644 index b9513d898..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Operator.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math; - - -public class Operator implements IValue { - public Operation operation; - public IValue a; - public IValue b; - - public Operator(Operation op, IValue a, IValue b) { - this.operation = op; - this.a = a; - this.b = b; - } - - @Override - public double get() { - return this.operation.calculate(a.get(), b.get()); - } - - @Override - public String toString() { - return a.toString() + " " + this.operation.sign + " " + b.toString(); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Ternary.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Ternary.java deleted file mode 100644 index d695f8f46..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Ternary.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math; - - -public class Ternary implements IValue { - public IValue condition; - public IValue ifTrue; - public IValue ifFalse; - - public Ternary(IValue condition, IValue ifTrue, IValue ifFalse) { - this.condition = condition; - this.ifTrue = ifTrue; - this.ifFalse = ifFalse; - } - - @Override - public double get() { - return this.condition.get() != 0 ? this.ifTrue.get() : this.ifFalse.get(); - } - - @Override - public String toString() { - return this.condition.toString() + " ? " + this.ifTrue.toString() + " : " + this.ifFalse.toString(); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Variable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Variable.java deleted file mode 100644 index c2d8dbe8d..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/Variable.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math; - - -public class Variable implements IValue { - private String name; - private double value; - - public Variable(String name, double value) { - this.name = name; - this.value = value; - } - - public void set(double value) { - this.value = value; - } - - @Override - public double get() { - return this.value; - } - - public String getName() { - return name; - } - - @Override - public String toString() { - return this.name; - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/Function.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/Function.java deleted file mode 100644 index 3a06fd30b..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/Function.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; - - -public abstract class Function implements IValue { - protected IValue[] args; - protected String name; - - public Function(IValue[] values, String name) throws Exception { - if (values.length < this.getRequiredArguments()) { - String message = String.format("Function '%s' requires at least %s arguments. %s are given!", this.getName(), this.getRequiredArguments(), values.length); - throw new Exception(message); - } - this.args = values; - this.name = name; - } - - /** - * 获取第 N 个参数的值 - */ - public double getArg(int index) { - if (index < 0 || index >= this.args.length) { - return 0; - } - return this.args[index].get(); - } - - @Override - public String toString() { - StringBuilder args = new StringBuilder(); - for (int i = 0; i < this.args.length; i++) { - args.append(this.args[i].toString()); - if (i < this.args.length - 1) { - args.append(", "); - } - } - return this.getName() + "(" + args + ")"; - } - - /** - * 获取函数名 - */ - public String getName() { - return this.name; - } - - /** - * 获取此函数所需的最小参数量 - */ - - public int getRequiredArguments() { - return 0; - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/ACos.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/ACos.java deleted file mode 100644 index 7ab4c3d06..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/ACos.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.classic; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class ACos extends Function { - public ACos(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 1; - } - - @Override - public double get() { - return Math.acos(getArg(0)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/ASin.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/ASin.java deleted file mode 100644 index eb0c346ec..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/ASin.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.classic; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class ASin extends Function { - public ASin(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 1; - } - - @Override - public double get() { - return Math.asin(getArg(0)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/ATan.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/ATan.java deleted file mode 100644 index 6e568a4b9..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/ATan.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.classic; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class ATan extends Function { - public ATan(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 1; - } - - @Override - public double get() { - return Math.atan(getArg(0)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/ATan2.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/ATan2.java deleted file mode 100644 index a86a32fa6..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/ATan2.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.classic; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class ATan2 extends Function { - public ATan2(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 2; - } - - @Override - public double get() { - return Math.atan2(getArg(0), getArg(1)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Abs.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Abs.java deleted file mode 100644 index f1ade6334..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Abs.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.classic; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class Abs extends Function { - public Abs(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 1; - } - - @Override - public double get() { - return Math.abs(this.getArg(0)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Cos.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Cos.java deleted file mode 100644 index 18589ecbc..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Cos.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.classic; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class Cos extends Function { - public Cos(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 1; - } - - @Override - public double get() { - return Math.cos(this.getArg(0)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Exp.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Exp.java deleted file mode 100644 index 35b2d326f..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Exp.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.classic; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class Exp extends Function { - public Exp(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 1; - } - - @Override - public double get() { - return Math.exp(this.getArg(0)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Ln.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Ln.java deleted file mode 100644 index 02efbd8c4..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Ln.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.classic; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class Ln extends Function { - public Ln(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 1; - } - - @Override - public double get() { - return Math.log(this.getArg(0)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Mod.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Mod.java deleted file mode 100644 index 62a6ed290..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Mod.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.classic; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class Mod extends Function { - public Mod(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 2; - } - - @Override - public double get() { - return this.getArg(0) % this.getArg(1); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Pi.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Pi.java deleted file mode 100644 index 851b52ee0..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Pi.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.classic; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class Pi extends Function { - public Pi(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public double get() { - return 3.141592653589793d; - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Pow.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Pow.java deleted file mode 100644 index e913f3b92..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Pow.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.classic; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class Pow extends Function { - public Pow(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 2; - } - - @Override - public double get() { - return Math.pow(this.getArg(0), this.getArg(1)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Sin.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Sin.java deleted file mode 100644 index 67cce74e9..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Sin.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.classic; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class Sin extends Function { - public Sin(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 1; - } - - @Override - public double get() { - return Math.sin(this.getArg(0)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Sqrt.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Sqrt.java deleted file mode 100644 index e87b7506a..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/classic/Sqrt.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.classic; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class Sqrt extends Function { - public Sqrt(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 1; - } - - @Override - public double get() { - return Math.sqrt(this.getArg(0)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/limit/Clamp.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/limit/Clamp.java deleted file mode 100644 index 70a45b80e..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/limit/Clamp.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.limit; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; -import net.minecraft.util.Mth; - -public class Clamp extends Function { - public Clamp(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 3; - } - - @Override - public double get() { - return Mth.clamp(this.getArg(0), this.getArg(1), this.getArg(2)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/limit/Max.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/limit/Max.java deleted file mode 100644 index 207b3f694..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/limit/Max.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.limit; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class Max extends Function { - public Max(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 2; - } - - @Override - public double get() { - return Math.max(this.getArg(0), this.getArg(1)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/limit/Min.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/limit/Min.java deleted file mode 100644 index d0e40a14d..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/limit/Min.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.limit; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class Min extends Function { - public Min(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 2; - } - - @Override - public double get() { - return Math.min(this.getArg(0), this.getArg(1)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/rounding/Ceil.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/rounding/Ceil.java deleted file mode 100644 index b51f5a0e9..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/rounding/Ceil.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.rounding; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class Ceil extends Function { - public Ceil(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 1; - } - - @Override - public double get() { - return Math.ceil(this.getArg(0)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/rounding/Floor.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/rounding/Floor.java deleted file mode 100644 index 99375208d..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/rounding/Floor.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.rounding; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class Floor extends Function { - public Floor(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 1; - } - - @Override - public double get() { - return Math.floor(this.getArg(0)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/rounding/Round.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/rounding/Round.java deleted file mode 100644 index 0302b8841..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/rounding/Round.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.rounding; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class Round extends Function { - public Round(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 1; - } - - @Override - public double get() { - return Math.round(this.getArg(0)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/rounding/Trunc.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/rounding/Trunc.java deleted file mode 100644 index 674c871f4..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/rounding/Trunc.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.rounding; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class Trunc extends Function { - public Trunc(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 1; - } - - @Override - public double get() { - double value = this.getArg(0); - - return value < 0 ? Math.ceil(value) : Math.floor(value); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/DieRoll.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/DieRoll.java deleted file mode 100644 index 543bb9bf1..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/DieRoll.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.utility; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class DieRoll extends Function { - public java.util.Random random; - - public DieRoll(IValue[] values, String name) throws Exception { - super(values, name); - this.random = new java.util.Random(); - } - - @Override - public int getRequiredArguments() { - return 3; - } - - @Override - public double get() { - double i = 0; - double total = 0; - while (i < this.getArg(0)) { - total += Math.random() * (this.getArg(2) - this.getArg(2)); - } - return total; - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/DieRollInteger.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/DieRollInteger.java deleted file mode 100644 index 866d64aea..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/DieRollInteger.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.utility; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class DieRollInteger extends Function { - public java.util.Random random; - - public DieRollInteger(IValue[] values, String name) throws Exception { - super(values, name); - this.random = new java.util.Random(); - } - - @Override - public int getRequiredArguments() { - return 3; - } - - @Override - public double get() { - double i = 0; - double total = 0; - while (i < this.getArg(0)) { - total += (double) Math.round(this.getArg(1) + Math.random() * (this.getArg(2) - this.getArg(1))); - } - return total; - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/HermiteBlend.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/HermiteBlend.java deleted file mode 100644 index 8a6935c16..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/HermiteBlend.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.utility; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class HermiteBlend extends Function { - public java.util.Random random; - - public HermiteBlend(IValue[] values, String name) throws Exception { - super(values, name); - this.random = new java.util.Random(); - } - - @Override - public int getRequiredArguments() { - return 1; - } - - @Override - public double get() { - double min = Math.ceil(this.getArg(0)); - return Math.floor(3.0 * Math.pow(min, 2.0) - 2.0 * Math.pow(min, 3.0)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/Lerp.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/Lerp.java deleted file mode 100644 index e940b6ee5..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/Lerp.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.utility; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; -import com.github.tartaricacid.touhoulittlemaid.mclib.utils.Interpolations; - - -public class Lerp extends Function { - public Lerp(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 3; - } - - @Override - public double get() { - return Interpolations.lerp(this.getArg(0), this.getArg(1), this.getArg(2)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/LerpRotate.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/LerpRotate.java deleted file mode 100644 index 727795309..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/LerpRotate.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.utility; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; -import com.github.tartaricacid.touhoulittlemaid.mclib.utils.Interpolations; - - -public class LerpRotate extends Function { - public LerpRotate(IValue[] values, String name) throws Exception { - super(values, name); - } - - @Override - public int getRequiredArguments() { - return 3; - } - - @Override - public double get() { - return Interpolations.lerpYaw(this.getArg(0), this.getArg(1), this.getArg(2)); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/Random.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/Random.java deleted file mode 100644 index d3ddde64c..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/Random.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.utility; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class Random extends Function { - public java.util.Random random; - - public Random(IValue[] values, String name) throws Exception { - super(values, name); - this.random = new java.util.Random(); - } - - @Override - public double get() { - double random = 0; - if (this.args.length >= 3) { - this.random.setSeed((long) this.getArg(2)); - random = this.random.nextDouble(); - } else { - random = Math.random(); - } - if (this.args.length >= 2) { - double a = this.getArg(0); - double b = this.getArg(1); - double min = Math.min(a, b); - double max = Math.max(a, b); - random = random * (max - min) + min; - } else if (this.args.length >= 1) { - random = random * this.getArg(0); - } - return random; - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/RandomInteger.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/RandomInteger.java deleted file mode 100644 index 139fc2526..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/math/functions/utility/RandomInteger.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.utility; - -import com.github.tartaricacid.touhoulittlemaid.mclib.math.IValue; -import com.github.tartaricacid.touhoulittlemaid.mclib.math.functions.Function; - - -public class RandomInteger extends Function { - public java.util.Random random; - - public RandomInteger(IValue[] values, String name) throws Exception { - super(values, name); - this.random = new java.util.Random(); - } - - @Override - public int getRequiredArguments() { - return 2; - } - - @Override - public double get() { - double min = Math.ceil(this.getArg(0)); - double max = Math.floor(this.getArg(1)); - return Math.floor(Math.random() * (max - min) + min); - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/utils/Interpolation.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/utils/Interpolation.java deleted file mode 100644 index e5a4cbd6f..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/utils/Interpolation.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.mclib.utils; - - -public enum Interpolation { - // 插值类型 - LINEAR("linear") { - @Override - public float interpolate(float a, float b, float x) { - return Interpolations.lerp(a, b, x); - } - }, - QUAD_IN("quad_in") { - @Override - public float interpolate(float a, float b, float x) { - return a + (b - a) * x * x; - } - }, - QUAD_OUT("quad_out") { - @Override - public float interpolate(float a, float b, float x) { - return a - (b - a) * x * (x - 2); - } - }, - QUAD_INOUT("quad_inout") { - @Override - public float interpolate(float a, float b, float x) { - x *= 2; - if (x < 1F) { - return a + (b - a) / 2 * x * x; - } - x -= 1; - return a - (b - a) / 2 * (x * (x - 2) - 1); - } - }, - CUBIC_IN("cubic_in") { - @Override - public float interpolate(float a, float b, float x) { - return a + (b - a) * x * x * x; - } - }, - CUBIC_OUT("cubic_out") { - @Override - public float interpolate(float a, float b, float x) { - x -= 1; - return a + (b - a) * (x * x * x + 1); - } - }, - CUBIC_INOUT("cubic_inout") { - @Override - public float interpolate(float a, float b, float x) { - x *= 2; - if (x < 1F) { - return a + (b - a) / 2 * x * x * x; - } - x -= 2; - return a + (b - a) / 2 * (x * x * x + 2); - } - }, - EXP_IN("exp_in") { - @Override - public float interpolate(float a, float b, float x) { - return a + (b - a) * (float) Math.pow(2, 10 * (x - 1)); - } - }, - EXP_OUT("exp_out") { - @Override - public float interpolate(float a, float b, float x) { - return a + (b - a) * (float) (-Math.pow(2, -10 * x) + 1); - } - }, - EXP_INOUT("exp_inout") { - @Override - public float interpolate(float a, float b, float x) { - if (x == 0) { - return a; - } - if (x == 1) { - return b; - } - x *= 2; - if (x < 1F) { - return a + (b - a) / 2 * (float) Math.pow(2, 10 * (x - 1)); - } - x -= 1; - return a + (b - a) / 2 * (float) (-Math.pow(2, -10 * x) + 2); - } - }; - - public final String key; - - private Interpolation(String key) { - this.key = key; - } - - public abstract float interpolate(float a, float b, float x); - - public String getName() { - return "mclib.interpolations." + this.key; - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/utils/Interpolations.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/utils/Interpolations.java index c3dca50ce..d53b65730 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/utils/Interpolations.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/mclib/utils/Interpolations.java @@ -42,6 +42,20 @@ public static double cubicHermiteYaw(float y0, float y1, float y2, float y3, flo return cubicHermite(y0, y1, y2, y3, position); } + /** + * Yaw 的 Hermite 三次插值 + */ + public static double cubicHermiteYaw(double y0, double y1, double y2, double y3, double position) { + y0 = MathHelper.wrapDegrees(y0); + y1 = MathHelper.wrapDegrees(y1); + y2 = MathHelper.wrapDegrees(y2); + y3 = MathHelper.wrapDegrees(y3); + y1 = normalizeYaw(y0, y1); + y2 = normalizeYaw(y1, y2); + y3 = normalizeYaw(y2, y3); + return cubicHermite(y0, y1, y2, y3, position); + } + /** * y1 和 y2 之间的三次插值 */ diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/MolangEngine.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/MolangEngine.java new file mode 100644 index 000000000..137324282 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/MolangEngine.java @@ -0,0 +1,82 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang; + +import com.github.tartaricacid.touhoulittlemaid.molang.lexer.Cursor; +import com.github.tartaricacid.touhoulittlemaid.molang.parser.ParseException; +import com.github.tartaricacid.touhoulittlemaid.molang.parser.ast.Expression; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding.ObjectBinding; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.List; + +/** + * The engine's entry class. Provides methods to evaluate + * and parse Molang code from strings and readers. + * + * @since 3.0.0 + */ +public interface MolangEngine { + + /** + * Parses the data from the given {@code reader} + * to a {@link List} of {@link Expression} + * + * Note that this method won't close + * the given {@code reader} + * + * @throws ParseException If read failed or there + * are syntax errors in the script + */ + List parse(Reader reader) throws IOException; + + /** + * Parses the given {@code string} to a list of + * {@link Expression} + * + * @param string The MoLang string + * @return The list of parsed expressions + * @throws ParseException If parsing fails + */ + default List parse(String string) throws ParseException { + try (Reader reader = new StringReader(string)) { + return parse(reader); + } catch (ParseException e) { + throw e; + } catch (IOException e) { + throw new ParseException("Failed to close string reader", e, new Cursor(0, 0)); + } + } + + static MolangEngine fromCustomBinding(ObjectBinding binding) { + return new MolangEngineImpl(binding); + } + + static MolangEngine createEmpty() { + return new MolangEngineImpl(ObjectBinding.EMPTY); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/MolangEngineImpl.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/MolangEngineImpl.java new file mode 100644 index 000000000..3b179e342 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/MolangEngineImpl.java @@ -0,0 +1,46 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang; + +import com.github.tartaricacid.touhoulittlemaid.molang.parser.MolangParser; +import com.github.tartaricacid.touhoulittlemaid.molang.parser.ast.Expression; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding.ObjectBinding; + +import java.io.IOException; +import java.io.Reader; +import java.util.List; + +final class MolangEngineImpl implements MolangEngine { + private final ObjectBinding bindings; + + MolangEngineImpl(ObjectBinding bindings) { + this.bindings = bindings; + } + + @Override + public List parse(Reader reader) throws IOException { + return MolangParser.parser(reader, this.bindings).parseAll(); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/Characters.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/Characters.java new file mode 100644 index 000000000..f65bc786b --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/Characters.java @@ -0,0 +1,50 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.lexer; + + +/** + * Utility class holding utility static + * methods for working with character + * tokens + */ +final class Characters { + + private Characters() { + } + + public static boolean isDigit(final int c) { + return Character.isDigit(c); + } + + public static boolean isValidForWordStart(final int c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'; + } + + public static boolean isValidForWordContinuation(final int c) { + return isValidForWordStart(c) || isDigit(c); + } + +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/Cursor.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/Cursor.java new file mode 100644 index 000000000..edce9ad96 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/Cursor.java @@ -0,0 +1,101 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.lexer; + +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * Mutable class that tracks the position of characters + * when performing lexical analysis + * + *

Can be used to show the position of lexical errors + * in a human-readable way

+ * + * @since 3.0.0 + */ +public final class Cursor implements Cloneable { + + private int index = 0; + private int line = 0; + private int column = 0; + + public Cursor(final int line, final int column) { + this.line = line; + this.column = column; + } + + public Cursor() { + } + + public int index() { + return index; + } + + public int line() { + return line; + } + + public int column() { + return column; + } + + public void push(final int character) { + index++; + if (character == '\n') { + // if it's a line break, + // reset the column + line++; + column = 1; + } else { + column++; + } + } + + @Override + public @NotNull Cursor clone() { + return new Cursor(line, column); + } + + @Override + public String toString() { + return "line " + line + ", column " + column; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Cursor that = (Cursor) o; + return line == that.line && column == that.column; + } + + @Override + public int hashCode() { + return Objects.hash(line, column); + } + +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/MolangLexer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/MolangLexer.java new file mode 100644 index 000000000..d026de846 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/MolangLexer.java @@ -0,0 +1,188 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.lexer; + +import org.jetbrains.annotations.NotNull; + +import java.io.Closeable; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; + +/** + * Lexical analyzer for the Molang language. + * + *

The lexical analyzer converts character streams + * to token streams

+ * + *

Note that this is a stream-based lexer, this means + * that it will not consume the entire reader if it doesn't + * continue having next() calls

+ * + *

See the following example on correctly lexing a string:

+ *
{@code
+ *     MolangLexer lexer = MolangLexer.lexer(new StringReader("1 + 1"));
+ *     List tokens = new ArrayList<>();
+ *     Token token;
+ *     while ((token = lexer.next()).kind() != TokenKind.EOF) {
+ *         tokens.add(token);
+ *     }
+ *     // tokens: [ Double, Plus, Double ]
+ * }
+ * + *

Or using the shorter, convenience method:

+ *
{@code
+ *      List tokens = MolangLexer.tokenizeAll("1 + 1");
+ *      // tokens: [ Double, Plus, Double ]
+ * }
+ * + * @since 3.0.0 + */ +public /* sealed */ interface MolangLexer /* permits MolangLexerImpl */ extends Closeable { + + /** + * Returns the cursor for this lexer, the cursor maintains + * track of the current line and column, it is used for + * error reporting. + * + * @return The lexer cursor + * @since 3.0.0 + */ + @NotNull Cursor cursor(); + + /** + * Returns the last emitted token (the last token value + * returned when calling {@link MolangLexer#next()}) + * + *

Requires the user to call {@link MolangLexer#next()} + * at least once first.

+ * + * @return The last emitted token + * @throws IllegalStateException If there is no current token + * @since 3.0.0 + */ + @NotNull Token current(); + + /** + * Reads the internal reader until it gets a token and + * then returns it. + * + *

The returned token will never be null, but it can + * be of kind {@link TokenKind#EOF} or {@link TokenKind#ERROR}.

+ * + *

We can stop lexing when we find a {@link TokenKind#EOF} token + * for the first time, since following tokens will be EOF too.

+ * + * @return The emitted token after reading characters from the internal reader + * @throws IOException If reading fails + * @since 3.0.0 + */ + @NotNull Token next() throws IOException; + + /** + * Reads all the tokens until it finds a {@link TokenKind#EOF}. + * + *

After this method is called, the lexer should be + * done and all next tokens should be EOF

+ * + * @return All the read tokens + * @throws IOException If reading fails + * @since 3.0.0 + */ + default @NotNull List tokenizeAll() throws IOException { + List tokens = new ArrayList<>(); + Token token; + while ((token = next()).kind() != TokenKind.EOF) { + tokens.add(token); + } + return tokens; + } + + /** + * Closes this lexer and the internal {@link Reader}. + * + * @throws IOException If closing fails + * @since 3.0.0 + */ + @Override + void close() throws IOException; + + /** + * Creates a new lexer that will read the characters from the + * given reader. + * + * @param reader The reader to use. + * @return The created lexer + * @throws IOException If lexer initialization fails. + * @since 3.0.0 + */ + static @NotNull MolangLexer lexer(final @NotNull Reader reader) throws IOException { + return new MolangLexerImpl(reader); + } + + /** + * Creates a new lexer that will read the characters from + * the given string. + * + * @param string The string to tokenize. + * @return The created lexer + * @throws IOException If lexer initialization fails. + * @since 3.0.0 + */ + static @NotNull MolangLexer lexer(final @NotNull String string) throws IOException { + return lexer(new StringReader(string)); + } + + /** + * Tokenizes all the data from the given reader. + * + * @param reader The reader. + * @return The emitted tokens. + * @throws IOException If reading fails. + * @since 3.0.0 + */ + static @NotNull List tokenizeAll(final @NotNull Reader reader) throws IOException { + try (MolangLexer lexer = lexer(reader)) { + return lexer.tokenizeAll(); + } + } + + /** + * Tokenizes the provided string. + * + * @param string The string. + * @return The emitted tokens. + * @throws IOException If reading fails. + * @since 3.0.0 + */ + static @NotNull List tokenizeAll(final @NotNull String string) throws IOException { + try (MolangLexer lexer = lexer(string)) { + return lexer.tokenizeAll(); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/MolangLexerImpl.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/MolangLexerImpl.java new file mode 100644 index 000000000..a6dc7094b --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/MolangLexerImpl.java @@ -0,0 +1,301 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.lexer; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.io.Reader; + +import static java.util.Objects.requireNonNull; + +final class MolangLexerImpl implements MolangLexer { + + // the source reader + private final Reader reader; + + // the current index + private final Cursor cursor = new Cursor(); + + // the next character to be checked + private int next; + + // the current token + private Token lastToken = null; + private Token token = null; + + MolangLexerImpl(final @NotNull Reader reader) throws IOException { + this.reader = requireNonNull(reader, "reader"); + this.next = reader.read(); + } + + @Override + public @NotNull Cursor cursor() { + return cursor; + } + + @Override + public @NotNull Token current() { + if (token == null) { + throw new IllegalStateException("No current token, please call next() at least once"); + } + return token; + } + + @Override + public @NotNull Token next() throws IOException { + lastToken = token; + return token = next0(); + } + + @Override + public void close() throws IOException { + this.reader.close(); + } + + private @NotNull Token next0() throws IOException { + int c = next; + if (c == -1) { + // EOF reached + return new Token(TokenKind.EOF, null, cursor.index(), cursor.index() + 1); + } + + // skip whitespace (including tabs and newlines) + while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { + c = read(); + } + + // additional spaces, lines, etc. at the end? + if (c == -1) { + // EOF reached + return new Token(TokenKind.EOF, null, cursor.index(), cursor.index() + 1); + } + + int start = cursor.index(); + if (c == '.' && lastToken != null && lastToken.kind() == TokenKind.RPAREN) { + read(); + return new Token(TokenKind.DOT, null, start, cursor.index()); + } + boolean isLastIdentifier = (lastToken != null && lastToken.kind() == TokenKind.IDENTIFIER); + if (Characters.isDigit(c) || (!isLastIdentifier && c == '.')) { + StringBuilder builder = new StringBuilder(8); + if(!isLastIdentifier) { + builder.appendCodePoint(c); + + // first char is a digit, continue reading number + while (Characters.isDigit(c = read())) { + builder.appendCodePoint(c); + } + } else { + builder.append('0'); + } + + if (c == '.') { + builder.append('.'); + while (Characters.isDigit(c = read())) { + builder.appendCodePoint(c); + } + } + + return new Token(TokenKind.FLOAT, builder.toString(), start, cursor.index()); + } else if (Characters.isValidForWordStart(c)) { + // may be an identifier or a keyword + StringBuilder builder = new StringBuilder(); + do { + builder.appendCodePoint(c); + } while (Characters.isValidForWordContinuation(c = read())); + String word = builder.toString().toLowerCase(); + TokenKind kind; + switch (word) { + //@formatter:off + case "break": kind = TokenKind.BREAK; break; + case "continue": kind = TokenKind.CONTINUE; break; + case "return": kind = TokenKind.RETURN; break; + case "true": kind = TokenKind.TRUE; break; + case "false": kind = TokenKind.FALSE; break; + default: kind = TokenKind.IDENTIFIER; break; + //@formatter:on + } + + return new Token( + kind, + // keywords do not have values + kind == TokenKind.IDENTIFIER ? word : null, + start, + cursor.index() + ); + } else if (c == '\'') { // single quote means string start + StringBuilder value = new StringBuilder(16); + while (true) { + c = read(); + if (c == -1) { + // the heck? you didn't close the string + return new Token(TokenKind.ERROR, "Found end-of-file before closing quote", start, cursor.index()); + } else if (c == '\'') { + // string was closed! + break; + } else { + // TODO: should we allow escaping quotes? should we disallow line breaks? + // not end of file nor quote, this is inside the string literal + value.appendCodePoint(c); + } + } + // Here, "c" should be a quote, so skip it and give it to the next person + read(); + return new Token(TokenKind.STRING, value.toString(), start, cursor.index()); + } else { + // here we are sure that "c" is NOT: + // - EOF + // - Single Quote (') + // - A-Za-z_ + // - 0-9 + // so it must be some sign like ?, *, +, - + TokenKind tokenKind; + String value = null; // only set of token kind = ERROR, value is error message + int c1 = -2; // only set if "c" may have a continuation, for example "==", "!=", "??" + switch (c) { + case '!': { + c1 = read(); + if (c1 == '=') { + read(); + tokenKind = TokenKind.BANGEQ; + } else { + tokenKind = TokenKind.BANG; + } + break; + } + case '&': { + c1 = read(); + if (c1 == '&') { + read(); + tokenKind = TokenKind.AMPAMP; + } else { + tokenKind = TokenKind.ERROR; + value = "Unexpected token '" + ((char) c1) + "', expected '&' (Molang doesn't support bitwise operators)"; + } + break; + } + case '|': { + c1 = read(); + if (c1 == '|') { + read(); + tokenKind = TokenKind.BARBAR; + } else { + tokenKind = TokenKind.ERROR; + value = "Unexpected token '" + ((char) c1) + "', expected '|' (Molang doesn't support bitwise operators)"; + } + break; + } + case '<': { + c1 = read(); + if (c1 == '=') { + read(); + tokenKind = TokenKind.LTE; + } else { + tokenKind = TokenKind.LT; + } + break; + } + case '>': { + c1 = read(); + if (c1 == '=') { + read(); + tokenKind = TokenKind.GTE; + } else { + tokenKind = TokenKind.GT; + } + break; + } + case '=': { + c1 = read(); + if (c1 == '=') { + read(); + tokenKind = TokenKind.EQEQ; + } else { + tokenKind = TokenKind.EQ; + } + break; + } + case '-': { + c1 = read(); + if (c1 == '>') { + read(); + tokenKind = TokenKind.ARROW; + } else { + tokenKind = TokenKind.SUB; + } + break; + } + case '?': { + c1 = read(); + if (c1 == '?') { + read(); + tokenKind = TokenKind.QUESQUES; + } else { + tokenKind = TokenKind.QUES; + } + break; + } + //@formatter:off + case '/': tokenKind = TokenKind.SLASH; break; + case '*': tokenKind = TokenKind.STAR; break; + case '+': tokenKind = TokenKind.PLUS; break; + case ',': tokenKind = TokenKind.COMMA; break; + case '.': tokenKind = TokenKind.DOT; break; + case '(': tokenKind = TokenKind.LPAREN; break; + case ')': tokenKind = TokenKind.RPAREN; break; + case '{': tokenKind = TokenKind.LBRACE; break; + case '}': tokenKind = TokenKind.RBRACE; break; + case ':': tokenKind = TokenKind.COLON; break; + case '[': tokenKind = TokenKind.LBRACKET; break; + case ']': tokenKind = TokenKind.RBRACKET; break; + case ';': tokenKind = TokenKind.SEMICOLON; break; + //@formatter:on + default: { + // "c" is something we don't know about! + tokenKind = TokenKind.ERROR; + value = "Unexpected token '" + ((char) c) + "': invalid token"; + break; + } + } + + if (c1 == -2) { + // if token kind was known and the token didn't + // check for an extra character + read(); + } + + return new Token(tokenKind, value, start, cursor.index()); + } + } + + private int read() throws IOException { + int c = reader.read(); + cursor.push(c); + next = c; + return c; + } + +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/Token.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/Token.java new file mode 100644 index 000000000..ed3b91273 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/Token.java @@ -0,0 +1,136 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.lexer; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +/** + * Class representing a Molang token. Each token has some + * information set by the lexer (i.e. start/end position, + * token kind and optional value) + * + * @since 3.0.0 + */ +public final class Token { + + private final TokenKind kind; + private final @Nullable String value; + private final int start; + private final int end; + + public Token( + final @NotNull TokenKind kind, + final @Nullable String value, + final int start, + final int end + ) { + this.kind = requireNonNull(kind, "kind"); + this.value = value; + this.start = start; + this.end = end; + + // verify state, token kinds that have HAS_VALUE tag, must have a non-null value + if (kind.hasTag(TokenKind.Tag.HAS_VALUE) && value == null) { + throw new IllegalArgumentException("A token with kind " + + kind + " must have a non-null value"); + } + } + + /** + * Gets the token kind. + * + * @return The token kind + * @since 3.0.0 + */ + public @NotNull TokenKind kind() { + return kind; + } + + /** + * Gets the token value. Null if this kind + * of tokens doesn't allow values. + * + * @return The token value + * @since 3.0.0 + */ + public String value() { + return value; + } + + /** + * Gets the start index of this token. + * + * @return The token start + * @since 3.0.0 + */ + public int start() { + return start; + } + + /** + * Gets the end index of this token. + * + * @return The token end + * @since 3.0.0 + */ + public int end() { + return end; + } + + @Override + public String toString() { + if (kind.hasTag(TokenKind.Tag.HAS_VALUE)) { + return kind + "(" + value + ")"; + } else { + return kind.toString(); + } + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Token token = (Token) o; + if (start != token.start) return false; + if (end != token.end) return false; + if (kind != token.kind) return false; + return Objects.equals(value, token.value); + } + + @Override + public int hashCode() { + int result = kind.hashCode(); + result = 31 * result + (value != null ? value.hashCode() : 0); + result = 31 * result + start; + result = 31 * result + end; + return result; + } + +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/TokenKind.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/TokenKind.java new file mode 100644 index 000000000..9343d4bb1 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/lexer/TokenKind.java @@ -0,0 +1,201 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.lexer; + +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +/** + * An enum of token kinds. Represents a single token kind. + * + *

Tokens are, commonly, a sequence of one or more continuous + * characters, like "??", "->", "!", "true", "1.0", "2.0", ...

+ * + *

Tokens do not have an specific behavior, they just group + * certain characters that can be used by the parser.

+ * + * @since 3.0.0 + */ +public enum TokenKind { + + /** End-of-file token, means that the end was reached */ + EOF, + + /** Error token, means that there was an error there */ + ERROR(Tag.HAS_VALUE), + + /** Identifier token, has a string value of the identifier name */ + IDENTIFIER(Tag.HAS_VALUE), + + /** String literal token, has a string value of its content */ + STRING(Tag.HAS_VALUE), + + /** + * Float literal token, has a string value of its content, + * which can be parsed to a floating-point number + */ + FLOAT(Tag.HAS_VALUE), + + /** 'True' literal boolean token */ + TRUE, + + /** 'False' literal boolean token */ + FALSE, + + /** The "break" keyword */ + BREAK, + + /** The "continue" keyword */ + CONTINUE, + + /** The "return" keyword */ + RETURN, + + /** The dot symbol (.) */ + DOT, + + /** The bang or exclamation symbol (!) */ + BANG, + + /** Double ampersand token (&&) */ + AMPAMP, + + /** Double bar token (||) */ + BARBAR, + + /** Less-than token (<) */ + LT, + + /** Less-than-or-equal token (<=) */ + LTE, + + /** Greater-than token (>) */ + GT, + + /** Greater-than-or-equal token (>=) */ + GTE, + + /** Equal symbol (=) */ + EQ, + + /** Equal-equal token (==) */ + EQEQ, + + /** Bang-eq token (!=) */ + BANGEQ, + + /** Star symbol (*) */ + STAR, + + /** Slash symbol (/) */ + SLASH, + + /** Plus symbol (+) */ + PLUS, + + /** Hyphen/sub symbol (-) */ + SUB, + + /** Left-parenthesis symbol "(" */ + LPAREN, + + /** Right-parenthesis symbol ")" */ + RPAREN, + + /** Left-brace symbol "{" */ + LBRACE, + + /** Right-brace symbol "}" */ + RBRACE, + + /** Question-question token (??) */ + QUESQUES, + + /** Question symbol (?) */ + QUES, + + /** Colon symbol (:) */ + COLON, + + /** Arrow token (->) */ + ARROW, + + /** Left-bracket token "[" */ + LBRACKET, + + /** Right-bracket "] */ + RBRACKET, + + /** Comma symbol (,) */ + COMMA, + + /** Semicolon symbol (;) */ + SEMICOLON; + + private final Set tags; + + TokenKind(final Tag...tags) { + this.tags = EnumSet.copyOf(Arrays.asList(tags)); + } + + TokenKind() { + this.tags = Collections.emptySet(); + } + + /** + * Determines if this token kind has a certain + * tag. + * + * @param tag The tag to check. + * @return True if this token kind is tagged with + * the given tag + * @since 3.0.0 + */ + public boolean hasTag(final @NotNull Tag tag) { + Objects.requireNonNull(tag, "tag"); + return tags.contains(tag); + } + + /** + * An enum of tags for token kinds. Tags specify + * certain features of token kinds. + * + * @since 3.0.0 + */ + public enum Tag { + + /** + * A token kind with HAS_VALUE tag will have a variable value, + * for example, double or string literal tokens have variable + * values, but they are still parsed with the same token kind. + * + * @since 3.0.0 + */ + HAS_VALUE + + } + +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/MolangParser.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/MolangParser.java new file mode 100644 index 000000000..534e5e06e --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/MolangParser.java @@ -0,0 +1,198 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.parser; + +import com.github.tartaricacid.touhoulittlemaid.molang.lexer.Cursor; +import com.github.tartaricacid.touhoulittlemaid.molang.lexer.MolangLexer; +import com.github.tartaricacid.touhoulittlemaid.molang.lexer.TokenKind; +import com.github.tartaricacid.touhoulittlemaid.molang.parser.ast.Expression; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding.ObjectBinding; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.Closeable; +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; + +/** + * Parser for the Molang language. + * + *

The parser converts token streams to expression + * streams

+ * + *

Note that this is a stream-based parser, this means + * that it will not consume the entire lexer if it doesn't + * continue having next() calls

+ * + * @since 3.0.0 + */ +public /* sealed */ interface MolangParser /* permits MolangParserImpl */ extends Closeable { + + /** + * Returns the internal lexer being used. + * + * @return The lexer for this parser. + * @since 3.0.0 + */ + @NotNull MolangLexer lexer(); + + /** + * Returns the cursor for this parser, the cursor maintains + * track of the current line and column, it is used for + * error reporting. + * + * @return The cursor. + * @since 3.0.0 + */ + default @NotNull Cursor cursor() { + //noinspection resource + return lexer().cursor(); + } + + /** + * Returns the last emitted expression (the last expression value + * returned when calling {@link MolangParser#next()}) + * + *

Requires the user to call {@link MolangParser#next()} + * at least once first.

+ * + * @return The last emitted expression + * @throws IllegalStateException If there is no current expression + * @since 3.0.0 + */ + @Nullable Expression current(); + + /** + * Parses the next expression. + * + *

This method returns {@code null} if it reaches + * the end of file and throws a {@link ParseException} + * if there is an error.

+ * + * @return The parsed expression + * @throws IOException If reading or parsing fails + * @since 3.0.0 + */ + @Nullable Expression next() throws IOException; + + /** + * Parses all the tokens until it finds a {@link TokenKind#EOF}. + * + *

After this method is called, the parser should be + * done and all next expressions will be null

+ * + * @return All the read expressions + * @throws IOException If reading or parsing fails + * @since 3.0.0 + */ + default @NotNull List parseAll() throws IOException { + List tokens = new ArrayList<>(); + Expression expr; + while ((expr = next()) != null) { + tokens.add(expr); + } + return tokens; + } + + /** + * Closes this parser and the internal {@link MolangLexer}. + * + * @throws IOException If closing fails + * @since 3.0.0 + */ + @Override + void close() throws IOException; + + /** + * Creates a new parser that will read the tokens from + * the given lexer. + * + * @param lexer The lexer + * @return The created parser + * @throws IOException If parser initialization fails. + * @since 3.0.0 + */ + static @NotNull MolangParser parser(final @NotNull MolangLexer lexer, @NotNull ObjectBinding binding) throws IOException { + return new MolangParserImpl(lexer, binding); + } + + /** + * Creates a new parser that will read the tokens from + * the given reader. + * + * @param reader The reader + * @return The created parser + * @throws IOException If parser initialization fails. + * @since 3.0.0 + */ + static @NotNull MolangParser parser(final @NotNull Reader reader, @NotNull ObjectBinding binding) throws IOException { + return parser(MolangLexer.lexer(reader), binding); + } + + + /** + * Creates a new parser that will read the tokens from + * the given string. + * + * @param string The string + * @return The created parser + * @throws IOException If parser initialization fails. + * @since 3.0.0 + */ + static @NotNull MolangParser parser(final @NotNull String string, @NotNull ObjectBinding binding) throws IOException { + return parser(MolangLexer.lexer(string), binding); + } + + /** + * Parses all the expressions from the given reader. + * + * @param reader The reader. + * @return The emitted expressions. + * @throws IOException If reading or parsing fails. + * @since 3.0.0 + */ + static @NotNull List parseAll(final @NotNull Reader reader, @NotNull ObjectBinding binding) throws IOException { + try (MolangParser parser = parser(reader, binding)) { + return parser.parseAll(); + } + } + + /** + * Parses the provided string. + * + * @param string The string. + * @return The emitted expressions. + * @throws IOException If reading or parsing fails. + * @since 3.0.0 + */ + static @NotNull List parseAll(final @NotNull String string, @NotNull ObjectBinding binding) throws IOException { + try (MolangParser parser = parser(string, binding)) { + return parser.parseAll(); + } + } + +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/MolangParserImpl.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/MolangParserImpl.java new file mode 100644 index 000000000..626098e34 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/MolangParserImpl.java @@ -0,0 +1,356 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.parser; + +import com.github.tartaricacid.touhoulittlemaid.molang.lexer.MolangLexer; +import com.github.tartaricacid.touhoulittlemaid.molang.lexer.Token; +import com.github.tartaricacid.touhoulittlemaid.molang.lexer.TokenKind; +import com.github.tartaricacid.touhoulittlemaid.molang.parser.ast.*; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding.ObjectBinding; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static java.util.Objects.requireNonNull; + +final class MolangParserImpl implements MolangParser { + private static final int PRECEDENCE_QUES = 1400; // 三元条件运算符优先级的值必须与其他运算符不同 + private static final Object UNSET_FLAG = new Object(); + + private final MolangLexer lexer; + private final ObjectBinding binding; + + // the last parsed expression, returned by next() + // we have to use Object and a flag since null is a valid value too + private @Nullable Object current = UNSET_FLAG; + + MolangParserImpl(final @NotNull MolangLexer lexer, @NotNull ObjectBinding binding) { + this.lexer = requireNonNull(lexer, "lexer"); + this.binding = requireNonNull(binding, "binding"); + } + + // + // Parses a single expression. + // Single expressions don't require a left-hand expression + // to be parsed, e.g. literals, statements, identifiers, + // wrapped expressions and execution scopes + // + @NotNull + Expression parseSingle(final @NotNull MolangLexer lexer) throws IOException { + Token token = lexer.current(); + switch (token.kind()) { + case FLOAT: + lexer.next(); + return new DoubleExpression(Double.parseDouble(token.value())); + case STRING: + lexer.next(); + return new StringExpression(token.value()); + case TRUE: + lexer.next(); + return DoubleExpression.ONE; + case FALSE: + lexer.next(); + return DoubleExpression.ZERO; + case LPAREN: + lexer.next(); + // wrapped expression: (expression) + Expression expression = parseCompoundExpression(lexer, 0); + token = lexer.current(); + if (token.kind() != TokenKind.RPAREN) { + throw new ParseException("Non closed expression", lexer.cursor()); + } + lexer.next(); + return expression; + case LBRACE: + lexer.next(); + List expressions = new ArrayList<>(); + while (true) { + expressions.add(parseCompoundExpression(lexer, 0)); + token = lexer.current(); + if (token.kind() == TokenKind.RBRACE) { + lexer.next(); + break; + } else if (token.kind() == TokenKind.EOF) { + // end reached but not closed yet, huh? + throw new ParseException( + "Found the end before the execution scope closing token", + lexer.cursor() + ); + } else if (token.kind() == TokenKind.ERROR) { + throw new ParseException("Found an invalid token (error): " + token.value(), lexer.cursor()); + } else { + if (token.kind() != TokenKind.SEMICOLON) { + throw new ParseException("Missing semicolon", lexer.cursor()); + } + lexer.next(); + } + } + return new ExecutionScopeExpression(expressions); + case BREAK: + lexer.next(); + return new StatementExpression(StatementExpression.Op.BREAK); + case CONTINUE: + lexer.next(); + return new StatementExpression(StatementExpression.Op.CONTINUE); + case IDENTIFIER: + Object lastTarget = binding.getProperty(token.value()); + if (lastTarget == null) { + throw new ParseException("Failed to get property: " + token.value(), lexer.cursor()); + } + Expression expr = IdentifierExpression.get(token.value(), lastTarget); + token = lexer.next(); + if (token.kind() == TokenKind.DOT) { + token = lexer.next(); + + if (token.kind() != TokenKind.IDENTIFIER) { + throw new ParseException("Unexpected token, expected a valid field token", lexer.cursor()); + } + + if (lastTarget instanceof ObjectBinding) { + lastTarget = ((ObjectBinding) lastTarget).getProperty(token.value()); + } else { + throw new ParseException("Illegal access to : " + token.value(), lexer.cursor()); + } + if (lastTarget == null) { + throw new ParseException("Failed to get property: " + token.value(), lexer.cursor()); + } + + expr = IdentifierExpression.get(token.value(), lastTarget); + lexer.next(); + } + + return expr; + case PLUS: + lexer.next(); + return parseCompoundExpression(lexer, UnaryExpression.Op.PLUS.precedence()); + case SUB: + lexer.next(); + return new UnaryExpression(UnaryExpression.Op.ARITHMETICAL_NEGATION, parseCompoundExpression(lexer, UnaryExpression.Op.ARITHMETICAL_NEGATION.precedence())); + case BANG: + lexer.next(); + return new UnaryExpression(UnaryExpression.Op.LOGICAL_NEGATION, parseCompoundExpression(lexer, UnaryExpression.Op.LOGICAL_NEGATION.precedence())); + case RETURN: + lexer.next(); + return new UnaryExpression(UnaryExpression.Op.RETURN, parseCompoundExpression(lexer, UnaryExpression.Op.RETURN.precedence())); + } + + throw new ParseException("Expected an expression.", lexer.cursor()); + } + + @NotNull + Expression parseCompoundExpression( + final @NotNull MolangLexer lexer, + final int lastPrecedence + ) throws IOException { + Expression expr = parseSingle(lexer); + while (true) { + final Expression compoundExpr = parseCompound(lexer, expr, lastPrecedence); + + // current token + final Token current = lexer.current(); + if (current.kind() == TokenKind.EOF || current.kind() == TokenKind.SEMICOLON) { + // found eof, stop parsing, return expr + return compoundExpr; + } else if (compoundExpr == expr) { + return expr; + } + + expr = compoundExpr; + } + } + + @NotNull + Expression parseCompound( + final @NotNull MolangLexer lexer, + final @NotNull Expression left, + final int lastPrecedence + ) throws IOException { + Token current = lexer.current(); + + switch (current.kind()) { + case RPAREN: + case EOF: + return left; + case LPAREN: { // CALL EXPRESSION: "left(" + if (left instanceof IdentifierExpression) { + lexer.next(); + final List arguments = new ArrayList<>(); + + // start reading the arguments + while (true) { + arguments.add(parseCompoundExpression(lexer, 0)); + // update current character + current = lexer.current(); + if (current.kind() == TokenKind.EOF) { + throw new ParseException("Found EOF before closing RPAREN", null); + } else if (current.kind() == TokenKind.RPAREN) { + lexer.next(); + break; + } else { + if (current.kind() != TokenKind.COMMA) { + throw new ParseException("Expected a comma", lexer.cursor()); + } + lexer.next(); + } + } + + Object target = ((IdentifierExpression) left).target(); + String name = ((IdentifierExpression) left).name(); + if (target instanceof Function) { + Function func = (Function) target; + if (!func.validateArgumentSize(arguments.size())) { + throw new ParseException("Function call to \"" + name + "\" has illegal parameter size", null); + } + return new CallExpression(func, new Function.ArgumentCollection(arguments)); + } + throw new ParseException("\"" + name + "\" is not a function", null); + } else { + if (lastPrecedence >= BinaryExpression.Op.MUL.precedence()) { + return left; + } + Expression right = parseCompoundExpression(lexer, BinaryExpression.Op.MUL.precedence()); + return new BinaryExpression(BinaryExpression.Op.MUL, left, right); + } + } + case QUES: { + // 嵌套的三元条件表达式从右往左执行 + if (lastPrecedence > PRECEDENCE_QUES) { + return left; + } + + lexer.next(); + final Expression trueValue = parseCompoundExpression(lexer, PRECEDENCE_QUES); + + if (lexer.current().kind() == TokenKind.COLON) { + // then it's a ternary expression, since there is a ':', indicating the next expression + lexer.next(); + return new TernaryConditionalExpression(left, trueValue, parseCompoundExpression(lexer, PRECEDENCE_QUES)); + } else { + return new BinaryExpression(BinaryExpression.Op.CONDITIONAL, left, trueValue); + } + } + } + + if (current.kind() == TokenKind.DOT) { + current = lexer.next(); + if (current.kind() == TokenKind.IDENTIFIER) { + lexer.next(); + return new StructAccessExpression(left, current.value()); + } else { + throw new ParseException("Expect a identifier after struct access operator", lexer.cursor()); + } + } + + // check for binary expressions + final BinaryExpression.Op op; + + // @formatter:off + // I wish this was java 17 + switch (current.kind()) { + case AMPAMP: op = BinaryExpression.Op.AND; break; + case BARBAR: op = BinaryExpression.Op.OR; break; + case LT: op = BinaryExpression.Op.LT; break; + case LTE: op = BinaryExpression.Op.LTE; break; + case GT: op = BinaryExpression.Op.GT; break; + case GTE: op = BinaryExpression.Op.GTE; break; + case PLUS: op = BinaryExpression.Op.ADD; break; + case SUB: op = BinaryExpression.Op.SUB; break; + case STAR: op = BinaryExpression.Op.MUL; break; + case SLASH: op = BinaryExpression.Op.DIV; break; + case QUESQUES: op = BinaryExpression.Op.NULL_COALESCE; break; + case EQ: op = BinaryExpression.Op.ASSIGN; break; + case EQEQ: op = BinaryExpression.Op.EQ; break; + case BANGEQ: op = BinaryExpression.Op.NEQ; break; + case ARROW: op = BinaryExpression.Op.ARROW; break; + default: return left; + } + // @formatter:on + + final int precedence = op.precedence(); + if (lastPrecedence >= precedence) { + return left; + } + + lexer.next(); + return new BinaryExpression(op, left, parseCompoundExpression(lexer, precedence)); + } + + @Override + public @NotNull MolangLexer lexer() { + return lexer; + } + + @Override + public @Nullable Expression current() { + if (current == UNSET_FLAG) { + throw new IllegalStateException("No current parsed expression, call next() at least once!"); + } + return (Expression) current; + } + + @Override + public @Nullable Expression next() throws IOException { + final Expression expr = next0(); + current = expr; + return expr; + } + + // + // Parses an expression until it finds an unexpected token, + // a semicolon, or an end-of-file token. + // + private @Nullable Expression next0() throws IOException { + Token token = lexer.next(); + + if (token.kind() == TokenKind.EOF) { + // reached end-of-file! + return null; + } + + if (token.kind() == TokenKind.ERROR) { + // tokenization error! + throw new ParseException("Found an invalid token (error): " + token.value(), cursor()); + } + + final Expression expression = parseCompoundExpression(lexer, -10); + + // check current token, should be a semicolon or an eof + token = lexer.current(); + if (token.kind() != TokenKind.EOF && token.kind() != TokenKind.SEMICOLON) { + throw new ParseException("Expected a semicolon, but was " + token, lexer.cursor()); + } + + return expression; + } + + @Override + public void close() throws IOException { + this.lexer.close(); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ParseException.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ParseException.java new file mode 100644 index 000000000..c703068f2 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ParseException.java @@ -0,0 +1,72 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.parser; + +import com.github.tartaricacid.touhoulittlemaid.molang.lexer.Cursor; + +import java.io.IOException; + +/** + * Exception that can be thrown during the + * parsing phase + * + * @since 3.0.0 + */ +public class ParseException extends IOException { + + private final Cursor cursor; + + public ParseException(Cursor cursor) { + this.cursor = cursor; + } + + public ParseException(String message, Cursor cursor) { + super(appendCursor(message, cursor)); + this.cursor = cursor; + } + + public ParseException(Throwable cause, Cursor cursor) { + super(cause); + this.cursor = cursor; + } + + public ParseException(String message, Throwable cause, Cursor cursor) { + super(appendCursor(message, cursor), cause); + this.cursor = cursor; + } + + public Cursor cursor() { + return cursor; + } + + private static String appendCursor(String message, Cursor cursor) { + if (cursor == null) return message; // todo + // default format for exception messages, i.e. + // "unexpected token: '%'" + // " at line 2, column 6" + return message + "\n at " + cursor.toString(); + } + +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/AssignableVariableExpression.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/AssignableVariableExpression.java new file mode 100644 index 000000000..face2b18f --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/AssignableVariableExpression.java @@ -0,0 +1,42 @@ +package com.github.tartaricacid.touhoulittlemaid.molang.parser.ast; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.AssignableVariable; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +public class AssignableVariableExpression implements Expression { + private final AssignableVariable target; + + public AssignableVariableExpression(AssignableVariable target) { + Objects.requireNonNull(target, "target"); + this.target = target; + } + + public AssignableVariable target() { + return target; + } + + @Override + public R visit(final @NotNull ExpressionVisitor visitor) { + return visitor.visitAssignableVariable(this); + } + + @Override + public String toString() { + return target.toString(); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AssignableVariableExpression that = (AssignableVariableExpression) o; + return target.equals(that.target); + } + + @Override + public int hashCode() { + return target.hashCode(); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/BinaryExpression.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/BinaryExpression.java new file mode 100644 index 000000000..814e2ead2 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/BinaryExpression.java @@ -0,0 +1,151 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.parser.ast; + +import org.jetbrains.annotations.NotNull; + +import static java.util.Objects.requireNonNull; + +/** + * Expression implementation for binary expressions + * (expressions composed by two other expressions) + * + *

Example binary expressions: {@code 1 + 1}, {@code 5 * 9}, + * {@code a == b}, {@code a < b}, {@code true ?? false}

+ * + * @since 3.0.0 + */ +public final class BinaryExpression implements Expression { + + private final Op op; + private final Expression left; + private final Expression right; + + public BinaryExpression( + final @NotNull Op op, + final @NotNull Expression left, + final @NotNull Expression right + ) { + this.op = requireNonNull(op, "op"); + this.left = requireNonNull(left, "left"); + this.right = requireNonNull(right, "right"); + } + + /** + * Gets the binary expression type/operation. + * + * @return The expression operation. + * @since 3.0.0 + */ + public @NotNull Op op() { + return op; + } + + /** + * Gets the left-hand expression for this + * binary expression. + * + * @return The left-hand expression + * @since 3.0.0 + */ + public @NotNull Expression left() { + return left; + } + + /** + * Gets the right-hand expression for this + * binary expression. + * + * @return The right-hand expression + * @since 3.0.0 + */ + public @NotNull Expression right() { + return right; + } + + @Override + public R visit(final @NotNull ExpressionVisitor visitor) { + return visitor.visitBinary(this); + } + + @Override + public String toString() { + return op.name() + "(" + left + ", " + right + ")"; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BinaryExpression that = (BinaryExpression) o; + if (op != that.op) return false; + if (!left.equals(that.left)) return false; + return right.equals(that.right); + } + + @Override + public int hashCode() { + int result = op.hashCode(); + result = 31 * result + left.hashCode(); + result = 31 * result + right.hashCode(); + return result; + } + + public enum Op { + AND(1800, 0), + OR(1600, 1), + LT(2200, 2), + LTE(2200, 3), + GT(2200, 4), + GTE(2200, 5), + ADD(2400, 6), + SUB(2400, 7), + MUL(2600, 8), + DIV(2600, 9), + ARROW(3000, 10), // ? + NULL_COALESCE(1200, 11), + ASSIGN(1, 12), + CONDITIONAL(1400, 13), // ? + EQ(2000, 14), + NEQ(2000, 15); + + private final int precedence; + private final int index; + + Op(final int precedence, final int index) { + this.precedence = precedence; + this.index = index; + } + + public int precedence() { + return precedence; + } + + public int index() { + return index; + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/CallExpression.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/CallExpression.java new file mode 100644 index 000000000..cb3dd3c62 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/CallExpression.java @@ -0,0 +1,98 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.parser.ast; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; +import org.jetbrains.annotations.NotNull; + +import static java.util.Objects.requireNonNull; + +/** + * Call expression implementation, executes function + * with certain arguments. + * + *

Example call expressions: {@code print('hello')}, + * {@code math.sqrt(9)}, {@code math.pow(3, 2)}

+ * + * @since 3.0.0 + */ +public final class CallExpression implements Expression { + + private final Function function; + private final Function.ArgumentCollection arguments; + + public CallExpression( + final @NotNull Function function, + final @NotNull Function.ArgumentCollection arguments + ) { + this.function = requireNonNull(function, "function"); + this.arguments = requireNonNull(arguments, "arguments"); + } + + /** + * Gets the function expression. + * + * @since 3.0.0 + */ + public @NotNull Function function() { + return function; + } + + /** + * Gets the list of arguments to pass to + * the function. + * + * @since 3.0.0 + */ + public @NotNull Function.ArgumentCollection arguments() { + return arguments; + } + + @Override + public R visit(final @NotNull ExpressionVisitor visitor) { + return visitor.visitCall(this); + } + + @Override + public String toString() { + return "Call(" + function + ", " + arguments + ")"; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CallExpression that = (CallExpression) o; + if (!function.equals(that.function)) return false; + return arguments.equals(that.arguments); + } + + @Override + public int hashCode() { + int result = function.hashCode(); + result = 31 * result + arguments.hashCode(); + return result; + } +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/DoubleExpression.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/DoubleExpression.java new file mode 100644 index 000000000..134702560 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/DoubleExpression.java @@ -0,0 +1,82 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.parser.ast; + +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * Literal double expression implementation for Molang + * numerical values. + * + *

Example double expressions: {@code 2.0}, {@code 59}, {@code 20}, {@code 5.002}

+ * + * @since 3.0.0 + */ +public final class DoubleExpression implements Expression { + + public static final DoubleExpression ZERO = new DoubleExpression(0.0D); + public static final DoubleExpression ONE = new DoubleExpression(1.0D); + + private final double value; + + public DoubleExpression(final double value) { + this.value = value; + } + + /** + * Gets the double expression value. + * + * @since 3.0.0 + */ + public double value() { + return value; + } + + @Override + public R visit(final @NotNull ExpressionVisitor visitor) { + return visitor.visitDouble(this); + } + + @Override + public String toString() { + return "Double(" + value + ")"; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DoubleExpression that = (DoubleExpression) o; + return Double.compare(that.value, value) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/ExecutionScopeExpression.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/ExecutionScopeExpression.java new file mode 100644 index 000000000..a6b5e2909 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/ExecutionScopeExpression.java @@ -0,0 +1,86 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.parser.ast; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Objects; + +/** + * Execution scope expression implementation. Execution + * scopes define a new scope and a new sequence of + * expressions to evaluate. + * + *

Execution scope expression examples: {@code { print('a'); print('b'); }}, + * {@code { doThisFirst(); thenDoThis(); }}, {@code { v.x = v.x + 1; }}

+ * + * @since 3.0.0 + */ +public final class ExecutionScopeExpression implements Expression { + + private final List expressions; + + public ExecutionScopeExpression(final @NotNull List expressions) { + this.expressions = Objects.requireNonNull(expressions, "expressions"); + } + + /** + * Returns the expressions inside this + * execution scope, never null + */ + public @NotNull List expressions() { + return expressions; + } + + @Override + public R visit(final @NotNull ExpressionVisitor visitor) { + return visitor.visitExecutionScope(this); + } + + public Function buildFunction(final @NotNull ExpressionVisitor visitor) { + return visitor.buildExecutionScopeFunction(this); + } + + @Override + public String toString() { + return "ExecutionScope(" + this.expressions + ")"; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ExecutionScopeExpression that = (ExecutionScopeExpression) o; + return expressions.equals(that.expressions); + } + + @Override + public int hashCode() { + return Objects.hash(expressions); + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/Expression.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/Expression.java new file mode 100644 index 000000000..a1661256f --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/Expression.java @@ -0,0 +1,53 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.parser.ast; + +import org.jetbrains.annotations.NotNull; + +/** + * The expression interface. It's the super-interface for + * all the expression types. + * + *

Expressions are evaluable parts of code, expressions + * are emitted by the parser.

+ * + *

In Molang, almost every expression evaluates to a numerical + * value

+ * + * @since 3.0.0 + */ +public interface Expression { + + /** + * Visits this expression with the given visitor. + * + * @param visitor The expression visitor + * @param The visit result return type + * @return The visit result + * @since 3.0.0 + */ + R visit(final @NotNull ExpressionVisitor visitor); + +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/ExpressionVisitor.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/ExpressionVisitor.java new file mode 100644 index 000000000..910917fc6 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/ExpressionVisitor.java @@ -0,0 +1,174 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.parser.ast; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; +import org.jetbrains.annotations.NotNull; + +/** + * An {@link Expression} visitor. Provides a way to add + * functionalities to the expression interface and all + * of its implementations. + * + *

See the following example on visiting an expression:

+ *
{@code
+ *      Expression expr = ...;
+ *      String str = expr.visit(new ToStringVisitor());
+ * }
+ * + *

Please note that users MUST use {@link Expression#visit(ExpressionVisitor)} + * and NOT ExpressionVisitor's {@link ExpressionVisitor#visit(Expression)}, because + * it will not work as intended.

+ * + * @param The visit result type + * @since 3.0.0 + */ +public interface ExpressionVisitor { + + /** + * Evaluate for the given unknown expression. + * + * @param expression The expression. + * @return The result. + * @since 3.0.0 + */ + R visit(final @NotNull Expression expression); + + /** + * Evaluate for double expression. + * + * @param expression The expression. + * @return The result. + * @since 3.0.0 + */ + default R visitDouble(final @NotNull DoubleExpression expression) { + return visit(expression); + } + + /** + * Evaluate for string expression. + * + * @param expression The expression. + * @return The result. + * @since 3.0.0 + */ + default R visitString(final @NotNull StringExpression expression) { + return visit(expression); + } + + /** + * Evaluate for identifier expression. + * + * @param expression The expression. + * @return The result. + * @since 3.0.0 + */ + default R visitIdentifier(final @NotNull IdentifierExpression expression) { + return visit(expression); + } + + default R visitVariable(final @NotNull VariableExpression expression) { + return visit(expression); + } + + default R visitAssignableVariable(final @NotNull AssignableVariableExpression expression) { + return visit(expression); + } + + default R visitStruct(final @NotNull StructAccessExpression expression) { + return visit(expression); + } + + /** + * Evaluate for ternary conditional expression. + * + * @param expression The expression. + * @return The result. + * @since 3.0.0 + */ + default R visitTernaryConditional(final @NotNull TernaryConditionalExpression expression) { + return visit(expression); + } + + /** + * Evaluate for unary expression. + * + * @param expression The expression. + * @return The result. + * @since 3.0.0 + */ + default R visitUnary(final @NotNull UnaryExpression expression) { + return visit(expression); + } + + /** + * Evaluate for execution scope expression. + * + * @param expression The expression. + * @return The result. + * @since 3.0.0 + */ + default R visitExecutionScope(final @NotNull ExecutionScopeExpression expression) { + return visit(expression); + } + + default Function buildExecutionScopeFunction(final @NotNull ExecutionScopeExpression expression) { + throw new UnsupportedOperationException("Unsupported expression type: " + expression); + } + + /** + * Evaluate for binary expression. + * + * @param expression The expression. + * @return The result. + * @since 3.0.0 + */ + default R visitBinary(final @NotNull BinaryExpression expression) { + return visit(expression); + } + + /** + * Evaluate for call expression. + * + * @param expression The expression. + * @return The result. + * @since 3.0.0 + */ + default R visitCall(final @NotNull CallExpression expression) { + return visit(expression); + } + + /** + * Evaluate for statement expression. + * + * @param expression The expression. + * @return The result. + * @since 3.0.0 + */ + default R visitStatement(final @NotNull StatementExpression expression) { + return visit(expression); + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/IdentifierExpression.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/IdentifierExpression.java new file mode 100644 index 000000000..0844a6e60 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/IdentifierExpression.java @@ -0,0 +1,106 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.parser.ast; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.AssignableVariable; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Variable; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * Identifier expression implementation for Molang. + * + *

Note that, identifiers in Molang are always + * case-insensitive

+ * + *

Example identifier expressions: {@code math}, + * {@code name}, {@code this}, {@code print}

+ * + * @since 3.0.0 + */ +public final class IdentifierExpression implements Expression { + + private final String name; + private final Object target; + + private IdentifierExpression(final @NotNull String name, Object target) { + Objects.requireNonNull(name, "name"); + + this.name = name.toLowerCase(); // case-insensitive + this.target = target; + } + + public static Expression get(String name, Object target) { + if(target instanceof Number) { + return new DoubleExpression(((Number) target).doubleValue()); + } else if(target instanceof String) { + return new StringExpression((String) target); + } else if(target instanceof AssignableVariable) { + return new AssignableVariableExpression((AssignableVariable) target); + } else if(target instanceof Variable) { + return new VariableExpression((Variable) target); + } + return new IdentifierExpression(name, target); + } + + /** + * Gets the identifier name. + * + * @return The identifier name. + * @since 3.0.0 + */ + public @NotNull String name() { + return name; + } + + public Object target() { + return target; + } + + @Override + public R visit(final @NotNull ExpressionVisitor visitor) { + return visitor.visitIdentifier(this); + } + + @Override + public String toString() { + return "Identifier(" + name + ")"; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + IdentifierExpression that = (IdentifierExpression) o; + return name.equals(that.name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/StatementExpression.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/StatementExpression.java new file mode 100644 index 000000000..a07b99b75 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/StatementExpression.java @@ -0,0 +1,86 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.parser.ast; + +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * Statement expression implementation. Statement expressions + * do not have children expressions, they just have a single + * operation type. + * + *

Example statement expressions: {@code break}, {@code continue}

+ * + * @since 3.0.0 + */ +public final class StatementExpression implements Expression { + + private final Op op; + + public StatementExpression(final @NotNull Op op) { + this.op = Objects.requireNonNull(op, "op"); + } + + /** + * Gets the operation/type of this statement. + * + * @return The statement operation/type. + * @since 3.0.0 + */ + public @NotNull Op op() { + return op; + } + + @Override + public R visit(final @NotNull ExpressionVisitor visitor) { + return visitor.visitStatement(this); + } + + + /** + * Enum containing all the possible operations/types + * of statement expressions. + * + * @since 3.0.0 + */ + public enum Op { + /** + * The break statement type + * + * @since 3.0.0 + */ + BREAK, + + /** + * The continue statement type + * + * @since 3.0.0 + */ + CONTINUE + } + +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/StringExpression.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/StringExpression.java new file mode 100644 index 000000000..a1d2577a4 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/StringExpression.java @@ -0,0 +1,79 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.parser.ast; + +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * String literal expression implementation for Molang. + * + *

Example string expressions: {@code 'hello world'}, + * {@code 'hey there'}, {@code 'name'}, {@code 'the game'}

+ * + * @since 3.0.0 + */ +public final class StringExpression implements Expression { + + private final String value; + + public StringExpression(final @NotNull String value) { + this.value = Objects.requireNonNull(value, "value"); + } + + /** + * Gets the string value for this expression. + * + * @return The string value. + * @since 3.0.0 + */ + public @NotNull String value() { + return value; + } + + @Override + public R visit(final @NotNull ExpressionVisitor visitor) { + return visitor.visitString(this); + } + @Override + public String toString() { + return "String('" + value + "')"; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StringExpression that = (StringExpression) o; + return value.equals(that.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/StructAccessExpression.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/StructAccessExpression.java new file mode 100644 index 000000000..dbd3c551a --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/StructAccessExpression.java @@ -0,0 +1,27 @@ +package com.github.tartaricacid.touhoulittlemaid.molang.parser.ast; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.util.StringPool; +import org.jetbrains.annotations.NotNull; + +public class StructAccessExpression implements Expression { + private final Expression left; + private final int path; + + public StructAccessExpression(Expression left, String path) { + this.left = left; + this.path = StringPool.computeIfAbsent(path); + } + + @Override + public R visit(@NotNull ExpressionVisitor visitor) { + return visitor.visitStruct(this); + } + + public Expression left() { + return left; + } + + public int path() { + return path; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/TernaryConditionalExpression.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/TernaryConditionalExpression.java new file mode 100644 index 000000000..19de761dd --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/TernaryConditionalExpression.java @@ -0,0 +1,119 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.parser.ast; + +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +/** + * Ternary conditional expression implementation, similar to + * "if {...} else {...}" expressions in other languages. + * + *

If the {@code conditional} expression evaluates to a + * truthy value, then {@code trueExpression} is evaluated + * as the result, otherwise, {@code falseExpression} is.

+ * + *

Example ternary conditional expressions: {@code true ? 1 : 0}, + * {@code (age > 18) ? 'adult' : 'minor'}, {@code open ? 'open' : 'closed'}

+ * + * @since 3.0.0 + */ +public final class TernaryConditionalExpression implements Expression { + + private final Expression conditional; + private final Expression trueExpression; + private final Expression falseExpression; + + public TernaryConditionalExpression( + final @NotNull Expression conditional, + final @NotNull Expression trueExpression, + final @NotNull Expression falseExpression + ) { + this.conditional = requireNonNull(conditional, "conditional"); + this.trueExpression = requireNonNull(trueExpression, "trueExpression"); + this.falseExpression = requireNonNull(falseExpression, "falseExpression"); + } + + /** + * Gets the expression condition. + * + * @since 3.0.0 + */ + public @NotNull Expression condition() { + return conditional; + } + + /** + * Gets the expression that should be used when + * condition is evaluated as a truthy value. + * + * @since 3.0.0 + */ + public @NotNull Expression trueExpression() { + return trueExpression; + } + + + /** + * Gets the expression that should be used when + * condition is evaluated as a falsy value. + * + * @since 3.0.0 + */ + public @NotNull Expression falseExpression() { + return falseExpression; + } + + @Override + public R visit(final @NotNull ExpressionVisitor visitor) { + return visitor.visitTernaryConditional(this); + } + + @Override + public String toString() { + return "TernaryCondition(" + conditional + ", " + + trueExpression + ", " + + falseExpression + ")"; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TernaryConditionalExpression that = (TernaryConditionalExpression) o; + return conditional.equals(that.conditional) + && trueExpression.equals(that.trueExpression) + && falseExpression.equals(that.falseExpression); + } + + @Override + public int hashCode() { + return Objects.hash(conditional, trueExpression, falseExpression); + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/UnaryExpression.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/UnaryExpression.java new file mode 100644 index 000000000..ac67fe452 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/UnaryExpression.java @@ -0,0 +1,118 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.parser.ast; + +import org.jetbrains.annotations.NotNull; + +import static java.util.Objects.requireNonNull; + +/** + * Unary expression implementation, performs a single operation + * to a single expression, like logical negation, arithmetical + * negation, or "return expr;". + * + *

Example unary expressions: {@code -hello}, {@code !p}, + * {@code !q}, {@code -(10 * 5)}, {@code return this}, + * {@code return 5}

+ * + * @since 3.0.0 + */ +public final class UnaryExpression implements Expression { + + private final Op op; + private final Expression expression; + + public UnaryExpression( + final @NotNull Op op, + final @NotNull Expression expression + ) { + this.op = requireNonNull(op, "op"); + this.expression = requireNonNull(expression, "expression"); + } + + /** + * Gets the unary expression operation. + * + * @return The unary expression operation. + * @since 3.0.0 + */ + public @NotNull Op op() { + return op; + } + + /** + * Gets the operated expression. + * + * @return The operated expression. + * @since 3.0.0 + */ + public @NotNull Expression expression() { + return expression; + } + + @Override + public R visit(final @NotNull ExpressionVisitor visitor) { + return visitor.visitUnary(this); + } + + @Override + public String toString() { + return "Unary(" + op + ")(" + expression + ")"; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + UnaryExpression that = (UnaryExpression) o; + if (op != that.op) return false; + return expression.equals(that.expression); + } + + @Override + public int hashCode() { + int result = op.hashCode(); + result = 31 * result + expression.hashCode(); + return result; + } + + public enum Op { + LOGICAL_NEGATION(2800), + ARITHMETICAL_NEGATION(2800), + PLUS(2800), + RETURN(-1); + + final int precedence; + + Op(int precedence) { + this.precedence = precedence; + } + + public int precedence() { + return precedence; + } + } + +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/VariableExpression.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/VariableExpression.java new file mode 100644 index 000000000..8d800cab6 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/parser/ast/VariableExpression.java @@ -0,0 +1,42 @@ +package com.github.tartaricacid.touhoulittlemaid.molang.parser.ast; + +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Variable; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +public class VariableExpression implements Expression { + private final Variable target; + + public VariableExpression(Variable target) { + Objects.requireNonNull(target, "target"); + this.target = target; + } + + public Variable target() { + return target; + } + + @Override + public R visit(final @NotNull ExpressionVisitor visitor) { + return visitor.visitVariable(this); + } + + @Override + public String toString() { + return target.toString(); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + VariableExpression that = (VariableExpression) o; + return target.equals(that.target); + } + + @Override + public int hashCode() { + return target.hashCode(); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/AssignableVariable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/AssignableVariable.java new file mode 100644 index 000000000..a94ee03c7 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/AssignableVariable.java @@ -0,0 +1,7 @@ +package com.github.tartaricacid.touhoulittlemaid.molang.runtime; + +import org.jetbrains.annotations.NotNull; + +public interface AssignableVariable extends Variable { + void assign(final @NotNull ExecutionContext context, Object value); +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/ExecutionContext.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/ExecutionContext.java new file mode 100644 index 000000000..52c1288c4 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/ExecutionContext.java @@ -0,0 +1,35 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.runtime; + +import com.github.tartaricacid.touhoulittlemaid.molang.parser.ast.Expression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ExecutionContext { + TEntity entity(); + + @Nullable Object eval(final @NotNull Expression expression); +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/ExpressionEvaluator.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/ExpressionEvaluator.java new file mode 100644 index 000000000..857b411ce --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/ExpressionEvaluator.java @@ -0,0 +1,107 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.runtime; + +import com.github.tartaricacid.touhoulittlemaid.molang.parser.ast.Expression; +import com.github.tartaricacid.touhoulittlemaid.molang.parser.ast.ExpressionVisitor; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding.ObjectBinding; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * An {@link ExpressionVisitor} implementation that evaluates + * (interprets) the expressions it visits and returns a single + * value, commonly, a double value. + * + * @since 3.0.0 + */ +@SuppressWarnings("rawtypes") +public /* sealed */ interface ExpressionEvaluator /* permits ExpressionEvaluatorImpl */ extends ExecutionContext, ExpressionVisitor { + /** + * Creates a new {@link ExpressionEvaluator} instance with + * the given bindings. + * + * @param entity The entity object + * @return The created expression evaluator. + * @since 3.0.0 + */ + static @NotNull ExpressionEvaluator evaluator(final @Nullable TEntity entity) { + return new ExpressionEvaluatorImpl<>(entity); + } + + /** + * Creates a new {@link ExpressionEvaluator} instance + * without bindings. + * + * @return The created expression evaluator. + * @since 3.0.0 + */ + static @NotNull ExpressionEvaluator evaluator() { + return evaluator(ObjectBinding.EMPTY); + } + + @Override + default @Nullable Object eval(final @NotNull Expression expression) { + return expression.visit(this); + } + + /** + * Creates a new, child, expression evaluator. + * + *

Child evaluators have all the bindings of + * their parents and may have extra bindings.

+ * + *

Child evaluators have their own stack.

+ * + * @return The child expression evaluator. + * @since 3.0.0 + */ + @NotNull ExpressionEvaluator createChild(); + + /** + * Creates a new, child, expression evaluator. + * + *

Child evaluators have all the bindings of + * their parents and may have extra bindings.

+ * + *

Child evaluators have their own stack.

+ * + * @param entity The new entity value + * @return The child expression evaluator. + * @since 3.0.0 + */ + @NotNull ExpressionEvaluator createChild(final @Nullable TNewEntity entity); + + /** + * Pops the return value, set by the last "return" + * expression. + * + * @return The return value, null if no "return" + * expression is found. + * @since 3.0.0 + */ + @Nullable Object popReturnValue(); + +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/ExpressionEvaluatorImpl.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/ExpressionEvaluatorImpl.java new file mode 100644 index 000000000..595e69df0 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/ExpressionEvaluatorImpl.java @@ -0,0 +1,335 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.runtime; + +import com.github.tartaricacid.touhoulittlemaid.molang.parser.ast.*; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding.ValueConversions; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +@SuppressWarnings("rawtypes,unchecked") +public final class ExpressionEvaluatorImpl implements ExpressionEvaluator { + private static final Evaluator[] BINARY_EVALUATORS = { + bool((a, b) -> a.eval() && b.eval()), + bool((a, b) -> a.eval() || b.eval()), + compare((a, b) -> a.eval() < b.eval()), + compare((a, b) -> a.eval() <= b.eval()), + compare((a, b) -> a.eval() > b.eval()), + compare((a, b) -> a.eval() >= b.eval()), + (evaluator, a, b) -> { + final Object aVal = a.visit(evaluator); + final Object bVal = b.visit(evaluator); + return ValueConversions.asFloat(aVal) + ValueConversions.asFloat(bVal); + }, + arithmetic((a, b) -> a.eval() - b.eval()), + arithmetic((a, b) -> a.eval() * b.eval()), + arithmetic((a, b) -> { + // Molang allows division by zero, + // which is always equal to 0 + float dividend = a.eval(); + float divisor = b.eval(); + if (divisor == 0) return 0; + else return dividend / divisor; + }), + (evaluator, a, b) -> { // arrow + final Object val = a.visit(evaluator); + if (val == null) { + return null; + } else { + return b.visit(evaluator.createChild(val)); + } + }, + (evaluator, a, b) -> { // null coalesce + Object val = a.visit(evaluator); + if (val == null) { + return b.visit(evaluator); + } else { + return val; + } + }, + (evaluator, a, b) -> { // assignation + Object val = b.visit(evaluator); + if (a instanceof AssignableVariableExpression) { + AssignableVariable var = ((AssignableVariableExpression) a).target(); + if (val instanceof Struct) { + val = ((Struct) val).copy(); + } + var.assign(evaluator, val); + } else if (a instanceof StructAccessExpression) { + if (val instanceof Struct) { + // 不允许结构体嵌套 + return val; + } + StructAccessExpression exp = (StructAccessExpression) a; + Object value = exp.left().visit(evaluator); + if (value == null) { + if (exp.left() instanceof AssignableVariableExpression) { + AssignableVariable variable = ((AssignableVariableExpression) exp.left()).target(); + Struct struct = new HashMapStruct(); + struct.putProperty(exp.path(), val); + variable.assign(evaluator, struct); + } + } else if (value instanceof Struct) { + ((Struct) value).putProperty(exp.path(), val); + } + } + // TODO: (else case) This isn't fail-fast, we can only assign to access expressions + return val; + }, + (evaluator, a, b) -> { // conditional + Object condition = a.visit(evaluator); + if (ValueConversions.asBoolean(condition)) { + return b.visit(evaluator); + } + return null; + }, + (evaluator, a, b) -> { + Object left = a.visit(evaluator); + Object right = b.visit(evaluator); + if (left == right) + return true; + if (right == null) + return false; + if (right instanceof Number) { + return ValueConversions.asFloat(right) == ValueConversions.asFloat(left); + } else if (right instanceof String) { + return right.equals(left); + } + return false; + }, // eq + (evaluator, a, b) -> { + Object left = a.visit(evaluator); + Object right = b.visit(evaluator); + if (left == right) + return false; + if (right == null) + return true; + if (right instanceof Number) { + return ValueConversions.asFloat(right) != ValueConversions.asFloat(left); + } else if (right instanceof String) { + return !right.equals(left); + } + return false; + } + }; + + private final TEntity entity; + private @Nullable Object returnValue; + + public ExpressionEvaluatorImpl(final @Nullable TEntity entity) { + this.entity = entity; + } + + private static Evaluator bool(BooleanOperator op) { + return (evaluator, a, b) -> op.operate( + () -> ValueConversions.asBoolean(a.visit(evaluator)), + () -> ValueConversions.asBoolean(b.visit(evaluator)) + ); + } + + private static Evaluator compare(Comparator comp) { + return (evaluator, a, b) -> comp.compare( + () -> ValueConversions.asFloat(a.visit(evaluator)), + () -> ValueConversions.asFloat(b.visit(evaluator)) + ); + } + + private static Evaluator arithmetic(ArithmeticOperator op) { + return (evaluator, a, b) -> op.operate( + () -> ValueConversions.asFloat(a.visit(evaluator)), + () -> ValueConversions.asFloat(b.visit(evaluator)) + ); + } + + @Override + public TEntity entity() { + return entity; + } + + @Override + public @NotNull ExpressionEvaluator createChild(final @Nullable TNewEntity entity) { + return new ExpressionEvaluatorImpl<>(entity); + } + + @Override + public @NotNull ExpressionEvaluator createChild() { + // Note that it will have its own returnValue, but same bindings + // (Should we create new bindings?) + return new ExpressionEvaluatorImpl<>(this.entity); + } + + @Override + public @Nullable Object popReturnValue() { + Object val = this.returnValue; + this.returnValue = null; + return val; + } + + @Override + public @Nullable Object visitCall(final @NotNull CallExpression expression) { + final Function function = expression.function(); + return function.evaluate(this, expression.arguments()); + } + + @Override + public Object visitDouble(@NotNull DoubleExpression expression) { + return expression.value(); + } + + @Override + public Object visitExecutionScope(@NotNull ExecutionScopeExpression executionScope) { + return buildExecutionScopeFunction(executionScope).evaluate(this, Function.EMPTY_ARGUMENT); + } + + @Override + public Function buildExecutionScopeFunction(final @NotNull ExecutionScopeExpression executionScope) { + List expressions = executionScope.expressions(); + var evaluatorForThisScope = createChild(); + return (context, arguments) -> { + Object lastResult = null; + for (Expression expression : expressions) { + // eval expression, ignore result + lastResult = evaluatorForThisScope.eval(expression); + // check for return values + Object returnValue = evaluatorForThisScope.popReturnValue(); + if (returnValue != null) { + return returnValue; + } + } + return lastResult; + }; + } + + @Override + public Object visitIdentifier(@NotNull IdentifierExpression expression) { + throw new RuntimeException("Unknown identifier type"); + } + + @Override + public Object visitVariable(final @NotNull VariableExpression expression) { + return expression.target().evaluate(this); + } + + public Object visitAssignableVariable(final @NotNull AssignableVariableExpression expression) { + return expression.target().evaluate(this); + } + + @Override + public Object visitStruct(final @NotNull StructAccessExpression expression) { + Object value = expression.left().visit(this); + if (value instanceof Struct) { + return ((Struct) value).getProperty(expression.path()); + } else { + return null; + } + } + + @Override + public Object visitBinary(@NotNull BinaryExpression expression) { + return BINARY_EVALUATORS[expression.op().index()].eval( + this, + expression.left(), + expression.right() + ); + } + + @Override + public Object visitUnary(@NotNull UnaryExpression expression) { + Object value = expression.expression().visit(this); + switch (expression.op()) { + case LOGICAL_NEGATION: + return !ValueConversions.asBoolean(value); + case ARITHMETICAL_NEGATION: + return -ValueConversions.asFloat(value); + case RETURN: { + this.returnValue = value; + return 0D; + } + default: + throw new IllegalStateException("Unknown operation"); + } + } + + @Override + public Object visitStatement(@NotNull StatementExpression expression) { + switch (expression.op()) { + case BREAK: { + this.returnValue = StatementExpression.Op.BREAK; + break; + } + case CONTINUE: { + this.returnValue = StatementExpression.Op.CONTINUE; + break; + } + } + return null; + } + + @Override + public Object visitString(@NotNull StringExpression expression) { + return expression.value(); + } + + @Override + public Object visitTernaryConditional(@NotNull TernaryConditionalExpression expression) { + Object obj = expression.condition().visit(this); + obj = ValueConversions.asBoolean(obj) + ? expression.trueExpression().visit(this) + : expression.falseExpression().visit(this); + return obj; + } + + @Override + public Object visit(@NotNull Expression expression) { + throw new UnsupportedOperationException("Unsupported expression type: " + expression); + } + + private interface Evaluator { + Object eval(ExpressionEvaluator evaluator, Expression a, Expression b); + } + + private interface BooleanOperator { + boolean operate(LazyEvaluableBoolean a, LazyEvaluableBoolean b); + } + + interface LazyEvaluableBoolean { + boolean eval(); + } + + interface LazyEvaluableFloat { + float eval(); + } + + private interface Comparator { + boolean compare(LazyEvaluableFloat a, LazyEvaluableFloat b); + + } + + private interface ArithmeticOperator { + float operate(LazyEvaluableFloat a, LazyEvaluableFloat b); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/Function.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/Function.java new file mode 100644 index 000000000..01254ca20 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/Function.java @@ -0,0 +1,103 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.runtime; + +import com.github.tartaricacid.touhoulittlemaid.molang.parser.ast.Expression; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding.ValueConversions; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a Molang function. Receives a certain amount of + * parameters and (optionally) returns a value. Can be called + * from Molang code using call expressions: {@code my_function(1, 2, 3)} + * + *

This is a very low-level function that is "expression-sensitive", + * this means, it takes the raw expression arguments instead of the + * evaluated expression argument values.

+ * + * @since 3.0.0 + */ +@FunctionalInterface +public interface Function { + /** + * Executes this function with the given arguments. + * + * @param context The execution context + * @param arguments The arguments + * @return The function result + * @since 3.0.0 + */ + @Nullable Object evaluate(final @NotNull ExecutionContext context, final @NotNull ArgumentCollection arguments); + + default boolean validateArgumentSize(int size) { + return true; + } + + ArgumentCollection EMPTY_ARGUMENT = new ArgumentCollection(new ArrayList<>()); + + class ArgumentCollection { + private final List arguments; + + public ArgumentCollection(List arguments) { + this.arguments = arguments; + } + + public int size() { + return arguments.size(); + } + + public String getAsString(@NotNull ExecutionContext ctx, final int index) { + return ValueConversions.asString(ctx.eval(arguments.get(index))); + } + + public double getAsDouble(@NotNull ExecutionContext ctx, final int index) { + return ValueConversions.asDouble(ctx.eval(arguments.get(index))); + } + + public int getAsInt(@NotNull ExecutionContext ctx, final int index) { + return ValueConversions.asInt(ctx.eval(arguments.get(index))); + } + + public float getAsFloat(@NotNull ExecutionContext ctx, final int index) { + return ValueConversions.asFloat(ctx.eval(arguments.get(index))); + } + + public boolean getAsBoolean(@NotNull ExecutionContext ctx, final int index) { + return ValueConversions.asBoolean(ctx.eval(arguments.get(index))); + } + + public Object getValue(@NotNull ExecutionContext ctx, final int index) { + return ctx.eval(arguments.get(index)); + } + + public Expression getExpression(final int index) { + return arguments.get(index); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/HashMapStruct.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/HashMapStruct.java new file mode 100644 index 000000000..5af86a5e8 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/HashMapStruct.java @@ -0,0 +1,59 @@ +package com.github.tartaricacid.touhoulittlemaid.molang.runtime; + +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.util.PooledStringHashMap; +import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.molang.util.StringPool; +import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; + +public class HashMapStruct implements Struct { + private final PooledStringHashMap properties; + private final boolean isRightValue; + + public HashMapStruct() { + this(false); + } + + public HashMapStruct(boolean isRightValue) { + this.properties = new PooledStringHashMap<>(); + this.isRightValue = isRightValue; + } + + private HashMapStruct(PooledStringHashMap properties) { + this.properties = properties; + this.isRightValue = false; + } + + @Override + public Object getProperty(int name) { + return properties.get(name); + } + + @Override + public void putProperty(int name, Object value) { + properties.put(name, value); + } + + @Override + public Struct copy() { + if (isRightValue) { + return new HashMapStruct(properties); + } else { + return new HashMapStruct(new PooledStringHashMap<>(properties)); + } + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("struct{"); + boolean first = true; + for (Int2ReferenceMap.Entry entry : properties.int2ReferenceEntrySet()) { + if (!first) { + builder.append(", "); + } + first = false; + builder.append(String.format("%s=%s", StringPool.getString(entry.getIntKey()), entry.getValue() == null ? "null" : entry.getValue().toString())); + } + builder.append("}"); + return builder.toString(); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/Struct.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/Struct.java new file mode 100644 index 000000000..4876ff265 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/Struct.java @@ -0,0 +1,10 @@ +package com.github.tartaricacid.touhoulittlemaid.molang.runtime; + +public interface Struct { + Object getProperty(int name); + + void putProperty(int name, Object value); + + // FIXME: 作为 foreign 变量被访问时有线程安全问题 + Struct copy(); +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/Variable.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/Variable.java new file mode 100644 index 000000000..a21f5a4c8 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/Variable.java @@ -0,0 +1,8 @@ +package com.github.tartaricacid.touhoulittlemaid.molang.runtime; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface Variable { + @Nullable Object evaluate(final @NotNull ExecutionContext context); +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/binding/ObjectBinding.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/binding/ObjectBinding.java new file mode 100644 index 000000000..525aa82ab --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/binding/ObjectBinding.java @@ -0,0 +1,41 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding; + +/** + * Represents an object-like binding, + * these objects can have properties + * (or fields) that can be read and + * sometimes written + */ +public interface ObjectBinding { + ObjectBinding EMPTY = name -> null; + + /** + * Gets the property value in this + * object with the given {@code name} + */ + Object getProperty(String name); +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/binding/StandardBindings.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/binding/StandardBindings.java new file mode 100644 index 000000000..bf45372fc --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/binding/StandardBindings.java @@ -0,0 +1,119 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding; + +import com.github.tartaricacid.touhoulittlemaid.molang.parser.ast.*; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.AssignableVariable; +import com.github.tartaricacid.touhoulittlemaid.molang.runtime.Function; + +import java.util.Arrays; + +/** + * Class holding some default bindings and + * static utility methods for ease working + * with bindings + */ +public final class StandardBindings { + private static final int MAX_LOOP_ROUND = 1024; + + public static final Function LOOP_FUNC = (ctx, args) -> { + // Parameters: + // - double: How many times should we loop + // - CallableBinding: The looped expressions + + if (args.size() < 2) { + return null; + } + + int n = Math.min((int) Math.round(args.getAsDouble(ctx, 0)), MAX_LOOP_ROUND); + Object expr = args.getExpression(1); + + if (expr instanceof ExecutionScopeExpression) { + Function callable = ((ExecutionScopeExpression) expr).buildFunction((ExpressionVisitor) ctx); + if (callable != null) { + for (int i = 0; i < n; i++) { + Object value = callable.evaluate(ctx, Function.EMPTY_ARGUMENT); + if (value == StatementExpression.Op.BREAK) { + break; + } + // (not necessary, callable already exits when returnValue + // is set to any non-null value) + // if (value == StatementExpression.Op.CONTINUE) continue; + } + } + } + return null; + }; + + public static final Function FOR_EACH_FUNC = (ctx, args) -> { + // Parameters: + // - any: Variable + // - array: Any array + // - CallableBinding: The looped expressions + + if (args.size() < 3) { + return null; + } + + final Expression variableExpr = args.getExpression(0); + if (!(variableExpr instanceof AssignableVariableExpression)) { + // first argument must be an access expression, + // e.g. 'variable.test', 'v.pig', 't.entity' or + // 't.entity.location.world' + return null; + } + final AssignableVariable variableAccess = ((AssignableVariableExpression) variableExpr).target(); + + final Object array = args.getValue(ctx, 1); + final Iterable arrayIterable; + if (array instanceof Object[]) { + arrayIterable = Arrays.asList((Object[]) array); + } else if (array instanceof Iterable) { + arrayIterable = (Iterable) array; + } else { + // second argument must be an array or iterable + return null; + } + + final Expression expr = args.getExpression(2); + + if (expr instanceof ExecutionScopeExpression) { + Function callable = ((ExecutionScopeExpression) expr).buildFunction((ExpressionVisitor) ctx); + if (callable != null) { + for (final Object val : arrayIterable) { + // set 'val' as current value + // eval (objectExpr.propertyName = val) + variableAccess.assign(ctx, val); + final Object returnValue = callable.evaluate(ctx, Function.EMPTY_ARGUMENT); + + if (returnValue == StatementExpression.Op.BREAK) { + break; + } + } + } + } + return null; + }; +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/binding/ValueConversions.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/binding/ValueConversions.java new file mode 100644 index 000000000..00f3584dc --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/molang/runtime/binding/ValueConversions.java @@ -0,0 +1,91 @@ +/* + * This file is part of molang, licensed under the MIT license + * + * Copyright (c) 2021-2023 Unnamed Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.tartaricacid.touhoulittlemaid.molang.runtime.binding; + +import org.jetbrains.annotations.Nullable; + +public final class ValueConversions { + public static boolean asBoolean(Object obj) { + if (obj == null) { + return false; + } + if (obj instanceof Boolean) { + return (Boolean) obj; + } + if (obj instanceof Number) { + // '0' is considered false here, anything else + // is considered true. + return ((Number) obj).floatValue() != 0; + } + return true; + } + + public static float asFloat(Object obj) { + if (obj == null) { + return 0; + } + if ((obj instanceof Number)) { + return ((Number) obj).floatValue(); + } + if (obj instanceof Boolean) { + return ((Boolean) obj) ? 1 : 0; + } + return 1; + } + + public static int asInt(Object obj) { + if (obj == null) { + return 0; + } + if ((obj instanceof Number)) { + return ((Number) obj).intValue(); + } + if (obj instanceof Boolean) { + return ((Boolean) obj) ? 1 : 0; + } + return 1; + } + + public static double asDouble(final @Nullable Object obj) { + if (obj == null) { + return 0; + } + if (obj instanceof Number) { + return ((Number) obj).doubleValue(); + } + if (obj instanceof Boolean) { + return ((Boolean) obj) ? 1 : 0; + } + return 1; + } + + public static String asString(final @Nullable Object obj) { + if (obj instanceof String) { + return ((String) obj); + } else { + return null; + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/EquipmentUtil.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/EquipmentUtil.java new file mode 100644 index 000000000..430da47e1 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/EquipmentUtil.java @@ -0,0 +1,21 @@ +package com.github.tartaricacid.touhoulittlemaid.util; + +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; + +public class EquipmentUtil { + public static ItemStack getEquippedItem(LivingEntity entity, EquipmentSlot slot) { + return entity.getItemBySlot(slot); + } + + public static ItemStack getEquippedElytraItem(LivingEntity entity) { + // 原版鞘翅 + var stack = entity.getItemBySlot(EquipmentSlot.CHEST); + if (stack.getItem() == Items.ELYTRA) { + return stack; + } + return ItemStack.EMPTY; + } +}