чё то чё то есть

This commit is contained in:
vladislav 2026-09-22 21:08:47 +03:00
parent f632508497
commit f12b4aef0d
14 changed files with 571 additions and 97 deletions

View File

@ -1,24 +1,35 @@
using _Project.Scripts.PlayerUtils; using UnityEngine;
using _Project.Scripts.Utilities; using _Project.Scripts.Utilities;
namespace _Project.Scripts namespace _Project.Scripts.PlayerUtils
{ {
public class Player : MonoSingleton<Player> public class Player : MonoSingleton<Player>
{ {
private PlayerMovement movement; public PlayerControls Controls { get; private set; }
public PlayerControls controls { get; set; }
public PlayerMovement Movement { get; private set; }
public PlayerCamera CameraController { get; private set; }
public PlayerCombat Combat { get; private set; }
public PlayerSound Sound { get; private set; }
public PlayerUI UI { get; private set; } // Новый модуль
void Awake() protected override void Awake()
{ {
base.Awake(); base.Awake();
Controls = new PlayerControls();
movement = GetComponent<PlayerMovement>(); Movement = GetComponent<PlayerMovement>();
controls = GetComponent<PlayerControls>(); CameraController = GetComponent<PlayerCamera>();
Combat = GetComponent<PlayerCombat>();
Sound = GetComponent<PlayerSound>();
UI = GetComponent<PlayerUI>(); // Инициализация
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
} }
void Update() private void OnEnable() => Controls.Player.Enable();
{ private void OnDisable() => Controls.Player.Disable();
}
} }
} }

View File

