Upload core-dump files
11
README.md
@@ -11,6 +11,11 @@ We initially intended for AstroDX to be fully open-source after it's uploaded to
|
||||
|
||||
However, If you have issues, please don't hesitate to point it out in issues and we'll try to answer them as best as we can.
|
||||
|
||||
This game is a clean-room implementation of maimai, and has been developed without using any original arcade data.
|
||||
|
||||
- For simai interpreter and serializer used in AstroDX: [SimaiSharp](https://github.com/reflektone-games/SimaiSharp)
|
||||
- For game open-source parts: [AstroDX core-dump](https://github.com/2394425147/maipaddx/tree/main/core-dump)
|
||||
|
||||
# Q&A
|
||||
|
||||
## Which version should I download?
|
||||
@@ -31,6 +36,8 @@ Due to restriction of Apple, developer must be 18 (or 19 depends on local law) t
|
||||
|
||||
Each public link only can hold 10k players (count by Apple ID), so please NOT duplicate join the test if you already have AstroDX installed.
|
||||
|
||||
Since both of those slots are full again, we are planning on create another one soon, but ETA is not guaranteed on this.
|
||||
|
||||
## Are there any tutorials on importing?
|
||||
|
||||
Yep, they should be on the [wiki](https://github.com/2394425147/maipaddx/wiki/Importing-levels) of this repo.
|
||||
@@ -43,6 +50,10 @@ We **don't recommend** doing this, as it violates SEGA's policies.
|
||||
|
||||
You can open an issue [here](https://github.com/2394425147/maipaddx/issues).
|
||||
|
||||
We welcome issues written in Mandarin, Japanese and English. However, it would be strongly suggested to provide translations (even using online translator) to English when submitting them, thus other people could understand as well.
|
||||
|
||||
When submitting issues, please always ensure that you are running the latest released version. We also recommend reviewing existing issues to avoid duplication of questions or concerns.
|
||||
|
||||
[](https://discord.gg/6fpETgpvjZ)
|
||||
Alternatively, on our Discord server, we also have a help forum dedicated for issues, an faq, as well as a suggestions channel for feedback.
|
||||
|
||||
|
||||
98
core-dump/Scripts/CurveCcwGenerator.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System.Collections.Generic;
|
||||
using AstroDX.Contexts.Gameplay.PlayerScope;
|
||||
using AstroDX.Utilities;
|
||||
using SimaiSharp.Structures;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AstroDX.Contexts.Gameplay.SlideGenerators
|
||||
{
|
||||
public sealed class CurveCcwGenerator : SlideGenerator
|
||||
{
|
||||
private const float CurveRadius = RenderManager.CenterRadius;
|
||||
private const float RingRadius = RenderManager.PlayFieldRadius;
|
||||
private readonly float _curveLength;
|
||||
private readonly float _endForward;
|
||||
private readonly Vector2 _endPoint;
|
||||
|
||||
private readonly float _startForward;
|
||||
|
||||
private readonly float _startLength;
|
||||
|
||||
private readonly Vector2 _startPoint;
|
||||
private readonly Vector2 _tangentInPoint;
|
||||
private readonly float _tangentInRotation;
|
||||
private readonly Vector2 _tangentOutPoint;
|
||||
private readonly float _totalLength;
|
||||
|
||||
public CurveCcwGenerator(IReadOnlyList<Location> vertices)
|
||||
{
|
||||
var startRotation = GetRotation(vertices[0]);
|
||||
var endRotation = GetRotation(vertices[1]);
|
||||
|
||||
_tangentInRotation = startRotation +
|
||||
Trigonometry.GetTangentAngleDelta(CurveRadius, RingRadius, false);
|
||||
var tangentOutRotation = endRotation +
|
||||
Trigonometry.GetTangentAngleDelta(CurveRadius, RingRadius, true);
|
||||
|
||||
_startPoint = GetPositionRadial(startRotation);
|
||||
_tangentInPoint = GetPositionRadial(_tangentInRotation, CurveRadius);
|
||||
_tangentOutPoint = GetPositionRadial(tangentOutRotation, CurveRadius);
|
||||
_endPoint = GetPositionRadial(endRotation);
|
||||
|
||||
var startSegment = _tangentInPoint - _startPoint;
|
||||
_startLength = startSegment.magnitude;
|
||||
_startForward = Mathf.Atan2(startSegment.y, startSegment.x);
|
||||
|
||||
_curveLength = Trigonometry.GetAngleSpan(_tangentInRotation, tangentOutRotation,
|
||||
false) * CurveRadius;
|
||||
|
||||
var endSegment = _endPoint - _tangentOutPoint;
|
||||
var endLength = endSegment.magnitude;
|
||||
_endForward = Mathf.Atan2(endSegment.y, endSegment.x);
|
||||
|
||||
_totalLength = _startLength + _curveLength + endLength;
|
||||
}
|
||||
|
||||
public override float GetLength()
|
||||
{
|
||||
return _totalLength;
|
||||
}
|
||||
|
||||
public override void GetPoint(float t, out Vector2 position, out float rotation)
|
||||
{
|
||||
var distanceFromStart = t * _totalLength;
|
||||
|
||||
if (distanceFromStart < _startLength)
|
||||
{
|
||||
position = Vector2.Lerp(_startPoint,
|
||||
_tangentInPoint,
|
||||
Mathf.InverseLerp(0,
|
||||
_startLength,
|
||||
distanceFromStart));
|
||||
|
||||
rotation = _startForward;
|
||||
}
|
||||
else if (distanceFromStart < _startLength + _curveLength)
|
||||
{
|
||||
var localT = Mathf.InverseLerp(_startLength, _startLength + _curveLength, distanceFromStart);
|
||||
position = new Vector2(Mathf.Cos(_tangentInRotation + _curveLength / CurveRadius * localT) *
|
||||
CurveRadius,
|
||||
Mathf.Sin(_tangentInRotation + _curveLength / CurveRadius * localT) *
|
||||
CurveRadius);
|
||||
|
||||
var forward = position.Rotate(Trigonometry.Tau / 4);
|
||||
rotation = Mathf.Atan2(forward.y, forward.x);
|
||||
}
|
||||
else
|
||||
{
|
||||
position = Vector2.Lerp(_tangentOutPoint,
|
||||
_endPoint,
|
||||
Mathf.InverseLerp(_startLength + _curveLength,
|
||||
_totalLength,
|
||||
distanceFromStart));
|
||||
|
||||
rotation = _endForward;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
98
core-dump/Scripts/CurveCwGenerator.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System.Collections.Generic;
|
||||
using AstroDX.Contexts.Gameplay.PlayerScope;
|
||||
using AstroDX.Utilities;
|
||||
using SimaiSharp.Structures;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AstroDX.Contexts.Gameplay.SlideGenerators
|
||||
{
|
||||
public sealed class CurveCwGenerator : SlideGenerator
|
||||
{
|
||||
private const float CurveRadius = RenderManager.CenterRadius;
|
||||
private const float RingRadius = RenderManager.PlayFieldRadius;
|
||||
private readonly float _curveLength;
|
||||
private readonly float _endForward;
|
||||
private readonly Vector2 _endPoint;
|
||||
|
||||
private readonly float _startForward;
|
||||
|
||||
private readonly float _startLength;
|
||||
|
||||
private readonly Vector2 _startPoint;
|
||||
private readonly Vector2 _tangentInPoint;
|
||||
private readonly float _tangentInRotation;
|
||||
private readonly Vector2 _tangentOutPoint;
|
||||
private readonly float _totalLength;
|
||||
|
||||
public CurveCwGenerator(IReadOnlyList<Location> vertices)
|
||||
{
|
||||
var startRotation = GetRotation(vertices[0]);
|
||||
var endRotation = GetRotation(vertices[1]);
|
||||
|
||||
_tangentInRotation = startRotation +
|
||||
Trigonometry.GetTangentAngleDelta(CurveRadius, RingRadius, true);
|
||||
var tangentOutRotation = endRotation +
|
||||
Trigonometry.GetTangentAngleDelta(CurveRadius, RingRadius, false);
|
||||
|
||||
_startPoint = GetPositionRadial(startRotation);
|
||||
_tangentInPoint = GetPositionRadial(_tangentInRotation, CurveRadius);
|
||||
_tangentOutPoint = GetPositionRadial(tangentOutRotation, CurveRadius);
|
||||
_endPoint = GetPositionRadial(endRotation);
|
||||
|
||||
var startSegment = _tangentInPoint - _startPoint;
|
||||
_startLength = startSegment.magnitude;
|
||||
_startForward = Mathf.Atan2(startSegment.y, startSegment.x);
|
||||
|
||||
_curveLength = Trigonometry.GetAngleSpan(_tangentInRotation, tangentOutRotation,
|
||||
true) * CurveRadius;
|
||||
|
||||
var endSegment = _endPoint - _tangentOutPoint;
|
||||
var endLength = endSegment.magnitude;
|
||||
_endForward = Mathf.Atan2(endSegment.y, endSegment.x);
|
||||
|
||||
_totalLength = _startLength + _curveLength + endLength;
|
||||
}
|
||||
|
||||
public override float GetLength()
|
||||
{
|
||||
return _totalLength;
|
||||
}
|
||||
|
||||
public override void GetPoint(float t, out Vector2 position, out float rotation)
|
||||
{
|
||||
var distanceFromStart = t * _totalLength;
|
||||
|
||||
if (distanceFromStart < _startLength)
|
||||
{
|
||||
position = Vector2.Lerp(_startPoint,
|
||||
_tangentInPoint,
|
||||
Mathf.InverseLerp(0,
|
||||
_startLength,
|
||||
distanceFromStart));
|
||||
|
||||
rotation = _startForward;
|
||||
}
|
||||
else if (distanceFromStart < _startLength + _curveLength)
|
||||
{
|
||||
var localT = Mathf.InverseLerp(_startLength, _startLength + _curveLength, distanceFromStart);
|
||||
position = new Vector2(Mathf.Cos(_tangentInRotation - _curveLength / CurveRadius * localT) *
|
||||
CurveRadius,
|
||||
Mathf.Sin(_tangentInRotation - _curveLength / CurveRadius * localT) *
|
||||
CurveRadius);
|
||||
|
||||
var forward = position.Rotate(-Trigonometry.Tau / 4);
|
||||
rotation = Mathf.Atan2(forward.y, forward.x);
|
||||
}
|
||||
else
|
||||
{
|
||||
position = Vector2.Lerp(_tangentOutPoint,
|
||||
_endPoint,
|
||||
Mathf.InverseLerp(_startLength + _curveLength,
|
||||
_totalLength,
|
||||
distanceFromStart));
|
||||
|
||||
rotation = _endForward;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
116
core-dump/Scripts/EdgeCurveCcwGenerator.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using System.Collections.Generic;
|
||||
using AstroDX.Contexts.Gameplay.PlayerScope;
|
||||
using AstroDX.Utilities;
|
||||
using SimaiSharp.Structures;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AstroDX.Contexts.Gameplay.SlideGenerators
|
||||
{
|
||||
public sealed class EdgeCurveCcwGenerator : SlideGenerator
|
||||
{
|
||||
private const float CurveRadius = RenderManager.CenterRadius * 1.2f;
|
||||
private const float CenterAngularOffset = Trigonometry.Tau / 4 - Trigonometry.Tau / 16;
|
||||
private const float CenterRadialOffset = RenderManager.PlayFieldRadius * 0.4662f;
|
||||
|
||||
private readonly Vector2 _centerPosition;
|
||||
private readonly float _curveLength;
|
||||
private readonly Vector2 _endPoint;
|
||||
|
||||
private readonly float _startRotation;
|
||||
private readonly float _endRotation;
|
||||
|
||||
private readonly float _startLength;
|
||||
private readonly Vector2 _startPoint;
|
||||
private readonly Vector2 _tangentInPoint;
|
||||
private readonly float _tangentInRotation;
|
||||
private readonly Vector2 _tangentOutPoint;
|
||||
private readonly float _totalLength;
|
||||
|
||||
public EdgeCurveCcwGenerator(IReadOnlyList<Location> vertices)
|
||||
{
|
||||
var startRotation = GetRotation(vertices[0]);
|
||||
var endRotation = GetRotation(vertices[1]);
|
||||
|
||||
var centerAngle = startRotation - CenterAngularOffset;
|
||||
_centerPosition = new Vector2(CenterRadialOffset * Mathf.Cos(centerAngle),
|
||||
CenterRadialOffset * Mathf.Sin(centerAngle));
|
||||
|
||||
_startPoint = GetPositionRadial(startRotation);
|
||||
|
||||
var relativeStartRotation = Trigonometry.ToPolarAngle(_startPoint, _centerPosition);
|
||||
|
||||
var magnitude = (_centerPosition - _startPoint).magnitude;
|
||||
var startDelta = Trigonometry.GetTangentAngleDelta(CurveRadius, magnitude, false);
|
||||
|
||||
_tangentInRotation = relativeStartRotation + startDelta;
|
||||
_tangentInPoint = GetPositionRadial(_tangentInRotation, CurveRadius) +
|
||||
_centerPosition;
|
||||
|
||||
_endPoint = GetPositionRadial(endRotation);
|
||||
|
||||
var relativeEndRotation = Trigonometry.ToPolarAngle(_endPoint, _centerPosition);
|
||||
var endMagnitude = (_endPoint - _centerPosition).magnitude;
|
||||
var endDelta = Trigonometry.GetTangentAngleDelta(CurveRadius, endMagnitude, true);
|
||||
|
||||
var tangentOutRotation = relativeEndRotation + endDelta;
|
||||
_tangentOutPoint = GetPositionRadial(tangentOutRotation, CurveRadius) +
|
||||
_centerPosition;
|
||||
|
||||
var startSegment = _tangentInPoint - _startPoint;
|
||||
_startLength = startSegment.magnitude;
|
||||
_startRotation = Mathf.Atan2(startSegment.y, startSegment.x);
|
||||
|
||||
_curveLength = Trigonometry.GetAngleSpan(_tangentInRotation, tangentOutRotation,
|
||||
false, Trigonometry.Tau / 4f) * CurveRadius;
|
||||
|
||||
var endSegment = _endPoint - _tangentOutPoint;
|
||||
var endLength = endSegment.magnitude;
|
||||
_endRotation = Mathf.Atan2(endSegment.y, endSegment.x);
|
||||
|
||||
_totalLength = _startLength + _curveLength + endLength;
|
||||
}
|
||||
|
||||
public override float GetLength()
|
||||
{
|
||||
return _totalLength;
|
||||
}
|
||||
|
||||
public override void GetPoint(float t, out Vector2 position, out float rotation)
|
||||
{
|
||||
var distanceFromStart = t * _totalLength;
|
||||
|
||||
if (distanceFromStart < _startLength)
|
||||
{
|
||||
position = Vector2.Lerp(_startPoint,
|
||||
_tangentInPoint,
|
||||
Mathf.InverseLerp(0,
|
||||
_startLength,
|
||||
distanceFromStart));
|
||||
|
||||
rotation = _startRotation;
|
||||
}
|
||||
else if (distanceFromStart < _startLength + _curveLength)
|
||||
{
|
||||
var localT = Mathf.InverseLerp(_startLength, _startLength + _curveLength, distanceFromStart);
|
||||
position = new Vector2(Mathf.Cos(_tangentInRotation + _curveLength / CurveRadius * localT) *
|
||||
CurveRadius,
|
||||
Mathf.Sin(_tangentInRotation + _curveLength / CurveRadius * localT) *
|
||||
CurveRadius);
|
||||
|
||||
var forward = position.Rotate(Trigonometry.Tau / 4);
|
||||
rotation = Mathf.Atan2(forward.y, forward.x);
|
||||
position += _centerPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
position = Vector2.Lerp(_tangentOutPoint,
|
||||
_endPoint,
|
||||
Mathf.InverseLerp(_startLength + _curveLength,
|
||||
_totalLength,
|
||||
distanceFromStart));
|
||||
|
||||
rotation = _endRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
116
core-dump/Scripts/EdgeCurveCwGenerator.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using System.Collections.Generic;
|
||||
using AstroDX.Contexts.Gameplay.PlayerScope;
|
||||
using AstroDX.Utilities;
|
||||
using SimaiSharp.Structures;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AstroDX.Contexts.Gameplay.SlideGenerators
|
||||
{
|
||||
public sealed class EdgeCurveCwGenerator : SlideGenerator
|
||||
{
|
||||
private const float CurveRadius = RenderManager.CenterRadius * 1.2f;
|
||||
private const float CenterAngularOffset = Trigonometry.Tau / 4 - Trigonometry.Tau / 16;
|
||||
private const float CenterRadialOffset = RenderManager.PlayFieldRadius * 0.4662f;
|
||||
|
||||
private readonly Vector2 _centerPosition;
|
||||
private readonly float _curveLength;
|
||||
private readonly Vector2 _endPoint;
|
||||
|
||||
private readonly float _startRotation;
|
||||
private readonly float _endRotation;
|
||||
|
||||
private readonly float _startLength;
|
||||
private readonly Vector2 _startPoint;
|
||||
private readonly Vector2 _tangentInPoint;
|
||||
private readonly float _tangentInRotation;
|
||||
private readonly Vector2 _tangentOutPoint;
|
||||
private readonly float _totalLength;
|
||||
|
||||
public EdgeCurveCwGenerator(IReadOnlyList<Location> vertices)
|
||||
{
|
||||
var startRotation = GetRotation(vertices[0]);
|
||||
var endRotation = GetRotation(vertices[1]);
|
||||
|
||||
var centerAngle = startRotation + CenterAngularOffset;
|
||||
_centerPosition = new Vector2(CenterRadialOffset * Mathf.Cos(centerAngle),
|
||||
CenterRadialOffset * Mathf.Sin(centerAngle));
|
||||
|
||||
_startPoint = GetPositionRadial(startRotation);
|
||||
|
||||
var relativeStartRotation = Trigonometry.ToPolarAngle(_startPoint, _centerPosition);
|
||||
|
||||
var magnitude = (_centerPosition - _startPoint).magnitude;
|
||||
var startDelta = Trigonometry.GetTangentAngleDelta(CurveRadius, magnitude, true);
|
||||
|
||||
_tangentInRotation = relativeStartRotation + startDelta;
|
||||
_tangentInPoint = GetPositionRadial(_tangentInRotation, CurveRadius) +
|
||||
_centerPosition;
|
||||
|
||||
_endPoint = GetPositionRadial(endRotation);
|
||||
|
||||
var relativeEndRotation = Trigonometry.ToPolarAngle(_endPoint, _centerPosition);
|
||||
var endMagnitude = (_endPoint - _centerPosition).magnitude;
|
||||
var endDelta = Trigonometry.GetTangentAngleDelta(CurveRadius, endMagnitude, false);
|
||||
|
||||
var tangentOutRotation = relativeEndRotation + endDelta;
|
||||
_tangentOutPoint = GetPositionRadial(tangentOutRotation, CurveRadius) +
|
||||
_centerPosition;
|
||||
|
||||
var startSegment = _tangentInPoint - _startPoint;
|
||||
_startLength = startSegment.magnitude;
|
||||
_startRotation = Mathf.Atan2(startSegment.y, startSegment.x);
|
||||
|
||||
_curveLength = Trigonometry.GetAngleSpan(_tangentInRotation, tangentOutRotation,
|
||||
true, Trigonometry.Tau / 4f) * CurveRadius;
|
||||
|
||||
var endSegment = _endPoint - _tangentOutPoint;
|
||||
var endLength = endSegment.magnitude;
|
||||
_endRotation = Mathf.Atan2(endSegment.y, endSegment.x);
|
||||
|
||||
_totalLength = _startLength + _curveLength + endLength;
|
||||
}
|
||||
|
||||
public override float GetLength()
|
||||
{
|
||||
return _totalLength;
|
||||
}
|
||||
|
||||
public override void GetPoint(float t, out Vector2 position, out float rotation)
|
||||
{
|
||||
var distanceFromStart = t * _totalLength;
|
||||
|
||||
if (distanceFromStart < _startLength)
|
||||
{
|
||||
position = Vector2.Lerp(_startPoint,
|
||||
_tangentInPoint,
|
||||
Mathf.InverseLerp(0,
|
||||
_startLength,
|
||||
distanceFromStart));
|
||||
|
||||
rotation = _startRotation;
|
||||
}
|
||||
else if (distanceFromStart < _startLength + _curveLength)
|
||||
{
|
||||
var localT = Mathf.InverseLerp(_startLength, _startLength + _curveLength, distanceFromStart);
|
||||
position = new Vector2(Mathf.Cos(_tangentInRotation - _curveLength / CurveRadius * localT) *
|
||||
CurveRadius,
|
||||
Mathf.Sin(_tangentInRotation - _curveLength / CurveRadius * localT) *
|
||||
CurveRadius);
|
||||
|
||||
var forward = position.Rotate(-Trigonometry.Tau / 4);
|
||||
rotation = Mathf.Atan2(forward.y, forward.x);
|
||||
position += _centerPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
position = Vector2.Lerp(_tangentOutPoint,
|
||||
_endPoint,
|
||||
Mathf.InverseLerp(_startLength + _curveLength,
|
||||
_totalLength,
|
||||
distanceFromStart));
|
||||
|
||||
rotation = _endRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
63
core-dump/Scripts/EdgeFoldGenerator.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Generic;
|
||||
using SimaiSharp.Structures;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AstroDX.Contexts.Gameplay.SlideGenerators
|
||||
{
|
||||
public sealed class EdgeFoldGenerator : SlideGenerator
|
||||
{
|
||||
private readonly Vector2 _endPoint;
|
||||
private readonly Vector2 _midPoint;
|
||||
private readonly Vector2 _startPoint;
|
||||
|
||||
private readonly float _startRotation;
|
||||
private readonly float _endRotation;
|
||||
|
||||
private readonly float _startSegmentLength;
|
||||
private readonly float _totalLength;
|
||||
|
||||
public EdgeFoldGenerator(IReadOnlyList<Location> vertices)
|
||||
{
|
||||
_startPoint = GetPosition(vertices[0]);
|
||||
_midPoint = GetPosition(vertices[1]);
|
||||
_endPoint = GetPosition(vertices[2]);
|
||||
|
||||
var startSegment = _midPoint - _startPoint;
|
||||
_startSegmentLength = startSegment.magnitude;
|
||||
_startRotation = Mathf.Atan2(startSegment.y, startSegment.x);
|
||||
|
||||
var endSegment = _endPoint - _midPoint;
|
||||
var endSegmentSpan = endSegment.magnitude;
|
||||
_endRotation = Mathf.Atan2(endSegment.y, endSegment.x);
|
||||
|
||||
_totalLength = _startSegmentLength + endSegmentSpan;
|
||||
}
|
||||
|
||||
public override float GetLength()
|
||||
{
|
||||
return _totalLength;
|
||||
}
|
||||
|
||||
public override void GetPoint(float t, out Vector2 position, out float rotation)
|
||||
{
|
||||
var distanceFromStart = t * _totalLength;
|
||||
|
||||
if (distanceFromStart < _startSegmentLength)
|
||||
{
|
||||
position = Vector2.Lerp(_startPoint, _midPoint,
|
||||
Mathf.InverseLerp(0,
|
||||
_startSegmentLength / _totalLength,
|
||||
t));
|
||||
rotation = _startRotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
position = Vector2.Lerp(_midPoint, _endPoint,
|
||||
Mathf.InverseLerp(_startSegmentLength / _totalLength,
|
||||
1,
|
||||
t));
|
||||
rotation = _endRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
327
core-dump/Scripts/FanSlideSegmentHandler.cs
Normal file
@@ -0,0 +1,327 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AstroDX.Contexts.Gameplay.Behaviours.Slide.SlideMarkers;
|
||||
using AstroDX.Contexts.Gameplay.Interactions;
|
||||
using AstroDX.Contexts.Gameplay.PlayerScope;
|
||||
using AstroDX.Contexts.Gameplay.SlideGenerators;
|
||||
using AstroDX.Globals;
|
||||
using Medicine;
|
||||
using SimaiSharp.Structures;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AstroDX.Contexts.Gameplay.Behaviours.Slide.Handlers
|
||||
{
|
||||
public class FanSlideSegmentHandler : SlideSegmentHandler
|
||||
{
|
||||
private const int Margin = 2;
|
||||
private const int ArrowCount = 11;
|
||||
|
||||
private readonly List<FanSlideArrow> _arrows = new();
|
||||
private readonly Vector2[] _endPositions;
|
||||
|
||||
private readonly List<List<TouchInteractable>> _interactableGroups = new();
|
||||
|
||||
private readonly Vector2 _startPosition;
|
||||
private readonly Vector2[] _ups;
|
||||
|
||||
private int _interactionReceivedState;
|
||||
|
||||
private SlideStarBehaviour[] _stars;
|
||||
|
||||
public FanSlideSegmentHandler(SlideBehaviour slideBehaviour,
|
||||
SlideSegment segment,
|
||||
Location startLocation) :
|
||||
base(slideBehaviour)
|
||||
{
|
||||
var vertices = segment.vertices.ToList();
|
||||
vertices.Insert(0, startLocation);
|
||||
|
||||
_startPosition = SlideGenerator.GetPosition(vertices[0]);
|
||||
|
||||
var ccwEndLocation = startLocation;
|
||||
ccwEndLocation.index += 3;
|
||||
|
||||
var centerEndLocation = startLocation;
|
||||
centerEndLocation.index += 4;
|
||||
|
||||
var cwEndLocation = startLocation;
|
||||
cwEndLocation.index += 5;
|
||||
|
||||
_endPositions = new[]
|
||||
{
|
||||
SlideGenerator.GetPosition(ccwEndLocation),
|
||||
SlideGenerator.GetPosition(centerEndLocation),
|
||||
SlideGenerator.GetPosition(cwEndLocation)
|
||||
};
|
||||
|
||||
_ups = new[]
|
||||
{
|
||||
_endPositions[0] - _startPosition,
|
||||
_endPositions[1] - _startPosition,
|
||||
_endPositions[2] - _startPosition
|
||||
};
|
||||
|
||||
_stars = new[]
|
||||
{
|
||||
SlideManager.slideStars.Get(),
|
||||
SlideManager.slideStars.Get(),
|
||||
SlideManager.slideStars.Get()
|
||||
};
|
||||
|
||||
foreach (var star in _stars) star.Initialize(ParentPath, ParentNote.parentCollection);
|
||||
|
||||
GenerateSensors();
|
||||
GenerateArrows();
|
||||
}
|
||||
|
||||
[Inject.Single]
|
||||
private SlideManager SlideManager { get; }
|
||||
|
||||
[Inject.Single]
|
||||
private TouchManager TouchManager { get; }
|
||||
|
||||
public override float GetLength()
|
||||
{
|
||||
return RenderManager.PlayFieldRadius * 2;
|
||||
}
|
||||
|
||||
public override void OnUpdate(float segmentT)
|
||||
{
|
||||
if (IsJudgementTarget)
|
||||
CheckInteraction();
|
||||
|
||||
UpdateSlideStarPosition(segmentT);
|
||||
UpdateArrowOpacity();
|
||||
}
|
||||
|
||||
private void UpdateArrowOpacity()
|
||||
{
|
||||
var timeSinceNoteStart = TimeSinceSlideStart + ParentPath.delay;
|
||||
|
||||
var alpha = 0.8f;
|
||||
if (timeSinceNoteStart < 0)
|
||||
{
|
||||
var colorPoint = Mathf.InverseLerp(-Persistent.Settings.Gameplay.slideFadeInDuration,
|
||||
0,
|
||||
(float)timeSinceNoteStart);
|
||||
|
||||
alpha = Mathf.Lerp(0, 0.8f, colorPoint);
|
||||
}
|
||||
|
||||
foreach (var arrow in _arrows.Select(a => a.spriteRenderer))
|
||||
{
|
||||
var color = arrow.color;
|
||||
color.a = alpha;
|
||||
|
||||
arrow.color = color;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckInteraction()
|
||||
{
|
||||
if (_interactionReceivedState == 0)
|
||||
{
|
||||
if (!_interactableGroups[0][0].GetSlideJudgement())
|
||||
return;
|
||||
|
||||
_interactionReceivedState = 1;
|
||||
foreach (var slideArrow in _arrows
|
||||
.Where(a => a.interactionGroupIndex == 0)
|
||||
.ToList())
|
||||
{
|
||||
_arrows.Remove(slideArrow);
|
||||
slideArrow.ExitSequence();
|
||||
}
|
||||
}
|
||||
|
||||
if (_interactionReceivedState >= 3)
|
||||
{
|
||||
Cleared = true;
|
||||
return;
|
||||
}
|
||||
|
||||
for (var groupIndex = _interactionReceivedState; groupIndex < 3; groupIndex++)
|
||||
{
|
||||
foreach (var interactable in
|
||||
_interactableGroups[groupIndex]
|
||||
.Where(interactable => interactable.GetSlideJudgement())
|
||||
.ToList())
|
||||
_interactableGroups[groupIndex].Remove(interactable);
|
||||
|
||||
if (_interactableGroups[groupIndex].Count > 0 ||
|
||||
_interactionReceivedState < groupIndex)
|
||||
continue;
|
||||
|
||||
foreach (var slideArrow in _arrows
|
||||
.Where(a => a.interactionGroupIndex == _interactionReceivedState)
|
||||
.ToList())
|
||||
{
|
||||
_arrows.Remove(slideArrow);
|
||||
slideArrow.ExitSequence();
|
||||
}
|
||||
|
||||
_interactionReceivedState++;
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetRemainingLength()
|
||||
{
|
||||
return (3 - _interactionReceivedState) / 3f * GetLength();
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
foreach (var slideArrow in _arrows)
|
||||
SlideManager.fanSlideArrows.Release(slideArrow);
|
||||
|
||||
foreach (var star in _stars)
|
||||
SlideManager.slideStars.Release(star);
|
||||
}
|
||||
|
||||
private void UpdateSlideStarPosition(float segmentT)
|
||||
{
|
||||
if (disposed || _stars is null)
|
||||
return;
|
||||
|
||||
for (var index = 0; index < _stars.Length; index++)
|
||||
{
|
||||
var star = _stars[index];
|
||||
|
||||
if (star == null)
|
||||
continue;
|
||||
|
||||
var transform = star.transform;
|
||||
|
||||
if (segmentT >= 1 && indexInSlide != ParentPath.segments.Count - 1)
|
||||
{
|
||||
transform.localScale = Vector3.one * Persistent.Settings.Gameplay.noteScale;
|
||||
star.Appearance.Color = new Color(1, 1, 1, 0);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var position = Vector3.Lerp(_startPosition,
|
||||
_endPositions[index],
|
||||
segmentT);
|
||||
|
||||
transform.localPosition = position;
|
||||
transform.up = _ups[index];
|
||||
|
||||
if (TimeSinceSlideStart < 0 &&
|
||||
indexInSlide == 0 &&
|
||||
ParentNote.slideMorph != SlideMorph.SuddenIn)
|
||||
{
|
||||
var interpolation =
|
||||
Mathf.InverseLerp(-ParentPath.delay, 0,
|
||||
(float)TimeSinceSlideStart);
|
||||
transform.localScale = new Vector3(interpolation, interpolation) *
|
||||
Persistent.Settings.Gameplay.noteScale;
|
||||
star.Appearance.Color = new Color(1, 1, 1, interpolation);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (segmentT < 0)
|
||||
return;
|
||||
|
||||
transform.localScale = Vector3.one * Persistent.Settings.Gameplay.noteScale;
|
||||
star.Appearance.Color = Color.white;
|
||||
}
|
||||
}
|
||||
|
||||
public override void GetJudgementVector(out Vector2 position, out float rotation)
|
||||
{
|
||||
position = _endPositions[1];
|
||||
rotation = Mathf.Atan2(_ups[1].y, _ups[1].x);
|
||||
}
|
||||
|
||||
private void GenerateSensors()
|
||||
{
|
||||
_interactableGroups.AddRange(new List<TouchInteractable>[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
TouchManager.GetCollider(new Location
|
||||
{
|
||||
group = NoteGroup.ASensor,
|
||||
index = ParentNote.location.index
|
||||
})
|
||||
},
|
||||
new()
|
||||
{
|
||||
TouchManager.GetCollider(new Location
|
||||
{
|
||||
group = NoteGroup.BSensor,
|
||||
index = (ParentNote.location.index + 2) %
|
||||
8
|
||||
}),
|
||||
TouchManager.GetCollider(new Location
|
||||
{
|
||||
group = NoteGroup.BSensor,
|
||||
index = (ParentNote.location.index + 6) %
|
||||
8
|
||||
})
|
||||
},
|
||||
new()
|
||||
{
|
||||
TouchManager.GetCollider(new Location
|
||||
{
|
||||
group = NoteGroup.ASensor,
|
||||
index = (ParentNote.location.index + 5) %
|
||||
8
|
||||
}),
|
||||
TouchManager.GetCollider(new Location
|
||||
{
|
||||
group = NoteGroup.ASensor,
|
||||
index = (ParentNote.location.index + 3) %
|
||||
8
|
||||
}),
|
||||
TouchManager.GetCollider(new Location
|
||||
{
|
||||
group = NoteGroup.ASensor,
|
||||
index = (ParentNote.location.index + 4) %
|
||||
8
|
||||
})
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void GenerateArrows()
|
||||
{
|
||||
var halfMargin = Mathf.RoundToInt(Margin / 2f);
|
||||
for (var i = 0;
|
||||
i < ArrowCount;
|
||||
i++)
|
||||
{
|
||||
var position = Vector3.Lerp(_startPosition,
|
||||
_endPositions[1],
|
||||
(float)(i + halfMargin) / (ArrowCount + Margin));
|
||||
|
||||
var arrow = SlideManager.fanSlideArrows.Get();
|
||||
arrow.Initialize(position,
|
||||
_ups[1],
|
||||
ArrowCount,
|
||||
i,
|
||||
isBreak,
|
||||
isEach);
|
||||
|
||||
AssignArrows(arrow, i);
|
||||
}
|
||||
}
|
||||
|
||||
private void AssignArrows(FanSlideArrow arrow, int index)
|
||||
{
|
||||
_arrows.Add(arrow);
|
||||
|
||||
if (index < ArrowCount / 3f)
|
||||
arrow.interactionGroupIndex = 0;
|
||||
else if (index < ArrowCount * 2 / 3f)
|
||||
arrow.interactionGroupIndex = 1;
|
||||
else
|
||||
arrow.interactionGroupIndex = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
63
core-dump/Scripts/FoldGenerator.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Generic;
|
||||
using SimaiSharp.Structures;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AstroDX.Contexts.Gameplay.SlideGenerators
|
||||
{
|
||||
public sealed class FoldGenerator : SlideGenerator
|
||||
{
|
||||
private readonly Vector2 _endPoint;
|
||||
private readonly float _endRotation;
|
||||
private readonly Vector2 _midPoint;
|
||||
private readonly Vector2 _startPoint;
|
||||
|
||||
private readonly float _startRotation;
|
||||
|
||||
private readonly float _startSegmentLength;
|
||||
private readonly float _totalLength;
|
||||
|
||||
public FoldGenerator(IReadOnlyList<Location> vertices)
|
||||
{
|
||||
_startPoint = GetPosition(vertices[0]);
|
||||
_endPoint = GetPosition(vertices[1]);
|
||||
_midPoint = Vector2.zero;
|
||||
|
||||
var startSegment = _midPoint - _startPoint;
|
||||
_startSegmentLength = startSegment.magnitude;
|
||||
_startRotation = Mathf.Atan2(startSegment.y, startSegment.x);
|
||||
|
||||
var endSegment = _endPoint - _midPoint;
|
||||
var endSegmentSpan = endSegment.magnitude;
|
||||
_endRotation = Mathf.Atan2(endSegment.y, endSegment.x);
|
||||
|
||||
_totalLength = _startSegmentLength + endSegmentSpan;
|
||||
}
|
||||
|
||||
public override float GetLength()
|
||||
{
|
||||
return _totalLength;
|
||||
}
|
||||
|
||||
public override void GetPoint(float t, out Vector2 position, out float rotation)
|
||||
{
|
||||
var distanceFromStart = t * _totalLength;
|
||||
|
||||
if (distanceFromStart < _startSegmentLength)
|
||||
{
|
||||
position = Vector2.Lerp(_startPoint, _midPoint,
|
||||
Mathf.InverseLerp(0,
|
||||
_startSegmentLength / _totalLength,
|
||||
t));
|
||||
rotation = _startRotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
position = Vector2.Lerp(_midPoint, _endPoint,
|
||||
Mathf.InverseLerp(_startSegmentLength / _totalLength,
|
||||
1,
|
||||
t));
|
||||
rotation = _endRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
69
core-dump/Scripts/GameTime.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Timing
|
||||
{
|
||||
public class GameTime : MonoBehaviour
|
||||
{
|
||||
private double _lastCapturedDspTime;
|
||||
public static bool Active { get; private set; }
|
||||
public static double StartTime { get; private set; }
|
||||
public static double TimeSinceClipStart { get; private set; }
|
||||
|
||||
private static NativeLinearRegression TimePrediction { get; set; }
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!Active) return;
|
||||
|
||||
#region Smoothen DSP buffer time
|
||||
|
||||
if (AudioSettings.dspTime > _lastCapturedDspTime)
|
||||
{
|
||||
TimePrediction.Sample(new double2(Time.realtimeSinceStartupAsDouble, AudioSettings.dspTime));
|
||||
_lastCapturedDspTime = AudioSettings.dspTime;
|
||||
}
|
||||
|
||||
var smoothDspTime = TimePrediction.SampleCount < 2
|
||||
? AudioSettings.dspTime
|
||||
: TimePrediction.Predict(Time.realtimeSinceStartupAsDouble);
|
||||
|
||||
#endregion
|
||||
|
||||
TimeSinceClipStart = smoothDspTime - StartTime;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
TimePrediction.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tells the time manager to start counting time
|
||||
/// </summary>
|
||||
public static void StartFrom(double startTime)
|
||||
{
|
||||
StartTime = startTime;
|
||||
TimeSinceClipStart = AudioSettings.dspTime - startTime;
|
||||
Active = true;
|
||||
|
||||
TimePrediction ??= new NativeLinearRegression();
|
||||
|
||||
TimePrediction.Clear();
|
||||
}
|
||||
|
||||
public static void Pause()
|
||||
{
|
||||
Active = false;
|
||||
}
|
||||
|
||||
public static void UnPause(double timeSinceClipStart)
|
||||
{
|
||||
TimePrediction.Clear();
|
||||
StartTime = AudioSettings.dspTime - timeSinceClipStart;
|
||||
TimeSinceClipStart = timeSinceClipStart;
|
||||
|
||||
Active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
102
core-dump/Scripts/NativeLinearRegression.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Timing
|
||||
{
|
||||
public sealed class NativeLinearRegression : IDisposable
|
||||
{
|
||||
public int SampleCount { get; private set; }
|
||||
public int MaxSampleCount { get; }
|
||||
|
||||
private NativeArray<double2> _samples;
|
||||
|
||||
public NativeLinearRegression(int maxSampleCount = 12)
|
||||
{
|
||||
MaxSampleCount = maxSampleCount;
|
||||
_samples = new NativeArray<double2>(maxSampleCount, Allocator.Persistent);
|
||||
}
|
||||
|
||||
public void Sample(double2 plot)
|
||||
{
|
||||
if (SampleCount < MaxSampleCount)
|
||||
SampleCount++;
|
||||
else
|
||||
for (var i = 1; i < SampleCount; i++)
|
||||
_samples[i - 1] = _samples[i];
|
||||
|
||||
_samples[SampleCount - 1] = plot;
|
||||
}
|
||||
|
||||
public double Predict(in double x)
|
||||
{
|
||||
using var result = new NativeArray<double>(2, Allocator.TempJob);
|
||||
|
||||
var jobData = new LinearRegressionJob
|
||||
{
|
||||
samples = _samples.Slice(0, SampleCount),
|
||||
yInterceptAndSlope = result
|
||||
};
|
||||
|
||||
jobData.Schedule().Complete();
|
||||
|
||||
var yIntercept = result[0];
|
||||
var slope = result[1];
|
||||
|
||||
return x * slope + yIntercept;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
SampleCount = 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_samples.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct LinearRegressionJob : IJob
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeSlice<double2> samples;
|
||||
|
||||
[WriteOnly]
|
||||
public NativeArray<double> yInterceptAndSlope;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
double sumOfX = 0;
|
||||
double sumOfY = 0;
|
||||
double sumOfXSq = 0;
|
||||
double sumCoDeviates = 0;
|
||||
|
||||
var sampleCount = samples.Length;
|
||||
|
||||
for (var i = 0; i < sampleCount; i++)
|
||||
{
|
||||
var plot = samples[i];
|
||||
sumCoDeviates += plot.x * plot.y;
|
||||
sumOfX += plot.x;
|
||||
sumOfY += plot.y;
|
||||
sumOfXSq += plot.x * plot.x;
|
||||
}
|
||||
|
||||
var ssX = sumOfXSq - sumOfX * sumOfX / sampleCount;
|
||||
var sCo = sumCoDeviates - sumOfX * sumOfY / sampleCount;
|
||||
|
||||
var meanX = sumOfX / sampleCount;
|
||||
var meanY = sumOfY / sampleCount;
|
||||
|
||||
// y-intercept
|
||||
yInterceptAndSlope[0] = meanY - sCo / ssX * meanX;
|
||||
|
||||
// slope
|
||||
yInterceptAndSlope[1] = sCo / ssX;
|
||||
}
|
||||
}
|
||||
}
|
||||
248
core-dump/Scripts/RegularSlideSegmentHandler.cs
Normal file
@@ -0,0 +1,248 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AstroDX.Contexts.Gameplay.Behaviours.Slide.SlideMarkers;
|
||||
using AstroDX.Contexts.Gameplay.Interactions;
|
||||
using AstroDX.Contexts.Gameplay.PlayerScope;
|
||||
using AstroDX.Contexts.Gameplay.SlideGenerators;
|
||||
using AstroDX.Globals;
|
||||
using Medicine;
|
||||
using SimaiSharp.Structures;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AstroDX.Contexts.Gameplay.Behaviours.Slide.Handlers
|
||||
{
|
||||
public class RegularSlideSegmentHandler : SlideSegmentHandler
|
||||
{
|
||||
private readonly SlideGenerator _generator;
|
||||
|
||||
private readonly
|
||||
List<(TouchInteractable relevantSensor,
|
||||
List<RegularSlideArrow> relevantArrows)> _interactionPath = new();
|
||||
|
||||
/// <summary>
|
||||
/// Used in auto judgement
|
||||
/// </summary>
|
||||
private int _initialSensorCount;
|
||||
|
||||
/// <summary>
|
||||
/// Used in auto judgement
|
||||
/// </summary>
|
||||
private int _lastCheckedClearIndex;
|
||||
|
||||
private readonly SlideStarBehaviour _slideStar;
|
||||
|
||||
public RegularSlideSegmentHandler(SlideBehaviour slideBehaviour,
|
||||
SlideSegment segment,
|
||||
Location startLocation) :
|
||||
base(slideBehaviour)
|
||||
{
|
||||
var vertices = segment.vertices.ToList();
|
||||
vertices.Insert(0, startLocation);
|
||||
|
||||
_generator = SlideManager.GetGenerator(segment.slideType, vertices);
|
||||
|
||||
_slideStar = SlideManager.slideStars.Get();
|
||||
_slideStar.Initialize(ParentPath, ParentNote.parentCollection);
|
||||
|
||||
GenerateArrows();
|
||||
}
|
||||
|
||||
[Inject.Single]
|
||||
private SlideManager SlideManager { get; }
|
||||
|
||||
public override float GetLength()
|
||||
{
|
||||
return _generator.GetLength();
|
||||
}
|
||||
|
||||
public override void OnUpdate(float segmentT)
|
||||
{
|
||||
if (IsJudgementTarget)
|
||||
CheckInteraction(segmentT);
|
||||
|
||||
UpdateSlideStarPosition(segmentT);
|
||||
UpdateArrowOpacity();
|
||||
}
|
||||
|
||||
private float _arrowOpacity;
|
||||
|
||||
private void UpdateArrowOpacity()
|
||||
{
|
||||
var timeSinceNoteStart = TimeSinceSlideStart + ParentPath.delay;
|
||||
|
||||
if (timeSinceNoteStart < 0)
|
||||
{
|
||||
_arrowOpacity = Mathf.InverseLerp(-Persistent.Settings.Gameplay.slideFadeInDuration,
|
||||
0,
|
||||
(float)timeSinceNoteStart);
|
||||
|
||||
_arrowOpacity = Mathf.Lerp(0, 0.8f, _arrowOpacity);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_arrowOpacity >= 1)
|
||||
return;
|
||||
|
||||
_arrowOpacity = 1;
|
||||
}
|
||||
|
||||
foreach (var (_, relevantArrows) in _interactionPath)
|
||||
{
|
||||
foreach (var arrow in relevantArrows)
|
||||
{
|
||||
arrow.SetAlpha(_arrowOpacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckInteraction(float segmentT)
|
||||
{
|
||||
var clearCount = GetClearCount(segmentT);
|
||||
|
||||
for (; clearCount > 0 && _interactionPath.Count > 0; clearCount--)
|
||||
{
|
||||
foreach (var slideArrow in _interactionPath[0].relevantArrows)
|
||||
slideArrow.ExitSequence();
|
||||
|
||||
_interactionPath.RemoveAt(0);
|
||||
}
|
||||
|
||||
Cleared = _interactionPath.Count == 0;
|
||||
}
|
||||
|
||||
public override float GetRemainingLength()
|
||||
{
|
||||
return (float)_interactionPath.Count / _initialSensorCount * GetLength();
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
foreach (var (_, arrows) in _interactionPath)
|
||||
foreach (var slideArrow in arrows)
|
||||
SlideManager.regularSlideArrows.Release(slideArrow);
|
||||
|
||||
SlideManager.slideStars.Release(_slideStar);
|
||||
}
|
||||
|
||||
public override void GetJudgementVector(out Vector2 position, out float rotation)
|
||||
{
|
||||
_generator.GetPoint(1, out position, out rotation);
|
||||
}
|
||||
|
||||
private void UpdateSlideStarPosition(float segmentT)
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
var starTransform = _slideStar.transform;
|
||||
|
||||
if (segmentT >= 1 && IsLastSegment)
|
||||
{
|
||||
starTransform.localScale = Vector3.one * Persistent.Settings.Gameplay.noteScale;
|
||||
_slideStar.Appearance.Color = new Color(1, 1, 1, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
_generator.GetPoint(Mathf.Clamp01(segmentT), out var position, out var rotation);
|
||||
starTransform.SetLocalPositionAndRotation(position, Quaternion.Euler(0, 0, rotation * Mathf.Rad2Deg - 90));
|
||||
|
||||
if (TimeSinceSlideStart < 0 &&
|
||||
indexInSlide == 0 &&
|
||||
ParentNote.slideMorph != SlideMorph.SuddenIn)
|
||||
{
|
||||
var interpolation =
|
||||
Mathf.InverseLerp(-ParentPath.delay, 0,
|
||||
(float)TimeSinceSlideStart);
|
||||
starTransform.localScale = new Vector3(interpolation, interpolation) *
|
||||
Persistent.Settings.Gameplay.noteScale;
|
||||
_slideStar.Appearance.Color = new Color(1, 1, 1, interpolation);
|
||||
}
|
||||
|
||||
if (segmentT < 0)
|
||||
return;
|
||||
|
||||
starTransform.localScale = Vector3.one * Persistent.Settings.Gameplay.noteScale;
|
||||
_slideStar.Appearance.Color = Color.white;
|
||||
}
|
||||
|
||||
private void GenerateArrows()
|
||||
{
|
||||
var relevantArrows = new List<RegularSlideArrow>();
|
||||
TouchInteractable relevantSensor = null;
|
||||
|
||||
var totalLength = GetLength();
|
||||
|
||||
var arrowCount = Mathf.FloorToInt(totalLength / SlideManager.ArrowDistance);
|
||||
|
||||
for (var (distance, index) = (SlideManager.ArrowDistance, 0);
|
||||
distance <= totalLength;
|
||||
distance += SlideManager.ArrowDistance)
|
||||
{
|
||||
_generator.GetPoint(distance / totalLength, out var position, out var rotation);
|
||||
|
||||
var slideArrow = SlideManager.regularSlideArrows.Get();
|
||||
slideArrow.Initialize(position, rotation,
|
||||
arrowCount, index, isBreak, isEach);
|
||||
|
||||
var sensor = slideArrow.GetClosestSensor();
|
||||
|
||||
if (relevantSensor != sensor)
|
||||
{
|
||||
if (relevantSensor != null)
|
||||
_interactionPath.Add((relevantSensor, relevantArrows));
|
||||
|
||||
relevantArrows = new List<RegularSlideArrow>();
|
||||
relevantSensor = sensor;
|
||||
}
|
||||
|
||||
relevantArrows.Add(slideArrow);
|
||||
index++;
|
||||
}
|
||||
|
||||
if (relevantArrows.Count > 0) _interactionPath.Add((relevantSensor, relevantArrows));
|
||||
|
||||
_initialSensorCount = _interactionPath.Count;
|
||||
}
|
||||
|
||||
private int GetClearCount(double segmentT)
|
||||
{
|
||||
if (Persistent.Settings.Mods.Auto && segmentT >= 0)
|
||||
{
|
||||
var clearCount = Mathf.FloorToInt((float)(segmentT * _initialSensorCount));
|
||||
|
||||
|
||||
if (_lastCheckedClearIndex >= clearCount)
|
||||
return 0;
|
||||
|
||||
var lastCheckedClearIndex = _lastCheckedClearIndex;
|
||||
_lastCheckedClearIndex = clearCount;
|
||||
return clearCount - lastCheckedClearIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
var clearCount = 0;
|
||||
var scanIndex = 0;
|
||||
var tolerance = Persistent.Settings.Judgement.slideTolerance;
|
||||
|
||||
while (tolerance >= 0 && scanIndex < _interactionPath.Count)
|
||||
{
|
||||
if (_interactionPath[scanIndex].relevantSensor.GetSlideJudgement())
|
||||
{
|
||||
tolerance = Persistent.Settings.Judgement.slideTolerance;
|
||||
clearCount = scanIndex + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
tolerance--;
|
||||
}
|
||||
|
||||
scanIndex++;
|
||||
}
|
||||
|
||||
return clearCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
core-dump/Scripts/RingCcwGenerator.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System.Collections.Generic;
|
||||
using AstroDX.Utilities;
|
||||
using SimaiSharp.Structures;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AstroDX.Contexts.Gameplay.SlideGenerators
|
||||
{
|
||||
public sealed class RingCcwGenerator : SlideGenerator
|
||||
{
|
||||
private readonly float _angleSpan;
|
||||
private readonly float _endRadius;
|
||||
private readonly float _length;
|
||||
|
||||
private readonly float _startRadius;
|
||||
private readonly float _startRotation;
|
||||
|
||||
public RingCcwGenerator(IReadOnlyList<Location> vertices)
|
||||
{
|
||||
var inPosition = GetPosition(vertices[0]);
|
||||
var inRadians = Trigonometry.ToPolarAngle(inPosition);
|
||||
|
||||
var outPosition = GetPosition(vertices[1]);
|
||||
var outRadians = Trigonometry.ToPolarAngle(outPosition);
|
||||
|
||||
_startRotation = inRadians;
|
||||
|
||||
_angleSpan = Trigonometry.GetAngleSpan(inRadians, outRadians, false);
|
||||
_startRadius = GetRadiusFromCenter(vertices[0]);
|
||||
_endRadius = GetRadiusFromCenter(vertices[1]);
|
||||
|
||||
_length = _angleSpan * (_startRadius + _endRadius) / 2;
|
||||
}
|
||||
|
||||
public override float GetLength()
|
||||
{
|
||||
return _length;
|
||||
}
|
||||
|
||||
public override void GetPoint(float t,
|
||||
out Vector2 position,
|
||||
out float rotation)
|
||||
{
|
||||
var radiusAtT = Mathf.Lerp(_startRadius, _endRadius, t);
|
||||
var rotationAtT = _startRotation + _angleSpan * t;
|
||||
|
||||
position = new Vector2(Mathf.Cos(rotationAtT) * radiusAtT,
|
||||
Mathf.Sin(rotationAtT) * radiusAtT);
|
||||
|
||||
rotation = rotationAtT + Trigonometry.Tau / 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
core-dump/Scripts/RingCwGenerator.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System.Collections.Generic;
|
||||
using AstroDX.Utilities;
|
||||
using SimaiSharp.Structures;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AstroDX.Contexts.Gameplay.SlideGenerators
|
||||
{
|
||||
public sealed class RingCwGenerator : SlideGenerator
|
||||
{
|
||||
private readonly float _angleSpan;
|
||||
private readonly float _endRadius;
|
||||
private readonly float _length;
|
||||
|
||||
private readonly float _startRadius;
|
||||
private readonly float _startRotation;
|
||||
|
||||
public RingCwGenerator(IReadOnlyList<Location> vertices)
|
||||
{
|
||||
var inPosition = GetPosition(vertices[0]);
|
||||
var inRadians = Trigonometry.ToPolarAngle(inPosition);
|
||||
|
||||
var outPosition = GetPosition(vertices[1]);
|
||||
var outRadians = Trigonometry.ToPolarAngle(outPosition);
|
||||
|
||||
_startRotation = inRadians;
|
||||
|
||||
_angleSpan = Trigonometry.GetAngleSpan(_startRotation, outRadians, true);
|
||||
_startRadius = GetRadiusFromCenter(vertices[0]);
|
||||
_endRadius = GetRadiusFromCenter(vertices[1]);
|
||||
|
||||
_length = _angleSpan * (_startRadius + _endRadius) / 2;
|
||||
}
|
||||
|
||||
public override float GetLength()
|
||||
{
|
||||
return _length;
|
||||
}
|
||||
|
||||
public override void GetPoint(float t,
|
||||
out Vector2 position,
|
||||
out float rotation)
|
||||
{
|
||||
var radiusAtT = Mathf.Lerp(_startRadius, _endRadius, t);
|
||||
var rotationAtT = _startRotation - _angleSpan * t;
|
||||
|
||||
position = new Vector2(Mathf.Cos(rotationAtT) * radiusAtT,
|
||||
Mathf.Sin(rotationAtT) * radiusAtT);
|
||||
|
||||
rotation = rotationAtT - Trigonometry.Tau / 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
190
core-dump/Scripts/SlideBehaviour.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AstroDX.Contexts.Gameplay.Behaviours.Slide.Handlers;
|
||||
using AstroDX.Contexts.Gameplay.PlayerScope;
|
||||
using AstroDX.Contexts.Gameplay.SceneScope;
|
||||
using AstroDX.Globals;
|
||||
using Medicine;
|
||||
using SimaiSharp.Structures;
|
||||
using UnityEngine;
|
||||
using TimeSpan = AstroDX.Models.Scoring.TimeSpan;
|
||||
|
||||
namespace AstroDX.Contexts.Gameplay.Behaviours.Slide
|
||||
{
|
||||
public sealed class SlideBehaviour : MonoBehaviour, IDisposable
|
||||
{
|
||||
private readonly List<(float distance, SlideSegmentHandler handler)> _segments = new();
|
||||
private float _endRotation;
|
||||
|
||||
private Vector2 _endPosition;
|
||||
private SlideType _endSegmentType;
|
||||
private bool _judged;
|
||||
|
||||
private int _judgedSegmentCount;
|
||||
|
||||
private float _length;
|
||||
|
||||
private float _slideStartTime;
|
||||
|
||||
[Inject.Single]
|
||||
private StatisticsManager StatisticsManager { get; }
|
||||
|
||||
[Inject.Single]
|
||||
private MusicManager MusicManager { get; }
|
||||
|
||||
[Inject.Single]
|
||||
private SlideManager SlideManager { get; }
|
||||
|
||||
public SlidePath Path { get; private set; }
|
||||
public Note ParentNote { get; private set; }
|
||||
|
||||
public double TimeSinceStart => MusicManager.Time - _slideStartTime;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
UpdateSegments();
|
||||
|
||||
if (TimeSinceStart >= 0)
|
||||
CheckInteraction();
|
||||
|
||||
if (Persistent.Settings.Mods.Auto &&
|
||||
TimeSinceStart > Path.duration)
|
||||
Judge();
|
||||
else if (TimeSinceStart > Path.duration +
|
||||
TimeSpan.SlideFinish.TimingWindows[^1].lateSpan)
|
||||
Judge();
|
||||
}
|
||||
|
||||
public void Init(in SlidePath path, in Note parent)
|
||||
{
|
||||
_judgedSegmentCount = 0;
|
||||
_judged = false;
|
||||
|
||||
Path = path;
|
||||
ParentNote = parent;
|
||||
_slideStartTime = ParentNote.parentCollection.time + Path.delay;
|
||||
|
||||
GenerateHandlers(path);
|
||||
}
|
||||
|
||||
private void UpdateSegments()
|
||||
{
|
||||
var t = (float)TimeSinceStart / Path.duration * _length;
|
||||
|
||||
var isJudgementTarget = TimeSinceStart + Path.delay >= 0;
|
||||
|
||||
for (var i = 0; i < _segments.Count; i++)
|
||||
{
|
||||
var (segmentStartT, handler) = _segments[i];
|
||||
var segmentEndT = i + 1 < _segments.Count ? _segments[i + 1].distance : _length;
|
||||
|
||||
var segmentT = (t - segmentStartT) / (segmentEndT - segmentStartT);
|
||||
|
||||
handler.IsJudgementTarget = isJudgementTarget;
|
||||
handler.OnUpdate(segmentT);
|
||||
|
||||
if (!handler.Cleared)
|
||||
isJudgementTarget = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckInteraction()
|
||||
{
|
||||
while (_judgedSegmentCount < _segments.Count)
|
||||
{
|
||||
var currentHandler = _segments[_judgedSegmentCount].handler;
|
||||
|
||||
var segmentCleared = currentHandler.Cleared;
|
||||
|
||||
if (!segmentCleared)
|
||||
return;
|
||||
|
||||
_judgedSegmentCount++;
|
||||
}
|
||||
|
||||
Judge();
|
||||
}
|
||||
|
||||
private void GenerateHandlers(in SlidePath slidePath)
|
||||
{
|
||||
if (_segments.Count > 0)
|
||||
_segments.Clear();
|
||||
|
||||
var totalDistance = 0f;
|
||||
|
||||
var handlers = new List<SlideSegmentHandler>();
|
||||
|
||||
for (var i = 0; i < slidePath.segments.Count; i++)
|
||||
{
|
||||
var segment = slidePath.segments[i];
|
||||
|
||||
var startLocation = i > 0 ? slidePath.segments[i - 1].vertices[^1] : ParentNote.location;
|
||||
|
||||
var handler = SlideSegmentHandler.Recommend(this, segment, startLocation);
|
||||
handlers.Add(handler);
|
||||
}
|
||||
|
||||
var index = 0;
|
||||
foreach (var segmentHandler in handlers)
|
||||
{
|
||||
segmentHandler.SetIndex(index);
|
||||
_segments.Add((totalDistance, segmentHandler));
|
||||
totalDistance += segmentHandler.GetLength();
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
_segments[^1].handler.GetJudgementVector(out _endPosition, out _endRotation);
|
||||
_endSegmentType = Path.segments[^1].slideType;
|
||||
|
||||
_length = totalDistance;
|
||||
}
|
||||
|
||||
private void Judge()
|
||||
{
|
||||
if (_judged)
|
||||
return;
|
||||
|
||||
_judged = true;
|
||||
|
||||
var incomplete = _judgedSegmentCount < _segments.Count;
|
||||
|
||||
var startTime = ParentNote.parentCollection.time +
|
||||
Path.delay;
|
||||
|
||||
var timeFromEnd = MusicManager.Time - (startTime + Path.duration);
|
||||
|
||||
var distanceToEnd = incomplete
|
||||
? _segments[_judgedSegmentCount].handler.GetRemainingLength()
|
||||
: _length - Mathf.InverseLerp(startTime,
|
||||
startTime + Path.duration,
|
||||
(float)MusicManager.Time) * _length;
|
||||
|
||||
if (incomplete)
|
||||
{
|
||||
var multipleSegmentsRemaining = _judgedSegmentCount + 1 < _segments.Count;
|
||||
|
||||
if (multipleSegmentsRemaining)
|
||||
distanceToEnd += _length - _segments[_judgedSegmentCount + 1].distance;
|
||||
}
|
||||
|
||||
StatisticsManager.TallySlide(Path,
|
||||
distanceToEnd,
|
||||
timeFromEnd,
|
||||
_endPosition,
|
||||
_endRotation,
|
||||
_endSegmentType,
|
||||
incomplete);
|
||||
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var segment in _segments)
|
||||
segment.handler.OnDestroy();
|
||||
|
||||
SlideManager.slides.Release(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
54
core-dump/Scripts/SlideGenerator.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using AstroDX.Contexts.Gameplay.PlayerScope;
|
||||
using AstroDX.Utilities;
|
||||
using SimaiSharp.Structures;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AstroDX.Contexts.Gameplay.SlideGenerators
|
||||
{
|
||||
public abstract class SlideGenerator
|
||||
{
|
||||
public abstract float GetLength();
|
||||
|
||||
public abstract void GetPoint(float t, out Vector2 position, out float rotation);
|
||||
|
||||
protected static float GetRotation(in Location location)
|
||||
{
|
||||
const float initialAngle = Trigonometry.Tau / 4f - Trigonometry.Tau / 16f;
|
||||
|
||||
var angle = initialAngle - Trigonometry.Tau / 8f * location.index;
|
||||
|
||||
if (location.group is NoteGroup.DSensor or NoteGroup.ESensor) angle += Trigonometry.Tau / 16f;
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
public static Vector2 GetPosition(in Location location)
|
||||
{
|
||||
var radius = GetRadiusFromCenter(location);
|
||||
|
||||
return GetPositionRadial(GetRotation(location), radius);
|
||||
}
|
||||
|
||||
protected static Vector2 GetPositionRadial(in float rotationRadians,
|
||||
in float radius = RenderManager.PlayFieldRadius)
|
||||
{
|
||||
return new Vector2(Mathf.Cos(rotationRadians) * radius,
|
||||
Mathf.Sin(rotationRadians) * radius);
|
||||
}
|
||||
|
||||
protected static float GetRadiusFromCenter(Location location)
|
||||
{
|
||||
return location.group switch
|
||||
{
|
||||
NoteGroup.Tap => RenderManager.PlayFieldRadius,
|
||||
NoteGroup.ASensor => RenderManager.AreaARadius,
|
||||
NoteGroup.BSensor => RenderManager.AreaBRadius,
|
||||
NoteGroup.CSensor => 0,
|
||||
NoteGroup.DSensor => RenderManager.AreaDRadius,
|
||||
NoteGroup.ESensor => RenderManager.AreaERadius,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
70
core-dump/Scripts/SlideSegmentHandler.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System.Linq;
|
||||
using SimaiSharp.Structures;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AstroDX.Contexts.Gameplay.Behaviours.Slide.Handlers
|
||||
{
|
||||
public abstract class SlideSegmentHandler
|
||||
{
|
||||
protected readonly bool isBreak;
|
||||
protected readonly bool isEach;
|
||||
|
||||
protected bool disposed;
|
||||
|
||||
protected int indexInSlide = -1;
|
||||
|
||||
protected SlideSegmentHandler(SlideBehaviour slideBehaviour)
|
||||
{
|
||||
disposed = false;
|
||||
|
||||
ParentSlide = slideBehaviour;
|
||||
|
||||
isEach = ParentSlide.ParentNote.parentCollection
|
||||
.Sum(n => n.slidePaths.Count) > 1;
|
||||
|
||||
isBreak = ParentSlide.Path.type == NoteType.Break;
|
||||
}
|
||||
|
||||
public bool Cleared { get; protected set; }
|
||||
|
||||
protected SlideBehaviour ParentSlide { get; }
|
||||
protected SlidePath ParentPath => ParentSlide.Path;
|
||||
protected Note ParentNote => ParentSlide.ParentNote;
|
||||
|
||||
protected double TimeSinceSlideStart => ParentSlide.TimeSinceStart;
|
||||
protected bool IsLastSegment => indexInSlide != ParentPath.segments.Count - 1;
|
||||
public bool IsJudgementTarget { get; set; }
|
||||
|
||||
public void SetIndex(int index)
|
||||
{
|
||||
indexInSlide = index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes the position and the up vector for the judgement text.
|
||||
/// </summary>
|
||||
public abstract void GetJudgementVector(out Vector2 position, out float rotation);
|
||||
|
||||
public abstract void OnUpdate(float segmentT);
|
||||
|
||||
public virtual void OnDestroy()
|
||||
{
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
public abstract float GetLength();
|
||||
|
||||
public abstract float GetRemainingLength();
|
||||
|
||||
public static SlideSegmentHandler Recommend(SlideBehaviour slideBehaviour,
|
||||
SlideSegment segment,
|
||||
Location startLocation)
|
||||
{
|
||||
return segment.slideType switch
|
||||
{
|
||||
SlideType.Fan => new FanSlideSegmentHandler(slideBehaviour, segment, startLocation),
|
||||
_ => new RegularSlideSegmentHandler(slideBehaviour, segment, startLocation)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
35
core-dump/Scripts/StraightGenerator.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
using SimaiSharp.Structures;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AstroDX.Contexts.Gameplay.SlideGenerators
|
||||
{
|
||||
public sealed class StraightGenerator : SlideGenerator
|
||||
{
|
||||
private readonly Vector2 _endPoint;
|
||||
private readonly float _rotation;
|
||||
private readonly float _length;
|
||||
private readonly Vector2 _startPoint;
|
||||
|
||||
public StraightGenerator(IReadOnlyList<Location> vertices)
|
||||
{
|
||||
_startPoint = GetPosition(vertices[0]);
|
||||
_endPoint = GetPosition(vertices[1]);
|
||||
|
||||
var segment = _endPoint - _startPoint;
|
||||
_length = segment.magnitude;
|
||||
_rotation = Mathf.Atan2(segment.y, segment.x);
|
||||
}
|
||||
|
||||
public override float GetLength()
|
||||
{
|
||||
return _length;
|
||||
}
|
||||
|
||||
public override void GetPoint(float t, out Vector2 position, out float rotation)
|
||||
{
|
||||
position = Vector2.Lerp(_startPoint, _endPoint, t);
|
||||
rotation = _rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
84
core-dump/Scripts/Trigonometry.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace AstroDX.Utilities
|
||||
{
|
||||
public static class Trigonometry
|
||||
{
|
||||
public const float Tau = Mathf.PI * 2;
|
||||
|
||||
public static Vector2 Rotate(in this Vector2 v, in float degreesRad)
|
||||
{
|
||||
var magnitude = v.magnitude;
|
||||
var originalDegrees = Mathf.Atan2(v.y, v.x);
|
||||
var newDegrees = originalDegrees + degreesRad;
|
||||
|
||||
var newX = Mathf.Cos(newDegrees) * magnitude;
|
||||
var newY = Mathf.Sin(newDegrees) * magnitude;
|
||||
|
||||
return new Vector2(newX, newY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates a point's angle relative to a center point.
|
||||
/// </summary>
|
||||
/// <param name="position">The absolute position of a point.</param>
|
||||
/// <param name="offset">The offset of the calculation's center point.</param>
|
||||
/// <returns>The relative angle of a point from the given center point.</returns>
|
||||
internal static float ToPolarAngle(in Vector2 position, in Vector2? offset = null)
|
||||
{
|
||||
if (!offset.HasValue)
|
||||
return Mathf.Atan2(position.y, position.x);
|
||||
|
||||
var difference = position - offset.Value;
|
||||
return Mathf.Atan2(difference.y, difference.x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the angle between a line to a point on a ring
|
||||
/// and another line perpendicular from a tangent line of that point.
|
||||
/// </summary>
|
||||
/// <param name="adjacent"></param>
|
||||
/// <param name="hypotenuse"></param>
|
||||
/// <param name="clockwise"></param>
|
||||
/// <returns></returns>
|
||||
internal static float GetTangentAngleDelta(in float adjacent,
|
||||
in float hypotenuse,
|
||||
in bool clockwise)
|
||||
{
|
||||
var angleDiff = Mathf.Acos(adjacent / hypotenuse);
|
||||
return clockwise ? -angleDiff : angleDiff;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Calculates the angle between <c>startRotation</c> and <c>endRotation</c>,
|
||||
/// given its traversing direction.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="startRotation">The starting rotation.</param>
|
||||
/// <param name="endRotation">The ending rotation.</param>
|
||||
/// <param name="clockwise">Traversing direction.</param>
|
||||
/// <param name="wrapThreshold">
|
||||
/// <para>Wraps to full circle for spans smaller than this value.</para>
|
||||
/// <para><code>Tau / 4f</code> is recommended for offset circles</para>
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The span between the starting rotation and the ending rotation on a unit circle,
|
||||
/// in radians.
|
||||
/// </returns>
|
||||
public static float GetAngleSpan(in float startRotation,
|
||||
in float endRotation,
|
||||
bool clockwise,
|
||||
float wrapThreshold = Tau / 32f)
|
||||
{
|
||||
var span = clockwise
|
||||
? (startRotation - endRotation + 2 * Tau) % Tau
|
||||
: (endRotation - startRotation + 2 * Tau) % Tau;
|
||||
|
||||
if (span <= wrapThreshold)
|
||||
span += Tau;
|
||||
|
||||
return span;
|
||||
}
|
||||
}
|
||||
}
|
||||
95
core-dump/Scripts/ZigZagSGenerator.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using System.Collections.Generic;
|
||||
using AstroDX.Contexts.Gameplay.PlayerScope;
|
||||
using AstroDX.Utilities;
|
||||
using SimaiSharp.Structures;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AstroDX.Contexts.Gameplay.SlideGenerators
|
||||
{
|
||||
public sealed class ZigZagSGenerator : SlideGenerator
|
||||
{
|
||||
private readonly Vector2 _endPoint;
|
||||
private readonly Vector2 _endZagPoint;
|
||||
private readonly float _midSegmentLength;
|
||||
private readonly Vector2 _startPoint;
|
||||
|
||||
private readonly float _startRotation;
|
||||
private readonly float _midRotation;
|
||||
private readonly float _endRotation;
|
||||
|
||||
private readonly float _startSegmentLength;
|
||||
private readonly Vector2 _startZagPoint;
|
||||
private readonly float _totalLength;
|
||||
|
||||
public ZigZagSGenerator(IReadOnlyList<Location> vertices)
|
||||
{
|
||||
const float distance = RenderManager.PlayFieldRadius;
|
||||
const float inner = RenderManager.CenterRadius;
|
||||
|
||||
var startRotation = GetRotation(vertices[0]);
|
||||
var endRotation = GetRotation(vertices[1]);
|
||||
|
||||
var startZag = startRotation + Trigonometry.Tau / 4f;
|
||||
var endZag = endRotation + Trigonometry.Tau / 4f;
|
||||
|
||||
_startPoint = new Vector2(distance * Mathf.Cos(startRotation),
|
||||
distance * Mathf.Sin(startRotation));
|
||||
|
||||
_startZagPoint = new Vector2(inner * Mathf.Cos(startZag),
|
||||
inner * Mathf.Sin(startZag));
|
||||
|
||||
var startSegment = _startZagPoint - _startPoint;
|
||||
_startSegmentLength = startSegment.magnitude;
|
||||
_startRotation = Mathf.Atan2(startSegment.y, startSegment.x);
|
||||
|
||||
_endZagPoint = new Vector2(inner * Mathf.Cos(endZag),
|
||||
inner * Mathf.Sin(endZag));
|
||||
|
||||
var midSegment = _endZagPoint - _startZagPoint;
|
||||
_midSegmentLength = midSegment.magnitude;
|
||||
_midRotation = Mathf.Atan2(midSegment.y, midSegment.x);
|
||||
|
||||
_endPoint = new Vector2(distance * Mathf.Cos(endRotation),
|
||||
distance * Mathf.Sin(endRotation));
|
||||
|
||||
var endSegment = _endPoint - _endZagPoint;
|
||||
var endSegmentLength = endSegment.magnitude;
|
||||
_endRotation = Mathf.Atan2(endSegment.y, endSegment.x);
|
||||
|
||||
_totalLength = _startSegmentLength + _midSegmentLength + endSegmentLength;
|
||||
}
|
||||
|
||||
public override float GetLength()
|
||||
{
|
||||
return _totalLength;
|
||||
}
|
||||
|
||||
public override void GetPoint(float t, out Vector2 position, out float rotation)
|
||||
{
|
||||
var distanceFromStart = t * _totalLength;
|
||||
|
||||
if (distanceFromStart < _startSegmentLength)
|
||||
{
|
||||
position = Vector2.Lerp(_startPoint, _startZagPoint,
|
||||
Mathf.InverseLerp(0, _startSegmentLength, distanceFromStart));
|
||||
rotation = _startRotation;
|
||||
}
|
||||
else if (distanceFromStart < _startSegmentLength + _midSegmentLength)
|
||||
{
|
||||
var midLength = _startSegmentLength + _midSegmentLength;
|
||||
|
||||
position = Vector2.Lerp(_startZagPoint, _endZagPoint,
|
||||
Mathf.InverseLerp(_startSegmentLength, midLength, distanceFromStart));
|
||||
rotation = _midRotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
var midLength = _startSegmentLength + _midSegmentLength;
|
||||
|
||||
position = Vector2.Lerp(_endZagPoint, _endPoint,
|
||||
Mathf.InverseLerp(midLength, _totalLength, distanceFromStart));
|
||||
rotation = _endRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
95
core-dump/Scripts/ZigZagZGenerator.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using System.Collections.Generic;
|
||||
using AstroDX.Contexts.Gameplay.PlayerScope;
|
||||
using AstroDX.Utilities;
|
||||
using SimaiSharp.Structures;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AstroDX.Contexts.Gameplay.SlideGenerators
|
||||
{
|
||||
public sealed class ZigZagZGenerator : SlideGenerator
|
||||
{
|
||||
private readonly Vector2 _endPoint;
|
||||
private readonly Vector2 _endZagPoint;
|
||||
private readonly float _midSegmentLength;
|
||||
private readonly Vector2 _startPoint;
|
||||
|
||||
private readonly float _startRotation;
|
||||
private readonly float _midRotation;
|
||||
private readonly float _endRotation;
|
||||
|
||||
private readonly float _startSegmentLength;
|
||||
private readonly Vector2 _startZagPoint;
|
||||
private readonly float _totalLength;
|
||||
|
||||
public ZigZagZGenerator(IReadOnlyList<Location> vertices)
|
||||
{
|
||||
const float distance = RenderManager.PlayFieldRadius;
|
||||
const float inner = RenderManager.CenterRadius;
|
||||
|
||||
var startRotation = GetRotation(vertices[0]);
|
||||
var endRotation = GetRotation(vertices[1]);
|
||||
|
||||
var startZag = startRotation - Trigonometry.Tau / 4f;
|
||||
var endZag = endRotation - Trigonometry.Tau / 4f;
|
||||
|
||||
_startPoint = new Vector2(distance * Mathf.Cos(startRotation),
|
||||
distance * Mathf.Sin(startRotation));
|
||||
|
||||
_startZagPoint = new Vector2(inner * Mathf.Cos(startZag),
|
||||
inner * Mathf.Sin(startZag));
|
||||
|
||||
var startSegment = _startZagPoint - _startPoint;
|
||||
_startSegmentLength = startSegment.magnitude;
|
||||
_startRotation = Mathf.Atan2(startSegment.y, startSegment.x);
|
||||
|
||||
_endZagPoint = new Vector2(inner * Mathf.Cos(endZag),
|
||||
inner * Mathf.Sin(endZag));
|
||||
|
||||
var midSegment = _endZagPoint - _startZagPoint;
|
||||
_midSegmentLength = midSegment.magnitude;
|
||||
_midRotation = Mathf.Atan2(midSegment.y, midSegment.x);
|
||||
|
||||
_endPoint = new Vector2(distance * Mathf.Cos(endRotation),
|
||||
distance * Mathf.Sin(endRotation));
|
||||
|
||||
var endSegment = _endPoint - _endZagPoint;
|
||||
var endSegmentLength = endSegment.magnitude;
|
||||
_endRotation = Mathf.Atan2(endSegment.y, endSegment.x);
|
||||
|
||||
_totalLength = _startSegmentLength + _midSegmentLength + endSegmentLength;
|
||||
}
|
||||
|
||||
public override float GetLength()
|
||||
{
|
||||
return _totalLength;
|
||||
}
|
||||
|
||||
public override void GetPoint(float t, out Vector2 position, out float rotation)
|
||||
{
|
||||
var distanceFromStart = t * _totalLength;
|
||||
|
||||
if (distanceFromStart < _startSegmentLength)
|
||||
{
|
||||
position = Vector2.Lerp(_startPoint, _startZagPoint,
|
||||
Mathf.InverseLerp(0, _startSegmentLength, distanceFromStart));
|
||||
rotation = _startRotation;
|
||||
}
|
||||
else if (distanceFromStart < _startSegmentLength + _midSegmentLength)
|
||||
{
|
||||
var midLength = _startSegmentLength + _midSegmentLength;
|
||||
|
||||
position = Vector2.Lerp(_startZagPoint, _endZagPoint,
|
||||
Mathf.InverseLerp(_startSegmentLength, midLength, distanceFromStart));
|
||||
rotation = _midRotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
var midLength = _startSegmentLength + _midSegmentLength;
|
||||
|
||||
position = Vector2.Lerp(_endZagPoint, _endPoint,
|
||||
Mathf.InverseLerp(midLength, _totalLength, distanceFromStart));
|
||||
rotation = _endRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
122
core-dump/Shader/Note.shader
Normal file
@@ -0,0 +1,122 @@
|
||||
Shader "AstroDX/Note"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
|
||||
_GradientTex ("Gradient Texture", 2D) = "white" {}
|
||||
|
||||
_Color ("Tint", Color) = (1,1,1,1)
|
||||
_ShadowColor ("Shadow Tint", Color) = (0,0,0,1)
|
||||
_Grayscale("Grayscale", Range(0, 1)) = 0
|
||||
[MaterialToggle] _Shine ("Shine", Float) = 0
|
||||
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
Tags
|
||||
{
|
||||
"Queue"="Transparent"
|
||||
"IgnoreProjector"="True"
|
||||
"RenderType"="Transparent"
|
||||
"PreviewType"="Plane"
|
||||
"CanUseSpriteAtlas"="True"
|
||||
}
|
||||
|
||||
Cull Off
|
||||
Lighting Off
|
||||
ZWrite Off
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#pragma multi_compile _ PIXELSNAP_ON
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
struct appdata_t
|
||||
{
|
||||
fixed4 vertex : POSITION;
|
||||
fixed4 color : COLOR;
|
||||
fixed2 texcoord : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
fixed4 vertex : SV_POSITION;
|
||||
fixed4 color : COLOR;
|
||||
fixed2 texcoord : TEXCOORD0;
|
||||
};
|
||||
|
||||
fixed4 _Color;
|
||||
fixed _Shine;
|
||||
|
||||
v2f vert(appdata_t IN)
|
||||
{
|
||||
v2f OUT;
|
||||
OUT.vertex = UnityObjectToClipPos(IN.vertex);
|
||||
OUT.texcoord = IN.texcoord;
|
||||
OUT.color = IN.color * _Color;
|
||||
#ifdef PIXELSNAP_ON
|
||||
OUT.vertex = UnityPixelSnap (OUT.vertex);
|
||||
#endif
|
||||
|
||||
return OUT;
|
||||
}
|
||||
|
||||
sampler2D _MainTex;
|
||||
sampler2D _GradientTex;
|
||||
sampler2D _AlphaTex;
|
||||
fixed4 _ShadowColor;
|
||||
fixed _AlphaSplitEnabled;
|
||||
fixed _Grayscale;
|
||||
|
||||
fixed4 sample_sprite_texture(float2 uv)
|
||||
{
|
||||
const fixed4 color = tex2D(_MainTex, uv);
|
||||
|
||||
// setting to 0.004 and 0.996 prevents getting wrong texture edge colors
|
||||
const fixed gradient_position = lerp(0.005, 0.995, saturate(color.r * 2 - 1));
|
||||
const fixed4 gradient_map = tex2D(_GradientTex, fixed2(gradient_position, 0));
|
||||
|
||||
#if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
|
||||
if (_AlphaSplitEnabled)
|
||||
{
|
||||
color.a = tex2D (_AlphaTex, uv).r;
|
||||
}
|
||||
#endif //UNITY_TEXTURE_ALPHASPLIT_ALLOWED
|
||||
|
||||
// choke starts at 0.1 higher than the gradient map threshold
|
||||
// to allow a smoother color blending (see touch)
|
||||
const fixed red_choke = smoothstep(0, 0.5, color.r);
|
||||
|
||||
const fixed3 shadow_color = fixed3(color.b * _ShadowColor.rgb);
|
||||
|
||||
fixed3 sum = gradient_map * red_choke + shadow_color * _ShadowColor.a * color.b;
|
||||
sum = color.g.xxxx + sum * (1 - color.g);
|
||||
|
||||
return fixed4(sum.rgb, color.a);
|
||||
}
|
||||
|
||||
fixed4 frag(v2f IN) : SV_Target
|
||||
{
|
||||
fixed4 c = sample_sprite_texture(IN.texcoord);
|
||||
|
||||
//Rough human eye adjusted grayscale computation
|
||||
const fixed mono_rgb = 0.299 * c.r + 0.587 * c.g + 0.114 * c.b;
|
||||
|
||||
c.rgb *= IN.color.rgb;
|
||||
|
||||
const fixed shine_multiplier = (sin(_Time.y * 16) * 0.5 + 0.5) * _Shine;
|
||||
c.rgb += shine_multiplier.rrr * 0.2;
|
||||
c.rgb *= 1 + shine_multiplier.rrr * 0.3;
|
||||
|
||||
const fixed3 out_color = lerp(c.rgb, mono_rgb.rrr, _Grayscale);
|
||||
return fixed4(out_color, c.a * IN.color.a);
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
core-dump/Sprites/IMG_GAME_HOLD_0.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
core-dump/Sprites/IMG_GAME_HOLD_1.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
core-dump/Sprites/IMG_GAME_STAR_0.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
core-dump/Sprites/IMG_GAME_STAR_1.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
core-dump/Sprites/IMG_GAME_TAP_0.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
core-dump/Sprites/IMG_GAME_TAP_1.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
core-dump/Sprites/IMG_GAME_TOUCH.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
core-dump/Sprites/IMG_GAME_TOUCH_STAR.png
Normal file
|
After Width: | Height: | Size: 18 KiB |