From 74bd79fe77d9cacaedfb5be4bccf133dbdd3cb61 Mon Sep 17 00:00:00 2001 From: Yoshman29 <81191621+YoshiCrafter29@users.noreply.github.com> Date: Thu, 19 Jan 2023 14:58:17 +0100 Subject: [PATCH] strum system rework --- buildnumber.txt | 2 +- source/funkin/game/Note.hx | 21 +- source/funkin/game/PlayState.hx | 219 +++++++----------- source/funkin/game/StrumLine.hx | 103 ++++++++ .../scripting/events/NoteCreationEvent.hx | 5 + 5 files changed, 208 insertions(+), 142 deletions(-) create mode 100644 source/funkin/game/StrumLine.hx diff --git a/buildnumber.txt b/buildnumber.txt index 36a4e171..4a90f671 100644 --- a/buildnumber.txt +++ b/buildnumber.txt @@ -1 +1 @@ -2072 \ No newline at end of file +2078 \ No newline at end of file diff --git a/source/funkin/game/Note.hx b/source/funkin/game/Note.hx index 45ec63ae..39810168 100644 --- a/source/funkin/game/Note.hx +++ b/source/funkin/game/Note.hx @@ -18,7 +18,19 @@ class Note extends FlxSprite { public var strumTime:Float = 0; - public var mustPress:Bool = false; + public var mustPress(get, never):Bool; + public var player(get, never):StrumLine; + private function get_player() { + if (PlayState.instance != null) + return PlayState.instance.players[playerID]; + return null; + } + + private function get_mustPress():Bool { + if (PlayState.instance != null) + return PlayState.instance.players[playerID] != null && !PlayState.instance.players[playerID].cpu; + return playerID == 1; + } public var noteData:Int = 0; public var canBeHit:Bool = false; public var tooLate:Bool = false; @@ -60,6 +72,7 @@ class Note extends FlxSprite public var flipSustain:Bool = true; public var noteTypeID:Int = 0; + public var playerID:Int = 0; // TO APPLY THOSE ON A SINGLE NOTE public var scrollSpeed:Null = null; @@ -80,7 +93,7 @@ class Note extends FlxSprite public var animSuffix:String = ""; - public function new(strumTime:Float, noteData:Int, noteType:Int = 0, mustPress:Bool = true, ?prevNote:Note, ?sustainNote:Bool = false, animSuffix:String = "") + public function new(strumTime:Float, noteData:Int, noteType:Int = 0, playerID:Int = 1, ?prevNote:Note, ?sustainNote:Bool = false, animSuffix:String = "") { super(); @@ -88,7 +101,7 @@ class Note extends FlxSprite prevNote = this; this.prevNote = prevNote; - this.mustPress = mustPress; + this.playerID = playerID; this.noteTypeID = noteType; isSustainNote = sustainNote; @@ -100,7 +113,7 @@ class Note extends FlxSprite this.noteData = noteData; var customType = Paths.image('game/notes/${this.noteType}'); - var event = EventManager.get(NoteCreationEvent).recycle(this, strumID, this.noteType, noteTypeID, mustPress, Assets.exists(customType) ? 'game/notes/${this.noteType}' : 'game/NOTE_assets', 0.7, animSuffix); + var event = EventManager.get(NoteCreationEvent).recycle(this, strumID, this.noteType, noteTypeID, playerID, mustPress, Assets.exists(customType) ? 'game/notes/${this.noteType}' : 'game/NOTE_assets', 0.7, animSuffix); if (PlayState.instance != null) event = PlayState.instance.scripts.event("onNoteCreation", event); diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 4b13d061..6031e066 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -98,6 +98,11 @@ class PlayState extends MusicBeatState */ public var scripts:ScriptPack; + /** + * Array of all the players in the stage. + */ + public var players:Array = []; + /** * Game Over Song. (assets/music/gameOver.ogg) */ @@ -185,11 +190,11 @@ class PlayState extends MusicBeatState /** * Player strums. */ - public var playerStrums:FlxTypedGroup; + public var playerStrums:StrumLine; /** * CPU strums. */ - public var cpuStrums:FlxTypedGroup; + public var cpuStrums:StrumLine; /** * Note splashes container @@ -582,9 +587,10 @@ class PlayState extends MusicBeatState strumLine.scrollFactor.set(); strumLineNotes = new FlxTypedGroup(); - playerStrums = new FlxTypedGroup(); - cpuStrums = new FlxTypedGroup(); add(strumLineNotes); + + players.push(cpuStrums = new StrumLine([dad], 0.25, true, controls)); + players.push(playerStrums = new StrumLine([boyfriend], 0.75, false, controls)); splashHandler = new SplashHandler(); add(splashHandler); @@ -720,8 +726,7 @@ class PlayState extends MusicBeatState inCutscene = false; - generateStaticArrows(0); - generateStaticArrows(1); + generateStrums(); scripts.call("onStartCountdown"); startedCountdown = true; @@ -806,6 +811,8 @@ class PlayState extends MusicBeatState inst.play(); updateDiscordPresence(); + + scripts.call("onStartSong"); } public override function destroy() { @@ -820,6 +827,8 @@ class PlayState extends MusicBeatState } public function generateNotes(songData:SwagSong) { + // TODO: redo with proper chart parser & codename chart format + if (songData == null) return; if (songData.noteTypes == null) songData.noteTypes = []; @@ -858,7 +867,7 @@ class PlayState extends MusicBeatState else swagNote = null; - swagNote.nextNote = (swagNote = new Note(daStrumTime, daNoteData % 4, daNoteType, gottaHitNote, swagNote, false, section.altAnim ? "-alt" : "")); + swagNote.nextNote = (swagNote = new Note(daStrumTime, daNoteData % 4, daNoteType, gottaHitNote ? 1 : 0, swagNote, false, section.altAnim ? "-alt" : "")); swagNote.sustainLength = songNotes[2]; swagNote.scrollFactor.set(0, 0); swagNote.stepLength = stepCrochet; @@ -871,7 +880,7 @@ class PlayState extends MusicBeatState // create sustains for (susNote in 0...Math.floor(susLength)) { - swagNote = new Note(daStrumTime + (stepCrochet * susNote), daNoteData % 4, daNoteType, gottaHitNote, swagNote, true, section.altAnim ? "-alt" : ""); + swagNote = new Note(daStrumTime + (stepCrochet * susNote), daNoteData % 4, daNoteType, gottaHitNote ? 1 : 0, swagNote, true, section.altAnim ? "-alt" : ""); swagNote.scrollFactor.set(); swagNote.stepLength = stepCrochet; notes.add(swagNote); @@ -942,72 +951,10 @@ class PlayState extends MusicBeatState return FlxSort.byValues(FlxSort.ASCENDING, Obj1.strumTime, Obj2.strumTime); } - private function generateStaticArrows(player:Int):Void + private function generateStrums():Void { - for (i in 0...4) - { - // FlxG.log.add(i); - var babyArrow:Strum = new Strum((FlxG.width * 0.25) + (Note.swagWidth * (i - 2)) + ((FlxG.width / 2) * player), strumLine.y); - babyArrow.ID = i; - - var event = scripts.event("onStrumCreation", EventManager.get(StrumCreationEvent).recycle(babyArrow, player, i)); - - if (!event.cancelled) { - switch (curStage) - { - // case "school": - default: - babyArrow.frames = Paths.getFrames(event.sprite); - babyArrow.animation.addByPrefix('green', 'arrowUP'); - babyArrow.animation.addByPrefix('blue', 'arrowDOWN'); - babyArrow.animation.addByPrefix('purple', 'arrowLEFT'); - babyArrow.animation.addByPrefix('red', 'arrowRIGHT'); - - babyArrow.antialiasing = true; - babyArrow.setGraphicSize(Std.int(babyArrow.width * 0.7)); - - switch (babyArrow.ID % 4) - { - case 0: - babyArrow.animation.addByPrefix('static', 'arrowLEFT'); - babyArrow.animation.addByPrefix('pressed', 'left press', 24, false); - babyArrow.animation.addByPrefix('confirm', 'left confirm', 24, false); - case 1: - babyArrow.animation.addByPrefix('static', 'arrowDOWN'); - babyArrow.animation.addByPrefix('pressed', 'down press', 24, false); - babyArrow.animation.addByPrefix('confirm', 'down confirm', 24, false); - case 2: - babyArrow.animation.addByPrefix('static', 'arrowUP'); - babyArrow.animation.addByPrefix('pressed', 'up press', 24, false); - babyArrow.animation.addByPrefix('confirm', 'up confirm', 24, false); - case 3: - babyArrow.animation.addByPrefix('static', 'arrowRIGHT'); - babyArrow.animation.addByPrefix('pressed', 'right press', 24, false); - babyArrow.animation.addByPrefix('confirm', 'right confirm', 24, false); - } - } - } - - babyArrow.updateHitbox(); - babyArrow.scrollFactor.set(); - - if (event.__doAnimation) - { - babyArrow.y -= 10; - babyArrow.alpha = 0; - FlxTween.tween(babyArrow, {y: babyArrow.y + 10, alpha: 1}, 1, {ease: FlxEase.circOut, startDelay: 0.5 + (0.2 * i)}); - } - - - if (event.player == 1) - playerStrums.add(babyArrow); - else { - babyArrow.cpu = true; - cpuStrums.add(babyArrow); - } - babyArrow.animation.play('static'); - strumLineNotes.add(babyArrow); - } + for(p in players) + p.generateStrums(); } override function openSubState(SubState:FlxSubState) @@ -1273,7 +1220,7 @@ class PlayState extends MusicBeatState var __updateNote_event:NoteUpdateEvent = null; function updateNote(daNote:Note) { - for(e in (daNote.mustPress ? playerStrums : cpuStrums).members) { + for(e in players[daNote.playerID].members) { if (e.ID == daNote.noteData % 4) { __updateNote_strum = e; break; //ing bad @@ -1283,19 +1230,14 @@ class PlayState extends MusicBeatState var event = PlayState.instance.scripts.event("onNoteUpdate", __updateNote_event.recycle(daNote, FlxG.elapsed, __updateNote_strum)); if (!event.cancelled) { if (event.__updateHitWindow) { - if (daNote.mustPress) - { - daNote.canBeHit = (daNote.strumTime > Conductor.songPosition - (hitWindow * daNote.latePressWindow) - && daNote.strumTime < Conductor.songPosition + (hitWindow * daNote.earlyPressWindow)); + daNote.canBeHit = (daNote.strumTime > Conductor.songPosition - (hitWindow * daNote.latePressWindow) + && daNote.strumTime < Conductor.songPosition + (hitWindow * daNote.earlyPressWindow)); - if (daNote.strumTime < Conductor.songPosition - hitWindow && !daNote.wasGoodHit) - daNote.tooLate = true; - } - else - daNote.canBeHit = false; + if (daNote.strumTime < Conductor.songPosition - hitWindow && !daNote.wasGoodHit) + daNote.tooLate = true; } - if (event.__autoCPUHit && !daNote.mustPress && !daNote.wasGoodHit && daNote.strumTime < Conductor.songPosition) goodNoteHit(daNote); + if (event.__autoCPUHit && (daNote.player == null || daNote.player.cpu) && !daNote.wasGoodHit && daNote.strumTime < Conductor.songPosition) goodNoteHit(daNote); if (daNote.wasGoodHit && daNote.isSustainNote && daNote.strumTime + (daNote.stepLength) < Conductor.songPosition) { deleteNote(daNote); @@ -1411,54 +1353,60 @@ class PlayState extends MusicBeatState var __justReleased:Array = []; private function keyShit():Void { - __funcsToExec.clear(); - __pressed.clear(); - __justPressed.clear(); - __justReleased.clear(); + for(id=>p in players) { + if (p.cpu) continue; - __pressed.pushGroup(controls.NOTE_LEFT, controls.NOTE_DOWN, controls.NOTE_UP, controls.NOTE_RIGHT); - __justPressed.pushGroup(controls.NOTE_LEFT_P, controls.NOTE_DOWN_P, controls.NOTE_UP_P, controls.NOTE_RIGHT_P); - __justReleased.pushGroup(controls.NOTE_LEFT_R, controls.NOTE_DOWN_R, controls.NOTE_UP_R, controls.NOTE_RIGHT_R); + __funcsToExec.clear(); + __pressed.clear(); + __justPressed.clear(); + __justReleased.clear(); - var event = scripts.event("onKeyShit", EventManager.get(InputSystemEvent).recycle(__pressed, __justPressed, __justReleased)); - if (event.cancelled) return; - - __pressed = CoolUtil.getDefault(event.pressed, []); - __justPressed = CoolUtil.getDefault(event.justPressed, []); - __justReleased = CoolUtil.getDefault(event.justReleased, []); - - - if (__pressed.contains(true)) { - __funcsToExec.push(function(note:Note) { - if (__pressed[note.strumID] && note.isSustainNote && note.canBeHit && note.mustPress && !note.wasGoodHit) { - goodNoteHit(note); - } + // TODO: player identifier in event + + __pressed.pushGroup(p.controls.NOTE_LEFT, p.controls.NOTE_DOWN, p.controls.NOTE_UP, p.controls.NOTE_RIGHT); + __justPressed.pushGroup(p.controls.NOTE_LEFT_P, p.controls.NOTE_DOWN_P, p.controls.NOTE_UP_P, p.controls.NOTE_RIGHT_P); + __justReleased.pushGroup(p.controls.NOTE_LEFT_R, p.controls.NOTE_DOWN_R, p.controls.NOTE_UP_R, p.controls.NOTE_RIGHT_R); + + var event = scripts.event("onKeyShit", EventManager.get(InputSystemEvent).recycle(__pressed, __justPressed, __justReleased)); + if (event.cancelled) return; + + __pressed = CoolUtil.getDefault(event.pressed, []); + __justPressed = CoolUtil.getDefault(event.justPressed, []); + __justReleased = CoolUtil.getDefault(event.justReleased, []); + + + if (__pressed.contains(true)) { + __funcsToExec.push(function(note:Note) { + if (note.playerID == id && __pressed[note.strumID] && note.isSustainNote && note.canBeHit && !note.wasGoodHit) { + goodNoteHit(note); + } + }); + } + + var notePerStrum = [for(i in 0...4) null]; + if (__justPressed.contains(true)) { + __funcsToExec.push(function(note:Note) { + if (__justPressed[note.strumID] && !note.isSustainNote && note.playerID == id && !note.wasGoodHit && note.canBeHit) { + if (notePerStrum[note.strumID] == null) notePerStrum[note.strumID] = note; + else if (Math.abs(notePerStrum[note.strumID].strumTime - note.strumTime) <= 5) deleteNote(note); + else if (note.strumTime < notePerStrum[note.strumID].strumTime) notePerStrum[note.strumID] = note; + } + }); + } + + if (__funcsToExec.length > 0) { + notes.forEachAlive(function(note:Note) { + for(e in __funcsToExec) e(note); + }); + } + + for(e in notePerStrum) if (e != null) goodNoteHit(e); + + p.forEach(function(str:Strum) { + str.updatePlayerInput(__pressed[str.ID], __justPressed[str.ID], __justReleased[str.ID]); }); + scripts.call("onPostKeyShit"); } - - var notePerStrum = [for(i in 0...4) null]; - if (__justPressed.contains(true)) { - __funcsToExec.push(function(note:Note) { - if (__justPressed[note.strumID] && !note.isSustainNote && note.mustPress && !note.wasGoodHit && note.canBeHit) { - if (notePerStrum[note.strumID] == null) notePerStrum[note.strumID] = note; - else if (Math.abs(notePerStrum[note.strumID].strumTime - note.strumTime) <= 5) deleteNote(note); - else if (note.strumTime < notePerStrum[note.strumID].strumTime) notePerStrum[note.strumID] = note; - } - }); - } - - if (__funcsToExec.length > 0) { - notes.forEachAlive(function(note:Note) { - for(e in __funcsToExec) e(note); - }); - } - - for(e in notePerStrum) if (e != null) goodNoteHit(e); - - playerStrums.forEach(function(str:Strum) { - str.updatePlayerInput(__pressed[str.ID], __justPressed[str.ID], __justReleased[str.ID]); - }); - scripts.call("onPostKeyShit"); } function noteMiss(note:Note):Void @@ -1537,10 +1485,11 @@ class PlayState extends MusicBeatState } var event:NoteHitEvent; - if (note.mustPress) - event = scripts.event("onPlayerHit", EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, note, [boyfriend], true, note.noteType, "", "game/score/", "", note.strumID, score, note.isSustainNote ? null : accuracy, note.noteData > 0 ? 0.023 : 0.004, daRating, !note.isSustainNote && daRating == "sick")); + if (note.player != null && !note.player.cpu) + event = scripts.event("onPlayerHit", EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, note, note.player.characters, true, note.noteType, "", "game/score/", "", note.strumID, score, note.isSustainNote ? null : accuracy, note.noteData > 0 ? 0.023 : 0.004, daRating, !note.isSustainNote && daRating == "sick")); else - event = scripts.event("onDadHit", EventManager.get(NoteHitEvent).recycle(false, false, false, note, [dad], false, note.noteType, "", "game/score/", "", note.strumID, 0, null, 0, daRating, false)); + event = scripts.event("onDadHit", EventManager.get(NoteHitEvent).recycle(false, false, false, note, note.player.characters, false, note.noteType, "", "game/score/", "", note.strumID, 0, null, 0, daRating, false)); + scripts.event("onNoteHit", event); if (!event.cancelled) { if (event.accuracy != null) { @@ -1634,12 +1583,8 @@ class PlayState extends MusicBeatState if (char != null) char.playSingAnim(event.direction, event.animSuffix); - (event.player ? playerStrums : cpuStrums).forEach(function(str:Strum) { - if (str.ID == Math.abs(note.strumID)) { - if (!event.strumGlowCancelled) str.press(note.strumTime); - if (event.showSplash) splashHandler.showSplash(note.splash, str); - } - }); + if (!event.strumGlowCancelled) note.__strum.press(note.strumTime); + if (event.showSplash) splashHandler.showSplash(note.splash, note.__strum); } if (event.unmuteVocals) vocals.volume = 1; diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx new file mode 100644 index 00000000..12b560b8 --- /dev/null +++ b/source/funkin/game/StrumLine.hx @@ -0,0 +1,103 @@ +package funkin.game; + +import funkin.system.Controls; +import funkin.scripting.events.StrumCreationEvent; +import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; +import flixel.FlxG; + +class StrumLine extends FlxTypedGroup { + /** + * Array containing all of the characters "attached" to those strums. + */ + public var characters:Array; + /** + * Whenever this strumline is controlled by cpu or not. + */ + public var cpu(default, set):Bool = false; + /** + * Controls assigned to this strumline. + */ + public var controls:Controls = null; + + private var strumOffset:Float = 0.25; + + public function new(characters:Array, strumOffset:Float = 0.25, cpu:Bool = false, ?controls:Controls) { + super(); + this.characters = characters; + this.strumOffset = strumOffset; + this.cpu = cpu; + this.controls = controls; + } + + + public function generateStrums(amount:Int = 4) { + for (i in 0...4) + { + var babyArrow:Strum = new Strum((FlxG.width * strumOffset) + (Note.swagWidth * (i - 2)), PlayState.instance.strumLine.y); + babyArrow.ID = i; + + var event = PlayState.instance.scripts.event("onStrumCreation", EventManager.get(StrumCreationEvent).recycle(babyArrow, PlayState.instance.players.indexOf(this), i)); + + if (!event.cancelled) { + babyArrow.frames = Paths.getFrames(event.sprite); + babyArrow.animation.addByPrefix('green', 'arrowUP'); + babyArrow.animation.addByPrefix('blue', 'arrowDOWN'); + babyArrow.animation.addByPrefix('purple', 'arrowLEFT'); + babyArrow.animation.addByPrefix('red', 'arrowRIGHT'); + + babyArrow.antialiasing = true; + babyArrow.setGraphicSize(Std.int(babyArrow.width * 0.7)); + + switch (babyArrow.ID % 4) + { + case 0: + babyArrow.animation.addByPrefix('static', 'arrowLEFT'); + babyArrow.animation.addByPrefix('pressed', 'left press', 24, false); + babyArrow.animation.addByPrefix('confirm', 'left confirm', 24, false); + case 1: + babyArrow.animation.addByPrefix('static', 'arrowDOWN'); + babyArrow.animation.addByPrefix('pressed', 'down press', 24, false); + babyArrow.animation.addByPrefix('confirm', 'down confirm', 24, false); + case 2: + babyArrow.animation.addByPrefix('static', 'arrowUP'); + babyArrow.animation.addByPrefix('pressed', 'up press', 24, false); + babyArrow.animation.addByPrefix('confirm', 'up confirm', 24, false); + case 3: + babyArrow.animation.addByPrefix('static', 'arrowRIGHT'); + babyArrow.animation.addByPrefix('pressed', 'right press', 24, false); + babyArrow.animation.addByPrefix('confirm', 'right confirm', 24, false); + } + } + + babyArrow.cpu = cpu; + babyArrow.updateHitbox(); + babyArrow.scrollFactor.set(); + + if (event.__doAnimation) + { + babyArrow.y -= 10; + babyArrow.alpha = 0; + FlxTween.tween(babyArrow, {y: babyArrow.y + 10, alpha: 1}, 1, {ease: FlxEase.circOut, startDelay: 0.5 + (0.2 * i)}); + } + + add(babyArrow); + + babyArrow.playAnim('static'); + PlayState.instance.strumLineNotes.add(babyArrow); + } + } + + /** + * SETTERS & GETTERS + */ + #if REGION + private inline function set_cpu(b:Bool):Bool { + for(s in members) + if (s != null) + s.cpu = b; + return cpu = b; + } + #end +} \ No newline at end of file diff --git a/source/funkin/scripting/events/NoteCreationEvent.hx b/source/funkin/scripting/events/NoteCreationEvent.hx index 06095fff..d0298c62 100644 --- a/source/funkin/scripting/events/NoteCreationEvent.hx +++ b/source/funkin/scripting/events/NoteCreationEvent.hx @@ -23,6 +23,11 @@ class NoteCreationEvent extends CancellableEvent { */ public var noteTypeID:Int; + /** + * ID of the player. + */ + public var playerID:Int; + /** * Whenever the note will need to be hit by the player */