1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2024-12-27 15:37:49 +00:00

Merge branch 'rewrite/master' into feature/stage-solids

This commit is contained in:
Cameron Taylor 2024-01-17 21:40:26 -05:00
commit 96d02261c5
15 changed files with 206 additions and 78 deletions

View file

@ -11,7 +11,7 @@
"name": "flixel",
"type": "git",
"dir": null,
"ref": "a83738673e7edbf8acba3a1426af284dfe6719fe",
"ref": "07c6018008801972d12275690fc144fcc22e3de6",
"url": "https://github.com/FunkinCrew/flixel"
},
{
@ -37,7 +37,7 @@
"name": "flxanimate",
"type": "git",
"dir": null,
"ref": "d7c5621be742e2c98d523dfe5af7528835eaff1e",
"ref": "9bacdd6ea39f5e3a33b0f5dfb7bc583fe76060d4",
"url": "https://github.com/FunkinCrew/flxanimate"
},
{
@ -54,14 +54,14 @@
"name": "haxeui-core",
"type": "git",
"dir": null,
"ref": "2561076c5abeee0a60f3a2a65a8ecb7832a6a62a",
"ref": "bb904f8b4b205755a310c23ff25219f9dcd62711",
"url": "https://github.com/haxeui/haxeui-core"
},
{
"name": "haxeui-flixel",
"type": "git",
"dir": null,
"ref": "4f1842e55a410014dd4a188b576b31019631493a",
"ref": "1ec470c297afd7758a90dc9399aa1e3a4ea6ca0b",
"url": "https://github.com/haxeui/haxeui-flixel"
},
{

View file

@ -21,7 +21,6 @@ abstract Tallies(RawTallies)
bad: 0,
good: 0,
sick: 0,
killer: 0,
totalNotes: 0,
totalNotesHit: 0,
maxCombo: 0,
@ -43,7 +42,6 @@ typedef RawTallies =
var bad:Int;
var good:Int;
var sick:Int;
var killer:Int;
var maxCombo:Int;
var isNewHighscore:Bool;

View file

@ -100,6 +100,7 @@ class GameOverSubState extends MusicBeatSubState
// but it's normally opaque.
bg.alpha = transparent ? 0.25 : 1.0;
bg.scrollFactor.set();
bg.screenCenter();
add(bg);
// Pluck Boyfriend from the PlayState and place him (in the same position) in the GameOverSubState.
@ -221,6 +222,7 @@ class GameOverSubState extends MusicBeatSubState
playJeffQuote();
// Start music at lower volume
startDeathMusic(0.2, false);
boyfriend.playAnimation('deathLoop' + animationSuffix);
}
default:
// Start music at normal volume once the initial death animation finishes.

View file

