mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-27 01:55:52 -05:00
Merge branch 'master' into feature/offset-editor-tweaks
This commit is contained in:
commit
90bd5af376
59 changed files with 2482 additions and 4150 deletions
10
.github/actions/setup-haxeshit/action.yml
vendored
10
.github/actions/setup-haxeshit/action.yml
vendored
|
@ -10,11 +10,19 @@ runs:
|
||||||
run: |
|
run: |
|
||||||
haxelib config
|
haxelib config
|
||||||
shell: bash
|
shell: bash
|
||||||
|
- name: Restore/create existing haxelib cache for faster downloads
|
||||||
|
uses: actions/cache@v3
|
||||||
|
id: cache-haxelib-windows
|
||||||
|
with:
|
||||||
|
# wha?
|
||||||
|
key: cache-haxelib-${{ runner.os }}-${{ hashFiles('**/hmm.json')}}
|
||||||
|
path: |
|
||||||
|
.haxelib/
|
||||||
- name: Installing Haxe lol
|
- name: Installing Haxe lol
|
||||||
run: |
|
run: |
|
||||||
haxe -version
|
haxe -version
|
||||||
haxelib git haxelib https://github.com/HaxeFoundation/haxelib.git
|
haxelib git haxelib https://github.com/HaxeFoundation/haxelib.git
|
||||||
haxelib version
|
haxelib version
|
||||||
haxelib --global install hmm
|
haxelib --global install hmm
|
||||||
haxelib --global run hmm install --quiet
|
haxelib --global run hmm install
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
44
.github/workflows/build-shit.yml
vendored
44
.github/workflows/build-shit.yml
vendored
|
@ -2,7 +2,7 @@ name: build-upload
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check_date:
|
check_date:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -44,18 +44,6 @@ jobs:
|
||||||
actions: write
|
actions: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Restore existing build cache for faster compilation
|
|
||||||
uses: actions/cache/restore@v3
|
|
||||||
id: cache-windows-shit
|
|
||||||
with:
|
|
||||||
# wha?
|
|
||||||
key: cache-build-win
|
|
||||||
path: |
|
|
||||||
.haxelib/
|
|
||||||
export/debug/windows/haxe/
|
|
||||||
export/debug/windows/obj/
|
|
||||||
restore-keys: |
|
|
||||||
cache-build-windows
|
|
||||||
- uses: ./.github/actions/setup-haxeshit
|
- uses: ./.github/actions/setup-haxeshit
|
||||||
- name: Build game
|
- name: Build game
|
||||||
run: |
|
run: |
|
||||||
|
@ -66,33 +54,3 @@ jobs:
|
||||||
butler-key: ${{ secrets.BUTLER_API_KEY}}
|
butler-key: ${{ secrets.BUTLER_API_KEY}}
|
||||||
build-dir: export/debug/windows/bin
|
build-dir: export/debug/windows/bin
|
||||||
target: win
|
target: win
|
||||||
- name: Clearing already existing cache
|
|
||||||
uses: actions/github-script@v6
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const caches = await github.rest.actions.getActionsCacheList({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
})
|
|
||||||
for (const cache of caches.data.actions_caches) {
|
|
||||||
if (cache.key == "cache-build-windows") {
|
|
||||||
console.log('Clearing ' + cache.key + '...')
|
|
||||||
await github.rest.actions.deleteActionsCacheById({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
cache_id: cache.id,
|
|
||||||
})
|
|
||||||
console.log("Cache cleared.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
- name: Uploading new cache
|
|
||||||
uses: actions/cache/save@v3
|
|
||||||
with:
|
|
||||||
# caching again since for some reason it doesnt work with the first post cache shit
|
|
||||||
key: cache-build-windows
|
|
||||||
path: |
|
|
||||||
.haxelib/
|
|
||||||
export/debug/windows/haxe/
|
|
||||||
export/debug/windows/obj/
|
|
||||||
restore-keys: |
|
|
||||||
cache-build-windows
|
|
||||||
|
|
|
@ -105,7 +105,7 @@
|
||||||
<haxelib name="haxeui-flixel" /> <!-- Integrate HaxeUI with Flixel -->
|
<haxelib name="haxeui-flixel" /> <!-- Integrate HaxeUI with Flixel -->
|
||||||
<haxelib name="polymod" /> <!-- Modding framework -->
|
<haxelib name="polymod" /> <!-- Modding framework -->
|
||||||
<haxelib name="flxanimate" /> <!-- Texture atlas rendering -->
|
<haxelib name="flxanimate" /> <!-- Texture atlas rendering -->
|
||||||
<!-- <haxelib name="hxcodec" /> Video playback -->
|
<haxelib name="hxCodec" /> <!-- Video playback -->
|
||||||
<haxelib name="json2object" /> <!-- JSON parsing -->
|
<haxelib name="json2object" /> <!-- JSON parsing -->
|
||||||
<haxelib name="thx.semver" />
|
<haxelib name="thx.semver" />
|
||||||
<haxelib name="hxcpp-debug-server" if="desktop debug" />
|
<haxelib name="hxcpp-debug-server" if="desktop debug" />
|
||||||
|
|
2
hmm.json
2
hmm.json
|
@ -65,7 +65,7 @@
|
||||||
"version": "2.5.0"
|
"version": "2.5.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "hxcodec",
|
"name": "hxCodec",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "a56f4b4",
|
"ref": "a56f4b4",
|
||||||
|
|
|
@ -5,14 +5,14 @@ import flixel.group.FlxGroup.FlxTypedGroup;
|
||||||
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
||||||
import flixel.util.FlxTimer;
|
import flixel.util.FlxTimer;
|
||||||
|
|
||||||
class ComboCounter extends FlxTypedSpriteGroup<FlxSprite>
|
class ComboMilestone extends FlxTypedSpriteGroup<FlxSprite>
|
||||||
{
|
{
|
||||||
var effectStuff:FlxSprite;
|
var effectStuff:FlxSprite;
|
||||||
|
|
||||||
var wasComboSetup:Bool = false;
|
var wasComboSetup:Bool = false;
|
||||||
var daCombo:Int = 0;
|
var daCombo:Int = 0;
|
||||||
|
|
||||||
var grpNumbers:FlxTypedGroup<ComboNumber>;
|
var grpNumbers:FlxTypedGroup<ComboMilestoneNumber>;
|
||||||
|
|
||||||
var onScreenTime:Float = 0;
|
var onScreenTime:Float = 0;
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ class ComboCounter extends FlxTypedSpriteGroup<FlxSprite>
|
||||||
this.daCombo = daCombo;
|
this.daCombo = daCombo;
|
||||||
|
|
||||||
effectStuff = new FlxSprite(0, 0);
|
effectStuff = new FlxSprite(0, 0);
|
||||||
effectStuff.frames = Paths.getSparrowAtlas('noteCombo');
|
effectStuff.frames = Paths.getSparrowAtlas('comboMilestone');
|
||||||
effectStuff.animation.addByPrefix('funny', 'NOTE COMBO animation', 24, false);
|
effectStuff.animation.addByPrefix('funny', 'NOTE COMBO animation', 24, false);
|
||||||
effectStuff.animation.play('funny');
|
effectStuff.animation.play('funny');
|
||||||
effectStuff.antialiasing = true;
|
effectStuff.antialiasing = true;
|
||||||
|
@ -33,7 +33,7 @@ class ComboCounter extends FlxTypedSpriteGroup<FlxSprite>
|
||||||
effectStuff.setGraphicSize(Std.int(effectStuff.width * 0.7));
|
effectStuff.setGraphicSize(Std.int(effectStuff.width * 0.7));
|
||||||
add(effectStuff);
|
add(effectStuff);
|
||||||
|
|
||||||
grpNumbers = new FlxTypedGroup<ComboNumber>();
|
grpNumbers = new FlxTypedGroup<ComboMilestoneNumber>();
|
||||||
// add(grpNumbers);
|
// add(grpNumbers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ class ComboCounter extends FlxTypedSpriteGroup<FlxSprite>
|
||||||
{
|
{
|
||||||
if (onScreenTime < 0.9)
|
if (onScreenTime < 0.9)
|
||||||
{
|
{
|
||||||
new FlxTimer().start((Conductor.crochet / 1000) * 0.25, function(tmr) {
|
new FlxTimer().start((Conductor.beatLengthMs / 1000) * 0.25, function(tmr) {
|
||||||
forceFinish();
|
forceFinish();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -62,14 +62,14 @@ class ComboCounter extends FlxTypedSpriteGroup<FlxSprite>
|
||||||
|
|
||||||
if (effectStuff.animation.curAnim.curFrame == 18)
|
if (effectStuff.animation.curAnim.curFrame == 18)
|
||||||
{
|
{
|
||||||
grpNumbers.forEach(function(spr:ComboNumber) {
|
grpNumbers.forEach(function(spr:ComboMilestoneNumber) {
|
||||||
spr.animation.reset();
|
spr.animation.reset();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (effectStuff.animation.curAnim.curFrame == 20)
|
if (effectStuff.animation.curAnim.curFrame == 20)
|
||||||
{
|
{
|
||||||
grpNumbers.forEach(function(spr:ComboNumber) {
|
grpNumbers.forEach(function(spr:ComboMilestoneNumber) {
|
||||||
spr.kill();
|
spr.kill();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ class ComboCounter extends FlxTypedSpriteGroup<FlxSprite>
|
||||||
|
|
||||||
while (daCombo > 0)
|
while (daCombo > 0)
|
||||||
{
|
{
|
||||||
var comboNumber:ComboNumber = new ComboNumber(450 - (100 * loopNum), 20 + 14 * loopNum, daCombo % 10);
|
var comboNumber:ComboMilestoneNumber = new ComboMilestoneNumber(450 - (100 * loopNum), 20 + 14 * loopNum, daCombo % 10);
|
||||||
comboNumber.setGraphicSize(Std.int(comboNumber.width * 0.7));
|
comboNumber.setGraphicSize(Std.int(comboNumber.width * 0.7));
|
||||||
grpNumbers.add(comboNumber);
|
grpNumbers.add(comboNumber);
|
||||||
add(comboNumber);
|
add(comboNumber);
|
||||||
|
@ -95,27 +95,17 @@ class ComboCounter extends FlxTypedSpriteGroup<FlxSprite>
|
||||||
|
|
||||||
daCombo = Math.floor(daCombo / 10);
|
daCombo = Math.floor(daCombo / 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
// var comboNumber:ComboNumber = new ComboNumber(420, 0, 0);
|
|
||||||
|
|
||||||
// add to both, in the group just for ez organize/accessing
|
|
||||||
// grpNumbers.add(comboNumber);
|
|
||||||
// add(comboNumber);
|
|
||||||
|
|
||||||
// var comboNumber2:ComboNumber = new ComboNumber(420 - 134, 44, 0);
|
|
||||||
// grpNumbers.add(comboNumber2);
|
|
||||||
// add(comboNumber2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ComboNumber extends FlxSprite
|
class ComboMilestoneNumber extends FlxSprite
|
||||||
{
|
{
|
||||||
public function new(x:Float, y:Float, digit:Int)
|
public function new(x:Float, y:Float, digit:Int)
|
||||||
{
|
{
|
||||||
super(x - 20, y);
|
super(x - 20, y);
|
||||||
|
|
||||||
var stringNum:String = Std.string(digit);
|
var stringNum:String = Std.string(digit);
|
||||||
frames = Paths.getSparrowAtlas('noteComboNumbers');
|
frames = Paths.getSparrowAtlas('comboMilestoneNumbers');
|
||||||
animation.addByPrefix(stringNum, stringNum, 24, false);
|
animation.addByPrefix(stringNum, stringNum, 24, false);
|
||||||
animation.play(stringNum);
|
animation.play(stringNum);
|
||||||
antialiasing = true;
|
antialiasing = true;
|
|
@ -1,9 +1,8 @@
|
||||||
package funkin;
|
package funkin;
|
||||||
|
|
||||||
import flixel.util.FlxSignal;
|
|
||||||
import funkin.SongLoad.SwagSong;
|
|
||||||
import funkin.play.song.Song.SongDifficulty;
|
|
||||||
import funkin.play.song.SongData.SongTimeChange;
|
import funkin.play.song.SongData.SongTimeChange;
|
||||||
|
import flixel.util.FlxSignal;
|
||||||
|
import funkin.play.song.Song.SongDifficulty;
|
||||||
|
|
||||||
typedef BPMChangeEvent =
|
typedef BPMChangeEvent =
|
||||||
{
|
{
|
||||||
|
@ -12,18 +11,27 @@ typedef BPMChangeEvent =
|
||||||
var bpm:Float;
|
var bpm:Float;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A global source of truth for timing information.
|
||||||
|
*/
|
||||||
class Conductor
|
class Conductor
|
||||||
{
|
{
|
||||||
/**
|
static final STEPS_PER_BEAT:Int = 4;
|
||||||
* The list of time changes in the song.
|
|
||||||
* There should be at least one time change (at the beginning of the song) to define the BPM.
|
|
||||||
*/
|
|
||||||
static var timeChanges:Array<SongTimeChange> = [];
|
|
||||||
|
|
||||||
/**
|
// onBeatHit is called every quarter note
|
||||||
* The current time change.
|
// onStepHit is called every sixteenth note
|
||||||
*/
|
// 4/4 = 4 beats per measure = 16 steps per measure
|
||||||
static var currentTimeChange:SongTimeChange;
|
// 120 BPM = 120 quarter notes per minute = 2 onBeatHit per second
|
||||||
|
// 120 BPM = 480 sixteenth notes per minute = 8 onStepHit per second
|
||||||
|
// 60 BPM = 60 quarter notes per minute = 1 onBeatHit per second
|
||||||
|
// 60 BPM = 240 sixteenth notes per minute = 4 onStepHit per second
|
||||||
|
// 3/4 = 3 beats per measure = 12 steps per measure
|
||||||
|
// (IDENTICAL TO 4/4 but shorter measure length)
|
||||||
|
// 120 BPM = 120 quarter notes per minute = 2 onBeatHit per second
|
||||||
|
// 120 BPM = 480 sixteenth notes per minute = 8 onStepHit per second
|
||||||
|
// 60 BPM = 60 quarter notes per minute = 1 onBeatHit per second
|
||||||
|
// 60 BPM = 240 sixteenth notes per minute = 4 onStepHit per second
|
||||||
|
// 7/8 = 3.5 beats per measure = 14 steps per measure
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current position in the song in milliseconds.
|
* The current position in the song in milliseconds.
|
||||||
|
@ -47,29 +55,60 @@ class Conductor
|
||||||
|
|
||||||
static var bpmOverride:Null<Float> = null;
|
static var bpmOverride:Null<Float> = null;
|
||||||
|
|
||||||
// OLD, replaced with timeChanges.
|
/**
|
||||||
public static var bpmChangeMap:Array<BPMChangeEvent> = [];
|
* Current position in the song, in whole measures.
|
||||||
|
*/
|
||||||
|
public static var currentMeasure(default, null):Int;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Duration of a beat in millisecond. Calculated based on bpm.
|
* Current position in the song, in whole beats.
|
||||||
*/
|
**/
|
||||||
public static var crochet(get, null):Float;
|
public static var currentBeat(default, null):Int;
|
||||||
|
|
||||||
static function get_crochet():Float
|
/**
|
||||||
|
* Current position in the song, in whole steps.
|
||||||
|
*/
|
||||||
|
public static var currentStep(default, null):Int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current position in the song, in steps and fractions of a step.
|
||||||
|
*/
|
||||||
|
public static var currentStepTime(default, null):Float;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duration of a measure in milliseconds. Calculated based on bpm.
|
||||||
|
*/
|
||||||
|
public static var measureLengthMs(get, null):Float;
|
||||||
|
|
||||||
|
static function get_measureLengthMs():Float
|
||||||
{
|
{
|
||||||
|
return beatLengthMs * timeSignatureNumerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duration of a beat (quarter note) in milliseconds. Calculated based on bpm.
|
||||||
|
*/
|
||||||
|
public static var beatLengthMs(get, null):Float;
|
||||||
|
|
||||||
|
static function get_beatLengthMs():Float
|
||||||
|
{
|
||||||
|
// Tied directly to BPM.
|
||||||
return ((60 / bpm) * 1000);
|
return ((60 / bpm) * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Duration of a step (quarter) in milliseconds. Calculated based on bpm.
|
* Duration of a step (sixteenth) in milliseconds. Calculated based on bpm.
|
||||||
*/
|
*/
|
||||||
public static var stepCrochet(get, null):Float;
|
public static var stepLengthMs(get, null):Float;
|
||||||
|
|
||||||
static function get_stepCrochet():Float
|
static function get_stepLengthMs():Float
|
||||||
{
|
{
|
||||||
return crochet / timeSignatureNumerator;
|
return beatLengthMs / STEPS_PER_BEAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The numerator of the current time signature (number of notes in a measure)
|
||||||
|
*/
|
||||||
public static var timeSignatureNumerator(get, null):Int;
|
public static var timeSignatureNumerator(get, null):Int;
|
||||||
|
|
||||||
static function get_timeSignatureNumerator():Int
|
static function get_timeSignatureNumerator():Int
|
||||||
|
@ -79,6 +118,9 @@ class Conductor
|
||||||
return currentTimeChange.timeSignatureNum;
|
return currentTimeChange.timeSignatureNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The numerator of the current time signature (length of notes in a measure)
|
||||||
|
*/
|
||||||
public static var timeSignatureDenominator(get, null):Int;
|
public static var timeSignatureDenominator(get, null):Int;
|
||||||
|
|
||||||
static function get_timeSignatureDenominator():Int
|
static function get_timeSignatureDenominator():Int
|
||||||
|
@ -88,30 +130,57 @@ class Conductor
|
||||||
return currentTimeChange.timeSignatureDen;
|
return currentTimeChange.timeSignatureDen;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Current position in the song, in beats.
|
|
||||||
**/
|
|
||||||
public static var currentBeat(default, null):Int;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current position in the song, in steps.
|
|
||||||
*/
|
|
||||||
public static var currentStep(default, null):Int;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current position in the song, in steps and fractions of a step.
|
|
||||||
*/
|
|
||||||
public static var currentStepTime(default, null):Float;
|
|
||||||
|
|
||||||
public static var beatHit(default, null):FlxSignal = new FlxSignal();
|
|
||||||
public static var stepHit(default, null):FlxSignal = new FlxSignal();
|
|
||||||
|
|
||||||
public static var lastSongPos:Float;
|
|
||||||
public static var visualOffset:Float = 0;
|
|
||||||
public static var audioOffset:Float = 0;
|
|
||||||
public static var offset:Float = 0;
|
public static var offset:Float = 0;
|
||||||
|
|
||||||
// TODO: Add code to update this.
|
// TODO: What's the difference between visualOffset and audioOffset?
|
||||||
|
public static var visualOffset:Float = 0;
|
||||||
|
public static var audioOffset:Float = 0;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Signals
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal that is dispatched every measure.
|
||||||
|
* At 120 BPM 4/4, this is dispatched every 2 seconds.
|
||||||
|
* At 120 BPM 3/4, this is dispatched every 1.5 seconds.
|
||||||
|
*/
|
||||||
|
public static var measureHit(default, null):FlxSignal = new FlxSignal();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal that is dispatched every beat.
|
||||||
|
* At 120 BPM 4/4, this is dispatched every 0.5 seconds.
|
||||||
|
* At 120 BPM 3/4, this is dispatched every 0.5 seconds.
|
||||||
|
*/
|
||||||
|
public static var beatHit(default, null):FlxSignal = new FlxSignal();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal that is dispatched when a step is hit.
|
||||||
|
* At 120 BPM 4/4, this is dispatched every 0.125 seconds.
|
||||||
|
* At 120 BPM 3/4, this is dispatched every 0.125 seconds.
|
||||||
|
*/
|
||||||
|
public static var stepHit(default, null):FlxSignal = new FlxSignal();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Internal Variables
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of time changes in the song.
|
||||||
|
* There should be at least one time change (at the beginning of the song) to define the BPM.
|
||||||
|
*/
|
||||||
|
static var timeChanges:Array<SongTimeChange> = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current time change.
|
||||||
|
*/
|
||||||
|
static var currentTimeChange:SongTimeChange;
|
||||||
|
|
||||||
|
public static var lastSongPos:Float;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of beats (whole notes) in a measure.
|
||||||
|
*/
|
||||||
public static var beatsPerMeasure(get, null):Int;
|
public static var beatsPerMeasure(get, null):Int;
|
||||||
|
|
||||||
static function get_beatsPerMeasure():Int
|
static function get_beatsPerMeasure():Int
|
||||||
|
@ -119,33 +188,17 @@ class Conductor
|
||||||
return timeSignatureNumerator;
|
return timeSignatureNumerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of steps (quarter-notes) in a measure.
|
||||||
|
*/
|
||||||
public static var stepsPerMeasure(get, null):Int;
|
public static var stepsPerMeasure(get, null):Int;
|
||||||
|
|
||||||
static function get_stepsPerMeasure():Int
|
static function get_stepsPerMeasure():Int
|
||||||
{
|
{
|
||||||
// Is this always x4?
|
// This is always 4, b
|
||||||
return timeSignatureNumerator * 4;
|
return timeSignatureNumerator * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
function new() {}
|
|
||||||
|
|
||||||
public static function getLastBPMChange()
|
|
||||||
{
|
|
||||||
var lastChange:BPMChangeEvent =
|
|
||||||
{
|
|
||||||
stepTime: 0,
|
|
||||||
songTime: 0,
|
|
||||||
bpm: 0
|
|
||||||
}
|
|
||||||
for (i in 0...Conductor.bpmChangeMap.length)
|
|
||||||
{
|
|
||||||
if (Conductor.songPosition >= Conductor.bpmChangeMap[i].songTime) lastChange = Conductor.bpmChangeMap[i];
|
|
||||||
|
|
||||||
if (Conductor.songPosition < Conductor.bpmChangeMap[i].songTime) break;
|
|
||||||
}
|
|
||||||
return lastChange;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forcibly defines the current BPM of the song.
|
* Forcibly defines the current BPM of the song.
|
||||||
* Useful for things like the chart editor that need to manipulate BPM in real time.
|
* Useful for things like the chart editor that need to manipulate BPM in real time.
|
||||||
|
@ -155,11 +208,16 @@ class Conductor
|
||||||
* WARNING: Avoid this for things like setting the BPM of the title screen music,
|
* WARNING: Avoid this for things like setting the BPM of the title screen music,
|
||||||
* you should have a metadata file for it instead.
|
* you should have a metadata file for it instead.
|
||||||
*/
|
*/
|
||||||
public static function forceBPM(?bpm:Float = null)
|
public static function forceBPM(?bpm:Float = null):Void
|
||||||
{
|
{
|
||||||
if (bpm != null) trace('[CONDUCTOR] Forcing BPM to ' + bpm);
|
if (bpm != null)
|
||||||
|
{
|
||||||
|
trace('[CONDUCTOR] Forcing BPM to ' + bpm);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
trace('[CONDUCTOR] Resetting BPM to default');
|
trace('[CONDUCTOR] Resetting BPM to default');
|
||||||
|
}
|
||||||
Conductor.bpmOverride = bpm;
|
Conductor.bpmOverride = bpm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,15 +228,15 @@ class Conductor
|
||||||
* @param songPosition The current position in the song in milliseconds.
|
* @param songPosition The current position in the song in milliseconds.
|
||||||
* Leave blank to use the FlxG.sound.music position.
|
* Leave blank to use the FlxG.sound.music position.
|
||||||
*/
|
*/
|
||||||
public static function update(songPosition:Float = null)
|
public static function update(songPosition:Float = null):Void
|
||||||
{
|
{
|
||||||
if (songPosition == null) songPosition = (FlxG.sound.music != null) ? FlxG.sound.music.time + Conductor.offset : 0.0;
|
if (songPosition == null) songPosition = (FlxG.sound.music != null) ? FlxG.sound.music.time + Conductor.offset : 0.0;
|
||||||
|
|
||||||
var oldBeat = currentBeat;
|
var oldMeasure:Int = currentMeasure;
|
||||||
var oldStep = currentStep;
|
var oldBeat:Int = currentBeat;
|
||||||
|
var oldStep:Int = currentStep;
|
||||||
|
|
||||||
Conductor.songPosition = songPosition;
|
Conductor.songPosition = songPosition;
|
||||||
// Conductor.bpm = Conductor.getLastBPMChange().bpm;
|
|
||||||
|
|
||||||
currentTimeChange = timeChanges[0];
|
currentTimeChange = timeChanges[0];
|
||||||
for (i in 0...timeChanges.length)
|
for (i in 0...timeChanges.length)
|
||||||
|
@ -194,14 +252,14 @@ class Conductor
|
||||||
}
|
}
|
||||||
else if (currentTimeChange != null)
|
else if (currentTimeChange != null)
|
||||||
{
|
{
|
||||||
currentStepTime = (currentTimeChange.beatTime * 4) + (songPosition - currentTimeChange.timeStamp) / stepCrochet;
|
currentStepTime = (currentTimeChange.beatTime * 4) + (songPosition - currentTimeChange.timeStamp) / stepLengthMs;
|
||||||
currentStep = Math.floor(currentStepTime);
|
currentStep = Math.floor(currentStepTime);
|
||||||
currentBeat = Math.floor(currentStep / 4);
|
currentBeat = Math.floor(currentStep / 4);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Assume a constant BPM equal to the forced value.
|
// Assume a constant BPM equal to the forced value.
|
||||||
currentStepTime = (songPosition / stepCrochet);
|
currentStepTime = (songPosition / stepLengthMs);
|
||||||
currentStep = Math.floor(currentStepTime);
|
currentStep = Math.floor(currentStepTime);
|
||||||
currentBeat = Math.floor(currentStep / 4);
|
currentBeat = Math.floor(currentStep / 4);
|
||||||
}
|
}
|
||||||
|
@ -216,37 +274,14 @@ class Conductor
|
||||||
{
|
{
|
||||||
beatHit.dispatch();
|
beatHit.dispatch();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@:deprecated // Switch to TimeChanges instead.
|
if (currentMeasure != oldMeasure)
|
||||||
public static function mapBPMChanges(song:SwagSong)
|
|
||||||
{
|
|
||||||
bpmChangeMap = [];
|
|
||||||
|
|
||||||
var curBPM:Float = song.bpm;
|
|
||||||
var totalSteps:Int = 0;
|
|
||||||
var totalPos:Float = 0;
|
|
||||||
for (i in 0...SongLoad.getSong().length)
|
|
||||||
{
|
{
|
||||||
if (SongLoad.getSong()[i].changeBPM && SongLoad.getSong()[i].bpm != curBPM)
|
measureHit.dispatch();
|
||||||
{
|
|
||||||
curBPM = SongLoad.getSong()[i].bpm;
|
|
||||||
var event:BPMChangeEvent =
|
|
||||||
{
|
|
||||||
stepTime: totalSteps,
|
|
||||||
songTime: totalPos,
|
|
||||||
bpm: curBPM
|
|
||||||
};
|
|
||||||
bpmChangeMap.push(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
var deltaSteps:Int = SongLoad.getSong()[i].lengthInSteps;
|
|
||||||
totalSteps += deltaSteps;
|
|
||||||
totalPos += ((60 / curBPM) * 1000 / 4) * deltaSteps;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function mapTimeChanges(songTimeChanges:Array<SongTimeChange>)
|
public static function mapTimeChanges(songTimeChanges:Array<SongTimeChange>):Void
|
||||||
{
|
{
|
||||||
timeChanges = [];
|
timeChanges = [];
|
||||||
|
|
||||||
|
@ -268,7 +303,7 @@ class Conductor
|
||||||
if (timeChanges.length == 0)
|
if (timeChanges.length == 0)
|
||||||
{
|
{
|
||||||
// Assume a constant BPM equal to the forced value.
|
// Assume a constant BPM equal to the forced value.
|
||||||
return Math.floor(ms / stepCrochet);
|
return Math.floor(ms / stepLengthMs);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -289,7 +324,7 @@ class Conductor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resultStep += Math.floor((ms - lastTimeChange.timeStamp) / stepCrochet);
|
resultStep += Math.floor((ms - lastTimeChange.timeStamp) / stepLengthMs);
|
||||||
|
|
||||||
return resultStep;
|
return resultStep;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,12 +41,18 @@ enum Control
|
||||||
ACCEPT;
|
ACCEPT;
|
||||||
BACK;
|
BACK;
|
||||||
PAUSE;
|
PAUSE;
|
||||||
|
CUTSCENE_ADVANCE;
|
||||||
|
CUTSCENE_SKIP;
|
||||||
|
VOLUME_UP;
|
||||||
|
VOLUME_DOWN;
|
||||||
|
VOLUME_MUTE;
|
||||||
#if CAN_CHEAT
|
#if CAN_CHEAT
|
||||||
CHEAT;
|
CHEAT;
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
enum abstract Action(String) to String from String
|
@:enum
|
||||||
|
abstract Action(String) to String from String
|
||||||
{
|
{
|
||||||
var UI_UP = "ui_up";
|
var UI_UP = "ui_up";
|
||||||
var UI_LEFT = "ui_left";
|
var UI_LEFT = "ui_left";
|
||||||
|
@ -75,6 +81,11 @@ enum abstract Action(String) to String from String
|
||||||
var ACCEPT = "accept";
|
var ACCEPT = "accept";
|
||||||
var BACK = "back";
|
var BACK = "back";
|
||||||
var PAUSE = "pause";
|
var PAUSE = "pause";
|
||||||
|
var CUTSCENE_ADVANCE = "cutscene_advance";
|
||||||
|
var CUTSCENE_SKIP = "cutscene_skip";
|
||||||
|
var VOLUME_UP = "volume_up";
|
||||||
|
var VOLUME_DOWN = "volume_down";
|
||||||
|
var VOLUME_MUTE = "volume_mute";
|
||||||
var RESET = "reset";
|
var RESET = "reset";
|
||||||
#if CAN_CHEAT
|
#if CAN_CHEAT
|
||||||
var CHEAT = "cheat";
|
var CHEAT = "cheat";
|
||||||
|
@ -129,6 +140,11 @@ class Controls extends FlxActionSet
|
||||||
var _back = new FlxActionDigital(Action.BACK);
|
var _back = new FlxActionDigital(Action.BACK);
|
||||||
var _pause = new FlxActionDigital(Action.PAUSE);
|
var _pause = new FlxActionDigital(Action.PAUSE);
|
||||||
var _reset = new FlxActionDigital(Action.RESET);
|
var _reset = new FlxActionDigital(Action.RESET);
|
||||||
|
var _cutscene_advance = new FlxActionDigital(Action.CUTSCENE_ADVANCE);
|
||||||
|
var _cutscene_skip = new FlxActionDigital(Action.CUTSCENE_SKIP);
|
||||||
|
var _volume_up = new FlxActionDigital(Action.VOLUME_UP);
|
||||||
|
var _volume_down = new FlxActionDigital(Action.VOLUME_DOWN);
|
||||||
|
var _volume_mute = new FlxActionDigital(Action.VOLUME_MUTE);
|
||||||
#if CAN_CHEAT
|
#if CAN_CHEAT
|
||||||
var _cheat = new FlxActionDigital(Action.CHEAT);
|
var _cheat = new FlxActionDigital(Action.CHEAT);
|
||||||
#end
|
#end
|
||||||
|
@ -273,6 +289,31 @@ class Controls extends FlxActionSet
|
||||||
inline function get_PAUSE()
|
inline function get_PAUSE()
|
||||||
return _pause.check();
|
return _pause.check();
|
||||||
|
|
||||||
|
public var CUTSCENE_ADVANCE(get, never):Bool;
|
||||||
|
|
||||||
|
inline function get_CUTSCENE_ADVANCE()
|
||||||
|
return _cutscene_advance.check();
|
||||||
|
|
||||||
|
public var CUTSCENE_SKIP(get, never):Bool;
|
||||||
|
|
||||||
|
inline function get_CUTSCENE_SKIP()
|
||||||
|
return _cutscene_skip.check();
|
||||||
|
|
||||||
|
public var VOLUME_UP(get, never):Bool;
|
||||||
|
|
||||||
|
inline function get_VOLUME_UP()
|
||||||
|
return _volume_up.check();
|
||||||
|
|
||||||
|
public var VOLUME_DOWN(get, never):Bool;
|
||||||
|
|
||||||
|
inline function get_VOLUME_DOWN()
|
||||||
|
return _volume_down.check();
|
||||||
|
|
||||||
|
public var VOLUME_MUTE(get, never):Bool;
|
||||||
|
|
||||||
|
inline function get_VOLUME_MUTE()
|
||||||
|
return _volume_mute.check();
|
||||||
|
|
||||||
public var RESET(get, never):Bool;
|
public var RESET(get, never):Bool;
|
||||||
|
|
||||||
inline function get_RESET()
|
inline function get_RESET()
|
||||||
|
@ -316,6 +357,11 @@ class Controls extends FlxActionSet
|
||||||
add(_accept);
|
add(_accept);
|
||||||
add(_back);
|
add(_back);
|
||||||
add(_pause);
|
add(_pause);
|
||||||
|
add(_cutscene_advance);
|
||||||
|
add(_cutscene_skip);
|
||||||
|
add(_volume_up);
|
||||||
|
add(_volume_down);
|
||||||
|
add(_volume_mute);
|
||||||
add(_reset);
|
add(_reset);
|
||||||
#if CAN_CHEAT
|
#if CAN_CHEAT
|
||||||
add(_cheat);
|
add(_cheat);
|
||||||
|
@ -377,6 +423,11 @@ class Controls extends FlxActionSet
|
||||||
case BACK: _back;
|
case BACK: _back;
|
||||||
case PAUSE: _pause;
|
case PAUSE: _pause;
|
||||||
case RESET: _reset;
|
case RESET: _reset;
|
||||||
|
case CUTSCENE_ADVANCE: _cutscene_advance;
|
||||||
|
case CUTSCENE_SKIP: _cutscene_skip;
|
||||||
|
case VOLUME_UP: _volume_up;
|
||||||
|
case VOLUME_DOWN: _volume_down;
|
||||||
|
case VOLUME_MUTE: _volume_mute;
|
||||||
#if CAN_CHEAT
|
#if CAN_CHEAT
|
||||||
case CHEAT: _cheat;
|
case CHEAT: _cheat;
|
||||||
#end
|
#end
|
||||||
|
@ -437,6 +488,16 @@ class Controls extends FlxActionSet
|
||||||
func(_back, JUST_PRESSED);
|
func(_back, JUST_PRESSED);
|
||||||
case PAUSE:
|
case PAUSE:
|
||||||
func(_pause, JUST_PRESSED);
|
func(_pause, JUST_PRESSED);
|
||||||
|
case CUTSCENE_ADVANCE:
|
||||||
|
func(_cutscene_advance, JUST_PRESSED);
|
||||||
|
case CUTSCENE_SKIP:
|
||||||
|
func(_cutscene_skip, PRESSED);
|
||||||
|
case VOLUME_UP:
|
||||||
|
func(_volume_up, JUST_PRESSED);
|
||||||
|
case VOLUME_DOWN:
|
||||||
|
func(_volume_down, JUST_PRESSED);
|
||||||
|
case VOLUME_MUTE:
|
||||||
|
func(_volume_mute, JUST_PRESSED);
|
||||||
case RESET:
|
case RESET:
|
||||||
func(_reset, JUST_PRESSED);
|
func(_reset, JUST_PRESSED);
|
||||||
#if CAN_CHEAT
|
#if CAN_CHEAT
|
||||||
|
@ -454,37 +515,70 @@ class Controls extends FlxActionSet
|
||||||
switch(device)
|
switch(device)
|
||||||
{
|
{
|
||||||
case Keys:
|
case Keys:
|
||||||
forEachBound(control, function(action, _) replaceKey(action, toAdd, toRemove));
|
forEachBound(control, function(action, state) replaceKey(action, toAdd, toRemove, state));
|
||||||
|
|
||||||
case Gamepad(id):
|
case Gamepad(id):
|
||||||
forEachBound(control, function(action, _) replaceButton(action, id, toAdd, toRemove));
|
forEachBound(control, function(action, state) replaceButton(action, id, toAdd, toRemove, state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceKey(action:FlxActionDigital, toAdd:Int, toRemove:Int)
|
function replaceKey(action:FlxActionDigital, toAdd:FlxKey, toRemove:FlxKey, state:FlxInputState)
|
||||||
{
|
{
|
||||||
|
if (action.inputs.length == 0) {
|
||||||
|
// Add the keybind, don't replace.
|
||||||
|
addKeys(action, [toAdd], state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasReplaced:Bool = false;
|
||||||
for (i in 0...action.inputs.length)
|
for (i in 0...action.inputs.length)
|
||||||
{
|
{
|
||||||
var input = action.inputs[i];
|
var input = action.inputs[i];
|
||||||
|
if (input == null) continue;
|
||||||
|
|
||||||
if (input.device == KEYBOARD && input.inputID == toRemove)
|
if (input.device == KEYBOARD && input.inputID == toRemove)
|
||||||
{
|
{
|
||||||
@:privateAccess
|
if (toAdd == FlxKey.NONE) {
|
||||||
action.inputs[i].inputID = toAdd;
|
// Remove the keybind, don't replace.
|
||||||
|
action.inputs.remove(input);
|
||||||
|
} else {
|
||||||
|
// Replace the keybind.
|
||||||
|
@:privateAccess
|
||||||
|
action.inputs[i].inputID = toAdd;
|
||||||
|
}
|
||||||
|
hasReplaced = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!hasReplaced) {
|
||||||
|
addKeys(action, [toAdd], state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceButton(action:FlxActionDigital, deviceID:Int, toAdd:Int, toRemove:Int)
|
function replaceButton(action:FlxActionDigital, deviceID:Int, toAdd:FlxGamepadInputID, toRemove:FlxGamepadInputID, state:FlxInputState)
|
||||||
{
|
{
|
||||||
|
if (action.inputs.length == 0) {
|
||||||
|
addButtons(action, [toAdd], state, deviceID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasReplaced:Bool = false;
|
||||||
for (i in 0...action.inputs.length)
|
for (i in 0...action.inputs.length)
|
||||||
{
|
{
|
||||||
var input = action.inputs[i];
|
var input = action.inputs[i];
|
||||||
|
if (input == null) continue;
|
||||||
|
|
||||||
if (isGamepad(input, deviceID) && input.inputID == toRemove)
|
if (isGamepad(input, deviceID) && input.inputID == toRemove)
|
||||||
{
|
{
|
||||||
@:privateAccess
|
@:privateAccess
|
||||||
action.inputs[i].inputID = toAdd;
|
action.inputs[i].inputID = toAdd;
|
||||||
|
hasReplaced = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!hasReplaced) {
|
||||||
|
addButtons(action, [toAdd], state, deviceID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function copyFrom(controls:Controls, ?device:Device)
|
public function copyFrom(controls:Controls, ?device:Device)
|
||||||
|
@ -558,10 +652,12 @@ class Controls extends FlxActionSet
|
||||||
forEachBound(control, function(action, _) removeKeys(action, keys));
|
forEachBound(control, function(action, _) removeKeys(action, keys));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static function addKeys(action:FlxActionDigital, keys:Array<FlxKey>, state:FlxInputState)
|
static function addKeys(action:FlxActionDigital, keys:Array<FlxKey>, state:FlxInputState)
|
||||||
{
|
{
|
||||||
for (key in keys)
|
for (key in keys) {
|
||||||
|
if (key == FlxKey.NONE) continue; // Ignore unbound keys.
|
||||||
action.addKey(key, state);
|
action.addKey(key, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static function removeKeys(action:FlxActionDigital, keys:Array<FlxKey>)
|
static function removeKeys(action:FlxActionDigital, keys:Array<FlxKey>)
|
||||||
|
@ -582,54 +678,95 @@ class Controls extends FlxActionSet
|
||||||
|
|
||||||
keyboardScheme = scheme;
|
keyboardScheme = scheme;
|
||||||
|
|
||||||
switch(scheme)
|
bindKeys(Control.UI_UP, getDefaultKeybinds(scheme, Control.UI_UP));
|
||||||
{
|
bindKeys(Control.UI_DOWN, getDefaultKeybinds(scheme, Control.UI_DOWN));
|
||||||
case Solo:
|
bindKeys(Control.UI_LEFT, getDefaultKeybinds(scheme, Control.UI_LEFT));
|
||||||
bindKeys(Control.UI_UP, [W, FlxKey.UP]);
|
bindKeys(Control.UI_RIGHT, getDefaultKeybinds(scheme, Control.UI_RIGHT));
|
||||||
bindKeys(Control.UI_DOWN, [S, FlxKey.DOWN]);
|
bindKeys(Control.NOTE_UP, getDefaultKeybinds(scheme, Control.NOTE_UP));
|
||||||
bindKeys(Control.UI_LEFT, [A, FlxKey.LEFT]);
|
bindKeys(Control.NOTE_DOWN, getDefaultKeybinds(scheme, Control.NOTE_DOWN));
|
||||||
bindKeys(Control.UI_RIGHT, [D, FlxKey.RIGHT]);
|
bindKeys(Control.NOTE_LEFT, getDefaultKeybinds(scheme, Control.NOTE_LEFT));
|
||||||
bindKeys(Control.NOTE_UP, [W, FlxKey.UP]);
|
bindKeys(Control.NOTE_RIGHT, getDefaultKeybinds(scheme, Control.NOTE_RIGHT));
|
||||||
bindKeys(Control.NOTE_DOWN, [S, FlxKey.DOWN]);
|
bindKeys(Control.ACCEPT, getDefaultKeybinds(scheme, Control.ACCEPT));
|
||||||
bindKeys(Control.NOTE_LEFT, [A, FlxKey.LEFT]);
|
bindKeys(Control.BACK, getDefaultKeybinds(scheme, Control.BACK));
|
||||||
bindKeys(Control.NOTE_RIGHT, [D, FlxKey.RIGHT]);
|
bindKeys(Control.PAUSE, getDefaultKeybinds(scheme, Control.PAUSE));
|
||||||
bindKeys(Control.ACCEPT, [Z, SPACE, ENTER]);
|
bindKeys(Control.CUTSCENE_ADVANCE, getDefaultKeybinds(scheme, Control.CUTSCENE_ADVANCE));
|
||||||
bindKeys(Control.BACK, [X, BACKSPACE, ESCAPE]);
|
bindKeys(Control.CUTSCENE_SKIP, getDefaultKeybinds(scheme, Control.CUTSCENE_SKIP));
|
||||||
bindKeys(Control.PAUSE, [P, ENTER, ESCAPE]);
|
bindKeys(Control.VOLUME_UP, getDefaultKeybinds(scheme, Control.VOLUME_UP));
|
||||||
bindKeys(Control.RESET, [R]);
|
bindKeys(Control.VOLUME_DOWN, getDefaultKeybinds(scheme, Control.VOLUME_DOWN));
|
||||||
case Duo(true):
|
bindKeys(Control.VOLUME_MUTE, getDefaultKeybinds(scheme, Control.VOLUME_MUTE));
|
||||||
bindKeys(Control.UI_UP, [W]);
|
|
||||||
bindKeys(Control.UI_DOWN, [S]);
|
|
||||||
bindKeys(Control.UI_LEFT, [A]);
|
|
||||||
bindKeys(Control.UI_RIGHT, [D]);
|
|
||||||
bindKeys(Control.NOTE_UP, [W]);
|
|
||||||
bindKeys(Control.NOTE_DOWN, [S]);
|
|
||||||
bindKeys(Control.NOTE_LEFT, [A]);
|
|
||||||
bindKeys(Control.NOTE_RIGHT, [D]);
|
|
||||||
bindKeys(Control.ACCEPT, [G, Z]);
|
|
||||||
bindKeys(Control.BACK, [H, X]);
|
|
||||||
bindKeys(Control.PAUSE, [ONE]);
|
|
||||||
bindKeys(Control.RESET, [R]);
|
|
||||||
case Duo(false):
|
|
||||||
bindKeys(Control.UI_UP, [FlxKey.UP]);
|
|
||||||
bindKeys(Control.UI_DOWN, [FlxKey.DOWN]);
|
|
||||||
bindKeys(Control.UI_LEFT, [FlxKey.LEFT]);
|
|
||||||
bindKeys(Control.UI_RIGHT, [FlxKey.RIGHT]);
|
|
||||||
bindKeys(Control.NOTE_UP, [FlxKey.UP]);
|
|
||||||
bindKeys(Control.NOTE_DOWN, [FlxKey.DOWN]);
|
|
||||||
bindKeys(Control.NOTE_LEFT, [FlxKey.LEFT]);
|
|
||||||
bindKeys(Control.NOTE_RIGHT, [FlxKey.RIGHT]);
|
|
||||||
bindKeys(Control.ACCEPT, [O]);
|
|
||||||
bindKeys(Control.BACK, [P]);
|
|
||||||
bindKeys(Control.PAUSE, [ENTER]);
|
|
||||||
bindKeys(Control.RESET, [BACKSPACE]);
|
|
||||||
case None: // nothing
|
|
||||||
case Custom: // nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
bindMobileLol();
|
bindMobileLol();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDefaultKeybinds(scheme:KeyboardScheme, control:Control):Array<FlxKey> {
|
||||||
|
switch (scheme) {
|
||||||
|
case Solo:
|
||||||
|
switch (control) {
|
||||||
|
case Control.UI_UP: return [W, FlxKey.UP];
|
||||||
|
case Control.UI_DOWN: return [S, FlxKey.DOWN];
|
||||||
|
case Control.UI_LEFT: return [A, FlxKey.LEFT];
|
||||||
|
case Control.UI_RIGHT: return [D, FlxKey.RIGHT];
|
||||||
|
case Control.NOTE_UP: return [W, FlxKey.UP];
|
||||||
|
case Control.NOTE_DOWN: return [S, FlxKey.DOWN];
|
||||||
|
case Control.NOTE_LEFT: return [A, FlxKey.LEFT];
|
||||||
|
case Control.NOTE_RIGHT: return [D, FlxKey.RIGHT];
|
||||||
|
case Control.ACCEPT: return [Z, SPACE, ENTER];
|
||||||
|
case Control.BACK: return [X, BACKSPACE, ESCAPE];
|
||||||
|
case Control.PAUSE: return [P, ENTER, ESCAPE];
|
||||||
|
case Control.CUTSCENE_ADVANCE: return [Z, ENTER];
|
||||||
|
case Control.CUTSCENE_SKIP: return [P, ESCAPE];
|
||||||
|
case Control.VOLUME_UP: return [PLUS, NUMPADPLUS];
|
||||||
|
case Control.VOLUME_DOWN: return [MINUS, NUMPADMINUS];
|
||||||
|
case Control.VOLUME_MUTE: return [ZERO, NUMPADZERO];
|
||||||
|
case Control.RESET: return [R];
|
||||||
|
}
|
||||||
|
case Duo(true):
|
||||||
|
switch (control) {
|
||||||
|
case Control.UI_UP: return [W];
|
||||||
|
case Control.UI_DOWN: return [S];
|
||||||
|
case Control.UI_LEFT: return [A];
|
||||||
|
case Control.UI_RIGHT: return [D];
|
||||||
|
case Control.NOTE_UP: return [W];
|
||||||
|
case Control.NOTE_DOWN: return [S];
|
||||||
|
case Control.NOTE_LEFT: return [A];
|
||||||
|
case Control.NOTE_RIGHT: return [D];
|
||||||
|
case Control.ACCEPT: return [G, Z];
|
||||||
|
case Control.BACK: return [H, X];
|
||||||
|
case Control.PAUSE: return [ONE];
|
||||||
|
case Control.CUTSCENE_ADVANCE: return [G, Z];
|
||||||
|
case Control.CUTSCENE_SKIP: return [ONE];
|
||||||
|
case Control.VOLUME_UP: return [PLUS];
|
||||||
|
case Control.VOLUME_DOWN: return [MINUS];
|
||||||
|
case Control.VOLUME_MUTE: return [ZERO];
|
||||||
|
case Control.RESET: return [R];
|
||||||
|
}
|
||||||
|
case Duo(false):
|
||||||
|
switch (control) {
|
||||||
|
case Control.UI_UP: return [FlxKey.UP];
|
||||||
|
case Control.UI_DOWN: return [FlxKey.DOWN];
|
||||||
|
case Control.UI_LEFT: return [FlxKey.LEFT];
|
||||||
|
case Control.UI_RIGHT: return [FlxKey.RIGHT];
|
||||||
|
case Control.NOTE_UP: return [FlxKey.UP];
|
||||||
|
case Control.NOTE_DOWN: return [FlxKey.DOWN];
|
||||||
|
case Control.NOTE_LEFT: return [FlxKey.LEFT];
|
||||||
|
case Control.NOTE_RIGHT: return [FlxKey.RIGHT];
|
||||||
|
case Control.ACCEPT: return [ENTER];
|
||||||
|
case Control.BACK: return [ESCAPE];
|
||||||
|
case Control.PAUSE: return [ONE];
|
||||||
|
case Control.CUTSCENE_ADVANCE: return [ENTER];
|
||||||
|
case Control.CUTSCENE_SKIP: return [ONE];
|
||||||
|
case Control.VOLUME_UP: return [NUMPADPLUS];
|
||||||
|
case Control.VOLUME_DOWN: return [NUMPADMINUS];
|
||||||
|
case Control.VOLUME_MUTE: return [NUMPADZERO];
|
||||||
|
case Control.RESET: return [R];
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Fallthrough.
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
function bindMobileLol()
|
function bindMobileLol()
|
||||||
{
|
{
|
||||||
#if FLX_TOUCH
|
#if FLX_TOUCH
|
||||||
|
@ -704,23 +841,51 @@ class Controls extends FlxActionSet
|
||||||
{
|
{
|
||||||
addGamepadLiteral(id, [
|
addGamepadLiteral(id, [
|
||||||
|
|
||||||
Control.ACCEPT => [#if switch B #else A #end],
|
Control.ACCEPT => getDefaultGamepadBinds(Control.ACCEPT),
|
||||||
Control.BACK => [#if switch A #else B #end, FlxGamepadInputID.BACK],
|
Control.BACK => getDefaultGamepadBinds(Control.BACK),
|
||||||
Control.UI_UP => [DPAD_UP, LEFT_STICK_DIGITAL_UP],
|
Control.UI_UP => getDefaultGamepadBinds(Control.UI_UP),
|
||||||
Control.UI_DOWN => [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN],
|
Control.UI_DOWN => getDefaultGamepadBinds(Control.UI_DOWN),
|
||||||
Control.UI_LEFT => [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT],
|
Control.UI_LEFT => getDefaultGamepadBinds(Control.UI_LEFT),
|
||||||
Control.UI_RIGHT => [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT],
|
Control.UI_RIGHT => getDefaultGamepadBinds(Control.UI_RIGHT),
|
||||||
// don't swap A/B or X/Y for switch on these. A is always the bottom face button
|
// don't swap A/B or X/Y for switch on these. A is always the bottom face button
|
||||||
Control.NOTE_UP => [DPAD_UP, Y, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP],
|
Control.NOTE_UP => getDefaultGamepadBinds(Control.NOTE_UP),
|
||||||
Control.NOTE_DOWN => [DPAD_DOWN, A, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN],
|
Control.NOTE_DOWN => getDefaultGamepadBinds(Control.NOTE_DOWN),
|
||||||
Control.NOTE_LEFT => [DPAD_LEFT, X, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT],
|
Control.NOTE_LEFT => getDefaultGamepadBinds(Control.NOTE_LEFT),
|
||||||
Control.NOTE_RIGHT => [DPAD_RIGHT, B, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT],
|
Control.NOTE_RIGHT => getDefaultGamepadBinds(Control.NOTE_RIGHT),
|
||||||
Control.PAUSE => [START],
|
Control.PAUSE => getDefaultGamepadBinds(Control.PAUSE),
|
||||||
Control.RESET => [RIGHT_SHOULDER]
|
// Control.VOLUME_UP => [RIGHT_SHOULDER],
|
||||||
#if CAN_CHEAT, Control.CHEAT => [X] #end
|
// Control.VOLUME_DOWN => [LEFT_SHOULDER],
|
||||||
|
// Control.VOLUME_MUTE => [RIGHT_TRIGGER],
|
||||||
|
Control.CUTSCENE_ADVANCE => getDefaultGamepadBinds(Control.CUTSCENE_ADVANCE),
|
||||||
|
Control.CUTSCENE_SKIP => getDefaultGamepadBinds(Control.CUTSCENE_SKIP),
|
||||||
|
Control.RESET => getDefaultGamepadBinds(Control.RESET),
|
||||||
|
#if CAN_CHEAT, Control.CHEAT => getDefaultGamepadBinds(Control.CHEAT) #end
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDefaultGamepadBinds(control:Control):Array<FlxGamepadInputID> {
|
||||||
|
switch(control) {
|
||||||
|
case Control.ACCEPT: return [#if switch B #else A #end];
|
||||||
|
case Control.BACK: return [#if switch A #else B #end, FlxGamepadInputID.BACK];
|
||||||
|
case Control.UI_UP: return [DPAD_UP, LEFT_STICK_DIGITAL_UP];
|
||||||
|
case Control.UI_DOWN: return [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN];
|
||||||
|
case Control.UI_LEFT: return [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT];
|
||||||
|
case Control.UI_RIGHT: return [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT];
|
||||||
|
case Control.NOTE_UP: return [DPAD_UP, Y, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP];
|
||||||
|
case Control.NOTE_DOWN: return [DPAD_DOWN, A, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN];
|
||||||
|
case Control.NOTE_LEFT: return [DPAD_LEFT, X, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT];
|
||||||
|
case Control.NOTE_RIGHT: return [DPAD_RIGHT, B, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT];
|
||||||
|
case Control.PAUSE: return [START];
|
||||||
|
case Control.CUTSCENE_ADVANCE: return [A];
|
||||||
|
case Control.CUTSCENE_SKIP: return [START];
|
||||||
|
case Control.RESET: return [RIGHT_SHOULDER];
|
||||||
|
#if CAN_CHEAT, Control.CHEAT: return [X]; #end
|
||||||
|
default:
|
||||||
|
// Fallthrough.
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets all actions that pertain to the binder to trigger when the supplied keys are used.
|
* Sets all actions that pertain to the binder to trigger when the supplied keys are used.
|
||||||
* If binder is a literal you can inline this
|
* If binder is a literal you can inline this
|
||||||
|
@ -749,8 +914,10 @@ class Controls extends FlxActionSet
|
||||||
|
|
||||||
inline static function addButtons(action:FlxActionDigital, buttons:Array<FlxGamepadInputID>, state, id)
|
inline static function addButtons(action:FlxActionDigital, buttons:Array<FlxGamepadInputID>, state, id)
|
||||||
{
|
{
|
||||||
for (button in buttons)
|
for (button in buttons) {
|
||||||
|
if (button == FlxGamepadInputID.NONE) continue; // Ignore unbound keys.
|
||||||
action.addGamepad(button, state, id);
|
action.addGamepad(button, state, id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static function removeButtons(action:FlxActionDigital, gamepadID:Int, buttons:Array<FlxGamepadInputID>)
|
static function removeButtons(action:FlxActionDigital, gamepadID:Int, buttons:Array<FlxGamepadInputID>)
|
||||||
|
@ -798,6 +965,11 @@ class Controls extends FlxActionSet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE: When loading controls:
|
||||||
|
* An EMPTY array means the control is uninitialized and needs to be reset to default.
|
||||||
|
* An array with a single FlxKey.NONE means the control was intentionally unbound by the user.
|
||||||
|
*/
|
||||||
public function fromSaveData(data:Dynamic, device:Device)
|
public function fromSaveData(data:Dynamic, device:Device)
|
||||||
{
|
{
|
||||||
for (control in Control.createAll())
|
for (control in Control.createAll())
|
||||||
|
@ -805,17 +977,44 @@ class Controls extends FlxActionSet
|
||||||
var inputs:Array<Int> = Reflect.field(data, control.getName());
|
var inputs:Array<Int> = Reflect.field(data, control.getName());
|
||||||
if (inputs != null)
|
if (inputs != null)
|
||||||
{
|
{
|
||||||
|
if (inputs.length == 0) {
|
||||||
|
trace('Control ${control} is missing bindings, resetting to default.');
|
||||||
|
switch(device)
|
||||||
|
{
|
||||||
|
case Keys:
|
||||||
|
bindKeys(control, getDefaultKeybinds(Solo, control));
|
||||||
|
case Gamepad(id):
|
||||||
|
bindButtons(control, id, getDefaultGamepadBinds(control));
|
||||||
|
}
|
||||||
|
} else if (inputs == [FlxKey.NONE]) {
|
||||||
|
trace('Control ${control} is unbound, leaving it be.');
|
||||||
|
} else {
|
||||||
|
switch(device)
|
||||||
|
{
|
||||||
|
case Keys:
|
||||||
|
bindKeys(control, inputs.copy());
|
||||||
|
case Gamepad(id):
|
||||||
|
bindButtons(control, id, inputs.copy());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
trace('Control ${control} is missing bindings, resetting to default.');
|
||||||
switch(device)
|
switch(device)
|
||||||
{
|
{
|
||||||
case Keys:
|
case Keys:
|
||||||
bindKeys(control, inputs.copy());
|
bindKeys(control, getDefaultKeybinds(Solo, control));
|
||||||
case Gamepad(id):
|
case Gamepad(id):
|
||||||
bindButtons(control, id, inputs.copy());
|
bindButtons(control, id, getDefaultGamepadBinds(control));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE: When saving controls:
|
||||||
|
* An EMPTY array means the control is uninitialized and needs to be reset to default.
|
||||||
|
* An array with a single FlxKey.NONE means the control was intentionally unbound by the user.
|
||||||
|
*/
|
||||||
public function createSaveData(device:Device):Dynamic
|
public function createSaveData(device:Device):Dynamic
|
||||||
{
|
{
|
||||||
var isEmpty = true;
|
var isEmpty = true;
|
||||||
|
@ -825,6 +1024,8 @@ class Controls extends FlxActionSet
|
||||||
var inputs = getInputsFor(control, device);
|
var inputs = getInputsFor(control, device);
|
||||||
isEmpty = isEmpty && inputs.length == 0;
|
isEmpty = isEmpty && inputs.length == 0;
|
||||||
|
|
||||||
|
if (inputs.length == 0) inputs = [FlxKey.NONE];
|
||||||
|
|
||||||
Reflect.setField(data, control.getName(), inputs);
|
Reflect.setField(data, control.getName(), inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,13 +20,6 @@ import openfl.filters.ShaderFilter;
|
||||||
|
|
||||||
class CoolUtil
|
class CoolUtil
|
||||||
{
|
{
|
||||||
public static var difficultyArray:Array<String> = ['EASY', "NORMAL", "HARD"];
|
|
||||||
|
|
||||||
public static function difficultyString():String
|
|
||||||
{
|
|
||||||
return difficultyArray[PlayState.storyDifficulty];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function coolBaseLog(base:Float, fin:Float):Float
|
public static function coolBaseLog(base:Float, fin:Float):Float
|
||||||
{
|
{
|
||||||
return Math.log(fin) / Math.log(base);
|
return Math.log(fin) / Math.log(base);
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
package funkin;
|
|
||||||
|
|
||||||
import flixel.FlxSprite;
|
|
||||||
import flixel.FlxState;
|
|
||||||
import flixel.addons.display.FlxGridOverlay;
|
|
||||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
|
||||||
import flixel.math.FlxPoint;
|
|
||||||
import flixel.text.FlxText;
|
|
||||||
import flixel.util.FlxColor;
|
|
||||||
import openfl.Assets;
|
|
||||||
import openfl.display.BitmapData;
|
|
||||||
import openfl.display.MovieClip;
|
|
||||||
import openfl.display.Timeline;
|
|
||||||
import openfl.geom.Matrix;
|
|
||||||
import openfl.geom.Rectangle;
|
|
||||||
|
|
||||||
class CutsceneAnimTestState extends FlxState
|
|
||||||
{
|
|
||||||
var cutsceneGroup:CutsceneCharacter;
|
|
||||||
|
|
||||||
var curSelected:Int = 0;
|
|
||||||
var debugTxt:FlxText;
|
|
||||||
|
|
||||||
var funnySprite:FlxSprite = new FlxSprite();
|
|
||||||
var clip:MovieClip;
|
|
||||||
|
|
||||||
public function new()
|
|
||||||
{
|
|
||||||
super();
|
|
||||||
|
|
||||||
var gridBG:FlxSprite = FlxGridOverlay.create(10, 10);
|
|
||||||
gridBG.scrollFactor.set(0.5, 0.5);
|
|
||||||
add(gridBG);
|
|
||||||
|
|
||||||
debugTxt = new FlxText(900, 20, 0, "", 20);
|
|
||||||
debugTxt.color = FlxColor.BLUE;
|
|
||||||
add(debugTxt);
|
|
||||||
|
|
||||||
clip = Assets.getMovieClip("tanky:");
|
|
||||||
// clip.x = FlxG.width/2;
|
|
||||||
// clip.y = FlxG.height/2;
|
|
||||||
FlxG.stage.addChild(clip);
|
|
||||||
|
|
||||||
var swagShit:MovieClip = Assets.getMovieClip('tankBG:');
|
|
||||||
// swagShit.scaleX = 5;
|
|
||||||
|
|
||||||
FlxG.stage.addChild(swagShit);
|
|
||||||
swagShit.gotoAndStop(13);
|
|
||||||
|
|
||||||
var swfMountain = new BitmapData(FlxG.width, FlxG.height, true, 0x00000000);
|
|
||||||
swfMountain.draw(swagShit, swagShit.transform.matrix);
|
|
||||||
|
|
||||||
var mountains:FlxSprite = new FlxSprite().loadGraphic(swfMountain);
|
|
||||||
// add(mountains);
|
|
||||||
|
|
||||||
FlxG.stage.removeChild(swagShit);
|
|
||||||
|
|
||||||
funnySprite.x = FlxG.width / 2;
|
|
||||||
funnySprite.y = FlxG.height / 2;
|
|
||||||
add(funnySprite);
|
|
||||||
}
|
|
||||||
|
|
||||||
override function update(elapsed:Float)
|
|
||||||
{
|
|
||||||
super.update(elapsed);
|
|
||||||
|
|
||||||
// jam sprite into top left corner
|
|
||||||
var drawMatrix:Matrix = clip.transform.matrix;
|
|
||||||
var bounds:Rectangle = clip.getBounds(null);
|
|
||||||
drawMatrix.tx = -bounds.x;
|
|
||||||
drawMatrix.ty = -bounds.y;
|
|
||||||
// make bitmapdata only as big as it needs to be
|
|
||||||
var funnyBmp:BitmapData = new BitmapData(Math.ceil(bounds.width), Math.ceil(bounds.height), true, 0x00000000);
|
|
||||||
funnyBmp.draw(clip, drawMatrix, true);
|
|
||||||
funnySprite.loadGraphic(funnyBmp);
|
|
||||||
// jam sprite back into place lol
|
|
||||||
funnySprite.offset.x = -bounds.x;
|
|
||||||
funnySprite.offset.y = -bounds.y;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
package funkin;
|
|
||||||
|
|
||||||
import flixel.FlxSprite;
|
|
||||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
|
||||||
import flixel.math.FlxPoint;
|
|
||||||
|
|
||||||
class CutsceneCharacter extends FlxTypedGroup<FlxSprite>
|
|
||||||
{
|
|
||||||
public var coolPos:FlxPoint = FlxPoint.get();
|
|
||||||
public var animShit:Map<String, FlxPoint> = new Map();
|
|
||||||
|
|
||||||
var imageShit:String;
|
|
||||||
|
|
||||||
public function new(x:Float, y:Float, imageShit:String)
|
|
||||||
{
|
|
||||||
super();
|
|
||||||
|
|
||||||
coolPos.set(x, y);
|
|
||||||
|
|
||||||
this.imageShit = imageShit;
|
|
||||||
parseOffsets();
|
|
||||||
createCutscene(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// shitshow, oh well
|
|
||||||
var arrayLMFAOOOO:Array<String> = [];
|
|
||||||
|
|
||||||
function parseOffsets()
|
|
||||||
{
|
|
||||||
var splitShit:Array<String> = CoolUtil.coolTextFile(Paths.file('images/cutsceneStuff/' + imageShit + "CutsceneOffsets.txt"));
|
|
||||||
|
|
||||||
for (i in splitShit)
|
|
||||||
{
|
|
||||||
var xAndY:FlxPoint = FlxPoint.get();
|
|
||||||
var dumbSplit:Array<String> = i.split('---')[1].trim().split(' ');
|
|
||||||
trace('cool split: ' + i.split('---')[1]);
|
|
||||||
trace(dumbSplit);
|
|
||||||
xAndY.set(Std.parseFloat(dumbSplit[0]), Std.parseFloat(dumbSplit[1]));
|
|
||||||
|
|
||||||
animShit.set(i.split('---')[0].trim(), xAndY);
|
|
||||||
arrayLMFAOOOO.push(i.split('---')[0].trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
trace(animShit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createCutscene(daNum:Int = 0)
|
|
||||||
{
|
|
||||||
var cutScene:FlxSprite = new FlxSprite(coolPos.x + animShit.get(arrayLMFAOOOO[daNum]).x, coolPos.y + animShit.get(arrayLMFAOOOO[daNum]).y);
|
|
||||||
cutScene.frames = Paths.getSparrowAtlas('cutsceneStuff/' + imageShit + "-" + daNum);
|
|
||||||
cutScene.animation.addByPrefix('weed', arrayLMFAOOOO[daNum], 24, false);
|
|
||||||
cutScene.animation.play('weed');
|
|
||||||
cutScene.antialiasing = true;
|
|
||||||
|
|
||||||
cutScene.animation.finishCallback = function(anim:String) {
|
|
||||||
cutScene.kill();
|
|
||||||
cutScene.destroy();
|
|
||||||
cutScene = null;
|
|
||||||
|
|
||||||
if (daNum + 1 < arrayLMFAOOOO.length) createCutscene(daNum + 1);
|
|
||||||
else
|
|
||||||
ended();
|
|
||||||
};
|
|
||||||
|
|
||||||
add(cutScene);
|
|
||||||
}
|
|
||||||
|
|
||||||
public var onFinish:Void->Void;
|
|
||||||
|
|
||||||
public function ended():Void
|
|
||||||
{
|
|
||||||
if (onFinish != null) onFinish();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -38,7 +38,7 @@ class DialogueBox extends FlxSpriteGroup
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
||||||
switch (PlayState.currentSong.song.toLowerCase())
|
switch (PlayState.instance.currentSong.songId.toLowerCase())
|
||||||
{
|
{
|
||||||
case 'senpai':
|
case 'senpai':
|
||||||
FlxG.sound.playMusic(Paths.music('Lunchbox'), 0);
|
FlxG.sound.playMusic(Paths.music('Lunchbox'), 0);
|
||||||
|
@ -79,7 +79,7 @@ class DialogueBox extends FlxSpriteGroup
|
||||||
box = new FlxSprite(-20, 45);
|
box = new FlxSprite(-20, 45);
|
||||||
|
|
||||||
var hasDialog:Bool = false;
|
var hasDialog:Bool = false;
|
||||||
switch (PlayState.currentSong.song.toLowerCase())
|
switch (PlayState.instance.currentSong.songId.toLowerCase())
|
||||||
{
|
{
|
||||||
case 'senpai':
|
case 'senpai':
|
||||||
hasDialog = true;
|
hasDialog = true;
|
||||||
|
@ -151,8 +151,8 @@ class DialogueBox extends FlxSpriteGroup
|
||||||
override function update(elapsed:Float):Void
|
override function update(elapsed:Float):Void
|
||||||
{
|
{
|
||||||
// HARD CODING CUZ IM STUPDI
|
// HARD CODING CUZ IM STUPDI
|
||||||
if (PlayState.currentSong.song.toLowerCase() == 'roses') portraitLeft.visible = false;
|
if (PlayState.instance.currentSong.songId.toLowerCase() == 'roses') portraitLeft.visible = false;
|
||||||
if (PlayState.currentSong.song.toLowerCase() == 'thorns')
|
if (PlayState.instance.currentSong.songId.toLowerCase() == 'thorns')
|
||||||
{
|
{
|
||||||
portraitLeft.color = FlxColor.BLACK;
|
portraitLeft.color = FlxColor.BLACK;
|
||||||
swagDialogue.color = FlxColor.WHITE;
|
swagDialogue.color = FlxColor.WHITE;
|
||||||
|
@ -188,8 +188,8 @@ class DialogueBox extends FlxSpriteGroup
|
||||||
{
|
{
|
||||||
isEnding = true;
|
isEnding = true;
|
||||||
|
|
||||||
if (PlayState.currentSong.song.toLowerCase() == 'senpai'
|
if (PlayState.instance.currentSong.songId.toLowerCase() == 'senpai'
|
||||||
|| PlayState.currentSong.song.toLowerCase() == 'thorns') FlxG.sound.music.fadeOut(2.2, 0);
|
|| PlayState.instance.currentSong.songId.toLowerCase() == 'thorns') FlxG.sound.music.fadeOut(2.2, 0);
|
||||||
|
|
||||||
new FlxTimer().start(0.2, function(tmr:FlxTimer) {
|
new FlxTimer().start(0.2, function(tmr:FlxTimer) {
|
||||||
box.alpha -= 1 / 5;
|
box.alpha -= 1 / 5;
|
||||||
|
|
|
@ -34,12 +34,14 @@ import funkin.play.song.SongData.SongDataParser;
|
||||||
import funkin.shaderslmfao.AngleMask;
|
import funkin.shaderslmfao.AngleMask;
|
||||||
import funkin.shaderslmfao.PureColor;
|
import funkin.shaderslmfao.PureColor;
|
||||||
import funkin.shaderslmfao.StrokeShader;
|
import funkin.shaderslmfao.StrokeShader;
|
||||||
|
import funkin.play.PlayStatePlaylist;
|
||||||
|
import funkin.play.song.Song;
|
||||||
import lime.app.Future;
|
import lime.app.Future;
|
||||||
import lime.utils.Assets;
|
import lime.utils.Assets;
|
||||||
|
|
||||||
class FreeplayState extends MusicBeatSubState
|
class FreeplayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
var songs:Array<SongMetadata> = [];
|
var songs:Array<FreeplaySongData> = [];
|
||||||
|
|
||||||
// var selector:FlxText;
|
// var selector:FlxText;
|
||||||
var curSelected:Int = 0;
|
var curSelected:Int = 0;
|
||||||
|
@ -112,15 +114,15 @@ class FreeplayState extends MusicBeatSubState
|
||||||
|
|
||||||
#if debug
|
#if debug
|
||||||
isDebug = true;
|
isDebug = true;
|
||||||
addSong('Test', 1, 'bf-pixel');
|
addSong('Test', 'tutorial', 'bf-pixel');
|
||||||
addSong('Pyro', 8, 'darnell');
|
addSong('Pyro', 'weekend1', 'darnell');
|
||||||
#end
|
#end
|
||||||
|
|
||||||
var initSonglist = CoolUtil.coolTextFile(Paths.txt('freeplaySonglist'));
|
var initSonglist = CoolUtil.coolTextFile(Paths.txt('freeplaySonglist'));
|
||||||
|
|
||||||
for (i in 0...initSonglist.length)
|
for (i in 0...initSonglist.length)
|
||||||
{
|
{
|
||||||
songs.push(new SongMetadata(initSonglist[i], 1, 'gf'));
|
songs.push(new FreeplaySongData(initSonglist[i], 'tutorial', 'gf'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FlxG.sound.music != null)
|
if (FlxG.sound.music != null)
|
||||||
|
@ -128,22 +130,28 @@ class FreeplayState extends MusicBeatSubState
|
||||||
if (!FlxG.sound.music.playing) FlxG.sound.playMusic(Paths.music('freakyMenu'));
|
if (!FlxG.sound.music.playing) FlxG.sound.playMusic(Paths.music('freakyMenu'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StoryMenuState.weekUnlocked[2] || isDebug) addWeek(['Bopeebo', 'Fresh', 'Dadbattle'], 1, ['dad']);
|
// if (StoryMenuState.weekUnlocked[2] || isDebug)
|
||||||
|
addWeek(['Bopeebo', 'Fresh', 'Dadbattle'], 'week1', ['dad']);
|
||||||
|
|
||||||
if (StoryMenuState.weekUnlocked[2] || isDebug) addWeek(['Spookeez', 'South', 'Monster'], 2, ['spooky', 'spooky', 'monster']);
|
// if (StoryMenuState.weekUnlocked[2] || isDebug)
|
||||||
|
addWeek(['Spookeez', 'South', 'Monster'], 'week2', ['spooky', 'spooky', 'monster']);
|
||||||
|
|
||||||
if (StoryMenuState.weekUnlocked[3] || isDebug) addWeek(['Pico', 'Philly', 'Blammed'], 3, ['pico']);
|
// if (StoryMenuState.weekUnlocked[3] || isDebug)
|
||||||
|
addWeek(['Pico', 'Philly-Nice', 'Blammed'], 'week3', ['pico']);
|
||||||
|
|
||||||
if (StoryMenuState.weekUnlocked[4] || isDebug) addWeek(['Satin-Panties', 'High', 'Milf'], 4, ['mom']);
|
// if (StoryMenuState.weekUnlocked[4] || isDebug)
|
||||||
|
addWeek(['Satin-Panties', 'High', 'MILF'], 'week4', ['mom']);
|
||||||
|
|
||||||
if (StoryMenuState.weekUnlocked[5] || isDebug) addWeek(['Cocoa', 'Eggnog', 'Winter-Horrorland'], 5,
|
// if (StoryMenuState.weekUnlocked[5] || isDebug)
|
||||||
['parents-christmas', 'parents-christmas', 'monster-christmas']);
|
addWeek(['Cocoa', 'Eggnog', 'Winter-Horrorland'], 'week5', ['parents-christmas', 'parents-christmas', 'monster-christmas']);
|
||||||
|
|
||||||
if (StoryMenuState.weekUnlocked[6] || isDebug) addWeek(['Senpai', 'Roses', 'Thorns'], 6, ['senpai', 'senpai', 'spirit']);
|
// if (StoryMenuState.weekUnlocked[6] || isDebug)
|
||||||
|
addWeek(['Senpai', 'Roses', 'Thorns'], 'week6', ['senpai', 'senpai', 'spirit']);
|
||||||
|
|
||||||
if (StoryMenuState.weekUnlocked[7] || isDebug) addWeek(['Ugh', 'Guns', 'Stress'], 7, ['tankman']);
|
// if (StoryMenuState.weekUnlocked[7] || isDebug)
|
||||||
|
addWeek(['Ugh', 'Guns', 'Stress'], 'week7', ['tankman']);
|
||||||
|
|
||||||
addWeek(["Darnell", "lit-up", "2hot", "blazin"], 8, ['darnell']);
|
addWeek(["Darnell", "lit-up", "2hot", "blazin"], 'weekend1', ['darnell']);
|
||||||
|
|
||||||
// LOAD MUSIC
|
// LOAD MUSIC
|
||||||
|
|
||||||
|
@ -464,7 +472,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
grpCapsules.clear();
|
grpCapsules.clear();
|
||||||
|
|
||||||
// var regexp:EReg = regexp;
|
// var regexp:EReg = regexp;
|
||||||
var tempSongs:Array<SongMetadata> = songs;
|
var tempSongs:Array<FreeplaySongData> = songs;
|
||||||
|
|
||||||
if (filterStuff != null)
|
if (filterStuff != null)
|
||||||
{
|
{
|
||||||
|
@ -553,19 +561,19 @@ class FreeplayState extends MusicBeatSubState
|
||||||
changeDiff();
|
changeDiff();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addSong(songName:String, weekNum:Int, songCharacter:String)
|
public function addSong(songName:String, levelId:String, songCharacter:String)
|
||||||
{
|
{
|
||||||
songs.push(new SongMetadata(songName, weekNum, songCharacter));
|
songs.push(new FreeplaySongData(songName, levelId, songCharacter));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addWeek(songs:Array<String>, weekNum:Int, ?songCharacters:Array<String>)
|
public function addWeek(songs:Array<String>, levelId:String, ?songCharacters:Array<String>)
|
||||||
{
|
{
|
||||||
if (songCharacters == null) songCharacters = ['bf'];
|
if (songCharacters == null) songCharacters = ['bf'];
|
||||||
|
|
||||||
var num:Int = 0;
|
var num:Int = 0;
|
||||||
for (song in songs)
|
for (song in songs)
|
||||||
{
|
{
|
||||||
addSong(song, weekNum, songCharacters[num]);
|
addSong(song, levelId, songCharacters[num]);
|
||||||
|
|
||||||
if (songCharacters.length != 1) num++;
|
if (songCharacters.length != 1) num++;
|
||||||
}
|
}
|
||||||
|
@ -851,11 +859,9 @@ class FreeplayState extends MusicBeatSubState
|
||||||
curDifficulty = 1;
|
curDifficulty = 1;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
PlayState.currentSong = SongLoad.loadFromJson(poop, songs[curSelected].songName.toLowerCase());
|
PlayStatePlaylist.isStoryMode = false;
|
||||||
PlayState.currentSong_NEW = SongDataParser.fetchSong(songs[curSelected].songName.toLowerCase());
|
var targetSong:Song = SongDataParser.fetchSong(songs[curSelected].songName.toLowerCase());
|
||||||
PlayState.isStoryMode = false;
|
var targetDifficulty:String = switch (curDifficulty)
|
||||||
PlayState.storyDifficulty = curDifficulty;
|
|
||||||
PlayState.storyDifficulty_NEW = switch (curDifficulty)
|
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
'easy';
|
'easy';
|
||||||
|
@ -865,27 +871,41 @@ class FreeplayState extends MusicBeatSubState
|
||||||
'hard';
|
'hard';
|
||||||
default: 'normal';
|
default: 'normal';
|
||||||
};
|
};
|
||||||
// SongLoad.curDiff = Highscore.formatSong()
|
|
||||||
|
|
||||||
SongLoad.curDiff = PlayState.storyDifficulty_NEW;
|
// TODO: Implement additional difficulties into the interface properly.
|
||||||
|
if (FlxG.keys.pressed.E)
|
||||||
|
{
|
||||||
|
targetDifficulty = 'erect';
|
||||||
|
}
|
||||||
|
|
||||||
PlayState.storyWeek = songs[curSelected].week;
|
// TODO: Implement Pico into the interface properly.
|
||||||
// trace(' CUR WEEK ' + PlayState.storyWeek);
|
var targetCharacter:String = 'bf';
|
||||||
|
if (FlxG.keys.pressed.P)
|
||||||
|
{
|
||||||
|
targetCharacter = 'pico';
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayStatePlaylist.campaignId = songs[curSelected].levelId;
|
||||||
|
|
||||||
// Visual and audio effects.
|
// Visual and audio effects.
|
||||||
FlxG.sound.play(Paths.sound('confirmMenu'));
|
FlxG.sound.play(Paths.sound('confirmMenu'));
|
||||||
dj.confirm();
|
dj.confirm();
|
||||||
|
|
||||||
new FlxTimer().start(1, function(tmr:FlxTimer) {
|
new FlxTimer().start(1, function(tmr:FlxTimer) {
|
||||||
LoadingState.loadAndSwitchState(new PlayState(), true);
|
LoadingState.loadAndSwitchState(new PlayState(
|
||||||
|
{
|
||||||
|
targetSong: targetSong,
|
||||||
|
targetDifficulty: targetDifficulty,
|
||||||
|
targetCharacter: targetCharacter,
|
||||||
|
}), true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override function startOutro(onComplete:() -> Void):Void
|
override function switchTo(nextState:FlxState):Bool
|
||||||
{
|
{
|
||||||
clearDaCache(songs[curSelected].songName);
|
clearDaCache(songs[curSelected].songName);
|
||||||
super.startOutro(onComplete);
|
return super.switchTo(nextState);
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeDiff(change:Int = 0)
|
function changeDiff(change:Int = 0)
|
||||||
|
@ -901,19 +921,6 @@ class FreeplayState extends MusicBeatSubState
|
||||||
intendedScore = Highscore.getScore(songs[curSelected].songName, curDifficulty);
|
intendedScore = Highscore.getScore(songs[curSelected].songName, curDifficulty);
|
||||||
intendedCompletion = Highscore.getCompletion(songs[curSelected].songName, curDifficulty);
|
intendedCompletion = Highscore.getCompletion(songs[curSelected].songName, curDifficulty);
|
||||||
|
|
||||||
PlayState.storyDifficulty = curDifficulty;
|
|
||||||
PlayState.storyDifficulty_NEW = switch (curDifficulty)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
'easy';
|
|
||||||
case 1:
|
|
||||||
'normal';
|
|
||||||
case 2:
|
|
||||||
'hard';
|
|
||||||
default:
|
|
||||||
'normal';
|
|
||||||
};
|
|
||||||
|
|
||||||
grpDifficulties.group.forEach(function(spr) {
|
grpDifficulties.group.forEach(function(spr) {
|
||||||
spr.visible = false;
|
spr.visible = false;
|
||||||
});
|
});
|
||||||
|
@ -1040,17 +1047,17 @@ enum abstract FilterType(String)
|
||||||
var ALL;
|
var ALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
class SongMetadata
|
class FreeplaySongData
|
||||||
{
|
{
|
||||||
public var songName:String = "";
|
public var songName:String = "";
|
||||||
public var week:Int = 0;
|
public var levelId:String = "";
|
||||||
public var songCharacter:String = "";
|
public var songCharacter:String = "";
|
||||||
public var isFav:Bool = false;
|
public var isFav:Bool = false;
|
||||||
|
|
||||||
public function new(song:String, week:Int, songCharacter:String, ?isFav:Bool = false)
|
public function new(song:String, levelId:String, songCharacter:String, ?isFav:Bool = false)
|
||||||
{
|
{
|
||||||
this.songName = song;
|
this.songName = song;
|
||||||
this.week = week;
|
this.levelId = levelId;
|
||||||
this.songCharacter = songCharacter;
|
this.songCharacter = songCharacter;
|
||||||
this.isFav = isFav;
|
this.isFav = isFav;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,16 @@ class GitarooPause extends MusicBeatState
|
||||||
|
|
||||||
var replaySelect:Bool = false;
|
var replaySelect:Bool = false;
|
||||||
|
|
||||||
public function new():Void
|
var previousParams:PlayStateParams;
|
||||||
|
|
||||||
|
public function new(previousParams:PlayStateParams):Void
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
this.previousParams = previousParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
override function create()
|
override function create():Void
|
||||||
{
|
{
|
||||||
if (FlxG.sound.music != null) FlxG.sound.music.stop();
|
if (FlxG.sound.music != null) FlxG.sound.music.stop();
|
||||||
|
|
||||||
|
@ -49,7 +53,7 @@ class GitarooPause extends MusicBeatState
|
||||||
super.create();
|
super.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
override function update(elapsed:Float)
|
override function update(elapsed:Float):Void
|
||||||
{
|
{
|
||||||
if (controls.UI_LEFT_P || controls.UI_RIGHT_P) changeThing();
|
if (controls.UI_LEFT_P || controls.UI_RIGHT_P) changeThing();
|
||||||
|
|
||||||
|
@ -57,7 +61,7 @@ class GitarooPause extends MusicBeatState
|
||||||
{
|
{
|
||||||
if (replaySelect)
|
if (replaySelect)
|
||||||
{
|
{
|
||||||
FlxG.switchState(new PlayState());
|
FlxG.switchState(new PlayState(previousParams));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,7 +39,17 @@ class Highscore
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function saveCompletion(song:String, completion:Float, ?diff:Int = 0):Bool
|
public static function saveScoreForDifficulty(song:String, score:Int = 0, diff:String = 'normal'):Bool
|
||||||
|
{
|
||||||
|
var diffInt:Int = 1;
|
||||||
|
|
||||||
|
if (diff == 'easy') diffInt = 0;
|
||||||
|
else if (diff == 'hard') diffInt = 2;
|
||||||
|
|
||||||
|
return saveScore(song, score, diffInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function saveCompletion(song:String, completion:Float, diff:Int = 0):Bool
|
||||||
{
|
{
|
||||||
var formattedSong:String = formatSong(song, diff);
|
var formattedSong:String = formatSong(song, diff);
|
||||||
|
|
||||||
|
@ -57,20 +67,42 @@ class Highscore
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function saveWeekScore(week:Int = 1, score:Int = 0, ?diff:Int = 0):Void
|
public static function saveCompletionForDifficulty(song:String, completion:Float, diff:String = 'normal'):Bool
|
||||||
|
{
|
||||||
|
var diffInt:Int = 1;
|
||||||
|
|
||||||
|
if (diff == 'easy') diffInt = 0;
|
||||||
|
else if (diff == 'hard') diffInt = 2;
|
||||||
|
|
||||||
|
return saveCompletion(song, completion, diffInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function saveWeekScore(week:String, score:Int = 0, diff:Int = 0):Void
|
||||||
{
|
{
|
||||||
#if newgrounds
|
#if newgrounds
|
||||||
NGio.postScore(score, "Week " + week);
|
NGio.postScore(score, 'Campaign ID $week');
|
||||||
#end
|
#end
|
||||||
|
|
||||||
var formattedSong:String = formatSong('week' + week, diff);
|
var formattedSong:String = formatSong(week, diff);
|
||||||
|
|
||||||
if (songScores.exists(formattedSong))
|
if (songScores.exists(formattedSong))
|
||||||
{
|
{
|
||||||
if (songScores.get(formattedSong) < score) setScore(formattedSong, score);
|
if (songScores.get(formattedSong) < score) setScore(formattedSong, score);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
setScore(formattedSong, score);
|
setScore(formattedSong, score);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function saveWeekScoreForDifficulty(week:String, score:Int = 0, diff:String = 'normal'):Void
|
||||||
|
{
|
||||||
|
var diffInt:Int = 1;
|
||||||
|
|
||||||
|
if (diff == 'easy') diffInt = 0;
|
||||||
|
else if (diff == 'hard') diffInt = 2;
|
||||||
|
|
||||||
|
saveWeekScore(week, score, diffInt);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function setCompletion(formattedSong:String, completion:Float):Void
|
static function setCompletion(formattedSong:String, completion:Float):Void
|
||||||
|
@ -122,7 +154,7 @@ class Highscore
|
||||||
return songCompletion.get(formatSong(song, diff));
|
return songCompletion.get(formatSong(song, diff));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getAllScores()
|
public static function getAllScores():Void
|
||||||
{
|
{
|
||||||
trace(songScores.toString());
|
trace(songScores.toString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import flixel.util.FlxColor;
|
||||||
import funkin.modding.module.ModuleHandler;
|
import funkin.modding.module.ModuleHandler;
|
||||||
import funkin.play.PlayState;
|
import funkin.play.PlayState;
|
||||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||||
import funkin.play.event.SongEvent.SongEventParser;
|
import funkin.play.event.SongEventData.SongEventParser;
|
||||||
import funkin.play.song.SongData.SongDataParser;
|
import funkin.play.song.SongData.SongDataParser;
|
||||||
import funkin.ui.PreferencesMenu;
|
import funkin.ui.PreferencesMenu;
|
||||||
import funkin.util.WindowUtil;
|
import funkin.util.WindowUtil;
|
||||||
|
@ -140,10 +140,10 @@ class InitState extends FlxTransitionableState
|
||||||
// WEEK UNLOCK PROGRESSION!!
|
// WEEK UNLOCK PROGRESSION!!
|
||||||
// StoryMenuState.weekUnlocked = FlxG.save.data.weekUnlocked;
|
// StoryMenuState.weekUnlocked = FlxG.save.data.weekUnlocked;
|
||||||
|
|
||||||
if (StoryMenuState.weekUnlocked.length < 4) StoryMenuState.weekUnlocked.insert(0, true);
|
// if (StoryMenuState.weekUnlocked.length < 4) StoryMenuState.weekUnlocked.insert(0, true);
|
||||||
|
|
||||||
// QUICK PATCH OOPS!
|
// QUICK PATCH OOPS!
|
||||||
if (!StoryMenuState.weekUnlocked[0]) StoryMenuState.weekUnlocked[0] = true;
|
// if (!StoryMenuState.weekUnlocked[0]) StoryMenuState.weekUnlocked[0] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FlxG.save.data.seenVideo != null) VideoState.seenVideo = FlxG.save.data.seenVideo;
|
if (FlxG.save.data.seenVideo != null) VideoState.seenVideo = FlxG.save.data.seenVideo;
|
||||||
|
@ -237,20 +237,18 @@ class InitState extends FlxTransitionableState
|
||||||
{
|
{
|
||||||
var dif:Int = getDif();
|
var dif:Int = getDif();
|
||||||
|
|
||||||
PlayState.currentSong = SongLoad.loadFromJson(song, song);
|
var targetDifficulty = switch (dif)
|
||||||
PlayState.currentSong_NEW = SongDataParser.fetchSong(song);
|
|
||||||
PlayState.isStoryMode = isStoryMode;
|
|
||||||
PlayState.storyDifficulty = dif;
|
|
||||||
PlayState.storyDifficulty_NEW = switch (dif)
|
|
||||||
{
|
{
|
||||||
case 0: 'easy';
|
case 0: 'easy';
|
||||||
case 1: 'normal';
|
case 1: 'normal';
|
||||||
case 2: 'hard';
|
case 2: 'hard';
|
||||||
default: 'normal';
|
default: 'normal';
|
||||||
};
|
};
|
||||||
SongLoad.curDiff = PlayState.storyDifficulty_NEW;
|
LoadingState.loadAndSwitchState(new PlayState(
|
||||||
PlayState.storyWeek = week;
|
{
|
||||||
LoadingState.loadAndSwitchState(new PlayState());
|
targetSong: SongDataParser.fetchSong(song),
|
||||||
|
targetDifficulty: targetDifficulty,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,14 +88,14 @@ class LatencyState extends MusicBeatSubState
|
||||||
// // musSpec.visType = FREQUENCIES;
|
// // musSpec.visType = FREQUENCIES;
|
||||||
// add(musSpec);
|
// add(musSpec);
|
||||||
|
|
||||||
for (beat in 0...Math.floor(FlxG.sound.music.length / Conductor.crochet))
|
for (beat in 0...Math.floor(FlxG.sound.music.length / Conductor.beatLengthMs))
|
||||||
{
|
{
|
||||||
var beatTick:FlxSprite = new FlxSprite(songPosToX(beat * Conductor.crochet), FlxG.height - 15);
|
var beatTick:FlxSprite = new FlxSprite(songPosToX(beat * Conductor.beatLengthMs), FlxG.height - 15);
|
||||||
beatTick.makeGraphic(2, 15);
|
beatTick.makeGraphic(2, 15);
|
||||||
beatTick.alpha = 0.3;
|
beatTick.alpha = 0.3;
|
||||||
add(beatTick);
|
add(beatTick);
|
||||||
|
|
||||||
var offsetTxt:FlxText = new FlxText(songPosToX(beat * Conductor.crochet), FlxG.height - 26, 0, "swag");
|
var offsetTxt:FlxText = new FlxText(songPosToX(beat * Conductor.beatLengthMs), FlxG.height - 26, 0, "swag");
|
||||||
offsetTxt.alpha = 0.5;
|
offsetTxt.alpha = 0.5;
|
||||||
diffGrp.add(offsetTxt);
|
diffGrp.add(offsetTxt);
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ class LatencyState extends MusicBeatSubState
|
||||||
|
|
||||||
for (i in 0...32)
|
for (i in 0...32)
|
||||||
{
|
{
|
||||||
var note:Note = new Note(Conductor.crochet * i, 1);
|
var note:Note = new Note(Conductor.beatLengthMs * i, 1);
|
||||||
noteGrp.add(note);
|
noteGrp.add(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,9 +143,9 @@ class LatencyState extends MusicBeatSubState
|
||||||
|
|
||||||
override function stepHit():Bool
|
override function stepHit():Bool
|
||||||
{
|
{
|
||||||
if (curStep % 4 == 2)
|
if (Conductor.currentStep % 4 == 2)
|
||||||
{
|
{
|
||||||
blocks.members[((curBeat % 8) + 1) % 8].alpha = 0.5;
|
blocks.members[((Conductor.currentBeat % 8) + 1) % 8].alpha = 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.stepHit();
|
return super.stepHit();
|
||||||
|
@ -153,11 +153,11 @@ class LatencyState extends MusicBeatSubState
|
||||||
|
|
||||||
override function beatHit():Bool
|
override function beatHit():Bool
|
||||||
{
|
{
|
||||||
if (curBeat % 8 == 0) blocks.forEach(blok -> {
|
if (Conductor.currentBeat % 8 == 0) blocks.forEach(blok -> {
|
||||||
blok.alpha = 0;
|
blok.alpha = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
blocks.members[curBeat % 8].alpha = 1;
|
blocks.members[Conductor.currentBeat % 8].alpha = 1;
|
||||||
// block.visible = !block.visible;
|
// block.visible = !block.visible;
|
||||||
|
|
||||||
return super.beatHit();
|
return super.beatHit();
|
||||||
|
@ -198,8 +198,8 @@ class LatencyState extends MusicBeatSubState
|
||||||
|
|
||||||
offsetText.text = "AUDIO Offset: " + Conductor.audioOffset + "ms";
|
offsetText.text = "AUDIO Offset: " + Conductor.audioOffset + "ms";
|
||||||
offsetText.text += "\nVIDOE Offset: " + Conductor.visualOffset + "ms";
|
offsetText.text += "\nVIDOE Offset: " + Conductor.visualOffset + "ms";
|
||||||
offsetText.text += "\ncurStep: " + curStep;
|
offsetText.text += "\ncurrentStep: " + Conductor.currentStep;
|
||||||
offsetText.text += "\ncurBeat: " + curBeat;
|
offsetText.text += "\ncurrentBeat: " + Conductor.currentBeat;
|
||||||
|
|
||||||
var avgOffsetInput:Float = 0;
|
var avgOffsetInput:Float = 0;
|
||||||
|
|
||||||
|
@ -255,7 +255,7 @@ class LatencyState extends MusicBeatSubState
|
||||||
if (daNote.y < 0 - daNote.height)
|
if (daNote.y < 0 - daNote.height)
|
||||||
{
|
{
|
||||||
daNote.alpha = 1;
|
daNote.alpha = 1;
|
||||||
// daNote.data.strumTime += Conductor.crochet * 8;
|
// daNote.data.strumTime += Conductor.beatLengthMs * 8;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -266,12 +266,12 @@ class LatencyState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
Conductor.songPosition = swagSong.getTimeWithDiff();
|
Conductor.songPosition = swagSong.getTimeWithDiff();
|
||||||
|
|
||||||
var closestBeat:Int = Math.round(Conductor.songPosition / Conductor.crochet) % diffGrp.members.length;
|
var closestBeat:Int = Math.round(Conductor.songPosition / Conductor.beatLengthMs) % diffGrp.members.length;
|
||||||
var getDiff:Float = Conductor.songPosition - (closestBeat * Conductor.crochet);
|
var getDiff:Float = Conductor.songPosition - (closestBeat * Conductor.beatLengthMs);
|
||||||
getDiff -= Conductor.visualOffset;
|
getDiff -= Conductor.visualOffset;
|
||||||
|
|
||||||
// lil fix for end of song
|
// lil fix for end of song
|
||||||
if (closestBeat == 0 && getDiff >= Conductor.crochet * 2) getDiff -= FlxG.sound.music.length;
|
if (closestBeat == 0 && getDiff >= Conductor.beatLengthMs * 2) getDiff -= FlxG.sound.music.length;
|
||||||
|
|
||||||
trace("\tDISTANCE TO CLOSEST BEAT: " + getDiff + "ms");
|
trace("\tDISTANCE TO CLOSEST BEAT: " + getDiff + "ms");
|
||||||
trace("\tCLOSEST BEAT: " + closestBeat);
|
trace("\tCLOSEST BEAT: " + closestBeat);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package funkin;
|
package funkin;
|
||||||
|
|
||||||
|
import funkin.play.PlayStatePlaylist;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.FlxState;
|
import flixel.FlxState;
|
||||||
import flixel.math.FlxMath;
|
import flixel.math.FlxMath;
|
||||||
|
@ -32,7 +33,7 @@ class LoadingState extends MusicBeatState
|
||||||
this.stopMusic = stopMusic;
|
this.stopMusic = stopMusic;
|
||||||
}
|
}
|
||||||
|
|
||||||
override function create()
|
override function create():Void
|
||||||
{
|
{
|
||||||
var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, 0xFFcaff4d);
|
var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, 0xFFcaff4d);
|
||||||
add(bg);
|
add(bg);
|
||||||
|
@ -52,57 +53,56 @@ class LoadingState extends MusicBeatState
|
||||||
|
|
||||||
initSongsManifest().onComplete(function(lib) {
|
initSongsManifest().onComplete(function(lib) {
|
||||||
callbacks = new MultiCallback(onLoad);
|
callbacks = new MultiCallback(onLoad);
|
||||||
var introComplete = callbacks.add("introComplete");
|
var introComplete = callbacks.add('introComplete');
|
||||||
checkLoadSong(getSongPath());
|
// checkLoadSong(getSongPath());
|
||||||
if (PlayState.currentSong.needsVoices)
|
// if (PlayState.currentSong.needsVoices)
|
||||||
{
|
// {
|
||||||
var files = PlayState.currentSong.voiceList;
|
// var files = PlayState.currentSong.voiceList;
|
||||||
|
//
|
||||||
|
// if (files == null) files = ['']; // loads with no file name assumption, to load 'Voices.ogg' or whatev normally
|
||||||
|
//
|
||||||
|
// for (sndFile in files)
|
||||||
|
// {
|
||||||
|
// checkLoadSong(getVocalPath(sndFile));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if (files == null) files = [""]; // loads with no file name assumption, to load "Voices.ogg" or whatev normally
|
checkLibrary('shared');
|
||||||
|
checkLibrary(PlayStatePlaylist.campaignId);
|
||||||
|
checkLibrary('tutorial');
|
||||||
|
|
||||||
for (sndFile in files)
|
var fadeTime:Float = 0.5;
|
||||||
{
|
|
||||||
checkLoadSong(getVocalPath(sndFile));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkLibrary("shared");
|
|
||||||
if (PlayState.storyWeek > 0) checkLibrary("week" + PlayState.storyWeek);
|
|
||||||
else
|
|
||||||
checkLibrary("tutorial");
|
|
||||||
|
|
||||||
var fadeTime = 0.5;
|
|
||||||
FlxG.camera.fade(FlxG.camera.bgColor, fadeTime, true);
|
FlxG.camera.fade(FlxG.camera.bgColor, fadeTime, true);
|
||||||
new FlxTimer().start(fadeTime + MIN_TIME, function(_) introComplete());
|
new FlxTimer().start(fadeTime + MIN_TIME, function(_) introComplete());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkLoadSong(path:String)
|
function checkLoadSong(path:String):Void
|
||||||
{
|
{
|
||||||
if (!Assets.cache.hasSound(path))
|
if (!Assets.cache.hasSound(path))
|
||||||
{
|
{
|
||||||
var library = Assets.getLibrary("songs");
|
var library = Assets.getLibrary('songs');
|
||||||
var symbolPath = path.split(":").pop();
|
var symbolPath = path.split(':').pop();
|
||||||
// @:privateAccess
|
// @:privateAccess
|
||||||
// library.types.set(symbolPath, SOUND);
|
// library.types.set(symbolPath, SOUND);
|
||||||
// @:privateAccess
|
// @:privateAccess
|
||||||
// library.pathGroups.set(symbolPath, [library.__cacheBreak(symbolPath)]);
|
// library.pathGroups.set(symbolPath, [library.__cacheBreak(symbolPath)]);
|
||||||
var callback = callbacks.add("song:" + path);
|
var callback = callbacks.add('song:' + path);
|
||||||
Assets.loadSound(path).onComplete(function(_) {
|
Assets.loadSound(path).onComplete(function(_) {
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkLibrary(library:String)
|
function checkLibrary(library:String):Void
|
||||||
{
|
{
|
||||||
trace(Assets.hasLibrary(library));
|
trace(Assets.hasLibrary(library));
|
||||||
if (Assets.getLibrary(library) == null)
|
if (Assets.getLibrary(library) == null)
|
||||||
{
|
{
|
||||||
@:privateAccess
|
@:privateAccess
|
||||||
if (!LimeAssets.libraryPaths.exists(library)) throw "Missing library: " + library;
|
if (!LimeAssets.libraryPaths.exists(library)) throw 'Missing library: ' + library;
|
||||||
|
|
||||||
var callback = callbacks.add("library:" + library);
|
var callback = callbacks.add('library:' + library);
|
||||||
Assets.loadLibrary(library).onComplete(function(_) {
|
Assets.loadLibrary(library).onComplete(function(_) {
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
|
@ -121,7 +121,7 @@ class LoadingState extends MusicBeatState
|
||||||
|
|
||||||
var targetShit:Float = 0;
|
var targetShit:Float = 0;
|
||||||
|
|
||||||
override function update(elapsed:Float)
|
override function update(elapsed:Float):Void
|
||||||
{
|
{
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
|
|
||||||
|
@ -147,57 +147,41 @@ class LoadingState extends MusicBeatState
|
||||||
}
|
}
|
||||||
|
|
||||||
#if debug
|
#if debug
|
||||||
if (FlxG.keys.justPressed.SPACE) trace('fired: ' + callbacks.getFired() + " unfired:" + callbacks.getUnfired());
|
if (FlxG.keys.justPressed.SPACE) trace('fired: ' + callbacks.getFired() + ' unfired:' + callbacks.getUnfired());
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLoad()
|
function onLoad():Void
|
||||||
{
|
{
|
||||||
if (stopMusic && FlxG.sound.music != null) FlxG.sound.music.stop();
|
if (stopMusic && FlxG.sound.music != null) FlxG.sound.music.stop();
|
||||||
|
|
||||||
FlxG.switchState(target);
|
FlxG.switchState(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function getSongPath()
|
static function getSongPath():String
|
||||||
{
|
{
|
||||||
return Paths.inst(PlayState.currentSong.song);
|
return Paths.inst(PlayState.instance.currentSong.songId);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function getVocalPath(?suffix:String)
|
inline static public function loadAndSwitchState(nextState:FlxState, shouldStopMusic = false):Void
|
||||||
{
|
{
|
||||||
return Paths.voices(PlayState.currentSong.song, suffix);
|
FlxG.switchState(getNextState(nextState, shouldStopMusic));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static public function loadAndSwitchState(target:FlxState, stopMusic = false)
|
static function getNextState(nextState:FlxState, shouldStopMusic = false):FlxState
|
||||||
{
|
{
|
||||||
FlxG.switchState(getNextState(target, stopMusic));
|
Paths.setCurrentLevel(PlayStatePlaylist.campaignId);
|
||||||
}
|
|
||||||
|
|
||||||
static function getNextState(target:FlxState, stopMusic = false):FlxState
|
|
||||||
{
|
|
||||||
if (PlayState.storyWeek == 0)
|
|
||||||
{
|
|
||||||
Paths.setCurrentLevel('tutorial');
|
|
||||||
}
|
|
||||||
else if (PlayState.storyWeek == 8)
|
|
||||||
{
|
|
||||||
// TODO: Refactor this code.
|
|
||||||
Paths.setCurrentLevel("weekend1");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Paths.setCurrentLevel("week" + PlayState.storyWeek);
|
|
||||||
}
|
|
||||||
#if NO_PRELOAD_ALL
|
#if NO_PRELOAD_ALL
|
||||||
var loaded = isSoundLoaded(getSongPath())
|
// var loaded = isSoundLoaded(getSongPath())
|
||||||
&& (!PlayState.currentSong.needsVoices || isSoundLoaded(getVocalPath()))
|
// && (!PlayState.currentSong.needsVoices || isSoundLoaded(getVocalPath()))
|
||||||
&& isLibraryLoaded("shared");
|
// && isLibraryLoaded('shared');
|
||||||
|
//
|
||||||
if (!loaded) return new LoadingState(target, stopMusic);
|
if (true) return new LoadingState(nextState, shouldStopMusic);
|
||||||
#end
|
#end
|
||||||
if (stopMusic && FlxG.sound.music != null) FlxG.sound.music.stop();
|
if (shouldStopMusic && FlxG.sound.music != null) FlxG.sound.music.stop();
|
||||||
|
|
||||||
return target;
|
return nextState;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if NO_PRELOAD_ALL
|
#if NO_PRELOAD_ALL
|
||||||
|
@ -212,16 +196,16 @@ class LoadingState extends MusicBeatState
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
|
|
||||||
override function destroy()
|
override function destroy():Void
|
||||||
{
|
{
|
||||||
super.destroy();
|
super.destroy();
|
||||||
|
|
||||||
callbacks = null;
|
callbacks = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function initSongsManifest()
|
static function initSongsManifest():Future<AssetLibrary>
|
||||||
{
|
{
|
||||||
var id = "songs";
|
var id = 'songs';
|
||||||
var promise = new Promise<AssetLibrary>();
|
var promise = new Promise<AssetLibrary>();
|
||||||
|
|
||||||
var library = LimeAssets.getLibrary(id);
|
var library = LimeAssets.getLibrary(id);
|
||||||
|
@ -243,10 +227,10 @@ class LoadingState extends MusicBeatState
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (path.endsWith(".bundle"))
|
if (path.endsWith('.bundle'))
|
||||||
{
|
{
|
||||||
rootPath = path;
|
rootPath = path;
|
||||||
path += "/library.json";
|
path += '/library.json';
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -259,7 +243,7 @@ class LoadingState extends MusicBeatState
|
||||||
AssetManifest.loadFromFile(path, rootPath).onComplete(function(manifest) {
|
AssetManifest.loadFromFile(path, rootPath).onComplete(function(manifest) {
|
||||||
if (manifest == null)
|
if (manifest == null)
|
||||||
{
|
{
|
||||||
promise.error("Cannot parse asset manifest for library \"" + id + "\"");
|
promise.error('Cannot parse asset manifest for library \'' + id + '\'');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +251,7 @@ class LoadingState extends MusicBeatState
|
||||||
|
|
||||||
if (library == null)
|
if (library == null)
|
||||||
{
|
{
|
||||||
promise.error("Cannot open library \"" + id + "\"");
|
promise.error('Cannot open library \'' + id + '\'');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -277,7 +261,7 @@ class LoadingState extends MusicBeatState
|
||||||
promise.completeWith(Future.withValue(library));
|
promise.completeWith(Future.withValue(library));
|
||||||
}
|
}
|
||||||
}).onError(function(_) {
|
}).onError(function(_) {
|
||||||
promise.error("There is no asset library with an ID of \"" + id + "\"");
|
promise.error('There is no asset library with an ID of \'' + id + '\'');
|
||||||
});
|
});
|
||||||
|
|
||||||
return promise.future;
|
return promise.future;
|
||||||
|
@ -300,7 +284,7 @@ class MultiCallback
|
||||||
this.logId = logId;
|
this.logId = logId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function add(id = "untitled")
|
public function add(id = 'untitled'):Void->Void
|
||||||
{
|
{
|
||||||
id = '$length:$id';
|
id = '$length:$id';
|
||||||
length++;
|
length++;
|
||||||
|
@ -333,9 +317,9 @@ class MultiCallback
|
||||||
if (logId != null) trace('$logId: $msg');
|
if (logId != null) trace('$logId: $msg');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFired()
|
public function getFired():Array<String>
|
||||||
return fired.copy();
|
return fired.copy();
|
||||||
|
|
||||||
public function getUnfired()
|
public function getUnfired():Array<Void->Void>
|
||||||
return unfired.array();
|
return unfired.array();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package funkin;
|
package funkin;
|
||||||
|
|
||||||
|
import funkin.ui.debug.DebugMenuSubState;
|
||||||
import flixel.FlxObject;
|
import flixel.FlxObject;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.FlxState;
|
import flixel.FlxState;
|
||||||
|
@ -299,7 +300,14 @@ class MainMenuState extends MusicBeatState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlxG.camera.followLerp = CoolUtil.camLerpShit(0.06);
|
// ` / ~ to open the debug menu.
|
||||||
|
if (FlxG.keys.justPressed.GRAVEACCENT)
|
||||||
|
{
|
||||||
|
// TODO: Does this break anything?
|
||||||
|
this.persistentUpdate = false;
|
||||||
|
this.persistentDraw = false;
|
||||||
|
FlxG.state.openSubState(new DebugMenuSubState());
|
||||||
|
}
|
||||||
|
|
||||||
if (FlxG.sound.music.volume < 0.8)
|
if (FlxG.sound.music.volume < 0.8)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,7 +9,6 @@ import flixel.util.FlxSort;
|
||||||
import funkin.modding.PolymodHandler;
|
import funkin.modding.PolymodHandler;
|
||||||
import funkin.modding.events.ScriptEvent;
|
import funkin.modding.events.ScriptEvent;
|
||||||
import funkin.modding.module.ModuleHandler;
|
import funkin.modding.module.ModuleHandler;
|
||||||
import funkin.ui.debug.DebugMenuSubState;
|
|
||||||
import funkin.util.SortUtil;
|
import funkin.util.SortUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,15 +65,6 @@ class MusicBeatState extends FlxUIState
|
||||||
// This can now be used in EVERY STATE YAY!
|
// This can now be used in EVERY STATE YAY!
|
||||||
if (FlxG.keys.justPressed.F5) debug_refreshModules();
|
if (FlxG.keys.justPressed.F5) debug_refreshModules();
|
||||||
|
|
||||||
// ` / ~ to open the debug menu.
|
|
||||||
if (FlxG.keys.justPressed.GRAVEACCENT)
|
|
||||||
{
|
|
||||||
// TODO: Does this break anything?
|
|
||||||
this.persistentUpdate = false;
|
|
||||||
this.persistentDraw = false;
|
|
||||||
FlxG.state.openSubState(new DebugMenuSubState());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display Conductor info in the watch window.
|
// Display Conductor info in the watch window.
|
||||||
FlxG.watch.addQuick("songPos", Conductor.songPosition);
|
FlxG.watch.addQuick("songPos", Conductor.songPosition);
|
||||||
FlxG.watch.addQuick("currentStepTime", Conductor.currentStepTime);
|
FlxG.watch.addQuick("currentStepTime", Conductor.currentStepTime);
|
||||||
|
|
|
@ -5,62 +5,96 @@ import flixel.util.FlxColor;
|
||||||
import funkin.Conductor.BPMChangeEvent;
|
import funkin.Conductor.BPMChangeEvent;
|
||||||
import funkin.modding.events.ScriptEvent;
|
import funkin.modding.events.ScriptEvent;
|
||||||
import funkin.modding.module.ModuleHandler;
|
import funkin.modding.module.ModuleHandler;
|
||||||
|
import flixel.text.FlxText;
|
||||||
|
import funkin.modding.PolymodHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MusicBeatSubState reincorporates the functionality of MusicBeatState into an FlxSubState.
|
* MusicBeatSubState reincorporates the functionality of MusicBeatState into an FlxSubState.
|
||||||
*/
|
*/
|
||||||
class MusicBeatSubState extends FlxSubState
|
class MusicBeatSubState extends FlxSubState
|
||||||
{
|
{
|
||||||
|
public var leftWatermarkText:FlxText = null;
|
||||||
|
public var rightWatermarkText:FlxText = null;
|
||||||
|
|
||||||
public function new(bgColor:FlxColor = FlxColor.TRANSPARENT)
|
public function new(bgColor:FlxColor = FlxColor.TRANSPARENT)
|
||||||
{
|
{
|
||||||
super(bgColor);
|
super(bgColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
var curStep:Int = 0;
|
|
||||||
var curBeat:Int = 0;
|
|
||||||
var controls(get, never):Controls;
|
var controls(get, never):Controls;
|
||||||
|
|
||||||
inline function get_controls():Controls
|
inline function get_controls():Controls
|
||||||
return PlayerSettings.player1.controls;
|
return PlayerSettings.player1.controls;
|
||||||
|
|
||||||
override function update(elapsed:Float)
|
override function create():Void
|
||||||
{
|
{
|
||||||
// everyStep();
|
super.create();
|
||||||
var oldStep:Int = curStep;
|
|
||||||
|
|
||||||
updateCurStep();
|
createWatermarkText();
|
||||||
curBeat = Math.floor(curStep / 4);
|
|
||||||
|
|
||||||
if (oldStep != curStep && curStep >= 0) stepHit();
|
Conductor.beatHit.add(this.beatHit);
|
||||||
|
Conductor.stepHit.add(this.stepHit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function destroy():Void
|
||||||
|
{
|
||||||
|
super.destroy();
|
||||||
|
Conductor.beatHit.remove(this.beatHit);
|
||||||
|
Conductor.stepHit.remove(this.stepHit);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function update(elapsed:Float):Void
|
||||||
|
{
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
|
|
||||||
|
// Rebindable volume keys.
|
||||||
|
if (controls.VOLUME_MUTE) FlxG.sound.toggleMuted();
|
||||||
|
else if (controls.VOLUME_UP) FlxG.sound.changeVolume(0.1);
|
||||||
|
else if (controls.VOLUME_DOWN) FlxG.sound.changeVolume(-0.1);
|
||||||
|
|
||||||
|
// Emergency exit button.
|
||||||
|
if (FlxG.keys.justPressed.F4) FlxG.switchState(new MainMenuState());
|
||||||
|
|
||||||
|
// This can now be used in EVERY STATE YAY!
|
||||||
|
if (FlxG.keys.justPressed.F5) debug_refreshModules();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCurStep():Void
|
function debug_refreshModules()
|
||||||
{
|
{
|
||||||
var lastChange:BPMChangeEvent =
|
PolymodHandler.forceReloadAssets();
|
||||||
{
|
|
||||||
stepTime: 0,
|
|
||||||
songTime: 0,
|
|
||||||
bpm: 0
|
|
||||||
}
|
|
||||||
for (i in 0...Conductor.bpmChangeMap.length)
|
|
||||||
{
|
|
||||||
if (Conductor.songPosition > Conductor.bpmChangeMap[i].songTime) lastChange = Conductor.bpmChangeMap[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
curStep = lastChange.stepTime + Math.floor(((Conductor.songPosition - Conductor.audioOffset) - lastChange.songTime) / Conductor.stepCrochet);
|
// Restart the current state, so old data is cleared.
|
||||||
|
FlxG.resetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a step is hit in the current song.
|
||||||
|
* Continues outside of PlayState, for things like animations in menus.
|
||||||
|
* @return Whether the event should continue (not canceled).
|
||||||
|
*/
|
||||||
public function stepHit():Bool
|
public function stepHit():Bool
|
||||||
{
|
{
|
||||||
var event = new SongTimeScriptEvent(ScriptEvent.SONG_STEP_HIT, curBeat, curStep);
|
var event:ScriptEvent = new SongTimeScriptEvent(ScriptEvent.SONG_STEP_HIT, Conductor.currentBeat, Conductor.currentStep);
|
||||||
|
|
||||||
dispatchEvent(event);
|
dispatchEvent(event);
|
||||||
|
|
||||||
if (event.eventCanceled) return false;
|
if (event.eventCanceled) return false;
|
||||||
|
|
||||||
if (curStep % 4 == 0) beatHit();
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a beat is hit in the current song.
|
||||||
|
* Continues outside of PlayState, for things like animations in menus.
|
||||||
|
* @return Whether the event should continue (not canceled).
|
||||||
|
*/
|
||||||
|
public function beatHit():Bool
|
||||||
|
{
|
||||||
|
var event:ScriptEvent = new SongTimeScriptEvent(ScriptEvent.SONG_BEAT_HIT, Conductor.currentBeat, Conductor.currentStep);
|
||||||
|
|
||||||
|
dispatchEvent(event);
|
||||||
|
|
||||||
|
if (event.eventCanceled) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -70,6 +104,25 @@ class MusicBeatSubState extends FlxSubState
|
||||||
ModuleHandler.callEvent(event);
|
ModuleHandler.callEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createWatermarkText():Void
|
||||||
|
{
|
||||||
|
// Both have an xPos of 0, but a width equal to the full screen.
|
||||||
|
// The rightWatermarkText is right aligned, which puts the text in the correct spot.
|
||||||
|
leftWatermarkText = new FlxText(0, FlxG.height - 18, FlxG.width, '', 12);
|
||||||
|
rightWatermarkText = new FlxText(0, FlxG.height - 18, FlxG.width, '', 12);
|
||||||
|
|
||||||
|
// 100,000 should be good enough.
|
||||||
|
leftWatermarkText.zIndex = 100000;
|
||||||
|
rightWatermarkText.zIndex = 100000;
|
||||||
|
leftWatermarkText.scrollFactor.set(0, 0);
|
||||||
|
rightWatermarkText.scrollFactor.set(0, 0);
|
||||||
|
leftWatermarkText.setFormat('VCR OSD Mono', 16, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
|
||||||
|
rightWatermarkText.setFormat('VCR OSD Mono', 16, FlxColor.WHITE, RIGHT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
|
||||||
|
|
||||||
|
add(leftWatermarkText);
|
||||||
|
add(rightWatermarkText);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close this substate and replace it with a different one.
|
* Close this substate and replace it with a different one.
|
||||||
*/
|
*/
|
||||||
|
@ -78,15 +131,4 @@ class MusicBeatSubState extends FlxSubState
|
||||||
this.close();
|
this.close();
|
||||||
this._parentState.openSubState(substate);
|
this._parentState.openSubState(substate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function beatHit():Bool
|
|
||||||
{
|
|
||||||
var event = new SongTimeScriptEvent(ScriptEvent.SONG_BEAT_HIT, curBeat, curStep);
|
|
||||||
|
|
||||||
dispatchEvent(event);
|
|
||||||
|
|
||||||
if (event.eventCanceled) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package funkin;
|
package funkin;
|
||||||
|
|
||||||
|
import funkin.play.Strumline.StrumlineArrow;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.math.FlxMath;
|
import flixel.math.FlxMath;
|
||||||
import funkin.noteStuff.NoteBasic.NoteData;
|
import funkin.noteStuff.NoteBasic.NoteData;
|
||||||
|
@ -206,7 +207,7 @@ class Note extends FlxSprite
|
||||||
prevNote.animation.play(prevNote.colorName + 'hold');
|
prevNote.animation.play(prevNote.colorName + 'hold');
|
||||||
prevNote.updateHitbox();
|
prevNote.updateHitbox();
|
||||||
|
|
||||||
var scaleThing:Float = Math.round((Conductor.stepCrochet) * (0.45 * FlxMath.roundDecimal(SongLoad.getSpeed(), 2)));
|
var scaleThing:Float = Math.round((Conductor.stepLengthMs) * (0.45 * FlxMath.roundDecimal(PlayState.instance.currentChart.scrollSpeed, 2)));
|
||||||
// get them a LIL closer together cuz the antialiasing blurs the edges
|
// get them a LIL closer together cuz the antialiasing blurs the edges
|
||||||
if (antialiasing) scaleThing *= 1.0 + (1.0 / prevNote.frameHeight);
|
if (antialiasing) scaleThing *= 1.0 + (1.0 / prevNote.frameHeight);
|
||||||
prevNote.scale.y = scaleThing / prevNote.frameHeight;
|
prevNote.scale.y = scaleThing / prevNote.frameHeight;
|
||||||
|
@ -215,6 +216,24 @@ class Note extends FlxSprite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function alignToSturmlineArrow(arrow:StrumlineArrow):Void
|
||||||
|
{
|
||||||
|
x = arrow.x;
|
||||||
|
|
||||||
|
if (isSustainNote && prevNote != null)
|
||||||
|
{
|
||||||
|
if (prevNote.isSustainNote)
|
||||||
|
{
|
||||||
|
x = prevNote.x;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x += prevNote.width / 2;
|
||||||
|
x -= width / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override function destroy()
|
override function destroy()
|
||||||
{
|
{
|
||||||
prevNote = null;
|
prevNote = null;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package funkin;
|
package funkin;
|
||||||
|
|
||||||
|
import funkin.play.PlayStatePlaylist;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.addons.transition.FlxTransitionableState;
|
import flixel.addons.transition.FlxTransitionableState;
|
||||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||||
import flixel.sound.FlxSound;
|
import flixel.system.FlxSound;
|
||||||
import flixel.text.FlxText;
|
import flixel.text.FlxText;
|
||||||
import flixel.tweens.FlxEase;
|
import flixel.tweens.FlxEase;
|
||||||
import flixel.tweens.FlxTween;
|
import flixel.tweens.FlxTween;
|
||||||
|
@ -20,9 +21,9 @@ class PauseSubState extends MusicBeatSubState
|
||||||
'Restart Song',
|
'Restart Song',
|
||||||
'Change Difficulty',
|
'Change Difficulty',
|
||||||
'Toggle Practice Mode',
|
'Toggle Practice Mode',
|
||||||
'Exit to menu'
|
'Exit to Menu'
|
||||||
];
|
];
|
||||||
var difficultyChoices:Array<String> = ['EASY', 'NORMAL', 'HARD', 'BACK'];
|
var difficultyChoices:Array<String> = ['EASY', 'NORMAL', 'HARD', 'ERECT', 'BACK'];
|
||||||
|
|
||||||
var menuItems:Array<String> = [];
|
var menuItems:Array<String> = [];
|
||||||
var curSelected:Int = 0;
|
var curSelected:Int = 0;
|
||||||
|
@ -41,10 +42,14 @@ class PauseSubState extends MusicBeatSubState
|
||||||
|
|
||||||
menuItems = pauseOG;
|
menuItems = pauseOG;
|
||||||
|
|
||||||
if (PlayState.storyWeek == 6) // consistent with logic that decides asset lib!!
|
if (PlayStatePlaylist.campaignId == 'week6')
|
||||||
|
{
|
||||||
pauseMusic = new FlxSound().loadEmbedded(Paths.music('breakfast-pixel'), true, true);
|
pauseMusic = new FlxSound().loadEmbedded(Paths.music('breakfast-pixel'), true, true);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
pauseMusic = new FlxSound().loadEmbedded(Paths.music('breakfast'), true, true);
|
pauseMusic = new FlxSound().loadEmbedded(Paths.music('breakfast'), true, true);
|
||||||
|
}
|
||||||
pauseMusic.volume = 0;
|
pauseMusic.volume = 0;
|
||||||
pauseMusic.play(false, FlxG.random.int(0, Std.int(pauseMusic.length / 2)));
|
pauseMusic.play(false, FlxG.random.int(0, Std.int(pauseMusic.length / 2)));
|
||||||
|
|
||||||
|
@ -58,43 +63,38 @@ class PauseSubState extends MusicBeatSubState
|
||||||
metaDataGrp = new FlxTypedGroup<FlxSprite>();
|
metaDataGrp = new FlxTypedGroup<FlxSprite>();
|
||||||
add(metaDataGrp);
|
add(metaDataGrp);
|
||||||
|
|
||||||
var levelInfo:FlxText = new FlxText(20, 15, 0, "", 32);
|
var levelInfo:FlxText = new FlxText(20, 15, 0, '', 32);
|
||||||
if (PlayState.instance.currentChart != null)
|
if (PlayState.instance.currentChart != null)
|
||||||
{
|
{
|
||||||
levelInfo.text += '${PlayState.instance.currentChart.songName} - ${PlayState.instance.currentChart.songArtist}';
|
levelInfo.text += '${PlayState.instance.currentChart.songName} - ${PlayState.instance.currentChart.songArtist}';
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
levelInfo.text += PlayState.currentSong.song;
|
|
||||||
}
|
|
||||||
levelInfo.scrollFactor.set();
|
levelInfo.scrollFactor.set();
|
||||||
levelInfo.setFormat(Paths.font("vcr.ttf"), 32);
|
levelInfo.setFormat(Paths.font('vcr.ttf'), 32);
|
||||||
levelInfo.updateHitbox();
|
levelInfo.updateHitbox();
|
||||||
metaDataGrp.add(levelInfo);
|
metaDataGrp.add(levelInfo);
|
||||||
|
|
||||||
var levelDifficulty:FlxText = new FlxText(20, 15 + 32, 0, "", 32);
|
var levelDifficulty:FlxText = new FlxText(20, 15 + 32, 0, '', 32);
|
||||||
levelDifficulty.text += CoolUtil.difficultyString();
|
levelDifficulty.text += PlayState.instance.currentDifficulty.toTitleCase();
|
||||||
levelDifficulty.scrollFactor.set();
|
levelDifficulty.scrollFactor.set();
|
||||||
levelDifficulty.setFormat(Paths.font('vcr.ttf'), 32);
|
levelDifficulty.setFormat(Paths.font('vcr.ttf'), 32);
|
||||||
levelDifficulty.updateHitbox();
|
levelDifficulty.updateHitbox();
|
||||||
metaDataGrp.add(levelDifficulty);
|
metaDataGrp.add(levelDifficulty);
|
||||||
|
|
||||||
var deathCounter:FlxText = new FlxText(20, 15 + 64, 0, "", 32);
|
var deathCounter:FlxText = new FlxText(20, 15 + 64, 0, '', 32);
|
||||||
deathCounter.text = "Blue balled: " + PlayState.deathCounter;
|
deathCounter.text = 'Blue balled: ${PlayState.instance.deathCounter}';
|
||||||
deathCounter.text += "\n" + Highscore.tallies.totalNotesHit;
|
FlxG.watch.addQuick('totalNotesHit', Highscore.tallies.totalNotesHit);
|
||||||
deathCounter.text += "\n" + Highscore.tallies.totalNotes;
|
FlxG.watch.addQuick('totalNotes', Highscore.tallies.totalNotes);
|
||||||
deathCounter.text += "\n" + Std.string(Highscore.tallies.totalNotesHit / Highscore.tallies.totalNotes);
|
|
||||||
deathCounter.scrollFactor.set();
|
deathCounter.scrollFactor.set();
|
||||||
deathCounter.setFormat(Paths.font('vcr.ttf'), 32);
|
deathCounter.setFormat(Paths.font('vcr.ttf'), 32);
|
||||||
deathCounter.updateHitbox();
|
deathCounter.updateHitbox();
|
||||||
metaDataGrp.add(deathCounter);
|
metaDataGrp.add(deathCounter);
|
||||||
|
|
||||||
practiceText = new FlxText(20, 15 + 64 + 32, 0, "PRACTICE MODE", 32);
|
practiceText = new FlxText(20, 15 + 64 + 32, 0, 'PRACTICE MODE', 32);
|
||||||
practiceText.scrollFactor.set();
|
practiceText.scrollFactor.set();
|
||||||
practiceText.setFormat(Paths.font('vcr.ttf'), 32);
|
practiceText.setFormat(Paths.font('vcr.ttf'), 32);
|
||||||
practiceText.updateHitbox();
|
practiceText.updateHitbox();
|
||||||
practiceText.x = FlxG.width - (practiceText.width + 20);
|
practiceText.x = FlxG.width - (practiceText.width + 20);
|
||||||
practiceText.visible = PlayState.isPracticeMode;
|
practiceText.visible = PlayState.instance.isPracticeMode;
|
||||||
metaDataGrp.add(practiceText);
|
metaDataGrp.add(practiceText);
|
||||||
|
|
||||||
levelDifficulty.alpha = 0;
|
levelDifficulty.alpha = 0;
|
||||||
|
@ -137,7 +137,7 @@ class PauseSubState extends MusicBeatSubState
|
||||||
changeSelection();
|
changeSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
override function update(elapsed:Float)
|
override function update(elapsed:Float):Void
|
||||||
{
|
{
|
||||||
if (pauseMusic.volume < 0.5) pauseMusic.volume += 0.01 * elapsed;
|
if (pauseMusic.volume < 0.5) pauseMusic.volume += 0.01 * elapsed;
|
||||||
|
|
||||||
|
@ -180,41 +180,39 @@ class PauseSubState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
var daSelected:String = menuItems[curSelected];
|
var daSelected:String = menuItems[curSelected];
|
||||||
|
|
||||||
|
// TODO: Why is this based on the menu item's name? Make this an enum or something.
|
||||||
switch (daSelected)
|
switch (daSelected)
|
||||||
{
|
{
|
||||||
case "Resume":
|
case 'Resume':
|
||||||
close();
|
close();
|
||||||
case "EASY" | 'NORMAL' | "HARD":
|
|
||||||
PlayState.currentSong = SongLoad.loadFromJson(PlayState.currentSong.song.toLowerCase(), PlayState.currentSong.song.toLowerCase());
|
|
||||||
PlayState.currentSong_NEW = SongDataParser.fetchSong(PlayState.currentSong.song.toLowerCase());
|
|
||||||
SongLoad.curDiff = daSelected.toLowerCase();
|
|
||||||
|
|
||||||
PlayState.storyDifficulty = curSelected;
|
|
||||||
PlayState.storyDifficulty_NEW = daSelected.toLowerCase();
|
|
||||||
|
|
||||||
PlayState.needsReset = true;
|
|
||||||
|
|
||||||
close();
|
|
||||||
|
|
||||||
case 'Toggle Practice Mode':
|
|
||||||
PlayState.isPracticeMode = !PlayState.isPracticeMode;
|
|
||||||
practiceText.visible = PlayState.isPracticeMode;
|
|
||||||
|
|
||||||
case 'Change Difficulty':
|
case 'Change Difficulty':
|
||||||
menuItems = difficultyChoices;
|
menuItems = difficultyChoices;
|
||||||
regenMenu();
|
regenMenu();
|
||||||
|
|
||||||
|
case 'EASY' | 'NORMAL' | 'HARD' | 'ERECT':
|
||||||
|
PlayState.instance.currentSong = SongDataParser.fetchSong(PlayState.instance.currentSong.songId.toLowerCase());
|
||||||
|
|
||||||
|
PlayState.instance.currentDifficulty = daSelected.toLowerCase();
|
||||||
|
|
||||||
|
PlayState.instance.needsReset = true;
|
||||||
|
|
||||||
|
close();
|
||||||
case 'BACK':
|
case 'BACK':
|
||||||
menuItems = pauseOG;
|
menuItems = pauseOG;
|
||||||
regenMenu();
|
regenMenu();
|
||||||
case "Restart Song":
|
|
||||||
PlayState.needsReset = true;
|
|
||||||
|
|
||||||
|
case 'Toggle Practice Mode':
|
||||||
|
PlayState.instance.isPracticeMode = true;
|
||||||
|
practiceText.visible = PlayState.instance.isPracticeMode;
|
||||||
|
|
||||||
|
case 'Restart Song':
|
||||||
|
PlayState.instance.needsReset = true;
|
||||||
close();
|
close();
|
||||||
// FlxG.resetState();
|
|
||||||
case "Exit to menu":
|
case 'Exit to Menu':
|
||||||
exitingToMenu = true;
|
exitingToMenu = true;
|
||||||
PlayState.seenCutscene = false;
|
PlayState.instance.deathCounter = 0;
|
||||||
PlayState.deathCounter = 0;
|
|
||||||
|
|
||||||
for (item in grpMenuShit.members)
|
for (item in grpMenuShit.members)
|
||||||
{
|
{
|
||||||
|
@ -225,9 +223,9 @@ class PauseSubState extends MusicBeatSubState
|
||||||
FlxTransitionableState.skipNextTransIn = true;
|
FlxTransitionableState.skipNextTransIn = true;
|
||||||
FlxTransitionableState.skipNextTransOut = true;
|
FlxTransitionableState.skipNextTransOut = true;
|
||||||
|
|
||||||
if (PlayState.isStoryMode) openSubState(new funkin.ui.StickerSubState(null, STORY));
|
if (PlayStatePlaylist.isStoryMode) openSubState(new funkin.ui.StickerSubState(null, STORY));
|
||||||
else
|
else
|
||||||
openSubState(new funkin.ui.StickerSubState());
|
openSubState(new funkin.ui.StickerSubState(null, FREEPLAY));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +237,7 @@ class PauseSubState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override function destroy()
|
override function destroy():Void
|
||||||
{
|
{
|
||||||
pauseMusic.destroy();
|
pauseMusic.destroy();
|
||||||
|
|
||||||
|
@ -260,12 +258,10 @@ class PauseSubState extends MusicBeatSubState
|
||||||
item.targetY = index - curSelected;
|
item.targetY = index - curSelected;
|
||||||
|
|
||||||
item.alpha = 0.6;
|
item.alpha = 0.6;
|
||||||
// item.setGraphicSize(Std.int(item.width * 0.8));
|
|
||||||
|
|
||||||
if (item.targetY == 0)
|
if (item.targetY == 0)
|
||||||
{
|
{
|
||||||
item.alpha = 1;
|
item.alpha = 1;
|
||||||
// item.setGraphicSize(Std.int(item.width));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,497 +0,0 @@
|
||||||
package funkin;
|
|
||||||
|
|
||||||
import flixel.FlxSprite;
|
|
||||||
import flixel.addons.transition.FlxTransitionableState;
|
|
||||||
import flixel.graphics.frames.FlxAtlasFrames;
|
|
||||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
|
||||||
import flixel.group.FlxGroup;
|
|
||||||
import flixel.math.FlxMath;
|
|
||||||
import flixel.text.FlxText;
|
|
||||||
import flixel.tweens.FlxTween;
|
|
||||||
import flixel.util.FlxColor;
|
|
||||||
import flixel.util.FlxTimer;
|
|
||||||
import funkin.MenuItem.WeekType;
|
|
||||||
import funkin.play.PlayState;
|
|
||||||
import funkin.play.song.SongData.SongDataParser;
|
|
||||||
import lime.net.curl.CURLCode;
|
|
||||||
import openfl.Assets;
|
|
||||||
import funkin.ui.StickerSubState;
|
|
||||||
#if discord_rpc
|
|
||||||
import Discord.DiscordClient;
|
|
||||||
#end
|
|
||||||
|
|
||||||
class StoryMenuState extends MusicBeatState
|
|
||||||
{
|
|
||||||
var scoreText:FlxText;
|
|
||||||
|
|
||||||
var weekData:Array<Array<String>> = [
|
|
||||||
['Tutorial'],
|
|
||||||
['Bopeebo', 'Fresh', 'Dadbattle'],
|
|
||||||
['Spookeez', 'South', "Monster"],
|
|
||||||
['Pico', 'Philly', "Blammed"],
|
|
||||||
['Satin-Panties', "High", "Milf"],
|
|
||||||
['Cocoa', 'Eggnog', 'Winter-Horrorland'],
|
|
||||||
['Senpai', 'Roses', 'Thorns'],
|
|
||||||
['Ugh', 'Guns', 'Stress'],
|
|
||||||
['Darnell', "lit-up", "2hot", "blazin"]
|
|
||||||
];
|
|
||||||
var curDifficulty:Int = 1;
|
|
||||||
|
|
||||||
// TODO: This info is just hardcoded right now.
|
|
||||||
// We should probably make it so that weeks must be completed in order to unlock the next week.
|
|
||||||
public static var weekUnlocked:Array<Bool> = [true, true, true, true, true, true, true, true, true];
|
|
||||||
|
|
||||||
var weekCharacters:Array<Dynamic> = [
|
|
||||||
['dad', 'bf', 'gf'],
|
|
||||||
['dad', 'bf', 'gf'],
|
|
||||||
['spooky', 'bf', 'gf'],
|
|
||||||
['pico', 'bf', 'gf'],
|
|
||||||
['mom', 'bf', 'gf'],
|
|
||||||
['parents-christmas', 'bf', 'gf'],
|
|
||||||
['senpai', 'bf', 'gf'],
|
|
||||||
['tankman', 'bf', 'gf'],
|
|
||||||
['darnell', 'pico', 'nene']
|
|
||||||
];
|
|
||||||
|
|
||||||
var weekNames:Array<String> = [
|
|
||||||
"",
|
|
||||||
"Daddy Dearest",
|
|
||||||
"Spooky Month",
|
|
||||||
"PICO",
|
|
||||||
"MOMMY MUST MURDER",
|
|
||||||
"RED SNOW",
|
|
||||||
"hating simulator ft. moawling",
|
|
||||||
"TANKMAN",
|
|
||||||
"Due Debts"
|
|
||||||
];
|
|
||||||
|
|
||||||
var weekType:Array<WeekType> = [WEEK, WEEK, WEEK, WEEK, WEEK, WEEK, WEEK, WEEK, WEEKEND];
|
|
||||||
var weekTypeInc:Map<WeekType, Int> = new Map();
|
|
||||||
|
|
||||||
var txtWeekTitle:FlxText;
|
|
||||||
|
|
||||||
var curWeek:Int = 0;
|
|
||||||
|
|
||||||
var txtTracklist:FlxText;
|
|
||||||
|
|
||||||
var grpWeekText:FlxTypedGroup<MenuItem>;
|
|
||||||
var grpWeekCharacters:Array<FlxTypedGroup<MenuCharacter>>;
|
|
||||||
|
|
||||||
// var grpWeekCharacters:FlxTypedGroup<MenuCharacter>;
|
|
||||||
var grpLocks:FlxTypedGroup<FlxSprite>;
|
|
||||||
|
|
||||||
var difficultySelectors:FlxGroup;
|
|
||||||
var sprDifficulty:FlxSprite;
|
|
||||||
var leftArrow:FlxSprite;
|
|
||||||
var rightArrow:FlxSprite;
|
|
||||||
var yellowBG:FlxSprite; // not actually, yellow, lol!
|
|
||||||
var targetColor:Int = 0xFFF9CF51;
|
|
||||||
|
|
||||||
var stickerSubState:StickerSubState;
|
|
||||||
|
|
||||||
public function new(?stickers:StickerSubState = null)
|
|
||||||
{
|
|
||||||
if (stickers != null)
|
|
||||||
{
|
|
||||||
stickerSubState = stickers;
|
|
||||||
}
|
|
||||||
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
override function create()
|
|
||||||
{
|
|
||||||
transIn = FlxTransitionableState.defaultTransIn;
|
|
||||||
transOut = FlxTransitionableState.defaultTransOut;
|
|
||||||
|
|
||||||
if (FlxG.sound.music != null)
|
|
||||||
{
|
|
||||||
if (!FlxG.sound.music.playing) FlxG.sound.playMusic(Paths.music('freakyMenu'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stickerSubState != null)
|
|
||||||
{
|
|
||||||
this.persistentUpdate = true;
|
|
||||||
this.persistentDraw = true;
|
|
||||||
|
|
||||||
openSubState(stickerSubState);
|
|
||||||
stickerSubState.degenStickers();
|
|
||||||
|
|
||||||
// resetSubState();
|
|
||||||
}
|
|
||||||
|
|
||||||
persistentUpdate = persistentDraw = true;
|
|
||||||
|
|
||||||
scoreText = new FlxText(10, 10, 0, "SCORE: 49324858");
|
|
||||||
scoreText.setFormat("VCR OSD Mono", 32);
|
|
||||||
|
|
||||||
txtWeekTitle = new FlxText(FlxG.width * 0.7, 10, 0, "");
|
|
||||||
txtWeekTitle.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, RIGHT);
|
|
||||||
txtWeekTitle.alpha = 0.7;
|
|
||||||
|
|
||||||
var rankText:FlxText = new FlxText(0, 10);
|
|
||||||
rankText.text = 'RANK: GREAT';
|
|
||||||
rankText.setFormat(Paths.font("vcr.ttf"), 32);
|
|
||||||
rankText.size = scoreText.size;
|
|
||||||
rankText.screenCenter(X);
|
|
||||||
|
|
||||||
var ui_tex = Paths.getSparrowAtlas('campaign_menu_UI_assets');
|
|
||||||
yellowBG = new FlxSprite(0, 56).makeGraphic(FlxG.width, 400, FlxColor.WHITE);
|
|
||||||
yellowBG.color = 0xFFF9CF51;
|
|
||||||
// 0xFF413CAE blue
|
|
||||||
// 0xFFF9CF51 yello
|
|
||||||
|
|
||||||
grpWeekText = new FlxTypedGroup<MenuItem>();
|
|
||||||
add(grpWeekText);
|
|
||||||
|
|
||||||
var blackBarThingie:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, 56, FlxColor.BLACK);
|
|
||||||
add(blackBarThingie);
|
|
||||||
|
|
||||||
// grpWeekCharacters = new FlxTypedGroup<MenuCharacter>();
|
|
||||||
grpWeekCharacters = [];
|
|
||||||
|
|
||||||
grpLocks = new FlxTypedGroup<FlxSprite>();
|
|
||||||
add(grpLocks);
|
|
||||||
|
|
||||||
#if discord_rpc
|
|
||||||
// Updating Discord Rich Presence
|
|
||||||
DiscordClient.changePresence("In the Menus", null);
|
|
||||||
#end
|
|
||||||
|
|
||||||
for (i in 0...weekData.length)
|
|
||||||
{
|
|
||||||
if (!weekTypeInc.exists(weekType[i])) weekTypeInc[weekType[i]] = 1;
|
|
||||||
|
|
||||||
if (i == 0 && weekType[i] == WEEK) weekTypeInc[weekType[i]] = 0; // set week to 0 by default?
|
|
||||||
|
|
||||||
var weekThing:MenuItem = new MenuItem(0, yellowBG.y + yellowBG.height + 10, weekTypeInc[weekType[i]], weekType[i]);
|
|
||||||
weekThing.y += ((weekThing.height + 20) * i);
|
|
||||||
weekThing.targetY = i;
|
|
||||||
grpWeekText.add(weekThing);
|
|
||||||
|
|
||||||
weekTypeInc[weekType[i]] += 1;
|
|
||||||
|
|
||||||
weekThing.screenCenter(X);
|
|
||||||
weekThing.antialiasing = true;
|
|
||||||
// weekThing.updateHitbox();
|
|
||||||
|
|
||||||
// Needs an offset thingie
|
|
||||||
if (!weekUnlocked[i])
|
|
||||||
{
|
|
||||||
var lock:FlxSprite = new FlxSprite(weekThing.width + 10 + weekThing.x);
|
|
||||||
lock.frames = ui_tex;
|
|
||||||
lock.animation.addByPrefix('lock', 'lock');
|
|
||||||
lock.animation.play('lock');
|
|
||||||
lock.ID = i;
|
|
||||||
lock.antialiasing = true;
|
|
||||||
grpLocks.add(lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var sizeChart:Map<String, Array<Float>> = new Map();
|
|
||||||
|
|
||||||
var sizeTxt:Array<String> = Assets.getText(Paths.file("data/storychardata.txt")).split("\n");
|
|
||||||
|
|
||||||
for (item in sizeTxt)
|
|
||||||
{
|
|
||||||
var items:Array<String> = item.split(" ");
|
|
||||||
|
|
||||||
var stuf:Array<Float> = [];
|
|
||||||
var name:String = items.shift();
|
|
||||||
|
|
||||||
for (num in items)
|
|
||||||
stuf.push(Std.parseFloat(num));
|
|
||||||
|
|
||||||
sizeChart.set(name, stuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (index => week in weekCharacters)
|
|
||||||
{
|
|
||||||
grpWeekCharacters.push(new FlxTypedGroup<MenuCharacter>());
|
|
||||||
|
|
||||||
for (char in 0...week.length)
|
|
||||||
{
|
|
||||||
var weekCharacterThing:MenuCharacter = new MenuCharacter((FlxG.width * 0.25) * (1 + char) - 150, weekCharacters[index][char]);
|
|
||||||
weekCharacterThing.y += 70;
|
|
||||||
weekCharacterThing.antialiasing = true;
|
|
||||||
|
|
||||||
var size:Float = 0.9;
|
|
||||||
|
|
||||||
switch (char)
|
|
||||||
{
|
|
||||||
case 0 | 2:
|
|
||||||
size = 0.5;
|
|
||||||
if (char == 0 && weekCharacterThing.character == "pico") weekCharacterThing.flipX = true;
|
|
||||||
case 1:
|
|
||||||
size = 0.9;
|
|
||||||
weekCharacterThing.x -= 80;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sizeChart.exists(weekCharacterThing.character))
|
|
||||||
{
|
|
||||||
var nums:Array<Float> = sizeChart[weekCharacterThing.character];
|
|
||||||
size = nums[char];
|
|
||||||
|
|
||||||
// IDK, this might be busted ass null shit?
|
|
||||||
if (char != 1)
|
|
||||||
{
|
|
||||||
weekCharacterThing.x += nums[3];
|
|
||||||
weekCharacterThing.y += nums[4];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
weekCharacterThing.setGraphicSize(Std.int(weekCharacterThing.width * size));
|
|
||||||
weekCharacterThing.updateHitbox();
|
|
||||||
|
|
||||||
grpWeekCharacters[index].add(weekCharacterThing);
|
|
||||||
trace("ADD CHARACTER");
|
|
||||||
}
|
|
||||||
|
|
||||||
trace(grpWeekCharacters[index].toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
difficultySelectors = new FlxGroup();
|
|
||||||
add(difficultySelectors);
|
|
||||||
|
|
||||||
leftArrow = new FlxSprite(grpWeekText.members[0].x + grpWeekText.members[0].width + 10, grpWeekText.members[0].y + 10);
|
|
||||||
leftArrow.frames = ui_tex;
|
|
||||||
leftArrow.animation.addByPrefix('idle', "arrow left");
|
|
||||||
leftArrow.animation.addByPrefix('press', "arrow push left");
|
|
||||||
leftArrow.animation.play('idle');
|
|
||||||
difficultySelectors.add(leftArrow);
|
|
||||||
|
|
||||||
sprDifficulty = new FlxSprite(leftArrow.x + 130, leftArrow.y);
|
|
||||||
sprDifficulty.frames = ui_tex;
|
|
||||||
sprDifficulty.animation.addByPrefix('easy', 'EASY');
|
|
||||||
sprDifficulty.animation.addByPrefix('normal', 'NORMAL');
|
|
||||||
sprDifficulty.animation.addByPrefix('hard', 'HARD');
|
|
||||||
sprDifficulty.animation.play('easy');
|
|
||||||
changeDifficulty();
|
|
||||||
|
|
||||||
difficultySelectors.add(sprDifficulty);
|
|
||||||
|
|
||||||
rightArrow = new FlxSprite(sprDifficulty.x + sprDifficulty.width + 50, leftArrow.y);
|
|
||||||
rightArrow.frames = ui_tex;
|
|
||||||
rightArrow.animation.addByPrefix('idle', 'arrow right');
|
|
||||||
rightArrow.animation.addByPrefix('press', "arrow push right", 24, false);
|
|
||||||
rightArrow.animation.play('idle');
|
|
||||||
difficultySelectors.add(rightArrow);
|
|
||||||
|
|
||||||
add(yellowBG);
|
|
||||||
for (grp in grpWeekCharacters)
|
|
||||||
{
|
|
||||||
add(grp);
|
|
||||||
// trace("ADDED GRP");
|
|
||||||
}
|
|
||||||
|
|
||||||
// add(grpWeekCharacters);
|
|
||||||
|
|
||||||
txtTracklist = new FlxText(FlxG.width * 0.05, yellowBG.x + yellowBG.height + 100, 0, "Tracks", 32);
|
|
||||||
txtTracklist.alignment = CENTER;
|
|
||||||
txtTracklist.font = rankText.font;
|
|
||||||
txtTracklist.color = 0xFFe55777;
|
|
||||||
add(txtTracklist);
|
|
||||||
// add(rankText);
|
|
||||||
add(scoreText);
|
|
||||||
add(txtWeekTitle);
|
|
||||||
|
|
||||||
updateText();
|
|
||||||
|
|
||||||
super.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
override function update(elapsed:Float)
|
|
||||||
{
|
|
||||||
// scoreText.setFormat('VCR OSD Mono', 32);
|
|
||||||
|
|
||||||
yellowBG.color = FlxColor.interpolate(yellowBG.color, targetColor, 0.06);
|
|
||||||
|
|
||||||
lerpScore = CoolUtil.coolLerp(lerpScore, intendedScore, 0.5);
|
|
||||||
|
|
||||||
scoreText.text = "WEEK SCORE:" + Math.round(lerpScore);
|
|
||||||
|
|
||||||
txtWeekTitle.text = weekNames[curWeek].toUpperCase();
|
|
||||||
txtWeekTitle.x = FlxG.width - (txtWeekTitle.width + 10);
|
|
||||||
|
|
||||||
// FlxG.watch.addQuick('font', scoreText.font);
|
|
||||||
|
|
||||||
difficultySelectors.visible = weekUnlocked[curWeek];
|
|
||||||
|
|
||||||
grpLocks.forEach(function(lock:FlxSprite) {
|
|
||||||
lock.y = grpWeekText.members[lock.ID].y;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!movedBack)
|
|
||||||
{
|
|
||||||
if (!selectedWeek)
|
|
||||||
{
|
|
||||||
if (controls.UI_UP_P)
|
|
||||||
{
|
|
||||||
changeWeek(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (controls.UI_DOWN_P)
|
|
||||||
{
|
|
||||||
changeWeek(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (controls.UI_RIGHT) rightArrow.animation.play('press')
|
|
||||||
else
|
|
||||||
rightArrow.animation.play('idle');
|
|
||||||
|
|
||||||
if (controls.UI_LEFT) leftArrow.animation.play('press');
|
|
||||||
else
|
|
||||||
leftArrow.animation.play('idle');
|
|
||||||
|
|
||||||
if (controls.UI_RIGHT_P) changeDifficulty(1);
|
|
||||||
if (controls.UI_LEFT_P) changeDifficulty(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (controls.ACCEPT)
|
|
||||||
{
|
|
||||||
selectWeek();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (controls.BACK && !movedBack && !selectedWeek)
|
|
||||||
{
|
|
||||||
FlxG.sound.play(Paths.sound('cancelMenu'));
|
|
||||||
movedBack = true;
|
|
||||||
FlxG.switchState(new MainMenuState());
|
|
||||||
}
|
|
||||||
|
|
||||||
super.update(elapsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
var movedBack:Bool = false;
|
|
||||||
var selectedWeek:Bool = false;
|
|
||||||
var stopspamming:Bool = false;
|
|
||||||
|
|
||||||
function selectWeek()
|
|
||||||
{
|
|
||||||
if (weekUnlocked[curWeek])
|
|
||||||
{
|
|
||||||
if (stopspamming == false)
|
|
||||||
{
|
|
||||||
FlxG.sound.play(Paths.sound('confirmMenu'));
|
|
||||||
|
|
||||||
grpWeekText.members[curWeek].startFlashing();
|
|
||||||
grpWeekCharacters[curWeek].members[1].animation.play('bfConfirm');
|
|
||||||
stopspamming = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayState.storyPlaylist = weekData[curWeek];
|
|
||||||
PlayState.isStoryMode = true;
|
|
||||||
selectedWeek = true;
|
|
||||||
|
|
||||||
PlayState.currentSong = SongLoad.loadFromJson(PlayState.storyPlaylist[0].toLowerCase(), PlayState.storyPlaylist[0].toLowerCase());
|
|
||||||
PlayState.currentSong_NEW = SongDataParser.fetchSong(PlayState.storyPlaylist[0].toLowerCase());
|
|
||||||
PlayState.storyWeek = curWeek;
|
|
||||||
PlayState.campaignScore = 0;
|
|
||||||
|
|
||||||
PlayState.storyDifficulty = curDifficulty;
|
|
||||||
PlayState.storyDifficulty_NEW = switch (curDifficulty)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
'easy';
|
|
||||||
case 1:
|
|
||||||
'normal';
|
|
||||||
case 2:
|
|
||||||
'hard';
|
|
||||||
default:
|
|
||||||
'normal';
|
|
||||||
};
|
|
||||||
SongLoad.curDiff = PlayState.storyDifficulty_NEW;
|
|
||||||
|
|
||||||
new FlxTimer().start(1, function(tmr:FlxTimer) {
|
|
||||||
LoadingState.loadAndSwitchState(new PlayState(), true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeDifficulty(change:Int = 0):Void
|
|
||||||
{
|
|
||||||
curDifficulty += change;
|
|
||||||
|
|
||||||
if (curDifficulty < 0) curDifficulty = 2;
|
|
||||||
if (curDifficulty > 2) curDifficulty = 0;
|
|
||||||
|
|
||||||
sprDifficulty.offset.x = 0;
|
|
||||||
|
|
||||||
switch (curDifficulty)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
sprDifficulty.animation.play('easy');
|
|
||||||
sprDifficulty.offset.x = 20;
|
|
||||||
case 1:
|
|
||||||
sprDifficulty.animation.play('normal');
|
|
||||||
sprDifficulty.offset.x = 70;
|
|
||||||
case 2:
|
|
||||||
sprDifficulty.animation.play('hard');
|
|
||||||
sprDifficulty.offset.x = 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
sprDifficulty.alpha = 0;
|
|
||||||
|
|
||||||
// USING THESE WEIRD VALUES SO THAT IT DOESNT FLOAT UP
|
|
||||||
sprDifficulty.y = leftArrow.y - 15;
|
|
||||||
intendedScore = Highscore.getWeekScore(curWeek, curDifficulty);
|
|
||||||
|
|
||||||
FlxTween.tween(sprDifficulty, {y: leftArrow.y + 15, alpha: 1}, 0.07);
|
|
||||||
}
|
|
||||||
|
|
||||||
var lerpScore:Float = 0;
|
|
||||||
var intendedScore:Int = 0;
|
|
||||||
|
|
||||||
function changeWeek(change:Int = 0):Void
|
|
||||||
{
|
|
||||||
curWeek += change;
|
|
||||||
|
|
||||||
if (curWeek >= weekData.length) curWeek = 0;
|
|
||||||
if (curWeek < 0) curWeek = weekData.length - 1;
|
|
||||||
|
|
||||||
var bullShit:Int = 0;
|
|
||||||
|
|
||||||
for (item in grpWeekText.members)
|
|
||||||
{
|
|
||||||
item.targetY = bullShit - curWeek;
|
|
||||||
if (item.targetY == Std.int(0) && weekUnlocked[curWeek]) item.alpha = 1;
|
|
||||||
else
|
|
||||||
item.alpha = 0.6;
|
|
||||||
bullShit++;
|
|
||||||
}
|
|
||||||
|
|
||||||
FlxG.sound.play(Paths.sound('scrollMenu'));
|
|
||||||
|
|
||||||
updateText();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateText()
|
|
||||||
{
|
|
||||||
switch (weekType[curWeek])
|
|
||||||
{
|
|
||||||
case WEEK:
|
|
||||||
targetColor = 0xFFF9CF51;
|
|
||||||
case WEEKEND:
|
|
||||||
targetColor = 0xFF413CAE;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ind => grp in grpWeekCharacters)
|
|
||||||
grp.visible = ind == curWeek;
|
|
||||||
|
|
||||||
txtTracklist.text = "Tracks\n";
|
|
||||||
|
|
||||||
var trackNames:Array<String> = weekData[curWeek];
|
|
||||||
for (i in trackNames)
|
|
||||||
{
|
|
||||||
txtTracklist.text += '\n${i}';
|
|
||||||
}
|
|
||||||
|
|
||||||
txtTracklist.text = txtTracklist.text.toUpperCase();
|
|
||||||
|
|
||||||
txtTracklist.screenCenter(X);
|
|
||||||
txtTracklist.x -= FlxG.width * 0.35;
|
|
||||||
|
|
||||||
intendedScore = Highscore.getWeekScore(curWeek, curDifficulty);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -283,44 +283,6 @@ class TitleState extends MusicBeatState
|
||||||
FlxTween.tween(FlxG.stage.window, {y: FlxG.stage.window.y + 100}, 0.7, {ease: FlxEase.quadInOut, type: PINGPONG});
|
FlxTween.tween(FlxG.stage.window, {y: FlxG.stage.window.y + 100}, 0.7, {ease: FlxEase.quadInOut, type: PINGPONG});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
FlxG.watch.addQuick('cur display', FlxG.stage.window.display.id);
|
|
||||||
if (FlxG.keys.justPressed.Y)
|
|
||||||
{
|
|
||||||
// trace(FlxG.stage.window.display.name);
|
|
||||||
|
|
||||||
if (FlxG.gamepads.firstActive != null)
|
|
||||||
{
|
|
||||||
trace(FlxG.gamepads.firstActive.model);
|
|
||||||
FlxG.gamepads.firstActive.id
|
|
||||||
}
|
|
||||||
else
|
|
||||||
trace('gamepad null');
|
|
||||||
|
|
||||||
// FlxG.stage.window.title = Std.string(FlxG.random.int(0, 20000));
|
|
||||||
// FlxG.stage.window.setIcon(Image.fromFile('assets/images/icon16.png'));
|
|
||||||
// FlxG.stage.window.readPixels;
|
|
||||||
|
|
||||||
if (FlxG.stage.window.width == Std.int(FlxG.stage.window.display.bounds.width))
|
|
||||||
{
|
|
||||||
FlxG.stage.window.width = 1280;
|
|
||||||
FlxG.stage.window.height = 720;
|
|
||||||
FlxG.stage.window.y = 30;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FlxG.stage.window.width = Std.int(FlxG.stage.window.display.bounds.width);
|
|
||||||
FlxG.stage.window.height = Std.int(FlxG.stage.window.display.bounds.height);
|
|
||||||
FlxG.stage.window.x = Std.int(FlxG.stage.window.display.bounds.x);
|
|
||||||
FlxG.stage.window.y = Std.int(FlxG.stage.window.display.bounds.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if debug
|
|
||||||
if (FlxG.keys.justPressed.EIGHT) FlxG.switchState(new CutsceneAnimTestState());
|
|
||||||
#end
|
|
||||||
|
|
||||||
if (FlxG.sound.music != null) Conductor.songPosition = FlxG.sound.music.time;
|
if (FlxG.sound.music != null) Conductor.songPosition = FlxG.sound.music.time;
|
||||||
if (FlxG.keys.justPressed.F) FlxG.fullscreen = !FlxG.fullscreen;
|
if (FlxG.keys.justPressed.F) FlxG.fullscreen = !FlxG.fullscreen;
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -16,7 +16,7 @@ class FlxAtlasSprite extends FlxAnimate
|
||||||
FrameRate: 24.0,
|
FrameRate: 24.0,
|
||||||
Reversed: false,
|
Reversed: false,
|
||||||
// ?OnComplete:Void -> Void,
|
// ?OnComplete:Void -> Void,
|
||||||
ShowPivot: #if debug true #else false #end,
|
ShowPivot: #if debug false #else false #end,
|
||||||
Antialiasing: true,
|
Antialiasing: true,
|
||||||
ScrollFactor: new FlxPoint(1, 1),
|
ScrollFactor: new FlxPoint(1, 1),
|
||||||
// Offset: new FlxPoint(0, 0), // This is just FlxSprite.offset
|
// Offset: new FlxPoint(0, 0), // This is just FlxSprite.offset
|
||||||
|
|
|
@ -20,7 +20,7 @@ class FlxVideo extends FlxBasic
|
||||||
/**
|
/**
|
||||||
* Doesn't actually interact with Flixel shit, only just a pleasant to use class
|
* Doesn't actually interact with Flixel shit, only just a pleasant to use class
|
||||||
*/
|
*/
|
||||||
public function new(vidSrc:String)
|
public function new(videoPath:String)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ class FlxVideo extends FlxBasic
|
||||||
netStream = new NetStream(netConnection);
|
netStream = new NetStream(netConnection);
|
||||||
netStream.client = {onMetaData: client_onMetaData};
|
netStream.client = {onMetaData: client_onMetaData};
|
||||||
netConnection.addEventListener(NetStatusEvent.NET_STATUS, netConnection_onNetStatus);
|
netConnection.addEventListener(NetStatusEvent.NET_STATUS, netConnection_onNetStatus);
|
||||||
netStream.play(Paths.file(vidSrc));
|
netStream.play(videoPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function finishVideo():Void
|
public function finishVideo():Void
|
||||||
|
|
|
@ -8,6 +8,7 @@ import funkin.play.stage.StageData;
|
||||||
import polymod.Polymod;
|
import polymod.Polymod;
|
||||||
import polymod.backends.PolymodAssets.PolymodAssetType;
|
import polymod.backends.PolymodAssets.PolymodAssetType;
|
||||||
import polymod.format.ParseRules.TextFileFormat;
|
import polymod.format.ParseRules.TextFileFormat;
|
||||||
|
import funkin.play.event.SongEventData.SongEventParser;
|
||||||
import funkin.util.FileUtil;
|
import funkin.util.FileUtil;
|
||||||
|
|
||||||
class PolymodHandler
|
class PolymodHandler
|
||||||
|
@ -279,6 +280,11 @@ class PolymodHandler
|
||||||
// TODO: Reload event callbacks
|
// TODO: Reload event callbacks
|
||||||
|
|
||||||
funkin.data.level.LevelRegistry.instance.loadEntries();
|
funkin.data.level.LevelRegistry.instance.loadEntries();
|
||||||
|
SongEventParser.loadEventCache();
|
||||||
|
// TODO: Uncomment this once conversation data is implemented.
|
||||||
|
// ConversationDataParser.loadConversationCache();
|
||||||
|
// DialogueBoxDataParser.loadDialogueBoxCache();
|
||||||
|
// SpeakerDataParser.loadSpeakerCache();
|
||||||
SongDataParser.loadSongCache();
|
SongDataParser.loadSongCache();
|
||||||
StageDataParser.loadStageCache();
|
StageDataParser.loadStageCache();
|
||||||
CharacterDataParser.loadCharacterCache();
|
CharacterDataParser.loadCharacterCache();
|
||||||
|
|
|
@ -22,7 +22,7 @@ class ScriptEvent
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final CREATE:ScriptEventType = "CREATE";
|
public static inline final CREATE:ScriptEventType = 'CREATE';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the relevant object is destroyed.
|
* Called when the relevant object is destroyed.
|
||||||
|
@ -30,7 +30,7 @@ class ScriptEvent
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final DESTROY:ScriptEventType = "DESTROY";
|
public static inline final DESTROY:ScriptEventType = 'DESTROY';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the relevent object is added to the game state.
|
* Called when the relevent object is added to the game state.
|
||||||
|
@ -46,35 +46,35 @@ class ScriptEvent
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final UPDATE:ScriptEventType = "UPDATE";
|
public static inline final UPDATE:ScriptEventType = 'UPDATE';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the player moves to pause the game.
|
* Called when the player moves to pause the game.
|
||||||
*
|
*
|
||||||
* This event IS cancelable! Canceling the event will prevent the game from pausing.
|
* This event IS cancelable! Canceling the event will prevent the game from pausing.
|
||||||
*/
|
*/
|
||||||
public static inline final PAUSE:ScriptEventType = "PAUSE";
|
public static inline final PAUSE:ScriptEventType = 'PAUSE';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the player moves to unpause the game while paused.
|
* Called when the player moves to unpause the game while paused.
|
||||||
*
|
*
|
||||||
* This event IS cancelable! Canceling the event will prevent the game from resuming.
|
* This event IS cancelable! Canceling the event will prevent the game from resuming.
|
||||||
*/
|
*/
|
||||||
public static inline final RESUME:ScriptEventType = "RESUME";
|
public static inline final RESUME:ScriptEventType = 'RESUME';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called once per step in the song. This happens 4 times per measure.
|
* Called once per step in the song. This happens 4 times per measure.
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final SONG_BEAT_HIT:ScriptEventType = "BEAT_HIT";
|
public static inline final SONG_BEAT_HIT:ScriptEventType = 'BEAT_HIT';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called once per step in the song. This happens 16 times per measure.
|
* Called once per step in the song. This happens 16 times per measure.
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final SONG_STEP_HIT:ScriptEventType = "STEP_HIT";
|
public static inline final SONG_STEP_HIT:ScriptEventType = 'STEP_HIT';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a character hits a note.
|
* Called when a character hits a note.
|
||||||
|
@ -83,7 +83,7 @@ class ScriptEvent
|
||||||
* This event IS cancelable! Canceling this event prevents the note from being hit,
|
* This event IS cancelable! Canceling this event prevents the note from being hit,
|
||||||
* and will likely result in a miss later.
|
* and will likely result in a miss later.
|
||||||
*/
|
*/
|
||||||
public static inline final NOTE_HIT:ScriptEventType = "NOTE_HIT";
|
public static inline final NOTE_HIT:ScriptEventType = 'NOTE_HIT';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a character misses a note.
|
* Called when a character misses a note.
|
||||||
|
@ -92,7 +92,7 @@ class ScriptEvent
|
||||||
* This event IS cancelable! Canceling this event prevents the note from being considered missed,
|
* This event IS cancelable! Canceling this event prevents the note from being considered missed,
|
||||||
* avoiding a combo break and lost health.
|
* avoiding a combo break and lost health.
|
||||||
*/
|
*/
|
||||||
public static inline final NOTE_MISS:ScriptEventType = "NOTE_MISS";
|
public static inline final NOTE_MISS:ScriptEventType = 'NOTE_MISS';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a character presses a note when there was none there, causing them to lose health.
|
* Called when a character presses a note when there was none there, causing them to lose health.
|
||||||
|
@ -101,7 +101,7 @@ class ScriptEvent
|
||||||
* This event IS cancelable! Canceling this event prevents the note from being considered missed,
|
* This event IS cancelable! Canceling this event prevents the note from being considered missed,
|
||||||
* avoiding lost health/score and preventing the miss animation.
|
* avoiding lost health/score and preventing the miss animation.
|
||||||
*/
|
*/
|
||||||
public static inline final NOTE_GHOST_MISS:ScriptEventType = "NOTE_GHOST_MISS";
|
public static inline final NOTE_GHOST_MISS:ScriptEventType = 'NOTE_GHOST_MISS';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a song event is reached in the chart.
|
* Called when a song event is reached in the chart.
|
||||||
|
@ -109,21 +109,21 @@ class ScriptEvent
|
||||||
* This event IS cancelable! Cancelling this event prevents the event from being triggered,
|
* This event IS cancelable! Cancelling this event prevents the event from being triggered,
|
||||||
* thus blocking its normal functionality.
|
* thus blocking its normal functionality.
|
||||||
*/
|
*/
|
||||||
public static inline final SONG_EVENT:ScriptEventType = "SONG_EVENT";
|
public static inline final SONG_EVENT:ScriptEventType = 'SONG_EVENT';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the song starts. This occurs as the countdown ends and the instrumental and vocals begin.
|
* Called when the song starts. This occurs as the countdown ends and the instrumental and vocals begin.
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final SONG_START:ScriptEventType = "SONG_START";
|
public static inline final SONG_START:ScriptEventType = 'SONG_START';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the song ends. This happens as the instrumental and vocals end.
|
* Called when the song ends. This happens as the instrumental and vocals end.
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final SONG_END:ScriptEventType = "SONG_END";
|
public static inline final SONG_END:ScriptEventType = 'SONG_END';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the countdown begins. This occurs before the song starts.
|
* Called when the countdown begins. This occurs before the song starts.
|
||||||
|
@ -132,7 +132,7 @@ class ScriptEvent
|
||||||
* - The song will not start until you call Countdown.performCountdown() later.
|
* - The song will not start until you call Countdown.performCountdown() later.
|
||||||
* - Note that calling performCountdown() will trigger this event again, so be sure to add logic to ignore it.
|
* - Note that calling performCountdown() will trigger this event again, so be sure to add logic to ignore it.
|
||||||
*/
|
*/
|
||||||
public static inline final COUNTDOWN_START:ScriptEventType = "COUNTDOWN_START";
|
public static inline final COUNTDOWN_START:ScriptEventType = 'COUNTDOWN_START';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a step of the countdown happens.
|
* Called when a step of the countdown happens.
|
||||||
|
@ -141,21 +141,21 @@ class ScriptEvent
|
||||||
* This event IS cancelable! Canceling this event will pause the countdown.
|
* This event IS cancelable! Canceling this event will pause the countdown.
|
||||||
* - The countdown will not resume until you call PlayState.resumeCountdown().
|
* - The countdown will not resume until you call PlayState.resumeCountdown().
|
||||||
*/
|
*/
|
||||||
public static inline final COUNTDOWN_STEP:ScriptEventType = "COUNTDOWN_STEP";
|
public static inline final COUNTDOWN_STEP:ScriptEventType = 'COUNTDOWN_STEP';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the countdown is done but just before the song starts.
|
* Called when the countdown is done but just before the song starts.
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final COUNTDOWN_END:ScriptEventType = "COUNTDOWN_END";
|
public static inline final COUNTDOWN_END:ScriptEventType = 'COUNTDOWN_END';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called before the game over screen triggers and the death animation plays.
|
* Called before the game over screen triggers and the death animation plays.
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final GAME_OVER:ScriptEventType = "GAME_OVER";
|
public static inline final GAME_OVER:ScriptEventType = 'GAME_OVER';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called after the player presses a key to restart the game.
|
* Called after the player presses a key to restart the game.
|
||||||
|
@ -163,21 +163,21 @@ class ScriptEvent
|
||||||
*
|
*
|
||||||
* This event IS cancelable! Canceling this event will prevent the game from restarting.
|
* This event IS cancelable! Canceling this event will prevent the game from restarting.
|
||||||
*/
|
*/
|
||||||
public static inline final SONG_RETRY:ScriptEventType = "SONG_RETRY";
|
public static inline final SONG_RETRY:ScriptEventType = 'SONG_RETRY';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the player pushes down any key on the keyboard.
|
* Called when the player pushes down any key on the keyboard.
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final KEY_DOWN:ScriptEventType = "KEY_DOWN";
|
public static inline final KEY_DOWN:ScriptEventType = 'KEY_DOWN';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the player releases a key on the keyboard.
|
* Called when the player releases a key on the keyboard.
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final KEY_UP:ScriptEventType = "KEY_UP";
|
public static inline final KEY_UP:ScriptEventType = 'KEY_UP';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the game has finished loading the notes from JSON.
|
* Called when the game has finished loading the notes from JSON.
|
||||||
|
@ -185,49 +185,49 @@ class ScriptEvent
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final SONG_LOADED:ScriptEventType = "SONG_LOADED";
|
public static inline final SONG_LOADED:ScriptEventType = 'SONG_LOADED';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the game is about to switch the current FlxState.
|
* Called when the game is about to switch the current FlxState.
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final STATE_CHANGE_BEGIN:ScriptEventType = "STATE_CHANGE_BEGIN";
|
public static inline final STATE_CHANGE_BEGIN:ScriptEventType = 'STATE_CHANGE_BEGIN';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the game has finished switching the current FlxState.
|
* Called when the game has finished switching the current FlxState.
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final STATE_CHANGE_END:ScriptEventType = "STATE_CHANGE_END";
|
public static inline final STATE_CHANGE_END:ScriptEventType = 'STATE_CHANGE_END';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the game is about to open a new FlxSubState.
|
* Called when the game is about to open a new FlxSubState.
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final SUBSTATE_OPEN_BEGIN:ScriptEventType = "SUBSTATE_OPEN_BEGIN";
|
public static inline final SUBSTATE_OPEN_BEGIN:ScriptEventType = 'SUBSTATE_OPEN_BEGIN';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the game has finished opening a new FlxSubState.
|
* Called when the game has finished opening a new FlxSubState.
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final SUBSTATE_OPEN_END:ScriptEventType = "SUBSTATE_OPEN_END";
|
public static inline final SUBSTATE_OPEN_END:ScriptEventType = 'SUBSTATE_OPEN_END';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the game is about to close the current FlxSubState.
|
* Called when the game is about to close the current FlxSubState.
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final SUBSTATE_CLOSE_BEGIN:ScriptEventType = "SUBSTATE_CLOSE_BEGIN";
|
public static inline final SUBSTATE_CLOSE_BEGIN:ScriptEventType = 'SUBSTATE_CLOSE_BEGIN';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the game has finished closing the current FlxSubState.
|
* Called when the game has finished closing the current FlxSubState.
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final SUBSTATE_CLOSE_END:ScriptEventType = "SUBSTATE_CLOSE_END";
|
public static inline final SUBSTATE_CLOSE_END:ScriptEventType = 'SUBSTATE_CLOSE_END';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the game is exiting the current FlxState.
|
* Called when the game is exiting the current FlxState.
|
||||||
|
@ -276,9 +276,12 @@ class ScriptEvent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel this event.
|
||||||
|
* This is an alias for cancelEvent() but I make this typo all the time.
|
||||||
|
*/
|
||||||
public function cancel():Void
|
public function cancel():Void
|
||||||
{
|
{
|
||||||
// This typo happens enough that I just added this.
|
|
||||||
cancelEvent();
|
cancelEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,11 +319,17 @@ class NoteScriptEvent extends ScriptEvent
|
||||||
*/
|
*/
|
||||||
public var comboCount(default, null):Int;
|
public var comboCount(default, null):Int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to play the record scratch sound (if this eventn type is `NOTE_MISS`).
|
||||||
|
*/
|
||||||
|
public var playSound(default, default):Bool;
|
||||||
|
|
||||||
public function new(type:ScriptEventType, note:Note, comboCount:Int = 0, cancelable:Bool = false):Void
|
public function new(type:ScriptEventType, note:Note, comboCount:Int = 0, cancelable:Bool = false):Void
|
||||||
{
|
{
|
||||||
super(type, cancelable);
|
super(type, cancelable);
|
||||||
this.note = note;
|
this.note = note;
|
||||||
this.comboCount = comboCount;
|
this.comboCount = comboCount;
|
||||||
|
this.playSound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override function toString():String
|
public override function toString():String
|
||||||
|
@ -468,7 +477,7 @@ class CountdownScriptEvent extends ScriptEvent
|
||||||
*/
|
*/
|
||||||
public var step(default, null):CountdownStep;
|
public var step(default, null):CountdownStep;
|
||||||
|
|
||||||
public function new(type:ScriptEventType, step:CountdownStep, cancelable = true):Void
|
public function new(type:ScriptEventType, step:CountdownStep, cancelable:Bool = true):Void
|
||||||
{
|
{
|
||||||
super(type, cancelable);
|
super(type, cancelable);
|
||||||
this.step = step;
|
this.step = step;
|
||||||
|
|
|
@ -37,8 +37,8 @@ class Countdown
|
||||||
// Stop any existing countdown.
|
// Stop any existing countdown.
|
||||||
stopCountdown();
|
stopCountdown();
|
||||||
|
|
||||||
PlayState.isInCountdown = true;
|
PlayState.instance.isInCountdown = true;
|
||||||
Conductor.songPosition = Conductor.crochet * -5;
|
Conductor.songPosition = Conductor.beatLengthMs * -5;
|
||||||
// Handle onBeatHit events manually
|
// Handle onBeatHit events manually
|
||||||
@:privateAccess
|
@:privateAccess
|
||||||
PlayState.instance.dispatchEvent(new SongTimeScriptEvent(ScriptEvent.SONG_BEAT_HIT, 0, 0));
|
PlayState.instance.dispatchEvent(new SongTimeScriptEvent(ScriptEvent.SONG_BEAT_HIT, 0, 0));
|
||||||
|
@ -46,7 +46,7 @@ class Countdown
|
||||||
// The timer function gets called based on the beat of the song.
|
// The timer function gets called based on the beat of the song.
|
||||||
countdownTimer = new FlxTimer();
|
countdownTimer = new FlxTimer();
|
||||||
|
|
||||||
countdownTimer.start(Conductor.crochet / 1000, function(tmr:FlxTimer) {
|
countdownTimer.start(Conductor.beatLengthMs / 1000, function(tmr:FlxTimer) {
|
||||||
countdownStep = decrement(countdownStep);
|
countdownStep = decrement(countdownStep);
|
||||||
|
|
||||||
// Handle onBeatHit events manually
|
// Handle onBeatHit events manually
|
||||||
|
@ -212,7 +212,7 @@ class Countdown
|
||||||
countdownSprite.screenCenter();
|
countdownSprite.screenCenter();
|
||||||
|
|
||||||
// Fade sprite in, then out, then destroy it.
|
// Fade sprite in, then out, then destroy it.
|
||||||
FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100, alpha: 0}, Conductor.crochet / 1000,
|
FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100, alpha: 0}, Conductor.beatLengthMs / 1000,
|
||||||
{
|
{
|
||||||
ease: FlxEase.cubeInOut,
|
ease: FlxEase.cubeInOut,
|
||||||
onComplete: function(twn:FlxTween) {
|
onComplete: function(twn:FlxTween) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package funkin.play;
|
package funkin.play;
|
||||||
|
|
||||||
|
import flixel.FlxG;
|
||||||
import flixel.FlxObject;
|
import flixel.FlxObject;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.system.FlxSound;
|
import flixel.system.FlxSound;
|
||||||
|
@ -97,7 +98,6 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
boyfriend.isDead = true;
|
boyfriend.isDead = true;
|
||||||
add(boyfriend);
|
add(boyfriend);
|
||||||
boyfriend.resetCharacter();
|
boyfriend.resetCharacter();
|
||||||
boyfriend.playAnimation('firstDeath', true, true);
|
|
||||||
|
|
||||||
// Assign a camera follow point to the boyfriend's position.
|
// Assign a camera follow point to the boyfriend's position.
|
||||||
cameraFollowPoint = new FlxObject(PlayState.instance.cameraFollowPoint.x, PlayState.instance.cameraFollowPoint.y, 1, 1);
|
cameraFollowPoint = new FlxObject(PlayState.instance.cameraFollowPoint.x, PlayState.instance.cameraFollowPoint.y, 1, 1);
|
||||||
|
@ -118,15 +118,30 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
|
|
||||||
// The conductor now represents the BPM of the game over music.
|
// The conductor now represents the BPM of the game over music.
|
||||||
Conductor.songPosition = 0;
|
Conductor.songPosition = 0;
|
||||||
|
|
||||||
// Play the "blue balled" sound. May play a variant if one has been assigned.
|
|
||||||
playBlueBalledSFX();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hasStartedAnimation:Bool = false;
|
||||||
|
|
||||||
override function update(elapsed:Float)
|
override function update(elapsed:Float)
|
||||||
{
|
{
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
|
|
||||||
|
if (!hasStartedAnimation)
|
||||||
|
{
|
||||||
|
hasStartedAnimation = true;
|
||||||
|
|
||||||
|
if (boyfriend.hasAnimation('fakeoutDeath') && FlxG.random.bool((1 / 4096) * 100))
|
||||||
|
{
|
||||||
|
boyfriend.playAnimation('fakeoutDeath', true, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
boyfriend.playAnimation('firstDeath', true, true);
|
||||||
|
// Play the "blue balled" sound. May play a variant if one has been assigned.
|
||||||
|
playBlueBalledSFX();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Handle user inputs.
|
// Handle user inputs.
|
||||||
//
|
//
|
||||||
|
@ -145,19 +160,21 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
|
|
||||||
// KEYBOARD ONLY: Restart the level when pressing the assigned key.
|
// KEYBOARD ONLY: Restart the level when pressing the assigned key.
|
||||||
if (controls.ACCEPT)
|
if (controls.ACCEPT && blueballed)
|
||||||
{
|
{
|
||||||
|
blueballed = false;
|
||||||
confirmDeath();
|
confirmDeath();
|
||||||
}
|
}
|
||||||
|
|
||||||
// KEYBOARD ONLY: Return to the menu when pressing the assigned key.
|
// KEYBOARD ONLY: Return to the menu when pressing the assigned key.
|
||||||
if (controls.BACK)
|
if (controls.BACK)
|
||||||
{
|
{
|
||||||
PlayState.deathCounter = 0;
|
blueballed = false;
|
||||||
PlayState.seenCutscene = false;
|
PlayState.instance.deathCounter = 0;
|
||||||
|
// PlayState.seenCutscene = false; // old thing...
|
||||||
gameOverMusic.stop();
|
gameOverMusic.stop();
|
||||||
|
|
||||||
if (PlayState.isStoryMode) FlxG.switchState(new StoryMenuState());
|
if (PlayStatePlaylist.isStoryMode) FlxG.switchState(new StoryMenuState());
|
||||||
else
|
else
|
||||||
FlxG.switchState(new FreeplayState());
|
FlxG.switchState(new FreeplayState());
|
||||||
}
|
}
|
||||||
|
@ -171,11 +188,11 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Music hasn't started yet.
|
// Music hasn't started yet.
|
||||||
switch (PlayState.storyWeek)
|
switch (PlayStatePlaylist.campaignId)
|
||||||
{
|
{
|
||||||
// TODO: Make the behavior for playing Jeff's voicelines generic or un-hardcoded.
|
// TODO: Make the behavior for playing Jeff's voicelines generic or un-hardcoded.
|
||||||
// This will simplify the class and make it easier for mods to add death quotes.
|
// This will simplify the class and make it easier for mods to add death quotes.
|
||||||
case 7:
|
case "week7":
|
||||||
if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && boyfriend.isAnimationFinished() && !playingJeffQuote)
|
if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && boyfriend.isAnimationFinished() && !playingJeffQuote)
|
||||||
{
|
{
|
||||||
playingJeffQuote = true;
|
playingJeffQuote = true;
|
||||||
|
@ -214,7 +231,7 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
FlxG.camera.fade(FlxColor.BLACK, 2, false, function() {
|
FlxG.camera.fade(FlxColor.BLACK, 2, false, function() {
|
||||||
// ...close the GameOverSubState.
|
// ...close the GameOverSubState.
|
||||||
FlxG.camera.fade(FlxColor.BLACK, 1, true, null, true);
|
FlxG.camera.fade(FlxColor.BLACK, 1, true, null, true);
|
||||||
PlayState.needsReset = true;
|
PlayState.instance.needsReset = true;
|
||||||
|
|
||||||
// Readd Boyfriend to the stage.
|
// Readd Boyfriend to the stage.
|
||||||
boyfriend.isDead = false;
|
boyfriend.isDead = false;
|
||||||
|
@ -254,12 +271,15 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static var blueballed:Bool = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Play the sound effect that occurs when
|
* Play the sound effect that occurs when
|
||||||
* boyfriend's testicles get utterly annihilated.
|
* boyfriend's testicles get utterly annihilated.
|
||||||
*/
|
*/
|
||||||
function playBlueBalledSFX()
|
public static function playBlueBalledSFX()
|
||||||
{
|
{
|
||||||
|
blueballed = true;
|
||||||
FlxG.sound.play(Paths.sound('fnf_loss_sfx' + blueBallSuffix));
|
FlxG.sound.play(Paths.sound('fnf_loss_sfx' + blueBallSuffix));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,8 +148,7 @@ class HealthIcon extends FlxSprite
|
||||||
{
|
{
|
||||||
if (characterId == 'beta')
|
if (characterId == 'beta')
|
||||||
{
|
{
|
||||||
// characterId = PlayState.instance.currentPlayerId;
|
characterId = PlayState.instance.currentPlayerId;
|
||||||
characterId = 'bf';
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
File diff suppressed because it is too large
Load diff
56
source/funkin/play/PlayStatePlaylist.hx
Normal file
56
source/funkin/play/PlayStatePlaylist.hx
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package funkin.play;
|
||||||
|
|
||||||
|
import funkin.util.Constants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages playback of multiple songs in a row.
|
||||||
|
*
|
||||||
|
* TODO: Add getters/setters for all these properties to validate them.
|
||||||
|
*/
|
||||||
|
class PlayStatePlaylist
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Whether the game is currently in Story Mode. If false, we are in Free Play Mode.
|
||||||
|
*/
|
||||||
|
public static var isStoryMode(default, default):Bool = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The loist of upcoming songs to be played.
|
||||||
|
* When the user completes a song in Story Mode, the first entry in this list is played.
|
||||||
|
* When this list is empty, move to the Results screen instead.
|
||||||
|
*/
|
||||||
|
public static var playlistSongIds:Array<String> = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cumulative score for all the songs in the playlist.
|
||||||
|
*/
|
||||||
|
public static var campaignScore:Int = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The title of this playlist, for example `Week 4` or `Weekend 1`
|
||||||
|
*/
|
||||||
|
public static var campaignTitle:String = 'UNKNOWN';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The internal ID of the current playlist, for example `week4` or `weekend-1`.
|
||||||
|
*/
|
||||||
|
public static var campaignId:String = 'unknown';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current difficulty selected for this level (as a named ID).
|
||||||
|
*/
|
||||||
|
public static var currentDifficulty(default, default):String = Constants.DEFAULT_DIFFICULTY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the playlist to its default state.
|
||||||
|
*/
|
||||||
|
public static function reset():Void
|
||||||
|
{
|
||||||
|
isStoryMode = false;
|
||||||
|
playlistSongIds = [];
|
||||||
|
campaignScore = 0;
|
||||||
|
campaignTitle = 'UNKNOWN';
|
||||||
|
campaignId = 'unknown';
|
||||||
|
currentDifficulty = Constants.DEFAULT_DIFFICULTY;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package funkin.play;
|
package funkin.play;
|
||||||
|
|
||||||
|
import funkin.ui.story.StoryMenuState;
|
||||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||||
import flixel.FlxBasic;
|
import flixel.FlxBasic;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
|
@ -118,16 +119,16 @@ class ResultState extends MusicBeatSubState
|
||||||
|
|
||||||
difficulty = new FlxSprite(555);
|
difficulty = new FlxSprite(555);
|
||||||
|
|
||||||
var diffSpr:String = switch (CoolUtil.difficultyString())
|
var diffSpr:String = switch (PlayState.instance.currentDifficulty)
|
||||||
{
|
{
|
||||||
case "EASY":
|
case 'EASY':
|
||||||
"difEasy";
|
'difEasy';
|
||||||
case "NORMAL":
|
case 'NORMAL':
|
||||||
"difNormal";
|
'difNormal';
|
||||||
case "HARD":
|
case 'HARD':
|
||||||
"difHard";
|
'difHard';
|
||||||
case _:
|
case _:
|
||||||
"difNormal";
|
'difNormal';
|
||||||
}
|
}
|
||||||
|
|
||||||
difficulty.loadGraphic(Paths.image("resultScreen/" + diffSpr));
|
difficulty.loadGraphic(Paths.image("resultScreen/" + diffSpr));
|
||||||
|
@ -144,7 +145,7 @@ class ResultState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
songName.text += PlayState.currentSong.song;
|
songName.text += PlayState.instance.currentSong.songId;
|
||||||
}
|
}
|
||||||
|
|
||||||
songName.antialiasing = true;
|
songName.antialiasing = true;
|
||||||
|
@ -355,7 +356,17 @@ class ResultState extends MusicBeatSubState
|
||||||
|
|
||||||
if (FlxG.keys.justPressed.COMMA) songName.angle -= 0.1;
|
if (FlxG.keys.justPressed.COMMA) songName.angle -= 0.1;
|
||||||
|
|
||||||
if (controls.PAUSE) FlxG.switchState(new FreeplayState());
|
if (controls.PAUSE)
|
||||||
|
{
|
||||||
|
if (PlayStatePlaylist.isStoryMode)
|
||||||
|
{
|
||||||
|
FlxG.switchState(new StoryMenuState());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FlxG.switchState(new FreeplayState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -368,7 +368,7 @@ class BaseCharacter extends Bopper
|
||||||
// This lets you add frames to the end of the sing animation to ease back into the idle!
|
// This lets you add frames to the end of the sing animation to ease back into the idle!
|
||||||
|
|
||||||
holdTimer += event.elapsed;
|
holdTimer += event.elapsed;
|
||||||
var singTimeSec:Float = singTimeSec * (Conductor.crochet * 0.001); // x beats, to ms.
|
var singTimeSec:Float = singTimeSec * (Conductor.beatLengthMs * 0.001); // x beats, to ms.
|
||||||
|
|
||||||
if (getCurrentAnimation().endsWith('miss')) singTimeSec *= 2; // makes it feel more awkward when you miss
|
if (getCurrentAnimation().endsWith('miss')) singTimeSec *= 2; // makes it feel more awkward when you miss
|
||||||
|
|
||||||
|
|
|
@ -56,10 +56,12 @@ class MultiSparrowCharacter extends BaseCharacter
|
||||||
|
|
||||||
if (_data.isPixel)
|
if (_data.isPixel)
|
||||||
{
|
{
|
||||||
|
this.isPixel = true;
|
||||||
this.antialiasing = false;
|
this.antialiasing = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
this.isPixel = false;
|
||||||
this.antialiasing = true;
|
this.antialiasing = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,10 +41,12 @@ class PackerCharacter extends BaseCharacter
|
||||||
|
|
||||||
if (_data.isPixel)
|
if (_data.isPixel)
|
||||||
{
|
{
|
||||||
|
this.isPixel = true;
|
||||||
this.antialiasing = false;
|
this.antialiasing = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
this.isPixel = false;
|
||||||
this.antialiasing = true;
|
this.antialiasing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
package funkin.play.cutscene;
|
package funkin.play.cutscene;
|
||||||
|
|
||||||
// import hxcodec.flixel.FlxVideoSprite;
|
|
||||||
// import hxcodec.flixel.FlxCutsceneState;
|
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.tweens.FlxEase;
|
import flixel.tweens.FlxEase;
|
||||||
import flixel.tweens.FlxTween;
|
import flixel.tweens.FlxTween;
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
import flixel.util.FlxTimer;
|
import flixel.util.FlxTimer;
|
||||||
import funkin.graphics.video.FlxVideo;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Static methods for playing cutscenes in the PlayState.
|
* Static methods for playing cutscenes in the PlayState.
|
||||||
|
@ -15,112 +12,46 @@ import funkin.graphics.video.FlxVideo;
|
||||||
*/
|
*/
|
||||||
class VanillaCutscenes
|
class VanillaCutscenes
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Well, well, well, what have we got here?
|
|
||||||
*/
|
|
||||||
public static function playUghCutscene():Void
|
|
||||||
{
|
|
||||||
playVideoCutscene('music/ughCutscene.mp4');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Nice bars for an ugly, boring teenager!
|
|
||||||
*/
|
|
||||||
public static function playGunsCutscene():Void
|
|
||||||
{
|
|
||||||
playVideoCutscene('music/gunsCutscene.mp4');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Don't you have a school to shoot up?
|
|
||||||
*/
|
|
||||||
public static function playStressCutscene():Void
|
|
||||||
{
|
|
||||||
playVideoCutscene('music/stressCutscene.mp4');
|
|
||||||
}
|
|
||||||
|
|
||||||
static var blackScreen:FlxSprite;
|
static var blackScreen:FlxSprite;
|
||||||
|
|
||||||
/**
|
static final TWEEN_DURATION:Float = 2.0;
|
||||||
* Plays a cutscene from a video file, then starts the countdown once the video is done.
|
|
||||||
* TODO: Cutscene is currently skipped on native platforms.
|
|
||||||
*/
|
|
||||||
static function playVideoCutscene(path:String):Void
|
|
||||||
{
|
|
||||||
// Tell PlayState to stop the song until the video is done.
|
|
||||||
PlayState.isInCutscene = true;
|
|
||||||
PlayState.instance.camHUD.visible = false;
|
|
||||||
|
|
||||||
// Display a black screen to hide the game while the video is playing.
|
|
||||||
blackScreen = new FlxSprite(-200, -200).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK);
|
|
||||||
blackScreen.scrollFactor.set(0, 0);
|
|
||||||
blackScreen.cameras = [PlayState.instance.camCutscene];
|
|
||||||
PlayState.instance.add(blackScreen);
|
|
||||||
|
|
||||||
#if html5
|
|
||||||
// Video displays OVER the FlxState.
|
|
||||||
vid = new FlxVideo(path);
|
|
||||||
vid.finishCallback = finishCutscene.bind(0.5);
|
|
||||||
#else
|
|
||||||
// Video displays OVER the FlxState.
|
|
||||||
// vid = new FlxVideoSprite(0, 0);
|
|
||||||
|
|
||||||
vid.cameras = [PlayState.instance.camCutscene];
|
|
||||||
|
|
||||||
PlayState.instance.add(vid);
|
|
||||||
|
|
||||||
vid.playVideo(Paths.file(path), false);
|
|
||||||
vid.onEndReached.add(finishCutscene.bind(0.5));
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
|
|
||||||
static var vid:#if html5 FlxVideo #else Dynamic /**FlxVideoSprite **/ #end;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does the cleanup to start the countdown after the video is done.
|
* Plays the cutscene that appears at the start of Winter Horrorland.
|
||||||
* Gets called immediately if the video can't be played.
|
* TODO: Move this to `winter-horrorland.hxc`
|
||||||
*/
|
|
||||||
public static function finishCutscene(?transitionTime:Float = 2.5):Void
|
|
||||||
{
|
|
||||||
trace('ALERT: Finish cutscene called!');
|
|
||||||
|
|
||||||
#if html5
|
|
||||||
#else
|
|
||||||
vid.stop();
|
|
||||||
PlayState.instance.remove(vid);
|
|
||||||
#end
|
|
||||||
|
|
||||||
PlayState.instance.camHUD.visible = true;
|
|
||||||
|
|
||||||
FlxTween.tween(blackScreen, {alpha: 0}, transitionTime,
|
|
||||||
{
|
|
||||||
ease: FlxEase.quadInOut,
|
|
||||||
onComplete: function(twn:FlxTween) {
|
|
||||||
PlayState.instance.remove(blackScreen);
|
|
||||||
blackScreen = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
FlxTween.tween(FlxG.camera, {zoom: PlayState.defaultCameraZoom}, transitionTime,
|
|
||||||
{
|
|
||||||
ease: FlxEase.quadInOut,
|
|
||||||
onComplete: function(twn:FlxTween) {
|
|
||||||
PlayState.instance.startCountdown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FNF corruption mod???
|
|
||||||
*/
|
*/
|
||||||
public static function playHorrorStartCutscene():Void
|
public static function playHorrorStartCutscene():Void
|
||||||
{
|
{
|
||||||
PlayState.isInCutscene = true;
|
PlayState.instance.isInCutscene = true;
|
||||||
PlayState.instance.camHUD.visible = false;
|
PlayState.instance.camHUD.visible = false;
|
||||||
|
|
||||||
blackScreen = new FlxSprite(-200, -200).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK);
|
blackScreen = new FlxSprite(-200, -200).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK);
|
||||||
blackScreen.scrollFactor.set(0, 0);
|
blackScreen.scrollFactor.set(0, 0);
|
||||||
|
blackScreen.zIndex = 1000000;
|
||||||
PlayState.instance.add(blackScreen);
|
PlayState.instance.add(blackScreen);
|
||||||
|
|
||||||
new FlxTimer().start(0.1, _ -> finishCutscene(2.5));
|
new FlxTimer().start(0.1, function(_) {
|
||||||
|
trace('Playing horrorland cutscene...');
|
||||||
|
PlayState.instance.remove(blackScreen);
|
||||||
|
|
||||||
|
// Force set the camera position and zoom.
|
||||||
|
PlayState.instance.cameraFollowPoint.setPosition(400, -2050);
|
||||||
|
PlayState.instance.resetCamera();
|
||||||
|
FlxG.camera.zoom = 2.5;
|
||||||
|
|
||||||
|
// Play the Sound effect.
|
||||||
|
FlxG.sound.play(Paths.sound('Lights_Turn_On'), function() {
|
||||||
|
// Fade in the HUD.
|
||||||
|
trace('SFX done...');
|
||||||
|
PlayState.instance.camHUD.visible = true;
|
||||||
|
PlayState.instance.camHUD.alpha = 0.0; // Use alpha rather than visible to let us fade it in.
|
||||||
|
FlxTween.tween(PlayState.instance.camHUD, {alpha: 1.0}, TWEEN_DURATION, {ease: FlxEase.quadInOut});
|
||||||
|
|
||||||
|
// Start the countdown.
|
||||||
|
trace('Zoom out done...');
|
||||||
|
trace('Begin countdown (ends cutscene)');
|
||||||
|
PlayState.instance.startCountdown();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
155
source/funkin/play/cutscene/VideoCutscene.hx
Normal file
155
source/funkin/play/cutscene/VideoCutscene.hx
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
package funkin.play.cutscene;
|
||||||
|
|
||||||
|
import funkin.play.PlayState;
|
||||||
|
import flixel.FlxSprite;
|
||||||
|
import flixel.tweens.FlxEase;
|
||||||
|
import flixel.tweens.FlxTween;
|
||||||
|
import flixel.util.FlxColor;
|
||||||
|
import flixel.util.FlxTimer;
|
||||||
|
#if html5
|
||||||
|
import funkin.graphics.video.FlxVideo;
|
||||||
|
#else
|
||||||
|
import hxcodec.flixel.FlxVideoSprite;
|
||||||
|
#end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assumes you are in the PlayState.
|
||||||
|
*/
|
||||||
|
class VideoCutscene
|
||||||
|
{
|
||||||
|
static var blackScreen:FlxSprite;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play a video cutscene.
|
||||||
|
* TODO: Currently this is hardcoded to start the countdown after the video is done.
|
||||||
|
* @param path The path to the video file. Use Paths.file(path) to get the correct path.
|
||||||
|
*/
|
||||||
|
public static function play(filePath:String):Void
|
||||||
|
{
|
||||||
|
if (PlayState.instance == null) return;
|
||||||
|
|
||||||
|
if (!openfl.Assets.exists(filePath))
|
||||||
|
{
|
||||||
|
trace('ERROR: Video file does not exist: ${filePath}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger the cutscene. Don't play the song in the background.
|
||||||
|
PlayState.instance.isInCutscene = true;
|
||||||
|
PlayState.instance.camHUD.visible = false;
|
||||||
|
PlayState.instance.camCutscene.visible = true;
|
||||||
|
|
||||||
|
// Display a black screen to hide the game while the video is playing.
|
||||||
|
blackScreen = new FlxSprite(-200, -200).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK);
|
||||||
|
blackScreen.scrollFactor.set(0, 0);
|
||||||
|
blackScreen.cameras = [PlayState.instance.camCutscene];
|
||||||
|
PlayState.instance.add(blackScreen);
|
||||||
|
|
||||||
|
#if html5
|
||||||
|
playVideoHTML5(filePath);
|
||||||
|
#else
|
||||||
|
playVideoNative(filePath);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isPlaying():Bool
|
||||||
|
{
|
||||||
|
return vid != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if html5
|
||||||
|
static var vid:FlxVideo;
|
||||||
|
|
||||||
|
static function playVideoHTML5(filePath:String):Void
|
||||||
|
{
|
||||||
|
// Video displays OVER the FlxState.
|
||||||
|
vid = new FlxVideo(filePath);
|
||||||
|
if (vid != null)
|
||||||
|
{
|
||||||
|
vid.zIndex = 0;
|
||||||
|
|
||||||
|
vid.finishCallback = finishVideo.bind(0.5);
|
||||||
|
|
||||||
|
vid.cameras = [PlayState.instance.camCutscene];
|
||||||
|
|
||||||
|
PlayState.instance.add(vid);
|
||||||
|
|
||||||
|
PlayState.instance.refresh();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace('ALERT: Video is null! Could not play cutscene!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static var vid:FlxVideoSprite;
|
||||||
|
|
||||||
|
static function playVideoNative(filePath:String):Void
|
||||||
|
{
|
||||||
|
// Video displays OVER the FlxState.
|
||||||
|
vid = new FlxVideoSprite(0, 0);
|
||||||
|
|
||||||
|
if (vid != null)
|
||||||
|
{
|
||||||
|
vid.zIndex = 0;
|
||||||
|
vid.bitmap.onEndReached.add(finishVideo.bind(0.5));
|
||||||
|
|
||||||
|
vid.cameras = [PlayState.instance.camCutscene];
|
||||||
|
|
||||||
|
PlayState.instance.add(vid);
|
||||||
|
|
||||||
|
PlayState.instance.refresh();
|
||||||
|
vid.play(filePath, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace('ALERT: Video is null! Could not play cutscene!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
|
public static function finishVideo(?transitionTime:Float = 0.5):Void
|
||||||
|
{
|
||||||
|
trace('ALERT: Finish video cutscene called!');
|
||||||
|
|
||||||
|
#if html5
|
||||||
|
if (vid != null)
|
||||||
|
{
|
||||||
|
PlayState.instance.remove(vid);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (vid != null)
|
||||||
|
{
|
||||||
|
vid.stop();
|
||||||
|
PlayState.instance.remove(vid);
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
vid.destroy();
|
||||||
|
vid = null;
|
||||||
|
|
||||||
|
PlayState.instance.camCutscene.visible = true;
|
||||||
|
PlayState.instance.camHUD.visible = true;
|
||||||
|
|
||||||
|
FlxTween.tween(blackScreen, {alpha: 0}, transitionTime,
|
||||||
|
{
|
||||||
|
ease: FlxEase.quadInOut,
|
||||||
|
onComplete: function(twn:FlxTween) {
|
||||||
|
PlayState.instance.remove(blackScreen);
|
||||||
|
blackScreen = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
FlxTween.tween(FlxG.camera, {zoom: PlayState.instance.defaultCameraZoom}, transitionTime,
|
||||||
|
{
|
||||||
|
ease: FlxEase.quadInOut,
|
||||||
|
onComplete: function(twn:FlxTween) {
|
||||||
|
PlayState.instance.startCountdown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
trace('Video playback failed (${e})');
|
||||||
|
vid = null;
|
||||||
|
finishCutscene(0.5);
|
||||||
|
*/
|
|
@ -1,7 +1,9 @@
|
||||||
package funkin.play.event;
|
package funkin.play.event;
|
||||||
|
|
||||||
import funkin.play.event.SongEvent;
|
|
||||||
import funkin.play.song.SongData;
|
import funkin.play.song.SongData;
|
||||||
|
import funkin.play.event.SongEvent;
|
||||||
|
import funkin.play.event.SongEventData.SongEventFieldType;
|
||||||
|
import funkin.play.event.SongEventData.SongEventSchema;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a handler for a type of song event.
|
* This class represents a handler for a type of song event.
|
||||||
|
|
|
@ -3,6 +3,8 @@ package funkin.play.event;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import funkin.play.character.BaseCharacter;
|
import funkin.play.character.BaseCharacter;
|
||||||
import funkin.play.event.SongEvent;
|
import funkin.play.event.SongEvent;
|
||||||
|
import funkin.play.event.SongEventData.SongEventFieldType;
|
||||||
|
import funkin.play.event.SongEventData.SongEventSchema;
|
||||||
import funkin.play.song.SongData;
|
import funkin.play.song.SongData;
|
||||||
|
|
||||||
class PlayAnimationSongEvent extends SongEvent
|
class PlayAnimationSongEvent extends SongEvent
|
||||||
|
|
90
source/funkin/play/event/SetCameraBopSongEvent.hx
Normal file
90
source/funkin/play/event/SetCameraBopSongEvent.hx
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
package funkin.play.event;
|
||||||
|
|
||||||
|
import funkin.util.Constants;
|
||||||
|
import flixel.tweens.FlxTween;
|
||||||
|
import flixel.FlxCamera;
|
||||||
|
import flixel.tweens.FlxEase;
|
||||||
|
import funkin.play.event.SongEvent;
|
||||||
|
import funkin.play.song.SongData;
|
||||||
|
import funkin.play.event.SongEventData;
|
||||||
|
import funkin.play.event.SongEventData.SongEventFieldType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents a handler for configuring camera bop intensity and rate.
|
||||||
|
*
|
||||||
|
* Example: Bop the camera twice as hard, once per beat (rather than once every four beats).
|
||||||
|
* ```
|
||||||
|
* {
|
||||||
|
* 'e': 'SetCameraBop',
|
||||||
|
* 'v': {
|
||||||
|
* 'intensity': 2.0,
|
||||||
|
* 'rate': 1,
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Example: Reset the camera bop to default values.
|
||||||
|
* ```
|
||||||
|
* {
|
||||||
|
* 'e': 'SetCameraBop',
|
||||||
|
* 'v': {}
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
class SetCameraBopSongEvent extends SongEvent
|
||||||
|
{
|
||||||
|
public function new()
|
||||||
|
{
|
||||||
|
super('SetCameraBop');
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function handleEvent(data:SongEventData):Void
|
||||||
|
{
|
||||||
|
// Does nothing if there is no PlayState camera or stage.
|
||||||
|
if (PlayState.instance == null) return;
|
||||||
|
|
||||||
|
var rate:Null<Int> = data.getInt('rate');
|
||||||
|
if (rate == null) rate = Constants.DEFAULT_ZOOM_RATE;
|
||||||
|
var intensity:Null<Float> = data.getFloat('intensity');
|
||||||
|
if (intensity == null) intensity = 1.0;
|
||||||
|
|
||||||
|
PlayState.instance.cameraZoomIntensity = Constants.DEFAULT_ZOOM_INTENSITY * intensity;
|
||||||
|
PlayState.instance.hudCameraZoomIntensity = Constants.DEFAULT_ZOOM_INTENSITY * intensity * 2.0;
|
||||||
|
PlayState.instance.cameraZoomRate = rate;
|
||||||
|
trace('Set camera zoom rate to ${PlayState.instance.cameraZoomRate}');
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function getTitle():String
|
||||||
|
{
|
||||||
|
return 'Set Camera Bop';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ```
|
||||||
|
* {
|
||||||
|
* 'intensity': FLOAT, // Zoom amount
|
||||||
|
* 'rate': INT, // Zoom rate (beats/zoom)
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
* @return SongEventSchema
|
||||||
|
*/
|
||||||
|
public override function getEventSchema():SongEventSchema
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'intensity',
|
||||||
|
title: 'Intensity',
|
||||||
|
defaultValue: 1.0,
|
||||||
|
step: 0.1,
|
||||||
|
type: SongEventFieldType.FLOAT
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'rate',
|
||||||
|
title: 'Rate (beats/zoom)',
|
||||||
|
defaultValue: 4,
|
||||||
|
step: 1,
|
||||||
|
type: SongEventFieldType.INTEGER,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package funkin.play.event;
|
package funkin.play.event;
|
||||||
|
|
||||||
import funkin.util.macro.ClassMacro;
|
|
||||||
import funkin.play.song.SongData.SongEventData;
|
import funkin.play.song.SongData.SongEventData;
|
||||||
|
import funkin.play.event.SongEventData.SongEventSchema;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a handler for a type of song event.
|
* This class represents a handler for a type of song event.
|
||||||
|
@ -52,232 +52,3 @@ class SongEvent
|
||||||
return 'SongEvent(${this.id})';
|
return 'SongEvent(${this.id})';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This class statically handles the parsing of internal and scripted song event handlers.
|
|
||||||
*/
|
|
||||||
class SongEventParser
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Every built-in event class must be added to this list.
|
|
||||||
* Thankfully, with the power of `SongEventMacro`, this is done automatically.
|
|
||||||
*/
|
|
||||||
static final BUILTIN_EVENTS:List<Class<SongEvent>> = ClassMacro.listSubclassesOf(SongEvent);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map of internal handlers for song events.
|
|
||||||
* These may be either `ScriptedSongEvents` or built-in classes extending `SongEvent`.
|
|
||||||
*/
|
|
||||||
static final eventCache:Map<String, SongEvent> = new Map<String, SongEvent>();
|
|
||||||
|
|
||||||
public static function loadEventCache():Void
|
|
||||||
{
|
|
||||||
clearEventCache();
|
|
||||||
|
|
||||||
//
|
|
||||||
// BASE GAME EVENTS
|
|
||||||
//
|
|
||||||
registerBaseEvents();
|
|
||||||
registerScriptedEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
static function registerBaseEvents()
|
|
||||||
{
|
|
||||||
trace('Instantiating ${BUILTIN_EVENTS.length} built-in song events...');
|
|
||||||
for (eventCls in BUILTIN_EVENTS)
|
|
||||||
{
|
|
||||||
var eventClsName:String = Type.getClassName(eventCls);
|
|
||||||
if (eventClsName == 'funkin.play.event.SongEvent' || eventClsName == 'funkin.play.event.ScriptedSongEvent') continue;
|
|
||||||
|
|
||||||
var event:SongEvent = Type.createInstance(eventCls, ["UNKNOWN"]);
|
|
||||||
|
|
||||||
if (event != null)
|
|
||||||
{
|
|
||||||
trace(' Loaded built-in song event: (${event.id})');
|
|
||||||
eventCache.set(event.id, event);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
trace(' Failed to load built-in song event: ${Type.getClassName(eventCls)}');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static function registerScriptedEvents()
|
|
||||||
{
|
|
||||||
var scriptedEventClassNames:Array<String> = ScriptedSongEvent.listScriptClasses();
|
|
||||||
if (scriptedEventClassNames == null || scriptedEventClassNames.length == 0) return;
|
|
||||||
|
|
||||||
trace('Instantiating ${scriptedEventClassNames.length} scripted song events...');
|
|
||||||
for (eventCls in scriptedEventClassNames)
|
|
||||||
{
|
|
||||||
var event:SongEvent = ScriptedSongEvent.init(eventCls, "UKNOWN");
|
|
||||||
|
|
||||||
if (event != null)
|
|
||||||
{
|
|
||||||
trace(' Loaded scripted song event: ${event.id}');
|
|
||||||
eventCache.set(event.id, event);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
trace(' Failed to instantiate scripted song event class: ${eventCls}');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function listEventIds():Array<String>
|
|
||||||
{
|
|
||||||
return eventCache.keys().array();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function listEvents():Array<SongEvent>
|
|
||||||
{
|
|
||||||
return eventCache.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getEvent(id:String):SongEvent
|
|
||||||
{
|
|
||||||
return eventCache.get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getEventSchema(id:String):SongEventSchema
|
|
||||||
{
|
|
||||||
var event:SongEvent = getEvent(id);
|
|
||||||
if (event == null) return null;
|
|
||||||
|
|
||||||
return event.getEventSchema();
|
|
||||||
}
|
|
||||||
|
|
||||||
static function clearEventCache()
|
|
||||||
{
|
|
||||||
eventCache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function handleEvent(data:SongEventData):Void
|
|
||||||
{
|
|
||||||
var eventType:String = data.event;
|
|
||||||
var eventHandler:SongEvent = eventCache.get(eventType);
|
|
||||||
|
|
||||||
if (eventHandler != null)
|
|
||||||
{
|
|
||||||
eventHandler.handleEvent(data);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
trace('WARNING: No event handler for event with id: ${eventType}');
|
|
||||||
}
|
|
||||||
|
|
||||||
data.activated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static inline function handleEvents(events:Array<SongEventData>):Void
|
|
||||||
{
|
|
||||||
for (event in events)
|
|
||||||
{
|
|
||||||
handleEvent(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a list of song events and the current timestamp,
|
|
||||||
* return a list of events that should be handled.
|
|
||||||
*/
|
|
||||||
public static function queryEvents(events:Array<SongEventData>, currentTime:Float):Array<SongEventData>
|
|
||||||
{
|
|
||||||
return events.filter(function(event:SongEventData):Bool {
|
|
||||||
// If the event is already activated, don't activate it again.
|
|
||||||
if (event.activated) return false;
|
|
||||||
|
|
||||||
// If the event is in the future, don't activate it.
|
|
||||||
if (event.time > currentTime) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset activation of all the provided events.
|
|
||||||
*/
|
|
||||||
public static function resetEvents(events:Array<SongEventData>):Void
|
|
||||||
{
|
|
||||||
for (event in events)
|
|
||||||
{
|
|
||||||
event.activated = false;
|
|
||||||
// TODO: Add an onReset() method to SongEvent?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum abstract SongEventFieldType(String) from String to String
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The STRING type will display as a text field.
|
|
||||||
*/
|
|
||||||
var STRING = "string";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The INTEGER type will display as a text field that only accepts numbers.
|
|
||||||
*/
|
|
||||||
var INTEGER = "integer";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The FLOAT type will display as a text field that only accepts numbers.
|
|
||||||
*/
|
|
||||||
var FLOAT = "float";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The BOOL type will display as a checkbox.
|
|
||||||
*/
|
|
||||||
var BOOL = "bool";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The ENUM type will display as a dropdown.
|
|
||||||
* Make sure to specify the `keys` field in the schema.
|
|
||||||
*/
|
|
||||||
var ENUM = "enum";
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef SongEventSchemaField =
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name of the property as it should be saved in the event data.
|
|
||||||
*/
|
|
||||||
name:String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The title of the field to display in the UI.
|
|
||||||
*/
|
|
||||||
title:String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of the field.
|
|
||||||
*/
|
|
||||||
type:SongEventFieldType,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for ENUM values.
|
|
||||||
* The key is the display name and the value is the actual value.
|
|
||||||
*/
|
|
||||||
?keys:Map<String, Dynamic>,
|
|
||||||
/**
|
|
||||||
* Used for INTEGER and FLOAT values.
|
|
||||||
* The minimum value that can be entered.
|
|
||||||
*/
|
|
||||||
?min:Float,
|
|
||||||
/**
|
|
||||||
* Used for INTEGER and FLOAT values.
|
|
||||||
* The maximum value that can be entered.
|
|
||||||
*/
|
|
||||||
?max:Float,
|
|
||||||
/**
|
|
||||||
* Used for INTEGER and FLOAT values.
|
|
||||||
* The step value that will be used when incrementing/decrementing the value.
|
|
||||||
*/
|
|
||||||
?step:Float,
|
|
||||||
/**
|
|
||||||
* An optional default value for the field.
|
|
||||||
*/
|
|
||||||
?defaultValue:Dynamic,
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef SongEventSchema = Array<SongEventSchemaField>;
|
|
||||||
|
|
235
source/funkin/play/event/SongEventData.hx
Normal file
235
source/funkin/play/event/SongEventData.hx
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
package funkin.play.event;
|
||||||
|
|
||||||
|
import funkin.play.event.SongEventData.SongEventSchema;
|
||||||
|
import funkin.play.song.SongData.SongEventData;
|
||||||
|
import funkin.util.macro.ClassMacro;
|
||||||
|
import funkin.play.event.ScriptedSongEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class statically handles the parsing of internal and scripted song event handlers.
|
||||||
|
*/
|
||||||
|
class SongEventParser
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Every built-in event class must be added to this list.
|
||||||
|
* Thankfully, with the power of `SongEventMacro`, this is done automatically.
|
||||||
|
*/
|
||||||
|
static final BUILTIN_EVENTS:List<Class<SongEvent>> = ClassMacro.listSubclassesOf(SongEvent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of internal handlers for song events.
|
||||||
|
* These may be either `ScriptedSongEvents` or built-in classes extending `SongEvent`.
|
||||||
|
*/
|
||||||
|
static final eventCache:Map<String, SongEvent> = new Map<String, SongEvent>();
|
||||||
|
|
||||||
|
public static function loadEventCache():Void
|
||||||
|
{
|
||||||
|
clearEventCache();
|
||||||
|
|
||||||
|
//
|
||||||
|
// BASE GAME EVENTS
|
||||||
|
//
|
||||||
|
registerBaseEvents();
|
||||||
|
registerScriptedEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
static function registerBaseEvents()
|
||||||
|
{
|
||||||
|
trace('Instantiating ${BUILTIN_EVENTS.length} built-in song events...');
|
||||||
|
for (eventCls in BUILTIN_EVENTS)
|
||||||
|
{
|
||||||
|
var eventClsName:String = Type.getClassName(eventCls);
|
||||||
|
if (eventClsName == 'funkin.play.event.SongEvent' || eventClsName == 'funkin.play.event.ScriptedSongEvent') continue;
|
||||||
|
|
||||||
|
var event:SongEvent = Type.createInstance(eventCls, ["UNKNOWN"]);
|
||||||
|
|
||||||
|
if (event != null)
|
||||||
|
{
|
||||||
|
trace(' Loaded built-in song event: (${event.id})');
|
||||||
|
eventCache.set(event.id, event);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace(' Failed to load built-in song event: ${Type.getClassName(eventCls)}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function registerScriptedEvents()
|
||||||
|
{
|
||||||
|
var scriptedEventClassNames:Array<String> = ScriptedSongEvent.listScriptClasses();
|
||||||
|
if (scriptedEventClassNames == null || scriptedEventClassNames.length == 0) return;
|
||||||
|
|
||||||
|
trace('Instantiating ${scriptedEventClassNames.length} scripted song events...');
|
||||||
|
for (eventCls in scriptedEventClassNames)
|
||||||
|
{
|
||||||
|
var event:SongEvent = ScriptedSongEvent.init(eventCls, "UKNOWN");
|
||||||
|
|
||||||
|
if (event != null)
|
||||||
|
{
|
||||||
|
trace(' Loaded scripted song event: ${event.id}');
|
||||||
|
eventCache.set(event.id, event);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace(' Failed to instantiate scripted song event class: ${eventCls}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function listEventIds():Array<String>
|
||||||
|
{
|
||||||
|
return eventCache.keys().array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function listEvents():Array<SongEvent>
|
||||||
|
{
|
||||||
|
return eventCache.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getEvent(id:String):SongEvent
|
||||||
|
{
|
||||||
|
return eventCache.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getEventSchema(id:String):SongEventSchema
|
||||||
|
{
|
||||||
|
var event:SongEvent = getEvent(id);
|
||||||
|
if (event == null) return null;
|
||||||
|
|
||||||
|
return event.getEventSchema();
|
||||||
|
}
|
||||||
|
|
||||||
|
static function clearEventCache()
|
||||||
|
{
|
||||||
|
eventCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function handleEvent(data:SongEventData):Void
|
||||||
|
{
|
||||||
|
var eventType:String = data.event;
|
||||||
|
var eventHandler:SongEvent = eventCache.get(eventType);
|
||||||
|
|
||||||
|
if (eventHandler != null)
|
||||||
|
{
|
||||||
|
eventHandler.handleEvent(data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace('WARNING: No event handler for event with id: ${eventType}');
|
||||||
|
}
|
||||||
|
|
||||||
|
data.activated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function handleEvents(events:Array<SongEventData>):Void
|
||||||
|
{
|
||||||
|
for (event in events)
|
||||||
|
{
|
||||||
|
handleEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a list of song events and the current timestamp,
|
||||||
|
* return a list of events that should be handled.
|
||||||
|
*/
|
||||||
|
public static function queryEvents(events:Array<SongEventData>, currentTime:Float):Array<SongEventData>
|
||||||
|
{
|
||||||
|
return events.filter(function(event:SongEventData):Bool {
|
||||||
|
// If the event is already activated, don't activate it again.
|
||||||
|
if (event.activated) return false;
|
||||||
|
|
||||||
|
// If the event is in the future, don't activate it.
|
||||||
|
if (event.time > currentTime) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset activation of all the provided events.
|
||||||
|
*/
|
||||||
|
public static function resetEvents(events:Array<SongEventData>):Void
|
||||||
|
{
|
||||||
|
for (event in events)
|
||||||
|
{
|
||||||
|
event.activated = false;
|
||||||
|
// TODO: Add an onReset() method to SongEvent?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum abstract SongEventFieldType(String) from String to String
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The STRING type will display as a text field.
|
||||||
|
*/
|
||||||
|
var STRING = "string";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The INTEGER type will display as a text field that only accepts numbers.
|
||||||
|
*/
|
||||||
|
var INTEGER = "integer";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The FLOAT type will display as a text field that only accepts numbers.
|
||||||
|
*/
|
||||||
|
var FLOAT = "float";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The BOOL type will display as a checkbox.
|
||||||
|
*/
|
||||||
|
var BOOL = "bool";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ENUM type will display as a dropdown.
|
||||||
|
* Make sure to specify the `keys` field in the schema.
|
||||||
|
*/
|
||||||
|
var ENUM = "enum";
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef SongEventSchemaField =
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name of the property as it should be saved in the event data.
|
||||||
|
*/
|
||||||
|
name:String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The title of the field to display in the UI.
|
||||||
|
*/
|
||||||
|
title:String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the field.
|
||||||
|
*/
|
||||||
|
type:SongEventFieldType,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for ENUM values.
|
||||||
|
* The key is the display name and the value is the actual value.
|
||||||
|
*/
|
||||||
|
?keys:Map<String, Dynamic>,
|
||||||
|
/**
|
||||||
|
* Used for INTEGER and FLOAT values.
|
||||||
|
* The minimum value that can be entered.
|
||||||
|
*/
|
||||||
|
?min:Float,
|
||||||
|
/**
|
||||||
|
* Used for INTEGER and FLOAT values.
|
||||||
|
* The maximum value that can be entered.
|
||||||
|
*/
|
||||||
|
?max:Float,
|
||||||
|
/**
|
||||||
|
* Used for INTEGER and FLOAT values.
|
||||||
|
* The step value that will be used when incrementing/decrementing the value.
|
||||||
|
*/
|
||||||
|
?step:Float,
|
||||||
|
/**
|
||||||
|
* An optional default value for the field.
|
||||||
|
*/
|
||||||
|
?defaultValue:Dynamic,
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef SongEventSchema = Array<SongEventSchemaField>;
|
148
source/funkin/play/event/ZoomCameraSongEvent.hx
Normal file
148
source/funkin/play/event/ZoomCameraSongEvent.hx
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
package funkin.play.event;
|
||||||
|
|
||||||
|
import flixel.tweens.FlxTween;
|
||||||
|
import flixel.FlxCamera;
|
||||||
|
import flixel.tweens.FlxEase;
|
||||||
|
import funkin.play.event.SongEvent;
|
||||||
|
import funkin.play.song.SongData;
|
||||||
|
import funkin.play.event.SongEventData;
|
||||||
|
import funkin.play.event.SongEventData.SongEventFieldType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents a handler for camera zoom events.
|
||||||
|
*
|
||||||
|
* Example: Zoom to 1.3x:
|
||||||
|
* ```
|
||||||
|
* {
|
||||||
|
* 'e': 'ZoomCamera',
|
||||||
|
* 'v': 1.3
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Example: Zoom to 1.3x
|
||||||
|
* ```
|
||||||
|
* {
|
||||||
|
* 'e': 'FocusCamera',
|
||||||
|
* 'v': {
|
||||||
|
* 'char': 2,
|
||||||
|
* 'y': -10,
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Example: Focus on (100, 100):
|
||||||
|
* ```
|
||||||
|
* {
|
||||||
|
* 'e': 'FocusCamera',
|
||||||
|
* 'v': {
|
||||||
|
* 'char': -1,
|
||||||
|
* 'x': 100,
|
||||||
|
* 'y': 100,
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
class ZoomCameraSongEvent extends SongEvent
|
||||||
|
{
|
||||||
|
public function new()
|
||||||
|
{
|
||||||
|
super('ZoomCamera');
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function handleEvent(data:SongEventData):Void
|
||||||
|
{
|
||||||
|
// Does nothing if there is no PlayState camera or stage.
|
||||||
|
if (PlayState.instance == null) return;
|
||||||
|
|
||||||
|
var zoom:Null<Float> = data.getFloat('zoom');
|
||||||
|
if (zoom == null) zoom = 1.0;
|
||||||
|
var duration:Null<Float> = data.getFloat('duration');
|
||||||
|
if (duration == null) duration = 4.0;
|
||||||
|
|
||||||
|
var ease:Null<String> = data.getString('ease');
|
||||||
|
if (ease == null) ease = 'linear';
|
||||||
|
|
||||||
|
// If it's a string, check the value.
|
||||||
|
switch (ease)
|
||||||
|
{
|
||||||
|
case 'INSTANT':
|
||||||
|
// Set the zoom. Use defaultCameraZoom to prevent breaking camera bops.
|
||||||
|
PlayState.instance.defaultCameraZoom = zoom * FlxCamera.defaultZoom;
|
||||||
|
default:
|
||||||
|
var easeFunction:Null<Float->Float> = Reflect.field(FlxEase, ease);
|
||||||
|
if (easeFunction == null)
|
||||||
|
{
|
||||||
|
trace('Invalid ease function: $ease');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlxTween.tween(PlayState.instance, {defaultCameraZoom: zoom * FlxCamera.defaultZoom}, (Conductor.stepLengthMs * duration / 1000),
|
||||||
|
{ease: easeFunction});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function getTitle():String
|
||||||
|
{
|
||||||
|
return 'Zoom Camera';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ```
|
||||||
|
* {
|
||||||
|
* 'zoom': FLOAT, // Target zoom level.
|
||||||
|
* 'duration': FLOAT, // Optional duration in steps
|
||||||
|
* 'ease': ENUM, // Optional easing function
|
||||||
|
* }
|
||||||
|
* @return SongEventSchema
|
||||||
|
*/
|
||||||
|
public override function getEventSchema():SongEventSchema
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'zoom',
|
||||||
|
title: 'Zoom Level',
|
||||||
|
defaultValue: 1.0,
|
||||||
|
step: 0.1,
|
||||||
|
type: SongEventFieldType.FLOAT
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'duration',
|
||||||
|
title: 'Duration (in steps)',
|
||||||
|
defaultValue: 4.0,
|
||||||
|
step: 0.5,
|
||||||
|
type: SongEventFieldType.FLOAT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ease',
|
||||||
|
title: 'Easing Type',
|
||||||
|
defaultValue: 'linear',
|
||||||
|
type: SongEventFieldType.ENUM,
|
||||||
|
keys: [
|
||||||
|
'Linear' => 'linear',
|
||||||
|
'Instant' => 'INSTANT',
|
||||||
|
'Quad In' => 'quadIn',
|
||||||
|
'Quad Out' => 'quadOut',
|
||||||
|
'Quad In/Out' => 'quadInOut',
|
||||||
|
'Cube In' => 'cubeIn',
|
||||||
|
'Cube Out' => 'cubeOut',
|
||||||
|
'Cube In/Out' => 'cubeInOut',
|
||||||
|
'Quart In' => 'quartIn',
|
||||||
|
'Quart Out' => 'quartOut',
|
||||||
|
'Quart In/Out' => 'quartInOut',
|
||||||
|
'Quint In' => 'quintIn',
|
||||||
|
'Quint Out' => 'quintOut',
|
||||||
|
'Quint In/Out' => 'quintInOut',
|
||||||
|
'Smooth Step In' => 'smoothStepIn',
|
||||||
|
'Smooth Step Out' => 'smoothStepOut',
|
||||||
|
'Smooth Step In/Out' => 'smoothStepInOut',
|
||||||
|
'Sine In' => 'sineIn',
|
||||||
|
'Sine Out' => 'sineOut',
|
||||||
|
'Sine In/Out' => 'sineInOut',
|
||||||
|
'Elastic In' => 'elasticIn',
|
||||||
|
'Elastic Out' => 'elasticOut',
|
||||||
|
'Elastic In/Out' => 'elasticInOut',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -365,7 +365,7 @@ abstract SongNoteData(RawSongNoteData)
|
||||||
public function get_stepTime():Float
|
public function get_stepTime():Float
|
||||||
{
|
{
|
||||||
// TODO: Account for changes in BPM.
|
// TODO: Account for changes in BPM.
|
||||||
return this.t / Conductor.stepCrochet;
|
return this.t / Conductor.stepLengthMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -551,7 +551,7 @@ abstract SongEventData(RawSongEventData)
|
||||||
public function get_stepTime():Float
|
public function get_stepTime():Float
|
||||||
{
|
{
|
||||||
// TODO: Account for changes in BPM.
|
// TODO: Account for changes in BPM.
|
||||||
return this.t / Conductor.stepCrochet;
|
return this.t / Conductor.stepLengthMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public var event(get, set):String;
|
public var event(get, set):String;
|
||||||
|
|
|
@ -658,7 +658,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
|
||||||
public function onBeatHit(event:SongTimeScriptEvent):Void
|
public function onBeatHit(event:SongTimeScriptEvent):Void
|
||||||
{
|
{
|
||||||
// Override me in your scripted stage to perform custom behavior!
|
// Override me in your scripted stage to perform custom behavior!
|
||||||
// Make sure to call super.onBeatHit(curBeat) if you want to keep the boppers dancing.
|
// Make sure to call super.onBeatHit(event) if you want to keep the boppers dancing.
|
||||||
|
|
||||||
for (bopper in boppers)
|
for (bopper in boppers)
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,7 +14,7 @@ import funkin.ui.TextMenuList;
|
||||||
|
|
||||||
class ControlsMenu extends funkin.ui.OptionsState.Page
|
class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
{
|
{
|
||||||
inline static public var COLUMNS = 2;
|
public static inline final COLUMNS = 2;
|
||||||
static var controlList = Control.createAll();
|
static var controlList = Control.createAll();
|
||||||
/*
|
/*
|
||||||
* Defines groups of controls that cannot share inputs, like left and right. Say, if ACCEPT is Z, Back is X,
|
* Defines groups of controls that cannot share inputs, like left and right. Say, if ACCEPT is Z, Back is X,
|
||||||
|
@ -23,7 +23,9 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
*/
|
*/
|
||||||
static var controlGroups:Array<Array<Control>> = [
|
static var controlGroups:Array<Array<Control>> = [
|
||||||
[NOTE_UP, NOTE_DOWN, NOTE_LEFT, NOTE_RIGHT],
|
[NOTE_UP, NOTE_DOWN, NOTE_LEFT, NOTE_RIGHT],
|
||||||
[UI_UP, UI_DOWN, UI_LEFT, UI_RIGHT, ACCEPT, BACK]
|
[UI_UP, UI_DOWN, UI_LEFT, UI_RIGHT, ACCEPT, BACK],
|
||||||
|
[CUTSCENE_ADVANCE, CUTSCENE_SKIP],
|
||||||
|
[VOLUME_UP, VOLUME_DOWN, VOLUME_MUTE]
|
||||||
];
|
];
|
||||||
|
|
||||||
var itemGroups:Array<Array<InputItem>> = [for (i in 0...controlGroups.length) []];
|
var itemGroups:Array<Array<InputItem>> = [for (i in 0...controlGroups.length) []];
|
||||||
|
@ -36,7 +38,7 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
var labels:FlxTypedGroup<AtlasText>;
|
var labels:FlxTypedGroup<AtlasText>;
|
||||||
|
|
||||||
var currentDevice:Device = Keys;
|
var currentDevice:Device = Keys;
|
||||||
var deviceListSelected = false;
|
var deviceListSelected:Bool = false;
|
||||||
|
|
||||||
public function new()
|
public function new()
|
||||||
{
|
{
|
||||||
|
@ -48,7 +50,7 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
camera = menuCamera;
|
camera = menuCamera;
|
||||||
|
|
||||||
labels = new FlxTypedGroup<AtlasText>();
|
labels = new FlxTypedGroup<AtlasText>();
|
||||||
var headers = new FlxTypedGroup<AtlasText>();
|
var headers:FlxTypedGroup<AtlasText> = new FlxTypedGroup<AtlasText>();
|
||||||
controlGrid = new MenuTypedList(Columns(COLUMNS), Vertical);
|
controlGrid = new MenuTypedList(Columns(COLUMNS), Vertical);
|
||||||
|
|
||||||
add(labels);
|
add(labels);
|
||||||
|
@ -57,20 +59,20 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
|
|
||||||
if (FlxG.gamepads.numActiveGamepads > 0)
|
if (FlxG.gamepads.numActiveGamepads > 0)
|
||||||
{
|
{
|
||||||
var devicesBg = new FlxSprite();
|
var devicesBg:FlxSprite = new FlxSprite();
|
||||||
devicesBg.makeGraphic(FlxG.width, 100, 0xFFfafd6d);
|
devicesBg.makeGraphic(FlxG.width, 100, 0xFFFAFD6D);
|
||||||
add(devicesBg);
|
add(devicesBg);
|
||||||
deviceList = new TextMenuList(Horizontal, None);
|
deviceList = new TextMenuList(Horizontal, None);
|
||||||
add(deviceList);
|
add(deviceList);
|
||||||
deviceListSelected = true;
|
deviceListSelected = true;
|
||||||
|
|
||||||
var item;
|
var item:TextMenuItem;
|
||||||
|
|
||||||
item = deviceList.createItem("Keyboard", AtlasFont.BOLD, selectDevice.bind(Keys));
|
item = deviceList.createItem('Keyboard', AtlasFont.BOLD, selectDevice.bind(Keys));
|
||||||
item.x = FlxG.width / 2 - item.width - 30;
|
item.x = FlxG.width / 2 - item.width - 30;
|
||||||
item.y = (devicesBg.height - item.height) / 2;
|
item.y = (devicesBg.height - item.height) / 2;
|
||||||
|
|
||||||
item = deviceList.createItem("Gamepad", AtlasFont.BOLD, selectDevice.bind(Gamepad(FlxG.gamepads.firstActive.id)));
|
item = deviceList.createItem('Gamepad', AtlasFont.BOLD, selectDevice.bind(Gamepad(FlxG.gamepads.firstActive.id)));
|
||||||
item.x = FlxG.width / 2 + 30;
|
item.x = FlxG.width / 2 + 30;
|
||||||
item.y = (devicesBg.height - item.height) / 2;
|
item.y = (devicesBg.height - item.height) / 2;
|
||||||
}
|
}
|
||||||
|
@ -96,6 +98,18 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
headers.add(new AtlasText(0, y, "NOTES", AtlasFont.BOLD)).screenCenter(X);
|
headers.add(new AtlasText(0, y, "NOTES", AtlasFont.BOLD)).screenCenter(X);
|
||||||
y += spacer;
|
y += spacer;
|
||||||
}
|
}
|
||||||
|
else if (currentHeader != "CUTSCENE_" && name.indexOf("CUTSCENE_") == 0)
|
||||||
|
{
|
||||||
|
currentHeader = "CUTSCENE_";
|
||||||
|
headers.add(new AtlasText(0, y, "CUTSCENE", AtlasFont.BOLD)).screenCenter(X);
|
||||||
|
y += spacer;
|
||||||
|
}
|
||||||
|
else if (currentHeader != "VOLUME_" && name.indexOf("VOLUME_") == 0)
|
||||||
|
{
|
||||||
|
currentHeader = "VOLUME_";
|
||||||
|
headers.add(new AtlasText(0, y, "VOLUME", AtlasFont.BOLD)).screenCenter(X);
|
||||||
|
y += spacer;
|
||||||
|
}
|
||||||
|
|
||||||
if (currentHeader != null && name.indexOf(currentHeader) == 0) name = name.substr(currentHeader.length);
|
if (currentHeader != null && name.indexOf(currentHeader) == 0) name = name.substr(currentHeader.length);
|
||||||
|
|
||||||
|
@ -128,7 +142,7 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
labels.members[Std.int(controlGrid.selectedIndex / COLUMNS)].alpha = 1.0;
|
labels.members[Std.int(controlGrid.selectedIndex / COLUMNS)].alpha = 1.0;
|
||||||
});
|
});
|
||||||
|
|
||||||
prompt = new Prompt("\nPress any key to rebind\n\n\n\n Escape to cancel", None);
|
prompt = new Prompt("\nPress any key to rebind\n\n\nBackspace to unbind\n Escape to cancel", None);
|
||||||
prompt.create();
|
prompt.create();
|
||||||
prompt.createBgFromMargin(100, 0xFFfafd6d);
|
prompt.createBgFromMargin(100, 0xFFfafd6d);
|
||||||
prompt.back.scrollFactor.set(0, 0);
|
prompt.back.scrollFactor.set(0, 0);
|
||||||
|
@ -149,6 +163,8 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
|
|
||||||
function onSelect():Void
|
function onSelect():Void
|
||||||
{
|
{
|
||||||
|
keyUsedToEnterPrompt = FlxG.keys.firstJustPressed();
|
||||||
|
|
||||||
controlGrid.enabled = false;
|
controlGrid.enabled = false;
|
||||||
canExit = false;
|
canExit = false;
|
||||||
prompt.exists = true;
|
prompt.exists = true;
|
||||||
|
@ -187,7 +203,9 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
canExit = false;
|
canExit = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
override function update(elapsed:Float)
|
var keyUsedToEnterPrompt:Null<Int> = null;
|
||||||
|
|
||||||
|
override function update(elapsed:Float):Void
|
||||||
{
|
{
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
|
|
||||||
|
@ -200,18 +218,35 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
{
|
{
|
||||||
case Keys:
|
case Keys:
|
||||||
{
|
{
|
||||||
// check released otherwise bugs can happen when you change the BACK key
|
// Um?
|
||||||
|
// Checking pressed causes problems when you change the BACK key,
|
||||||
|
// but checking released causes problems when the prompt is instant.
|
||||||
|
|
||||||
|
// keyUsedToEnterPrompt is my weird workaround.
|
||||||
|
|
||||||
var key = FlxG.keys.firstJustReleased();
|
var key = FlxG.keys.firstJustReleased();
|
||||||
if (key != NONE)
|
if (key != NONE && key != keyUsedToEnterPrompt)
|
||||||
{
|
{
|
||||||
if (key != ESCAPE) onInputSelect(key);
|
if (key == ESCAPE)
|
||||||
closePrompt();
|
{
|
||||||
|
closePrompt();
|
||||||
|
}
|
||||||
|
else if (key == BACKSPACE)
|
||||||
|
{
|
||||||
|
onInputSelect(NONE);
|
||||||
|
closePrompt();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
onInputSelect(key);
|
||||||
|
closePrompt();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case Gamepad(id):
|
case Gamepad(id):
|
||||||
{
|
{
|
||||||
var button = FlxG.gamepads.getByID(id).firstJustReleasedID();
|
var button = FlxG.gamepads.getByID(id).firstJustReleasedID();
|
||||||
if (button != NONE)
|
if (button != NONE && button != keyUsedToEnterPrompt)
|
||||||
{
|
{
|
||||||
if (button != BACK) onInputSelect(button);
|
if (button != BACK) onInputSelect(button);
|
||||||
closePrompt();
|
closePrompt();
|
||||||
|
@ -219,23 +254,32 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var keyJustReleased:Int = FlxG.keys.firstJustReleased();
|
||||||
|
if (keyJustReleased != NONE && keyJustReleased == keyUsedToEnterPrompt)
|
||||||
|
{
|
||||||
|
keyUsedToEnterPrompt = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onInputSelect(input:Int)
|
function onInputSelect(input:Int):Void
|
||||||
{
|
{
|
||||||
var item = controlGrid.selectedItem;
|
var item = controlGrid.selectedItem;
|
||||||
|
|
||||||
// check if that key is already set for this
|
// check if that key is already set for this
|
||||||
var column0 = Math.floor(controlGrid.selectedIndex / 2) * 2;
|
if (input != FlxKey.NONE)
|
||||||
for (i in 0...COLUMNS)
|
|
||||||
{
|
{
|
||||||
if (controlGrid.members[column0 + i].input == input) return;
|
var column0 = Math.floor(controlGrid.selectedIndex / 2) * 2;
|
||||||
|
for (i in 0...COLUMNS)
|
||||||
|
{
|
||||||
|
if (controlGrid.members[column0 + i].input == input) return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if items in the same group already have the new input
|
// Check if items in the same group already have the new input
|
||||||
for (group in itemGroups)
|
for (group in itemGroups)
|
||||||
{
|
{
|
||||||
if (group.contains(item))
|
if (input != FlxKey.NONE && group.contains(item))
|
||||||
{
|
{
|
||||||
for (otherItem in group)
|
for (otherItem in group)
|
||||||
{
|
{
|
||||||
|
@ -252,10 +296,45 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerSettings.player1.controls.replaceBinding(item.control, currentDevice, input, item.input);
|
PlayerSettings.player1.controls.replaceBinding(item.control, currentDevice, input, item.input);
|
||||||
|
|
||||||
// Don't use resetItem() since items share names/labels
|
// Don't use resetItem() since items share names/labels
|
||||||
item.input = input;
|
item.input = input;
|
||||||
item.label.text = item.getLabel(input);
|
item.label.text = item.getLabel(input);
|
||||||
|
|
||||||
|
// Shift left on the grid if the item on the right is bound and the item on the left is unbound.
|
||||||
|
if (controlGrid.selectedIndex % 2 == 1)
|
||||||
|
{
|
||||||
|
trace('Modified item on right side of grid');
|
||||||
|
var leftItem = controlGrid.members[controlGrid.selectedIndex - 1];
|
||||||
|
if (leftItem != null && input != FlxKey.NONE && leftItem.input == FlxKey.NONE)
|
||||||
|
{
|
||||||
|
trace('Left item is unbound and right item is not!');
|
||||||
|
// Swap them.
|
||||||
|
var temp = leftItem.input;
|
||||||
|
leftItem.input = item.input;
|
||||||
|
item.input = temp;
|
||||||
|
|
||||||
|
leftItem.label.text = leftItem.getLabel(leftItem.input);
|
||||||
|
item.label.text = item.getLabel(item.input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace('Modified item on left side of grid');
|
||||||
|
var rightItem = controlGrid.members[controlGrid.selectedIndex + 1];
|
||||||
|
if (rightItem != null && input == FlxKey.NONE && rightItem.input != FlxKey.NONE)
|
||||||
|
{
|
||||||
|
trace('Left item is unbound and right item is not!');
|
||||||
|
// Swap them.
|
||||||
|
var temp = item.input;
|
||||||
|
item.input = rightItem.input;
|
||||||
|
rightItem.input = temp;
|
||||||
|
|
||||||
|
item.label.text = item.getLabel(item.input);
|
||||||
|
rightItem.label.text = rightItem.getLabel(rightItem.input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PlayerSettings.player1.saveControls();
|
PlayerSettings.player1.saveControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,6 +385,8 @@ class InputItem extends TextMenuItem
|
||||||
this.input = getInput();
|
this.input = getInput();
|
||||||
|
|
||||||
super(x, y, getLabel(input), DEFAULT, callback);
|
super(x, y, getLabel(input), DEFAULT, callback);
|
||||||
|
|
||||||
|
this.fireInstantly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateDevice(device:Device)
|
public function updateDevice(device:Device)
|
||||||
|
@ -334,6 +415,6 @@ class InputItem extends TextMenuItem
|
||||||
|
|
||||||
public function getLabel(input:Int)
|
public function getLabel(input:Int)
|
||||||
{
|
{
|
||||||
return input == -1 ? "---" : InputFormatter.format(input, device);
|
return input == FlxKey.NONE ? "---" : InputFormatter.format(input, device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
|
||||||
remove(rating, true);
|
remove(rating, true);
|
||||||
rating.destroy();
|
rating.destroy();
|
||||||
},
|
},
|
||||||
startDelay: Conductor.crochet * 0.001
|
startDelay: Conductor.beatLengthMs * 0.001
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
|
||||||
remove(comboSpr, true);
|
remove(comboSpr, true);
|
||||||
comboSpr.destroy();
|
comboSpr.destroy();
|
||||||
},
|
},
|
||||||
startDelay: Conductor.crochet * 0.001
|
startDelay: Conductor.beatLengthMs * 0.001
|
||||||
});
|
});
|
||||||
|
|
||||||
var seperatedScore:Array<Int> = [];
|
var seperatedScore:Array<Int> = [];
|
||||||
|
@ -155,7 +155,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
|
||||||
remove(numScore, true);
|
remove(numScore, true);
|
||||||
numScore.destroy();
|
numScore.destroy();
|
||||||
},
|
},
|
||||||
startDelay: Conductor.crochet * 0.002
|
startDelay: Conductor.beatLengthMs * 0.002
|
||||||
});
|
});
|
||||||
|
|
||||||
daLoop++;
|
daLoop++;
|
||||||
|
|
|
@ -36,7 +36,8 @@ class StickerSubState extends MusicBeatSubState
|
||||||
add(grpStickers);
|
add(grpStickers);
|
||||||
|
|
||||||
// makes the stickers on the most recent camera, which is more often than not... a UI camera!!
|
// makes the stickers on the most recent camera, which is more often than not... a UI camera!!
|
||||||
grpStickers.cameras = [FlxG.cameras.list[FlxG.cameras.list.length - 1]];
|
// grpStickers.cameras = [FlxG.cameras.list[FlxG.cameras.list.length - 1]];
|
||||||
|
grpStickers.cameras = FlxG.cameras.list;
|
||||||
|
|
||||||
if (oldStickers != null)
|
if (oldStickers != null)
|
||||||
{
|
{
|
||||||
|
@ -208,8 +209,10 @@ class StickerSubState extends MusicBeatSubState
|
||||||
FlxG.switchState(new FreeplayState(this));
|
FlxG.switchState(new FreeplayState(this));
|
||||||
case STORY:
|
case STORY:
|
||||||
FlxG.switchState(new StoryMenuState(this));
|
FlxG.switchState(new StoryMenuState(this));
|
||||||
|
case MAIN_MENU:
|
||||||
|
FlxG.switchState(new MainMenuState());
|
||||||
default:
|
default:
|
||||||
FlxG.switchState(new FreeplayState(this));
|
FlxG.switchState(new MainMenuState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,6 +357,7 @@ typedef StickerShit =
|
||||||
|
|
||||||
enum abstract NEXTSTATE(String)
|
enum abstract NEXTSTATE(String)
|
||||||
{
|
{
|
||||||
|
var MAIN_MENU = 'mainmenu';
|
||||||
var FREEPLAY = 'freeplay';
|
var FREEPLAY = 'freeplay';
|
||||||
var STORY = 'story';
|
var STORY = 'story';
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,12 +189,12 @@ class ChartEditorState extends HaxeUIState
|
||||||
|
|
||||||
function get_scrollPositionInMs():Float
|
function get_scrollPositionInMs():Float
|
||||||
{
|
{
|
||||||
return scrollPositionInSteps * Conductor.stepCrochet;
|
return scrollPositionInSteps * Conductor.stepLengthMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_scrollPositionInMs(value:Float):Float
|
function set_scrollPositionInMs(value:Float):Float
|
||||||
{
|
{
|
||||||
scrollPositionInPixels = value / Conductor.stepCrochet;
|
scrollPositionInPixels = value / Conductor.stepLengthMs;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +223,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
|
|
||||||
function get_playheadPositionInMs():Float
|
function get_playheadPositionInMs():Float
|
||||||
{
|
{
|
||||||
return playheadPositionInSteps * Conductor.stepCrochet;
|
return playheadPositionInSteps * Conductor.stepLengthMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -263,7 +263,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
|
|
||||||
function get_songLengthInMs():Float
|
function get_songLengthInMs():Float
|
||||||
{
|
{
|
||||||
return songLengthInSteps * Conductor.stepCrochet;
|
return songLengthInSteps * Conductor.stepLengthMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_songLengthInMs(value:Float):Float
|
function set_songLengthInMs(value:Float):Float
|
||||||
|
@ -1667,7 +1667,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
// The song position of the cursor, in steps.
|
// The song position of the cursor, in steps.
|
||||||
var cursorFractionalStep:Float = cursorY / GRID_SIZE / (16 / noteSnapQuant);
|
var cursorFractionalStep:Float = cursorY / GRID_SIZE / (16 / noteSnapQuant);
|
||||||
var cursorStep:Int = Std.int(Math.floor(cursorFractionalStep));
|
var cursorStep:Int = Std.int(Math.floor(cursorFractionalStep));
|
||||||
var cursorMs:Float = cursorStep * Conductor.stepCrochet * (16 / noteSnapQuant);
|
var cursorMs:Float = cursorStep * Conductor.stepLengthMs * (16 / noteSnapQuant);
|
||||||
// The direction value for the column at the cursor.
|
// The direction value for the column at the cursor.
|
||||||
var cursorColumn:Int = Math.floor(cursorX / GRID_SIZE);
|
var cursorColumn:Int = Math.floor(cursorX / GRID_SIZE);
|
||||||
if (cursorColumn < 0) cursorColumn = 0;
|
if (cursorColumn < 0) cursorColumn = 0;
|
||||||
|
@ -1705,7 +1705,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
// We released the mouse. Select the notes in the box.
|
// We released the mouse. Select the notes in the box.
|
||||||
var cursorFractionalStepStart:Float = cursorYStart / GRID_SIZE;
|
var cursorFractionalStepStart:Float = cursorYStart / GRID_SIZE;
|
||||||
var cursorStepStart:Int = Math.floor(cursorFractionalStepStart);
|
var cursorStepStart:Int = Math.floor(cursorFractionalStepStart);
|
||||||
var cursorMsStart:Float = cursorStepStart * Conductor.stepCrochet;
|
var cursorMsStart:Float = cursorStepStart * Conductor.stepLengthMs;
|
||||||
var cursorColumnBase:Int = Math.floor(cursorX / GRID_SIZE);
|
var cursorColumnBase:Int = Math.floor(cursorX / GRID_SIZE);
|
||||||
var cursorColumnBaseStart:Int = Math.floor(cursorXStart / GRID_SIZE);
|
var cursorColumnBaseStart:Int = Math.floor(cursorXStart / GRID_SIZE);
|
||||||
|
|
||||||
|
@ -1878,11 +1878,11 @@ class ChartEditorState extends HaxeUIState
|
||||||
// Handle extending the note as you drag.
|
// Handle extending the note as you drag.
|
||||||
|
|
||||||
// Since use Math.floor and stepCrochet here, the hold notes will be beat snapped.
|
// Since use Math.floor and stepCrochet here, the hold notes will be beat snapped.
|
||||||
var dragLengthSteps:Float = Math.floor((cursorMs - currentPlaceNoteData.time) / Conductor.stepCrochet);
|
var dragLengthSteps:Float = Math.floor((cursorMs - currentPlaceNoteData.time) / Conductor.stepLengthMs);
|
||||||
|
|
||||||
// Without this, the newly placed note feels too short compared to the user's input.
|
// Without this, the newly placed note feels too short compared to the user's input.
|
||||||
var INCREMENT:Float = 1.0;
|
var INCREMENT:Float = 1.0;
|
||||||
var dragLengthMs:Float = (dragLengthSteps + INCREMENT) * Conductor.stepCrochet;
|
var dragLengthMs:Float = (dragLengthSteps + INCREMENT) * Conductor.stepLengthMs;
|
||||||
|
|
||||||
// TODO: Add and update some sort of preview?
|
// TODO: Add and update some sort of preview?
|
||||||
|
|
||||||
|
@ -2187,7 +2187,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the position the note should be at.
|
// Get the position the note should be at.
|
||||||
var noteTimePixels:Float = noteData.time / Conductor.stepCrochet * GRID_SIZE;
|
var noteTimePixels:Float = noteData.time / Conductor.stepLengthMs * GRID_SIZE;
|
||||||
|
|
||||||
// Make sure the note appears when scrolling up.
|
// Make sure the note appears when scrolling up.
|
||||||
var modifiedViewAreaTop = viewAreaTop - GRID_SIZE;
|
var modifiedViewAreaTop = viewAreaTop - GRID_SIZE;
|
||||||
|
@ -2213,7 +2213,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
{
|
{
|
||||||
// If the note is a hold, we need to make sure it's long enough.
|
// If the note is a hold, we need to make sure it's long enough.
|
||||||
var noteLengthMs:Float = noteSprite.noteData.length;
|
var noteLengthMs:Float = noteSprite.noteData.length;
|
||||||
var noteLengthSteps:Float = (noteLengthMs / Conductor.stepCrochet);
|
var noteLengthSteps:Float = (noteLengthMs / Conductor.stepLengthMs);
|
||||||
var lastNoteSprite:ChartEditorNoteSprite = noteSprite;
|
var lastNoteSprite:ChartEditorNoteSprite = noteSprite;
|
||||||
|
|
||||||
while (noteLengthSteps > 0)
|
while (noteLengthSteps > 0)
|
||||||
|
@ -2237,7 +2237,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
// Make sure the last note sprite shows the end cap properly.
|
// Make sure the last note sprite shows the end cap properly.
|
||||||
lastNoteSprite.childNoteSprite = null;
|
lastNoteSprite.childNoteSprite = null;
|
||||||
|
|
||||||
// var noteLengthPixels:Float = (noteLengthMs / Conductor.stepCrochet + 1) * GRID_SIZE;
|
// var noteLengthPixels:Float = (noteLengthMs / Conductor.stepLengthMs + 1) * GRID_SIZE;
|
||||||
// add(new FlxSprite(noteSprite.x, noteSprite.y - renderedNotes.y + noteLengthPixels).makeGraphic(40, 2, 0xFFFF0000));
|
// add(new FlxSprite(noteSprite.x, noteSprite.y - renderedNotes.y + noteLengthPixels).makeGraphic(40, 2, 0xFFFF0000));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2252,7 +2252,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the position the event should be at.
|
// Get the position the event should be at.
|
||||||
var eventTimePixels:Float = eventData.time / Conductor.stepCrochet * GRID_SIZE;
|
var eventTimePixels:Float = eventData.time / Conductor.stepLengthMs * GRID_SIZE;
|
||||||
|
|
||||||
// Make sure the event appears when scrolling up.
|
// Make sure the event appears when scrolling up.
|
||||||
var modifiedViewAreaTop = viewAreaTop - GRID_SIZE;
|
var modifiedViewAreaTop = viewAreaTop - GRID_SIZE;
|
||||||
|
|
|
@ -3,6 +3,7 @@ package funkin.ui.debug.charting;
|
||||||
import haxe.ui.data.ArrayDataSource;
|
import haxe.ui.data.ArrayDataSource;
|
||||||
import funkin.play.character.BaseCharacter.CharacterType;
|
import funkin.play.character.BaseCharacter.CharacterType;
|
||||||
import funkin.play.event.SongEvent;
|
import funkin.play.event.SongEvent;
|
||||||
|
import funkin.play.event.SongEventData;
|
||||||
import funkin.play.song.SongData.SongTimeChange;
|
import funkin.play.song.SongData.SongTimeChange;
|
||||||
import funkin.play.song.SongSerializer;
|
import funkin.play.song.SongSerializer;
|
||||||
import funkin.ui.haxeui.components.CharacterPlayer;
|
import funkin.ui.haxeui.components.CharacterPlayer;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package funkin.ui.stageBuildShit;
|
package funkin.ui.stageBuildShit;
|
||||||
|
|
||||||
|
import funkin.ui.stageBuildShit.StageOffsetSubState;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -260,7 +260,7 @@ class StageOffsetSubState extends HaxeUISubState
|
||||||
// if (uiStuff != null) remove(uiStuff);
|
// if (uiStuff != null) remove(uiStuff);
|
||||||
|
|
||||||
// uiStuff = null;
|
// uiStuff = null;
|
||||||
PlayState.disableKeys = false;
|
PlayState.instance.disableKeys = false;
|
||||||
PlayState.instance.resetCamera();
|
PlayState.instance.resetCamera();
|
||||||
FlxG.mouse.visible = false;
|
FlxG.mouse.visible = false;
|
||||||
close();
|
close();
|
||||||
|
|
|
@ -156,7 +156,10 @@ class Level implements IRegistryEntry<LevelData>
|
||||||
for (propIndex in 0..._data.props.length)
|
for (propIndex in 0..._data.props.length)
|
||||||
{
|
{
|
||||||
var propData = _data.props[propIndex];
|
var propData = _data.props[propIndex];
|
||||||
var propSprite:LevelProp = LevelProp.build(propData);
|
|
||||||
|
var propSprite:Null<LevelProp> = LevelProp.build(propData);
|
||||||
|
if (propSprite == null) continue;
|
||||||
|
|
||||||
propSprite.x += FlxG.width * 0.25 * propIndex;
|
propSprite.x += FlxG.width * 0.25 * propIndex;
|
||||||
props.push(propSprite);
|
props.push(propSprite);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,10 @@ class LevelProp extends Bopper
|
||||||
playAnimation('confirm', true, true);
|
playAnimation('confirm', true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function build(propData:LevelPropData):Null<LevelProp>
|
public static function build(propData:Null<LevelPropData>):Null<LevelProp>
|
||||||
{
|
{
|
||||||
|
if (propData == null) return null;
|
||||||
|
|
||||||
var isAnimated:Bool = propData.animations.length > 0;
|
var isAnimated:Bool = propData.animations.length > 0;
|
||||||
var prop:LevelProp = new LevelProp(propData.danceEvery);
|
var prop:LevelProp = new LevelProp(propData.danceEvery);
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@ import funkin.data.level.LevelRegistry;
|
||||||
import funkin.modding.events.ScriptEvent;
|
import funkin.modding.events.ScriptEvent;
|
||||||
import funkin.modding.events.ScriptEventDispatcher;
|
import funkin.modding.events.ScriptEventDispatcher;
|
||||||
import funkin.play.PlayState;
|
import funkin.play.PlayState;
|
||||||
|
import funkin.play.PlayStatePlaylist;
|
||||||
|
import funkin.play.song.Song;
|
||||||
import funkin.play.song.SongData.SongDataParser;
|
import funkin.play.song.SongData.SongDataParser;
|
||||||
import funkin.util.Constants;
|
import funkin.util.Constants;
|
||||||
|
|
||||||
|
@ -50,6 +52,11 @@ class StoryMenuState extends MusicBeatState
|
||||||
*/
|
*/
|
||||||
var scoreText:FlxText;
|
var scoreText:FlxText;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mode text at the top-middle.
|
||||||
|
*/
|
||||||
|
var modeText:FlxText;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of songs on the left.
|
* The list of songs on the left.
|
||||||
*/
|
*/
|
||||||
|
@ -112,8 +119,8 @@ class StoryMenuState extends MusicBeatState
|
||||||
{
|
{
|
||||||
FlxG.sound.playMusic(Paths.music('freakyMenu'));
|
FlxG.sound.playMusic(Paths.music('freakyMenu'));
|
||||||
FlxG.sound.music.fadeIn(4, 0, 0.7);
|
FlxG.sound.music.fadeIn(4, 0, 0.7);
|
||||||
Conductor.forceBPM(Constants.FREAKY_MENU_BPM);
|
|
||||||
}
|
}
|
||||||
|
Conductor.forceBPM(Constants.FREAKY_MENU_BPM);
|
||||||
|
|
||||||
if (stickerSubState != null)
|
if (stickerSubState != null)
|
||||||
{
|
{
|
||||||
|
@ -144,16 +151,22 @@ class StoryMenuState extends MusicBeatState
|
||||||
|
|
||||||
updateProps();
|
updateProps();
|
||||||
|
|
||||||
scoreText = new FlxText(10, 10, 0, 'HIGH SCORE: 42069420');
|
|
||||||
scoreText.setFormat("VCR OSD Mono", 32);
|
|
||||||
add(scoreText);
|
|
||||||
|
|
||||||
tracklistText = new FlxText(FlxG.width * 0.05, levelBackground.x + levelBackground.height + 100, 0, "Tracks", 32);
|
tracklistText = new FlxText(FlxG.width * 0.05, levelBackground.x + levelBackground.height + 100, 0, "Tracks", 32);
|
||||||
tracklistText.setFormat("VCR OSD Mono", 32);
|
tracklistText.setFormat("VCR OSD Mono", 32);
|
||||||
tracklistText.alignment = CENTER;
|
tracklistText.alignment = CENTER;
|
||||||
tracklistText.color = 0xFFe55777;
|
tracklistText.color = 0xFFe55777;
|
||||||
add(tracklistText);
|
add(tracklistText);
|
||||||
|
|
||||||
|
scoreText = new FlxText(10, 10, 0, 'HIGH SCORE: 42069420');
|
||||||
|
scoreText.setFormat("VCR OSD Mono", 32);
|
||||||
|
add(scoreText);
|
||||||
|
|
||||||
|
modeText = new FlxText(10, 10, 0, 'Base Game Levels [TAB to switch]');
|
||||||
|
modeText.setFormat("VCR OSD Mono", 32);
|
||||||
|
modeText.screenCenter(X);
|
||||||
|
modeText.visible = hasModdedLevels();
|
||||||
|
add(modeText);
|
||||||
|
|
||||||
levelTitleText = new FlxText(FlxG.width * 0.7, 10, 0, 'LEVEL 1');
|
levelTitleText = new FlxText(FlxG.width * 0.7, 10, 0, 'LEVEL 1');
|
||||||
levelTitleText.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, RIGHT);
|
levelTitleText.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, RIGHT);
|
||||||
levelTitleText.alpha = 0.7;
|
levelTitleText.alpha = 0.7;
|
||||||
|
@ -254,7 +267,7 @@ class StoryMenuState extends MusicBeatState
|
||||||
displayingModdedLevels = moddedLevels;
|
displayingModdedLevels = moddedLevels;
|
||||||
buildLevelTitles();
|
buildLevelTitles();
|
||||||
|
|
||||||
changeLevel(0);
|
changeLevel(999999); // Jump past the end of the list to the beginning.
|
||||||
changeDifficulty(0);
|
changeDifficulty(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,6 +279,9 @@ class StoryMenuState extends MusicBeatState
|
||||||
|
|
||||||
scoreText.text = 'LEVEL SCORE: ${Math.round(highScoreLerp)}';
|
scoreText.text = 'LEVEL SCORE: ${Math.round(highScoreLerp)}';
|
||||||
|
|
||||||
|
modeText.text = displayingModdedLevels ? 'Mods [TAB to switch]' : 'Base Game [TAB to switch]';
|
||||||
|
modeText.screenCenter(X);
|
||||||
|
|
||||||
levelTitleText.text = currentLevel.getTitle();
|
levelTitleText.text = currentLevel.getTitle();
|
||||||
levelTitleText.x = FlxG.width - (levelTitleText.width + 10); // Right align.
|
levelTitleText.x = FlxG.width - (levelTitleText.width + 10); // Right align.
|
||||||
|
|
||||||
|
@ -320,7 +336,7 @@ class StoryMenuState extends MusicBeatState
|
||||||
changeDifficulty(-1);
|
changeDifficulty(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FlxG.keys.justPressed.TAB)
|
if (FlxG.keys.justPressed.TAB && modeText.visible)
|
||||||
{
|
{
|
||||||
switchMode(!displayingModdedLevels);
|
switchMode(!displayingModdedLevels);
|
||||||
}
|
}
|
||||||
|
@ -340,6 +356,11 @@ class StoryMenuState extends MusicBeatState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasModdedLevels():Bool
|
||||||
|
{
|
||||||
|
return LevelRegistry.instance.listModdedLevelIds().length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the selected level.
|
* Changes the selected level.
|
||||||
* @param change +1 (down), -1 (up)
|
* @param change +1 (down), -1 (up)
|
||||||
|
@ -474,24 +495,25 @@ class StoryMenuState extends MusicBeatState
|
||||||
prop.playConfirm();
|
prop.playConfirm();
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayState.storyPlaylist = currentLevel.getSongs();
|
Paths.setCurrentLevel(currentLevel.id);
|
||||||
PlayState.isStoryMode = true;
|
|
||||||
|
|
||||||
PlayState.currentSong = SongLoad.loadFromJson(PlayState.storyPlaylist[0].toLowerCase(), PlayState.storyPlaylist[0].toLowerCase());
|
PlayStatePlaylist.playlistSongIds = currentLevel.getSongs();
|
||||||
PlayState.currentSong_NEW = SongDataParser.fetchSong(PlayState.storyPlaylist[0].toLowerCase());
|
PlayStatePlaylist.isStoryMode = true;
|
||||||
|
PlayStatePlaylist.campaignScore = 0;
|
||||||
|
|
||||||
// TODO: Fix this.
|
var targetSongId:String = PlayStatePlaylist.playlistSongIds.shift();
|
||||||
PlayState.storyWeek = 0;
|
|
||||||
PlayState.campaignScore = 0;
|
|
||||||
|
|
||||||
// TODO: Fix this.
|
var targetSong:Song = SongDataParser.fetchSong(targetSongId);
|
||||||
PlayState.storyDifficulty = 0;
|
|
||||||
PlayState.storyDifficulty_NEW = currentDifficultyId;
|
|
||||||
|
|
||||||
SongLoad.curDiff = PlayState.storyDifficulty_NEW;
|
PlayStatePlaylist.campaignId = currentLevel.id;
|
||||||
|
PlayStatePlaylist.campaignTitle = currentLevel.getTitle();
|
||||||
|
|
||||||
new FlxTimer().start(1, function(tmr:FlxTimer) {
|
new FlxTimer().start(1, function(tmr:FlxTimer) {
|
||||||
LoadingState.loadAndSwitchState(new PlayState(), true);
|
LoadingState.loadAndSwitchState(new PlayState(
|
||||||
|
{
|
||||||
|
targetSong: targetSong,
|
||||||
|
targetDifficulty: currentDifficultyId,
|
||||||
|
}), true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ class Constants
|
||||||
* The title of the game, for debug printing purposes.
|
* The title of the game, for debug printing purposes.
|
||||||
* Change this if you're making an engine.
|
* Change this if you're making an engine.
|
||||||
*/
|
*/
|
||||||
public static final TITLE = "Friday Night Funkin'";
|
public static final TITLE:String = "Friday Night Funkin'";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current version number of the game.
|
* The current version number of the game.
|
||||||
|
@ -26,7 +26,7 @@ class Constants
|
||||||
* A suffix to add to the game version.
|
* A suffix to add to the game version.
|
||||||
* Add a suffix to prototype builds and remove it for releases.
|
* Add a suffix to prototype builds and remove it for releases.
|
||||||
*/
|
*/
|
||||||
public static final VERSION_SUFFIX = ' PROTOTYPE';
|
public static final VERSION_SUFFIX:String = ' PROTOTYPE';
|
||||||
|
|
||||||
#if debug
|
#if debug
|
||||||
static function get_VERSION():String
|
static function get_VERSION():String
|
||||||
|
@ -48,12 +48,12 @@ class Constants
|
||||||
/**
|
/**
|
||||||
* Link to download the game on Itch.io.
|
* Link to download the game on Itch.io.
|
||||||
*/
|
*/
|
||||||
public static final URL_ITCH:String = "https://ninja-muffin24.itch.io/funkin/purchase";
|
public static final URL_ITCH:String = 'https://ninja-muffin24.itch.io/funkin/purchase';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Link to the game's page on Kickstarter.
|
* Link to the game's page on Kickstarter.
|
||||||
*/
|
*/
|
||||||
public static final URL_KICKSTARTER:String = "https://www.kickstarter.com/projects/funkin/friday-night-funkin-the-full-ass-game/";
|
public static final URL_KICKSTARTER:String = 'https://www.kickstarter.com/projects/funkin/friday-night-funkin-the-full-ass-game/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GIT REPO DATA
|
* GIT REPO DATA
|
||||||
|
@ -64,12 +64,12 @@ class Constants
|
||||||
/**
|
/**
|
||||||
* The current Git branch.
|
* The current Git branch.
|
||||||
*/
|
*/
|
||||||
public static final GIT_BRANCH = funkin.util.macro.GitCommit.getGitBranch();
|
public static final GIT_BRANCH:String = funkin.util.macro.GitCommit.getGitBranch();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current Git commit hash.
|
* The current Git commit hash.
|
||||||
*/
|
*/
|
||||||
public static final GIT_HASH = funkin.util.macro.GitCommit.getGitCommitHash();
|
public static final GIT_HASH:String = funkin.util.macro.GitCommit.getGitCommitHash();
|
||||||
#end
|
#end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,27 +87,70 @@ class Constants
|
||||||
*/
|
*/
|
||||||
public static final COLOR_HEALTH_BAR_GREEN:FlxColor = 0xFF66FF33;
|
public static final COLOR_HEALTH_BAR_GREEN:FlxColor = 0xFF66FF33;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default variation for charts.
|
||||||
|
*/
|
||||||
|
public static final DEFAULT_VARIATION:String = 'default';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STAGE DEFAULTS
|
||||||
|
*/
|
||||||
|
// ==============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default difficulty for charts.
|
||||||
|
*/
|
||||||
|
public static final DEFAULT_DIFFICULTY:String = 'normal';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default player character for charts.
|
||||||
|
*/
|
||||||
|
public static final DEFAULT_CHARACTER:String = 'bf';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default stage for charts.
|
||||||
|
*/
|
||||||
|
public static final DEFAULT_STAGE:String = 'mainStage';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default song for if the PlayState messes up.
|
||||||
|
*/
|
||||||
|
public static final DEFAULT_SONG:String = 'tutorial';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OTHER
|
* OTHER
|
||||||
*/
|
*/
|
||||||
// ==============================
|
// ==============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All MP3 decoders introduce a playback delay of `528` samples,
|
||||||
|
* which at 44,100 Hz (samples per second) is ~12 ms.
|
||||||
|
*/
|
||||||
|
public static final MP3_DELAY_MS:Float = 528 / 44100 * 1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The scale factor to use when increasing the size of pixel art graphics.
|
* The scale factor to use when increasing the size of pixel art graphics.
|
||||||
*/
|
*/
|
||||||
public static final PIXEL_ART_SCALE = 6;
|
public static final PIXEL_ART_SCALE:Float = 6;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The BPM of the title screen and menu music.
|
* The BPM of the title screen and menu music.
|
||||||
* TODO: Move to metadata file.
|
* TODO: Move to metadata file.
|
||||||
*/
|
*/
|
||||||
public static final FREAKY_MENU_BPM = 102;
|
public static final FREAKY_MENU_BPM:Float = 102;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The volume at which to play the countdown before the song starts.
|
* The volume at which to play the countdown before the song starts.
|
||||||
*/
|
*/
|
||||||
public static final COUNTDOWN_VOLUME = 0.6;
|
public static final COUNTDOWN_VOLUME:Float = 0.6;
|
||||||
|
|
||||||
public static final DEFAULT_VARIATION = 'default';
|
/**
|
||||||
public static final DEFAULT_DIFFICULTY = 'normal';
|
* The default intensity for camera zooms.
|
||||||
|
*/
|
||||||
|
public static final DEFAULT_ZOOM_INTENSITY:Float = 0.015;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default rate for camera zooms (in beats per zoom).
|
||||||
|
*/
|
||||||
|
public static final DEFAULT_ZOOM_RATE:Int = 4;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue