push what i have

This commit is contained in:
NotAKidoS 2023-07-26 08:27:28 -05:00
parent dc2916e8e6
commit d77afcc009
16 changed files with 496 additions and 91 deletions

View file

@ -1,6 +1,5 @@
using ABI_RC.Core.Player;
using ABI_RC.Systems.MovementSystem;
using RootMotion.FinalIK;
using UnityEngine;
namespace NAK.AlternateIKSystem.IK;
@ -54,6 +53,11 @@ public class BodyControl
#region Public Methods
public void Start()
{
}
public void Update()
{
TrackingAll = ShouldTrackAll();
@ -88,60 +92,4 @@ public class BodyControl
}
#endregion
#region Solver Weight Helpers
// I am unsure on this...?
public static void SetLookAtWeight(LookAtIK lookAtIk, float weight)
{
if (lookAtIk != null)
lookAtIk.solver.IKPositionWeight = weight;
}
public static void SetHeadWeight(IKSolverVR.Spine spine, float weight)
{
spine.positionWeight = weight;
spine.rotationWeight = weight;
}
public static void SetArmWeight(IKSolverVR.Arm arm, float weight)
{
arm.positionWeight = weight;
arm.rotationWeight = weight;
//arm.shoulderRotationWeight = weight;
//arm.shoulderTwistWeight = weight;
arm.bendGoalWeight = arm.bendGoal != null ? weight : 0f;
}
public static void SetLegWeight(IKSolverVR.Leg leg, float weight)
{
leg.positionWeight = weight;
leg.rotationWeight = weight;
leg.bendGoalWeight = leg.usingKneeTracker ? weight : 0f;
}
public static void SetPelvisWeight(IKSolverVR.Spine spine, float weight)
{
spine.pelvisPositionWeight = weight;
spine.pelvisRotationWeight = weight;
}
public static void SetLocomotionWeight(IKSolverVR.Locomotion locomotion, float weight)
{
locomotion.weight = weight;
}
public static void SetIKPositionWeight(IKSolverVR solver, float weight)
{
solver.IKPositionWeight = weight;
}
public static void SetIKPositionWeight(LookAtIK lookAtIk, float weight)
{
if (lookAtIk != null)
lookAtIk.solver.IKPositionWeight = weight;
}
#endregion
}

View file

@ -109,52 +109,42 @@ internal abstract class IKHandler
protected virtual void Update_HeadWeight()
{
float targetWeight = GetTargetWeight(BodyControl.TrackingHead, true);
BodyControl.SetHeadWeight(_solver.spine, targetWeight);
BodyControl.SetLookAtWeight(IKManager.lookAtIk, targetWeight);
}
protected virtual void Update_LeftArmWeight()
{
float leftArmWeight = GetTargetWeight(BodyControl.TrackingLeftArm, _solver.leftArm.target != null);
BodyControl.SetArmWeight(_solver.leftArm, leftArmWeight);
}
protected virtual void Update_RightArmWeight()
{
float rightArmWeight = GetTargetWeight(BodyControl.TrackingRightArm, _solver.rightArm.target != null);
BodyControl.SetArmWeight(_solver.rightArm, rightArmWeight);
}
protected virtual void Update_LeftLegWeight()
{
float leftLegWeight = GetTargetWeight(BodyControl.TrackingLeftLeg, _solver.leftLeg.target != null);
BodyControl.SetLegWeight(_solver.leftLeg, leftLegWeight);
}
protected virtual void Update_RightLegWeight()
{
float rightLegWeight = GetTargetWeight(BodyControl.TrackingRightLeg, _solver.rightLeg.target != null);
BodyControl.SetLegWeight(_solver.rightLeg, rightLegWeight);
}
protected virtual void Update_PelvisWeight()
{
float pelvisWeight = GetTargetWeight(BodyControl.TrackingPelvis, _solver.spine.pelvisTarget != null);
BodyControl.SetPelvisWeight(_solver.spine, pelvisWeight);
}
protected virtual void Update_LocomotionWeight()
{
_locomotionWeight = Mathf.Lerp(_locomotionWeight, BodyControl.TrackingLocomotion ? 1f : 0f,
Time.deltaTime * ModSettings.EntryIKLerpSpeed.Value * 2f);
BodyControl.SetLocomotionWeight(_solver.locomotion, _locomotionWeight);
}
protected virtual void Update_IKPositionWeight()
{
float ikPositionWeight = BodyControl.TrackingAll ? BodyControl.TrackingIKPositionWeight : 0f;
BodyControl.SetIKPositionWeight(_solver, ikPositionWeight);
BodyControl.SetIKPositionWeight(IKManager.lookAtIk, ikPositionWeight);
}
protected virtual float GetTargetWeight(bool isTracking, bool hasTarget)

