Initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
# Normalize EOL for all files that Git considers text files.
|
||||||
|
* text=auto eol=lf
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Godot 4+ specific ignores
|
||||||
|
.godot/
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://bqhdw2o5vq7ny"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://Scripts/Scenes/DebugChartLoader.cs" id="1_hjgpd"]
|
||||||
|
|
||||||
|
[node name="DebugChartLoader" type="MarginContainer" node_paths=PackedStringArray("inputField", "soundField", "difficultyButton", "playButton")]
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
theme_override_constants/margin_left = 32
|
||||||
|
theme_override_constants/margin_top = 32
|
||||||
|
theme_override_constants/margin_right = 32
|
||||||
|
theme_override_constants/margin_bottom = 32
|
||||||
|
script = ExtResource("1_hjgpd")
|
||||||
|
inputField = NodePath("VBoxContainer/HBoxContainer/PathLineEdit")
|
||||||
|
soundField = NodePath("VBoxContainer/HBoxContainer3/SoundLineEdit")
|
||||||
|
difficultyButton = NodePath("VBoxContainer/HBoxContainer/DifficultyOptionButton")
|
||||||
|
playButton = NodePath("VBoxContainer/HBoxContainer2/PlayButton")
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 8
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="PathLineEdit" type="LineEdit" parent="VBoxContainer/HBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(0, 35)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 8
|
||||||
|
size_flags_stretch_ratio = 5.97
|
||||||
|
text = "AkiraComplex - Ether Strike"
|
||||||
|
placeholder_text = "Folder path relative to \"user://songs/\""
|
||||||
|
|
||||||
|
[node name="DifficultyOptionButton" type="OptionButton" parent="VBoxContainer/HBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(0, 35)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 8
|
||||||
|
item_count = 4
|
||||||
|
selected = 0
|
||||||
|
popup/item_0/text = "0. Normal"
|
||||||
|
popup/item_0/id = 0
|
||||||
|
popup/item_1/text = "1. Hard"
|
||||||
|
popup/item_1/id = 1
|
||||||
|
popup/item_2/text = "2. Expert"
|
||||||
|
popup/item_2/id = 2
|
||||||
|
popup/item_3/text = "3. Inferno"
|
||||||
|
popup/item_3/id = 3
|
||||||
|
|
||||||
|
[node name="HBoxContainer3" type="HBoxContainer" parent="VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="SoundLineEdit" type="LineEdit" parent="VBoxContainer/HBoxContainer3"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
text = "music.wav"
|
||||||
|
placeholder_text = "Name of audio file in the folder specified above with extension"
|
||||||
|
|
||||||
|
[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="PlayButton" type="Button" parent="VBoxContainer/HBoxContainer2"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 0
|
||||||
|
text = "Play"
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[gd_scene format=3 uid="uid://bqh00ot0csqmk"]
|
||||||
|
|
||||||
|
[node name="Play" type="Node"]
|
||||||
|
|
||||||
|
[node name="Node3D" type="Node3D" parent="."]
|
||||||
|
|
||||||
|
[node name="Camera3D" type="Camera3D" parent="Node3D"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 4.09161)
|
||||||
|
fov = 60.0
|
||||||
|
near = 0.001
|
||||||
|
far = 100.0
|
||||||
|
|
||||||
|
[node name="Node2D" type="Node2D" parent="."]
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://bef4fdycrfvls"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://Scripts/Scenes/Startup.cs" id="1_yd1au"]
|
||||||
|
|
||||||
|
[node name="Control" type="Node"]
|
||||||
|
script = ExtResource("1_yd1au")
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace WacK.Configuration
|
||||||
|
{
|
||||||
|
public class PlaySettings
|
||||||
|
{
|
||||||
|
public static float playSpeedMultiplier = 2f;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dee0701f4dec1e53681db1444842f032
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace WacK
|
||||||
|
{
|
||||||
|
public class Constants
|
||||||
|
{
|
||||||
|
public const float SCROLL_MULT = 3f;
|
||||||
|
public const float NOTE_DRAW_DISTANCE = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 25140b91ea8636ea5acb3ff90421f11a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,332 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using WacK.Data.Mer;
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace WacK.Data.Chart
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Chart data.
|
||||||
|
/// </summary>
|
||||||
|
public class Chart
|
||||||
|
{
|
||||||
|
public static bool doneLoading { get; private set; } = false;
|
||||||
|
// [ms] = List<Note> (list for chord marking creation)
|
||||||
|
public SortedList<float, List<NotePlay>> playNotes { get; private set; }
|
||||||
|
public SortedList<float, NoteEvent<(int, int)>> timeSigChgs { get; private set; }
|
||||||
|
public SortedList<float, NoteEvent<float>> tempoChgs { get; private set; }
|
||||||
|
public SortedList<float, NoteEvent<int>> events { get; private set; }
|
||||||
|
|
||||||
|
public Chart()
|
||||||
|
{
|
||||||
|
doneLoading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// place notes and events relative to the previous
|
||||||
|
public void Load(Mer.Mer chart)
|
||||||
|
{
|
||||||
|
playNotes = new SortedList<float, List<NotePlay>>();
|
||||||
|
|
||||||
|
List<float> tempo = new List<float>();
|
||||||
|
List<int> tempoChangeMeasures = new List<int>();
|
||||||
|
List<int> tempoChangeBeats = new List<int>();
|
||||||
|
|
||||||
|
// TODO: switch to MeasureBeat
|
||||||
|
List<int> beatsPerMeasure = new List<int>();
|
||||||
|
List<int> bpmChangeMeasures = new List<int>();
|
||||||
|
|
||||||
|
tempo.Add(0);
|
||||||
|
tempoChangeMeasures.Add(0);
|
||||||
|
tempoChangeBeats.Add(0);
|
||||||
|
|
||||||
|
beatsPerMeasure.Add(0);
|
||||||
|
bpmChangeMeasures.Add(0);
|
||||||
|
|
||||||
|
float queuedTempo = -1;
|
||||||
|
int queuedBPM = -1;
|
||||||
|
|
||||||
|
// timing info of the previous beat
|
||||||
|
float prevTime = 0;
|
||||||
|
int prevMeasure = 0;
|
||||||
|
int prevBeat = 0; // (/1920 beats per measure)
|
||||||
|
|
||||||
|
Note prevNote = null;
|
||||||
|
Note curNote = null;
|
||||||
|
var prevHoldPoint = new System.Collections.Generic.Dictionary<int, NotePlay>(); // <note idx, previous point of hold>
|
||||||
|
var curHoldNote = new System.Collections.Generic.Dictionary<int, NoteHold>(); // <next hold idx, HoldStart>
|
||||||
|
|
||||||
|
// Notes and Events //
|
||||||
|
foreach (var measure in chart.notes) // `measure` = measure: List
|
||||||
|
{
|
||||||
|
foreach (var chartNote in measure.Value) // `chartNote` = beat, ChartNote
|
||||||
|
{
|
||||||
|
var curTime = prevTime + Util.NoteTime(measure.Key - prevMeasure, chartNote.Item1 - prevBeat, tempo.Last<float>(), beatsPerMeasure.Last<int>());
|
||||||
|
var mb = new MeasureBeat(measure.Key, chartNote.Item1);
|
||||||
|
|
||||||
|
if (prevMeasure != measure.Key && prevBeat != chartNote.Item1)
|
||||||
|
{
|
||||||
|
if (queuedTempo != -1)
|
||||||
|
{
|
||||||
|
tempo.Add(queuedTempo);
|
||||||
|
tempoChangeMeasures.Add(measure.Key);
|
||||||
|
tempoChangeBeats.Add(chartNote.Item1);
|
||||||
|
queuedTempo = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queuedBPM != -1)
|
||||||
|
{
|
||||||
|
beatsPerMeasure.Add(queuedBPM);
|
||||||
|
bpmChangeMeasures.Add(measure.Key);
|
||||||
|
queuedBPM = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// notetype-dependent operations
|
||||||
|
switch (chartNote.Item2.noteType)
|
||||||
|
{
|
||||||
|
// Beat map data
|
||||||
|
case MerType.Tempo:
|
||||||
|
if (tempo.Count == 1)
|
||||||
|
{
|
||||||
|
tempo.Add(float.Parse(chartNote.Item2.value));
|
||||||
|
tempoChangeMeasures.Add(measure.Key);
|
||||||
|
tempoChangeBeats.Add(chartNote.Item1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
queuedTempo = float.Parse(chartNote.Item2.value);
|
||||||
|
this.tempoChgs.Add(
|
||||||
|
curTime,
|
||||||
|
new NoteEvent<float> (
|
||||||
|
curTime, mb,
|
||||||
|
NoteEventType.Tempo,
|
||||||
|
value: float.Parse(chartNote.Item2.value)
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case MerType.TimeSignature:
|
||||||
|
var words = chartNote.Item2.value.Split();
|
||||||
|
var nu = int.Parse(words[0]);
|
||||||
|
var de = int.Parse(words[1]);
|
||||||
|
if (beatsPerMeasure.Count == 1)
|
||||||
|
{
|
||||||
|
beatsPerMeasure.Add(int.Parse(chartNote.Item2.value));
|
||||||
|
bpmChangeMeasures.Add(measure.Key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
queuedBPM = int.Parse(chartNote.Item2.value);
|
||||||
|
this.timeSigChgs.Add(
|
||||||
|
curTime,
|
||||||
|
new NoteEvent<(int, int)> (
|
||||||
|
curTime, mb,
|
||||||
|
NoteEventType.TimeSignature,
|
||||||
|
value: (nu, de)
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
// Playable notes
|
||||||
|
case MerType.Touch:
|
||||||
|
curNote = new NotePlay(
|
||||||
|
curTime, mb,
|
||||||
|
chartNote.Item2.position, chartNote.Item2.size
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case MerType.HoldStart:
|
||||||
|
curNote = new NoteHold(
|
||||||
|
curTime, mb,
|
||||||
|
chartNote.Item2.position, chartNote.Item2.size,
|
||||||
|
holdIndex: chartNote.Item2.holdIdx,
|
||||||
|
holdNext: chartNote.Item2.holdNextIdx
|
||||||
|
);
|
||||||
|
var nh = curNote as NoteHold;
|
||||||
|
prevHoldPoint[chartNote.Item2.holdNextIdx] = (NotePlay) curNote;
|
||||||
|
curHoldNote[chartNote.Item2.holdNextIdx] = nh;
|
||||||
|
break;
|
||||||
|
case MerType.HoldMid:
|
||||||
|
curNote = new NotePlay(
|
||||||
|
curTime, mb,
|
||||||
|
chartNote.Item2.position, chartNote.Item2.size,
|
||||||
|
type: NotePlayType.HoldMid,
|
||||||
|
holdIndex: chartNote.Item2.holdIdx,
|
||||||
|
holdNext: chartNote.Item2.holdNextIdx
|
||||||
|
);
|
||||||
|
prevHoldPoint[chartNote.Item2.holdNextIdx] = (NotePlay) curNote;
|
||||||
|
curHoldNote[chartNote.Item2.holdNextIdx] = curHoldNote[chartNote.Item2.holdIdx];
|
||||||
|
break;
|
||||||
|
case MerType.HoldEnd: // TODO: draw end note on cone texture
|
||||||
|
curNote = new NotePlay(
|
||||||
|
curTime, mb,
|
||||||
|
chartNote.Item2.position, chartNote.Item2.size,
|
||||||
|
type: NotePlayType.HoldEnd,
|
||||||
|
holdIndex: chartNote.Item2.holdIdx
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case MerType.Untimed:
|
||||||
|
curNote = new NotePlay(
|
||||||
|
curTime, mb,
|
||||||
|
chartNote.Item2.position, chartNote.Item2.size,
|
||||||
|
type: NotePlayType.Untimed
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case MerType.SwipeIn:
|
||||||
|
curNote = new NotePlay(
|
||||||
|
curTime, mb,
|
||||||
|
chartNote.Item2.position, chartNote.Item2.size,
|
||||||
|
type: NotePlayType.SwipeIn
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case MerType.SwipeOut:
|
||||||
|
curNote = new NotePlay(
|
||||||
|
curTime, mb,
|
||||||
|
chartNote.Item2.position, chartNote.Item2.size,
|
||||||
|
type: NotePlayType.SwipeOut
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case MerType.SwipeCW:
|
||||||
|
curNote = new NotePlay(
|
||||||
|
curTime, mb,
|
||||||
|
chartNote.Item2.position, chartNote.Item2.size,
|
||||||
|
type: NotePlayType.SwipeCW
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case MerType.SwipeCCW:
|
||||||
|
curNote = new NotePlay(
|
||||||
|
curTime, mb,
|
||||||
|
chartNote.Item2.position, chartNote.Item2.size,
|
||||||
|
type: NotePlayType.SwipeCCW
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
// Events (invisible modifier notes)
|
||||||
|
case MerType.BGAdd:
|
||||||
|
curNote = new NoteEvent<int>(
|
||||||
|
curTime, mb,
|
||||||
|
NoteEventType.BGAdd,
|
||||||
|
chartNote.Item2.position, chartNote.Item2.size,
|
||||||
|
value: int.Parse(chartNote.Item2.value)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case MerType.BGRem:
|
||||||
|
curNote = new NoteEvent<int>(
|
||||||
|
curTime, mb,
|
||||||
|
NoteEventType.BGRem,
|
||||||
|
chartNote.Item2.position, chartNote.Item2.size,
|
||||||
|
value: int.Parse(chartNote.Item2.value)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curNote == null || curNote == prevNote) continue;
|
||||||
|
|
||||||
|
/* Handle our crafted curNote, storing it somewhere in this Chart */
|
||||||
|
// NotePlay
|
||||||
|
var np = curNote as NotePlay;
|
||||||
|
if (np != null)
|
||||||
|
{
|
||||||
|
// hold point handling
|
||||||
|
if (np.type == NotePlayType.HoldMid || np.type == NotePlayType.HoldEnd)
|
||||||
|
{
|
||||||
|
curHoldNote[np.holdIdx].points[curTime] = np;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add note
|
||||||
|
if (!playNotes.ContainsKey(curTime))
|
||||||
|
{
|
||||||
|
playNotes[curTime] = new List<NotePlay>();
|
||||||
|
}
|
||||||
|
playNotes[curTime].Add(np);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoteEvent<float> -- tempo changes
|
||||||
|
var nef = curNote as NoteEvent<float>;
|
||||||
|
if (nef != null)
|
||||||
|
{
|
||||||
|
if (nef.type == NoteEventType.Tempo)
|
||||||
|
this.tempoChgs[curTime] = nef;
|
||||||
|
else
|
||||||
|
GD.Print($"Didn't add NoteEvent<float> of type {nef.type}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoteEvent<(int, int)> -- time signature changes
|
||||||
|
var neii = curNote as NoteEvent<(int, int)>;
|
||||||
|
if (neii != null)
|
||||||
|
{
|
||||||
|
this.timeSigChgs[curTime] = neii;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoteEvent<int>
|
||||||
|
var nei = curNote as NoteEvent<int>;
|
||||||
|
if (nei != null)
|
||||||
|
{
|
||||||
|
this.events[curTime] = nei;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update previous states
|
||||||
|
prevNote = curNote;
|
||||||
|
prevTime = curTime;
|
||||||
|
prevBeat = chartNote.Item1;
|
||||||
|
prevMeasure = measure.Key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// chords
|
||||||
|
foreach (KeyValuePair<float, List<NotePlay>> pair in playNotes)
|
||||||
|
{
|
||||||
|
List<Note> chordableNotes = new List<Note>();
|
||||||
|
foreach (NotePlay n in pair.Value)
|
||||||
|
{
|
||||||
|
if (n.type != NotePlayType.HoldEnd && n.type != NotePlayType.Untimed)
|
||||||
|
if (!(new NotePlayType[] { NotePlayType.HoldEnd, NotePlayType.Untimed, NotePlayType.HoldMid }).Contains(n.type))
|
||||||
|
chordableNotes.Add(n);
|
||||||
|
}
|
||||||
|
if (chordableNotes.Count >= 2)
|
||||||
|
{
|
||||||
|
GD.Print($"Found chord: {string.Join(", ", chordableNotes)}");
|
||||||
|
// TODO: draw chord indicators "Chordify"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measure Lines //
|
||||||
|
// TODO: adapt to tempo changes in the middle of a measure
|
||||||
|
// int tempoIdx = 1;
|
||||||
|
// int bpmIdx = 1;
|
||||||
|
// for (int curMeasure = 0; curMeasure < chart.notes.Count; curMeasure++)
|
||||||
|
// {
|
||||||
|
// while (curMeasure >= bpmChangeMeasures[bpmIdx] && bpmIdx < bpmChangeMeasures.Count - 1)
|
||||||
|
// ++bpmIdx;
|
||||||
|
// GD.Print($"{curMeasure}: {bpmIdx}");
|
||||||
|
|
||||||
|
// // last tempo change / only one tempo change exists
|
||||||
|
// if (tempoIdx == tempoChangeMeasures.Count - 1)
|
||||||
|
// {
|
||||||
|
// float pos = tempoChangePositions[tempoIdx] + Util.NotePosition(curMeasure - tempoChangeMeasures[tempoIdx], 0, tempo.Last(), beatsPerMeasure[bpmIdx]);
|
||||||
|
// var ml = measureLine.Instance<MeasureLine>();
|
||||||
|
// measureScroll.AddChild(ml);
|
||||||
|
// ml.Translation = new Vector3(0, 0, pos);
|
||||||
|
// ml.Text = $"{curMeasure}";
|
||||||
|
// }
|
||||||
|
// else if (tempoIdx < tempoChangeMeasures.Count)
|
||||||
|
// {
|
||||||
|
// // TODO: adapt to key signature changes
|
||||||
|
// while (curMeasure == tempoChangeMeasures[tempoIdx])
|
||||||
|
// {
|
||||||
|
// int measuresToCreate = tempoChangeMeasures[tempoIdx] - tempoChangeMeasures[tempoIdx - 1];
|
||||||
|
// for (int i = 0; i < measuresToCreate; ++i)
|
||||||
|
// {
|
||||||
|
// int measureNum = tempoChangeMeasures[tempoIdx - 1] + i;
|
||||||
|
// // GD.Print($"{tempoIdx} / {tempoChangePositions.Count}, {tempo.Count}");
|
||||||
|
// float pos = Util.InterpFloat(tempoChangePositions[tempoIdx - 1], tempoChangePositions[tempoIdx], (float)i/measuresToCreate);
|
||||||
|
|
||||||
|
// var ml = measureLine.Instance<MeasureLine>();
|
||||||
|
// measureScroll.AddChild(ml);
|
||||||
|
// ml.Translation = new Vector3(0, 0, pos);
|
||||||
|
// ml.Text = $"{curMeasure}";
|
||||||
|
// }
|
||||||
|
// tempoIdx = Mathf.Clamp(tempoIdx + 1, 0, tempo.Count - 1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
doneLoading = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 562cc671f5a30839c8769855d8d0483f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace WacK.Data.Chart
|
||||||
|
{
|
||||||
|
public enum DifficultyLevel
|
||||||
|
{
|
||||||
|
Normal, Hard, Expert, Inferno
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 02342af085b6384489297bdfaaff13f1
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Executable
+121
@@ -0,0 +1,121 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace WacK.Data.Chart
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a non-timed chart position in measure and beat as a fraction of the measure.
|
||||||
|
/// </summary>
|
||||||
|
public class MeasureBeat
|
||||||
|
{
|
||||||
|
public const int DENOMINATOR = 1920;
|
||||||
|
|
||||||
|
public int measure { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Beat in the measure, represented as beat/1920 of a measure.
|
||||||
|
/// </summary>
|
||||||
|
public int beat { get; private set; }
|
||||||
|
|
||||||
|
public MeasureBeat(int measure, int beat)
|
||||||
|
{
|
||||||
|
this.measure = measure;
|
||||||
|
this.beat = beat;
|
||||||
|
Normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Normalize()
|
||||||
|
{
|
||||||
|
// beats larger than denominator
|
||||||
|
if (Math.Abs(beat) >= DENOMINATOR)
|
||||||
|
{
|
||||||
|
measure += beat / DENOMINATOR;
|
||||||
|
beat %= DENOMINATOR;
|
||||||
|
}
|
||||||
|
// positive measure, negative beats
|
||||||
|
if (beat < 0 && measure > 0)
|
||||||
|
{
|
||||||
|
measure--;
|
||||||
|
beat += DENOMINATOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"MeasureBeat({this.measure}, {this.beat}/{DENOMINATOR})";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return measure*DENOMINATOR + beat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object tgt)
|
||||||
|
{
|
||||||
|
if (tgt == null) return false;
|
||||||
|
|
||||||
|
return this == (MeasureBeat)tgt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STATIC CONSTANTS */
|
||||||
|
public readonly static MeasureBeat ZERO = new MeasureBeat(0, 0);
|
||||||
|
|
||||||
|
/* STATIC OPERATORS */
|
||||||
|
public static MeasureBeat operator +(MeasureBeat a) => a;
|
||||||
|
public static MeasureBeat operator -(MeasureBeat a)
|
||||||
|
=> new MeasureBeat(-a.measure, -a.beat);
|
||||||
|
|
||||||
|
public static MeasureBeat operator +(MeasureBeat a, MeasureBeat b)
|
||||||
|
{
|
||||||
|
var meas = a.measure + b.measure;
|
||||||
|
var beat = a.beat + b.beat;
|
||||||
|
return new MeasureBeat(meas, beat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MeasureBeat operator -(MeasureBeat a, MeasureBeat b)
|
||||||
|
{
|
||||||
|
return a + -b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(MeasureBeat a, MeasureBeat b)
|
||||||
|
{
|
||||||
|
return a.measure == b.measure && a.beat == b.beat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(MeasureBeat a, MeasureBeat b)
|
||||||
|
{
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator <(MeasureBeat a, MeasureBeat b)
|
||||||
|
{
|
||||||
|
if (a.measure < b.measure) return true;
|
||||||
|
|
||||||
|
if (a.measure == b.measure)
|
||||||
|
{
|
||||||
|
return a.beat < b.beat;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator >(MeasureBeat a, MeasureBeat b)
|
||||||
|
{
|
||||||
|
if (a != b)
|
||||||
|
{
|
||||||
|
return !(a < b);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator <=(MeasureBeat a, MeasureBeat b)
|
||||||
|
{
|
||||||
|
if (a == b) return true;
|
||||||
|
return a < b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator >=(MeasureBeat a, MeasureBeat b)
|
||||||
|
{
|
||||||
|
if (a == b) return true;
|
||||||
|
return a > b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f19917e007c0523e9a5ccccea23e9524
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Executable
+43
@@ -0,0 +1,43 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace WacK.Data.Chart
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for various in-play note types.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class Note
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Time in milliseconds which the note occurs.
|
||||||
|
/// </summary>
|
||||||
|
public double time = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Time of the note in MeasureBeat.
|
||||||
|
/// </summary>
|
||||||
|
public MeasureBeat measureBeat;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The note's radial position out of 60.
|
||||||
|
/// </summary>
|
||||||
|
public int? position;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The radial size of the note.
|
||||||
|
/// 1 <= size <= 60
|
||||||
|
/// </summary>
|
||||||
|
public int? size;
|
||||||
|
|
||||||
|
public Note(double time, MeasureBeat measureBeat, int? position = null, int? size = null)
|
||||||
|
{
|
||||||
|
this.time = time;
|
||||||
|
this.measureBeat = measureBeat;
|
||||||
|
this.position = position;
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Note()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1ed8bb3e1326d18f9927b087b6e8fe7c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Executable
+36
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace WacK.Data.Chart
|
||||||
|
{
|
||||||
|
public enum NoteEventType
|
||||||
|
{
|
||||||
|
Tempo,
|
||||||
|
TimeSignature,
|
||||||
|
ScrollSpeedMultiplier,
|
||||||
|
BGAdd,
|
||||||
|
BGRem,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an unplayable event with some associated data value.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The value's type.</typeparam>
|
||||||
|
public class NoteEvent<T> : Note
|
||||||
|
{
|
||||||
|
public NoteEventType type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A value whose function will vary depending on the type of note.
|
||||||
|
/// </summary>
|
||||||
|
public T value;
|
||||||
|
|
||||||
|
public NoteEvent(double time, MeasureBeat measureBeat, NoteEventType type, int? position = null, int? size = null, T value = default(T)) :
|
||||||
|
base(time, measureBeat, position, size)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f133260cfe1efeb06bc84818f1f694c0
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Executable
+20
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace WacK.Data.Chart
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A hold note. Notably contains data about hold points.
|
||||||
|
/// </summary>
|
||||||
|
public class NoteHold : NotePlay
|
||||||
|
{
|
||||||
|
public SortedList<float, NotePlay> points;
|
||||||
|
|
||||||
|
public NoteHold(double time, MeasureBeat measureBeat, int position, int size, int holdIndex, int holdNext, bool bonus = false)
|
||||||
|
: base(time, measureBeat, position, size,holdIndex, holdNext, type: NotePlayType.HoldStart, bonus: false)
|
||||||
|
{
|
||||||
|
// points = (SortedList<float, Note>)holdPoints.Skip(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 74c839480bb719c9b96e11709817d2e6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Executable
+38
@@ -0,0 +1,38 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace WacK.Data.Chart
|
||||||
|
{
|
||||||
|
public enum NotePlayType
|
||||||
|
{
|
||||||
|
Touch,
|
||||||
|
HoldStart,
|
||||||
|
HoldMid,
|
||||||
|
HoldEnd,
|
||||||
|
Untimed,
|
||||||
|
SwipeIn,
|
||||||
|
SwipeOut,
|
||||||
|
SwipeCW,
|
||||||
|
SwipeCCW,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents playable notes.
|
||||||
|
/// </summary>
|
||||||
|
public class NotePlay : Note
|
||||||
|
{
|
||||||
|
public NotePlayType type { get; private set; }
|
||||||
|
public bool isBonus { get; private set; }
|
||||||
|
public int holdIdx { get; private set; }
|
||||||
|
public int holdNextIdx { get; private set; }
|
||||||
|
|
||||||
|
public NotePlay(double time, MeasureBeat measureBeat, int position, int size, int holdIndex = -1, int holdNext = -1,
|
||||||
|
NotePlayType type = NotePlayType.Touch, bool bonus = false)
|
||||||
|
: base(time, measureBeat, position, size)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
this.isBonus = bonus;
|
||||||
|
this.holdIdx = holdIndex;
|
||||||
|
this.holdNextIdx = holdNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ccfc6a251580ee67a83e2269870a227c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0ed6e3a31145daf8597c9bd80394d64d
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
/**
|
||||||
|
* Chart.cs
|
||||||
|
* Representation of a chart, constructed from a .mer file.
|
||||||
|
*
|
||||||
|
* by muskit
|
||||||
|
* July 1, 2022
|
||||||
|
**/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace WacK.Data.Mer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Structurized representation of a .mer file.
|
||||||
|
/// </summary>
|
||||||
|
public class Mer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// HIERARCHY:
|
||||||
|
/// Key is measure.
|
||||||
|
/// Value is List of (beat/1920, Notes) tuples.
|
||||||
|
/// </summary>
|
||||||
|
public SortedList<int, List<(int, MerNote)>> notes = new SortedList<int, List<(int, MerNote)>>();
|
||||||
|
|
||||||
|
public int playableNoteCount { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Construct Chart from contents of .mer file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str">Contents of a .mer file.</param>
|
||||||
|
public Mer(string str)
|
||||||
|
{
|
||||||
|
if (str == String.Empty) return;
|
||||||
|
|
||||||
|
playableNoteCount = 0;
|
||||||
|
|
||||||
|
List<string> lines = new List<string>(str.Split('\n'));
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
List<string> tokens = new List<string>(line.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries));
|
||||||
|
if (tokens.Count == 0) continue;
|
||||||
|
if (tokens[0][0] == '#') continue;
|
||||||
|
|
||||||
|
int currentMeasure = int.Parse(tokens[0]);
|
||||||
|
int currentBeat = int.Parse(tokens[1]);
|
||||||
|
|
||||||
|
if (!notes.ContainsKey(currentMeasure))
|
||||||
|
{
|
||||||
|
notes[currentMeasure] = new List<(int, MerNote)>();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (tokens[2])
|
||||||
|
{
|
||||||
|
case "1": // common note types
|
||||||
|
switch(tokens[3])
|
||||||
|
{
|
||||||
|
case "1": // touch
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), type: MerType.Touch)));
|
||||||
|
++playableNoteCount;
|
||||||
|
break;
|
||||||
|
case "2": // touch w/ bonus
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), type: MerType.Touch, bonus: true)));
|
||||||
|
++playableNoteCount;
|
||||||
|
break;
|
||||||
|
case "20": // touch w/ bonus (+ "big effect")
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), type: MerType.Touch, bonus: true)));
|
||||||
|
++playableNoteCount;
|
||||||
|
break;
|
||||||
|
case "16": // untimed
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), type: MerType.Untimed)));
|
||||||
|
++playableNoteCount;
|
||||||
|
break;
|
||||||
|
case "26": // untimed w/ bonus
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), type: MerType.Untimed, bonus: true)));
|
||||||
|
++playableNoteCount;
|
||||||
|
break;
|
||||||
|
case "3": // swipe in (red)
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), type: MerType.SwipeIn)));
|
||||||
|
++playableNoteCount;
|
||||||
|
break;
|
||||||
|
case "21": // swipe in w/ bonus
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), type: MerType.SwipeIn, bonus: true)));
|
||||||
|
++playableNoteCount;
|
||||||
|
break;
|
||||||
|
case "4": // swipe out (blue)
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), type: MerType.SwipeOut)));
|
||||||
|
++playableNoteCount;
|
||||||
|
break;
|
||||||
|
case "22": // swipe out w/ bonus
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), type: MerType.SwipeOut, bonus: true)));
|
||||||
|
++playableNoteCount;
|
||||||
|
break;
|
||||||
|
case "7": // swipe CCW
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), type: MerType.SwipeCCW)));
|
||||||
|
++playableNoteCount;
|
||||||
|
break;
|
||||||
|
case "8": // swipe CCW w/ bonus
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), type: MerType.SwipeCCW, bonus: true)));
|
||||||
|
++playableNoteCount;
|
||||||
|
break;
|
||||||
|
case "5": // swipe CW
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), type: MerType.SwipeCW)));
|
||||||
|
++playableNoteCount;
|
||||||
|
break;
|
||||||
|
case "6": // swipe CW w/ bonus
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), type: MerType.SwipeCW, bonus: true)));
|
||||||
|
++playableNoteCount;
|
||||||
|
break;
|
||||||
|
case "9": // hold start
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), holdIndex: int.Parse(tokens[4]), holdNext: int.Parse(tokens[8]), type: MerType.HoldStart)));
|
||||||
|
break;
|
||||||
|
case "25": // hold start (w/ bonus)
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), holdIndex: int.Parse(tokens[4]), holdNext: int.Parse(tokens[8]), type: MerType.HoldStart, bonus: true)));
|
||||||
|
++playableNoteCount;
|
||||||
|
break;
|
||||||
|
case "10": // hold middle
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), holdIndex: int.Parse(tokens[4]), holdNext: int.Parse(tokens[8]), type: MerType.HoldMid)));
|
||||||
|
break;
|
||||||
|
case "11": // hold end
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), holdIndex: int.Parse(tokens[4]), type: MerType.HoldEnd)));
|
||||||
|
break;
|
||||||
|
case "12": // BG add
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), value: tokens[8], type: MerType.BGAdd)));
|
||||||
|
break;
|
||||||
|
case "13": // BG rem
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(int.Parse(tokens[5]), int.Parse(tokens[6]), value: tokens[8], type: MerType.BGRem)));
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "2": // tempo
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(value: tokens[3], type: MerType.Tempo)));
|
||||||
|
break;
|
||||||
|
case "3": // beats per measure
|
||||||
|
notes[currentMeasure].Add((currentBeat, new MerNote(value: $"{tokens[3]} {tokens[4]}", type: MerType.TimeSignature)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var measure in notes)
|
||||||
|
{
|
||||||
|
measure.Value.Sort((x, y) => x.Item1.CompareTo(y.Item1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 03f65fd134dbd18faba49dbb870b3450
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* Note.cs
|
||||||
|
* A struct representing a note.
|
||||||
|
*
|
||||||
|
* by muskit
|
||||||
|
* July 1, 2022
|
||||||
|
**/
|
||||||
|
|
||||||
|
namespace WacK.Data.Mer
|
||||||
|
{
|
||||||
|
public enum MerType
|
||||||
|
{
|
||||||
|
Touch, HoldStart, HoldMid, HoldEnd, Untimed, SwipeIn, SwipeOut, SwipeCW, SwipeCCW, Tempo, TimeSignature, BGAdd, BGRem
|
||||||
|
}
|
||||||
|
public struct MerNote
|
||||||
|
{
|
||||||
|
public MerType noteType { get; private set; }
|
||||||
|
public bool isBonus { get; private set; }
|
||||||
|
|
||||||
|
// Radial values in minutes
|
||||||
|
public int position { get; private set; }
|
||||||
|
public int size { get; private set; } // 1 <= size <= 60
|
||||||
|
public string value { get; private set; }
|
||||||
|
public int holdIdx { get; private set; }
|
||||||
|
public int holdNextIdx { get; private set; }
|
||||||
|
|
||||||
|
public MerNote(int position = 0, int size = 1, string value = "", int holdIndex = -1, int holdNext = -1, MerType type = MerType.Touch, bool bonus = false)
|
||||||
|
{
|
||||||
|
this.position = position;
|
||||||
|
this.size = size;
|
||||||
|
this.value = value;
|
||||||
|
this.holdIdx = holdIndex;
|
||||||
|
this.holdNextIdx = holdNext;
|
||||||
|
this.noteType = type;
|
||||||
|
this.isBonus = bonus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f03d973b6879b96a5b79467d1dd3cbfc
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace WacK.Scenes
|
||||||
|
{
|
||||||
|
public partial class DebugChartLoader : Node
|
||||||
|
{
|
||||||
|
[Export]
|
||||||
|
private LineEdit inputField;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
private LineEdit soundField;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
private OptionButton difficultyButton;
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
private Button playButton;
|
||||||
|
|
||||||
|
|
||||||
|
// Called when the node enters the scene tree for the first time.
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
playButton.Pressed += PlayClicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PlayClicked()
|
||||||
|
{
|
||||||
|
// TODO: globally accessible verify song folder and chart/audio function
|
||||||
|
var songPath = $"user://songs/{inputField.Text}";
|
||||||
|
var chartPath = $"{songPath}/{difficultyButton.Selected}.mer";
|
||||||
|
var soundPath = $"{songPath}/{soundField.Text}";
|
||||||
|
GD.Print($"Song: {songPath}\nChart: {chartPath}\nSound: {soundPath}");
|
||||||
|
|
||||||
|
// folder check
|
||||||
|
using var dir = DirAccess.Open(songPath);
|
||||||
|
if (dir == null)
|
||||||
|
{
|
||||||
|
GD.PrintErr($"Error occurred opening song folder!\n{DirAccess.GetOpenError()}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// chart check
|
||||||
|
using var chartFile = FileAccess.Open(chartPath, FileAccess.ModeFlags.Read);
|
||||||
|
if (chartFile == null)
|
||||||
|
{
|
||||||
|
GD.PrintErr($"Error occurred opening chart!\n{FileAccess.GetOpenError()}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sound check
|
||||||
|
using var soundFile = FileAccess.Open(soundPath, FileAccess.ModeFlags.Read);
|
||||||
|
if (soundFile == null)
|
||||||
|
{
|
||||||
|
GD.PrintErr($"Error occurred opening sound!\n{FileAccess.GetOpenError()}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Play.playParams = new PlayParameters(chartPath, soundPath);
|
||||||
|
GetTree().ChangeSceneToFile("res://Scenes/Play.tscn");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _ExitTree()
|
||||||
|
{
|
||||||
|
playButton.Pressed -= PlayClicked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace WacK.Scenes
|
||||||
|
{
|
||||||
|
public class PlayParameters
|
||||||
|
{
|
||||||
|
/* TODO: store song ID from internal database
|
||||||
|
public string songID;
|
||||||
|
public Difficulty diff;
|
||||||
|
*/
|
||||||
|
public string chartPath;
|
||||||
|
public string soundPath;
|
||||||
|
|
||||||
|
public PlayParameters(string chPath, string snPath)
|
||||||
|
{
|
||||||
|
chartPath = chPath;
|
||||||
|
soundPath = snPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public partial class Play : Node
|
||||||
|
{
|
||||||
|
// initialized by another scene, BEFORE loading this one!
|
||||||
|
public static PlayParameters playParams;
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
playParams = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace WacK.Scenes
|
||||||
|
{
|
||||||
|
public partial class Startup : Node
|
||||||
|
{
|
||||||
|
// Called when the node enters the scene tree for the first time.
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
GD.Print($"User directory: {OS.GetUserDataDir()}");
|
||||||
|
using var songDir = DirAccess.Open("user://songs");
|
||||||
|
if (songDir != null)
|
||||||
|
{
|
||||||
|
GD.Print("Successfully opened songs directory!");
|
||||||
|
// load songs
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GD.Print("Could not find songs directory! Creating it...");
|
||||||
|
|
||||||
|
DirAccess.MakeDirAbsolute("user://songs");
|
||||||
|
using var newSongDir = DirAccess.Open("user://songs");
|
||||||
|
|
||||||
|
if (newSongDir != null)
|
||||||
|
{
|
||||||
|
GD.Print("Songs folder created successfully!");
|
||||||
|
// create note
|
||||||
|
var note = "Place song folders here. Nested folders supported for organization.\n";
|
||||||
|
using var f = FileAccess.Open($"{newSongDir.GetCurrentDir()}/note.txt", FileAccess.ModeFlags.Write);
|
||||||
|
f.StoreString(note);
|
||||||
|
|
||||||
|
// TODO: add in-game notice
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GD.PrintErr($"Could not create the songs directory!\n{DirAccess.GetOpenError()}");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change scenes
|
||||||
|
GetTree().ChangeSceneToFile("res://Scenes/DebugChartLoader.tscn");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+147
@@ -0,0 +1,147 @@
|
|||||||
|
/**
|
||||||
|
* Util.cs
|
||||||
|
* Various conversion functions.
|
||||||
|
*
|
||||||
|
* by muskit
|
||||||
|
* July 26, 2022
|
||||||
|
**/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
using WacK.Configuration;
|
||||||
|
|
||||||
|
namespace WacK
|
||||||
|
{
|
||||||
|
public static class Util
|
||||||
|
{
|
||||||
|
public static float Seg2Rad(float seg)
|
||||||
|
{
|
||||||
|
return Mathf.DegToRad(6f * seg);
|
||||||
|
}
|
||||||
|
public static float Rad2Seg(float angle)
|
||||||
|
{
|
||||||
|
return Mathf.RadToDeg(angle / 6f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int InterpInt(int a, int b, float ratio)
|
||||||
|
{
|
||||||
|
if (a == 0 && b == 0)
|
||||||
|
return 0;
|
||||||
|
return (int)Math.Round(a + (b - a) * ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float InterpFloat(float a, float b, float ratio)
|
||||||
|
{
|
||||||
|
if (a == 0 && b == 0)
|
||||||
|
return 0;
|
||||||
|
return a + (b - a) * ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns an equivalent destination angle that's closest to the origin.
|
||||||
|
public static float NearestAngle(float origin, float destination)
|
||||||
|
{
|
||||||
|
float result = destination;
|
||||||
|
|
||||||
|
float plus = destination + 2f * Mathf.Pi;
|
||||||
|
float minus = destination - 2f * Mathf.Pi;
|
||||||
|
float minusDelta = Mathf.Abs(minus - origin);
|
||||||
|
float normDelta = Mathf.Abs(destination - origin);
|
||||||
|
float plusDelta = Mathf.Abs(plus - origin);
|
||||||
|
if (plusDelta < normDelta)
|
||||||
|
result = plus;
|
||||||
|
if (minusDelta < normDelta)
|
||||||
|
result = minus;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an equivalent minute that's closest to the origin.
|
||||||
|
public static float NearestMinute(int origin, int destination)
|
||||||
|
{
|
||||||
|
int result = destination % 60;
|
||||||
|
|
||||||
|
int plus = destination + 60;
|
||||||
|
int minus = destination - 60;
|
||||||
|
int minusDelta = Math.Abs(minus - origin);
|
||||||
|
int normDelta = Math.Abs(destination - origin);
|
||||||
|
int plusDelta = Math.Abs(plus - origin);
|
||||||
|
if (plusDelta < normDelta)
|
||||||
|
result = plus;
|
||||||
|
if (minusDelta < normDelta)
|
||||||
|
result = minus;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float ScreenPixelToRad(Vector2 pos)
|
||||||
|
{
|
||||||
|
var resolution = DisplayServer.WindowGetSize();
|
||||||
|
var origin = new Vector2(resolution.X / 2 - 1, resolution.Y / 2 - 1);
|
||||||
|
|
||||||
|
return Mathf.Atan2(pos.Y - origin.Y, pos.X - origin.X);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int TouchPosToSegmentInt(Vector2 pos, Vector2 touchResolution)
|
||||||
|
{
|
||||||
|
var origin = new Vector2(touchResolution.X / 2 - 1, touchResolution.Y / 2 - 1);
|
||||||
|
var angle = Mathf.Atan2(pos.Y - origin.Y, pos.X - origin.X);
|
||||||
|
|
||||||
|
if (angle > 0)
|
||||||
|
angle = 2f * Mathf.Pi - angle;
|
||||||
|
|
||||||
|
return Mathf.FloorToInt(Mathf.Abs(angle) / 2f * Mathf.Pi * 60) % 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int ScreenPixelToSegmentInt(Vector2 pos)
|
||||||
|
{
|
||||||
|
var angle = ScreenPixelToRad(pos);
|
||||||
|
if (angle > 0)
|
||||||
|
angle = 2f * Mathf.Pi - angle;
|
||||||
|
|
||||||
|
return Mathf.FloorToInt(Mathf.Abs(angle) / 2f * Mathf.Pi * 60) % 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float NoteTime(int measure, int beat, float tempo, int beatsPerMeasure)
|
||||||
|
{
|
||||||
|
if (tempo == 0) return 0; // avoid divide by 0
|
||||||
|
|
||||||
|
return 60f / tempo * beatsPerMeasure * ((float)measure + (float)beat / 1920f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float NotePosition(int measure, int beat, float tempo, int beatsPerMeasure)
|
||||||
|
{
|
||||||
|
if (tempo == 0) return 0; // avoid divide by 0
|
||||||
|
return TimeToPosition(60f / tempo * beatsPerMeasure * ((float)measure + (float)beat / 1920f));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float TimeToPosition(float time)
|
||||||
|
{
|
||||||
|
return time * PlaySettings.playSpeedMultiplier * Constants.SCROLL_MULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float PositionToTime(float pos)
|
||||||
|
{
|
||||||
|
return pos / PlaySettings.playSpeedMultiplier / Constants.SCROLL_MULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: notes scale to scroll position instead of strikeline
|
||||||
|
// (where calibration offsets can be applied)
|
||||||
|
public static Vector3 NoteScale(float zPos, float zOrigin = 0)
|
||||||
|
{
|
||||||
|
var val = zPos - zOrigin;
|
||||||
|
if (val <= Constants.NOTE_DRAW_DISTANCE)
|
||||||
|
{
|
||||||
|
var ratio = Mathf.Clamp((Constants.NOTE_DRAW_DISTANCE - val) / Constants.NOTE_DRAW_DISTANCE, 0, 1);
|
||||||
|
return new Vector3(ratio, ratio, 1);
|
||||||
|
}
|
||||||
|
return Vector3.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string DifficultyValueToString(float diffPoint)
|
||||||
|
{
|
||||||
|
return Mathf.FloorToInt(diffPoint).ToString() + (diffPoint > Mathf.Floor(diffPoint) ? "+" : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[gd_scene format=3 uid="uid://b7dhdsryff4rn"]
|
||||||
|
|
||||||
|
[node name="TunnelObjects" type="Node2D"]
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[gd_scene format=3 uid="uid://cvxp0w0urvyhj"]
|
||||||
|
|
||||||
|
[node name="BackgroundSegment" type="Node2D"]
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<Project Sdk="Godot.NET.Sdk/4.1.1">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 2012
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WacK", "WacK.csproj", "{2797AE7C-0780-47EF-B89B-1F339403C89F}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
ExportDebug|Any CPU = ExportDebug|Any CPU
|
||||||
|
ExportRelease|Any CPU = ExportRelease|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{2797AE7C-0780-47EF-B89B-1F339403C89F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{2797AE7C-0780-47EF-B89B-1F339403C89F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{2797AE7C-0780-47EF-B89B-1F339403C89F}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU
|
||||||
|
{2797AE7C-0780-47EF-B89B-1F339403C89F}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU
|
||||||
|
{2797AE7C-0780-47EF-B89B-1F339403C89F}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU
|
||||||
|
{2797AE7C-0780-47EF-B89B-1F339403C89F}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg height="128" width="128" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="124" height="124" rx="14" fill="#363d52" stroke="#212532" stroke-width="4"/><g transform="scale(.101) translate(122 122)"><g fill="#fff"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 813 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H447l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c3 34 55 34 58 0v-86c-3-34-55-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 950 B |
@@ -0,0 +1,37 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cprfryf1c7kr2"
|
||||||
|
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://icon.svg"
|
||||||
|
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
; Engine configuration file.
|
||||||
|
; It's best edited using the editor UI and not directly,
|
||||||
|
; since the parameters that go here are not all obvious.
|
||||||
|
;
|
||||||
|
; Format:
|
||||||
|
; [section] ; section goes between []
|
||||||
|
; param=value ; assign values to parameters
|
||||||
|
|
||||||
|
config_version=5
|
||||||
|
|
||||||
|
[application]
|
||||||
|
|
||||||
|
config/name="WacK"
|
||||||
|
run/main_scene="res://Scenes/_STARTUP.tscn"
|
||||||
|
config/features=PackedStringArray("4.1", "C#", "Mobile")
|
||||||
|
config/icon="res://icon.svg"
|
||||||
|
|
||||||
|
[display]
|
||||||
|
|
||||||
|
window/size/viewport_width=1920
|
||||||
|
window/size/viewport_height=1080
|
||||||
|
window/stretch/mode="canvas_items"
|
||||||
|
window/stretch/aspect="expand"
|
||||||
|
window/vsync/vsync_mode=3
|
||||||
|
|
||||||
|
[dotnet]
|
||||||
|
|
||||||
|
project/assembly_name="WacK"
|
||||||
|
|
||||||
|
[rendering]
|
||||||
|
|
||||||
|
renderer/rendering_method="mobile"
|
||||||
Reference in New Issue
Block a user