Implement a botplay checkbox in the chart editor

This commit is contained in:
EliteMasterEric 2024-03-05 22:27:07 -05:00
parent 67788d762d
commit 1b1834e98b
6 changed files with 88 additions and 12 deletions

2
assets

@ -1 +1 @@
Subproject commit 69ebdb6a7aa57b6762ce509243679ab959615120 Subproject commit 51b02f0d47e5b34bf8589065c092953c10c5040d

View file

@ -111,6 +111,11 @@ typedef PlayStateParams =
* @default `false` * @default `false`
*/ */
?practiceMode:Bool, ?practiceMode:Bool,
/**
* Whether the song should start in Bot Play Mode.
* @default `false`
*/
?botPlayMode:Bool,
/** /**
* Whether the song should be in minimal mode. * Whether the song should be in minimal mode.
* @default `false` * @default `false`
@ -282,6 +287,12 @@ class PlayState extends MusicBeatSubState
*/ */
public var isPracticeMode:Bool = false; public var isPracticeMode:Bool = false;
/**
* Whether the game is currently in Bot Play Mode.
* If true, player will not lose gain or lose score from notes.
*/
public var isBotPlayMode:Bool = false;
/** /**
* Whether the player has dropped below zero health, * Whether the player has dropped below zero health,
* and we are just waiting for an animation to play out before transitioning. * and we are just waiting for an animation to play out before transitioning.
@ -566,6 +577,7 @@ class PlayState extends MusicBeatSubState
if (params.targetDifficulty != null) currentDifficulty = params.targetDifficulty; if (params.targetDifficulty != null) currentDifficulty = params.targetDifficulty;
if (params.targetVariation != null) currentVariation = params.targetVariation; if (params.targetVariation != null) currentVariation = params.targetVariation;
isPracticeMode = params.practiceMode ?? false; isPracticeMode = params.practiceMode ?? false;
isBotPlayMode = params.botPlayMode ?? false;
isMinimalMode = params.minimalMode ?? false; isMinimalMode = params.minimalMode ?? false;
startTimestamp = params.startTimestamp ?? 0.0; startTimestamp = params.startTimestamp ?? 0.0;
playbackRate = params.playbackRate ?? 1.0; playbackRate = params.playbackRate ?? 1.0;
@ -1614,7 +1626,7 @@ class PlayState extends MusicBeatSubState
var noteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyleId); var noteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyleId);
if (noteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); if (noteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault();
playerStrumline = new Strumline(noteStyle, true); playerStrumline = new Strumline(noteStyle, !isBotPlayMode);
opponentStrumline = new Strumline(noteStyle, false); opponentStrumline = new Strumline(noteStyle, false);
add(playerStrumline); add(playerStrumline);
add(opponentStrumline); add(opponentStrumline);
@ -1876,16 +1888,30 @@ class PlayState extends MusicBeatSubState
function updateScoreText():Void function updateScoreText():Void
{ {
// TODO: Add functionality for modules to update the score text. // TODO: Add functionality for modules to update the score text.
if (isBotPlayMode)
{
scoreText.text = 'Bot Play Enabled';
}
else
{
scoreText.text = 'Score:' + songScore; scoreText.text = 'Score:' + songScore;
} }
}
/** /**
* Updates the values of the health bar. * Updates the values of the health bar.
*/ */
function updateHealthBar():Void function updateHealthBar():Void
{
if (isBotPlayMode)
{
healthLerp = Constants.HEALTH_MAX;
}
else
{ {
healthLerp = FlxMath.lerp(healthLerp, health, 0.15); healthLerp = FlxMath.lerp(healthLerp, health, 0.15);
} }
}
/** /**
* Callback executed when one of the note keys is pressed. * Callback executed when one of the note keys is pressed.
@ -1928,13 +1954,16 @@ class PlayState extends MusicBeatSubState
if (Conductor.instance.songPosition > hitWindowEnd) if (Conductor.instance.songPosition > hitWindowEnd)
{ {
if (note.hasMissed) continue; if (note.hasMissed || note.hasBeenHit) continue;
note.tooEarly = false; note.tooEarly = false;
note.mayHit = false; note.mayHit = false;
note.hasMissed = true; note.hasMissed = true;
if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = true; if (note.holdNoteSprite != null)
{
note.holdNoteSprite.missedNote = true;
}
} }
else if (Conductor.instance.songPosition > hitWindowCenter) else if (Conductor.instance.songPosition > hitWindowCenter)
{ {
@ -2021,10 +2050,38 @@ class PlayState extends MusicBeatSubState
if (Conductor.instance.songPosition > hitWindowEnd) if (Conductor.instance.songPosition > hitWindowEnd)
{ {
if (note.hasMissed || note.hasBeenHit) continue;
note.tooEarly = false; note.tooEarly = false;
note.mayHit = false; note.mayHit = false;
note.hasMissed = true; note.hasMissed = true;
if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = true; if (note.holdNoteSprite != null)
{
note.holdNoteSprite.missedNote = true;
}
}
else if (isBotPlayMode && Conductor.instance.songPosition > hitWindowCenter)
{
if (note.hasBeenHit) continue;
// We call onHitNote to play the proper animations,
// but not goodNoteHit! This means zero score and zero notes hit for the results screen!
// Call an event to allow canceling the note hit.
// NOTE: This is what handles the character animations!
var event:NoteScriptEvent = new HitNoteScriptEvent(note, 0.0, 0, 'perfect', 0);
dispatchEvent(event);
// Calling event.cancelEvent() skips all the other logic! Neat!
if (event.eventCanceled) continue;
// Command the bot to hit the note on time.
// NOTE: This is what handles the strumline and cleaning up the note itself!
playerStrumline.hitNote(note);
if (note.holdNoteSprite != null)
{
playerStrumline.playNoteHoldCover(note.holdNoteSprite);
}
} }
else if (Conductor.instance.songPosition > hitWindowStart) else if (Conductor.instance.songPosition > hitWindowStart)
{ {
@ -2069,7 +2126,7 @@ class PlayState extends MusicBeatSubState
if (holdNote == null || !holdNote.alive) continue; if (holdNote == null || !holdNote.alive) continue;
// While the hold note is being hit, and there is length on the hold note... // While the hold note is being hit, and there is length on the hold note...
if (holdNote.hitNote && !holdNote.missedNote && holdNote.sustainLength > 0) if (!isBotPlayMode && holdNote.hitNote && !holdNote.missedNote && holdNote.sustainLength > 0)
{ {
// Grant the player health. // Grant the player health.
health += Constants.HEALTH_HOLD_BONUS_PER_SECOND * elapsed; health += Constants.HEALTH_HOLD_BONUS_PER_SECOND * elapsed;

View file

@ -38,6 +38,10 @@ class Strumline extends FlxSpriteGroup
return FlxG.height / 0.45; return FlxG.height / 0.45;
} }
/**
* Whether this strumline is controlled by the player's inputs.
* False means it's controlled by the opponent or Bot Play.
*/
public var isPlayer:Bool; public var isPlayer:Bool;
/** /**

View file

@ -592,6 +592,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
*/ */
var playtestPracticeMode:Bool = false; var playtestPracticeMode:Bool = false;
/**
* If true, playtesting a chart will make the computer do it for you!
*/
var playtestBotPlayMode:Bool = false;
/** /**
* Enables or disables the "debugger" popup that appears when you run into a flixel error. * Enables or disables the "debugger" popup that appears when you run into a flixel error.
*/ */
@ -5359,6 +5364,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
targetDifficulty: selectedDifficulty, targetDifficulty: selectedDifficulty,
targetVariation: selectedVariation, targetVariation: selectedVariation,
practiceMode: playtestPracticeMode, practiceMode: playtestPracticeMode,
botPlayMode: playtestBotPlayMode,
minimalMode: minimal, minimalMode: minimal,
startTimestamp: startTimestamp, startTimestamp: startTimestamp,
playbackRate: playbackRate, playbackRate: playbackRate,