View file

@ -1,4 +1,5 @@
using RootMotion.FinalIK;
using NAK.AlternateIKSystem.IK.WeightManipulators;
using RootMotion.FinalIK;
using UnityEngine;
namespace NAK.AlternateIKSystem.IK.IKHandlers;
@ -16,13 +17,13 @@ internal class IKHandlerDesktop : IKHandler
public override void OnInitializeIk()
{
// Default tracking for Desktop
shouldTrackHead = true;
shouldTrackLeftArm = false;
shouldTrackRightArm = false;
shouldTrackLeftLeg = false;
shouldTrackRightLeg = false;
shouldTrackPelvis = false;
shouldTrackLocomotion = true;
DeviceControlManipulator.shouldTrackHead = true;
DeviceControlManipulator.shouldTrackLeftArm = false;
DeviceControlManipulator.shouldTrackRightArm = false;
DeviceControlManipulator.shouldTrackLeftLeg = false;
DeviceControlManipulator.shouldTrackRightLeg = false;
DeviceControlManipulator.shouldTrackPelvis = false;
DeviceControlManipulator.shouldTrackLocomotion = true;
_vrik.onPreSolverUpdate.AddListener(OnPreSolverUpdateDesktop);
}
@ -36,11 +37,6 @@ internal class IKHandlerDesktop : IKHandler
// Reset avatar local position
_vrik.transform.localPosition = Vector3.zero;
_vrik.transform.localRotation = Quaternion.identity;
base.UpdateWeights();
// Desktop should never have head position weight
_solver.spine.positionWeight = 0f;
}
#endregion

View file

@ -1,4 +1,5 @@
using RootMotion.FinalIK;
using NAK.AlternateIKSystem.IK.WeightManipulators;
using RootMotion.FinalIK;
using UnityEngine;
namespace NAK.AlternateIKSystem.IK.IKHandlers;
@ -16,13 +17,13 @@ internal class IKHandlerHalfBody : IKHandler
public override void OnInitializeIk()
{
// Default tracking for HalfBody
shouldTrackHead = true;
shouldTrackLeftArm = true;
shouldTrackRightArm = true;
shouldTrackLeftLeg = false;
shouldTrackRightLeg = false;
shouldTrackPelvis = false;
shouldTrackLocomotion = true;
DeviceControlManipulator.shouldTrackHead = true;
DeviceControlManipulator.shouldTrackLeftArm = true;
DeviceControlManipulator.shouldTrackRightArm = true;
DeviceControlManipulator.shouldTrackLeftLeg = false;
DeviceControlManipulator.shouldTrackRightLeg = false;
DeviceControlManipulator.shouldTrackPelvis = false;
DeviceControlManipulator.shouldTrackLocomotion = true;
_vrik.onPreSolverUpdate.AddListener(OnPreSolverUpdateHalfBody);
}

View file

