Merge branch 'rewrite/master' of https://github.com/ninjamuffin99/Funkin-secret into rewrite/feature/snapped-paste

This commit is contained in:
Cameron Taylor 2023-10-15 19:23:06 -04:00
commit 937082fecf
37 changed files with 1784 additions and 521 deletions

View file

@ -3,18 +3,30 @@ description: "sets up haxe shit, using HMM!"
runs:
using: "composite"
steps:
- uses: krdlab/setup-haxe@v1.5.1
with:
haxe-version: 4.3.1
- name: Config haxelib
run: |
haxelib config
shell: bash
- name: Installing Haxe lol
run: |
haxe -version
haxelib git haxelib https://github.com/HaxeFoundation/haxelib.git development
haxelib version
haxelib --global install hmm
haxelib --global run hmm install --quiet
shell: bash
- uses: krdlab/setup-haxe@v1.5.1
with:
haxe-version: 4.3.1
- name: Config haxelib
run: |
haxelib config
shell: bash
- name: Installing Haxe lol
run: |
haxe -version
haxelib git haxelib https://github.com/HaxeFoundation/haxelib.git development
haxelib version
haxelib --global install hmm
shell: bash
- name: dependency install cache
id: cache-hmm
uses: actions/cache@v3
with:
path: .haxelib
key: ${{ runner.os }}-hmm-${{ hashFiles('**/hmm.json') }}
restore-keys: |
${{ runner.os }}-hmm-
- if: ${{ steps.cache-hmm.outputs.cache-hit != 'true' }}
name: hmm install
run: |
haxelib --global run hmm install
shell: bash

View file