View file

@ -299,6 +299,15 @@ class ChartEditorToolboxHandler
state.playtestStartTime = checkboxStartTime.selected; state.playtestStartTime = checkboxStartTime.selected;
}; };
var checkboxBotPlay:Null<CheckBox> = toolbox.findComponent('playtestBotPlayCheckbox', CheckBox);
if (checkboxBotPlay == null) throw 'ChartEditorToolboxHandler.buildToolboxPlaytestPropertiesLayout() - Could not find playtestBotPlayCheckbox component.';
checkboxBotPlay.selected = state.playtestBotPlayMode;
checkboxBotPlay.onClick = _ -> {
state.playtestBotPlayMode = checkboxBotPlay.selected;
};
var checkboxDebugger:Null<CheckBox> = toolbox.findComponent('playtestDebuggerCheckbox', CheckBox); var checkboxDebugger:Null<CheckBox> = toolbox.findComponent('playtestDebuggerCheckbox', CheckBox);
if (checkboxDebugger == null) throw 'ChartEditorToolboxHandler.buildToolboxPlaytestPropertiesLayout() - Could not find playtestDebuggerCheckbox component.'; if (checkboxDebugger == null) throw 'ChartEditorToolboxHandler.buildToolboxPlaytestPropertiesLayout() - Could not find playtestDebuggerCheckbox component.';

View file

@ -1143,12 +1143,12 @@ class FreeplayState extends MusicBeatSubState
targetSong: targetSong, targetSong: targetSong,
targetDifficulty: targetDifficulty, targetDifficulty: targetDifficulty,
targetVariation: targetVariation, targetVariation: targetVariation,
// TODO: Make this an option!
// startTimestamp: 0.0,
// TODO: Make this an option!
// playbackRate: 0.5,
practiceMode: false, practiceMode: false,
minimalMode: false, minimalMode: false,
// TODO: Make these an option! It's currently only accessible via chart editor.
// startTimestamp: 0.0,
// playbackRate: 0.5,
// botPlayMode: true,
}, true); }, true);
}); });
} }