Skip to content

Commit 5b2a86a

Browse files
Input System Rewrite (#810)
* the freaking rewrite bro! * backwards compatibility * listened to some feedback * legacyJudge now determines on the API number * listened to feedback (again)
1 parent 43d599a commit 5b2a86a

File tree

7 files changed

+314
-38
lines changed

7 files changed

+314
-38
lines changed

source/funkin/backend/scripting/events/note/NoteHitEvent.hx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ final class NoteHitEvent extends CancellableEvent {
118118
* The attached healthIcon used distinction for icons amongst others
119119
*/
120120
public var healthIcon:HealthIcon;
121+
/**
122+
* Whether note hits are judged in the old way or not.
123+
*/
124+
public var legacyJudge(get, set):Bool;
121125

122126
/**
123127
* Prevents the default sing animation from being played.
@@ -196,4 +200,15 @@ final class NoteHitEvent extends CancellableEvent {
196200
characters = [char];
197201
return char;
198202
}
203+
204+
private var _explicitLegacyJudge:Null<Bool> = null;
205+
private inline function get_legacyJudge():Bool {
206+
if (_explicitLegacyJudge != null)
207+
return _explicitLegacyJudge;
208+
return Flags.CURRENT_API_VERSION == 1;
209+
}
210+
private function set_legacyJudge(value:Bool):Bool {
211+
_explicitLegacyJudge = value;
212+
return value;
213+
}
199214
}

source/funkin/backend/system/Flags.hx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class Flags {
4141
@:lazy public static var SAVE_PATH:String = haxe.macro.Compiler.getDefine("SAVE_PATH");
4242
@:lazy public static var SAVE_NAME:String = haxe.macro.Compiler.getDefine("SAVE_NAME");
4343

44-
public static var CURRENT_API_VERSION:Int = 1;
44+
public static var CURRENT_API_VERSION:Int = 2;
4545
public static var COMMIT_NUMBER:Int = GitCommitMacro.commitNumber;
4646
public static var COMMIT_HASH:String = GitCommitMacro.commitHash;
4747
public static var COMMIT_MESSAGE:String = 'Commit $COMMIT_NUMBER ($COMMIT_HASH)';

source/funkin/game/Note.hx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ class Note extends FlxSprite
6060
*/
6161
public var sustainParent:Null<Note>;
6262

63+
/**
64+
* Number of active sustain pieces attached to this note
65+
*
66+
* Increases by 1 every time a hold piece is initialized.
67+
*
68+
* Decreases by 1 every time a hold piece gets destroyed.
69+
*/
70+
public var tailCount:Int = 0;
71+
6372
/**
6473
* Name of the splash.
6574
*/
@@ -104,6 +113,8 @@ class Note extends FlxSprite
104113

105114
public var animSuffix:String = null;
106115

116+
public var tripTimer:Float = 0; // ranges from 0 to 1
117+
107118

108119
private static function customTypePathExists(path:String) {
109120
if (__customNoteTypeExists.exists(path))

source/funkin/game/PlayState.hx

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import funkin.editors.charter.Charter;
2929
import funkin.editors.charter.CharterSelection;
3030
import funkin.game.SplashHandler;
3131
import funkin.game.cutscenes.*;
32+
import funkin.game.scoring.*;
33+
import funkin.game.scoring.RatingManager.Rating;
3234
import funkin.menus.*;
3335
import funkin.backend.week.WeekData;
3436
import funkin.savedata.FunkinSave;
@@ -529,6 +531,10 @@ class PlayState extends MusicBeatState
529531
* Group containing all of the combo sprites.
530532
*/
531533
public var comboGroup:RotatingSpriteGroup;
534+
/**
535+
* Manager that helps judge note hits to return ratings.
536+
*/
537+
public var ratingManager:RatingManager = new RatingManager();
532538
/**
533539
* Whenever the Rating sprites should be shown or not.
534540
*
@@ -1882,40 +1888,44 @@ class PlayState extends MusicBeatState
18821888
* CALCULATES RATING
18831889
*/
18841890
var noteDiff = Math.abs(Conductor.songPosition - note.strumTime);
1885-
var daRating:String = "sick";
1886-
var score:Int = 300;
1887-
var accuracy:Float = 1;
1888-
1889-
if (noteDiff > hitWindow * 0.9)
1890-
{
1891-
daRating = 'shit';
1892-
score = 50;
1893-
accuracy = 0.25;
1894-
}
1895-
else if (noteDiff > hitWindow * 0.75)
1896-
{
1897-
daRating = 'bad';
1898-
score = 100;
1899-
accuracy = 0.45;
1900-
}
1901-
else if (noteDiff > hitWindow * 0.2)
1902-
{
1903-
daRating = 'good';
1904-
score = 200;
1905-
accuracy = 0.75;
1906-
}
1891+
var daRating:Rating = ratingManager.judgeNote(noteDiff);
19071892

19081893
var event:NoteHitEvent;
19091894
if (strumLine != null && !strumLine.cpu)
1910-
event = EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, score, note.isSustainNote ? null : accuracy, 0.023, daRating, Options.splashesEnabled && !note.isSustainNote && daRating == "sick", 0.5, true, 0.7, true, true, iconP1);
1895+
event = EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, daRating.score, note.isSustainNote ? null : daRating.accuracy, 0.023, daRating.name, Options.splashesEnabled && !note.isSustainNote && daRating.splash, 0.5, true, 0.7, true, true, iconP1);
19111896
else
1912-
event = EventManager.get(NoteHitEvent).recycle(false, false, false, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, daRating, false, 0.5, true, 0.7, true, true, iconP2);
1897+
event = EventManager.get(NoteHitEvent).recycle(false, false, false, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, daRating.name, false, 0.5, true, 0.7, true, true, iconP2);
19131898
event.deleteNote = !note.isSustainNote; // work around, to allow sustain notes to be deleted
19141899
event = scripts.event(strumLine != null && !strumLine.cpu ? "onPlayerHit" : "onDadHit", event);
19151900
strumLine.onHit.dispatch(event);
19161901
gameAndCharsEvent("onNoteHit", event);
19171902

19181903
if (!event.cancelled) {
1904+
if (event.legacyJudge) {
1905+
event.rating = 'sick';
1906+
event.score = 300;
1907+
event.accuracy = 1;
1908+
1909+
if (noteDiff > hitWindow * 0.9)
1910+
{
1911+
event.rating = 'shit';
1912+
event.score = 50;
1913+
event.accuracy = 0.25;
1914+
}
1915+
else if (noteDiff > hitWindow * 0.75)
1916+
{
1917+
event.rating = 'bad';
1918+
event.score = 100;
1919+
event.accuracy = 0.45;
1920+
}
1921+
else if (noteDiff > hitWindow * 0.2)
1922+
{
1923+
event.rating = 'good';
1924+
event.score = 200;
1925+
event.accuracy = 0.75;
1926+
}
1927+
}
1928+
19191929
if (!note.isSustainNote) {
19201930
if (event.countScore) songScore += event.score;
19211931
if (event.accuracy != null) {

source/funkin/game/StrumLine.hx

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ class StrumLine extends FlxTypedGroup<Strum> {
161161
curLen = Math.min(len, Conductor.stepCrochet);
162162
notes.members[total-(il++)-1] = prev = new Note(this, note, true, curLen, note.sLen - len, prev);
163163
len -= curLen;
164+
165+
if (prev != null && prev.sustainParent != null)
166+
prev.sustainParent.tailCount++;
164167
}
165168
}
166169
}
@@ -236,10 +239,23 @@ class StrumLine extends FlxTypedGroup<Strum> {
236239

237240

238241
if (__updateNote_event.strum == null) return;
239-
240242
if (__updateNote_event.__reposNote) __updateNote_event.strum.updateNotePosition(daNote);
243+
244+
241245
if (daNote.isSustainNote)
246+
{
242247
daNote.updateSustain(__updateNote_event.strum);
248+
249+
if (daNote.tripTimer > 0 && daNote.tailCount > 3)
250+
{
251+
daNote.tripTimer -= 0.05 / daNote.sustainLength;
252+
if (daNote.tripTimer <= 0)
253+
{
254+
daNote.tripTimer = 0;
255+
daNote.canBeHit = false;
256+
}
257+
}
258+
}
243259
}
244260

245261
var __funcsToExec:Array<Note->Void> = [];
@@ -249,16 +265,27 @@ class StrumLine extends FlxTypedGroup<Strum> {
249265
var __notePerStrum:Array<Note> = [];
250266

251267
function __inputProcessPressed(note:Note) {
252-
if (__pressed[note.strumID] && note.isSustainNote && note.strumTime < __updateNote_songPos && !note.wasGoodHit) {
268+
if (__pressed[note.strumID] && note.isSustainNote && note.sustainParent != null && note.sustainParent.wasGoodHit && note.strumTime < __updateNote_songPos && !note.wasGoodHit) {
269+
note.tripTimer = 1;
253270
PlayState.instance.goodNoteHit(this, note);
254271
note.updateSustainClip();
255272
}
256273
}
257274
function __inputProcessJustPressed(note:Note) {
258275
if (__justPressed[note.strumID] && !note.isSustainNote && !note.wasGoodHit && note.canBeHit) {
259-
if (__notePerStrum[note.strumID] == null) __notePerStrum[note.strumID] = note;
260-
else if (Math.abs(__notePerStrum[note.strumID].strumTime - note.strumTime) <= 2) deleteNote(note);
261-
else if (note.strumTime < __notePerStrum[note.strumID].strumTime) __notePerStrum[note.strumID] = note;
276+
var cur = __notePerStrum[note.strumID];
277+
var songPos = __updateNote_songPos;
278+
279+
var noteDist = Math.abs(note.strumTime - songPos);
280+
var curDist = cur != null ? Math.abs(cur.strumTime - songPos) : 999999;
281+
282+
var notePenalty = note.avoid ? 1 : 0;
283+
var curPenalty = (cur != null && cur.avoid) ? 1 : 0;
284+
285+
if (cur == null
286+
|| notePenalty < curPenalty
287+
|| (notePenalty == curPenalty && noteDist < curDist))
288+
__notePerStrum[note.strumID] = note;
262289
}
263290
}
264291

@@ -272,14 +299,14 @@ class StrumLine extends FlxTypedGroup<Strum> {
272299
if (cpu) return;
273300

274301
__funcsToExec.clear();
275-
__pressed.clear();
276-
__justPressed.clear();
277-
__justReleased.clear();
278-
279-
for(s in members) {
280-
__pressed.push(s.__getPressed(this));
281-
__justPressed.push(s.__getJustPressed(this));
282-
__justReleased.push(s.__getJustReleased(this));
302+
__pressed.resize(members.length);
303+
__justPressed.resize(members.length);
304+
__justReleased.resize(members.length);
305+
306+
for(i in 0...members.length) {
307+
__pressed[i] = members[i].__getPressed(this);
308+
__justPressed[i] = members[i].__getJustPressed(this);
309+
__justReleased[i] = members[i].__getJustReleased(this);
283310
}
284311

285312
var event = EventManager.get(InputSystemEvent).recycle(__pressed, __justPressed, __justReleased, this, id);
@@ -411,6 +438,8 @@ class StrumLine extends FlxTypedGroup<Strum> {
411438
var event:SimpleNoteEvent = EventManager.get(SimpleNoteEvent).recycle(note);
412439
onNoteDelete.dispatch(event);
413440
if (!event.cancelled) {
441+
if (note.isSustainNote && note.sustainParent != null && note.sustainParent.tailCount > 0)
442+
note.sustainParent.tailCount--;
414443
note.kill();
415444
notes.remove(note, true);
416445
note.destroy();
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package funkin.game.scoring;
2+
3+
import haxe.ds.StringMap;
4+
5+
class HitWindowData
6+
{
7+
public static function getWindows(preset:WindowPreset):StringMap<Float>
8+
{
9+
var map = new StringMap<Float>();
10+
11+
switch (preset) {
12+
// Old Codename, really forgiving inputs (hard to get bad ratings)
13+
case CNE_CLASSIC:
14+
map.set("sick", 50.0);
15+
map.set("good", 187.5);
16+
map.set("bad", 225.0);
17+
map.set("shit", 250.0);
18+
// Week 7
19+
case FNF_CLASSIC:
20+
map.set("sick", 33.334);
21+
map.set("good", 125.0025);
22+
map.set("bad", 150.003);
23+
map.set("shit", 166.67);
24+
// V-Slice
25+
case FNF_VSLICE:
26+
map.set("sick", 45.0);
27+
map.set("good", 90.0);
28+
map.set("bad", 135.4);
29+
map.set("shit", 180.0);
30+
// Default, taken from Etterna
31+
case _:
32+
map.set("sick", 37.8);
33+
map.set("good", 75.6);
34+
map.set("bad", 113.4);
35+
map.set("shit", 180.0);
36+
}
37+
38+
return map;
39+
}
40+
41+
public static var JUDGE_SCALES:Array<Float> = [1.5, 1.33, 1.16, 1.0, 0.84, 0.66, 0.5, 0.33, 0.2];
42+
public static function scaleWindows(windows:StringMap<Float>, scale:Float):StringMap<Float>
43+
{
44+
var scaled = new StringMap<Float>();
45+
for (k in windows.keys())
46+
scaled.set(k, windows.get(k) * scale);
47+
return scaled;
48+
}
49+
50+
public static function offsetWindows(windows:StringMap<Float>, offset:Float):StringMap<Float>
51+
{
52+
var adjusted = new StringMap<Float>();
53+
for (k in windows.keys())
54+
adjusted.set(k, windows.get(k) + offset);
55+
return adjusted;
56+
}
57+
}
58+
59+
enum abstract WindowPreset(Int) from Int to Int
60+
{
61+
var DEFAULT = 0;
62+
var CNE_CLASSIC = 1;
63+
var FNF_CLASSIC = 2;
64+
var FNF_VSLICE = 3;
65+
66+
public function toString():String
67+
{
68+
return switch (cast this : WindowPreset)
69+
{
70+
case CNE_CLASSIC: "Codename (Classic)";
71+
case FNF_CLASSIC: "Funkin' (Week 7)";
72+
case FNF_VSLICE: "Funkin' (V-Slice)";
73+
case _: "Default";
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)