@ -1796,6 +1796,7 @@ class PlayState extends MusicBeatSubState
{
if (note == null) continue;
// TODO: Does this properly account for offsets?
var hitWindowStart = note.strumTime - Constants.HIT_WINDOW_MS;
var hitWindowCenter = note.strumTime;
var hitWindowEnd = note.strumTime + Constants.HIT_WINDOW_MS;
@ -1864,14 +1865,30 @@ class PlayState extends MusicBeatSubState
}
}
// TODO: Potential penalty for dropping a hold note?
// if (holdNote.missedNote && !holdNote.handledMiss) { holdNote.handledMiss = true; }
if (holdNote.missedNote && !holdNote.handledMiss)
{
// When the opponent drops a hold note.
holdNote.handledMiss = true;
// We dropped a hold note.
// Mute vocals and play miss animation, but don't penalize.
vocals.opponentVolume = 0;
currentStage.getOpponent().playSingAnimation(holdNote.noteData.getDirection(), true);
}
}
// Process notes on the player's side.
for (note in playerStrumline.notes.members)
{
if (note == null || note.hasBeenHit) continue;
if (note == null) continue;
if (note.hasBeenHit)
{
note.tooEarly = false;
note.mayHit = false;
note.hasMissed = false;
continue;
}
var hitWindowStart = note.strumTime - Constants.HIT_WINDOW_MS;
var hitWindowCenter = note.strumTime;
@ -1934,8 +1951,15 @@ class PlayState extends MusicBeatSubState
songScore += Std.int(Constants.SCORE_HOLD_BONUS_PER_SECOND * elapsed);
}
// TODO: Potential penalty for dropping a hold note?
// if (holdNote.missedNote && !holdNote.handledMiss) { holdNote.handledMiss = true; }
if (holdNote.missedNote && !holdNote.handledMiss)
{
// The player dropped a hold note.
holdNote.handledMiss = true;
// Mute vocals and play miss animation, but don't penalize.
vocals.playerVolume = 0;
currentStage.getBoyfriend().playSingAnimation(holdNote.noteData.getDirection(), true);
}
}
}
@ -2027,8 +2051,6 @@ class PlayState extends MusicBeatSubState
trace('Hit note! ${targetNote.noteData}');
goodNoteHit(targetNote, input);
targetNote.visible = false;
targetNote.kill();
notesInDirection.remove(targetNote);
// Play the strumline animation.
@ -2060,15 +2082,8 @@ class PlayState extends MusicBeatSubState
// Calling event.cancelEvent() skips all the other logic! Neat!
if (event.eventCanceled) return;
Highscore.tallies.combo++;
Highscore.tallies.totalNotesHit++;
if (Highscore.tallies.combo > Highscore.tallies.maxCombo) Highscore.tallies.maxCombo = Highscore.tallies.combo;
popUpScore(note, input);
playerStrumline.hitNote(note);
if (note.isHoldNote && note.holdNoteSprite != null)
{
playerStrumline.playNoteHoldCover(note.holdNoteSprite);
@ -2084,8 +2099,6 @@ class PlayState extends MusicBeatSubState
function onNoteMiss(note:NoteSprite):Void
{
// a MISS is when you let a note scroll past you!!
Highscore.tallies.missed++;
var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, Highscore.tallies.combo, true);
dispatchEvent(event);
// Calling event.cancelEvent() skips all the other logic! Neat!
@ -2133,8 +2146,11 @@ class PlayState extends MusicBeatSubState
}
vocals.playerVolume = 0;
Highscore.tallies.missed++;
if (Highscore.tallies.combo != 0)
{
// Break the combo.
Highscore.tallies.combo = comboPopUps.displayCombo(0);
}
@ -2282,29 +2298,51 @@ class PlayState extends MusicBeatSubState
var score = Scoring.scoreNote(noteDiff, PBOT1);
var daRating = Scoring.judgeNote(noteDiff, PBOT1);
if (daRating == 'miss')
{
// If daRating is 'miss', that means we made a mistake and should not continue.
trace('[WARNING] popUpScore judged a note as a miss!');
// TODO: Remove this.
comboPopUps.displayRating('miss');
return;
}
var isComboBreak = false;
switch (daRating)
{
case 'killer':
Highscore.tallies.killer += 1;
health += Constants.HEALTH_KILLER_BONUS;
case 'sick':
Highscore.tallies.sick += 1;
health += Constants.HEALTH_SICK_BONUS;
isComboBreak = Constants.JUDGEMENT_SICK_COMBO_BREAK;
case 'good':
Highscore.tallies.good += 1;
health += Constants.HEALTH_GOOD_BONUS;
isComboBreak = Constants.JUDGEMENT_GOOD_COMBO_BREAK;
case 'bad':
Highscore.tallies.bad += 1;
health += Constants.HEALTH_BAD_BONUS;
isComboBreak = Constants.JUDGEMENT_BAD_COMBO_BREAK;
case 'shit':
Highscore.tallies.shit += 1;
health += Constants.HEALTH_SHIT_BONUS;
case 'miss':
Highscore.tallies.missed += 1;
health -= Constants.HEALTH_MISS_PENALTY;
isComboBreak = Constants.JUDGEMENT_SHIT_COMBO_BREAK;
}
if (daRating == "sick" || daRating == "killer")
if (isComboBreak)
{
// Break the combo, but don't increment tallies.misses.
Highscore.tallies.combo = comboPopUps.displayCombo(0);
}
else
{
Highscore.tallies.combo++;
Highscore.tallies.totalNotesHit++;
if (Highscore.tallies.combo > Highscore.tallies.maxCombo) Highscore.tallies.maxCombo = Highscore.tallies.combo;
}
playerStrumline.hitNote(daNote, !isComboBreak);
if (daRating == "sick")
{
playerStrumline.playNoteSplash(daNote.noteData.getDirection());
}
@ -2440,7 +2478,6 @@ class PlayState extends MusicBeatSubState
score: songScore,
tallies:
{
killer: Highscore.tallies.killer,
sick: Highscore.tallies.sick,
good: Highscore.tallies.good,
bad: Highscore.tallies.bad,
@ -2491,7 +2528,6 @@ class PlayState extends MusicBeatSubState
tallies:
{
// TODO: Sum up the values for the whole level!
killer: 0,
sick: 0,
good: 0,
bad: 0,

View file

@ -4,6 +4,7 @@ import funkin.data.song.SongData.SongNoteData;
import funkin.play.notes.notestyle.NoteStyle;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.FlxSprite;
import funkin.graphics.shaders.HSVShader;
class NoteSprite extends FlxSprite
{
@ -11,6 +12,8 @@ class NoteSprite extends FlxSprite
public var holdNoteSprite:SustainTrail;
var hsvShader:HSVShader;
/**
* The time at which the note should be hit, in milliseconds.
*/
@ -102,6 +105,8 @@ class NoteSprite extends FlxSprite
this.strumTime = strumTime;
this.direction = direction;
this.hsvShader = new HSVShader();
if (this.strumTime < 0) this.strumTime = 0;
setupNoteGraphic(noteStyle);
@ -116,16 +121,57 @@ class NoteSprite extends FlxSprite
setGraphicSize(Strumline.STRUMLINE_SIZE);
updateHitbox();
this.shader = hsvShader;
}
#if FLX_DEBUG
/**
* Call this to override how debug bounding boxes are drawn for this sprite.
*/
public override function drawDebugOnCamera(camera:flixel.FlxCamera):Void
{
if (!camera.visible || !camera.exists || !isOnScreen(camera)) return;
var gfx = beginDrawDebug(camera);
var rect = getBoundingBox(camera);
trace('note sprite bounding box: ' + rect.x + ', ' + rect.y + ', ' + rect.width + ', ' + rect.height);
gfx.lineStyle(2, 0xFFFF66FF, 0.5); // thickness, color, alpha
gfx.drawRect(rect.x, rect.y, rect.width, rect.height);
gfx.lineStyle(2, 0xFFFFFF66, 0.5); // thickness, color, alpha
gfx.drawRect(rect.x, rect.y + rect.height / 2, rect.width, 1);
endDrawDebug(camera);
}
#end
public function desaturate():Void
{
this.hsvShader.saturation = 0.2;
}
public function setHue(hue:Float):Void
{
this.hsvShader.hue = hue;
}
public override function revive():Void
{
super.revive();
this.visible = true;
this.alpha = 1.0;
this.active = false;
this.tooEarly = false;
this.hasBeenHit = false;
this.mayHit = false;
this.hasMissed = false;
this.hsvShader.hue = 1.0;
this.hsvShader.saturation = 1.0;
this.hsvShader.value = 1.0;
}
public override function kill():Void

View file

@ -316,7 +316,7 @@ class Strumline extends FlxSpriteGroup
// Update rendering of notes.
for (note in notes.members)
{
if (note == null || !note.alive || note.hasBeenHit) continue;
if (note == null || !note.alive) continue;
var vwoosh:Bool = note.holdNoteSprite == null;
// Set the note's position.
@ -343,7 +343,7 @@ class Strumline extends FlxSpriteGroup
playStatic(holdNote.noteDirection);
holdNote.missedNote = true;
holdNote.visible = true;
holdNote.alpha = 0.0;
holdNote.alpha = 0.0; // Completely hide the dropped hold note.
}
}
@ -384,10 +384,6 @@ class Strumline extends FlxSpriteGroup
var yOffset:Float = (holdNote.fullSustainLength - holdNote.sustainLength) * Constants.PIXELS_PER_MS;
trace('yOffset: ' + yOffset);
trace('holdNote.fullSustainLength: ' + holdNote.fullSustainLength);
trace('holdNote.sustainLength: ' + holdNote.sustainLength);
var vwoosh:Bool = false;
if (Preferences.downscroll)
@ -531,11 +527,24 @@ class Strumline extends FlxSpriteGroup
this.noteData.insertionSort(compareNoteData.bind(FlxSort.ASCENDING));
}
public function hitNote(note:NoteSprite):Void
/**
* @param note The note to hit.
* @param removeNote True to remove the note immediately, false to make it transparent and let it move offscreen.
*/
public function hitNote(note:NoteSprite, removeNote:Bool = true):Void
{
playConfirm(note.direction);
note.hasBeenHit = true;
killNote(note);
if (removeNote)
{
killNote(note);
}
else
{
note.alpha = 0.5;
note.desaturate();
}
if (note.holdNoteSprite != null)
{

View file

@ -47,6 +47,11 @@ class SustainTrail extends FlxSprite
*/
public var missedNote:Bool = false;
/**
* Set to `true` after handling additional logic for missing notes.
*/
public var handledMiss:Bool = false;
// maybe BlendMode.MULTIPLY if missed somehow, drawTriangles does not support!
/**
@ -321,6 +326,7 @@ class SustainTrail extends FlxSprite
hitNote = false;
missedNote = false;
handledMiss = false;
}
override public function destroy():Void

View file

@ -158,8 +158,8 @@ class Scoring
return switch (absTiming)
{
case(_ < PBOT1_KILLER_THRESHOLD) => true:
'killer';
// case(_ < PBOT1_KILLER_THRESHOLD) => true:
// 'killer';
case(_ < PBOT1_SICK_THRESHOLD) => true:
'sick';
case(_ < PBOT1_GOOD_THRESHOLD) => true:

View file

@ -792,7 +792,6 @@ typedef SaveScoreData =
typedef SaveScoreTallyData =
{
var killer:Int;
var sick:Int;
var good:Int;
var bad:Int;

View file

@ -120,7 +120,6 @@ class SaveDataMigrator
accuracy: inputSaveData.songCompletion.get('${levelId}-easy') ?? 0.0,
tallies:
{
killer: 0,
sick: 0,
good: 0,
bad: 0,
@ -140,7 +139,6 @@ class SaveDataMigrator
accuracy: inputSaveData.songCompletion.get('${levelId}') ?? 0.0,
tallies:
{
killer: 0,
sick: 0,
good: 0,
bad: 0,
@ -160,7 +158,6 @@ class SaveDataMigrator
accuracy: inputSaveData.songCompletion.get('${levelId}-hard') ?? 0.0,
tallies:
{
killer: 0,
sick: 0,
good: 0,
bad: 0,
@ -183,7 +180,6 @@ class SaveDataMigrator
accuracy: 0,
tallies:
{
killer: 0,
sick: 0,
good: 0,
bad: 0,
@ -209,7 +205,6 @@ class SaveDataMigrator
accuracy: 0,
tallies:
{
killer: 0,
sick: 0,
good: 0,
bad: 0,
@ -235,7 +230,6 @@ class SaveDataMigrator
accuracy: 0,
tallies:
{
killer: 0,
sick: 0,
good: 0,
bad: 0,

View file

@ -25,6 +25,10 @@ class ChartEditorEventContextMenu extends ChartEditorBaseContextMenu
function initialize()
{
contextmenuEdit.onClick = function(_) {
chartEditorState.showToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_EVENT_DATA_LAYOUT);
}
contextmenuDelete.onClick = function(_) {
chartEditorState.performCommand(new RemoveEventsCommand([data]));
}

View file

@ -180,9 +180,9 @@ class Level implements IRegistryEntry<LevelData>
return difficulties;
}
public function buildProps():Array<LevelProp>
public function buildProps(?existingProps:Array<LevelProp>):Array<LevelProp>
{
var props:Array<LevelProp> = [];
var props:Array<LevelProp> = existingProps == null ? [] : [for (x in existingProps) x];
if (_data.props.length == 0) return props;
@ -190,11 +190,22 @@ class Level implements IRegistryEntry<LevelData>
{
var propData = _data.props[propIndex];
var propSprite:Null<LevelProp> = LevelProp.build(propData);
if (propSprite == null) continue;
// Attempt to reuse the `LevelProp` object.
// This prevents animations from resetting.
var existingProp:Null<LevelProp> = props[propIndex];
if (existingProp != null)
{
existingProp.propData = propData;
existingProp.x = propData.offsets[0] + FlxG.width * 0.25 * propIndex;
}
else
{
var propSprite:Null<LevelProp> = LevelProp.build(propData);
if (propSprite == null) continue;
propSprite.x += FlxG.width * 0.25 * propIndex;
props.push(propSprite);
propSprite.x = propData.offsets[0] + FlxG.width * 0.25 * propIndex;
props.push(propSprite);
}
}
return props;

View file

@ -6,9 +6,26 @@ import funkin.data.level.LevelData;
class LevelProp extends Bopper
{
public function new(danceEvery:Int)
public var propData(default, set):Null<LevelPropData> = null;
function set_propData(value:LevelPropData):LevelPropData
{
super(danceEvery);
// Only reset the prop if the asset path has changed.
if (propData == null || value.assetPath != this.propData.assetPath)
{
this.visible = (value != null);
this.propData = value;
danceEvery = this.propData.danceEvery;
applyData();
}
return this.propData;
}
public function new(propData:LevelPropData)
{
super(propData.danceEvery);
this.propData = propData;
}
public function playConfirm():Void
@ -16,50 +33,51 @@ class LevelProp extends Bopper
playAnimation('confirm', true, true);
}
public static function build(propData:Null<LevelPropData>):Null<LevelProp>
function applyData():Void
{
if (propData == null) return null;
var isAnimated:Bool = propData.animations.length > 0;
var prop:LevelProp = new LevelProp(propData.danceEvery);
if (isAnimated)
{
// Initalize sprite frames.
// Sparrow atlas only LEL.
prop.frames = Paths.getSparrowAtlas(propData.assetPath);
this.frames = Paths.getSparrowAtlas(propData.assetPath);
}
else
{
// Initalize static sprite.
prop.loadGraphic(Paths.image(propData.assetPath));
this.loadGraphic(Paths.image(propData.assetPath));
// Disables calls to update() for a performance boost.
prop.active = false;
this.active = false;
}
if (prop.frames == null || prop.frames.numFrames == 0)
if (this.frames == null || this.frames.numFrames == 0)
{
trace('ERROR: Could not build texture for level prop (${propData.assetPath}).');
return null;
return;
}
var scale:Float = propData.scale * (propData.isPixel ? 6 : 1);
prop.scale.set(scale, scale);
prop.antialiasing = !propData.isPixel;
prop.alpha = propData.alpha;
prop.x = propData.offsets[0];
prop.y = propData.offsets[1];
this.scale.set(scale, scale);
this.antialiasing = !propData.isPixel;
this.alpha = propData.alpha;
this.x = propData.offsets[0];
this.y = propData.offsets[1];
FlxAnimationUtil.addAtlasAnimations(prop, propData.animations);
FlxAnimationUtil.addAtlasAnimations(this, propData.animations);
for (propAnim in propData.animations)
{
prop.setAnimationOffsets(propAnim.name, propAnim.offsets[0], propAnim.offsets[1]);
this.setAnimationOffsets(propAnim.name, propAnim.offsets[0], propAnim.offsets[1]);
}
prop.dance();
prop.animation.paused = true;
this.dance();
this.animation.paused = true;
}
return prop;
public static function build(propData:Null<LevelPropData>):Null<LevelProp>
{
if (propData == null) return null;
return new LevelProp(propData);
}
}

View file

@ -637,8 +637,7 @@ class StoryMenuState extends MusicBeatState
function updateProps():Void
{
levelProps.clear();
for (prop in currentLevel.buildProps())
for (prop in currentLevel.buildProps(levelProps.members))
{
prop.zIndex = 1000;
levelProps.add(prop);

View file

@ -363,6 +363,12 @@ class Constants
*/
public static final SCORE_HOLD_BONUS_PER_SECOND:Float = 250.0;
public static final JUDGEMENT_KILLER_COMBO_BREAK:Bool = false;
public static final JUDGEMENT_SICK_COMBO_BREAK:Bool = false;
public static final JUDGEMENT_GOOD_COMBO_BREAK:Bool = false;
public static final JUDGEMENT_BAD_COMBO_BREAK:Bool = true;
public static final JUDGEMENT_SHIT_COMBO_BREAK:Bool = true;
/**
* FILE EXTENSIONS
*/