avatar/Assets/VRCFury/Scripts/Editor/VF/Feature/SecurityLockBuilder.cs

125 lines
4.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
using VF.Builder;
using VF.Builder.Exceptions;
using VF.Feature.Base;
using VF.Inspector;
using VF.Model.Feature;
using VRC.SDKBase;
namespace VF.Feature {
public class SecurityLockBuilder : FeatureBuilder<SecurityLock> {
private VFABool _unlockedParam = null;
private VFABool GetUnlockedParam() {
if (_unlockedParam == null) _unlockedParam = GetFx().NewBool("SecurityLockSync", synced: true);
return _unlockedParam;
}
public VFACondition GetEnabled() {
return GetUnlockedParam().IsTrue();
}
[FeatureBuilderAction]
public void Apply() {
if (allFeaturesInRun.Where(m => m is SecurityLock).Count() > 1) {
throw new VRCFBuilderException("Cannot have more than one SecurityLock feature on avatar.");
}
var unlockCode = model.pinNumber;
var digits = unlockCode
.Select(c => c - '0')
.ToArray();
if (digits.Length < 4) {
throw new VRCFBuilderException("Security lock must contain at least 4 digits");
}
if (digits.Length > 10) {
throw new VRCFBuilderException("Security lock must contain at most 10 digits");
}
foreach (var digit in digits) {
if (digit < 1 || digit > 7) {
throw new VRCFBuilderException("Security lock contains digit outside allowed bounds (1-7)");
}
}
var numDigits = digits.Length;
var numDigitSlots = 10;
var fx = GetFx();
var paramSecuritySync = GetUnlockedParam();
// This doesn't actually need synced, but vrc gets annoyed that the menu is using an unsynced param
var paramInput = fx.NewInt("SecurityInput", synced: true);
for (var i = 1; i < 8; i++) {
manager.GetMenu().NewMenuToggle("Security/" + i, paramInput, i);
}
manager.GetMenu().NewMenuToggle("Security/Unlocked", paramInput, 8);
var layer = fx.NewLayer("Security Lock");
var remote = layer.NewState("Remote Trap");
var entry = layer.NewState("Entry")
.Move(remote, 1, 0);
remote.TransitionsTo(entry).When(fx.IsLocal().IsTrue());
var digitParams = new List<VFANumber>();
for (var i = 0; i < numDigitSlots; i++) {
digitParams.Add(fx.NewInt("SecurityDigit" + i));
}
var saveStates = new List<VFAState>();
for (var i = numDigitSlots - 1; i >= 0; i--) {
var saveState = layer.NewState("Save " + i);
if (saveStates.Count == 0) saveState.Move(entry, -1, 1);
saveStates.Add(saveState);
var target = digitParams[i];
var source = i == 0 ? paramInput : digitParams[i - 1];
saveState.DrivesCopy(target, source);
if (saveStates.Count > 1) {
saveStates[saveStates.Count - 2].TransitionsTo(saveState).When(fx.Always());
} else {
entry.TransitionsTo(saveState).When(paramInput.IsGreaterThan(0).And(paramInput.IsLessThan(8)));
}
}
entry.TransitionsTo(entry).When(paramInput.IsEqualTo(8));
entry.Drives(paramInput, 0);
entry.Drives(paramSecuritySync, false);
var check = layer.NewState("Check").Move(1, -1);
saveStates[saveStates.Count - 1].TransitionsTo(check).When(fx.Always());
var unlocked = layer.NewState("Unlocked").Move(1,-1);
var digitsReversed = digits.Reverse().ToArray();
var unlockCondition = digitParams[0].IsEqualTo(digitsReversed[0]);
for (int i = 1; i < numDigits; i++) {
unlockCondition = unlockCondition.And(digitParams[i].IsEqualTo(digitsReversed[i]));
}
check.TransitionsTo(unlocked).When(unlockCondition);
check.TransitionsTo(entry).When(fx.Always());
unlocked.Drives(paramInput, 8);
unlocked.Drives(paramSecuritySync, true);
unlocked.TransitionsTo(entry).When(paramInput.IsNotEqualTo(8));
}
public override string GetEditorTitle() {
return "Security Lock";
}
public override VisualElement CreateEditor(SerializedProperty prop) {
var content = new VisualElement();
content.Add(VRCFuryEditorUtils.Info("This feature will enable the security submenu in your avatar's menu. You must enter the correct pin to unlock any VRCFury toggles marked with the 'Security' flag."));
content.Add(VRCFuryEditorUtils.WrappedLabel("Pin Number (min 4 digits, max 10 digits, can only use numbers 1-7)"));
content.Add(VRCFuryEditorUtils.Prop(prop.FindPropertyRelative("pinNumber")));
return content;
}
public override bool AvailableOnProps() {
return false;
}
}
}