@ -36,9 +36,9 @@ runs:
./butler -V
shell: bash
- name: Upload game to itch.io
env:
env:
BUTLER_API_KEY: ${{inputs.butler-key}}
run: |
./butler login
./butler push ${{inputs.build-dir}} ninja-muffin24/funkin-secret:${{inputs.target}}-${GITHUB_REF##*/}
shell: bash
./butler push ${{inputs.build-dir}} ninja-muffin24/funkin-secret:${{inputs.target}}-${GITHUB_REF_NAME}
shell: bash

5
.github/hooks/README.md vendored Normal file
View file

@ -0,0 +1,5 @@
# Git Hooks
These work even on Windows because of Git Bash.
## Setup
`git config core.hooksPath .github/hooks`

2
.github/hooks/post-checkout vendored Normal file
View file

@ -0,0 +1,2 @@
#!/bin/sh
git submodule update --init --recursive

2
.github/hooks/post-merge vendored Normal file
View file

@ -0,0 +1,2 @@
#!/bin/sh
git submodule update --init --recursive

5
.github/hooks/pre-push vendored Normal file
View file

@ -0,0 +1,5 @@
#!/bin/sh
if git diff --cached --submodule | grep -q "^+"; then
echo "WARNING: You have unpushed changes in submodules."
exit 1
fi

View file

@ -26,9 +26,11 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
token: ${{ secrets.GH_RO_PAT }}
- uses: ./.github/actions/setup-haxeshit
- name: Build game
run: |
sudo apt-get update
sudo apt-get install -y libx11-dev xorg-dev libgl-dev libxi-dev libxext-dev libasound2-dev libxinerama-dev libxrandr-dev libgl1-mesa-dev
haxelib run lime build html5 -release --times
ls
@ -48,28 +50,47 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
token: ${{ secrets.GH_RO_PAT }}
- uses: ./.github/actions/setup-haxeshit
- name: Make HXCPP cache dir
shell: bash
run: |
mkdir -p ${{ runner.temp }}\\hxcpp_cache
- name: Restore build cache
id: cache-build-win
uses: actions/cache@v3
with:
path: |
.haxelib
export
${{ runner.temp }}\\hxcpp_cache
key: ${{ runner.os }}-build-win-${{ github.ref_name }}
restore-keys: |
${{ runner.os }}-build-win-
- name: Build game
run: |
haxelib run lime build windows -release -DNO_REDIRECT_ASSETS_FOLDER
dir
env:
HXCPP_COMPILE_CACHE: "${{ runner.temp }}\\hxcpp_cache"
- uses: ./.github/actions/upload-itch
with:
butler-key: ${{ secrets.BUTLER_API_KEY}}
butler-key: ${{ secrets.BUTLER_API_KEY }}
build-dir: export/release/windows/bin
target: win
test-unit-win:
needs: create-nightly-win
runs-on: windows-latest
permissions:
contents: write
actions: write
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- uses: ./.github/actions/setup-haxeshit
- name: Run unit tests
run: |
cd ./tests/unit/
./start-win-native.bat
# test-unit-win:
# needs: create-nightly-win
# runs-on: windows-latest
# permissions:
# contents: write
# actions: write
# steps:
# - uses: actions/checkout@v3
# with:
# submodules: 'recursive'
# token: ${{ secrets.GH_RO_PAT }}
# - uses: ./.github/actions/setup-haxeshit
# - name: Run unit tests
# run: |
# cd ./tests/unit/
# ./start-win-native.bat

3
.gitignore vendored
View file

@ -3,4 +3,5 @@
APIStuff.hx
dump/
export/
RECOVER_*.fla
RECOVER_*.fla
shitAudio/

View file

@ -156,7 +156,6 @@
<haxedef name="HXCPP_CHECK_POINTER" />
<haxedef name="HXCPP_STACK_LINE" />
<haxedef name="HXCPP_STACK_TRACE" />
<haxedef name="openfl-enable-handle-error" />
<!-- This macro allows addition of new functionality to existing Flixel. -->
<haxeflag name="--macro" value="addMetadata('@:build(funkin.util.macro.FlxMacro.buildFlxBasic())', 'flixel.FlxBasic')" />
<!--Place custom nodes like icons here (higher priority to override the HaxeFlixel icon)-->
@ -196,6 +195,22 @@
<haxedef name="REDIRECT_ASSETS_FOLDER" />
</section>
<section>
<!--
This flag enables the popup/crashlog error handler.
However, it also messes with breakpoints on some platforms.
-->
<haxedef name="openfl-enable-handle-error" />
</section>
<section>
<!-- TODO: Add a flag to Github Actions to turn this on or something. -->
<!-- Forces the version string to include the Git hash even on release builds (which are used for performance reasons). -->
<haxedef name="FORCE_DEBUG_VERSION" />
</section>
<!-- Run a script before and after building. -->
<postbuild haxe="source/Prebuild.hx"/> -->
<postbuild haxe="source/Postbuild.hx"/> -->

2
assets

@ -1 +1 @@
Subproject commit 2becb7e61458c1185bdf23abb87df505eec28676
Subproject commit 8104d43e584a1f25e574438d7b21a7e671358969

View file

@ -32,7 +32,7 @@
"name": "flxanimate",
"type": "git",
"dir": null,
"ref": "a9136359271cae6ea3016b7fd9023c5c42562933",
"ref": "dd2903f7dc7024335b981edf2a770760cec912e1",
"url": "https://github.com/ninjamuffin99/flxanimate"
},
{
@ -139,7 +139,7 @@
"name": "openfl",
"type": "git",
"dir": null,
"ref": "d33d489a137ff8fdece4994cf1302f0b6334ed08",
"ref": "de9395d2f367a80f93f082e1b639b9cde2258bf1",
"url": "https://github.com/EliteMasterEric/openfl"
},
{

View file

@ -1,5 +1,6 @@
package funkin;
import funkin.shaderslmfao.HSVShader;
import funkin.ui.StickerSubState;
import flash.text.TextField;
import flixel.FlxCamera;
@ -12,6 +13,7 @@ import flixel.addons.ui.FlxInputText;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.group.FlxGroup;
import flixel.group.FlxSpriteGroup;
import flixel.system.debug.watch.Tracker.TrackerProfile;
import flixel.input.touch.FlxTouch;
import flixel.math.FlxAngle;
import flixel.math.FlxMath;
@ -21,6 +23,7 @@ import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import funkin.data.song.SongRegistry;
import funkin.data.level.LevelRegistry;
import flixel.util.FlxSpriteUtil;
import flixel.util.FlxTimer;
import funkin.Controls.Control;
@ -29,6 +32,7 @@ import funkin.freeplayStuff.DJBoyfriend;
import funkin.freeplayStuff.FreeplayScore;
import funkin.freeplayStuff.LetterSort;
import funkin.freeplayStuff.SongMenuItem;
import funkin.freeplayStuff.DifficultyStars;
import funkin.play.HealthIcon;
import funkin.play.PlayState;
import funkin.shaderslmfao.AngleMask;
@ -38,6 +42,7 @@ import funkin.play.PlayStatePlaylist;
import funkin.play.song.Song;
import lime.app.Future;
import lime.utils.Assets;
import funkin.graphics.adobeanimate.FlxAtlasSprite;
class FreeplayState extends MusicBeatSubState
{
@ -69,6 +74,7 @@ class FreeplayState extends MusicBeatSubState
var grpSongs:FlxTypedGroup<Alphabet>;
var grpCapsules:FlxTypedGroup<SongMenuItem>;
var curCapsule:SongMenuItem;
var curPlaying:Bool = false;
var dj:DJBoyfriend;
@ -114,44 +120,33 @@ class FreeplayState extends MusicBeatSubState
#if debug
isDebug = true;
addSong('Test', 'tutorial', 'bf-pixel');
addSong('Pyro', 'weekend1', 'darnell');
// addSong('Test', 'tutorial', 'bf-pixel');
// addSong('Pyro', 'weekend1', 'darnell');
#end
var initSonglist = CoolUtil.coolTextFile(Paths.txt('freeplaySonglist'));
// var initSonglist = CoolUtil.coolTextFile(Paths.txt('freeplaySonglist'));
for (i in 0...initSonglist.length)
{
songs.push(new FreeplaySongData(initSonglist[i], 'tutorial', 'gf'));
}
// for (i in 0...initSonglist.length)
// {
// songs.push(new FreeplaySongData(initSonglist[i], 'tutorial', 'gf'));
// }
if (FlxG.sound.music != null)
{
if (!FlxG.sound.music.playing) FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu'));
}
// if (StoryMenuState.weekUnlocked[2] || isDebug)
addWeek(['Bopeebo', 'Fresh', 'Dadbattle'], 'week1', ['dad']);
// if (StoryMenuState.weekUnlocked[2] || isDebug)
addWeek(['Spookeez', 'South', 'Monster'], 'week2', ['spooky', 'spooky', 'monster']);
// if (StoryMenuState.weekUnlocked[3] || isDebug)
addWeek(['Pico', 'Philly-Nice', 'Blammed'], 'week3', ['pico']);
// if (StoryMenuState.weekUnlocked[4] || isDebug)
addWeek(['Satin-Panties', 'High', 'MILF'], 'week4', ['mom']);
// if (StoryMenuState.weekUnlocked[5] || isDebug)
addWeek(['Cocoa', 'Eggnog', 'Winter-Horrorland'], 'week5', ['parents-christmas', 'parents-christmas', 'monster-christmas']);
// if (StoryMenuState.weekUnlocked[6] || isDebug)
addWeek(['Senpai', 'Roses', 'Thorns'], 'week6', ['senpai', 'senpai', 'spirit']);
// if (StoryMenuState.weekUnlocked[7] || isDebug)
addWeek(['Ugh', 'Guns', 'Stress'], 'week7', ['tankman']);
addWeek(["Darnell", "lit-up", "2hot", "blazin"], 'weekend1', ['darnell']);
// programmatically adds the songs via LevelRegistry and SongRegistry
for (coolWeek in LevelRegistry.instance.listBaseGameLevelIds())
{
for (coolSong in LevelRegistry.instance.parseEntryData(coolWeek).songs)
{
var metadata = SongRegistry.instance.parseEntryMetadata(coolSong);
var char = metadata.playData.characters.opponent;
var songName = metadata.songName;
addSong(songName, coolWeek, char);
}
}
// LOAD MUSIC
@ -169,7 +164,7 @@ class FreeplayState extends MusicBeatSubState
FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut});
add(pinkBack);
var orangeBackShit:FlxSprite = new FlxSprite(84, FlxG.height * 0.68).makeGraphic(Std.int(pinkBack.width), 50, 0xFFffd400);
var orangeBackShit:FlxSprite = new FlxSprite(84, 440).makeGraphic(Std.int(pinkBack.width), 75, 0xFFfeda00);
add(orangeBackShit);
var alsoOrangeLOL:FlxSprite = new FlxSprite(0, orangeBackShit.y).makeGraphic(100, Std.int(orangeBackShit.height), 0xFFffd400);
@ -191,9 +186,11 @@ class FreeplayState extends MusicBeatSubState
add(grpTxtScrolls);
grpTxtScrolls.visible = false;
var moreWays:BGScrollingText = new BGScrollingText(0, 200, "HOT BLOODED IN MORE WAYS THAN ONE", FlxG.width);
FlxG.debugger.addTrackerProfile(new TrackerProfile(BGScrollingText, ["x", "y", "speed", "size"]));
var moreWays:BGScrollingText = new BGScrollingText(0, 160, "HOT BLOODED IN MORE WAYS THAN ONE", FlxG.width, true, 43);
moreWays.funnyColor = 0xFFfff383;
moreWays.speed = 4;
moreWays.speed = 6.8;
grpTxtScrolls.add(moreWays);
exitMovers.set([moreWays],
@ -202,9 +199,9 @@ class FreeplayState extends MusicBeatSubState
speed: 0.4,
});
var funnyScroll:BGScrollingText = new BGScrollingText(0, 250, "BOYFRIEND", FlxG.width / 2);
var funnyScroll:BGScrollingText = new BGScrollingText(0, 220, "BOYFRIEND", FlxG.width / 2, false, 60);
funnyScroll.funnyColor = 0xFFff9963;
funnyScroll.speed = -1;
funnyScroll.speed = -3.8;
grpTxtScrolls.add(funnyScroll);
exitMovers.set([funnyScroll],
@ -215,7 +212,8 @@ class FreeplayState extends MusicBeatSubState
wait: 0
});
var txtNuts:BGScrollingText = new BGScrollingText(0, 300, "PROTECT YO NUTS", FlxG.width / 2);
var txtNuts:BGScrollingText = new BGScrollingText(0, 285, "PROTECT YO NUTS", FlxG.width / 2, true, 43);
txtNuts.speed = 3.5;
grpTxtScrolls.add(txtNuts);
exitMovers.set([txtNuts],
{
@ -223,9 +221,9 @@ class FreeplayState extends MusicBeatSubState
speed: 0.4,
});
var funnyScroll2:BGScrollingText = new BGScrollingText(0, 340, "BOYFRIEND", FlxG.width / 2);
var funnyScroll2:BGScrollingText = new BGScrollingText(0, 335, "BOYFRIEND", FlxG.width / 2, false, 60);
funnyScroll2.funnyColor = 0xFFff9963;
funnyScroll2.speed = -1.2;
funnyScroll2.speed = -3.8;
grpTxtScrolls.add(funnyScroll2);
exitMovers.set([funnyScroll2],
@ -234,9 +232,9 @@ class FreeplayState extends MusicBeatSubState
speed: 0.5,
});
var moreWays2:BGScrollingText = new BGScrollingText(0, 400, "HOT BLOODED IN MORE WAYS THAN ONE", FlxG.width);
var moreWays2:BGScrollingText = new BGScrollingText(0, 397, "HOT BLOODED IN MORE WAYS THAN ONE", FlxG.width, true, 43);
moreWays2.funnyColor = 0xFFfff383;
moreWays2.speed = 4.4;
moreWays2.speed = 6.8;
grpTxtScrolls.add(moreWays2);
exitMovers.set([moreWays2],
@ -245,9 +243,9 @@ class FreeplayState extends MusicBeatSubState
speed: 0.4
});
var funnyScroll3:BGScrollingText = new BGScrollingText(0, orangeBackShit.y, "BOYFRIEND", FlxG.width / 2);
funnyScroll3.funnyColor = 0xFFff9963;
funnyScroll3.speed = -0.8;
var funnyScroll3:BGScrollingText = new BGScrollingText(0, orangeBackShit.y + 10, "BOYFRIEND", FlxG.width / 2, 60);
funnyScroll3.funnyColor = 0xFFfea400;
funnyScroll3.speed = -3.8;
grpTxtScrolls.add(funnyScroll3);
exitMovers.set([funnyScroll3],
@ -256,7 +254,8 @@ class FreeplayState extends MusicBeatSubState
speed: 0.3
});
dj = new DJBoyfriend(0, -100);
// dj = new DJBoyfriend(0, -100);
dj = new DJBoyfriend(640, 366);
exitMovers.set([dj],
{
x: -dj.width * 1.6,
@ -312,6 +311,49 @@ class FreeplayState extends MusicBeatSubState
grpDifficulties.group.members[curDifficulty].visible = true;
var albumArt:FlxAtlasSprite = new FlxAtlasSprite(640, 360, Paths.animateAtlas("freeplay/albumRoll"));
albumArt.visible = false;
add(albumArt);
exitMovers.set([albumArt],
{
x: FlxG.width,
speed: 0.4,
wait: 0
});
var albumTitle:FlxSprite = new FlxSprite(947, 491).loadGraphic(Paths.image('freeplay/albumTitle-fnfvol1'));
var albumArtist:FlxSprite = new FlxSprite(1010, 607).loadGraphic(Paths.image('freeplay/albumArtist-kawaisprite'));
var difficultyStars:DifficultyStars = new DifficultyStars(140, 39);
difficultyStars.stars.visible = false;
albumTitle.visible = false;
albumArtist.visible = false;
exitMovers.set([albumTitle],
{
x: FlxG.width,
speed: 0.2,
wait: 0.1
});
exitMovers.set([albumArtist],
{
x: FlxG.width * 1.1,
speed: 0.2,
wait: 0.2
});
exitMovers.set([difficultyStars],
{
x: FlxG.width * 1.2,
speed: 0.2,
wait: 0.3
});
add(albumTitle);
add(albumArtist);
add(difficultyStars);
var overhangStuff:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, 64, FlxColor.BLACK);
overhangStuff.y -= overhangStuff.height;
add(overhangStuff);
@ -355,6 +397,28 @@ class FreeplayState extends MusicBeatSubState
txtCompletion.visible = false;
add(txtCompletion);
var letterSort:LetterSort = new LetterSort(400, 75);
add(letterSort);
letterSort.visible = false;
exitMovers.set([letterSort],
{
y: -100,
speed: 0.3
});
letterSort.changeSelectionCallback = (str) -> {
switch (str)
{
case "fav":
generateSongList({filterType: FAVORITE}, true);
case "ALL":
generateSongList(null, true);
default:
generateSongList({filterType: REGEXP, filterData: str}, true);
}
};
exitMovers.set([fp, txtCompletion, fnfHighscoreSpr],
{
x: FlxG.width,
@ -362,6 +426,24 @@ class FreeplayState extends MusicBeatSubState
});
dj.onIntroDone.add(function() {
// when boyfriend hits dat shiii
//
albumArt.visible = true;
albumArt.anim.play("");
albumArt.anim.onComplete = function() {
albumArt.anim.pause();
};
new FlxTimer().start(1, function(_) {
albumTitle.visible = true;
});
new FlxTimer().start(35 / 24, function(_) {
albumArtist.visible = true;
difficultyStars.stars.visible = true;
});
FlxTween.tween(grpDifficulties, {x: 90}, 0.6, {ease: FlxEase.quartOut});
var diffSelLeft = new DifficultySelector(20, grpDifficulties.y - 10, false, controls);
@ -370,33 +452,14 @@ class FreeplayState extends MusicBeatSubState
add(diffSelLeft);
add(diffSelRight);
letterSort.visible = true;
exitMovers.set([diffSelLeft, diffSelRight],
{
x: -diffSelLeft.width * 2,
speed: 0.26
});
var letterSort:LetterSort = new LetterSort(300, 100);
add(letterSort);
exitMovers.set([letterSort],
{
y: -100,
speed: 0.3
});
letterSort.changeSelectionCallback = (str) -> {
switch (str)
{
case "fav":
generateSongList({filterType: FAVORITE}, true);
case "ALL":
generateSongList(null, true);
default:
generateSongList({filterType: STARTSWITH, filterData: str}, true);
}
};
new FlxTimer().start(1 / 24, function(handShit) {
fnfHighscoreSpr.visible = true;
fnfFreeplay.visible = true;
@ -409,18 +472,18 @@ class FreeplayState extends MusicBeatSubState
new FlxTimer().start(1.5 / 24, function(bold) {
sillyStroke.width = 0;
sillyStroke.height = 0;
changeSelection();
});
});
pinkBack.color = 0xFFffd863;
// fnfFreeplay.visible = true;
bgDad.visible = true;
orangeBackShit.visible = true;
alsoOrangeLOL.visible = true;
grpTxtScrolls.visible = true;
});
generateSongList();
generateSongList(null, false);
// FlxG.sound.playMusic(Paths.music('title'), 0);
// FlxG.sound.music.fadeIn(2, 0, 0.8);
@ -466,9 +529,12 @@ class FreeplayState extends MusicBeatSubState
public function generateSongList(?filterStuff:SongFilter, force:Bool = false)
{
curSelected = 0;
curSelected = 1;
grpCapsules.clear();
for (cap in grpCapsules.members)
cap.kill();
// grpCapsules.clear();
// var regexp:EReg = regexp;
var tempSongs:Array<FreeplaySongData> = songs;
@ -477,6 +543,13 @@ class FreeplayState extends MusicBeatSubState
{
switch (filterStuff.filterType)
{
case REGEXP:
// filterStuff.filterData has a string with the first letter of the sorting range, and the second one
// this creates a filter to return all the songs that start with a letter between those two
var filterRegexp = new EReg("^[" + filterStuff.filterData + "].*", "i");
tempSongs = tempSongs.filter(str -> {
return filterRegexp.match(str.songName);
});
case STARTSWITH:
tempSongs = tempSongs.filter(str -> {
return str.songName.toLowerCase().startsWith(filterStuff.filterData);
@ -492,54 +565,48 @@ class FreeplayState extends MusicBeatSubState
}
}
var hsvShader:HSVShader = new HSVShader();
var randomCapsule:SongMenuItem = grpCapsules.recycle(SongMenuItem);
randomCapsule.init(FlxG.width, 0, "Random");
randomCapsule.onConfirm = function() {
capsuleOnConfirmRandom(randomCapsule);
};
randomCapsule.y = randomCapsule.intendedY(0) + 10;
randomCapsule.targetPos.x = randomCapsule.x;
randomCapsule.alpha = 0.5;
randomCapsule.songText.visible = false;
randomCapsule.favIcon.visible = false;
randomCapsule.initJumpIn(0, force);
randomCapsule.hsvShader = hsvShader;
grpCapsules.add(randomCapsule);
for (i in 0...tempSongs.length)
{
var funnyMenu:SongMenuItem = new SongMenuItem(FlxG.width, (i * 150) + 160, tempSongs[i].songName);
var funnyMenu:SongMenuItem = grpCapsules.recycle(SongMenuItem);
funnyMenu.init(FlxG.width, 0, tempSongs[i].songName);
if (tempSongs[i].songCharacter != null) funnyMenu.setCharacter(tempSongs[i].songCharacter);
funnyMenu.onConfirm = function() {
capsuleOnConfirmDefault(funnyMenu);
};
funnyMenu.y = funnyMenu.intendedY(i + 1) + 10;
funnyMenu.targetPos.x = funnyMenu.x;
funnyMenu.ID = i;
funnyMenu.alpha = 0.5;
funnyMenu.capsule.alpha = 0.5;
funnyMenu.songText.visible = false;
funnyMenu.favIcon.visible = tempSongs[i].isFav;
funnyMenu.hsvShader = hsvShader;
// fp.updateScore(0);
var maxTimer:Float = Math.min(i, 4);
new FlxTimer().start((1 / 24) * maxTimer, function(doShit) {
funnyMenu.doJumpIn = true;
});
new FlxTimer().start((0.09 * maxTimer) + 0.85, function(lerpTmr) {
funnyMenu.doLerp = true;
});
if (!force)
{
new FlxTimer().start(((0.20 * maxTimer) / (1 + maxTimer)) + 0.75, function(swagShi) {
funnyMenu.songText.visible = true;
funnyMenu.alpha = 1;
});
}
if (i < 8) funnyMenu.initJumpIn(Math.min(i, 4), force);
else
{
funnyMenu.songText.visible = true;
funnyMenu.alpha = 1;
}
funnyMenu.forcePosition();
grpCapsules.add(funnyMenu);
var songText:Alphabet = new Alphabet(0, (70 * i) + 30, tempSongs[i].songName, true, false);
songText.x += 100;
songText.isMenuItem = true;
songText.targetY = i;
// grpSongs.add(songText);
// songText.x += 40;
// DONT PUT X IN THE FIRST PARAMETER OF new ALPHABET() !!
// songText.screenCenter(X);
}
FlxG.console.registerFunction("changeSelection", changeSelection);
changeSelection();
changeDiff();
}
@ -576,6 +643,8 @@ class FreeplayState extends MusicBeatSubState
var spamTimer:Float = 0;
var spamming:Bool = false;
var busy:Bool = false; // Set to true once the user has pressed enter to select a song.
override function update(elapsed:Float)
{
super.update(elapsed);
@ -623,11 +692,13 @@ class FreeplayState extends MusicBeatSubState
fp.updateScore(Std.int(lerpScore));
txtCompletion.text = Math.floor(lerpCompletion * 100) + "%";
// trace(Highscore.getCompletion(songs[curSelected].songName, curDifficulty));
// trace(intendedScore);
// trace(lerpScore);
// Highscore.getAllScores();
handleInputs(elapsed);
}
function handleInputs(elapsed:Float):Void
{
if (busy) return;
var upP = controls.UI_UP_P;
var downP = controls.UI_DOWN_P;
@ -830,66 +901,7 @@ class FreeplayState extends MusicBeatSubState
{
// if (Assets.exists())
var poop:String = songs[curSelected].songName.toLowerCase();
// does not work properly, always just accidentally sets it to normal anyways!
/* if (!Assets.exists(Paths.json(songs[curSelected].songName + '/' + poop)))
{
// defaults to normal if HARD / EASY doesn't exist
// does not account if NORMAL doesn't exist!
FlxG.log.warn("CURRENT DIFFICULTY IS NOT CHARTED, DEFAULTING TO NORMAL!");
poop = Highscore.formatSong(songs[curSelected].songName.toLowerCase(), 1);
curDifficulty = 1;
}*/
PlayStatePlaylist.isStoryMode = false;
var songId:String = songs[curSelected].songName.toLowerCase();
var targetSong:Song = SongRegistry.instance.fetchEntry(songId);
var targetDifficulty:String = switch (curDifficulty)
{
case 0:
'easy';
case 1:
'normal';
case 2:
'hard';
default: 'normal';
};
// TODO: Implement additional difficulties into the interface properly.
if (FlxG.keys.pressed.E)
{
targetDifficulty = 'erect';
}
// TODO: Implement Pico into the interface properly.
var targetCharacter:String = 'bf';
if (FlxG.keys.pressed.P)
{
targetCharacter = 'pico';
}
PlayStatePlaylist.campaignId = songs[curSelected].levelId;
// Visual and audio effects.
FlxG.sound.play(Paths.sound('confirmMenu'));
dj.confirm();
if (targetSong != null)
{
// Load and cache the song's charts.
// TODO: Do this in the loading state.
targetSong.cacheCharts(true);
}
new FlxTimer().start(1, function(tmr:FlxTimer) {
LoadingState.loadAndSwitchState(new PlayState(
{
targetSong: targetSong,
targetDifficulty: targetDifficulty,
targetCharacter: targetCharacter,
}), true);
});
grpCapsules.members[curSelected].onConfirm();
}
}
@ -941,45 +953,133 @@ class FreeplayState extends MusicBeatSubState
}
}
function capsuleOnConfirmRandom(cap:SongMenuItem):Void
{
trace("RANDOM SELECTED");
busy = true;
}
function capsuleOnConfirmDefault(cap:SongMenuItem):Void
{
// var poop:String = songs[curSelected].songName.toLowerCase();
// does not work properly, always just accidentally sets it to normal anyways!
/* if (!Assets.exists(Paths.json(songs[curSelected].songName + '/' + poop)))
{
// defaults to normal if HARD / EASY doesn't exist
// does not account if NORMAL doesn't exist!
FlxG.log.warn("CURRENT DIFFICULTY IS NOT CHARTED, DEFAULTING TO NORMAL!");
poop = Highscore.formatSong(songs[curSelected].songName.toLowerCase(), 1);
curDifficulty = 1;
}*/
busy = true;
PlayStatePlaylist.isStoryMode = false;
var songId:String = cap.songTitle.toLowerCase();
var targetSong:Song = SongRegistry.instance.fetchEntry(songId);
var targetDifficulty:String = switch (curDifficulty)
{
case 0:
'easy';
case 1:
'normal';
case 2:
'hard';
default: 'normal';
};
// TODO: Implement additional difficulties into the interface properly.
if (FlxG.keys.pressed.E)
{
targetDifficulty = 'erect';
}
// TODO: Implement Pico into the interface properly.
var targetCharacter:String = 'bf';
if (FlxG.keys.pressed.P)
{
targetCharacter = 'pico';
}
PlayStatePlaylist.campaignId = songs[curSelected].levelId;
// Visual and audio effects.
FlxG.sound.play(Paths.sound('confirmMenu'));
dj.confirm();
// Load and cache the song's charts.
// TODO: Do this in the loading state.
targetSong.cacheCharts(true);
new FlxTimer().start(1, function(tmr:FlxTimer) {
LoadingState.loadAndSwitchState(new PlayState(
{
targetSong: targetSong,
targetDifficulty: targetDifficulty,
targetCharacter: targetCharacter,
}), true);
});
}
function changeSelection(change:Int = 0)
{
// fp.updateScore(12345);
NGio.logEvent('Fresh');
// NGio.logEvent('Fresh');
FlxG.sound.play(Paths.sound('scrollMenu'), 0.4);
// FlxG.sound.playMusic(Paths.inst(songs[curSelected].songName));
curSelected += change;
if (curSelected < 0) curSelected = grpCapsules.members.length - 1;
if (curSelected >= grpCapsules.members.length) curSelected = 0;
if (curSelected < 0) curSelected = grpCapsules.countLiving() - 1;
if (curSelected >= grpCapsules.countLiving()) curSelected = 0;
// selector.y = (70 * curSelected) + 30;
// intendedScore = Highscore.getScore(songs[curSelected].songName, curDifficulty);
intendedScore = Highscore.getScore(songs[curSelected].songName, curDifficulty);
intendedCompletion = Highscore.getCompletion(songs[curSelected].songName, curDifficulty);
if (songs[curSelected] != null)
{
intendedScore = Highscore.getScore(songs[curSelected].songName, curDifficulty);
intendedCompletion = Highscore.getCompletion(songs[curSelected].songName, curDifficulty);
}
else
{
intendedScore = 0;
intendedCompletion = 0;
}
// lerpScore = 0;
#if PRELOAD_ALL
// FlxG.sound.playMusic(Paths.inst(songs[curSelected].songName), 0);
#end
var bullShit:Int = 0;
for (index => capsule in grpCapsules.members)
{
capsule.selected = false;
index += 1;
capsule.targetPos.y = ((index - curSelected) * 150) + 160;
capsule.selected = index == curSelected + 1;
capsule.targetPos.y = capsule.intendedY(index - curSelected);
capsule.targetPos.x = 270 + (60 * (Math.sin(index - curSelected)));
// capsule.targetPos.x = 320 + (40 * (index - curSelected));
if (index < curSelected) capsule.targetPos.y -= 100; // another 100 for good measure
}
if (grpCapsules.members.length > 0) grpCapsules.members[curSelected].selected = true;
if (grpCapsules.countLiving() > 0)
{
if (curSelected == 0)
{
FlxG.sound.playMusic(Paths.music('freeplay/freeplayRandom'), 0);
FlxG.sound.music.fadeIn(2, 0, 0.8);
}
grpCapsules.members[curSelected].selected = true;
}
}
}
@ -1019,7 +1119,10 @@ class DifficultySelector extends FlxSprite
whiteShader.colorSet = true;
scale.x = scale.y = 0.5;
new FlxTimer().start(2 / 24, function(tmr) {
scale.x = scale.y = 1;
whiteShader.colorSet = false;
updateHitbox();
});
@ -1035,6 +1138,7 @@ typedef SongFilter =
enum abstract FilterType(String)
{
var STARTSWITH;
var REGEXP;
var FAVORITE;
var ALL;
}

