레스토랑 오더 상호작용 서브시스템 도입 및 주문/서빙 솔버 추가, 관련 상호작용/캐릭터 로직 갱신
This commit is contained in:
parent
4c7d9c17e6
commit
a28415330c
@ -3,11 +3,22 @@
|
|||||||
|
|
||||||
namespace DDD
|
namespace DDD
|
||||||
{
|
{
|
||||||
public interface IInteractionSubsystemObject<T> where T : Enum
|
public interface IInteractionSubsystemObject
|
||||||
|
{
|
||||||
|
Type GetSubsystemEnumType();
|
||||||
|
void InitializeSubsystem();
|
||||||
|
bool CanInteract();
|
||||||
|
bool OnInteracted(IInteractor interactor, ScriptableObject payloadSo = null);
|
||||||
|
}
|
||||||
|
public interface IInteractionSubsystemObject<T> : IInteractionSubsystemObject where T : Enum
|
||||||
{
|
{
|
||||||
T GetInteractionSubsystemType();
|
T GetInteractionSubsystemType();
|
||||||
}
|
}
|
||||||
public interface IInteractionSubsystemSolver<T> where T : Enum
|
|
||||||
|
public interface IInteractionSubsystemSolver
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public interface IInteractionSubsystemSolver<T> : IInteractionSubsystemSolver where T : Enum
|
||||||
{
|
{
|
||||||
bool ExecuteInteractionSubsystem(IInteractor interactor, IInteractable interactable, ScriptableObject payloadSo = null);
|
bool ExecuteInteractionSubsystem(IInteractor interactor, IInteractable interactable, ScriptableObject payloadSo = null);
|
||||||
bool CanExecuteInteractionSubsystem(IInteractor interactor = null, IInteractable interactable = null,
|
bool CanExecuteInteractionSubsystem(IInteractor interactor = null, IInteractable interactable = null,
|
||||||
|
@ -23,7 +23,7 @@ protected virtual void Start()
|
|||||||
var flag = typeToSolver.Key;
|
var flag = typeToSolver.Key;
|
||||||
if (flag == InteractionType.None) continue;
|
if (flag == InteractionType.None) continue;
|
||||||
|
|
||||||
if ((_interactionComponent.InteractionType & flag) == 0) continue;
|
if ((_interactionComponent.AvailableInteractions & flag) == 0) continue;
|
||||||
|
|
||||||
if (!TryGetComponent(typeToSolver.Value, out _))
|
if (!TryGetComponent(typeToSolver.Value, out _))
|
||||||
{
|
{
|
||||||
|
@ -6,10 +6,10 @@ namespace DDD
|
|||||||
{
|
{
|
||||||
public class RestaurantCharacterInteraction : MonoBehaviour, IInteractor, IEventHandler<RestaurantInteractionEvent>
|
public class RestaurantCharacterInteraction : MonoBehaviour, IInteractor, IEventHandler<RestaurantInteractionEvent>
|
||||||
{
|
{
|
||||||
[EnumToggleButtons, SerializeField] protected InteractionType _interactionType;
|
[EnumToggleButtons, SerializeField] protected InteractionType _availableInteractions;
|
||||||
[SerializeField, ReadOnly] protected Collider[] _nearColliders = new Collider[10];
|
[SerializeField, ReadOnly] protected Collider[] _nearColliders = new Collider[10];
|
||||||
|
|
||||||
public InteractionType InteractionType => _interactionType;
|
public InteractionType AvailableInteractions => _availableInteractions;
|
||||||
|
|
||||||
protected IInteractable _nearestInteractable;
|
protected IInteractable _nearestInteractable;
|
||||||
protected IInteractable _previousInteractable;
|
protected IInteractable _previousInteractable;
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
using System;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace DDD
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
public enum RestaurantOrderInteractionType : uint
|
|
||||||
{
|
|
||||||
// None = 0u,
|
|
||||||
WaitCustomer = 0,
|
|
||||||
// WaitCustomer = 1u << 0,
|
|
||||||
// WaitOrder = 1u << 1,
|
|
||||||
// WaitServe = 1u << 2,
|
|
||||||
// All = 0xFFFFFFFFu
|
|
||||||
}
|
|
||||||
public class RestaurantOrderInteraction : RestaurantInteractionComponent, IInteractionSubsystemObject<RestaurantOrderInteractionType>
|
|
||||||
{
|
|
||||||
[SerializeField] protected RestaurantOrderInteractionType _initialOrderInteractionType = RestaurantOrderInteractionType.WaitCustomer;
|
|
||||||
private RestaurantOrderInteractionType _currentRestaurantOrderInteractionType;
|
|
||||||
|
|
||||||
// EDITOR
|
|
||||||
private void Reset()
|
|
||||||
{
|
|
||||||
SetInteractionTypeToRestaurantOrder();
|
|
||||||
}
|
|
||||||
private void OnValidate()
|
|
||||||
{
|
|
||||||
SetInteractionTypeToRestaurantOrder();
|
|
||||||
}
|
|
||||||
// ~EDITOR
|
|
||||||
|
|
||||||
private void Start()
|
|
||||||
{
|
|
||||||
_currentRestaurantOrderInteractionType = _initialOrderInteractionType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetInteractionTypeToRestaurantOrder()
|
|
||||||
{
|
|
||||||
_interactionType = InteractionType.RestaurantOrder;
|
|
||||||
}
|
|
||||||
public override InteractionType GetInteractionType()
|
|
||||||
{
|
|
||||||
return InteractionType.RestaurantOrder;
|
|
||||||
}
|
|
||||||
public override bool CanInteract()
|
|
||||||
{
|
|
||||||
// 현재 RestaurantOrderInteractionType를 수행할 수 있는지?
|
|
||||||
if (GetInteractionSubsystemType() == RestaurantOrderInteractionType.WaitCustomer)
|
|
||||||
{
|
|
||||||
// Check WaitCustomer
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool OnInteracted(IInteractor interactor, ScriptableObject payloadSo = null)
|
|
||||||
{
|
|
||||||
// _currentRestaurantOrderInteractionType에 따라 동작이 달라지겠지
|
|
||||||
if (GetInteractionSubsystemType() == RestaurantOrderInteractionType.WaitCustomer)
|
|
||||||
{
|
|
||||||
// DO WAIT CUSTOMER
|
|
||||||
}
|
|
||||||
return base.OnInteracted(interactor, payloadSo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void InitializeInteraction(InteractionType interactionType)
|
|
||||||
{
|
|
||||||
// RestaurantOrderInteractionType에 따른 동작들을 초기화
|
|
||||||
// Initialize WaitCustomer actions
|
|
||||||
base.InitializeInteraction(interactionType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RestaurantOrderInteractionType GetInteractionSubsystemType()
|
|
||||||
{
|
|
||||||
return _currentRestaurantOrderInteractionType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace DDD
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
public enum RestaurantOrderType : uint
|
||||||
|
{
|
||||||
|
Wait = 0,
|
||||||
|
Order = 1u << 0,
|
||||||
|
Serve = 1u << 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RestaurantOrderInteractionSubsystem : MonoBehaviour, IInteractionSubsystemObject<RestaurantOrderType>
|
||||||
|
{
|
||||||
|
[SerializeField] protected RestaurantOrderType orderType = RestaurantOrderType.Wait;
|
||||||
|
private RestaurantOrderType currentRestaurantOrderType;
|
||||||
|
|
||||||
|
public Type GetSubsystemEnumType() => typeof(RestaurantOrderType);
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
currentRestaurantOrderType = orderType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanInteract()
|
||||||
|
{
|
||||||
|
// 현재 RestaurantOrderInteractionType를 수행할 수 있는지?
|
||||||
|
if (GetInteractionSubsystemType() == RestaurantOrderType.Wait)
|
||||||
|
{
|
||||||
|
Debug.Assert(false); // TODO
|
||||||
|
// Check WaitCustomer
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnInteracted(IInteractor interactor, ScriptableObject payloadSo = null)
|
||||||
|
{
|
||||||
|
// _currentRestaurantOrderInteractionType에 따라 동작이 달라지겠지
|
||||||
|
if (GetInteractionSubsystemType() == RestaurantOrderType.Wait)
|
||||||
|
{
|
||||||
|
// DO WAIT CUSTOMER
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializeSubsystem()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public RestaurantOrderType GetInteractionSubsystemType()
|
||||||
|
{
|
||||||
|
return currentRestaurantOrderType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using Sirenix.OdinInspector;
|
||||||
|
|
||||||
namespace DDD
|
namespace DDD
|
||||||
{
|
{
|
||||||
|
public static class RestaurantInteractionSubsystems
|
||||||
|
{
|
||||||
|
public static Dictionary<InteractionType, Type> TypeToSubsystem = new()
|
||||||
|
{
|
||||||
|
{InteractionType.RestaurantOrder, typeof(RestaurantOrderInteractionSubsystem)}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public class RestaurantInteractionComponent : MonoBehaviour, IInteractable
|
public class RestaurantInteractionComponent : MonoBehaviour, IInteractable
|
||||||
{
|
{
|
||||||
|
// Single interaction type
|
||||||
|
[ValueDropdown("GetAllInteractionTypes")]
|
||||||
[SerializeField] protected InteractionType _interactionType = InteractionType.None;
|
[SerializeField] protected InteractionType _interactionType = InteractionType.None;
|
||||||
[SerializeField] protected InteractionExecutionParameters _executionParameters = new InteractionExecutionParameters(1f);
|
[SerializeField] protected InteractionExecutionParameters _executionParameters = new InteractionExecutionParameters(1f);
|
||||||
[SerializeField] protected InteractionDisplayParameters _displayParameters = new InteractionDisplayParameters("");
|
[SerializeField] protected InteractionDisplayParameters _displayParameters = new InteractionDisplayParameters("");
|
||||||
[SerializeField] protected GameFlowState _interactionAvailableFlows;
|
[SerializeField] protected GameFlowState _interactionAvailableFlows;
|
||||||
[SerializeField] private Transform[] _aiInteractionPoints;
|
[SerializeField] private Transform[] _aiInteractionPoints;
|
||||||
|
|
||||||
|
private Dictionary<InteractionType, IInteractionSubsystemObject> _subsystems = new();
|
||||||
|
|
||||||
|
private static IEnumerable GetAllInteractionTypes()
|
||||||
|
{
|
||||||
|
return System.Enum.GetValues(typeof(InteractionType))
|
||||||
|
.Cast<InteractionType>()
|
||||||
|
.Where(x => x != InteractionType.All); // All은 제외
|
||||||
|
}
|
||||||
|
|
||||||
public virtual bool CanInteract()
|
public virtual bool CanInteract()
|
||||||
{
|
{
|
||||||
return !IsInteractionHidden();
|
bool isInteractionVisible = !IsInteractionHidden();
|
||||||
|
bool hasValidSubsystem = true;
|
||||||
|
if (HasSubsystem(_interactionType))
|
||||||
|
{
|
||||||
|
hasValidSubsystem = GetSubsystem(_interactionType).CanInteract();
|
||||||
|
}
|
||||||
|
return isInteractionVisible && hasValidSubsystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool IsInteractionHidden()
|
public virtual bool IsInteractionHidden()
|
||||||
@ -31,6 +61,10 @@ public virtual bool OnInteracted(IInteractor interactor, ScriptableObject payloa
|
|||||||
}
|
}
|
||||||
bool interactionResult = RestaurantInteractionEvents.RestaurantInteraction.RequestInteraction(interactor.GetInteractorGameObject(),
|
bool interactionResult = RestaurantInteractionEvents.RestaurantInteraction.RequestInteraction(interactor.GetInteractorGameObject(),
|
||||||
GetInteractableGameObject(), GetInteractionType(), payloadSo, true);
|
GetInteractableGameObject(), GetInteractionType(), payloadSo, true);
|
||||||
|
if (HasSubsystem(_interactionType))
|
||||||
|
{
|
||||||
|
interactionResult &= GetSubsystem(_interactionType).OnInteracted(interactor, payloadSo);
|
||||||
|
}
|
||||||
return interactionResult;
|
return interactionResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,6 +81,33 @@ public GameObject GetInteractableGameObject()
|
|||||||
public virtual void InitializeInteraction(InteractionType interactionType)
|
public virtual void InitializeInteraction(InteractionType interactionType)
|
||||||
{
|
{
|
||||||
_interactionType = interactionType;
|
_interactionType = interactionType;
|
||||||
|
|
||||||
|
InitializeSubsystems();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeSubsystems()
|
||||||
|
{
|
||||||
|
// Initialize Interaction Subsystems
|
||||||
|
bool hasSubsystemType = RestaurantInteractionSubsystems.TypeToSubsystem.TryGetValue(_interactionType, out var subsystemType);
|
||||||
|
if (!hasSubsystemType) return;
|
||||||
|
|
||||||
|
var subsystem = gameObject.GetComponent(subsystemType) as IInteractionSubsystemObject;
|
||||||
|
if (subsystem == null)
|
||||||
|
{
|
||||||
|
subsystem = gameObject.AddComponent(subsystemType) as IInteractionSubsystemObject;
|
||||||
|
}
|
||||||
|
_subsystems.Add(_interactionType, subsystem);
|
||||||
|
subsystem?.InitializeSubsystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasSubsystem(InteractionType interactionType)
|
||||||
|
{
|
||||||
|
return _subsystems.ContainsKey(interactionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IInteractionSubsystemObject GetSubsystem(InteractionType interactionType)
|
||||||
|
{
|
||||||
|
return _subsystems.GetValueOrDefault(interactionType) as IInteractionSubsystemObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 새로운 스트럭트 기반 메서드들
|
// 새로운 스트럭트 기반 메서드들
|
||||||
|
@ -1,48 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using DDD.RestaurantOrders;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace DDD
|
namespace DDD
|
||||||
{
|
{
|
||||||
public class RestaurantOrderSolver : MonoBehaviour, IInteractionSolver, IInteractionSubsystemSolver<RestaurantOrderInteractionType>
|
public static class RestaurantOrderSolvers
|
||||||
{
|
{
|
||||||
|
public static Dictionary<RestaurantOrderType, Type> TypeToOrderSolver = new()
|
||||||
|
{
|
||||||
|
{ RestaurantOrderType.Wait, typeof(RestaurantOrderSolver_Wait) },
|
||||||
|
{ RestaurantOrderType.Order, typeof(RestaurantOrderSolver_Order) },
|
||||||
|
{ RestaurantOrderType.Serve, typeof(RestaurantOrderSolver_Serve) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RestaurantOrderSolver : MonoBehaviour, IInteractionSolver
|
||||||
|
{
|
||||||
|
private Dictionary<RestaurantOrderType, IInteractionSubsystemSolver<RestaurantOrderType>> _solvers = new();
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
foreach (var orderSolver in RestaurantOrderSolvers.TypeToOrderSolver)
|
||||||
|
{
|
||||||
|
var solver = (IInteractionSubsystemSolver<RestaurantOrderType>)gameObject.AddComponent(orderSolver.Value);
|
||||||
|
_solvers.Add(orderSolver.Key, solver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool ExecuteInteraction(IInteractor interactor, IInteractable interactable, ScriptableObject payloadSo = null)
|
public bool ExecuteInteraction(IInteractor interactor, IInteractable interactable, ScriptableObject payloadSo = null)
|
||||||
{
|
{
|
||||||
return ExecuteInteractionSubsystem(interactor, interactable, payloadSo);
|
return TryGetSolver(interactable, out var solver) &&
|
||||||
|
solver.ExecuteInteractionSubsystem(interactor, interactable, payloadSo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanExecuteInteraction(IInteractor interactor = null, IInteractable interactable = null,
|
public bool CanExecuteInteraction(IInteractor interactor = null, IInteractable interactable = null,
|
||||||
ScriptableObject payloadSo = null)
|
ScriptableObject payloadSo = null)
|
||||||
{
|
{
|
||||||
return CanExecuteInteractionSubsystem(interactor, interactable, payloadSo);
|
return TryGetSolver(interactable, out var solver) &&
|
||||||
|
solver.CanExecuteInteractionSubsystem(interactor, interactable, payloadSo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ExecuteInteractionSubsystem(IInteractor interactor, IInteractable interactable, ScriptableObject payloadSo = null)
|
// Solver를 가져오는 공통 로직
|
||||||
|
private bool TryGetSolver(IInteractable interactable, out IInteractionSubsystemSolver<RestaurantOrderType> solver)
|
||||||
{
|
{
|
||||||
if (interactable is IInteractionSubsystemObject<RestaurantOrderInteractionType> subsystem)
|
solver = null;
|
||||||
{
|
|
||||||
RestaurantOrderInteractionType interactionType = subsystem.GetInteractionSubsystemType();
|
if (interactable is not IInteractionSubsystemObject<RestaurantOrderType> subsystem)
|
||||||
// Can I solve this interaction type?
|
return false;
|
||||||
if (interactionType == RestaurantOrderInteractionType.WaitCustomer)
|
|
||||||
{
|
var type = subsystem.GetInteractionSubsystemType();
|
||||||
// DO SOMETHING!!!
|
return _solvers.TryGetValue(type, out solver);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CanExecuteInteractionSubsystem(IInteractor interactor = null, IInteractable interactable = null,
|
|
||||||
ScriptableObject payloadSo = null)
|
|
||||||
{
|
|
||||||
if (interactable is IInteractionSubsystemObject<RestaurantOrderInteractionType> subsystem)
|
|
||||||
{
|
|
||||||
RestaurantOrderInteractionType interactionType = subsystem.GetInteractionSubsystemType();
|
|
||||||
// Can I solve this interaction type?
|
|
||||||
if (interactionType == RestaurantOrderInteractionType.WaitCustomer)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0a7eea23af674c2aa6ffc20bd5801efb
|
||||||
|
timeCreated: 1755672003
|
@ -0,0 +1,19 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace DDD.RestaurantOrders
|
||||||
|
{
|
||||||
|
public class RestaurantOrderSolver_Order : MonoBehaviour, IInteractionSubsystemSolver<RestaurantOrderType>
|
||||||
|
{
|
||||||
|
public bool ExecuteInteractionSubsystem(IInteractor interactor, IInteractable interactable, ScriptableObject payloadSo = null)
|
||||||
|
{
|
||||||
|
// TODO : DO SOMETHING!!!
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanExecuteInteractionSubsystem(IInteractor interactor = null, IInteractable interactable = null,
|
||||||
|
ScriptableObject payloadSo = null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3afc1759b02e4230967b3b72fe354ea3
|
||||||
|
timeCreated: 1755672492
|
@ -0,0 +1,19 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace DDD.RestaurantOrders
|
||||||
|
{
|
||||||
|
public class RestaurantOrderSolver_Serve : MonoBehaviour, IInteractionSubsystemSolver<RestaurantOrderType>
|
||||||
|
{
|
||||||
|
public bool ExecuteInteractionSubsystem(IInteractor interactor, IInteractable interactable, ScriptableObject payloadSo = null)
|
||||||
|
{
|
||||||
|
// TODO : DO SOMETHING!!!
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanExecuteInteractionSubsystem(IInteractor interactor = null, IInteractable interactable = null,
|
||||||
|
ScriptableObject payloadSo = null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4df15c40347044648623d5932bb0724e
|
||||||
|
timeCreated: 1755672501
|
@ -0,0 +1,19 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace DDD.RestaurantOrders
|
||||||
|
{
|
||||||
|
public class RestaurantOrderSolver_Wait : MonoBehaviour, IInteractionSubsystemSolver<RestaurantOrderType>
|
||||||
|
{
|
||||||
|
public bool ExecuteInteractionSubsystem(IInteractor interactor, IInteractable interactable, ScriptableObject payloadSo = null)
|
||||||
|
{
|
||||||
|
// TODO : DO SOMETHING!!!
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanExecuteInteractionSubsystem(IInteractor interactor = null, IInteractable interactable = null,
|
||||||
|
ScriptableObject payloadSo = null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cc81a22ff98a4b42b45ad27219ec05fa
|
||||||
|
timeCreated: 1755672371
|
Loading…
Reference in New Issue
Block a user