@ -2,6 +2,7 @@
using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using NAK.AlternateIKSystem.IK.IKHandlers;
using NAK.AlternateIKSystem.IK.WeightManipulators;
using NAK.AlternateIKSystem.VRIKHelpers;
using RootMotion.FinalIK;
using UnityEngine;
@ -15,6 +16,7 @@ public class IKManager : MonoBehaviour
#region Variables
public BodyControl BodyControl = new BodyControl();
public WeightManipulatorManager WeightManipulator = new WeightManipulatorManager();
public static VRIK vrik => _vrik;
private static VRIK _vrik;
@ -83,15 +85,23 @@ public class IKManager : MonoBehaviour
_leftHandRotations = _leftHandTarget.Find("LeftHandRotations");
_rightHandTarget = _rightController.Find("RightHandTarget");
_rightHandRotations = _rightHandTarget.Find("RightHandRotations");
WeightManipulator.AddOverride(new TrackingControlManipulator());
WeightManipulator.AddOverride(new DeviceControlManipulator());
BodyControl.Start();
}
private void Update()
{
BodyControl.Update();
if (!_isAvatarInitialized)
return;
BodyControl.Update();
if (vrik.solver != null)
WeightManipulator.UpdateWeights(vrik.solver);
_ikHandler?.UpdateWeights();
}

View file

@ -0,0 +1,87 @@
using UnityEngine;
using Valve.VR;
namespace AlternateIKSystem.IK.Tracking;
public class SteamVR_TrackerManager : MonoBehaviour
{
public static SteamVR_TrackerManager Instance { get; private set; }
private readonly Dictionary<uint, TrackedPoint> trackedPoints = new Dictionary<uint, TrackedPoint>();
private int lastPosesCount = 0;
private void Awake()
{
if (Instance != null)
{
DestroyImmediate(this);
return;
}
Instance = this;
}
private void OnEnable()
{
SteamVR_Events.NewPoses.AddListener(OnNewPoses);
}
private void OnDisable()
{
SteamVR_Events.NewPoses.RemoveListener(OnNewPoses);
}
private void OnNewPoses(TrackedDevicePose_t[] poses)
{
if (lastPosesCount < poses.Length)
{
// If the count has increased, a new tracker has been connected.
for (uint i = (uint)lastPosesCount; i < poses.Length; i++)
{
if (OpenVR.System.GetTrackedDeviceClass(i) == ETrackedDeviceClass.GenericTracker)
trackedPoints.Add(i, new TrackedPoint(i));
}
}
else if (lastPosesCount > poses.Length)
{
// If the count has decreased, a tracker has been disconnected.
for (uint i = (uint)poses.Length; i < lastPosesCount; i++)
{
if (!trackedPoints.ContainsKey(i))
continue;
trackedPoints[i].Destroy();
trackedPoints.Remove(i);
}
}
for (uint i = 0; i < poses.Length; i++)
{
if (trackedPoints.TryGetValue(i, out TrackedPoint point))
{
point.UpdatePose(poses[i]);
}
}
lastPosesCount = poses.Length;
}
private class TrackedPoint
{
private uint i;
public TrackedPoint(uint i)
{
this.i = i;
}
public void UpdatePose(TrackedDevicePose_t pose)
{
}
public void Destroy()
{
}
}
}

View file