View file

@ -49,9 +49,9 @@ class Paths
return getPath(file, type, library);
}
public static inline function animateAtlas(path:String, library:String)
public static inline function animateAtlas(path:String, ?library:String)
{
return getLibraryPathForce('images/$path', library);
return getLibraryPath('images/$path', library);
}
inline static public function txt(key:String, ?library:String)

View file

@ -150,6 +150,11 @@ class PauseSubState extends MusicBeatSubState
super.update(elapsed);
handleInputs();
}
function handleInputs():Void
{
var upP = controls.UI_UP_P;
var downP = controls.UI_DOWN_P;
var accepted = controls.ACCEPT;
@ -229,9 +234,14 @@ class PauseSubState extends MusicBeatSubState
FlxTransitionableState.skipNextTransIn = true;
FlxTransitionableState.skipNextTransOut = true;
if (PlayStatePlaylist.isStoryMode) openSubState(new funkin.ui.StickerSubState(null, STORY));
if (PlayStatePlaylist.isStoryMode)
{
openSubState(new funkin.ui.StickerSubState(null, STORY));
}
else
{
openSubState(new funkin.ui.StickerSubState(null, FREEPLAY));
}
case 'Exit to Chart Editor':
this.close();

View file

@ -0,0 +1,49 @@
package funkin.audio;
import flash.media.Sound;
#if flash11
import flash.utils.ByteArray;
#end
import flixel.sound.FlxSound;
import flixel.system.FlxAssets.FlxSoundAsset;
import openfl.Assets;
#if (openfl >= "8.0.0")
import openfl.utils.AssetType;
#end
/**
* a FlxSound that just overrides loadEmbedded to allow for "streamed" sounds to load with better performance!
*/
class FlxStreamSound extends FlxSound
{
public function new()
{
super();
}
override public function loadEmbedded(EmbeddedSound:FlxSoundAsset, Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void):FlxSound
{
if (EmbeddedSound == null) return this;
cleanup(true);
if ((EmbeddedSound is Sound))
{
_sound = EmbeddedSound;
}
else if ((EmbeddedSound is Class))
{
_sound = Type.createInstance(EmbeddedSound, []);
}
else if ((EmbeddedSound is String))
{
if (Assets.exists(EmbeddedSound, AssetType.SOUND)
|| Assets.exists(EmbeddedSound, AssetType.MUSIC)) _sound = Assets.getMusic(EmbeddedSound);
else
FlxG.log.error('Could not find a Sound asset with an ID of \'$EmbeddedSound\'.');
}
// NOTE: can't pull ID3 info from embedded sound currently
return init(Looped, AutoDestroy, OnComplete);
}
}

View file

@ -7,6 +7,7 @@ import flixel.math.FlxMath;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import flixel.util.FlxSort;
import flixel.util.FlxTimer;
// its kinda like marqeee html lol!
class BGScrollingText extends FlxSpriteGroup
@ -16,36 +17,53 @@ class BGScrollingText extends FlxSpriteGroup
public var widthShit:Float = FlxG.width;
public var placementOffset:Float = 20;
public var speed:Float = 1;
public var size(default, set):Int = 48;
public var funnyColor(default, set):Int = 0xFFFFFFFF;
public function new(x:Float, y:Float, text:String, widthShit:Float = 100)
public function new(x:Float, y:Float, text:String, widthShit:Float = 100, ?bold:Bool = false, ?size:Int = 48)
{
super(x, y);
this.widthShit = widthShit;
if (size != null) this.size = size;
grpTexts = new FlxTypedSpriteGroup<FlxText>();
add(grpTexts);
var testText:FlxText = new FlxText(0, 0, 0, text, 48);
var testText:FlxText = new FlxText(0, 0, 0, text, this.size);
testText.font = "5by7";
testText.bold = bold;
testText.updateHitbox();
grpTexts.add(testText);
var needed:Int = Math.ceil(widthShit / testText.frameWidth);
var needed:Int = Math.ceil(widthShit / testText.frameWidth) + 1;
for (i in 0...needed)
{
var lmfao:Int = i + 1;
var coolText:FlxText = new FlxText((lmfao * testText.frameWidth) + (lmfao * 20), 0, 0, text, 48);
var coolText:FlxText = new FlxText((lmfao * testText.frameWidth) + (lmfao * 20), 0, 0, text, this.size);
coolText.font = "5by7";
coolText.bold = bold;
coolText.updateHitbox();
grpTexts.add(coolText);
}
}
function set_size(value:Int):Int
{
if (grpTexts != null)
{
grpTexts.forEach(function(txt:FlxText) {
txt.size = value;
});
}
this.size = value;
return value;
}
function set_funnyColor(col:Int):Int
{
grpTexts.forEach(function(txt) {
@ -55,7 +73,7 @@ class BGScrollingText extends FlxSpriteGroup
return col;
}
override function update(elapsed:Float)
override public function update(elapsed:Float)
{
for (txt in grpTexts.group)
{
@ -66,14 +84,16 @@ class BGScrollingText extends FlxSpriteGroup
if (txt.x < -txt.frameWidth)
{
txt.x = grpTexts.group.members[grpTexts.length - 1].x + grpTexts.group.members[grpTexts.length - 1].frameWidth + placementOffset;
sortTextShit();
}
}
else
{
if (txt.x > widthShit)
if (txt.x > txt.frameWidth * 2)
{
txt.x = grpTexts.group.members[0].x - grpTexts.group.members[0].frameWidth - placementOffset;
sortTextShit();
}
}

View file

@ -0,0 +1,49 @@
package funkin.freeplayStuff;
import openfl.filters.BitmapFilterQuality;
import flixel.text.FlxText;
import flixel.group.FlxSpriteGroup;
import funkin.shaderslmfao.GaussianBlurShader;
class CapsuleText extends FlxSpriteGroup
{
public var blurredText:FlxText;
var whiteText:FlxText;
public var text(default, set):String;
public function new(x:Float, y:Float, songTitle:String, size:Float)
{
super(x, y);
blurredText = initText(songTitle, size);
blurredText.shader = new GaussianBlurShader(1);
whiteText = initText(songTitle, size);
// whiteText.shader = new GaussianBlurShader(0.3);
text = songTitle;
blurredText.color = 0xFF00ccff;
whiteText.color = 0xFFFFFFFF;
add(blurredText);
add(whiteText);
}
function initText(songTitle, size:Float):FlxText
{
var text:FlxText = new FlxText(0, 0, 0, songTitle, Std.int(size));
text.font = "5by7";
return text;
}
function set_text(value:String):String
{
blurredText.text = value;
whiteText.text = value;
whiteText.textField.filters = [
new openfl.filters.GlowFilter(0x00ccff, 1, 5, 5, 210, BitmapFilterQuality.MEDIUM),
// new openfl.filters.BlurFilter(5, 5, BitmapFilterQuality.LOW)
];
return value;
}
}

View file

@ -3,8 +3,12 @@ package funkin.freeplayStuff;
import flixel.FlxSprite;
import flixel.util.FlxSignal;
import funkin.util.assets.FlxAnimationUtil;
import funkin.graphics.adobeanimate.FlxAtlasSprite;
import flixel.system.FlxSound;
import flixel.util.FlxTimer;
import funkin.audio.FlxStreamSound;
class DJBoyfriend extends FlxSprite
class DJBoyfriend extends FlxAtlasSprite
{
// Represents the sprite's current status.
// Without state machines I would have driven myself crazy years ago.
@ -20,20 +24,55 @@ class DJBoyfriend extends FlxSprite
// TODO: Switch this class to use SwagSprite instead.
public var animOffsets:Map<String, Array<Dynamic>>;
static final SPOOK_PERIOD:Float = 180.0;
var gotSpooked:Bool = false;
static final SPOOK_PERIOD:Float = 120.0;
static final TV_PERIOD:Float = 180.0;
// Time since dad last SPOOKED you.
var timeSinceSpook:Float = 0;
public function new(x:Float, y:Float)
{
super(x, y);
super(x, y, Paths.animateAtlas("freeplay/freeplay-boyfriend", "preload"));
animOffsets = new Map<String, Array<Dynamic>>();
setupAnimations();
anim.callback = function(name, number) {
switch (name)
{
case "Boyfriend DJ watchin tv OG":
if (number == 85) runTvLogic();
default:
}
};
animation.finishCallback = onFinishAnim;
setupAnimations();
trace(listAnimations());
FlxG.debugger.track(this);
FlxG.console.registerObject("dj", this);
anim.onComplete = onFinishAnim;
FlxG.console.registerFunction("tv", function() {
currentState = TV;
});
}
/*
[remote hand under,boyfriend top head,brim piece,arm cringe l,red lazer,dj arm in,bf fist pump arm,hand raised right,forearm left,fist shaking,bf smile eyes closed face,arm cringe r,bf clenched face,face shrug,boyfriend falling,blue tint 1,shirt sleeve,bf clenched fist,head BF relaxed,blue tint 2,hand down left,blue tint 3,blue tint 4,head less smooshed,blue tint 5,boyfriend freeplay,BF head slight turn,blue tint 6,arm shrug l,blue tint 7,shoulder raised w sleeve,blue tint 8,fist pump face,blue tint 9,foot rested light,hand turnaround,arm chill right,Boyfriend DJ,arm shrug r,head back bf,hat top piece,dad bod,face surprise snap,Boyfriend DJ fist pump,office chair,foot rested right,chest down,office chair upright,body chill,bf dj afk,head mouth open dad,BF Head defalt HAIR BLOWING,hand shrug l,face piece,foot wag,turn table,shoulder up left,turntable lights,boyfriend dj body shirt blowing,body chunk turned,hand down right,dj arm out,hand shrug r,body chest out,rave hand,palm,chill face default,head back semi bf,boyfriend bottom head,DJ arm,shoulder right dad,bf surprise,boyfriend dj body,hs1,Boyfriend DJ watchin tv OG,spinning disk,hs2,arm chill left,boyfriend dj intro,hs3,hs4,chill face extra,hs5,remote hand upright,hs6,pant over table,face surprise,bf arm peace,arm turnaround,bf eyes 1,arm slammed table,eye squit,leg BF,head mid piece,arm backing,arm swoopin in,shoe right lowering,forearm right,hand out,blue tint 10,body falling back,remote thumb press,shoulder,hair spike single,bf bent
arm,crt,foot raised right,dad hand,chill face 1,chill face 2,clenched fist,head SMOOSHED,shoulder left dad,df1,body chunk upright,df2,df3,df4,hat front piece,df5,foot rested right 2,hand in,arm spun,shoe raised left,bf 1 finger hand,bf mouth 1,Boyfriend DJ confirm,forearm down ,hand raised left,remote thumb up]
*/
override public function listAnimations():Array<String>
{
var anims:Array<String> = [];
@:privateAccess
for (animKey in anim.symbolDictionary)
{
anims.push(animKey.name);
}
return anims;
}
public override function update(elapsed:Float):Void
@ -44,51 +83,68 @@ class DJBoyfriend extends FlxSprite
{
case Intro:
// Play the intro animation then leave this state immediately.
if (getCurrentAnimation() != 'intro') playAnimation('intro', true);
if (getCurrentAnimation() != 'boyfriend dj intro') playFlashAnimation('boyfriend dj intro', true);
timeSinceSpook = 0;
case Idle:
// We are in this state the majority of the time.
if (getCurrentAnimation() != 'idle' || animation.finished)
if (getCurrentAnimation() != 'Boyfriend DJ' || anim.finished)
{
if (timeSinceSpook > SPOOK_PERIOD)
if (timeSinceSpook > SPOOK_PERIOD && !gotSpooked)
{
currentState = Spook;
}
else if (timeSinceSpook > TV_PERIOD)
{
currentState = TV;
}
else
{
playAnimation('idle', false);
playFlashAnimation('Boyfriend DJ', false);
}
}
timeSinceSpook += elapsed;
case Confirm:
if (getCurrentAnimation() != 'confirm') playAnimation('confirm', false);
if (getCurrentAnimation() != 'Boyfriend DJ confirm') playFlashAnimation('Boyfriend DJ confirm', false);
timeSinceSpook = 0;
case Spook:
if (getCurrentAnimation() != 'spook')
if (getCurrentAnimation() != 'bf dj afk')
{
onSpook.dispatch();
playAnimation('spook', false);
playFlashAnimation('bf dj afk', false);
}
timeSinceSpook = 0;
case TV:
if (getCurrentAnimation() != 'Boyfriend DJ watchin tv OG') playFlashAnimation('Boyfriend DJ watchin tv OG', true);
timeSinceSpook = 0;
default:
// I shit myself.
}
}
function onFinishAnim(name:String):Void
function onFinishAnim():Void
{
var name = anim.curSymbol.name;
switch (name)
{
case "intro":
case "boyfriend dj intro":
// trace('Finished intro');
currentState = Idle;
onIntroDone.dispatch();
case "idle":
case "Boyfriend DJ":
// trace('Finished idle');
case "spook":
case "bf dj afk":
// trace('Finished spook');
currentState = Idle;
case "confirm":
case "Boyfriend DJ confirm":
case "Boyfriend DJ watchin tv OG":
var frame:Int = FlxG.random.bool(33) ? 112 : 166;
if (FlxG.random.bool(10))
{
frame = 60;
// boyfriend switches channel code?
}
anim.play("Boyfriend DJ watchin tv OG", true, false, frame);
// trace('Finished confirm');
}
}
@ -100,19 +156,66 @@ class DJBoyfriend extends FlxSprite
function setupAnimations():Void
{
frames = FlxAnimationUtil.combineFramesCollections(Paths.getSparrowAtlas('freeplay/bfFreeplay'), Paths.getSparrowAtlas('freeplay/bf-freeplay-afk'));
// frames = FlxAnimationUtil.combineFramesCollections(Paths.getSparrowAtlas('freeplay/bfFreeplay'), Paths.getSparrowAtlas('freeplay/bf-freeplay-afk'));
animation.addByPrefix('intro', "boyfriend dj intro", 24, false);
addOffset('intro', 0, 0);
// animation.addByPrefix('intro', "boyfriend dj intro", 24, false);
addOffset('boyfriend dj intro', 8, 3);
animation.addByPrefix('idle', "Boyfriend DJ0", 24, false);
addOffset('idle', -4, -426);
// animation.addByPrefix('idle', "Boyfriend DJ0", 24, false);
addOffset('Boyfriend DJ', 0, 0);
animation.addByPrefix('confirm', "Boyfriend DJ confirm", 24, false);
addOffset('confirm', 40, -451);
// animation.addByPrefix('confirm', "Boyfriend DJ confirm", 24, false);
addOffset('Boyfriend DJ confirm', 0, 0);
animation.addByPrefix('spook', "bf dj afk0", 24, false);
addOffset('spook', -3, -272);
// animation.addByPrefix('spook', "bf dj afk0", 24, false);
addOffset('bf dj afk', 0, 0);
}
var cartoonSnd:FlxStreamSound;
public var playingCartoon:Bool = false;
public function runTvLogic()
{
if (cartoonSnd == null)
{
// tv is OFF, but getting turned on
FlxG.sound.play(Paths.sound('tv_on'));
cartoonSnd = new FlxStreamSound();
FlxG.sound.defaultSoundGroup.add(cartoonSnd);
}
else
{
// plays it smidge after the click
new FlxTimer().start(0.1, function(_) {
FlxG.sound.play(Paths.sound('channel_switch'));
});
}
// cartoonSnd.loadEmbedded(Paths.sound("cartoons/peck"));
// cartoonSnd.play();
loadCartoon();
}
function loadCartoon()
{
cartoonSnd.loadEmbedded(Paths.sound(getRandomFlashToon()), false, false, function() {
anim.play("Boyfriend DJ watchin tv OG", true, false, 60);
});
cartoonSnd.play(true, FlxG.random.float(0, cartoonSnd.length));
}
var cartoonList:Array<String> = openfl.utils.Assets.list().filter(function(path) return path.startsWith("assets/sounds/cartoons/"));
function getRandomFlashToon():String
{
var randomFile = FlxG.random.getObject(cartoonList);
randomFile = randomFile.replace("assets/sounds/", "");
randomFile = randomFile.substring(0, randomFile.length - 4);
return randomFile;
}
public function confirm():Void
@ -125,15 +228,15 @@ class DJBoyfriend extends FlxSprite
animOffsets[name] = [x, y];
}
public function getCurrentAnimation():String
override public function getCurrentAnimation():String
{
if (this.animation == null || this.animation.curAnim == null) return "";
return this.animation.curAnim.name;
if (this.anim == null || this.anim.curSymbol == null) return "";
return this.anim.curSymbol.name;
}
public function playAnimation(AnimName:String, Force:Bool = false, Reversed:Bool = false, Frame:Int = 0):Void
public function playFlashAnimation(id:String, ?Force:Bool = false, ?Reverse:Bool = false, ?Frame:Int = 0):Void
{
animation.play(AnimName, Force, Reversed, Frame);
anim.play(id, Force, Reverse, Frame);
applyAnimOffset();
}
@ -156,4 +259,5 @@ enum DJBoyfriendState
Idle;
Confirm;
Spook;
TV;
}

View file

@ -0,0 +1,106 @@
package funkin.freeplayStuff;
import flixel.group.FlxSpriteGroup;
import funkin.graphics.adobeanimate.FlxAtlasSprite;
import funkin.shaderslmfao.HSVShader;
class DifficultyStars extends FlxSpriteGroup
{
/**
* Internal handler var for difficulty... ranges from 0... to 15
* 0 is 1 star... 15 is 0 stars!
*/
var curDifficulty(default, set):Int = 0;
/**
* Range between 0 and 15
*/
public var difficulty(default, set):Int = 1;
public var stars:FlxAtlasSprite;
var flames:FreeplayFlames;
var hsvShader:HSVShader;
public function new(x:Float, y:Float)
{
super(x, y);
hsvShader = new HSVShader();
flames = new FreeplayFlames(0, 0);
add(flames);
stars = new FlxAtlasSprite(0, 0, Paths.animateAtlas("freeplay/freeplayStars"));
stars.anim.play("diff stars");
add(stars);
stars.shader = hsvShader;
for (memb in flames.members)
memb.shader = hsvShader;
}
override function update(elapsed:Float):Void
{
super.update(elapsed);
// "loops" the current animation
// for clarity, the animation file looks like
// frame : stars
// 0-99: 1 star
// 100-199: 2 stars
// ......
// 1300-1499: 15 stars
// 1500 : 0 stars
if (curDifficulty < 15 && stars.anim.curFrame >= (curDifficulty + 1) * 100)
{
stars.anim.play("diff stars", true, false, curDifficulty * 100);
}
}
function set_difficulty(value:Int):Int
{
difficulty = value;
if (difficulty <= 0)
{
difficulty = 0;
curDifficulty = 15;
}
else if (difficulty <= 15)
{
difficulty = value;
curDifficulty = difficulty - 1;
}
else
{
difficulty = 15;
curDifficulty = difficulty - 1;
}
if (difficulty > 10) flames.flameCount = difficulty - 10;
else
flames.flameCount = 0;
return difficulty;
}
function set_curDifficulty(value:Int):Int
{
curDifficulty = value;
if (curDifficulty == 15)
{
stars.anim.play("diff stars", true, false, 1500);
stars.anim.pause();
}
else
{
stars.anim.curFrame = Std.int(curDifficulty * 100);
stars.anim.play("diff stars", true, false, curDifficulty * 100);
}
return curDifficulty;
}
}

View file

@ -0,0 +1,117 @@
package funkin.freeplayStuff;
import flixel.group.FlxSpriteGroup;
import flixel.FlxSprite;
import flixel.util.FlxTimer;
class FreeplayFlames extends FlxSpriteGroup
{
var flameX(default, set):Float = 917;
var flameY(default, set):Float = 103;
var flameSpreadX(default, set):Float = 29;
var flameSpreadY(default, set):Float = 6;
public var flameCount(default, set):Int = 0;
var flameTimer:Float = 0.25;
public function new(x:Float, y:Float)
{
super(x, y);
for (i in 0...5)
{
var flame:FlxSprite = new FlxSprite(flameX + (flameSpreadX * i), flameY + (flameSpreadY * i));
flame.frames = Paths.getSparrowAtlas("freeplay/freeplayFlame");
flame.animation.addByPrefix("flame", "fire loop", FlxG.random.int(23, 25), false);
flame.animation.play("flame");
flame.visible = false;
flameCount = 0;
// sets the loop... maybe better way to do this lol!
flame.animation.finishCallback = function(_) {
flame.animation.play("flame", true, false, 2);
};
add(flame);
}
}
var properPositions:Bool = false;
override public function update(elapsed:Float):Void
{
super.update(elapsed);
// doesn't work in create()/new() for some reason
// so putting it here bwah!
if (!properPositions)
{
setFlamePositions();
properPositions = true;
}
}
function set_flameCount(value:Int):Int
{
this.flameCount = value;
var visibleCount:Int = 0;
for (i in 0...5)
{
if (members[i] == null) continue;
var flame:FlxSprite = members[i];
if (i < flameCount)
{
if (!flame.visible)
{
new FlxTimer().start(flameTimer * visibleCount, function(_) {
flame.animation.play("flame", true);
flame.visible = true;
});
visibleCount++;
}
}
else
{
flame.visible = false;
}
}
return this.flameCount;
}
function setFlamePositions()
{
for (i in 0...5)
{
var flame:FlxSprite = members[i];
flame.x = flameX + (flameSpreadX * i);
flame.y = flameY + (flameSpreadY * i);
}
}
function set_flameX(value:Float):Float
{
this.flameX = value;
setFlamePositions();
return this.flameX;
}
function set_flameY(value:Float):Float
{
this.flameY = value;
setFlamePositions();
return this.flameY;
}
function set_flameSpreadX(value:Float):Float
{
this.flameSpreadX = value;
setFlamePositions();
return this.flameSpreadX;
}
function set_flameSpreadY(value:Float):Float
{
this.flameSpreadY = value;
setFlamePositions();
return this.flameSpreadY;
}
}

View file

@ -4,38 +4,68 @@ import flixel.FlxSprite;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.group.FlxGroup;
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
import flixel.tweens.FlxTween;
import flixel.tweens.FlxEase;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import funkin.graphics.adobeanimate.FlxAtlasSprite;
class LetterSort extends FlxTypedSpriteGroup<FreeplayLetter>
class LetterSort extends FlxTypedSpriteGroup<FlxSprite>
{
public var letters:Array<FreeplayLetter> = [];
var curSelection:Int = 0;
// starts at 2, cuz that's the middle letter on start (accounting for fav and #, it should begin at ALL filter)
var curSelection:Int = 2;
public var changeSelectionCallback:String->Void;
var leftArrow:FlxSprite;
var rightArrow:FlxSprite;
var grpSeperators:Array<FlxSprite> = [];
public function new(x, y)
{
super(x, y);
var leftArrow:FreeplayLetter = new FreeplayLetter(-20, 0);
leftArrow.animation.play("arrow");
leftArrow = new FlxSprite(-20, 15).loadGraphic(Paths.image("freeplay/miniArrow"));
// leftArrow.animation.play("arrow");
leftArrow.flipX = true;
add(leftArrow);
for (i in 0...6)
for (i in 0...5)
{
var letter:FreeplayLetter = new FreeplayLetter(i * 80, 0, i);
letter.x += 50;
letter.y += 50;
letter.ogY = y;
// letter.visible = false;
add(letter);
letters.push(letter);
if (i == 3) letter.alpha = 0.6;
if (i != 2) letter.scale.x = letter.scale.y = 0.8;
var sep:FreeplayLetter = new FreeplayLetter((i * 80) + 50, 0);
sep.animation.play("seperator");
var darkness:Float = Math.abs(i - 2) / 6;
letter.color = letter.color.getDarkened(darkness);
// don't put the last seperator
if (i == 4) continue;
var sep:FlxSprite = new FlxSprite((i * 80) + 55, 20).loadGraphic(Paths.image("freeplay/seperator"));
// sep.animation.play("seperator");
sep.color = letter.color.getDarkened(darkness);
add(sep);
grpSeperators.push(sep);
}
// changeSelection(-3);
rightArrow = new FlxSprite(380, 15).loadGraphic(Paths.image("freeplay/miniArrow"));
// rightArrow.animation.play("arrow");
add(rightArrow);
changeSelection(0);
}
override function update(elapsed:Float)
@ -48,53 +78,168 @@ class LetterSort extends FlxTypedSpriteGroup<FreeplayLetter>
public function changeSelection(diff:Int = 0)
{
for (letter in letters)
letter.changeLetter(diff);
var ezTimer:Int->FlxSprite->Float->Void = function(frameNum:Int, spr:FlxSprite, offsetNum:Float) {
new FlxTimer().start(frameNum / 24, function(_) {
spr.offset.x = offsetNum;
});
};
if (changeSelectionCallback != null) changeSelectionCallback(letters[3].arr[letters[3].curLetter]); // bullshit and long lol!
var positions:Array<Float> = [-10, -22, 2, 0];
if (diff < 0)
{
for (sep in grpSeperators)
{
ezTimer(0, sep, positions[0]);
ezTimer(1, sep, positions[1]);
ezTimer(2, sep, positions[2]);
ezTimer(3, sep, positions[3]);
}
for (index => letter in letters)
{
letter.offset.x = positions[0];
new FlxTimer().start(1 / 24, function(_) {
letter.offset.x = positions[1];
if (index == 0) letter.visible = false;
});
new FlxTimer().start(2 / 24, function(_) {
letter.offset.x = positions[2];
if (index == 0.) letter.visible = true;
});
if (index == 2)
{
ezTimer(3, letter, 0);
// letter.offset.x = 0;
continue;
}
ezTimer(3, letter, positions[3]);
}
leftArrow.offset.x = 3;
new FlxTimer().start(2 / 24, function(_) {
leftArrow.offset.x = 0;
});
}
else if (diff > 0)
{
for (sep in grpSeperators)
{
ezTimer(0, sep, -positions[0]);
ezTimer(1, sep, -positions[1]);
ezTimer(2, sep, -positions[2]);
ezTimer(3, sep, -positions[3]);
}
// same timing and functions and shit as the left one... except to the right!!
for (index => letter in letters)
{
letter.offset.x = -positions[0];
new FlxTimer().start(1 / 24, function(_) {
letter.offset.x = -positions[1];
if (index == 0) letter.visible = false;
});
new FlxTimer().start(2 / 24, function(_) {
letter.offset.x = -positions[2];
if (index == 0) letter.visible = true;
});
if (index == 2)
{
ezTimer(3, letter, 0);
// letter.offset.x = 0;
continue;
}
ezTimer(3, letter, -positions[3]);
}
rightArrow.offset.x = -3;
new FlxTimer().start(2 / 24, function(_) {
rightArrow.offset.x = 0;
});
}
curSelection += diff;
if (curSelection < 0) curSelection = letters[0].arr.length - 1;
if (curSelection >= letters[0].arr.length) curSelection = 0;
for (letter in letters)
letter.changeLetter(diff, curSelection);
if (changeSelectionCallback != null) changeSelectionCallback(letters[2].arr[letters[2].curLetter]); // bullshit and long lol!
}
}
class FreeplayLetter extends FlxSprite
class FreeplayLetter extends FlxAtlasSprite
{
public var arr:Array<String> = [];
public var curLetter:Int = 0;
public var ogY:Float = 0;
public function new(x:Float, y:Float, ?letterInd:Int)
{
super(x, y);
super(x, y, Paths.animateAtlas("freeplay/sortedLetters"));
// frames = Paths.getSparrowAtlas("freeplay/letterStuff");
// this.anim.play("AB");
// trace(this.anim.symbolDictionary);
frames = Paths.getSparrowAtlas("freeplay/letterStuff");
var alphabet:String = "abcdefghijklmnopqrstuvwxyz";
arr = alphabet.split("");
arr.insert(0, "#");
var alphabet:String = "AB-CD-EH-I L-MN-OR-s-t-UZ";
arr = alphabet.split("-");
arr.insert(0, "ALL");
arr.insert(0, "fav");
arr.insert(0, "#");
for (str in arr)
{
animation.addByPrefix(str, str + " "); // string followed by a space! intentional!
}
// trace(arr);
animation.addByPrefix("arrow", "mini arrow");
animation.addByPrefix("seperator", "seperator");
// for (str in arr)
// {
// animation.addByPrefix(str, str + " "); // string followed by a space! intentional!
// }
// animation.addByPrefix("arrow", "mini arrow");
// animation.addByPrefix("seperator", "seperator");
if (letterInd != null)
{
animation.play(arr[letterInd]);
this.anim.play(arr[letterInd] + " move");
this.anim.pause();
curLetter = letterInd;
}
}
public function changeLetter(diff:Int = 0)
public function changeLetter(diff:Int = 0, ?curSelection:Int)
{
curLetter += diff;
if (curLetter < 0) curLetter = arr.length - 1;
if (curLetter >= arr.length) curLetter = 0;
animation.play(arr[curLetter]);
var animName:String = arr[curLetter] + " move";
switch (arr[curLetter])
{
case "I L":
animName = "IL move";
case "s":
animName = "S move";
case "t":
animName = "T move";
}
this.anim.play(animName);
if (curSelection != curLetter)
{
this.anim.pause();
}
// updateHitbox();
}
}

View file

@ -1,5 +1,8 @@
package funkin.freeplayStuff;
import funkin.shaderslmfao.HSVShader;
import funkin.shaderslmfao.GaussianBlurShader;
import flixel.group.FlxGroup;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
@ -7,17 +10,29 @@ import flixel.group.FlxSpriteGroup;
import flixel.math.FlxMath;
import flixel.math.FlxPoint;
import flixel.text.FlxText;
import flixel.util.FlxTimer;
import funkin.shaderslmfao.Grayscale;
class SongMenuItem extends FlxSpriteGroup
{
var capsule:FlxSprite;
public var capsule:FlxSprite;
public var selected(default, set):Bool = false;
var pixelIcon:FlxSprite;
public var selected(default, set):Bool;
public var songTitle:String = "Test";
public var songText:FlxText;
public var songText:CapsuleText;
public var favIcon:FlxSprite;
public var ranking:FlxSprite;
var ranks:Array<String> = ["fail", "average", "great", "excellent", "perfect"];
// lol...
var diffRanks:Array<String> = [
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "14", "15"
];
public var targetPos:FlxPoint = new FlxPoint();
public var doLerp:Bool = false;
@ -25,7 +40,12 @@ class SongMenuItem extends FlxSpriteGroup
public var doJumpOut:Bool = false;
public function new(x:Float, y:Float, song:String)
public var onConfirm:Void->Void;
public var diffGrayscale:Grayscale;
public var hsvShader(default, set):HSVShader;
public function new(x:Float, y:Float, song:String, ?character:String)
{
super(x, y);
@ -38,19 +58,144 @@ class SongMenuItem extends FlxSpriteGroup
// capsule.animation
add(capsule);
songText = new FlxText(120, 40, 0, songTitle, 40);
songText.font = "5by7";
songText.color = 0xFF43C1EA;
add(songText);
// doesn't get added, simply is here to help with visibility of things for the pop in!
grpHide = new FlxGroup();
favIcon = new FlxSprite(440, 40);
var rank:String = FlxG.random.getObject(ranks);
ranking = new FlxSprite(capsule.width * 0.84, 30);
ranking.loadGraphic(Paths.image("freeplay/ranks/" + rank));
ranking.scale.x = ranking.scale.y = realScaled;
ranking.alpha = 0.75;
ranking.origin.set(capsule.origin.x - ranking.x, capsule.origin.y - ranking.y);
add(ranking);
grpHide.add(ranking);
diffGrayscale = new Grayscale(1);
var diffRank = new FlxSprite(145, 90).loadGraphic(Paths.image("freeplay/diffRankings/diff" + FlxG.random.getObject(diffRanks)));
diffRank.shader = diffGrayscale;
diffRank.visible = false;
add(diffRank);
diffRank.origin.set(capsule.origin.x - diffRank.x, capsule.origin.y - diffRank.y);
grpHide.add(diffRank);
switch (rank)
{
case "perfect":
ranking.x -= 10;
}
songText = new CapsuleText(capsule.width * 0.26, 45, songTitle, Std.int(40 * realScaled));
add(songText);
grpHide.add(songText);
pixelIcon = new FlxSprite(155, 15);
pixelIcon.makeGraphic(32, 32, 0x00000000);
pixelIcon.antialiasing = false;
pixelIcon.active = false;
add(pixelIcon);
grpHide.add(pixelIcon);
if (character != null) setCharacter(character);
favIcon = new FlxSprite(400, 40);
favIcon.frames = Paths.getSparrowAtlas('freeplay/favHeart');
favIcon.animation.addByPrefix('fav', "favorite heart", 24, false);
favIcon.animation.play('fav');
favIcon.setGraphicSize(60, 60);
favIcon.setGraphicSize(50, 50);
favIcon.visible = false;
add(favIcon);
// grpHide.add(favIcon);
selected = selected; // just to kickstart the set_selected
setVisibleGrp(false);
}
function set_hsvShader(value:HSVShader):HSVShader
{
this.hsvShader = value;
capsule.shader = hsvShader;
songText.shader = hsvShader;
return value;
}
function textAppear()
{
songText.scale.x = 1.7;
songText.scale.y = 0.2;
new FlxTimer().start(1 / 24, function(_) {
songText.scale.x = 0.4;
songText.scale.y = 1.4;
});
new FlxTimer().start(2 / 24, function(_) {
songText.scale.x = songText.scale.y = 1;
});
}
function setVisibleGrp(value:Bool)
{
for (spr in grpHide.members)
{
spr.visible = value;
}
if (value) textAppear();
selectedAlpha();
}
public function init(x:Float, y:Float, song:String, ?character:String)
{
this.x = x;
this.y = y;
this.songTitle = song;
songText.text = this.songTitle;
if (character != null) setCharacter(character);
selected = selected;
}
/**
* Set the character displayed next to this song in the freeplay menu.
* @param char The character ID used by this song.
* If the character has no freeplay icon, a warning will be thrown and nothing will display.
*/
public function setCharacter(char:String)
{
var charPath:String = "freeplay/icons/";
trace(char);
switch (char)
{
case "monster-christmas":
charPath += "monsterpixel";
case "mom-car":
charPath += "mommypixel";
case "dad":
charPath += "daddypixel";
case "darnell-blazin":
charPath += "darnellpixel";
case "senpai-angry":
charPath += "senpaipixel";
default:
charPath += char + "pixel";
}
if (!openfl.utils.Assets.exists(Paths.image(charPath)))
{
trace('[WARN] Character ${char} has no freeplay icon.');
return;
}
pixelIcon.loadGraphic(Paths.image(charPath));
pixelIcon.scale.x = pixelIcon.scale.y = 2;
pixelIcon.origin.x = 100;
// pixelIcon.origin.x = capsule.origin.x;
// pixelIcon.offset.x -= pixelIcon.origin.x;
}
var frameInTicker:Float = 0;
@ -63,6 +208,63 @@ class SongMenuItem extends FlxSpriteGroup
var xPosLerpLol:Array<Float> = [0.9, 0.4, 0.16, 0.16, 0.22, 0.22, 0.245]; // NUMBERS ARE JANK CUZ THE SCALING OR WHATEVER
var xPosOutLerpLol:Array<Float> = [0.245, 0.75, 0.98, 0.98, 1.2]; // NUMBERS ARE JANK CUZ THE SCALING OR WHATEVER
public var realScaled:Float = 0.8;
public function initJumpIn(maxTimer:Float, ?force:Bool):Void
{
frameInTypeBeat = 0;
new FlxTimer().start((1 / 24) * maxTimer, function(doShit) {
doJumpIn = true;
});
new FlxTimer().start((0.09 * maxTimer) + 0.85, function(lerpTmr) {
doLerp = true;
});
if (force)
{
visible = true;
capsule.alpha = 1;
setVisibleGrp(true);
}
else
{
new FlxTimer().start((xFrames.length / 24) * 2.5, function(_) {
visible = true;
capsule.alpha = 1;
setVisibleGrp(true);
});
}
}
var grpHide:FlxGroup;
public function forcePosition()
{
visible = true;
capsule.alpha = 1;
selectedAlpha();
doLerp = true;
doJumpIn = false;
doJumpOut = false;
frameInTypeBeat = xFrames.length;
frameOutTypeBeat = 0;
capsule.scale.x = xFrames[frameInTypeBeat - 1];
capsule.scale.y = 1 / xFrames[frameInTypeBeat - 1];
// x = FlxG.width * xPosLerpLol[Std.int(Math.min(frameInTypeBeat - 1, xPosLerpLol.length - 1))];
x = targetPos.x;
y = targetPos.y;
capsule.scale.x *= realScaled;
capsule.scale.y *= realScaled;
setVisibleGrp(true);
}
override function update(elapsed:Float)
{
if (doJumpIn)
@ -73,10 +275,13 @@ class SongMenuItem extends FlxSpriteGroup
{
frameInTicker = 0;
scale.x = xFrames[frameInTypeBeat];
scale.y = 1 / xFrames[frameInTypeBeat];
capsule.scale.x = xFrames[frameInTypeBeat];
capsule.scale.y = 1 / xFrames[frameInTypeBeat];
x = FlxG.width * xPosLerpLol[Std.int(Math.min(frameInTypeBeat, xPosLerpLol.length - 1))];
capsule.scale.x *= realScaled;
capsule.scale.y *= realScaled;
frameInTypeBeat += 1;
}
}
@ -89,10 +294,13 @@ class SongMenuItem extends FlxSpriteGroup
{
frameOutTicker = 0;
scale.x = xFrames[frameOutTypeBeat];
scale.y = 1 / xFrames[frameOutTypeBeat];
capsule.scale.x = xFrames[frameOutTypeBeat];
capsule.scale.y = 1 / xFrames[frameOutTypeBeat];
x = FlxG.width * xPosOutLerpLol[Std.int(Math.min(frameOutTypeBeat, xPosOutLerpLol.length - 1))];
capsule.scale.x *= realScaled;
capsule.scale.y *= realScaled;
frameOutTypeBeat += 1;
}
}
@ -106,14 +314,29 @@ class SongMenuItem extends FlxSpriteGroup
super.update(elapsed);
}
public function intendedY(index:Int):Float
{
return (index * ((height * realScaled) + 10)) + 120;
}
/**
* Merely a helper function to call set_selected, to make sure that the alpha is correct on the rankings/selections
*/
public function selectedAlpha():Void
{
selected = selected;
}
function set_selected(value:Bool):Bool
{
// trace(value);
// cute one liners, lol!
diffGrayscale.setAmount(value ? 0 : 0.8);
songText.alpha = value ? 1 : 0.6;
songText.blurredText.visible = value ? true : false;
capsule.offset.x = value ? 0 : -5;
capsule.animation.play(value ? "selected" : "unselected");
ranking.alpha = value ? 1 : 0.7;
ranking.color = value ? 0xFFFFFFFF : 0xFFAAAAAA;
return value;
}
}

