Skip to content

Commit 630fa56

Browse files
authored
0.6.0.100 - fix MIDI inputs, song-end/fail judgement, autoplay, refactored input binding, ... (#910)
- [Fix] Refine gameplay time point and fix related issues (after climbed to tower top in tower mode, 2 seconds after last note, 1 second after `#END`, 0 seconds after music ends) - [Fix] Stop aligning branch end (except at end of song) in TJAP3 or OOS compatibility mode - [Fix] Prevent branch judge point before last measure (or `#NEXTSONG`, or 4 beats before chart start) - [Fix] Every player now has individual lives in Kanpeki mode - [Enhancement] Make mid-song fail animation play per player, and stop chart right when all players have failed [WARNING: skinners might need to update `<skin>/Graphics/5_Game/9_End/ClearFailed/Script.lua`] - [Enhancement] Missing end-of-play animation/sounds now fallbacks to red-pass (for Dan-i mode) or regular chart's animation/sounds - [Fix] Non-keyboard devices could not use some menus due to `CKeyAssign.KeyIsPressed()` could only detect keyboard inputs (#908) - [Feat] Reimplement MIDI input using managed-midi, using TJAPlayer3-f's implementation as reference (#612) - [Enhancement] Remember up to 36 (from 10) input devices per device type (Keyboard and Mouse remain not remembered) - [Fix] Ensure gameplay inputs are processed in event timestamp order - [Fix] In training mode, prevent pausing or (un)set bookmark during playing when the keybind is also player's game input (#903) - [Fix] Prevent wrong judgement window due to wrongly using "actual player index" - [Fix] Autoplay could only properly hit 1 note per frame, could miss when game lags, and could not handle multi-hit note misses properly - [Fix] Displayed score became less than real score when exceeding 256 score increases in a frame - [Fix] Played song speed was old one when first playing after adjusting song speed in training mode - [Fix] Crashes on selecting undefined difficulties (`LEVEL` before `COURSE`), again - [Fix] Towers with missing or negative `LIFE:` immediately failed player while having 5 lives (unless using Pause -> Retry) - [Fix] Kanpeki fail in tower chart at top floor could be judged as pass - [Fix] Disable Kanpeki mode in training mode - [Fix] Adlib and bomb were neither hit by autoplay nor missed and broke Dan-i condition monitoring - [Fix] Bomb and fuze roll did not trigger hard gauges and Kanpeki mode failure - [Fix] Tight (notelock) mode did not give BAD for empty hits since 0.6.0.93 - [Fix] Lyrics from `#LYRICS` appeared at wrong time in Dan-i mode - [Fix] Make TJA `LYRICFILE:` alias of `LYRICS:` - [Fix] Warn and ignore bad characters from TJA note data, for handling ill-formed comment (ignore if not digit nor letter) - [Fix] `box.def` parsing trimmed too many characters for values and allow omitting of colon (`:`) - [Fix] Calling `AddGraph()` in BG Lua Script twice with same filepath caused exception - [Fix] All BG Lua script errors are now logged and shown on screen - [Fix] Force big note firework for balloon's flying note - [Fix] AI battle section progression bar was negative before chart start - [Fix] Bad note position precision for extremely big/small SCROLL combined with small/big BPM values - [Fix] Clearing fuzeroll lacked character animation - [Fix] Roll redness fading out speed depended on framerate - [Fix] Notes and bar lines now continue at end of song and stop when failed in tower/Dan-i/Kanpeki failure, instead of simply disappearing - [Fix] Balloon's and fuze's remaining pop count was semi-transparent after non-balloon roll count fading out - [Fix] Revert `#DIRECTION` behavior to TJAP3 (keep vertical scrolling of non-real `#SCROLL`), accidentally changed in v0.6.0.56 - [Fix] Characters now play clear-out + miss-down animation when failed mid-song - [Fix] Bugs related to song end or mid-song failure animation - [Fix] Wrong character animation played when finishing tower - [Fix] Draw pop select menu on top, above fading in/out - [Fix] Opened Kusudama is now shown when pause menu is open - [Optimize] Minor optimizing for note-count-heavy charts: - [Optimize] Pre-build name-to-index lookup dictionary for puchi charas - [Optimize] Cache double-to-integer converted chip time - [Optimize] Only update `chip.nLag` when the note is actually judged - [Optimize] Make `enum NoteType` values same as channelNo to reduce conversions - [Optimize] Skip chip post processing of TJA parsing if is for song enumeration - [Optimize] Make `listBPM` an actual List instead of a Dictionary (due to legacy DTX parsing) - [Optimize] Only query all input devices for gameplay once instead of `EPad.Max` (38) times - [Optimize] Replace `OrderBy()` with in-place sort stabilized by index to eliminate temporary list - [Optimize] song-end animation could load twice or unused sound resources by only (un)loading at (De)`Activate()` - [Chore] Extract and use `NotesManager.GetChipGameType()` - [Chore] Remove unused fields in `CChip` - [Chore] Simplify input device querying - [Chore] Merge `enum EInputDevice` into FDK's `enum InputDeviceType` - [Chore] Simplify key assignment defining and accessing
1 parent b55d8ea commit 630fa56

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1412
-2912
lines changed

FDK/FDK.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
88
</PropertyGroup>
99
<ItemGroup>
10+
<PackageReference Include="managed-midi" Version="1.10.1" />
1011
<PackageReference Include="ReadJEnc" Version="1.3.1.2" />
1112
<PackageReference Include="Silk.NET.OpenGLES.Extensions.ImGui" Version="2.21.0" />
1213
<PackageReference Include="Silk.NET.Windowing" Version="2.21.0" />

FDK/src/00.Common/CConversion.cs

Lines changed: 71 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -73,98 +73,85 @@ public static int StringToInt(string text, int defaultValue) {
7373
return num;
7474
}
7575

76-
public static int HexStringToInt(string strNum) {
77-
if (strNum.Length < 2)
78-
return -1;
79-
80-
int digit2 = HexChars.IndexOf(strNum[0]);
81-
if (digit2 < 0)
82-
return -1;
83-
84-
if (digit2 >= 16)
85-
digit2 -= (16 - 10); // A,B,C... -> 1,2,3...
86-
87-
int digit1 = HexChars.IndexOf(strNum[1]);
88-
if (digit1 < 0)
89-
return -1;
90-
91-
if (digit1 >= 16)
92-
digit1 -= (16 - 10);
93-
94-
return digit2 * 16 + digit1;
95-
}
96-
97-
public static int Base36StringToInt(string strNum) {
98-
if (strNum.Length < 2)
99-
return -1;
100-
101-
int digit2 = Base36Chars.IndexOf(strNum[0]);
102-
if (digit2 < 0)
103-
return -1;
104-
105-
if (digit2 >= 36)
106-
digit2 -= (36 - 10); // A,B,C... -> 1,2,3...
107-
108-
int digit1 = Base36Chars.IndexOf(strNum[1]);
109-
if (digit1 < 0)
110-
return -1;
111-
112-
if (digit1 >= 36)
113-
digit1 -= (36 - 10);
114-
115-
return digit2 * 36 + digit1;
116-
}
117-
118-
public static int ParseSectionNumber(string strNum) {
119-
if (strNum.Length >= 3) {
120-
int digit3 = Base36Chars.IndexOf(strNum[0]);
121-
if (digit3 < 0)
122-
return -1;
123-
124-
if (digit3 >= 36) // 3桁目は36進数
125-
digit3 -= (36 - 10);
126-
127-
int digit2 = HexChars.IndexOf(strNum[1]); // 2桁目は10進数
128-
if ((digit2 < 0) || (digit2 > 9))
129-
return -1;
130-
131-
int digit1 = HexChars.IndexOf(strNum[2]); // 1桁目も10進数
132-
if ((digit1 >= 0) && (digit1 <= 9))
133-
return digit3 * 100 + digit2 * 10 + digit1;
76+
private static bool TryParseHexChar(char num, out int digit) {
77+
digit = HexChars.IndexOf(num);
78+
if (digit < 0)
79+
return false;
80+
// treat lower case as upper case
81+
if (digit >= 0x10)
82+
digit -= (0x10 - 10);
83+
return true;
84+
}
85+
86+
public static bool TryParseBase36Char(char num, out int digit) {
87+
digit = Base36Chars.IndexOf(num);
88+
if (digit < 0)
89+
return false;
90+
// treat lower case as upper case
91+
if (digit >= 36)
92+
digit -= (36 - 10);
93+
return true;
94+
}
95+
96+
public static int HexStringToInt(string strNum) =>
97+
(strNum.Length >= 2
98+
&& TryParseHexChar(strNum[0], out int digit2)
99+
&& TryParseHexChar(strNum[1], out int digit1)
100+
) ? digit2 * 0x10 + digit1
101+
: -1;
102+
103+
public static int Base36StringToInt(string strNum) =>
104+
(strNum.Length >= 2
105+
&& TryParseBase36Char(strNum[0], out int digit2)
106+
&& TryParseBase36Char(strNum[1], out int digit1)
107+
) ? digit2 * 36 + digit1
108+
: -1;
109+
110+
public static int ParseSectionNumber(string strNum) =>
111+
(strNum.Length >= 3
112+
&& TryParseBase36Char(strNum[0], out int digit3)
113+
&& TryParseHexChar(strNum[1], out int digit2)
114+
&& TryParseHexChar(strNum[2], out int digit1)
115+
) ? digit3 * 100 + digit2 * 10 + digit1
116+
: -1;
117+
118+
public static bool TryConvertToHexChar(int num, out char digit) {
119+
if (num >= 0 && num < 0x10) {
120+
digit = HexChars[num];
121+
return true;
134122
}
135-
return -1;
123+
digit = '\0';
124+
return false;
136125
}
137126

138-
public static string SectionNumberToString(int num) {
139-
if ((num < 0) || (num >= 3600)) // 3600 == Z99 + 1
140-
return "000";
141-
142-
int digit4 = num / 100;
143-
int digit2 = (num % 100) / 10;
144-
int digit1 = (num % 100) % 10;
145-
char ch3 = Base36Chars[digit4];
146-
char ch2 = HexChars[digit2];
147-
char ch1 = HexChars[digit1];
148-
return (ch3.ToString() + ch2.ToString() + ch1.ToString());
127+
public static bool TryConvertToBase36Char(int num, out char digit) {
128+
if (num >= 0 && num < 36) {
129+
digit = Base36Chars[num];
130+
return true;
131+
}
132+
digit = '\0';
133+
return false;
149134
}
150135

151-
public static string IntToHexString(int num) {
152-
if ((num < 0) || (num >= 0x100))
153-
return "00";
136+
public static string SectionNumberToString(int num) =>
137+
(TryConvertToBase36Char(num / 100, out char ch3)
138+
&& TryConvertToHexChar((num % 100) / 10, out char ch2)
139+
&& TryConvertToHexChar((num % 100) % 10, out char ch1)
140+
) ? (ch3.ToString() + ch2.ToString() + ch1.ToString())
141+
: "000";
154142

155-
char ch2 = HexChars[num / 0x10];
156-
char ch1 = HexChars[num % 0x10];
157-
return (ch2.ToString() + ch1.ToString());
158-
}
143+
public static string IntToHexString(int num) =>
144+
(TryConvertToHexChar(num / 0x10, out char ch2)
145+
&& TryConvertToHexChar(num % 0x10, out char ch1)
146+
) ? (ch2.ToString() + ch1.ToString())
147+
: "00";
159148

160-
public static string IntToBase36String(int num) {
161-
if ((num < 0) || (num >= 36 * 36))
162-
return "00";
163149

164-
char ch2 = Base36Chars[num / 36];
165-
char ch1 = Base36Chars[num % 36];
166-
return (ch2.ToString() + ch1.ToString());
167-
}
150+
public static string IntToBase36String(int num) =>
151+
(TryConvertToBase36Char(num / 36, out char ch2)
152+
&& TryConvertToBase36Char(num % 36, out char ch1)
153+
) ? (ch2.ToString() + ch1.ToString())
154+
: "00";
168155

169156
public static int[] StringToIntArray(string str) {
170157
//0,1,2 ...の形式で書かれたstringをint配列に変換する。

FDK/src/02.Input/CInputButtonsBase.cs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ public CInputButtonsBase(int nButtonStates) {
1414

1515
// メソッド
1616

17-
public void SetID(int nID) => this.ID = nID;
17+
public virtual int GetVelocity(int index) => 0;
18+
protected virtual void SetVelocity(int index, int velocity) { }
1819

1920
#region [ IInputDevice 実装 ]
2021
//-----------------
21-
public Silk.NET.Input.IInputDevice Device { get; protected set; }
22+
public Silk.NET.Input.IInputDevice? Device { get; protected set; }
2223
public InputDeviceType CurrentType { get; protected set; }
2324
public string GUID { get; protected set; }
24-
public int ID { get; protected set; }
25+
public int ID { get; set; }
2526
public string Name { get; protected set; }
2627
public List<STInputEvent> InputEvents { get; protected set; }
2728
public string strDeviceName { get; set; }
@@ -34,20 +35,20 @@ public void Polling() {
3435
// for buffered input, the input buffer has already been filled.
3536
for (int i = 0; i < ButtonStates.Length; i++) {
3637
// Use the same timer used in gameplay to prevent desyncs between BGM/chart and input.
37-
this.ProcessButtonState(i, SoundManager.PlayTimer.SystemTimeMs);
38+
this.ProcessButtonState(i, SoundManager.PlayTimer.SystemTimeMs, this.GetVelocity(i));
3839
}
3940
// swap input buffer
4041
(this.InputEvents, this.EventBuffer) = (this.EventBuffer, this.InputEvents);
4142
}
4243

43-
protected void ProcessButtonState(int idxBtn, long msTimestamp) {
44+
protected void ProcessButtonState(int idxBtn, long msTimestamp, int velocity = 0) {
4445
if (ButtonStates[idxBtn].isPressed) {
4546
if (ButtonStates[idxBtn].state >= 1) {
4647
ButtonStates[idxBtn].state = 2;
4748
} else {
4849
ButtonStates[idxBtn].state = 1;
4950
if (!this.useBufferInput) {
50-
this.AddPressedEvent(idxBtn, msTimestamp);
51+
this.AddPressedEvent(idxBtn, msTimestamp, velocity);
5152
}
5253
}
5354
} else {
@@ -71,20 +72,21 @@ protected void AddReleasedEvent(int idxBtn, long msTImestamp)
7172
nVelocity = 0,
7273
});
7374

74-
protected void AddPressedEvent(int idxBtn, long msTimestamp)
75+
protected void AddPressedEvent(int idxBtn, long msTimestamp, int velocity = 0)
7576
=> this.EventBuffer.Add(new STInputEvent() {
7677
nKey = idxBtn,
7778
Pressed = true,
7879
Released = false,
7980
nTimeStamp = msTimestamp,
80-
nVelocity = 0,
81+
nVelocity = velocity,
8182
});
8283

83-
protected void ButtonDown(int idxBtn) {
84+
protected void ButtonDown(int idxBtn, int velocity = 0) {
8485
if (this.useBufferInput && !this.ButtonStates[idxBtn].isPressed) {
85-
this.AddPressedEvent(idxBtn, SoundManager.PlayTimer.msGetPreciseNowSoundTimerTime());
86+
this.AddPressedEvent(idxBtn, SoundManager.PlayTimer.msGetPreciseNowSoundTimerTime(), velocity);
8687
}
8788
this.ButtonStates[idxBtn].isPressed = true;
89+
this.SetVelocity(idxBtn, velocity);
8890
}
8991

9092
protected void ButtonUp(int idxBtn) {
@@ -111,7 +113,7 @@ public bool KeyReleasing(int nButton) {
111113

112114
#region [ IDisposable 実装 ]
113115
//-----------------
114-
public void Dispose() {
116+
public virtual void Dispose() {
115117
if (!this.IsDisposed) {
116118
this.InputEvents.Clear();
117119
this.EventBuffer.Clear();
@@ -128,7 +130,7 @@ public void Dispose() {
128130
//-----------------
129131
public List<STInputEvent> EventBuffer;
130132
public (bool isPressed, int state)[] ButtonStates { get; protected set; }
131-
private bool IsDisposed;
133+
protected bool IsDisposed;
132134
//-----------------
133135
#endregion
134136
}

0 commit comments

Comments
 (0)