Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions source/funkin/backend/scripting/events/note/NoteHitEvent.hx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ final class NoteHitEvent extends CancellableEvent {
* The attached healthIcon used distinction for icons amongst others
*/
public var healthIcon:HealthIcon;
/**
* Whether note hits are judged in the old way or not.
*/
public var legacyJudge:Bool = false;

/**
* Prevents the default sing animation from being played.
Expand Down
11 changes: 11 additions & 0 deletions source/funkin/game/Note.hx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ class Note extends FlxSprite
*/
public var sustainParent:Null<Note>;

/**
* Number of active sustain pieces attached to this note
*
* Increases by 1 every time a hold piece is initialized.
*
* Decreases by 1 every time a hold piece gets destroyed.
*/
public var tail:Int = 0;

/**
* Name of the splash.
*/
Expand Down Expand Up @@ -104,6 +113,8 @@ class Note extends FlxSprite

public var animSuffix:String = null;

public var tripTimer:Float = 0; // ranges from 0 to 1


private static function customTypePathExists(path:String) {
if (__customNoteTypeExists.exists(path))
Expand Down
58 changes: 34 additions & 24 deletions source/funkin/game/PlayState.hx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import funkin.editors.charter.Charter;
import funkin.editors.charter.CharterSelection;
import funkin.game.SplashHandler;
import funkin.game.cutscenes.*;
import funkin.game.scoring.*;
import funkin.game.scoring.RatingManager.Rating;
import funkin.menus.*;
import funkin.backend.week.WeekData;
import funkin.savedata.FunkinSave;
Expand Down Expand Up @@ -529,6 +531,10 @@ class PlayState extends MusicBeatState
* Group containing all of the combo sprites.
*/
public var comboGroup:RotatingSpriteGroup;
/**
* Manager that helps judge note hits to return ratings.
*/
public var ratingManager:RatingManager = new RatingManager();
/**
* Whenever the Rating sprites should be shown or not.
*
Expand Down Expand Up @@ -1880,40 +1886,44 @@ class PlayState extends MusicBeatState
* CALCULATES RATING
*/
var noteDiff = Math.abs(Conductor.songPosition - note.strumTime);
var daRating:String = "sick";
var score:Int = 300;
var accuracy:Float = 1;

if (noteDiff > hitWindow * 0.9)
{
daRating = 'shit';
score = 50;
accuracy = 0.25;
}
else if (noteDiff > hitWindow * 0.75)
{
daRating = 'bad';
score = 100;
accuracy = 0.45;
}
else if (noteDiff > hitWindow * 0.2)
{
daRating = 'good';
score = 200;
accuracy = 0.75;
}
var daRating:Rating = ratingManager.judgeNote(noteDiff);

var event:NoteHitEvent;
if (strumLine != null && !strumLine.cpu)
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);
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, false);
else
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);
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, false);
event.deleteNote = !note.isSustainNote; // work around, to allow sustain notes to be deleted
event = scripts.event(strumLine != null && !strumLine.cpu ? "onPlayerHit" : "onDadHit", event);
strumLine.onHit.dispatch(event);
gameAndCharsEvent("onNoteHit", event);