@ -0,0 +1,118 @@
using UnityEngine;
using UnityEngine.InputSystem;
namespace _Project.Scripts.PlayerUtils
{
public class PlayerCamera : MonoBehaviour
{
[Header("Камера и Настройки")]
[SerializeField] private Transform cameraPosition;
[SerializeField] private float mouseSensitivity = 0.1f;
[SerializeField] private float maxLookAngle = 80f;
[Header("Высота камеры")]
[SerializeField] private float standingCameraY = 1.6f;
[SerializeField] private float crouchingCameraY = 0.8f;
[SerializeField] private float crouchTransitionSpeed = 15f;
[SerializeField] private float slideCameraTiltAngle = 4f;
[Header("Тряска Камеры (Headbob)")]
public bool enableHeadBob = true;
[SerializeField] private float walkBobSpeed = 12f;
[SerializeField] private float walkBobAmount = 0.04f;
[SerializeField] private float sprintBobSpeed = 15f;
[SerializeField] private float sprintBobAmount = 0.07f;
[SerializeField] private float impactRecoverySpeed = 6f;
private PlayerControls controls;
private PlayerMovement movement;
private float cameraPitch = 0f;
private float yRotation = 0f;
private float currentCameraTilt = 0f;
private float baseCameraY;
private float bobTimer = 0f;
private Vector3 bobOffset = Vector3.zero;
private float currentImpactOffset = 0f;
private bool stepTaken = false;
private void Start()
{
controls = Player.Instance.Controls;
movement = Player.Instance.Movement;
mouseSensitivity = PlayerPrefs.GetFloat("MouseSensitivity", mouseSensitivity);
baseCameraY = standingCameraY;
yRotation = transform.eulerAngles.y;
}
private void Update()
{
HandleMouseLook();
HandleCameraAndBobbing();
}
private void HandleMouseLook()
{
Vector2 lookInput = controls.Player.Look.ReadValue<Vector2>();
cameraPitch -= lookInput.y * mouseSensitivity;
cameraPitch = Mathf.Clamp(cameraPitch, -maxLookAngle, maxLookAngle);
float targetTilt = movement.IsSliding ? -slideCameraTiltAngle : 0f;
currentCameraTilt = Mathf.Lerp(currentCameraTilt, targetTilt, Time.deltaTime * 10f);
cameraPosition.localRotation = Quaternion.Euler(cameraPitch, 0f, currentCameraTilt);
yRotation += lookInput.x * mouseSensitivity;
transform.rotation = Quaternion.Euler(0f, yRotation, 0f);
}
private void HandleCameraAndBobbing()
{
bool isLowered = movement.IsCrouching || movement.IsSliding;
float targetCamY = isLowered ? crouchingCameraY : standingCameraY;
baseCameraY = Mathf.Lerp(baseCameraY, targetCamY, Time.deltaTime * crouchTransitionSpeed);
bool isMoving = movement.MoveInput.sqrMagnitude > 0.01f && movement.IsGrounded && !movement.IsSliding;
if (isMoving && enableHeadBob)
{
float speed = movement.IsSprinting ? sprintBobSpeed : walkBobSpeed;
float amount = movement.IsSprinting ? sprintBobAmount : walkBobAmount;
bobTimer += Time.deltaTime * speed;
bobOffset.x = Mathf.Cos(bobTimer * 0.5f) * amount;
bobOffset.y = Mathf.Sin(bobTimer) * amount;
if (Mathf.Sin(bobTimer) <= -0.9f && !stepTaken)
{
// ВЫЗОВ ЗВУКА ИЗ НОВОГО МОДУЛЯ
Player.Instance.Sound.PlayFootstep(movement.IsSprinting);
stepTaken = true;
}
else if (Mathf.Sin(bobTimer) > -0.2f) stepTaken = false;
}
else
{
bobTimer = 0f;
stepTaken = false;
bobOffset = Vector3.Lerp(bobOffset, Vector3.zero, Time.deltaTime * 10f);
}
currentImpactOffset = Mathf.Lerp(currentImpactOffset, 0f, Time.deltaTime * impactRecoverySpeed);
cameraPosition.localPosition = new Vector3(bobOffset.x, baseCameraY + bobOffset.y - currentImpactOffset, 0f);
}
public void ApplyRecoil(float amount)
{
cameraPitch -= amount;
}
public void TriggerJumpImpact()
{
if (enableHeadBob) currentImpactOffset = 0.1f;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bbe0b1999f6642cdb1f20dadcd7c0934
timeCreated: 1790100214

View File

@ -0,0 +1,77 @@
using UnityEngine;
using UnityEngine.InputSystem;
namespace _Project.Scripts.PlayerUtils
{
public class PlayerCombat : MonoBehaviour
{
[Header("Оружие и Стрельба (Hitscan)")]
[SerializeField] private float damage = 25f;
[SerializeField] private float fireRate = 0.12f;
[SerializeField] private float weaponRange = 100f;
[SerializeField] private LayerMask hitLayers = ~0;
[SerializeField] private bool isAutomatic = true;
[SerializeField] private float recoilKick = 1.2f;
[Header("Эффекты (без звука)")]
[SerializeField] private ParticleSystem muzzleFlash;
[SerializeField] private GameObject impactPrefab;
[Header("Ссылки")]
[SerializeField] private Transform cameraPosition;
private PlayerControls controls;
private float shootTimer = 0f;
private bool isShootingInput = false;
private void Start()
{
controls = Player.Instance.Controls;
controls.Player.Attack.started += _ => isShootingInput = true;
controls.Player.Attack.canceled += _ => isShootingInput = false;
}
private void Update()
{
if (shootTimer > 0f) shootTimer -= Time.deltaTime;
bool shootPressed = isShootingInput || (Mouse.current != null && (isAutomatic ? Mouse.current.leftButton.isPressed : Mouse.current.leftButton.wasPressedThisFrame));
if (shootPressed && shootTimer <= 0f)
{
Shoot();
}
}
private void Shoot()
{
shootTimer = fireRate;
// ВЫЗОВ ЗВУКА ИЗ НОВОГО МОДУЛЯ
Player.Instance.Sound.PlayShoot();
if (muzzleFlash != null) muzzleFlash.Play();
Player.Instance.CameraController.ApplyRecoil(recoilKick);
Ray ray = new Ray(cameraPosition.position, cameraPosition.forward);
if (Physics.Raycast(ray, out RaycastHit hit, weaponRange, hitLayers, QueryTriggerInteraction.Ignore))
{
if (hit.collider.TryGetComponent<IDamageable>(out var damageable))
{
damageable.TakeDamage(damage);
}
else if (hit.rigidbody != null)
{
hit.rigidbody.AddForceAtPosition(-hit.normal * (damage * 10f), hit.point, ForceMode.Impulse);
}
if (impactPrefab != null)
{
GameObject impact = Instantiate(impactPrefab, hit.point + (hit.normal * 0.001f), Quaternion.LookRotation(hit.normal));
Destroy(impact, 3f);
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d9d6178191654e72a147db739b22275f
timeCreated: 1790100245

View File

@ -1,107 +1,249 @@
using _Project.Scripts.Utilities;
using UnityEngine; using UnityEngine;
using UnityEngine.InputSystem;
namespace _Project.Scripts.PlayerUtils namespace _Project.Scripts.PlayerUtils
{ {
public class PlayerMovement : MonoSingleton<PlayerMovement> [RequireComponent(typeof(Rigidbody), typeof(CapsuleCollider))]
public class PlayerMovement : MonoBehaviour
{ {
[Header("Камера и Мышь")] [Header("Скорости передвижения (м/с)")]
[SerializeField] private Transform cameraPosition; [SerializeField] private float moveSpeed = 6.5f;
[SerializeField] private float mouseSensitivity = 0.1f; [SerializeField] private float sprintSpeed = 9.5f;
[SerializeField] private float maxLookAngle = 80f; [SerializeField] private float crouchSpeed = 3f;
[Header("Скорости передвижения (м/с)")] [Header("Параметры Подката (Slide)")]
[SerializeField] private float moveSpeed = 6.5f; // Обычный бег (~250 u/s) [SerializeField] private float slideInitialSpeed = 15f;
[SerializeField] private float sprintSpeed = 9.5f; // Спринт (~360 u/s) [SerializeField] private float slideDuration = 0.85f;
[SerializeField] private float crouchSpeed = 3f; [SerializeField] private float slideFriction = 2.8f;
[SerializeField] private float slideCooldown = 0.35f;
[Header("Параметры Подката (Slide)")] [Header("Физика CS / Source (Bunnyhop & Strafe)")]
[Tooltip("Начальная взрывная скорость подката")] [SerializeField] private float groundFriction = 5f;
[SerializeField] private float slideInitialSpeed = 15f; // (~580 u/s) [SerializeField] private float groundAcceleration = 10f;
[Tooltip("Длительность скольжения в секундах")] [SerializeField] private float airAcceleration = 80f;
[SerializeField] private float slideDuration = 0.85f; [SerializeField] private float airCap = 2.0f;
[Tooltip("Трение коленей при подкате")] [SerializeField] private bool showSpeedometer = true;
[SerializeField] private float slideFriction = 2.8f;
[Tooltip("Наклон камеры набок при подкате (градусы)")]
[SerializeField] private float slideCameraTiltAngle = 4f;
[SerializeField] private float slideCooldown = 0.35f;
[Header("Физика CS / Source (Bunnyhop & Strafe)")] [Header("Гравитация и Прыжок")]
[Tooltip("Трение на земле (гасит скорость, если не успели прыгнуть вовремя)")] [SerializeField] private float jumpHeight = 1.35f;
[SerializeField] private float groundFriction = 5f; [SerializeField] private float gravity = -25f;
[Tooltip("Ускорение на земле")] [SerializeField] private float fallMultiplier = 1.8f;
[SerializeField] private float groundAcceleration = 10f; [SerializeField] private LayerMask groundLayer;
[Tooltip("Сила набора скорости в воздухе при стрейфах (sv_airaccelerate)")] [SerializeField] private float groundCheckDistance = 0.12f;
[SerializeField] private float airAcceleration = 80f; [SerializeField] private float jumpBufferTime = 0.12f;
[Tooltip("Лимит добавления скорости в воздухе")]
[SerializeField] private float airCap = 2.0f;
[Tooltip("Показывать спидометр на экране")]
[SerializeField] private bool showSpeedometer = true;
[Header("Гравитация и Прыжок")] [Header("Коллайдер (Приседание)")]
[SerializeField] private float jumpHeight = 1.35f; [SerializeField] private float standingHeight = 2f;
[SerializeField] private float gravity = -25f; [SerializeField] private float crouchingHeight = 1.05f;
[SerializeField] private float fallMultiplier = 1.8f; [SerializeField] private float crouchTransitionSpeed = 15f;
[SerializeField] private LayerMask groundLayer;
[SerializeField] private float groundCheckDistance = 0.12f;
[Tooltip("Буфер прыжка (время поблажки для колесика мыши)")]
[SerializeField] private float jumpBufferTime = 0.12f;
[Header("Параметры приседания")] private Rigidbody rb;
[SerializeField] private float standingHeight = 2f; private CapsuleCollider col;
[SerializeField] private float crouchingHeight = 1.05f; private PlayerControls controls;
[SerializeField] private float standingCameraY = 1.6f;
[SerializeField] private float crouchingCameraY = 0.8f;
[SerializeField] private float crouchTransitionSpeed = 15f;
private Rigidbody rb;
private CapsuleCollider col;
private Vector2 moveInput; public bool IsGrounded { get; private set; }
private Vector2 lookInput; public bool IsSprinting { get; private set; }
private Vector3 horizontalVelocity = Vector3.zero; public bool IsCrouching { get; private set; }
private float verticalVelocity = 0f; public bool IsSliding { get; private set; }
public Vector2 MoveInput { get; private set; }
private bool isGrounded; public float CurrentSpeed => horizontalVelocity.magnitude;
private bool wasGrounded; private bool wasGrounded;
private bool isSprinting = false; private float jumpBufferTimer = 0f;
private bool isCrouching = false; private Vector3 horizontalVelocity = Vector3.zero;
private float jumpBufferTimer = 0f; private float verticalVelocity = 0f;
// Подкат private float slideTimer = 0f;
private bool isSliding = false; private float slideCurrentSpeed = 0f;
private float slideTimer = 0f; private Vector3 slideDirection = Vector3.zero;
private float slideCurrentSpeed = 0f; private float slideCooldownTimer = 0f;
private Vector3 slideDirection = Vector3.zero;
private float slideCooldownTimer = 0f; private void Start()
private PlayerControls controls;
void Awake()
{ {
base.Awake();
rb = GetComponent<Rigidbody>(); rb = GetComponent<Rigidbody>();
col = GetComponent<CapsuleCollider>(); col = GetComponent<CapsuleCollider>();
mouseSensitivity = PlayerPrefs.GetFloat("MouseSensitivity", mouseSensitivity);
rb.useGravity = false; rb.useGravity = false;
rb.freezeRotation = true; rb.freezeRotation = true;
rb.interpolation = RigidbodyInterpolation.None; rb.interpolation = RigidbodyInterpolation.None;
controls = Player.Instance.Controls;
controls.Player.Sprint.started += _ => IsSprinting = true;
controls.Player.Sprint.canceled += _ => IsSprinting = false;
controls = new PlayerControls(); controls.Player.Crouch.started += _ => OnCrouchPressed();
controls.Player.Crouch.canceled += _ => OnCrouchReleased();
controls = Player.Instance.controls;
// Спринт controls.Player.Jump.performed += _ => QueueJump();
controls.Player.Sprint.started += ctx => isSprinting = true;
controls.Player.Sprint.canceled += ctx => isSprinting = false;
/* // Присед и Подкат
controls.Player.Crouch.started += ctx => OnCrouchPressed();
controls.Player.Crouch.canceled += ctx => OnCrouchReleased();
// Прыжок на Пробел (через буфер)
controls.Player.Jump.performed += ctx => QueueJump();
*/
} }
private void Update()
{
MoveInput = controls.Player.Move.ReadValue<Vector2>();
if (jumpBufferTimer > 0f) jumpBufferTimer -= Time.deltaTime;
if (slideCooldownTimer > 0f) slideCooldownTimer -= Time.deltaTime;
if (Mouse.current != null && Mathf.Abs(Mouse.current.scroll.y.ReadValue()) > 0.1f)
QueueJump();
HandleColliderHeight();
}
private void FixedUpdate()
{
CheckGrounded();
CalculateCustomGravity();
if (jumpBufferTimer > 0f && IsGrounded)
ExecuteJump();
ApplyMovement();
rb.linearVelocity = new Vector3(horizontalVelocity.x, verticalVelocity, horizontalVelocity.z);
wasGrounded = IsGrounded;
}
private void HandleColliderHeight()
{
bool isLowered = IsCrouching || IsSliding;
float targetHeight = isLowered ? crouchingHeight : standingHeight;
col.height = Mathf.Lerp(col.height, targetHeight, Time.deltaTime * crouchTransitionSpeed);
col.center = new Vector3(col.center.x, col.height * 0.5f, col.center.z);
}
private void QueueJump() => jumpBufferTimer = jumpBufferTime;
private void ExecuteJump()
{
if (IsSliding)
{
horizontalVelocity = slideDirection * slideCurrentSpeed;
StopSlide();
}
verticalVelocity = Mathf.Sqrt(jumpHeight * -2f * gravity);
jumpBufferTimer = 0f;
Player.Instance.CameraController.TriggerJumpImpact();
// ВЫЗОВ ЗВУКА ИЗ НОВОГО МОДУЛЯ
Player.Instance.Sound.PlayJump();
}
private void ApplyMovement()
{
if (IsSliding)
{
slideCurrentSpeed = Mathf.Lerp(slideCurrentSpeed, crouchSpeed, Time.fixedDeltaTime * slideFriction);
slideTimer -= Time.fixedDeltaTime;
Vector3 steerDir = (transform.forward * MoveInput.y + transform.right * MoveInput.x).normalized;
if (steerDir.sqrMagnitude > 0.01f)
slideDirection = Vector3.Slerp(slideDirection, steerDir, Time.fixedDeltaTime * 2.5f);
horizontalVelocity = slideDirection * slideCurrentSpeed;
if (slideTimer <= 0f || slideCurrentSpeed <= crouchSpeed + 0.5f)
{
StopSlide();
IsCrouching = controls.Player.Crouch.IsPressed();
}
return;
}
Vector3 wishDir = (transform.forward * MoveInput.y + transform.right * MoveInput.x);
wishDir.y = 0f;
wishDir.Normalize();
float targetSpeed = IsCrouching ? crouchSpeed : (IsSprinting ? sprintSpeed : moveSpeed);
if (IsGrounded)
{
if (jumpBufferTimer <= 0f) ApplyGroundFriction();
Accelerate(wishDir, targetSpeed, groundAcceleration);
}
else
{
AirAccelerate(wishDir, targetSpeed, airAcceleration, airCap);
}
}
private void ApplyGroundFriction()
{
float speed = horizontalVelocity.magnitude;
if (speed < 0.0001f) { horizontalVelocity = Vector3.zero; return; }
float drop = speed * groundFriction * Time.fixedDeltaTime;
horizontalVelocity *= (Mathf.Max(speed - drop, 0f) / speed);
}
private void Accelerate(Vector3 wishDir, float wishSpeed, float accel)
{
float currentSpeed = Vector3.Dot(horizontalVelocity, wishDir);
float addSpeed = Mathf.Max(wishSpeed - currentSpeed, 0f);
horizontalVelocity += wishDir * Mathf.Min(accel * wishSpeed * Time.fixedDeltaTime, addSpeed);
}
private void AirAccelerate(Vector3 wishDir, float wishSpeed, float accel, float cap)
{
float currentSpeed = Vector3.Dot(horizontalVelocity, wishDir);
float addSpeed = Mathf.Max(Mathf.Min(wishSpeed, cap) - currentSpeed, 0f);
horizontalVelocity += wishDir * Mathf.Min(accel * wishSpeed * Time.fixedDeltaTime, addSpeed);
}
private void CheckGrounded()
{
float checkRadius = col.radius * 0.85f;
float bottomOffset = (col.height * 0.5f) - checkRadius;
Vector3 rayOrigin = transform.position + col.center - (Vector3.up * bottomOffset) + (Vector3.up * 0.05f);
IsGrounded = Physics.SphereCast(rayOrigin, checkRadius, Vector3.down, out _, groundCheckDistance + 0.05f, groundLayer, QueryTriggerInteraction.Ignore);
if (!wasGrounded && IsGrounded)
{
// ВЫЗОВ ЗВУКА ИЗ НОВОГО МОДУЛЯ
Player.Instance.Sound.PlayLand();
}
if (IsGrounded && jumpBufferTimer <= 0f && rb.linearVelocity.magnitude > 0.01f && horizontalVelocity == Vector3.zero)
horizontalVelocity = new Vector3(rb.linearVelocity.x, 0f, rb.linearVelocity.z);
}
private void CalculateCustomGravity()
{
if (IsGrounded && verticalVelocity < 0f) verticalVelocity = -2f;
else verticalVelocity += ((verticalVelocity < 0f) ? (gravity * fallMultiplier) : gravity) * Time.fixedDeltaTime;
}
private void OnCrouchPressed()
{
if (IsSprinting && IsGrounded && MoveInput.y > 0.2f && slideCooldownTimer <= 0f) StartSlide();
else IsCrouching = true;
}
private void OnCrouchReleased()
{
IsCrouching = false;
if (IsSliding) StopSlide();
}
private void StartSlide()
{
IsSliding = true;
slideTimer = slideDuration;
slideCurrentSpeed = slideInitialSpeed;
slideDirection = (transform.forward * MoveInput.y + transform.right * MoveInput.x).normalized;
if (slideDirection.sqrMagnitude < 0.01f) slideDirection = transform.forward;
// ВЫЗОВ ЗВУКА ИЗ НОВОГО МОДУЛЯ
Player.Instance.Sound.PlaySlide();
}
private void StopSlide()
{
IsSliding = false;
slideCooldownTimer = slideCooldown;
}
} }
} }

View File

@ -0,0 +1,61 @@
using UnityEngine;
namespace _Project.Scripts.PlayerUtils
{
[RequireComponent(typeof(AudioSource))]
public class PlayerSound : MonoBehaviour
{
[Header("Настройки AudioSource")]
[SerializeField] private AudioSource audioSource;
[Header("Звуки Передвижения")]
[SerializeField] private AudioClip[] footstepSounds;
[SerializeField] private AudioClip jumpSound;
[SerializeField] private AudioClip landSound;
[SerializeField] private AudioClip slideSound;
[Header("Звуки Оружия")]
[SerializeField] private AudioClip shootSound;
private void Reset()
{
// Автоматически подцепит AudioSource при добавлении скрипта на объект
audioSource = GetComponent<AudioSource>();
}
public void PlayFootstep(bool isSprinting)
{
if (footstepSounds == null || footstepSounds.Length == 0) return;
int randomIndex = Random.Range(0, footstepSounds.Length);
// Немного меняем высоту звука для разнообразия
audioSource.pitch = Random.Range(0.9f, 1.1f);
float volume = isSprinting ? 0.6f : 0.4f;
audioSource.PlayOneShot(footstepSounds[randomIndex], volume);
audioSource.pitch = 1f; // Сбрасываем питч обратно
}
public void PlayJump()
{
if (jumpSound != null) audioSource.PlayOneShot(jumpSound, 0.7f);
}
public void PlayLand()
{
if (landSound != null) audioSource.PlayOneShot(landSound, 0.5f);
}
public void PlaySlide()
{
if (slideSound != null) audioSource.PlayOneShot(slideSound, 0.7f);
}
public void PlayShoot()
{
if (shootSound != null) audioSource.PlayOneShot(shootSound, 0.8f);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 995d210808ca417ca08ca58fab7f8624
timeCreated: 1790100334

View File

@ -0,0 +1,53 @@
using UnityEngine;
namespace _Project.Scripts.PlayerUtils
{
public class PlayerUI : MonoBehaviour
{
[Header("Настройки Интерфейса")]
[SerializeField] private bool showSpeedometer = true;
private PlayerMovement movement;
private void Start()
{
// Берем ссылку на компонент передвижения через фасад Player
movement = Player.Instance.Movement;
}
private void OnGUI()
{
if (!showSpeedometer || movement == null) return;
// Читаем публичное свойство скорости (мы добавим его ниже)
float speed = movement.CurrentSpeed;
float csUnits = speed * 38.5f;
GUIStyle style = new GUIStyle
{
fontSize = 24,
fontStyle = FontStyle.Bold
};
string state = "";
// Настраиваем цвет и текст состояния
if (movement.IsSliding)
{
style.normal.textColor = Color.yellow;
state = " [SLIDE]";
}
else if (!movement.IsGrounded)
{
style.normal.textColor = Color.green;
state = " [AIR]";
}
else
{
style.normal.textColor = Color.white;
}
GUI.Label(new Rect(30, 30, 500, 50), $"Speed: {speed:F1} m/s ({csUnits:F0} u/s){state}", style);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8184cc49f30946f4802bbf1bbe841e2f
timeCreated: 1790100443