@ -0,0 +1,132 @@
using RootMotion.FinalIK;
namespace NAK.AlternateIKSystem.IK.WeightManipulators.BodyParts;
public abstract class BodyPart
{
protected float positionWeight = 1f;
protected float rotationWeight = 1f;
protected bool isEnabled = true;
public void SetPositionWeight(float weight)
{
this.positionWeight *= weight;
}
public void SetRotationWeight(float weight)
{
this.rotationWeight *= weight;
}
public void SetEnabled(bool isEnabled)
{
this.isEnabled = isEnabled;
}
public abstract void ApplyWeightToSolver(IKSolverVR solver);
}
public class Head : BodyPart
{
public override void ApplyWeightToSolver(IKSolverVR solver)
{
if (!isEnabled) return;
solver.spine.positionWeight *= positionWeight;
solver.spine.rotationWeight *= rotationWeight;
}
}
public class Pelvis : BodyPart
{
public override void ApplyWeightToSolver(IKSolverVR solver)
{
if (!isEnabled) return;
solver.spine.pelvisPositionWeight *= positionWeight;
solver.spine.pelvisRotationWeight *= rotationWeight;
}
}
public class LeftArm : BodyPart
{
private float bendGoalWeight = 1f;
public void SetBendGoalWeight(float weight)
{
this.bendGoalWeight *= weight;
}
public override void ApplyWeightToSolver(IKSolverVR solver)
{
if (!isEnabled) return;
solver.leftArm.positionWeight *= positionWeight;
solver.leftArm.rotationWeight *= rotationWeight;
solver.leftArm.bendGoalWeight *= bendGoalWeight;
}
}
public class RightArm : BodyPart
{
private float bendGoalWeight = 1f;
public void SetBendGoalWeight(float weight)
{
this.bendGoalWeight *= weight;
}
public override void ApplyWeightToSolver(IKSolverVR solver)
{
if (!isEnabled) return;
solver.rightArm.positionWeight *= positionWeight;
solver.rightArm.rotationWeight *= rotationWeight;
solver.rightArm.bendGoalWeight *= bendGoalWeight;
}
}
public class LeftLeg : BodyPart
{
private float bendGoalWeight = 1f;
public void SetBendGoalWeight(float weight)
{
this.bendGoalWeight *= weight;
}
public override void ApplyWeightToSolver(IKSolverVR solver)
{
if (!isEnabled) return;
solver.leftLeg.positionWeight *= positionWeight;
solver.leftLeg.rotationWeight *= rotationWeight;
solver.leftLeg.bendGoalWeight *= bendGoalWeight;
}
}
public class RightLeg : BodyPart
{
private float bendGoalWeight = 1f;
public void SetBendGoalWeight(float weight)
{
this.bendGoalWeight *= weight;
}
public override void ApplyWeightToSolver(IKSolverVR solver)
{
if (!isEnabled) return;
solver.rightLeg.positionWeight *= positionWeight;
solver.rightLeg.rotationWeight *= rotationWeight;
solver.rightLeg.bendGoalWeight *= bendGoalWeight;
}
}
public class Locomotion : BodyPart
{
public override void ApplyWeightToSolver(IKSolverVR solver)
{
if (!isEnabled) return;
solver.locomotion.weight *= positionWeight;
}
}

View file

@ -0,0 +1,31 @@
using NAK.AlternateIKSystem.IK.WeightManipulators.Interface;
using RootMotion.FinalIK;
namespace NAK.AlternateIKSystem.IK.WeightManipulators;
public class DeviceControlManipulator : IWeightManipulator
{
public static bool shouldTrackAll = true;
public static bool shouldTrackHead = true;
public static bool shouldTrackPelvis = true;
public static bool shouldTrackLeftArm = true;
public static bool shouldTrackRightArm = true;
public static bool shouldTrackLeftLeg = true;
public static bool shouldTrackRightLeg = true;
public static bool shouldTrackLocomotion = true;
public WeightManipulatorManager Manager { get; set; }
// Manipulator for Connected Devices (Has final say)
public void Update(IKSolverVR solver)
{
Manager.TrackAll &= shouldTrackAll;
Manager.TrackHead &= shouldTrackHead;
Manager.TrackPelvis &= shouldTrackPelvis;
Manager.TrackLeftArm &= shouldTrackLeftArm;
Manager.TrackRightArm &= shouldTrackRightArm;
Manager.TrackLeftLeg &= shouldTrackLeftLeg;
Manager.TrackRightLeg &= shouldTrackRightLeg;
Manager.TrackLocomotion &= shouldTrackLocomotion;
}
}

View file

@ -0,0 +1,9 @@
using RootMotion.FinalIK;
namespace NAK.AlternateIKSystem.IK.WeightManipulators.Interface;
public interface IWeightManipulator
{
WeightManipulatorManager Manager { get; set; }
void Update(IKSolverVR solver);
}

View file