if (!event.cancelled) {
if (event.legacyJudge) {
event.rating = 'sick';
event.score = 300;
event.accuracy = 1;

if (noteDiff > hitWindow * 0.9)
{
event.rating = 'shit';
event.score = 50;
event.accuracy = 0.25;
}
else if (noteDiff > hitWindow * 0.75)
{
event.rating = 'bad';
event.score = 100;
event.accuracy = 0.45;
}
else if (noteDiff > hitWindow * 0.2)
{
event.rating = 'good';
event.score = 200;
event.accuracy = 0.75;
}
}

if (!note.isSustainNote) {
if (event.countScore) songScore += event.score;
if (event.accuracy != null) {
Expand Down
55 changes: 42 additions & 13 deletions source/funkin/game/StrumLine.hx
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ class StrumLine extends FlxTypedGroup<Strum> {
curLen = Math.min(len, Conductor.stepCrochet);
notes.members[total-(il++)-1] = prev = new Note(this, note, true, curLen, note.sLen - len, prev);
len -= curLen;

if (prev != null && prev.sustainParent != null)
prev.sustainParent.tail++;
}
}
}
Expand Down Expand Up @@ -236,10 +239,23 @@ class StrumLine extends FlxTypedGroup<Strum> {


if (__updateNote_event.strum == null) return;

if (__updateNote_event.__reposNote) __updateNote_event.strum.updateNotePosition(daNote);


if (daNote.isSustainNote)
{
daNote.updateSustain(__updateNote_event.strum);

if (daNote.tripTimer > 0 && daNote.tail > 3)
{
daNote.tripTimer -= 0.05 / daNote.sustainLength;
if (daNote.tripTimer <= 0)
{
daNote.tripTimer = 0;
daNote.canBeHit = false;
}
}
}
}

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

function __inputProcessPressed(note:Note) {
if (__pressed[note.strumID] && note.isSustainNote && note.strumTime < __updateNote_songPos && !note.wasGoodHit) {
if (__pressed[note.strumID] && note.isSustainNote && note.sustainParent != null && note.sustainParent.wasGoodHit && note.strumTime < __updateNote_songPos && !note.wasGoodHit) {
note.tripTimer = 1;
PlayState.instance.goodNoteHit(this, note);
note.updateSustainClip();
}
}
function __inputProcessJustPressed(note:Note) {
if (__justPressed[note.strumID] && !note.isSustainNote && !note.wasGoodHit && note.canBeHit) {
if (__notePerStrum[note.strumID] == null) __notePerStrum[note.strumID] = note;
else if (Math.abs(__notePerStrum[note.strumID].strumTime - note.strumTime) <= 2) deleteNote(note);
else if (note.strumTime < __notePerStrum[note.strumID].strumTime) __notePerStrum[note.strumID] = note;
var cur = __notePerStrum[note.strumID];
var songPos = __updateNote_songPos;

var noteDist = Math.abs(note.strumTime - songPos);
var curDist = cur != null ? Math.abs(cur.strumTime - songPos) : 999999;

var notePenalty = note.avoid ? 1 : 0;
var curPenalty = (cur != null && cur.avoid) ? 1 : 0;

if (cur == null
|| notePenalty < curPenalty
|| (notePenalty == curPenalty && noteDist < curDist))
__notePerStrum[note.strumID] = note;
}
}

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

__funcsToExec.clear();
__pressed.clear();
__justPressed.clear();
__justReleased.clear();

for(s in members) {
__pressed.push(s.__getPressed(this));
__justPressed.push(s.__getJustPressed(this));
__justReleased.push(s.__getJustReleased(this));
__pressed.resize(members.length);
__justPressed.resize(members.length);
__justReleased.resize(members.length);

for(i in 0...members.length) {
__pressed[i] = members[i].__getPressed(this);
__justPressed[i] = members[i].__getJustPressed(this);
__justReleased[i] = members[i].__getJustReleased(this);
}

var event = EventManager.get(InputSystemEvent).recycle(__pressed, __justPressed, __justReleased, this, id);
Expand Down Expand Up @@ -411,6 +438,8 @@ class StrumLine extends FlxTypedGroup<Strum> {
var event:SimpleNoteEvent = EventManager.get(SimpleNoteEvent).recycle(note);
onNoteDelete.dispatch(event);
if (!event.cancelled) {
if (note.isSustainNote && note.sustainParent != null && note.sustainParent.tail > 0)
note.sustainParent.tail--;
note.kill();
notes.remove(note, true);
note.destroy();
Expand Down
76 changes: 76 additions & 0 deletions source/funkin/game/scoring/HitWindowData.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package funkin.game.scoring;

import haxe.ds.StringMap;

class HitWindowData
{
public static function getWindows(preset:WindowPreset):StringMap<Float>
{
var map = new StringMap<Float>();

switch (preset) {
// Old Codename, really forgiving inputs (hard to get bad ratings)
case CNE_CLASSIC:
map.set("sick", 50.0);
map.set("good", 187.5);
map.set("bad", 225.0);
map.set("shit", 250.0);
// Week 7
case FNF_CLASSIC:
map.set("sick", 33.334);
map.set("good", 125.0025);
map.set("bad", 150.003);
map.set("shit", 166.67);
// V-Slice
case FNF_VSLICE:
map.set("sick", 45.0);
map.set("good", 90.0);
map.set("bad", 135.4);
map.set("shit", 180.0);
// Default, taken from Etterna
case _:
map.set("sick", 37.8);
map.set("good", 75.6);
map.set("bad", 113.4);
map.set("shit", 180.0);
}

return map;
}

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];
public static function scaleWindows(windows:StringMap<Float>, scale:Float):StringMap<Float>
{
var scaled = new StringMap<Float>();
for (k in windows.keys())
scaled.set(k, windows.get(k) * scale);
return scaled;
}

public static function offsetWindows(windows:StringMap<Float>, offset:Float):StringMap<Float>
{
var adjusted = new StringMap<Float>();
for (k in windows.keys())
adjusted.set(k, windows.get(k) + offset);
return adjusted;
}
}

enum abstract WindowPreset(Int) from Int to Int
{
var DEFAULT = 0;
var CNE_CLASSIC = 1;
var FNF_CLASSIC = 2;
var FNF_VSLICE = 3;

public function toString():String
{
return switch (cast this : WindowPreset)
{
case CNE_CLASSIC: "Codename (Classic)";
case FNF_CLASSIC: "Funkin' (Week 7)";
case FNF_VSLICE: "Funkin' (V-Slice)";
case _: "Default";
}
}
}
Loading