Upload from upload_mods.ps1

This commit is contained in:
Nathaniel Cosford
2025-06-04 16:44:53 +09:30
commit f1fbbe67bb
1722 changed files with 165268 additions and 0 deletions

View File

@@ -0,0 +1,773 @@
using UnityEngine.Events;
/*
* Thank you for using my Lockpicking asset! I hope it works great in your game. You can extend it to suit your game by
* manipulating some of the code below. For instance, if your player can have a various level of "lockpicking" skill,
* you may consider multiplying the value of lockGive by their skill, so that a higher skilled player would find it
* easier to open a lock.
*
* Enjoy!
*/
namespace Lockpicking
{
[RequireComponent(typeof(LockEmissive))]
public class Keyhole : MonoBehaviour
{
// 7 Days To Die stuff
public int NumLockPicks;
public EntityPlayer player;
public BlockValue blockValue;
[Header("Plumbing")] public GameObject keyhole; // The keyhole with lockpick A that turns the entire keyhole object to open it
public GameObject lockpickObject; // The lockpick that turns to match the secret lockAngle
public Animator lockpickAnimator; // Animator on the lockpick in the lockpickObject
public Animator _padlockAnimator;
public GameObject padlock1; // Link to the padlock 1 game object
public GameObject button;
public LocksetAudio audioTurnClick;
public LocksetAudio audioSqueek;
public LocksetAudio audioOpen;
public LocksetAudio audioJiggle;
public LocksetAudio audioJiggle2;
public LocksetAudio audioJiggle3;
public LocksetAudio audioPadlockOpen;
public LocksetAudio audioPadlockJiggle;
public LocksetAudio audioLockpickBreak;
public LocksetAudio audioLockpickEnter;
public LocksetAudio audioLockpickClick;
// Audio Settings
[Range(0f, 1f)] public float clickVolumeMin = 0.1f;
[Range(0f, 1f)] public float clickVolumeMax = 0.4f;
[Range(0, 100)] public int clickChance = 100;
[Range(0f, 15f)] public float clickRate = 10f;
[Range(0f, 1f)] public float squeekVolumeMin = 0.1f;
[Range(0f, 1f)] public float squeekVolumeMax = 0.4f;
[Range(0, 100)] public int squeekChance = 50;
[Range(0f, 360f)] public float squeekRate = 20f;
public bool buttonDown;
private LockEmissive _lockEmissive; // Link to the lockEmissive script on this object
private float _lockpickAnglePrev;
// Private variables
private float breakCounter; // Counter for taking a break after a broken lockpick
public float breakTimeCounter;
private bool isShaking; // Whether we are currently shaking or not
private Vector3 preshakeKeyhole; // Saves the pre-shake angles
private Vector3 preshakeLockpick; // Saves the pre-shake angles
private float shakeTimer; // Counter for the shake Time
private float squeekTimer;
private void Awake()
{
squeekTimer = squeekRate;
_pickAnglesDefault = LockpickAngles();
_lockEmissive = gameObject.GetComponent<LockEmissive>();
if (_lockEmissive == null)
gameObject.AddComponent<LockEmissive>();
if (padlock1 != null)
{
_padlockAnimator = padlock1.gameObject.GetComponent<Animator>();
if (_padlockAnimator == null)
_padlockAnimator = padlock1.gameObject.AddComponent<Animator>();
}
_closeTrigger = Animator.StringToHash(closeTrigger);
_openTrigger = Animator.StringToHash(openTrigger);
_lockpickBreakTrigger = Animator.StringToHash(lockPickBreakTrigger);
_lockpickInsertTrigger = Animator.StringToHash(lockPickInsertTrigger);
if (resetOnAwake)
ResetLock();
}
private void Update()
{
if (NumLockPicks > 0)
{
PassValuesToEmissiveScript();
if (BreakingForAnimation())
return;
HandlePlayerInput();
}
else
{
RefreshLockPicks();
}
}
private void OnEnable()
{
RefreshLockPicks();
ResetLock();
if (NumLockPicks > 0)
UpdateLockPicks(true);
else
UpdateLockPicks(false);
}
private void OnDisable()
{
NumLockPicks = 0;
}
private void OnValidate()
{
LockAngle = LockAngle;
LockGive = LockGive;
CloseDistance = CloseDistance;
_pickAngleMin = Mathf.Clamp(_pickAngleMin, minLockAngle, maxLockAngle);
_pickAngleMax = Mathf.Clamp(_pickAngleMax, minLockAngle, maxLockAngle);
minGiveAmount = Mathf.Clamp(minGiveAmount, 1f, 360f);
maxGiveAmount = Mathf.Clamp(maxGiveAmount, 1f, 360f);
minCloseDistance = Mathf.Clamp(minCloseDistance, 5f, 360f);
maxCloseDistance = Mathf.Clamp(maxCloseDistance, 5f, 360f);
}
public float BreakTimeCounter()
{
return breakTimeCounter;
}
public float LockPickAngle()
{
return GetAngle(LockpickAngles().z);
}
public float KeyholeAngle()
{
return GetAngle(KeyholeAngles().z);
}
public void EditorOnValidate()
{
#if UNITY_EDITOR
EditorUtility.SetDirty(this);
#endif
OnValidate();
}
private void UpdateLockPicks(bool enable)
{
// Hide the lock picks or show them.
keyhole.transform.FindInChilds("LockpickB (Turnable)").gameObject.SetActive(enable);
keyhole.transform.FindInChilds("LockpickA").gameObject.SetActive(enable);
}
private void RefreshLockPicks()
{
if (player == null)
return;
var uiforPlayer = LocalPlayerUI.GetUIForPlayer(player as EntityPlayerLocal);
var playerInventory = uiforPlayer.xui.PlayerInventory;
var item = ItemClass.GetItem("resourceLockPick");
if (item != null)
NumLockPicks = playerInventory.GetItemCount(item);
if (NumLockPicks > 0)
UpdateLockPicks(true);
else
UpdateLockPicks(false);
}
private void HandlePlayerInput()
{
if (_lockIsOpen) return;
if (openPressure > 0)
{
TryToTurnKeyhole();
}
else
{
StopShaking();
ReturnKeyholeToDefaultPosition();
TurnLockpick(turnSpeedLockpick * lockpickPressure);
}
}
private bool BreakingForAnimation()
{
if (breakCounter > 0f)
{
breakCounter -= Time.deltaTime;
ReturnKeyholeToDefaultPosition();
return true;
}
return false;
}
private void ReturnKeyholeToDefaultPosition()
{
TurnKeyhole(-returnSpeedKeyhole);
}
private void TryToTurnKeyhole()
{
if (LockCanTurn())
{
TurnKeyhole(turnSpeedKeyhole * openPressure);
if (KeyholeTurnValue() <= 0 && LockpickIsInPosition())
OpenLock();
}
else
{
Shake();
}
}
private void PassValuesToEmissiveScript()
{
_lockEmissive.breakpointValue = Mathf.Clamp(breakTimeCounter / breakTime, 0, 1);
_lockEmissive.successValue = Mathf.Clamp(KeyholeTurnValue(), 0, 1);
}
private bool LockpickIsInPosition()
{
return LockPickAngle() < _lockAngle + _lockGive && LockPickAngle() > _lockAngle - _lockGive;
}
private void Shake()
{
// If we are not already shaking, save the original rotations.
if (!isShaking)
{
if (audioPadlockJiggle && padlock1 != null && padlock1.activeSelf)
{
audioPadlockJiggle.PlayLoop();
}
else
{
if (audioJiggle)
audioJiggle.PlayLoop();
if (audioJiggle2 != null)
audioJiggle2.PlayLoop();
if (audioJiggle3 != null)
audioJiggle3.PlayLoop();
}
preshakeKeyhole = KeyholeAngles();
preshakeLockpick = LockpickAngles();
isShaking = true;
}
// Check breakTimeCounter to stop shaking at the right time
breakTimeCounter += Time.deltaTime;
//Log.Out($"Break Time Counter: {breakTimeCounter} Total BreakTime: {breakTime}");
if (breakTimeCounter > breakTime)
{
StopShaking();
BreakLockpick();
return;
}
shakeTimer -= Time.deltaTime;
if (shakeTimer <= 0)
{
// Start with the current values
var newShakeKeyhole = preshakeKeyhole;
var newShakeLockpick = preshakeLockpick;
// Add some modification
newShakeKeyhole.z += UnityEngine.Random.Range(-maxShake, maxShake);
newShakeLockpick.z += UnityEngine.Random.Range(-maxShake, maxShake);
// Set the value + modification
SetKeyholeAngles(newShakeKeyhole);
SetLockpickAngles(newShakeLockpick);
// Reset the timer
shakeTimer = shakeTime;
}
}
private void StopShaking()
{
if (isShaking)
{
if (audioPadlockJiggle && padlock1 != null && padlock1.activeSelf)
{
audioPadlockJiggle.StopLoop();
}
else if (audioJiggle)
{
audioJiggle.StopLoop();
if (audioJiggle2 != null)
audioJiggle2.StopLoop();
if (audioJiggle3 != null)
audioJiggle3.StopLoop();
}
SetKeyholeAngles(preshakeKeyhole);
SetLockpickAngles(preshakeLockpick);
isShaking = false;
}
}
private void SetKeyholeAngles(Vector3 value)
{
keyhole.transform.localEulerAngles = value;
}
private void SetLockpickAngles(Vector3 value)
{
lockpickObject.transform.localEulerAngles = value;
}
public void BreakLockpick()
{
//Log.Out("Lockpicking-BreakLockpick START");
if (audioLockpickBreak)
{
//Log.Out("Lockpicking-BreakLockpick PLAY SOUND: audioLockpickBreak");
//Log.Out("Lockpicking-BreakLockpick audioLockpickBreak.name: " + audioLockpickBreak.name);
//audioLockpickBreak.DelayPlay(1f);
audioLockpickBreak.PlayOnce();
}
breakCounter = breakPause; // Set so we can't do any actions for a short time
breakTimeCounter = 0f; // Reset the breakCounter
ResetLockpickPosition(); // Reset the lockpick position
lockpickAnimator.SetTrigger(_lockpickBreakTrigger); // Play the break animation
lockpickBroke.Invoke(); // Invoke this event in case other scripts are listening
/*if (audioLockpickEnter && audioLockpickEnter.isActiveAndEnabled)
{
Log.Out("Lockpicking-BreakLockpick PLAY SOUND: audioLockpickEnter");
audioLockpickEnter.DelayPlay(1f);
}*/
// Remove the broke pick lock.
if (player != null)
{
//Log.Out("Lockpicking-BreakLockpick REMOVE LOCK PICK");
var playerUI = (player as EntityPlayerLocal).PlayerUI;
var playerInventory = playerUI.xui.PlayerInventory;
var item = ItemClass.GetItem("resourceLockPick");
var itemStack = new ItemStack(item, 1);
playerInventory.RemoveItem(itemStack);
RefreshLockPicks();
}
if (NumLockPicks > 0)
{
//Log.Out("Lockpicking-BreakLockpick 1");
UpdateLockPicks(true);
}
else
{
//Log.Out("Lockpicking-BreakLockpick 2");
UpdateLockPicks(false);
}
}
/// <summary>
/// Call this when the lock is open successfully.
/// </summary>
public void OpenLock()
{
if (!_lockIsOpen)
{
if (audioPadlockOpen && padlock1 != null && padlock1.activeInHierarchy)
{
//Log.Out("Lockpicking-OpenLock PLAY SOUND: audioPadlockOpen");
audioPadlockOpen.PlayOnce();
if (_padlockAnimator != null)
_padlockAnimator.SetTrigger(_openTrigger);
}
else if (audioOpen)
{
//Log.Out("Lockpicking-OpenLock PLAY SOUND: audioOpen");
audioOpen.PlayOnce();
}
// Invoke the event for any other scripts that are listening
lockOpen.Invoke();
_lockIsOpen = true;
}
}
private void DoSqueekAudio(float speed)
{
if (audioSqueek)
if (squeekRate > 0)
{
squeekTimer -= Mathf.Abs(speed) * Time.deltaTime;
if (squeekTimer <= 0)
{
if (UnityEngine.Random.Range(0, 100) < squeekChance)
{
//Log.Out("Keyhole-DoSqueekAudio PLAY SOUND: audioSqueek");
audioSqueek.PlayAudioClip(UnityEngine.Random.Range(squeekVolumeMin, squeekVolumeMax));
}
squeekTimer = squeekRate;
}
}
}
private float GetAngle(float eulerAngle)
{
var angle = eulerAngle;
angle %= 360;
if (angle > 180)
angle -= 360;
return angle;
}
private void TurnLockpick(float speed)
{
// If we are at or outside of our max range, return
if (LockPickAngle() >= _pickAngleMax && speed > 0 || LockPickAngle() <= _pickAngleMin && speed < 0)
return;
// Set the new angle
var newAngle = new Vector3(LockpickAngles().x, LockpickAngles().y,
LockpickAngles().z + speed * Time.deltaTime);
SetLockpickAngles(newAngle);
DoClickAudio(speed, newAngle);
}
private void DoClickAudio(float speed, Vector3 newAngle)
{
var angleMod = newAngle.z % clickRate;
var prevMod = _lockpickAnglePrev % clickRate;
if (speed > 0 && angleMod < prevMod || speed < 0 && angleMod > prevMod)
if (audioTurnClick != null)
{
//Log.Out("Keyhole-DoClickAudio PLAY SOUND: audioTurnClick");
audioTurnClick.PlayAudioClip(UnityEngine.Random.Range(clickVolumeMin, clickVolumeMax));
}
_lockpickAnglePrev = newAngle.z;
}
private Vector3 LockpickAngles()
{
return lockpickObject.transform.localEulerAngles;
}
private void TurnKeyhole(float speed)
{
// If we are at or outside of our max range, return
if (KeyholeAngle() >= _keyholeAngleMax && speed > 0 || KeyholeAngle() <= _keyholeAngleDefault && speed < 0)
return;
// Set the new angle
SetKeyholeAngles(new Vector3(KeyholeAngles().x, KeyholeAngles().y, KeyholeAngles().z + speed * Time.deltaTime));
DoSqueekAudio(speed);
}
private Vector3 KeyholeAngles()
{
return keyhole.transform.localEulerAngles;
}
public float KeyholeTurnValue()
{
return (_keyholeAngleMax - KeyholeAngle()) / (_keyholeAngleMax - _keyholeAngleDefault);
}
public void SetLock(float newLockAngle, float newLockGive, float newCloseDistance)
{
_lockAngle = newLockAngle;
_lockGive = newLockGive;
_closeDistance = newCloseDistance;
if (audioLockpickEnter && audioLockpickEnter.isActiveAndEnabled)
{
//Log.Out("Lockpicking-BreakLockpick PLAY SOUND: audioLockpickEnter");
audioLockpickEnter.DelayPlay(0.7f);
}
ResetLockpickPosition();
lockpickAnimator.SetTrigger(_lockpickInsertTrigger); // Play the animation
}
public void SetLock(float lockAngleMin, float lockAngleMax, float lockGiveMin,
float lockGiveMax, float closeDistanceMin, float closeDistanceMax)
{
SetLock(UnityEngine.Random.Range(lockAngleMin, lockAngleMax),
UnityEngine.Random.Range(lockGiveMin, lockGiveMax),
UnityEngine.Random.Range(closeDistanceMin, closeDistanceMax));
}
public void ResetLock()
{
LockIsOpen = false;
ProgressionValue progressionValue = null;
var difficulty = 0;
var prevBreakTime = breakTime;
var prevMaxGive = maxGiveAmount;
var healthLeft = blockValue.Block.MaxDamage - blockValue.damage;
// Adjust the difficulty based on the lock damage
if (healthLeft <= 10000)
difficulty = 3;
if (healthLeft <= 2500)
difficulty = 2;
if (healthLeft <= 400)
difficulty = 1;
if (healthLeft <= 200)
difficulty = 0;
// give more time to avoid breaking pick locks.
if (player != null)
{
var secureBlock = Block.list[blockValue.type];
if (secureBlock != null)
{
if (secureBlock.Properties.Values.ContainsKey("LockPickDifficulty"))
difficulty = int.Parse(secureBlock.Properties.Values["LockPickDifficulty"]);
}
var maxGiveAmounts = "10,8,6,4".Split(','); //Configuration.GetPropertyValue("AdvancedLockpicking", "MaxGiveAmount").Split(',');
var breakTimes = "1.2,1.0,.8,.6".Split(','); //Configuration.GetPropertyValue("AdvancedLockpicking", "BreakTime").Split(',');
// Default values.
maxGiveAmount = 10f;
breakTime = 2f;
if (maxGiveAmounts.Length >= difficulty)
{
maxGiveAmount = StringParsers.ParseFloat(maxGiveAmounts[difficulty]);
breakTime = StringParsers.ParseFloat(breakTimes[difficulty]);
}
progressionValue = player.Progression.GetProgressionValue("perkLockPicking");
if (progressionValue is { Level: > 0 })
{
prevBreakTime = breakTime;
prevMaxGive = maxGiveAmount;
breakTime += (float)progressionValue.Level / 5;
maxGiveAmount += (float)progressionValue.Level * 2;
}
}
SetLock(minLockAngle, maxLockAngle,
minGiveAmount, maxGiveAmount,
minCloseDistance, maxCloseDistance);
if (GamePrefs.GetBool(EnumGamePrefs.DebugMenuEnabled))
{
var progression = " Progression: ";
progression = progressionValue != null ? $"{progression} Level {progressionValue.Level} BreakTime Before: {prevBreakTime} MaxGiveAmount Before: {prevMaxGive}" : $"{progression} N/A";
/*Log.Out("");
Log.Out("-------------------------------------------");
Log.Out($"Configured Lock Pick: Break Time: {breakTime} MaxGiveAmount: {maxGiveAmount} Lock Difficulty: {difficulty} Block Damage: {healthLeft} {progression} ");
Log.Out("Lock Angle: " + _lockAngle + " Give: " + _lockGive + " Close distance: " + _closeDistance);
Log.Out($"MinLockAngle: {minLockAngle} MaxLockAngle: {maxLockAngle} Min Give Amount: {minGiveAmount} Max Give Amount: {maxGiveAmount} Min Close distance: {minCloseDistance} Max Close Distance: {maxCloseDistance}");
Log.Out(" To Adjust, run console command lock 2 34");
Log.Out(" For breaktime of 2 and maxgive of 34");
Log.Out("-------------------------------------------");*/
}
RefreshLockPicks();
}
public void ResetLockpickPosition()
{
SetLockpickAngles(_pickAnglesDefault);
if (_padlockAnimator != null)
_padlockAnimator.SetTrigger(_closeTrigger);
}
public bool LockCanTurn()
{
return !(LockPickAngle() < GetAngle(_lockAngle) - _lockGive - _closeDistance * KeyholeTurnValue()) &&
!(LockPickAngle() > GetAngle(_lockAngle) + _lockGive + _closeDistance * KeyholeTurnValue());
}
public bool LockComplete()
{
if (!LockIsOpen)
return false;
if (padlock1 != null && padlock1.activeInHierarchy)
{
if (_padlockAnimator != null)
// If the padlock is fully animated
if (_padlockAnimator.GetCurrentAnimatorStateInfo(0).IsName("Padlock1Opened"))
return true;
}
else
{
return !audioOpen.isAudioPlaying();
}
return false;
}
#region ThirdParty
// Events
private readonly UnityEvent lockpickBroke = new UnityEvent();
private readonly UnityEvent lockOpen = new UnityEvent();
[Header("Player Input")] public float openPressure;
public float lockpickPressure;
[Header("Speed Settings")]
[Tooltip("Speed of the lockpick when input value is full.")]
[Range(1f, 720f)]
public float turnSpeedLockpick = 50f;
[Tooltip("Speed of the entire keyhole when input value is full.")]
[Range(1f, 720f)]
public float turnSpeedKeyhole = 50f;
[Tooltip("Speed at which the lock will return to normal when the input value is 0.")]
[Range(1f, 720f)]
public float returnSpeedKeyhole = 150f;
[Tooltip("Maximum shake distance per shake change.")]
[SerializeField]
private float maxShake = 0.5f;
[Tooltip("Amount of time between shake changes when shaking.")]
[SerializeField]
private float shakeTime = 0.1f;
[Header("Pick Settings")]
[Tooltip("Starting angle of the lock pick.")]
[SerializeField]
private Vector3 _pickAnglesDefault;
[Tooltip("Minimum angle the lock pick can travel to.")]
[SerializeField]
private float _pickAngleMin = -90f;
[Tooltip("Maximum angle the lock pick can travel to.")]
[SerializeField]
private float _pickAngleMax = 90f;
[Header("Keyhole Settings")]
[Tooltip("Starting angle of the keyhole.")]
[SerializeField]
private float _keyholeAngleDefault = 0f;
[Tooltip("Maximum angle of the keyhole. At this angle, the lock will open.")]
[SerializeField]
private float _keyholeAngleMax = 90f;
[Header("Lock Settings")]
[Tooltip("If true, lock details will be randomized on awake")]
public bool resetOnAwake = true;
[Tooltip("Minimum angle the lock can be set to.")]
[Range(0f, 180f)]
public float minLockAngle = -90f;
[Tooltip("Maximum angle the lock can be set to.")]
[Range(0f, 180f)]
public float maxLockAngle = 90f;
[Tooltip("Minimum distance (plus and minus) from the lock angle that the lock will open.")]
[Range(1f, 180f)]
public float minGiveAmount = 1f;
[Tooltip("Maximum distance (plus and minus) from the lock angle that the lock will open.")]
[Range(1f, 180f)]
public float maxGiveAmount = 30f;
[Tooltip("Minimum distance for the pick to be in for the lock will turn partially.")]
[Range(5f, 180f)]
public float minCloseDistance = 5f;
[Tooltip("Maximum distance for the pick to be in for the lock will turn partially.")]
[Range(5f, 180f)]
public float maxCloseDistance = 10f;
[Tooltip("Amount of time to ignore player input after a lock pick breaks.")]
[Range(0f, 5f)]
public float breakPause = 2f;
[Header("Lock Details")]
[Tooltip("True if the lock is already open (unlocked).")]
[SerializeField]
private bool _lockIsOpen;
public bool LockIsOpen
{
get => _lockIsOpen;
set => _lockIsOpen = value;
}
[Tooltip("The exact angle the lock is set to.")]
private float _lockAngle;
public float LockAngle
{
get => _lockAngle;
set => _lockAngle = Mathf.Clamp(value, minLockAngle, maxLockAngle);
}
[Tooltip("The distance to/from the LockAngle the lock pick needs to be in for the lock to open successfully.")]
[SerializeField]
private float _lockGive;
public float LockGive
{
get => _lockGive;
set => _lockGive = Mathf.Clamp(value, 1, maxGiveAmount);
}
[Tooltip("If the lock pick is within this distance to the angle range which the lock will open, the lock will turn partially when an open attempt is made.")]
[SerializeField]
private float _closeDistance;
public float CloseDistance
{
get => _closeDistance;
set => _closeDistance = Mathf.Clamp(value, 5, maxCloseDistance);
}
[Tooltip("The amount of time before a lock pick breaks when the lock is unable to be opened, but the player is attempting to open it.")]
[Range(0f, 5f)]
public float breakTime = 1f;
[Header("Animation Trigger Strings")] public string openTrigger = "OpenPadlock";
public string closeTrigger = "ClosePadlock";
public string lockPickBreakTrigger = "BreakLockpick1";
public string lockPickInsertTrigger = "InsertLockpick";
// Private animation hashes
private int _openTrigger;
private int _closeTrigger;
private int _lockpickBreakTrigger;
private int _lockpickInsertTrigger;
#endregion
}
}