@ -0,0 +1,22 @@
using NAK.AlternateIKSystem.IK.WeightManipulators.Interface;
using RootMotion.FinalIK;
namespace NAK.AlternateIKSystem.IK.WeightManipulators;
public class TrackingControlManipulator : IWeightManipulator
{
public WeightManipulatorManager Manager { get; set; }
// Manipulator for External Control (Auto, State Behaviour)
public void Update(IKSolverVR solver)
{
Manager.TrackAll |= BodyControl.TrackingAll;
Manager.TrackHead |= BodyControl.TrackingHead;
Manager.TrackPelvis |= BodyControl.TrackingPelvis;
Manager.TrackLeftArm |= BodyControl.TrackingLeftArm;
Manager.TrackRightArm |= BodyControl.TrackingRightArm;
Manager.TrackLeftLeg |= BodyControl.TrackingLeftLeg;
Manager.TrackRightLeg |= BodyControl.TrackingRightLeg;
Manager.TrackLocomotion |= BodyControl.TrackingLocomotion;
}
}

View file

@ -0,0 +1,84 @@
using NAK.AlternateIKSystem.IK.WeightManipulators.BodyParts;
using NAK.AlternateIKSystem.IK.WeightManipulators.Interface;
using RootMotion.FinalIK;
namespace NAK.AlternateIKSystem.IK.WeightManipulators;
public enum BodyPartEnum
{
Head,
Pelvis,
LeftArm,
RightArm,
LeftLeg,
RightLeg,
Locomotion,
All
}
public class WeightManipulatorManager
{
private readonly Dictionary<BodyPartEnum, BodyPart> _bodyParts = new Dictionary<BodyPartEnum, BodyPart>();
public WeightManipulatorManager()
{
_bodyParts.Add(BodyPartEnum.Head, new Head());
_bodyParts.Add(BodyPartEnum.Pelvis, new Pelvis());
_bodyParts.Add(BodyPartEnum.LeftArm, new LeftArm());
_bodyParts.Add(BodyPartEnum.RightArm, new RightArm());
_bodyParts.Add(BodyPartEnum.LeftLeg, new LeftLeg());
_bodyParts.Add(BodyPartEnum.RightLeg, new RightLeg());
_bodyParts.Add(BodyPartEnum.Locomotion, new Locomotion());
}
public void SetWeight(BodyPartEnum bodyPartName, float positionWeight, float rotationWeight)
{
var bodyPart = _bodyParts[bodyPartName];
bodyPart.SetPositionWeight(positionWeight);
bodyPart.SetRotationWeight(rotationWeight);
}
public void SetEnabled(BodyPartEnum bodyPartName, bool isEnabled)
{
var bodyPart = _bodyParts[bodyPartName];
bodyPart.SetEnabled(isEnabled);
}
public void ApplyWeightsToSolver(IKSolverVR solver)
{
foreach (var bodyPart in _bodyParts.Values)
{
bodyPart.ApplyWeightToSolver(solver);
}
}
}
public class BodyControl
{
private readonly WeightManipulatorManager _manager;
public BodyControl(WeightManipulatorManager manager)
{
_manager = manager;
}
public void SetWeight(string bodyPartName, float positionWeight, float rotationWeight)
{
_manager.SetWeight(bodyPartName, positionWeight, rotationWeight);
}
}
public class DeviceControl
{
private readonly WeightManipulatorManager _manager;
public DeviceControl(WeightManipulatorManager manager)
{
_manager = manager;
}
public void SetEnabled(string bodyPartName, bool isEnabled)
{
_manager.SetEnabled(bodyPartName, isEnabled);
}
}

View file

@ -43,7 +43,7 @@ public static class BTKUIAddon
// Fake root heading limit
AddMelonSlider(ref generalIKPage, ModSettings.EntryBodyHeadingLimit, 0, 90f, 0);
// Lerp Speed
AddMelonSlider(ref generalIKPage, ModSettings.EntryIKLerpSpeed, 0, 20f, 0);
}

View file