View file

@ -181,7 +181,7 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
updateKeyStates(key, true);
if (getInputByKey(key) ?.justPressed ?? false)
if (getInputByKey(key)?.justPressed ?? false)
{
onInputPressed.dispatch(
{
@ -203,7 +203,7 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
updateKeyStates(key, false);
if (getInputByKey(key) ?.justReleased ?? false)
if (getInputByKey(key)?.justReleased ?? false)
{
onInputReleased.dispatch(
{
@ -264,7 +264,7 @@ class PreciseInputList extends FlxKeyList
{
for (key in getKeysForDir(noteDir))
{
if (check(_preciseInputManager.getInputByKey(key) ?.ID)) return true;
if (check(_preciseInputManager.getInputByKey(key)?.ID)) return true;
}
return false;
}

View file

@ -511,41 +511,7 @@ class PlayState extends MusicBeatSubState
NoteSplash.buildSplashFrames();
// Returns null if the song failed to load or doesn't have the selected difficulty.
if (currentSong == null || currentChart == null)
{
// We have encountered a critical error. Prevent Flixel from trying to run any gameplay logic.
criticalFailure = true;
// Choose an error message.
var message:String = 'There was a critical error. Click OK to return to the main menu.';
if (currentSong == null)
{
message = 'The was a critical error loading this song\'s chart. Click OK to return to the main menu.';
}
else if (currentDifficulty == null)
{
message = 'The was a critical error selecting a difficulty for this song. Click OK to return to the main menu.';
}
else if (currentSong.getDifficulty(currentDifficulty) == null)
{
message = 'The was a critical error retrieving data for this song on "$currentDifficulty" difficulty. Click OK to return to the main menu.';
}
// Display a popup. This blocks the application until the user clicks OK.
lime.app.Application.current.window.alert(message, 'Error loading PlayState');
// Force the user back to the main menu.
if (isSubState)
{
this.close();
}
else
{
FlxG.switchState(new MainMenuState());
}
return;
}
if (!assertChartExists()) return;
if (false)
{
@ -660,6 +626,47 @@ class PlayState extends MusicBeatSubState
initialized = true;
}
function assertChartExists():Bool
{
// Returns null if the song failed to load or doesn't have the selected difficulty.
if (currentSong == null || currentChart == null)
{
// We have encountered a critical error. Prevent Flixel from trying to run any gameplay logic.
criticalFailure = true;
// Choose an error message.
var message:String = 'There was a critical error. Click OK to return to the main menu.';
if (currentSong == null)
{
message = 'The was a critical error loading this song\'s chart. Click OK to return to the main menu.';
}
else if (currentDifficulty == null)
{
message = 'The was a critical error selecting a difficulty for this song. Click OK to return to the main menu.';
}
else if (currentSong.getDifficulty(currentDifficulty) == null)
{
message = 'The was a critical error retrieving data for this song on "$currentDifficulty" difficulty. Click OK to return to the main menu.';
}
// Display a popup. This blocks the application until the user clicks OK.
lime.app.Application.current.window.alert(message, 'Error loading PlayState');
// Force the user back to the main menu.
if (isSubState)
{
this.close();
}
else
{
FlxG.switchState(new MainMenuState());
}
return false;
}
return true;
}
public override function update(elapsed:Float):Void
{
if (criticalFailure) return;
@ -672,6 +679,8 @@ class PlayState extends MusicBeatSubState
// Handle restarting the song when needed (player death or pressing Retry)
if (needsReset)
{
if (!assertChartExists()) return;
dispatchEvent(new ScriptEvent(ScriptEvent.SONG_RETRY));
resetCamera();
@ -686,8 +695,10 @@ class PlayState extends MusicBeatSubState
// Reset music properly.
FlxG.sound.music.pause();
vocals.pause();
FlxG.sound.music.time = (startTimestamp);
vocals = currentChart.buildVocals();
vocals.pause();
vocals.time = 0;
FlxG.sound.music.volume = 1;
@ -1879,6 +1890,7 @@ class PlayState extends MusicBeatSubState
{
// Grant the player health.
health += Constants.HEALTH_HOLD_BONUS_PER_SECOND * elapsed;
songScore += Std.int(Constants.SCORE_HOLD_BONUS_PER_SECOND * elapsed);
}
// TODO: Potential penalty for dropping a hold note?
@ -1999,103 +2011,6 @@ class PlayState extends MusicBeatSubState
}
}
/**
* Handle player inputs.
*/
function keyShit(test:Bool):Void
{
// control arrays, order L D R U
var holdArray:Array<Bool> = [controls.NOTE_LEFT, controls.NOTE_DOWN, controls.NOTE_UP, controls.NOTE_RIGHT];
var pressArray:Array<Bool> = [
controls.NOTE_LEFT_P,
controls.NOTE_DOWN_P,
controls.NOTE_UP_P,
controls.NOTE_RIGHT_P
];
var releaseArray:Array<Bool> = [
controls.NOTE_LEFT_R,
controls.NOTE_DOWN_R,
controls.NOTE_UP_R,
controls.NOTE_RIGHT_R
];
// if (pressArray.contains(true))
// {
// var lol:Array<Int> = cast pressArray;
// inputSpitter.push(Std.int(Conductor.songPosition) + ' ' + lol.join(' '));
// }
// HOLDS, check for sustain notes
if (holdArray.contains(true) && generatedMusic)
{
/*
activeNotes.forEachAlive(function(daNote:Note) {
if (daNote.isSustainNote && daNote.canBeHit && daNote.mustPress && holdArray[daNote.data.noteData]) goodNoteHit(daNote);
});
*/
}
// PRESSES, check for note hits
if (pressArray.contains(true) && generatedMusic)
{
Haptic.vibrate(100, 100);
if (currentStage != null && currentStage.getBoyfriend() != null)
{
currentStage.getBoyfriend().holdTimer = 0;
}
var possibleNotes:Array<NoteSprite> = []; // notes that can be hit
var directionList:Array<Int> = []; // directions that can be hit
var dumbNotes:Array<NoteSprite> = []; // notes to kill later
for (note in dumbNotes)
{
FlxG.log.add('killing dumb ass note at ' + note.noteData.time);
note.kill();
// activeNotes.remove(note, true);
note.destroy();
}
possibleNotes.sort((a, b) -> Std.int(a.noteData.time - b.noteData.time));
if (perfectMode)
{
goodNoteHit(possibleNotes[0], null);
}
else if (possibleNotes.length > 0)
{
for (shit in 0...pressArray.length)
{ // if a direction is hit that shouldn't be
if (pressArray[shit] && !directionList.contains(shit)) ghostNoteMiss(shit);
}
for (coolNote in possibleNotes)
{
if (pressArray[coolNote.noteData.getDirection()]) goodNoteHit(coolNote, null);
}
}
else
{
// HNGGG I really want to add an option for ghost tapping
// L + ratio
for (shit in 0...pressArray.length)
if (pressArray[shit]) ghostNoteMiss(shit, false);
}
}
if (currentStage == null) return;
for (keyId => isPressed in pressArray)
{
if (playerStrumline == null) continue;
var dir:NoteDirection = Strumline.DIRECTIONS[keyId];
if (isPressed && !playerStrumline.isConfirm(dir)) playerStrumline.playPress(dir);
if (!holdArray[keyId]) playerStrumline.playStatic(dir);
}
}
function goodNoteHit(note:NoteSprite, input:PreciseInputEvent):Void
{
var event:NoteScriptEvent = new NoteScriptEvent(ScriptEvent.NOTE_HIT, note, Highscore.tallies.combo + 1, true);
@ -2104,19 +2019,16 @@ class PlayState extends MusicBeatSubState
// Calling event.cancelEvent() skips all the other logic! Neat!
if (event.eventCanceled) return;
if (!note.isHoldNote)
{
Highscore.tallies.combo++;
Highscore.tallies.totalNotesHit++;
Highscore.tallies.combo++;
Highscore.tallies.totalNotesHit++;
if (Highscore.tallies.combo > Highscore.tallies.maxCombo) Highscore.tallies.maxCombo = Highscore.tallies.combo;
if (Highscore.tallies.combo > Highscore.tallies.maxCombo) Highscore.tallies.maxCombo = Highscore.tallies.combo;
popUpScore(note, input);
}
popUpScore(note, input);
playerStrumline.hitNote(note);
if (note.holdNoteSprite != null)
if (note.isHoldNote && note.holdNoteSprite != null)
{
playerStrumline.playNoteHoldCover(note.holdNoteSprite);
}

View file

@ -649,16 +649,20 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
}
boppers = [];
for (sprite in this.group)
if (group != null)
{
if (sprite != null)
for (sprite in this.group)
{
sprite.kill();
sprite.destroy();
remove(sprite);
if (sprite != null)
{
sprite.kill();
sprite.destroy();
remove(sprite);
}
}
group.clear();
}
group.clear();
if (debugIconGroup != null && debugIconGroup.group != null)
{
debugIconGroup.kill();

View file

@ -0,0 +1,23 @@
package funkin.shaderslmfao;
import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
import openfl.utils.Assets;
import openfl.display.BitmapData;
class BlendModesShader extends FlxRuntimeShader
{
public var camera:BitmapData;
public function new()
{
super(Assets.getText(Paths.frag('blendModes')));
}
public function setCamera(camera:BitmapData):Void
{
this.camera = camera;
this.setBitmapData('camera', camera);
}
}

View file

@ -0,0 +1,25 @@
package funkin.shaderslmfao;
import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
import openfl.utils.Assets;
/**
* Note... not actually gaussian!
*/
class GaussianBlurShader extends FlxRuntimeShader
{
public var amount:Float;
public function new(amount:Float = 1.0)
{
super(Assets.getText(Paths.frag("gaussianBlur")));
setAmount(amount);
}
public function setAmount(value:Float):Void
{
this.amount = value;
this.setFloat("amount", amount);
}
}

View file

@ -0,0 +1,22 @@
package funkin.shaderslmfao;
import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
import openfl.utils.Assets;
class Grayscale extends FlxRuntimeShader
{
public var amount:Float = 1;
public function new(amount:Float = 1)
{
super(Assets.getText(Paths.frag("grayscale")));
setAmount(amount);
}
public function setAmount(value:Float):Void
{
amount = value;
this.setFloat("amount", amount);
}
}

View file

@ -0,0 +1,44 @@
package funkin.shaderslmfao;
import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
import openfl.utils.Assets;
class HSVShader extends FlxRuntimeShader
{
public var hue(default, set):Float;
public var saturation(default, set):Float;
public var value(default, set):Float;
public function new()
{
super(Assets.getText(Paths.frag('hsv')));
hue = 1;
saturation = 1;
value = 1;
}
function set_hue(value:Float):Float
{
this.setFloat('hue', value);
this.hue = value;
return this.hue;
}
function set_saturation(value:Float):Float
{
this.setFloat('sat', value);
this.saturation = value;
return this.saturation;
}
function set_value(value:Float):Float
{
this.setFloat('val', value);
this.value = value;
return this.value;
}
}

View file

@ -17,6 +17,9 @@ import openfl.geom.Matrix;
import openfl.display.Sprite;
import openfl.display.Bitmap;
using Lambda;
using StringTools;
class StickerSubState extends MusicBeatSubState
{
public var grpStickers:FlxTypedGroup<StickerSprite>;
@ -26,10 +29,60 @@ class StickerSubState extends MusicBeatSubState
var nextState:NEXTSTATE = FREEPLAY;
// what "folders" to potentially load from (as of writing only "keys" exist)
var soundSelections:Array<String> = [];
// what "folder" was randomly selected
var soundSelection:String = "";
var sounds:Array<String> = [];
public function new(?oldStickers:Array<StickerSprite>, ?nextState:NEXTSTATE = FREEPLAY):Void
{
super();
// todo still
// make sure that ONLY plays mp3/ogg files
// if there's no mp3/ogg file, then it regenerates/reloads the random folder
var assetsInList = openfl.utils.Assets.list();
var soundFilterFunc = function(a:String) {
return a.startsWith('assets/shared/sounds/stickersounds/');
};
soundSelections = assetsInList.filter(soundFilterFunc);
soundSelections = soundSelections.map(function(a:String) {
return a.replace('assets/shared/sounds/stickersounds/', '').split('/')[0];
});
// cracked cleanup... yuchh...
for (i in soundSelections)
{
while (soundSelections.contains(i))
{
soundSelections.remove(i);
}
soundSelections.push(i);
}
trace(soundSelections);
soundSelection = FlxG.random.getObject(soundSelections);
var filterFunc = function(a:String) {
return a.startsWith('assets/shared/sounds/stickersounds/' + soundSelection + '/');
};
var assetsInList3 = openfl.utils.Assets.list();
sounds = assetsInList3.filter(filterFunc);
for (i in 0...sounds.length)
{
sounds[i] = sounds[i].replace('assets/shared/sounds/', '');
sounds[i] = sounds[i].substring(0, sounds[i].lastIndexOf('.'));
}
trace(sounds);
// trace(assetsInList);
this.nextState = nextState;
grpStickers = new FlxTypedGroup<StickerSprite>();
@ -66,6 +119,8 @@ class StickerSubState extends MusicBeatSubState
{
new FlxTimer().start(sticker.timing, _ -> {
sticker.visible = false;
var daSound:String = FlxG.random.getObject(sounds);
FlxG.sound.play(Paths.sound(daSound));
if (ind == grpStickers.members.length - 1)
{
@ -151,7 +206,11 @@ class StickerSubState extends MusicBeatSubState
sticker.timing = FlxMath.remapToRange(ind, 0, grpStickers.members.length, 0, 0.9);
new FlxTimer().start(sticker.timing, _ -> {
if (grpStickers == null) return;
sticker.visible = true;
var daSound:String = FlxG.random.getObject(sounds);
FlxG.sound.play(Paths.sound(daSound));
var frameTimer:Int = FlxG.random.int(0, 2);
@ -212,10 +271,10 @@ class StickerSubState extends MusicBeatSubState
{
super.update(elapsed);
if (FlxG.keys.justPressed.ANY)
{
regenStickers();
}
// if (FlxG.keys.justPressed.ANY)
// {
// regenStickers();
// }
}
var switchingState:Bool = false;

View file

@ -89,6 +89,7 @@ class ChartEditorDialogHandler
linkCreateBasic.onClick = function(_event) {
// Hide the welcome dialog
dialog.hideDialog(DialogButton.CANCEL);
state.stopWelcomeMusic();
//
// Create Song Wizard
@ -114,6 +115,7 @@ class ChartEditorDialogHandler
linkImportChartLegacy.onClick = function(_event) {
// Hide the welcome dialog
dialog.hideDialog(DialogButton.CANCEL);
state.stopWelcomeMusic();
// Open the "Import Chart" dialog
openImportChartWizard(state, 'legacy', false);
@ -124,6 +126,7 @@ class ChartEditorDialogHandler
buttonBrowse.onClick = function(_event) {
// Hide the welcome dialog
dialog.hideDialog(DialogButton.CANCEL);
state.stopWelcomeMusic();
// Open the "Open Chart" dialog
openBrowseWizard(state, false);
@ -152,6 +155,7 @@ class ChartEditorDialogHandler
linkTemplateSong.text = songName;
linkTemplateSong.onClick = function(_event) {
dialog.hideDialog(DialogButton.CANCEL);
state.stopWelcomeMusic();
// Load song from template
ChartEditorImportExportHandler.loadSongAsTemplate(state, targetSongId);
@ -160,6 +164,7 @@ class ChartEditorDialogHandler
splashTemplateContainer.addComponent(linkTemplateSong);
}
state.fadeInWelcomeMusic();
return dialog;
}
@ -552,11 +557,9 @@ class ChartEditorDialogHandler
var dialog:Null<Dialog> = openDialog(state, CHART_EDITOR_DIALOG_SONG_METADATA_LAYOUT, true, false);
if (dialog == null) throw 'Could not locate Song Metadata dialog';
var dialogContainer:Null<Dialog> = dialog.findComponent('metadataDialog', Dialog);
if (dialogContainer == null) throw 'Could not locate metadataDialog in Song Metadata dialog';
if (targetVariation != Constants.DEFAULT_VARIATION)
{
dialogContainer.title = 'New Chart - Provide Song Metadata (${targetVariation.toTitleCase()})';
dialog.title = 'New Chart - Provide Song Metadata (${targetVariation.toTitleCase()})';
}
var buttonCancel:Null<Button> = dialog.findComponent('dialogCancel', Button);

View file

@ -137,10 +137,10 @@ class ChartEditorImportExportHandler
/**
* @param force Whether to force the export without prompting the user for a file location.
* @param tmp If true, save to the temporary directory instead of the local `backup` directory.
*/
public static function exportAllSongData(state:ChartEditorState, force:Bool = false, tmp:Bool = false):Void
public static function exportAllSongData(state:ChartEditorState, force:Bool = false):Void
{
var tmp = false;
var zipEntries:Array<haxe.zip.Entry> = [];
for (variation in state.availableVariations)
@ -154,9 +154,9 @@ class ChartEditorImportExportHandler
if (variationId == '')
{
var variationMetadata:Null<SongMetadata> = state.songMetadata.get(variation);
if (variationMetadata != null) zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-metadata.json', SerializerUtil.toJSON(variationMetadata)));
if (variationMetadata != null) zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-metadata.json', variationMetadata.serialize()));
var variationChart:Null<SongChartData> = state.songChartData.get(variation);
if (variationChart != null) zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-chart.json', SerializerUtil.toJSON(variationChart)));
if (variationChart != null) zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-chart.json', variationChart.serialize()));
}
else
{

View file

@ -727,6 +727,16 @@ class ChartEditorState extends HaxeUIState
*/
var downKeyHandler:TurboKeyHandler = TurboKeyHandler.build(FlxKey.DOWN);
/**
* Variable used to track how long the user has been holding the W keybind.
*/
var wKeyHandler:TurboKeyHandler = TurboKeyHandler.build(FlxKey.W);
/**
* Variable used to track how long the user has been holding the S keybind.
*/
var sKeyHandler:TurboKeyHandler = TurboKeyHandler.build(FlxKey.S);
/**
* Variable used to track how long the user has been holding the page-up keybind.
*/
@ -788,6 +798,11 @@ class ChartEditorState extends HaxeUIState
*/
// ==============================
/**
* The chill audio track that plays when you open the Chart Editor.
*/
public var welcomeMusic:FlxSound = new FlxSound();
/**
* The audio track for the instrumental.
* Replaced when switching instrumentals.
@ -1234,6 +1249,9 @@ class ChartEditorState extends HaxeUIState
// Get rid of any music from the previous state.
FlxG.sound.music.stop();
// Play the welcome music.
setupWelcomeMusic();
buildDefaultSongData();
buildBackground();
@ -1258,6 +1276,26 @@ class ChartEditorState extends HaxeUIState
ChartEditorDialogHandler.openWelcomeDialog(this, false);
}
function setupWelcomeMusic()
{
this.welcomeMusic.loadEmbedded(Paths.music('chartEditorLoop/chartEditorLoop'));
this.welcomeMusic.looped = true;
// this.welcomeMusic.play();
// fadeInWelcomeMusic();
}
public function fadeInWelcomeMusic():Void
{
this.welcomeMusic.play();
this.welcomeMusic.fadeIn(4, 0, 1.0);
}
public function stopWelcomeMusic():Void
{
// this.welcomeMusic.fadeOut(4, 0);
this.welcomeMusic.pause();
}
function buildDefaultSongData():Void
{
selectedVariation = Constants.DEFAULT_VARIATION;
@ -1596,6 +1634,7 @@ class ChartEditorState extends HaxeUIState
addUIClickListener('menubarItemSaveChartAs', _ -> ChartEditorImportExportHandler.exportAllSongData(this));
addUIClickListener('menubarItemLoadInst', _ -> ChartEditorDialogHandler.openUploadInstDialog(this, true));
addUIClickListener('menubarItemImportChart', _ -> ChartEditorDialogHandler.openImportChartDialog(this, 'legacy', true));
addUIClickListener('menubarItemExit', _ -> quitChartEditor());
addUIClickListener('menubarItemUndo', _ -> undoLastCommand());
@ -1661,16 +1700,20 @@ class ChartEditorState extends HaxeUIState
addUIClickListener('menubarItemSelectNone', _ -> performCommand(new DeselectAllItemsCommand(currentNoteSelection, currentEventSelection)));
// TODO: Implement these.
// addUIClickListener('menubarItemSelectRegion', _ -> doSomething());
// addUIClickListener('menubarItemSelectBeforeCursor', _ -> doSomething());
// addUIClickListener('menubarItemSelectAfterCursor', _ -> doSomething());
addUIClickListener('menubarItemPlaytestFull', _ -> testSongInPlayState(false));
addUIClickListener('menubarItemPlaytestMinimal', _ -> testSongInPlayState(true));
addUIChangeListener('menubarItemInputStyleGroup', function(event:UIEvent) {
trace('Change input style: ${event.target}');
addUIClickListener('menuBarItemNoteSnapDecrease', _ -> noteSnapQuantIndex--);
addUIClickListener('menuBarItemNoteSnapIncrease', _ -> noteSnapQuantIndex++);
addUIChangeListener('menuBarItemInputStyleNone', function(event:UIEvent) {
currentLiveInputStyle = None;
});
addUIChangeListener('menuBarItemInputStyleNumberKeys', function(event:UIEvent) {
currentLiveInputStyle = NumberKeys;
});
addUIChangeListener('menuBarItemInputStyleWASD', function(event:UIEvent) {
currentLiveInputStyle = WASD;
});
addUIClickListener('menubarItemAbout', _ -> ChartEditorDialogHandler.openAboutDialog(this));
@ -1773,6 +1816,8 @@ class ChartEditorState extends HaxeUIState
add(redoKeyHandler);
add(upKeyHandler);
add(downKeyHandler);
add(wKeyHandler);
add(sKeyHandler);
add(pageUpKeyHandler);
add(pageDownKeyHandler);
}
@ -1799,7 +1844,7 @@ class ChartEditorState extends HaxeUIState
// Auto-save to local storage.
#else
// Auto-save to temp file.
ChartEditorImportExportHandler.exportAllSongData(this, true, true);
ChartEditorImportExportHandler.exportAllSongData(this, true);
#end
}
@ -1821,6 +1866,13 @@ class ChartEditorState extends HaxeUIState
public override function update(elapsed:Float):Void
{
// Override F4 behavior to include the autosave.
if (FlxG.keys.justPressed.F4)
{
quitChartEditor();
return;
}
// dispatchEvent gets called here.
super.update(elapsed);
@ -1900,20 +1952,33 @@ class ChartEditorState extends HaxeUIState
// Mouse Wheel = Scroll
if (FlxG.mouse.wheel != 0 && !FlxG.keys.pressed.CONTROL)
{
scrollAmount = -10 * FlxG.mouse.wheel;
scrollAmount = -50 * FlxG.mouse.wheel;
shouldPause = true;
}
// Up Arrow = Scroll Up
if (upKeyHandler.activated && currentLiveInputStyle != LiveInputStyle.WASD)
if (upKeyHandler.activated && currentLiveInputStyle == None)
{
scrollAmount = -GRID_SIZE * 0.25 * 5.0;
scrollAmount = -GRID_SIZE * 0.25 * 25.0;
shouldPause = true;
}
// Down Arrow = Scroll Down
if (downKeyHandler.activated && currentLiveInputStyle != LiveInputStyle.WASD)
if (downKeyHandler.activated && currentLiveInputStyle == None)
{
scrollAmount = GRID_SIZE * 0.25 * 5.0;
scrollAmount = GRID_SIZE * 0.25 * 25.0;
shouldPause = true;
}
// W = Scroll Up (doesn't work with Ctrl+Scroll)
if (wKeyHandler.activated && currentLiveInputStyle == None && !FlxG.keys.pressed.CONTROL)
{
scrollAmount = -GRID_SIZE * 0.25 * 25.0;
shouldPause = true;
}
// S = Scroll Down (doesn't work with Ctrl+Scroll)
if (sKeyHandler.activated && currentLiveInputStyle == None && !FlxG.keys.pressed.CONTROL)
{
scrollAmount = GRID_SIZE * 0.25 * 25.0;
shouldPause = true;
}
@ -1978,7 +2043,7 @@ class ChartEditorState extends HaxeUIState
// SHIFT + Scroll = Scroll Fast
if (FlxG.keys.pressed.SHIFT)
{
scrollAmount *= 5;
scrollAmount *= 2;
}
// CONTROL + Scroll = Scroll Precise
if (FlxG.keys.pressed.CONTROL)
@ -2049,14 +2114,17 @@ class ChartEditorState extends HaxeUIState
function handleSnap():Void
{
if (FlxG.keys.justPressed.LEFT && !FlxG.keys.pressed.CONTROL)
if (currentLiveInputStyle == None)
{
noteSnapQuantIndex--;
}
if (FlxG.keys.justPressed.LEFT && !FlxG.keys.pressed.CONTROL)
{
noteSnapQuantIndex--;
}
if (FlxG.keys.justPressed.RIGHT && !FlxG.keys.pressed.CONTROL)
{
noteSnapQuantIndex++;
if (FlxG.keys.justPressed.RIGHT && !FlxG.keys.pressed.CONTROL)
{
noteSnapQuantIndex++;
}
}
}
@ -3044,10 +3112,16 @@ class ChartEditorState extends HaxeUIState
// CTRL + Q = Quit to Menu
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.Q)
{
FlxG.switchState(new MainMenuState());
quitChartEditor();
}
}
function quitChartEditor():Void
{
autoSave();
FlxG.switchState(new MainMenuState());
}
/**
* Handle keybinds for edit menu items.
*/
@ -3148,13 +3222,17 @@ class ChartEditorState extends HaxeUIState
*/
function handleViewKeybinds():Void
{
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.LEFT)
if (currentLiveInputStyle == None)
{
incrementDifficulty(-1);
}
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.RIGHT)
{
incrementDifficulty(1);
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.LEFT)
{
incrementDifficulty(-1);
}
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.RIGHT)
{
incrementDifficulty(1);
}
// Would bind Ctrl+A and Ctrl+D here, but they are already bound to Select All and Select None.
}
}
@ -3261,7 +3339,7 @@ class ChartEditorState extends HaxeUIState
*/
function handleTestKeybinds():Void
{
if (!isHaxeUIDialogOpen && FlxG.keys.justPressed.ENTER)
if (!isHaxeUIDialogOpen && !isCursorOverHaxeUI && FlxG.keys.justPressed.ENTER)
{
var minimal = FlxG.keys.pressed.SHIFT;
testSongInPlayState(minimal);
@ -3853,25 +3931,26 @@ class ChartEditorState extends HaxeUIState
switch (currentLiveInputStyle)
{
case LiveInputStyle.WASD:
if (FlxG.keys.justPressed.A) placeNoteAtPlayhead(0);
if (FlxG.keys.justPressed.S) placeNoteAtPlayhead(1);
if (FlxG.keys.justPressed.W) placeNoteAtPlayhead(2);
if (FlxG.keys.justPressed.D) placeNoteAtPlayhead(3);
if (FlxG.keys.justPressed.A) placeNoteAtPlayhead(4);
if (FlxG.keys.justPressed.S) placeNoteAtPlayhead(5);
if (FlxG.keys.justPressed.W) placeNoteAtPlayhead(6);
if (FlxG.keys.justPressed.D) placeNoteAtPlayhead(7);
if (FlxG.keys.justPressed.LEFT) placeNoteAtPlayhead(4);
if (FlxG.keys.justPressed.DOWN) placeNoteAtPlayhead(5);
if (FlxG.keys.justPressed.UP) placeNoteAtPlayhead(6);
if (FlxG.keys.justPressed.RIGHT) placeNoteAtPlayhead(7);
if (FlxG.keys.justPressed.LEFT) placeNoteAtPlayhead(0);
if (FlxG.keys.justPressed.DOWN) placeNoteAtPlayhead(1);
if (FlxG.keys.justPressed.UP) placeNoteAtPlayhead(2);
if (FlxG.keys.justPressed.RIGHT) placeNoteAtPlayhead(3);
case LiveInputStyle.NumberKeys:
if (FlxG.keys.justPressed.ONE) placeNoteAtPlayhead(0);
if (FlxG.keys.justPressed.TWO) placeNoteAtPlayhead(1);
if (FlxG.keys.justPressed.THREE) placeNoteAtPlayhead(2);
if (FlxG.keys.justPressed.FOUR) placeNoteAtPlayhead(3);
// Flipped because Dad is on the left but represents data 0-3.
if (FlxG.keys.justPressed.ONE) placeNoteAtPlayhead(4);
if (FlxG.keys.justPressed.TWO) placeNoteAtPlayhead(5);
if (FlxG.keys.justPressed.THREE) placeNoteAtPlayhead(6);
if (FlxG.keys.justPressed.FOUR) placeNoteAtPlayhead(7);
if (FlxG.keys.justPressed.FIVE) placeNoteAtPlayhead(4);
if (FlxG.keys.justPressed.SIX) placeNoteAtPlayhead(5);
if (FlxG.keys.justPressed.SEVEN) placeNoteAtPlayhead(6);
if (FlxG.keys.justPressed.EIGHT) placeNoteAtPlayhead(7);
if (FlxG.keys.justPressed.FIVE) placeNoteAtPlayhead(0);
if (FlxG.keys.justPressed.SIX) placeNoteAtPlayhead(1);
if (FlxG.keys.justPressed.SEVEN) placeNoteAtPlayhead(2);
if (FlxG.keys.justPressed.EIGHT) placeNoteAtPlayhead(3);
case LiveInputStyle.None:
// Do nothing.
}
@ -3880,12 +3959,24 @@ class ChartEditorState extends HaxeUIState
function placeNoteAtPlayhead(column:Int):Void
{
var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels;
var playheadPosFractionalStep:Float = playheadPos / GRID_SIZE / (16 / noteSnapQuant);
var playheadPosFractionalStep:Float = playheadPos / GRID_SIZE / noteSnapRatio;
var playheadPosStep:Int = Std.int(Math.floor(playheadPosFractionalStep));
var playheadPosMs:Float = playheadPosStep * Conductor.stepLengthMs * (16 / noteSnapQuant);
var playheadPosSnappedMs:Float = playheadPosStep * Conductor.stepLengthMs * noteSnapRatio;
var newNoteData:SongNoteData = new SongNoteData(playheadPosMs, column, 0, selectedNoteKind);
performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL));
// Look for notes within 1 step of the playhead.
var notesAtPos:Array<SongNoteData> = SongDataUtils.getNotesInTimeRange(currentSongChartNoteData, playheadPosSnappedMs,
playheadPosSnappedMs + Conductor.stepLengthMs * noteSnapRatio);
notesAtPos = SongDataUtils.getNotesWithData(notesAtPos, [column]);
if (notesAtPos.length == 0)
{
var newNoteData:SongNoteData = new SongNoteData(playheadPosSnappedMs, column, 0, selectedNoteKind);
performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL));
}
else
{
trace('Already a note there.');
}
}
function set_scrollPositionInPixels(value:Float):Float
@ -3944,6 +4035,8 @@ class ChartEditorState extends HaxeUIState
*/
public function testSongInPlayState(minimal:Bool = false):Void
{
autoSave();
var startTimestamp:Float = 0;
if (playtestStartTime) startTimestamp = scrollPositionInMs + playheadPositionInMs;

View file

@ -0,0 +1,74 @@
package funkin.ui.title;
import flixel.FlxSprite;
import funkin.shaderslmfao.BlendModesShader;
import openfl.display.BitmapData;
import flixel.FlxCamera;
import flixel.FlxG;
import flixel.graphics.frames.FlxFrame.FlxFrameAngle;
class FlxSpriteOverlay extends FlxSprite
{
var blendShader:BlendModesShader;
var dipshitBitmap:BitmapData;
var temp:FlxSprite;
public function new(x:Float, y:Float)
{
super(x, y);
temp = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, 0xFF000000);
blendShader = new BlendModesShader();
dipshitBitmap = new BitmapData(2180, 1720, true, 0xFFCC00CC);
}
override function drawComplex(camera:FlxCamera):Void
{
_frame.prepareMatrix(_matrix, FlxFrameAngle.ANGLE_0, checkFlipX(), checkFlipY());
_matrix.translate(-origin.x, -origin.y);
_matrix.scale(scale.x, scale.y);
if (bakedRotationAngle <= 0)
{
updateTrig();
if (angle != 0) _matrix.rotateWithTrig(_cosAngle, _sinAngle);
}
getScreenPosition(_point, camera).subtractPoint(offset);
_point.add(origin.x, origin.y);
_matrix.translate(_point.x, _point.y);
if (isPixelPerfectRender(camera))
{
_matrix.tx = Math.floor(_matrix.tx);
_matrix.ty = Math.floor(_matrix.ty);
}
var sprRect = getScreenBounds();
// dipshitBitmap.draw(camera.canvas, camera.canvas.transform.matrix);
// blendShader.setCamera(dipshitBitmap);
// FlxG.bitmapLog.add(dipshitBitmap);
camera.drawPixels(_frame, framePixels, _matrix, colorTransform, blend, antialiasing, shader);
}
function copyToFlash(rect):openfl.geom.Rectangle
{
var flashRect = new openfl.geom.Rectangle();
flashRect.x = rect.x;
flashRect.y = rect.y;
flashRect.width = rect.width;
flashRect.height = rect.height;
return flashRect;
}
override public function isSimpleRender(?camera:FlxCamera):Bool
{
if (FlxG.renderBlit)
{
return super.isSimpleRender(camera);
}
else
{
return false;
}
}
}

View file

@ -23,6 +23,7 @@ import openfl.events.MouseEvent;
import openfl.events.NetStatusEvent;
import openfl.media.Video;
import openfl.net.NetStream;
import openfl.display.BlendMode;
#if desktop
#end
@ -101,7 +102,7 @@ class TitleState extends MusicBeatState
var logoBl:FlxSprite;
var outlineShaderShit:TitleOutline;
var gfDance:FlxSprite;
var gfDance:FlxSpriteOverlay;
var danceLeft:Bool = false;
var titleText:FlxSprite;
var maskShader = new LeftMaskShader();
@ -124,13 +125,11 @@ class TitleState extends MusicBeatState
outlineShaderShit = new TitleOutline();
gfDance = new FlxSprite(FlxG.width * 0.4, FlxG.height * 0.07);
gfDance = new FlxSpriteOverlay(FlxG.width * 0.4, FlxG.height * 0.07);
gfDance.frames = Paths.getSparrowAtlas('gfDanceTitle');
gfDance.animation.addByIndices('danceLeft', 'gfDance', [30, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], "", 24, false);
gfDance.animation.addByIndices('danceRight', 'gfDance', [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], "", 24, false);
add(gfDance);
// maskShader.swagSprX = gfDance.x;
// maskShader.swagMaskX = gfDance.x + 200;
// maskShader.frameUV = gfDance.frame.uv;
@ -142,6 +141,8 @@ class TitleState extends MusicBeatState
add(logoBl);
add(gfDance);
titleText = new FlxSprite(100, FlxG.height * 0.8);
titleText.frames = Paths.getSparrowAtlas('titleEnter');
titleText.animation.addByPrefix('idle', "Press Enter to Begin", 24);
@ -245,6 +246,8 @@ class TitleState extends MusicBeatState
override function update(elapsed:Float)
{
FlxG.bitmapLog.add(FlxG.camera.buffer);
#if HAS_PITCH
if (FlxG.keys.pressed.UP) FlxG.sound.music.pitch += 0.5 * elapsed;

View file

@ -39,7 +39,7 @@ class Constants
*/
public static final VERSION_SUFFIX:String = ' PROTOTYPE';
#if debug
#if (debug || FORCE_DEBUG_VERSION)
static function get_VERSION():String
{
return 'v${Application.current.meta.get('version')} (${GIT_BRANCH} : ${GIT_HASH})' + VERSION_SUFFIX;
@ -71,7 +71,7 @@ class Constants
*/
// ==============================
#if debug
#if (debug || FORCE_DEBUG_VERSION)
/**
* The current Git branch.
*/
@ -324,10 +324,15 @@ class Constants
public static final HEALTH_MINE_PENALTY:Float = 15.0 / 100.0 * HEALTH_MAX; // 15.0%
/**
* If true, the player will not receive the ghost miss penalty if there are no notes within the hit window.
* This is the thing people have been begging for forever lolol.
* SCORE VALUES
*/
public static final GHOST_TAPPING:Bool = false;
// ==============================
/**
* The amount of score the player gains for every send they hold a hold note.
* A fraction of this value is granted every frame.
*/
public static final SCORE_HOLD_BONUS_PER_SECOND:Float = 250.0;
/**
* FILE EXTENSIONS
@ -367,6 +372,12 @@ class Constants
*/
// ==============================
/**
* If true, the player will not receive the ghost miss penalty if there are no notes within the hit window.
* This is the thing people have been begging for forever lolol.
*/
public static final GHOST_TAPPING:Bool = false;
/**
* The separator between an asset library and the asset path.
*/

View file

@ -1,6 +1,6 @@
package funkin.util.macro;
#if debug
#if (debug || FORCE_DEBUG_VERSION)
class GitCommit
{
/**