Installed Surge, fixed compile errors, moved a bunch of external stuff into folder
This commit is contained in:
412
Assets/External/Pixelplacement/Surge/Chooser/Chooser.cs
vendored
Normal file
412
Assets/External/Pixelplacement/Surge/Chooser/Chooser.cs
vendored
Normal file
@@ -0,0 +1,412 @@
|
||||
/// <summary>
|
||||
/// SURGE FRAMEWORK
|
||||
/// Author: Bob Berkebile
|
||||
/// Email: bobb@pixelplacement.com
|
||||
///
|
||||
/// Simplify the act of selecting and interacting with things.
|
||||
///
|
||||
/// </summary>
|
||||
|
||||
#pragma warning disable 0649
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Pixelplacement
|
||||
{
|
||||
public class Chooser : MonoBehaviour
|
||||
{
|
||||
//Public Events:
|
||||
public GameObjectEvent OnSelected;
|
||||
public GameObjectEvent OnDeselected;
|
||||
public GameObjectEvent OnPressed;
|
||||
public GameObjectEvent OnReleased;
|
||||
|
||||
//Public Enums:
|
||||
public enum Method { Raycast, RaycastAll };
|
||||
|
||||
//Public Variables:
|
||||
public bool _cursorPropertiesFolded;
|
||||
public bool _unityEventsFolded;
|
||||
public Transform source;
|
||||
public float raycastDistance = 3;
|
||||
public LayerMask layermask = -1;
|
||||
public KeyCode[] pressedInput;
|
||||
public Transform cursor;
|
||||
public float surfaceOffset;
|
||||
public float idleDistance = 3f;
|
||||
public float stabilityDelta = 0.0127f;
|
||||
public float snapDelta = 1;
|
||||
public float stableSpeed = 2;
|
||||
public float unstableSpeed = 20;
|
||||
public bool flipForward;
|
||||
public bool matchSurfaceNormal = true;
|
||||
public bool autoHide;
|
||||
public bool cursorHidden;
|
||||
public bool flipCastDirection;
|
||||
public LineRenderer lineRenderer;
|
||||
|
||||
//Public Properties:
|
||||
public Transform[] Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return _current.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsHitting
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
//Private Variables:
|
||||
[SerializeField] Method _method;
|
||||
[SerializeField] bool _debugView = false;
|
||||
Transform _previousCursor;
|
||||
List<Transform> _current = new List<Transform>();
|
||||
List<Transform> _previous = new List<Transform>();
|
||||
Transform _currentRaycast;
|
||||
Transform _previousRaycast;
|
||||
Vector3 _targetPosition;
|
||||
bool _hidden;
|
||||
|
||||
//Init:
|
||||
private void Reset()
|
||||
{
|
||||
source = transform;
|
||||
pressedInput = new KeyCode[] { KeyCode.Mouse0 };
|
||||
}
|
||||
|
||||
//Flow:
|
||||
private void OnEnable()
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
source = transform;
|
||||
}
|
||||
|
||||
if (cursor != null)
|
||||
{
|
||||
cursor.position = source.position;
|
||||
cursor.gameObject.SetActive(true);
|
||||
}
|
||||
if (lineRenderer != null)
|
||||
{
|
||||
lineRenderer.positionCount = 0;
|
||||
lineRenderer.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (cursor != null) cursor.gameObject.SetActive(false);
|
||||
if (lineRenderer != null) lineRenderer.enabled = false;
|
||||
}
|
||||
|
||||
//Gizmos:
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (Application.isPlaying) return;
|
||||
|
||||
Vector3 castDirection = source.forward;
|
||||
if (flipCastDirection) castDirection *= -1;
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawRay(source.position, castDirection * raycastDistance);
|
||||
|
||||
if (cursor != null)
|
||||
{
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawLine(source.position, cursor.position);
|
||||
}
|
||||
}
|
||||
|
||||
//Public Methods:
|
||||
public void Pressed()
|
||||
{
|
||||
switch (_method)
|
||||
{
|
||||
case Method.Raycast:
|
||||
if (_currentRaycast != null)
|
||||
{
|
||||
_currentRaycast.SendMessage("Pressed", SendMessageOptions.DontRequireReceiver);
|
||||
if (OnPressed != null) OnPressed.Invoke(_currentRaycast.gameObject);
|
||||
}
|
||||
break;
|
||||
|
||||
case Method.RaycastAll:
|
||||
if (_current.Count > 0)
|
||||
{
|
||||
foreach (var item in _current)
|
||||
{
|
||||
item.SendMessage("Pressed", SendMessageOptions.DontRequireReceiver);
|
||||
if (OnPressed != null) OnPressed.Invoke(item.gameObject);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Released()
|
||||
{
|
||||
switch (_method)
|
||||
{
|
||||
case Method.Raycast:
|
||||
if (_currentRaycast != null)
|
||||
{
|
||||
_currentRaycast.SendMessage("Released", SendMessageOptions.DontRequireReceiver);
|
||||
if (OnReleased != null) OnReleased.Invoke(_currentRaycast.gameObject);
|
||||
}
|
||||
break;
|
||||
|
||||
case Method.RaycastAll:
|
||||
if (_current.Count > 0)
|
||||
{
|
||||
foreach (var item in _current)
|
||||
{
|
||||
item.SendMessage("Released", SendMessageOptions.DontRequireReceiver);
|
||||
if (OnReleased != null) OnReleased.Invoke(item.gameObject);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Loops:
|
||||
private void Update()
|
||||
{
|
||||
//cursor setup:
|
||||
if (cursor != _previousCursor)
|
||||
{
|
||||
_previousCursor = cursor;
|
||||
if (cursor == null) return;
|
||||
|
||||
foreach (var item in cursor.GetComponentsInChildren<Collider>())
|
||||
{
|
||||
Debug.Log("Cursor can not contain colliders. Disabling colliders on: " + item.name);
|
||||
item.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
//process input:
|
||||
if (pressedInput != null)
|
||||
{
|
||||
foreach (var item in pressedInput)
|
||||
{
|
||||
/* if (Input.GetKeyDown(item))
|
||||
{
|
||||
Pressed();
|
||||
}
|
||||
|
||||
if (Input.GetKeyUp(item))
|
||||
{
|
||||
Released();
|
||||
} */
|
||||
}
|
||||
}
|
||||
|
||||
//clear out:
|
||||
_current.Clear();
|
||||
|
||||
//raycast:
|
||||
RaycastHit hit;
|
||||
Vector3 castDirection = source.forward;
|
||||
if (flipCastDirection) castDirection *= -1;
|
||||
Physics.Raycast(source.position, castDirection, out hit, raycastDistance, layermask);
|
||||
_currentRaycast = hit.transform;
|
||||
IsHitting = hit.transform != null;
|
||||
|
||||
//cache:
|
||||
if (_method == Method.Raycast && IsHitting)
|
||||
{
|
||||
_current.Clear();
|
||||
_current.Add(hit.transform);
|
||||
}
|
||||
|
||||
//debug info:
|
||||
if (_debugView)
|
||||
{
|
||||
if (hit.transform != null)
|
||||
{
|
||||
Debug.DrawLine(source.position, hit.point, Color.green);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.DrawRay(source.position, castDirection * raycastDistance, Color.red);
|
||||
}
|
||||
}
|
||||
|
||||
//cursor visibility:
|
||||
if (cursor != null)
|
||||
{
|
||||
if (cursorHidden)
|
||||
{
|
||||
cursor.gameObject.SetActive(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (autoHide)
|
||||
{
|
||||
cursor.gameObject.SetActive(IsHitting);
|
||||
if (lineRenderer != null) lineRenderer.enabled = IsHitting;
|
||||
}
|
||||
else
|
||||
{
|
||||
cursor.gameObject.SetActive(true);
|
||||
if (lineRenderer != null) lineRenderer.enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//cursor management:
|
||||
if (cursor != null)
|
||||
{
|
||||
if (hit.transform != null)
|
||||
{
|
||||
//get position:
|
||||
_targetPosition = hit.point + hit.normal * surfaceOffset;
|
||||
|
||||
//get position speed:
|
||||
float posSpeed = unstableSpeed;
|
||||
float delta = Vector3.Distance(_targetPosition, cursor.position);
|
||||
if (delta <= stabilityDelta)
|
||||
{
|
||||
posSpeed = stableSpeed;
|
||||
}
|
||||
|
||||
if (delta >= snapDelta)
|
||||
{
|
||||
cursor.position = _targetPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
cursor.position = Vector3.Lerp(cursor.position, _targetPosition, Time.unscaledDeltaTime * posSpeed);
|
||||
}
|
||||
|
||||
//set rotation:
|
||||
if (matchSurfaceNormal)
|
||||
{
|
||||
cursor.rotation = Quaternion.LookRotation(hit.normal, source.up);
|
||||
}
|
||||
else
|
||||
{
|
||||
cursor.LookAt(source, Vector3.up);
|
||||
}
|
||||
|
||||
//adjust:
|
||||
if (flipForward)
|
||||
{
|
||||
cursor.Rotate(Vector3.up * 180);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//put out in front and face source (flip if needed):
|
||||
Vector3 inFront = source.position + castDirection * idleDistance;
|
||||
float delta = Vector3.Distance(inFront, cursor.position);
|
||||
float posSpeed = unstableSpeed;
|
||||
|
||||
if (delta <= stabilityDelta)
|
||||
{
|
||||
posSpeed = stableSpeed;
|
||||
}
|
||||
|
||||
if (delta >= snapDelta)
|
||||
{
|
||||
cursor.position = inFront;
|
||||
}
|
||||
else
|
||||
{
|
||||
cursor.position = Vector3.Lerp(cursor.position, inFront, Time.unscaledDeltaTime * posSpeed);
|
||||
}
|
||||
|
||||
cursor.LookAt(source.position);
|
||||
if (flipForward)
|
||||
{
|
||||
cursor.Rotate(Vector3.up * 180);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//handle raycast messages:
|
||||
if (_method == Method.Raycast)
|
||||
{
|
||||
//select:
|
||||
if (_previousRaycast == null && hit.transform != null)
|
||||
{
|
||||
hit.transform.SendMessage("Selected", SendMessageOptions.DontRequireReceiver);
|
||||
if (OnSelected != null) OnSelected.Invoke(hit.transform.gameObject);
|
||||
}
|
||||
|
||||
//updated select:
|
||||
if (hit.transform != null && _previousRaycast != null && _previousRaycast != hit.transform)
|
||||
{
|
||||
_previousRaycast.SendMessage("Deselected", SendMessageOptions.DontRequireReceiver);
|
||||
if (OnDeselected != null) OnDeselected.Invoke(_previousRaycast.gameObject);
|
||||
hit.transform.SendMessage("Selected", SendMessageOptions.DontRequireReceiver);
|
||||
if (OnSelected != null) OnSelected.Invoke(hit.transform.gameObject);
|
||||
}
|
||||
|
||||
//deselect:
|
||||
if (_previousRaycast != null && hit.transform == null)
|
||||
{
|
||||
_previousRaycast.SendMessage("Deselected", SendMessageOptions.DontRequireReceiver);
|
||||
if (OnDeselected != null) OnDeselected.Invoke(_previousRaycast.gameObject);
|
||||
}
|
||||
|
||||
//cache:
|
||||
_previousRaycast = hit.transform;
|
||||
}
|
||||
|
||||
//raycast all:
|
||||
if (_method == Method.RaycastAll)
|
||||
{
|
||||
//catalog:
|
||||
foreach (var item in Physics.RaycastAll(source.position, castDirection, raycastDistance, layermask))
|
||||
{
|
||||
_current.Add(item.transform);
|
||||
}
|
||||
|
||||
//handle selects:
|
||||
if (_current.Count > 0)
|
||||
{
|
||||
foreach (var item in _current)
|
||||
{
|
||||
if (_previous.Count == 0 || !_previous.Contains(item))
|
||||
{
|
||||
item.SendMessage("Selected", SendMessageOptions.DontRequireReceiver);
|
||||
if (OnSelected != null) OnSelected.Invoke(item.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//handle deselects:
|
||||
if (_previous.Count > 0)
|
||||
{
|
||||
foreach (var item in _previous)
|
||||
{
|
||||
if (_current.Count == 0 || !_current.Contains(item))
|
||||
{
|
||||
item.SendMessage("Deselected", SendMessageOptions.DontRequireReceiver);
|
||||
if (OnDeselected != null) OnDeselected.Invoke(item.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//cache:
|
||||
_previous.Clear();
|
||||
_previous.AddRange(_current);
|
||||
}
|
||||
|
||||
//line renderer:
|
||||
if (cursor != null && cursor.gameObject.activeSelf && lineRenderer != null )
|
||||
{
|
||||
if (lineRenderer.positionCount != 2) lineRenderer.positionCount = 2;
|
||||
lineRenderer.SetPosition(0, source.position);
|
||||
lineRenderer.SetPosition(1, cursor.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Assets/External/Pixelplacement/Surge/Chooser/Chooser.cs.meta
vendored
Normal file
20
Assets/External/Pixelplacement/Surge/Chooser/Chooser.cs.meta
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db04a89df0755a04cb9d5f03bb62b19d
|
||||
timeCreated: 1521224863
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 107312
|
||||
packageName: Surge
|
||||
packageVersion: 1.0.48
|
||||
assetPath: Assets/Pixelplacement/Surge/Chooser/Chooser.cs
|
||||
uploadId: 467433
|
||||
9
Assets/External/Pixelplacement/Surge/Chooser/Editor.meta
vendored
Normal file
9
Assets/External/Pixelplacement/Surge/Chooser/Editor.meta
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d863bca9b2680743bd08a98ae378772
|
||||
folderAsset: yes
|
||||
timeCreated: 1522266281
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
106
Assets/External/Pixelplacement/Surge/Chooser/Editor/ChooserEditor.cs
vendored
Normal file
106
Assets/External/Pixelplacement/Surge/Chooser/Editor/ChooserEditor.cs
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
/// <summary>
|
||||
/// SURGE FRAMEWORK
|
||||
/// Author: Bob Berkebile
|
||||
/// Email: bobb@pixelplacement.com
|
||||
///
|
||||
/// Custom inspector Chooser.
|
||||
///
|
||||
/// </summary>
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Pixelplacement
|
||||
{
|
||||
[CustomEditor(typeof(Chooser), true)]
|
||||
[CanEditMultipleObjects]
|
||||
public class ChooserEditor : UnityEditor.Editor
|
||||
{
|
||||
//Private Variables:
|
||||
Chooser _target;
|
||||
|
||||
//Flow:
|
||||
void OnEnable()
|
||||
{
|
||||
_target = target as Chooser;
|
||||
}
|
||||
|
||||
//Inspector GUI:
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
DrawPropertiesExcluding(serializedObject, new string[] {
|
||||
"OnSelected",
|
||||
"OnDeselected",
|
||||
"OnPressed",
|
||||
"OnReleased",
|
||||
"_cursorPropertiesFolded",
|
||||
"_unityEventsFolded",
|
||||
"source",
|
||||
"raycastDistance",
|
||||
"layermask",
|
||||
"pressedInput",
|
||||
"cursor",
|
||||
"surfaceOffset",
|
||||
"idleDistance",
|
||||
"stabilityDelta",
|
||||
"snapDelta",
|
||||
"stableSpeed",
|
||||
"unstableSpeed",
|
||||
"flipForward",
|
||||
"matchSurfaceNormal",
|
||||
"autoHide",
|
||||
"cursorHidden",
|
||||
"flipCastDirection",
|
||||
"lineRenderer",
|
||||
"_debugView",
|
||||
"_method"
|
||||
});
|
||||
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("source"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("raycastDistance"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("flipCastDirection"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("layermask"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("_method"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("_debugView"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("pressedInput"), true);
|
||||
|
||||
_target._cursorPropertiesFolded = EditorGUILayout.Foldout(_target._cursorPropertiesFolded, "Cursor Properties", true);
|
||||
if (_target._cursorPropertiesFolded)
|
||||
{
|
||||
EditorGUI.indentLevel = 1;
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("cursor"));
|
||||
GUI.enabled = _target.cursor != null;
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("cursorHidden"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("lineRenderer"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("surfaceOffset"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("idleDistance"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("stabilityDelta"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("snapDelta"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("stableSpeed"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("unstableSpeed"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("flipForward"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("matchSurfaceNormal"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("autoHide"));
|
||||
EditorGUI.indentLevel = 0;
|
||||
GUI.enabled = true;
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
_target._unityEventsFolded = EditorGUILayout.Foldout(_target._unityEventsFolded, "Unity Events", true);
|
||||
if (_target._unityEventsFolded)
|
||||
{
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("OnSelected"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("OnPressed"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("OnReleased"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("OnDeselected"));
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Assets/External/Pixelplacement/Surge/Chooser/Editor/ChooserEditor.cs.meta
vendored
Normal file
19
Assets/External/Pixelplacement/Surge/Chooser/Editor/ChooserEditor.cs.meta
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad17b9c71d7903743aa42f462c8826fb
|
||||
timeCreated: 1522266293
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 107312
|
||||
packageName: Surge
|
||||
packageVersion: 1.0.48
|
||||
assetPath: Assets/Pixelplacement/Surge/Chooser/Editor/ChooserEditor.cs
|
||||
uploadId: 467433
|
||||
9
Assets/External/Pixelplacement/Surge/Chooser/Interfaces.meta
vendored
Normal file
9
Assets/External/Pixelplacement/Surge/Chooser/Interfaces.meta
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e72ba21481229f4d94aad43a786efae
|
||||
folderAsset: yes
|
||||
timeCreated: 1524507533
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
10
Assets/External/Pixelplacement/Surge/Chooser/Interfaces/IChooser.cs
vendored
Normal file
10
Assets/External/Pixelplacement/Surge/Chooser/Interfaces/IChooser.cs
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Pixelplacement
|
||||
{
|
||||
interface IChooser
|
||||
{
|
||||
void Selected();
|
||||
void Deselected();
|
||||
void Pressed();
|
||||
void Released();
|
||||
}
|
||||
}
|
||||
19
Assets/External/Pixelplacement/Surge/Chooser/Interfaces/IChooser.cs.meta
vendored
Normal file
19
Assets/External/Pixelplacement/Surge/Chooser/Interfaces/IChooser.cs.meta
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fbf5a06b4e893814c8820c3be2ddcb66
|
||||
timeCreated: 1524507546
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 107312
|
||||
packageName: Surge
|
||||
packageVersion: 1.0.48
|
||||
assetPath: Assets/Pixelplacement/Surge/Chooser/Interfaces/IChooser.cs
|
||||
uploadId: 467433
|
||||
Reference in New Issue
Block a user