@ -79,6 +79,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AlternateIKSystem", "Altern
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NoDepthOnlyFlat", "NoDepthOnlyFlat\NoDepthOnlyFlat.csproj", "{6F2F8774-40CF-4DE1-BC6C-DC00CA76D6A8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ViewpointHeadScaleFix", "ViewpointHeadScaleFix\ViewpointHeadScaleFix.csproj", "{337A284D-C4AC-4B1B-B094-B6F758278BD8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -237,6 +239,10 @@ Global
{6F2F8774-40CF-4DE1-BC6C-DC00CA76D6A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6F2F8774-40CF-4DE1-BC6C-DC00CA76D6A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6F2F8774-40CF-4DE1-BC6C-DC00CA76D6A8}.Release|Any CPU.Build.0 = Release|Any CPU
{337A284D-C4AC-4B1B-B094-B6F758278BD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{337A284D-C4AC-4B1B-B094-B6F758278BD8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{337A284D-C4AC-4B1B-B094-B6F758278BD8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{337A284D-C4AC-4B1B-B094-B6F758278BD8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -0,0 +1,55 @@
using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using MelonLoader;
using System.Reflection;
using UnityEngine;
namespace NAK.ViewpointHeadScaleFix;
// Makes initialCameraPos scale with head bone scale
public class ViewpointHeadScaleFix : MelonMod
{
public override void OnInitializeMelon()
{
HarmonyInstance.Patch(
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.CalibrateAvatar)),
postfix: new HarmonyLib.HarmonyMethod(typeof(ViewpointHeadScaleFix).GetMethod(nameof(OnPlayerSetupCalibrateAvatar_Postfix), BindingFlags.NonPublic | BindingFlags.Static))
);
HarmonyInstance.Patch(
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetViewPointOffset)),
prefix: new HarmonyLib.HarmonyMethod(typeof(ViewpointHeadScaleFix).GetMethod(nameof(OnPlayerSetupSetViewPointOffset_Prefix), BindingFlags.NonPublic | BindingFlags.Static))
);
}
private static void OnPlayerSetupCalibrateAvatar_Postfix(ref PlayerSetup __instance)
{
if (!MetaPort.Instance.isUsingVr)
return;
Transform headTransform = __instance._animator.GetBoneTransform(HumanBodyBones.Head);
if (headTransform == null)
return;
_originalCameraPos = __instance.initialCameraPos;
_initialHeadScale = headTransform.localScale;
}
private static void OnPlayerSetupSetViewPointOffset_Prefix(ref PlayerSetup __instance)
{
if (!MetaPort.Instance.isUsingVr)
return;
Transform headTransform = __instance._animator.GetBoneTransform(HumanBodyBones.Head);
if (headTransform == null)
return;
__instance.initialCameraPos = __instance.MultiplyVectors(
_originalCameraPos,
__instance.DivideVectors(headTransform.localScale, _initialHeadScale)
);
}
private static Vector3 _originalCameraPos = Vector3.one;
private static Vector3 _initialHeadScale = Vector3.one;
}

View file

@ -0,0 +1,32 @@
using MelonLoader;
using NAK.ViewpointHeadScaleFix.Properties;
using System.Reflection;
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyTitle(nameof(NAK.ViewpointHeadScaleFix))]
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
[assembly: AssemblyProduct(nameof(NAK.ViewpointHeadScaleFix))]
[assembly: MelonInfo(
typeof(NAK.ViewpointHeadScaleFix.ViewpointHeadScaleFix),
nameof(NAK.ViewpointHeadScaleFix),
AssemblyInfoParams.Version,
AssemblyInfoParams.Author,
downloadLink: "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/AvatarScale"
)]
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonColor(255, 241, 200, 82)]
[assembly: MelonAuthorColor(255, 114, 17, 25)]
[assembly: HarmonyDontPatchAll]
namespace NAK.ViewpointHeadScaleFix.Properties;
internal static class AssemblyInfoParams
{
public const string Version = "1.0.0";
public const string Author = "NotAKidoS";
}

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk"/>