Compare commits
1 Commits
a2a55a7135
...
keyinsertm
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ae5d28cc9 |
@@ -1,13 +1,24 @@
|
|||||||
|
using System;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
public class InserKeysMinigameManager : MonoBehaviour
|
public class InserKeysMinigameManager : MonoBehaviour, ITask
|
||||||
{
|
{
|
||||||
public int correctIndex;
|
public int correctIndex;
|
||||||
public bool isInputLocked = false;
|
public bool isInputLocked = false;
|
||||||
|
|
||||||
|
public string TaskID { get; set; }
|
||||||
|
|
||||||
|
public TaskType TaskType { get; set; }
|
||||||
|
|
||||||
|
public string TaskName { get; set; }
|
||||||
|
|
||||||
|
public (double, double) TaskLocation { get; set; }
|
||||||
|
public bool IsCompleted { get; private set; }
|
||||||
|
private Action<ITask> _onCompleted;
|
||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
correctIndex = Random.Range(0, 9);
|
correctIndex = UnityEngine.Random.Range(0, 9);
|
||||||
Debug.Log("The correct keyhole is: " + correctIndex);
|
Debug.Log("The correct keyhole is: " + correctIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,4 +26,22 @@ public class InserKeysMinigameManager : MonoBehaviour
|
|||||||
{
|
{
|
||||||
return index == correctIndex;
|
return index == correctIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Initialize(System.Action<ITask> onCompleted)
|
||||||
|
{
|
||||||
|
IsCompleted = false;
|
||||||
|
_onCompleted = onCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExitTask(System.Action<ITask> onExit)
|
||||||
|
{
|
||||||
|
onExit.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Complete()
|
||||||
|
{
|
||||||
|
IsCompleted = true;
|
||||||
|
_onCompleted?.Invoke(this);
|
||||||
|
ExitTask(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ public class Key : MonoBehaviour
|
|||||||
|
|
||||||
Debug.Log("You win");
|
Debug.Log("You win");
|
||||||
if (winText != null) winText.SetActive(true);
|
if (winText != null) winText.SetActive(true);
|
||||||
|
manager.Complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
8
Assets/Scripts.meta
Normal file
8
Assets/Scripts.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 50c3453b214b4c24487f630d82fff48b
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
20
Assets/Scripts/KeyIDManager.cs
Normal file
20
Assets/Scripts/KeyIDManager.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class KeyIDManager : MonoBehaviour
|
||||||
|
{
|
||||||
|
public int KeyID;
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
System.Random KeyID = new System.Random();
|
||||||
|
KeyID.Next(0,9);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update is called once per frame
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/KeyIDManager.cs.meta
Normal file
2
Assets/Scripts/KeyIDManager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4aa26e9ef84d88c4ebc0a9b29ac62e02
|
||||||
51
Assets/Scripts/WindController.cs
Normal file
51
Assets/Scripts/WindController.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class WindController : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("settings větru")]
|
||||||
|
[Tooltip("Maximální síla větru (kladná i záporná)")]
|
||||||
|
public float maxWindTorque = 8f;
|
||||||
|
|
||||||
|
[Tooltip("Jak rychle se větr mění směrem/sílou")]
|
||||||
|
public float windChangeSpeed = 0.6f;
|
||||||
|
|
||||||
|
[Tooltip("Jak často se objeví silnější vichřice (v sekundách)")]
|
||||||
|
public float gustInterval = 4f;
|
||||||
|
|
||||||
|
[Tooltip("Multiplier pro sílu vichřice")]
|
||||||
|
public float gustMultiplier = 2.0f;
|
||||||
|
|
||||||
|
public float CurrentWindTorque { get; private set; }
|
||||||
|
|
||||||
|
private float targetTorque;
|
||||||
|
private float gustTimer;
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
PickNewTargetTorque();
|
||||||
|
gustTimer = gustInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
// Smoothly move wind toward target torque
|
||||||
|
CurrentWindTorque = Mathf.Lerp(CurrentWindTorque, targetTorque, Time.deltaTime * windChangeSpeed);
|
||||||
|
|
||||||
|
// Occasional gusts
|
||||||
|
gustTimer -= Time.deltaTime;
|
||||||
|
if (gustTimer <= 0f)
|
||||||
|
{
|
||||||
|
// Apply a short gust by shifting target torque more aggressively
|
||||||
|
float gust = Random.Range(-maxWindTorque, maxWindTorque) * gustMultiplier;
|
||||||
|
targetTorque = Mathf.Clamp(gust, -maxWindTorque * gustMultiplier, maxWindTorque * gustMultiplier);
|
||||||
|
|
||||||
|
gustTimer = gustInterval;
|
||||||
|
Invoke(nameof(PickNewTargetTorque), 0.8f); // gust lasts ~0.8s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PickNewTargetTorque()
|
||||||
|
{
|
||||||
|
targetTorque = Random.Range(-maxWindTorque, maxWindTorque);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/WindController.cs.meta
Normal file
2
Assets/Scripts/WindController.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 58e3f22231584b9459fe7c44ee63e515
|
||||||
97
Assets/Scripts/insertkeys.cs
Normal file
97
Assets/Scripts/insertkeys.cs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
public class DraggableKey : MonoBehaviour,
|
||||||
|
IBeginDragHandler, IDragHandler, IEndDragHandler, ITask
|
||||||
|
{
|
||||||
|
[Header("Key Settings")]
|
||||||
|
public string keyID;
|
||||||
|
public string correctSlotID;
|
||||||
|
public string previousSceneName;
|
||||||
|
|
||||||
|
[Header("Visual")]
|
||||||
|
public Color wrongAttemptColor = Color.red;
|
||||||
|
public float blinkDuration = 0.2f;
|
||||||
|
|
||||||
|
private RectTransform rectTransform;
|
||||||
|
private CanvasGroup canvasGroup;
|
||||||
|
private Vector2 startPosition;
|
||||||
|
|
||||||
|
private bool isOverCorrectSlot = false;
|
||||||
|
|
||||||
|
|
||||||
|
public string TaskID { get; set; }
|
||||||
|
public TaskType TaskType { get; set; }
|
||||||
|
public string TaskName { get; set; }
|
||||||
|
public (double, double) TaskLocation { get; set; }
|
||||||
|
public bool IsCompleted { get; private set; }
|
||||||
|
|
||||||
|
private Action<ITask> _onCompleted;
|
||||||
|
private Action<ITask> _onExit;
|
||||||
|
|
||||||
|
public void Initialize(Action<ITask> onCompleted)
|
||||||
|
{
|
||||||
|
IsCompleted = false;
|
||||||
|
_onCompleted = onCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Complete()
|
||||||
|
{
|
||||||
|
if (IsCompleted) return;
|
||||||
|
|
||||||
|
IsCompleted = true;
|
||||||
|
_onCompleted?.Invoke(this);
|
||||||
|
ExitTask(_onExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExitTask(Action<ITask> onExit)
|
||||||
|
{
|
||||||
|
onExit?.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== UNITY =====
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
rectTransform = GetComponent<RectTransform>();
|
||||||
|
canvasGroup = GetComponent<CanvasGroup>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnBeginDrag(PointerEventData eventData)
|
||||||
|
{
|
||||||
|
startPosition = rectTransform.anchoredPosition;
|
||||||
|
canvasGroup.blocksRaycasts = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDrag(PointerEventData eventData)
|
||||||
|
{
|
||||||
|
rectTransform.anchoredPosition += eventData.delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnEndDrag(PointerEventData eventData)
|
||||||
|
{
|
||||||
|
canvasGroup.blocksRaycasts = true;
|
||||||
|
|
||||||
|
if (isOverCorrectSlot)
|
||||||
|
{
|
||||||
|
Complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void SetSlotMatch(bool value)
|
||||||
|
{
|
||||||
|
isOverCorrectSlot = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ResetPosition()
|
||||||
|
{
|
||||||
|
rectTransform.anchoredPosition = startPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/insertkeys.cs.meta
Normal file
2
Assets/Scripts/insertkeys.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 21dc5fa96a2ceec428d7b0332e55cbe5
|
||||||
137
Assets/Scripts/kabely.cs
Normal file
137
Assets/Scripts/kabely.cs
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
public class CableMiniGame : MonoBehaviour, ITask
|
||||||
|
{
|
||||||
|
[System.Serializable]
|
||||||
|
public class Cable
|
||||||
|
{
|
||||||
|
public string colorName;
|
||||||
|
public Button sourceButton;
|
||||||
|
public Button targetButton;
|
||||||
|
public Image cableImage;
|
||||||
|
|
||||||
|
[HideInInspector] public bool connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public string TaskID { get; set; }
|
||||||
|
public TaskType TaskType { get; set; }
|
||||||
|
public string TaskName { get; set; }
|
||||||
|
public (double, double) TaskLocation { get; set; }
|
||||||
|
public bool IsCompleted { get; private set; }
|
||||||
|
|
||||||
|
private Action<ITask> _onCompleted;
|
||||||
|
private Action<ITask> _onExit;
|
||||||
|
|
||||||
|
[Header("MiniGame Settings")]
|
||||||
|
public Cable[] cables;
|
||||||
|
public string previousSceneName;
|
||||||
|
public Color wrongAttemptColor = Color.white;
|
||||||
|
public float blinkDuration = 0.2f;
|
||||||
|
|
||||||
|
private string selectedColor = null;
|
||||||
|
|
||||||
|
|
||||||
|
public void Initialize(Action<ITask> onCompleted)
|
||||||
|
{
|
||||||
|
IsCompleted = false;
|
||||||
|
_onCompleted = onCompleted;
|
||||||
|
|
||||||
|
foreach (var cable in cables)
|
||||||
|
cable.connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Complete()
|
||||||
|
{
|
||||||
|
if (IsCompleted) return;
|
||||||
|
|
||||||
|
IsCompleted = true;
|
||||||
|
_onCompleted?.Invoke(this);
|
||||||
|
ExitTask(_onExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExitTask(Action<ITask> onExit)
|
||||||
|
{
|
||||||
|
onExit?.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
foreach (var cable in cables)
|
||||||
|
{
|
||||||
|
Cable localCable = cable;
|
||||||
|
|
||||||
|
cable.sourceButton.onClick.AddListener(() => OnSourceClicked(localCable));
|
||||||
|
cable.targetButton.onClick.AddListener(() => OnTargetClicked(localCable));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnSourceClicked(Cable cable)
|
||||||
|
{
|
||||||
|
if (cable.connected) return;
|
||||||
|
|
||||||
|
selectedColor = cable.colorName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTargetClicked(Cable cable)
|
||||||
|
{
|
||||||
|
if (selectedColor == null || cable.connected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (selectedColor == cable.colorName)
|
||||||
|
{
|
||||||
|
cable.cableImage.color = GetColorFromName(selectedColor);
|
||||||
|
cable.connected = true;
|
||||||
|
|
||||||
|
CheckCompletion();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StartCoroutine(BlinkAndExit(cable.cableImage));
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedColor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckCompletion()
|
||||||
|
{
|
||||||
|
foreach (var cable in cables)
|
||||||
|
{
|
||||||
|
if (!cable.connected)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
Color GetColorFromName(string name)
|
||||||
|
{
|
||||||
|
switch (name.ToLower())
|
||||||
|
{
|
||||||
|
case "red": return Color.red;
|
||||||
|
case "blue": return Color.blue;
|
||||||
|
case "green": return Color.green;
|
||||||
|
case "yellow": return Color.yellow;
|
||||||
|
case "purple": return new Color(0.5f, 0, 0.5f);
|
||||||
|
default: return Color.white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator BlinkAndExit(Image img)
|
||||||
|
{
|
||||||
|
Color original = img.color;
|
||||||
|
img.color = wrongAttemptColor;
|
||||||
|
|
||||||
|
yield return new WaitForSeconds(blinkDuration);
|
||||||
|
|
||||||
|
img.color = original;
|
||||||
|
|
||||||
|
ExitTask(_onExit);
|
||||||
|
SceneManager.LoadScene(previousSceneName);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/kabely.cs.meta
Normal file
2
Assets/Scripts/kabely.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d3fa58532362d2547ab6cd0ab17d7bde
|
||||||
30
Assets/Scripts/keysminiigamemanager.cs
Normal file
30
Assets/Scripts/keysminiigamemanager.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
public class KeyminigameManager : MonoBehaviour
|
||||||
|
{
|
||||||
|
public static KeyminigameManager Instance;
|
||||||
|
|
||||||
|
private int correctCount = 0;
|
||||||
|
public int totalKeys = 3;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
Instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CheckWin()
|
||||||
|
{
|
||||||
|
correctCount++;
|
||||||
|
|
||||||
|
if (correctCount >= totalKeys)
|
||||||
|
{
|
||||||
|
Debug.Log("WIN");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Fail()
|
||||||
|
{
|
||||||
|
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/keysminiigamemanager.cs.meta
Normal file
2
Assets/Scripts/keysminiigamemanager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0ef083a6749a8ce459b724dafa1eb08f
|
||||||
BIN
Assets/koulickaone.fbx
Normal file
BIN
Assets/koulickaone.fbx
Normal file
Binary file not shown.
110
Assets/koulickaone.fbx.meta
Normal file
110
Assets/koulickaone.fbx.meta
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e6ea78ef939933a4ea7f950af6ae316e
|
||||||
|
ModelImporter:
|
||||||
|
serializedVersion: 24200
|
||||||
|
internalIDToNameTable: []
|
||||||
|
externalObjects: {}
|
||||||
|
materials:
|
||||||
|
materialImportMode: 2
|
||||||
|
materialName: 0
|
||||||
|
materialSearch: 1
|
||||||
|
materialLocation: 1
|
||||||
|
animations:
|
||||||
|
legacyGenerateAnimations: 4
|
||||||
|
bakeSimulation: 0
|
||||||
|
resampleCurves: 1
|
||||||
|
optimizeGameObjects: 0
|
||||||
|
removeConstantScaleCurves: 0
|
||||||
|
motionNodeName:
|
||||||
|
animationImportErrors:
|
||||||
|
animationImportWarnings:
|
||||||
|
animationRetargetingWarnings:
|
||||||
|
animationDoRetargetingWarnings: 0
|
||||||
|
importAnimatedCustomProperties: 0
|
||||||
|
importConstraints: 0
|
||||||
|
animationCompression: 1
|
||||||
|
animationRotationError: 0.5
|
||||||
|
animationPositionError: 0.5
|
||||||
|
animationScaleError: 0.5
|
||||||
|
animationWrapMode: 0
|
||||||
|
extraExposedTransformPaths: []
|
||||||
|
extraUserProperties: []
|
||||||
|
clipAnimations: []
|
||||||
|
isReadable: 0
|
||||||
|
meshes:
|
||||||
|
lODScreenPercentages: []
|
||||||
|
globalScale: 1
|
||||||
|
meshCompression: 0
|
||||||
|
addColliders: 0
|
||||||
|
useSRGBMaterialColor: 1
|
||||||
|
sortHierarchyByName: 1
|
||||||
|
importPhysicalCameras: 1
|
||||||
|
importVisibility: 1
|
||||||
|
importBlendShapes: 1
|
||||||
|
importCameras: 1
|
||||||
|
importLights: 1
|
||||||
|
nodeNameCollisionStrategy: 1
|
||||||
|
fileIdsGeneration: 2
|
||||||
|
swapUVChannels: 0
|
||||||
|
generateSecondaryUV: 0
|
||||||
|
useFileUnits: 1
|
||||||
|
keepQuads: 0
|
||||||
|
weldVertices: 1
|
||||||
|
bakeAxisConversion: 0
|
||||||
|
preserveHierarchy: 0
|
||||||
|
skinWeightsMode: 0
|
||||||
|
maxBonesPerVertex: 4
|
||||||
|
minBoneWeight: 0.001
|
||||||
|
optimizeBones: 1
|
||||||
|
generateMeshLods: 0
|
||||||
|
meshLodGenerationFlags: 0
|
||||||
|
maximumMeshLod: -1
|
||||||
|
meshOptimizationFlags: -1
|
||||||
|
indexFormat: 0
|
||||||
|
secondaryUVAngleDistortion: 8
|
||||||
|
secondaryUVAreaDistortion: 15.000001
|
||||||
|
secondaryUVHardAngle: 88
|
||||||
|
secondaryUVMarginMethod: 1
|
||||||
|
secondaryUVMinLightmapResolution: 40
|
||||||
|
secondaryUVMinObjectScale: 1
|
||||||
|
secondaryUVPackMargin: 4
|
||||||
|
useFileScale: 1
|
||||||
|
strictVertexDataChecks: 0
|
||||||
|
tangentSpace:
|
||||||
|
normalSmoothAngle: 60
|
||||||
|
normalImportMode: 0
|
||||||
|
tangentImportMode: 3
|
||||||
|
normalCalculationMode: 4
|
||||||
|
legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
|
||||||
|
blendShapeNormalImportMode: 1
|
||||||
|
normalSmoothingSource: 0
|
||||||
|
referencedClips: []
|
||||||
|
importAnimation: 1
|
||||||
|
humanDescription:
|
||||||
|
serializedVersion: 3
|
||||||
|
human: []
|
||||||
|
skeleton: []
|
||||||
|
armTwist: 0.5
|
||||||
|
foreArmTwist: 0.5
|
||||||
|
upperLegTwist: 0.5
|
||||||
|
legTwist: 0.5
|
||||||
|
armStretch: 0.05
|
||||||
|
legStretch: 0.05
|
||||||
|
feetSpacing: 0
|
||||||
|
globalScale: 1
|
||||||
|
rootMotionBoneName:
|
||||||
|
hasTranslationDoF: 0
|
||||||
|
hasExtraRoot: 0
|
||||||
|
skeletonHasParents: 1
|
||||||
|
lastHumanDescriptionAvatarSource: {instanceID: 0}
|
||||||
|
autoGenerateAvatarMappingIfUnspecified: 1
|
||||||
|
animationType: 2
|
||||||
|
humanoidOversampling: 1
|
||||||
|
avatarSetup: 0
|
||||||
|
addHumanoidExtraRootOnlyWhenUsingAvatar: 1
|
||||||
|
importBlendShapeDeformPercent: 1
|
||||||
|
remapMaterialsIfMaterialImportModeIsNone: 0
|
||||||
|
additionalBone: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Reference in New Issue
Block a user