mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-14 19:25:16 -05:00
Merge branch 'rewrite/master' into feature/charSelect
This commit is contained in:
commit
825327e1c8
54 changed files with 2253 additions and 937 deletions
2
.gitattributes
vendored
2
.gitattributes
vendored
|
@ -1 +1,3 @@
|
|||
* text=auto eol=lf
|
||||
*.hxc linguist-language=Haxe
|
||||
*.hxp linguist-language=Haxe
|
||||
|
|
21
.github/actions/setup-haxe/action.yml
vendored
21
.github/actions/setup-haxe/action.yml
vendored
|
@ -44,7 +44,7 @@ runs:
|
|||
g++ \
|
||||
libx11-dev libxi-dev libxext-dev libxinerama-dev libxrandr-dev \
|
||||
libgl-dev libgl1-mesa-dev \
|
||||
libasound2-dev
|
||||
libasound2-dev libpulse-dev
|
||||
ln -s /usr/lib/x86_64-linux-gnu/libffi.so.8 /usr/lib/x86_64-linux-gnu/libffi.so.6 || true
|
||||
- name: Install linux-specific dependencies
|
||||
if: ${{ runner.os == 'Linux' && contains(inputs.targets, 'linux') }}
|
||||
|
@ -56,12 +56,17 @@ runs:
|
|||
shell: bash
|
||||
run: |
|
||||
echo "TIMER_HAXELIB=$(date +%s)" >> "$GITHUB_ENV"
|
||||
haxelib --debug --never install haxelib 4.1.0 --global
|
||||
haxelib --debug --never deleterepo || true
|
||||
haxelib fixrepo --global || true
|
||||
haxelib --debug --never --global install haxelib 4.1.0
|
||||
haxelib --debug --global set haxelib 4.1.0
|
||||
haxelib --global remove haxelib git || true
|
||||
haxelib --global remove hmm || true
|
||||
rm -rf .haxelib
|
||||
haxelib --debug --never --global git haxelib https://github.com/FunkinCrew/haxelib.git funkin-patches --skip-dependencies
|
||||
haxelib --debug --never --global git hmm https://github.com/FunkinCrew/hmm funkin-patches
|
||||
haxelib --debug --never newrepo
|
||||
haxelib version
|
||||
echo "HAXEPATH=$(haxelib config)" >> "$GITHUB_ENV"
|
||||
haxelib --debug --always --global git haxelib https://github.com/FunkinCrew/haxelib.git funkin-patches --skip-dependencies
|
||||
haxelib --debug --global --always git hmm https://github.com/FunkinCrew/hmm funkin-patches
|
||||
echo "TIMER_DEPS=$(date +%s)" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Restore cached dependencies
|
||||
|
@ -75,7 +80,11 @@ runs:
|
|||
name: Prep git for dependency install
|
||||
uses: gacts/run-and-post-run@v1
|
||||
with:
|
||||
run: git config --global 'url.https://x-access-token:${{ inputs.gh-token }}@github.com/.insteadOf' https://github.com/
|
||||
run: |
|
||||
git config --global --name-only --get-regexp 'url\.https\:\/\/x-access-token:.+@github\.com\/\.insteadOf' \
|
||||
| xargs git config --global --unset
|
||||
git config -l --show-scope --show-origin
|
||||
git config --global 'url.https://x-access-token:${{ inputs.gh-token }}@github.com/.insteadOf' https://github.com/
|
||||
post: git config --global --unset 'url.https://x-access-token:${{ inputs.gh-token }}@github.com/.insteadOf'
|
||||
|
||||
- if: ${{ steps.cache-hmm.outputs.cache-hit != 'true' }}
|
||||
|
|
1
.github/labeler.yml
vendored
1
.github/labeler.yml
vendored
|
@ -1,7 +1,6 @@
|
|||
# Add Documentation tag to PR's changing markdown files, or anyhting in the docs folder
|
||||
Documentation:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- any-glob-to-any-file:
|
||||
- docs/*
|
||||
- '**/*.md'
|
||||
|
|
2
.github/workflows/build-game.yml
vendored
2
.github/workflows/build-game.yml
vendored
|
@ -111,7 +111,9 @@ jobs:
|
|||
name: Install dependencies
|
||||
run: |
|
||||
git config --global 'url.https://x-access-token:${{ steps.app_token.outputs.token }}@github.com/.insteadOf' https://github.com/
|
||||
git config --global advice.detachedHead false
|
||||
haxelib --global run hmm install -q
|
||||
cd .haxelib/hxcpp/git/tools/hxcpp && haxe compile.hxml
|
||||
|
||||
- if: ${{ matrix.target != 'html5' }}
|
||||
name: Restore hxcpp cache
|
||||
|
|
30
.vscode/settings.json
vendored
30
.vscode/settings.json
vendored
|
@ -94,12 +94,12 @@
|
|||
{
|
||||
"label": "Windows / Debug",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DFORCE_DEBUG_VERSION"]
|
||||
"args": ["-debug", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "Linux / Debug",
|
||||
"target": "linux",
|
||||
"args": ["-debug", "-DFORCE_DEBUG_VERSION"]
|
||||
"args": ["-debug", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug",
|
||||
|
@ -109,7 +109,7 @@
|
|||
{
|
||||
"label": "Windows / Debug (FlxAnimate Test)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DANIMATE", "-DFORCE_DEBUG_VERSION"]
|
||||
"args": ["-debug", "-DANIMATE", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (FlxAnimate Test)",
|
||||
|
@ -119,7 +119,7 @@
|
|||
{
|
||||
"label": "Windows / Debug (Straight to Freeplay)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DFREEPLAY", "-DFORCE_DEBUG_VERSION"]
|
||||
"args": ["-debug", "-DFREEPLAY", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Straight to Freeplay)",
|
||||
|
@ -132,13 +132,13 @@
|
|||
"args": [
|
||||
"-debug",
|
||||
"-DSONG=bopeebo -DDIFFICULTY=normal",
|
||||
"-DFORCE_DEBUG_VERSION"
|
||||
"-DFEATURE_DEBUG_FUNCTIONS"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Windows / Debug (Straight to Play - 2hot)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DSONG=2hot", "-DFORCE_DEBUG_VERSION"]
|
||||
"args": ["-debug", "-DSONG=2hot", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Straight to Play - Bopeebo Normal)",
|
||||
|
@ -148,7 +148,7 @@
|
|||
{
|
||||
"label": "Windows / Debug (Conversation Test)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DDIALOGUE", "-DFORCE_DEBUG_VERSION"]
|
||||
"args": ["-debug", "-DDIALOGUE", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Conversation Test)",
|
||||
|
@ -163,7 +163,7 @@
|
|||
{
|
||||
"label": "Windows / Debug (Straight to Chart Editor)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DCHARTING", "-DFORCE_DEBUG_VERSION"]
|
||||
"args": ["-debug", "-DCHARTING", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Straight to Chart Editor)",
|
||||
|
@ -173,12 +173,12 @@
|
|||
{
|
||||
"label": "Windows / Debug (Straight to Animation Editor)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DANIMDEBUG", "-DFORCE_DEBUG_VERSION"]
|
||||
"args": ["-debug", "-DANIMDEBUG", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "Windows / Debug (Debug hxCodec)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DHXC_LIBVLC_LOGGING", "-DFORCE_DEBUG_VERSION"]
|
||||
"args": ["-debug", "-DHXC_LIBVLC_LOGGING", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Straight to Animation Editor)",
|
||||
|
@ -188,7 +188,7 @@
|
|||
{
|
||||
"label": "Windows / Debug (Latency Test)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DLATENCY", "-DFORCE_DEBUG_VERSION"]
|
||||
"args": ["-debug", "-DLATENCY", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Latency Test)",
|
||||
|
@ -198,7 +198,7 @@
|
|||
{
|
||||
"label": "Windows / Debug (Waveform Test)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DWAVEFORM", "-DFORCE_DEBUG_VERSION"]
|
||||
"args": ["-debug", "-DWAVEFORM", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "Windows / Release",
|
||||
|
@ -218,17 +218,17 @@
|
|||
{
|
||||
"label": "HTML5 / Debug",
|
||||
"target": "html5",
|
||||
"args": ["-debug", "-DFORCE_DEBUG_VERSION"]
|
||||
"args": ["-debug", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "HTML5 / Debug (Watch)",
|
||||
"target": "html5",
|
||||
"args": ["-debug", "-watch", "-DFORCE_DEBUG_VERSION"]
|
||||
"args": ["-debug", "-watch", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "macOS / Debug",
|
||||
"target": "mac",
|
||||
"args": ["-debug", "-DFORCE_DEBUG_VERSION"]
|
||||
"args": ["-debug", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "macOS / Release",
|
||||
|
|
56
CHANGELOG.md
56
CHANGELOG.md
|
@ -4,6 +4,62 @@ All notable changes will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.5.0] - 2024-08-??
|
||||
### Added
|
||||
- Added a new Character Select screen to switch between playable characters in Freeplay
|
||||
- Modding isn't 100% there but we're working on it!
|
||||
- Added Pico as a playable character! Unlock him by completing Weekend 1 (if you haven't already done that)
|
||||
- The songs from Weekend 1 have moved; you must now switch to Pico in Freeplay to access them
|
||||
- Added ## new Pico remixes! Access them by selecting Pico from in the Character Select screen
|
||||
- Added 2 new Erect remixes! Access them by switching difficulty on the song
|
||||
- Implemented support for a new Instrumental Selector in Freeplay
|
||||
- Beating a Pico remix lets you use that instrumental when playing as Boyfriend
|
||||
- Added the first batch of Erect Stages! These graphical overhauls of the original stages will be used when playing Erect remixes and Pico remixes
|
||||
- Implemented support for scripted Note Kinds. You can use HScript define a different note style to display for these notes as well as custom behavior. (community feature by lemz1)
|
||||
- Implemented a new Strumline Background option, to display a darkened background behind the strumline with your choice of opacity.
|
||||
- Implemented support for Numeric and Selector options in the Options menu. (community feature by FlooferLand)
|
||||
## Changed
|
||||
- Girlfriend and Nene now perform previously unused animations when you achieve a large combo, or drop a large combo.
|
||||
- The pixel character icons in the Freeplay menu now display an animation!
|
||||
- Altered how Week 6 displays sprites to make things look more retro.
|
||||
- Character offsets are now independent of the character's scale.
|
||||
- This should resolve issues with offsets when porting characters from older mods.
|
||||
- Pixel character offsets have been modified to compensate.
|
||||
- Note style data can now specify custom combo count graphics, judgement graphics, countdown graphics, and countdown audio. (community feature by anysad)
|
||||
- These were previously using hardcoded values based on whether the stage was `school` or `schoolEvil`.
|
||||
- The `danceEvery` property of characters and stage props can now use values with a precision of `0.25`, to play their idle animation up to four times per beat.
|
||||
- Reworked the JSON merging system in Polymod; you can now include JSONPatch files under `_merge` in your mod folder to add, modify, or remove values in a JSON without replacing it entirely!
|
||||
- Cutscenes now automatically pause when tabbing out (community fix by AbnormalPoof)
|
||||
- Characters will now respect the `danceEvery` property (community fix by gamerbross)
|
||||
- The F5 function now reloads the current song's chart data from disc (community feature by gamerbross)
|
||||
- Refactored the compilation guide and added common troubleshooting steps (community fix by Hundrec)
|
||||
- Made several layout improvements and fixes to the Animation Offsets editor in the Debug menu (community fix by gamerbross)
|
||||
- Fixed a bug where the Back sound would be not played when leaving the Story menu and Options menu (community fix by AppleHair)
|
||||
- Animation offsets no longer directly modify the `x` and `y` position of props, which makes props work better with tweens (community fix by Sword352)
|
||||
- The YEAH! events in Tutorial now use chart events rather than being hard-coded (community fix by anysad)
|
||||
- The player's Score now displays commas in it (community fix by loggo)
|
||||
## Fixed
|
||||
- Fixed an issue where songs with no notes would crash on the Results screen.
|
||||
- Fixed an issue where the old icon easter egg would not work properly on pixel levels.
|
||||
- Fixed an issue where you could play notes during the Thorns cutscene.
|
||||
- Fixed an issue where the Heart icon when favoriting a song in Freeplay would be malformed.
|
||||
- Fixed an issue where Pico's death animation displays a faint blue background (community fix by doggogit)
|
||||
- Fixed an issue where mod songs would not play a preview in the Freeplay menu (community fix by KarimAkra)
|
||||
- Fixed an issue where the Memory Usage counter could overflow and display a negative number (community fix by KarimAkra)
|
||||
- Fixed an issue where pressing the Chart Editor keybind while playtesting a chart would reset the chart editor (community fix by gamerbross)
|
||||
- Fixed a crash bug when pressing F5 after seeing the sticker transition (community fix by gamerbross)
|
||||
- Fixed an issue where the Story Mode menu couldn't be scrolled with a mouse (community fix by JVNpixels)
|
||||
- Fixed an issue causing the song to majorly desync sometimes (community fix by Burgerballs)
|
||||
- Fixed an issue where the Freeplay song preview would not respect the instrumental ID specified in the song metadata (community fix by AppleHair)
|
||||
- Fixed an issue where Tankman's icon wouldn't display in the Chart Editor (community fix by hundrec)
|
||||
- Fixed an issue where pausing the game during a camera zoom would zoom the pause menu. (community fix by gamerbros)
|
||||
- Fixed an issue where certain UI elements would not flash at a consistent rate (community fix by cyn0x8)
|
||||
- Fixed an issue where the game would not use the placeholder health icon as a fallback (community fix by gamerbross)
|
||||
- Fixed an issue where the chart editor could get stuck creating a hold note when using Live Inputs (community fix by gamerbross)
|
||||
- Fixed an issue where character graphics could not be placed in week folders (community fix by 7oltan)
|
||||
- Fixed a crash issue when a Freeplay song has no `Normal` difficulty (community fix by Applehair and gamerbross)
|
||||
- Fixed an issue in Story Mode where a song that isn't valid for the current variation could be selected (community fix by Applehair)
|
||||
|
||||
## [0.4.1] - 2024-06-12
|
||||
### Added
|
||||
- Pressing ESCAPE on the title screen on desktop now exits the game, allowing you to exit the game while in fullscreen on desktop
|
||||
|
|
269
Project.xml
269
Project.xml
|
@ -1,269 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<project xmlns="http://lime.openfl.org/project/1.0.4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://lime.openfl.org/project/1.0.4 http://lime.openfl.org/xsd/project-1.0.4.xsd">
|
||||
<!-- _________________________ Application Settings _________________________ -->
|
||||
<app title="Friday Night Funkin'" file="Funkin" packageName="com.funkin.fnf" package="com.funkin.fnf" main="Main" version="0.5.0" company="ninjamuffin99" />
|
||||
<!--Switch Export with Unique ApplicationID and Icon-->
|
||||
<set name="APP_ID" value="0x0100f6c013bbc000" />
|
||||
|
||||
<!--
|
||||
Define the OpenFL sprite which displays the preloader.
|
||||
You can't replace the preloader's logic here, sadly, but you can extend it.
|
||||
Basic preloading logic is done by `openfl.display.Preloader`.
|
||||
-->
|
||||
<app preloader="funkin.ui.transition.preload.FunkinPreloader" />
|
||||
|
||||
<!--Minimum without FLX_NO_GAMEPAD: 11.8, without FLX_NO_NATIVE_CURSOR: 11.2-->
|
||||
<set name="SWF_VERSION" value="11.8" />
|
||||
<!-- ____________________________ Window Settings ___________________________ -->
|
||||
<!--These window settings apply to all targets-->
|
||||
<window width="1280" height="720" fps="60" background="#000000" hardware="true" vsync="false" />
|
||||
<!--HTML5-specific-->
|
||||
<window if="html5" resizable="true" />
|
||||
<!--Desktop-specific-->
|
||||
<window if="desktop" orientation="landscape" fullscreen="false" resizable="true" vsync="false" />
|
||||
<!--Mobile-specific-->
|
||||
<window if="mobile" orientation="landscape" fullscreen="true" width="0" height="0" resizable="false" />
|
||||
<!-- _____________________________ Path Settings ____________________________ -->
|
||||
|
||||
<set name="BUILD_DIR" value="export/debug" if="debug" />
|
||||
<set name="BUILD_DIR" value="export/release" unless="debug" />
|
||||
<set name="BUILD_DIR" value="export/32bit" if="32bit" />
|
||||
<source path="source" />
|
||||
<assets path="assets/preload" rename="assets" exclude="*.ogg|*.wav" if="web" />
|
||||
<assets path="assets/preload" rename="assets" exclude="*.mp3|*.wav" unless="web" />
|
||||
<define name="PRELOAD_ALL" unless="web" />
|
||||
<define name="NO_PRELOAD_ALL" unless="PRELOAD_ALL" />
|
||||
<section if="PRELOAD_ALL">
|
||||
<library name="songs" preload="true" />
|
||||
<library name="shared" preload="true" />
|
||||
<library name="tutorial" preload="true" />
|
||||
<library name="week1" preload="true" />
|
||||
<library name="week2" preload="true" />
|
||||
<library name="week3" preload="true" />
|
||||
<library name="week4" preload="true" />
|
||||
<library name="week5" preload="true" />
|
||||
<library name="week6" preload="true" />
|
||||
<library name="week7" preload="true" />
|
||||
<library name="weekend1" preload="true" />
|
||||
<library name="videos" preload="true" />
|
||||
</section>
|
||||
<section if="NO_PRELOAD_ALL">
|
||||
<library name="songs" preload="false" />
|
||||
<library name="shared" preload="false" />
|
||||
<library name="tutorial" preload="false" />
|
||||
<library name="week1" preload="false" />
|
||||
<library name="week2" preload="false" />
|
||||
<library name="week3" preload="false" />
|
||||
<library name="week4" preload="false" />
|
||||
<library name="week5" preload="false" />
|
||||
<library name="week6" preload="false" />
|
||||
<library name="week7" preload="false" />
|
||||
<library name="weekend1" preload="false" />
|
||||
<library name="videos" preload="false" />
|
||||
</section>
|
||||
<library name="art" preload="false" />
|
||||
<assets path="assets/songs" library="songs" exclude="*.fla|*.ogg|*.wav" if="web" />
|
||||
<assets path="assets/songs" library="songs" exclude="*.fla|*.mp3|*.wav" unless="web" />
|
||||
<!-- Videos go in their own library because web never needs to preload them, they can just be streamed. -->
|
||||
<assets path="assets/videos" library="videos" />
|
||||
<assets path="assets/shared" library="shared" exclude="*.fla|*.ogg|*.wav" if="web" />
|
||||
<assets path="assets/shared" library="shared" exclude="*.fla|*.mp3|*.wav" unless="web" />
|
||||
<assets path="assets/tutorial" library="tutorial" exclude="*.fla|*.ogg|*.wav" if="web" />
|
||||
<assets path="assets/tutorial" library="tutorial" exclude="*.fla|*.mp3|*.wav" unless="web" />
|
||||
<assets path="assets/week1" library="week1" exclude="*.fla|*.ogg|*.wav" if="web" />
|
||||
<assets path="assets/week1" library="week1" exclude="*.fla|*.mp3|*.wav" unless="web" />
|
||||
<assets path="assets/week2" library="week2" exclude="*.fla|*.ogg|*.wav" if="web" />
|
||||
<assets path="assets/week2" library="week2" exclude="*.fla|*.mp3|*.wav" unless="web" />
|
||||
<assets path="assets/week3" library="week3" exclude="*.fla|*.ogg|*.wav" if="web" />
|
||||
<assets path="assets/week3" library="week3" exclude="*.fla|*.mp3|*.wav" unless="web" />
|
||||
<assets path="assets/week4" library="week4" exclude="*.fla|*.ogg|*.wav" if="web" />
|
||||
<assets path="assets/week4" library="week4" exclude="*.fla|*.mp3|*.wav" unless="web" />
|
||||
<assets path="assets/week5" library="week5" exclude="*.fla|*.ogg|*.wav" if="web" />
|
||||
<assets path="assets/week5" library="week5" exclude="*.fla|*.mp3|*.wav" unless="web" />
|
||||
<assets path="assets/week6" library="week6" exclude="*.fla|*.ogg|*.wav" if="web" />
|
||||
<assets path="assets/week6" library="week6" exclude="*.fla|*.mp3|*.wav" unless="web" />
|
||||
<assets path="assets/week7" library="week7" exclude="*.fla|*.ogg|*.wav" if="web" />
|
||||
<assets path="assets/week7" library="week7" exclude="*.fla|*.mp3|*.wav" unless="web" />
|
||||
<assets path="assets/weekend1" library="weekend1" exclude="*.fla|*.ogg|*.wav" if="web" />
|
||||
<assets path="assets/weekend1" library="weekend1" exclude="*.fla|*.mp3|*.wav" unless="web" />
|
||||
<!-- <assets path='example_mods' rename='mods' embed='false'/> -->
|
||||
<!--
|
||||
AUTOMATICALLY MOVING EXAMPLE MODS INTO THE BUILD CAUSES ISSUES
|
||||
Currently, this line will add the mod files to the library manifest,
|
||||
which causes issues if the mod is not enabled.
|
||||
If we can exclude the `mods` folder from the manifest, we can re-enable this line.
|
||||
<assets path='example_mods' rename='mods' embed='false' exclude="*.md" />
|
||||
-->
|
||||
<assets path="art/readme.txt" rename="do NOT readme.txt" library="art"/>
|
||||
<assets path="CHANGELOG.md" rename="changelog.txt" library="art"/>
|
||||
<!-- NOTE FOR FUTURE SELF SINCE FONTS ARE ALWAYS FUCKY
|
||||
TO FIX ONE OF THEM, I CONVERTED IT TO OTF. DUNNO IF YOU NEED TO
|
||||
THEN UHHH I USED THE NAME OF THE FONT WITH SETFORMAT() ON THE TEXT!!!
|
||||
NOT USING A DIRECT THING TO THE ASSET!!!
|
||||
-->
|
||||
<assets path="assets/fonts" embed="true" />
|
||||
|
||||
<!-- If compiled via github actions, show debug version number. -->
|
||||
<define name="FORCE_DEBUG_VERSION" if="GITHUB_BUILD" />
|
||||
<define name="NO_REDIRECT_ASSETS_FOLDER" if="GITHUB_BUILD" />
|
||||
<define name="TOUCH_HERE_TO_PLAY" if="web" />
|
||||
|
||||
<!-- _______________________________ Libraries ______________________________ -->
|
||||
<haxelib name="lime" /> <!-- Game engine backend -->
|
||||
<haxelib name="openfl" /> <!-- Game engine backend -->
|
||||
<haxelib name="flixel" /> <!-- Game engine -->
|
||||
|
||||
<haxedev set="webgl" />
|
||||
|
||||
<haxelib name="flixel-addons" /> <!-- Additional utilities for Flixel -->
|
||||
<haxelib name="hscript" /> <!-- Scripting -->
|
||||
<haxelib name="flixel-ui" /> <!-- UI framework (DEPRECATED) -->
|
||||
<haxelib name="haxeui-core" /> <!-- UI framework -->
|
||||
<haxelib name="haxeui-flixel" /> <!-- Integrate HaxeUI with Flixel -->
|
||||
<haxelib name="flixel-text-input" /> <!-- Improved text field rendering for HaxeUI -->
|
||||
<haxelib name="polymod" /> <!-- Modding framework -->
|
||||
<haxelib name="flxanimate" /> <!-- Texture atlas rendering -->
|
||||
<haxelib name="hxCodec" if="desktop" unless="hl" /> <!-- Video playback -->
|
||||
<haxelib name="funkin.vis"/>
|
||||
<haxelib name="grig.audio" />
|
||||
|
||||
<haxelib name="FlxPartialSound" /> <!-- Loading partial sound data -->
|
||||
|
||||
<haxelib name="json2object" /> <!-- JSON parsing -->
|
||||
<haxelib name="thx.core" /> <!-- General utility library, "the lodash of Haxe" -->
|
||||
<haxelib name="thx.semver" /> <!-- Version string handling -->
|
||||
|
||||
<haxelib name="hxcpp-debug-server" if="desktop debug" /> <!-- VSCode debug support -->
|
||||
|
||||
<!--Disable the Flixel core focus lost screen-->
|
||||
<haxedef name="FLX_NO_FOCUS_LOST_SCREEN" />
|
||||
<!--Disable the Flixel core debugger. Automatically gets set whenever you compile in release mode!-->
|
||||
<haxedef name="FLX_NO_DEBUG" unless="debug || FORCE_DEBUG_VERSION" />
|
||||
<!--Enable this for Nape release builds for a serious peformance improvement-->
|
||||
<haxedef name="NAPE_RELEASE_BUILD" unless="debug" />
|
||||
|
||||
<!--
|
||||
Hide deprecation warnings until they're fixed.
|
||||
TODO: REMOVE THIS!!!!
|
||||
<haxeflag name="-w" value="-WDeprecated" />
|
||||
-->
|
||||
|
||||
<!-- Haxe 4.3.0+: Enable pretty syntax errors and stuff. -->
|
||||
<haxedef name="message.reporting" value="pretty" />
|
||||
|
||||
<!-- _________________________________ Custom _______________________________ -->
|
||||
<!-- Disable trace() calls in release builds to bump up performance.
|
||||
<haxeflag name="- -no-traces" unless="debug" />-->
|
||||
<!-- HScript relies heavily on Reflection, which means we can't use DCE. -->
|
||||
<haxeflag name="-dce no" />
|
||||
<!-- Ensure all Funkin' classes are available at runtime. -->
|
||||
<haxeflag name="--macro" value="include('funkin')" />
|
||||
<!-- Ensure all UI components are available at runtime. -->
|
||||
<haxeflag name="--macro" value="include('haxe.ui.backend.flixel.components')" />
|
||||
<haxeflag name="--macro" value="include('haxe.ui.containers.dialogs')" />
|
||||
<haxeflag name="--macro" value="include('haxe.ui.containers.menus')" />
|
||||
<haxeflag name="--macro" value="include('haxe.ui.containers.properties')" />
|
||||
<haxeflag name="--macro" value="include('haxe.ui.core')" />
|
||||
<haxeflag name="--macro" value="include('haxe.ui.components')" />
|
||||
<haxeflag name="--macro" value="include('haxe.ui.containers')" />
|
||||
<!--
|
||||
Ensure additional class packages are available at runtime (some only really used by scripts).
|
||||
Ignore packages we can't include.
|
||||
-->
|
||||
<haxeflag name="--macro" value="include('flixel', true, [ 'flixel.addons.editors.spine.*', 'flixel.addons.nape.*', 'flixel.system.macros.*' ])" />
|
||||
<!-- Necessary to provide stack traces for HScript. -->
|
||||
<haxedef name="hscriptPos" />
|
||||
<haxedef name="safeMode"/>
|
||||
<haxedef name="HXCPP_CHECK_POINTER" />
|
||||
<haxedef name="HXCPP_STACK_LINE" />
|
||||
<haxedef name="HXCPP_STACK_TRACE" />
|
||||
<!-- 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)-->
|
||||
<icon path="art/icon16.png" size="16" />
|
||||
<icon path="art/icon32.png" size="32" />
|
||||
<icon path="art/icon64.png" size="64" />
|
||||
<icon path="art/iconOG.png" />
|
||||
<haxedef name="CAN_OPEN_LINKS" unless="switch" />
|
||||
<haxedef name="CAN_CHEAT" if="switch debug" />
|
||||
<!-- I don't remember what this is for. -->
|
||||
<haxedef name="haxeui_no_mouse_reset" />
|
||||
<!-- Clicking outside a dialog should deselect the current focused component. -->
|
||||
<haxedef name="haxeui_focus_out_on_click" />
|
||||
<!-- Required to use haxe.ui.backend.flixel.UIState with build macros. -->
|
||||
<haxedef name="haxeui_dont_impose_base_class" />
|
||||
<haxedef name="HARDCODED_CREDITS" />
|
||||
|
||||
<!-- Skip the Intro -->
|
||||
<section if="debug">
|
||||
<!-- Starts the game at the specified week, at the first song -->
|
||||
<!-- <haxedef name="week" value="1" if="debug"/> -->
|
||||
<!-- Starts the game at the specified song -->
|
||||
<!-- <haxedef name="song" value="bopeebo" if="debug"/> -->
|
||||
<!-- Difficulty, only used for week or song, defaults to 1 -->
|
||||
<!-- <haxedef name="dif" value="2" if="debug"/> -->
|
||||
</section>
|
||||
<section if="newgrounds">
|
||||
<!-- Enables Ng.core.verbose -->
|
||||
<!-- <haxedef name="NG_VERBOSE" /> -->
|
||||
<!-- Enables a NG debug session, so medals don't permently unlock -->
|
||||
<!-- <haxedef name="NG_DEBUG" /> -->
|
||||
<!-- pretends that the saved session Id was expired, forcing the reconnect prompt -->
|
||||
<!-- <haxedef name="NG_FORCE_EXPIRED_SESSION" if="debug" /> -->
|
||||
</section>
|
||||
|
||||
<!-- Uncomment this to wipe your input settings. -->
|
||||
<!-- <haxedef name="CLEAR_INPUT_SAVE"/> -->
|
||||
|
||||
<section if="debug" unless="NO_REDIRECT_ASSETS_FOLDER || html5 || GITHUB_BUILD">
|
||||
<!--
|
||||
Use the parent assets folder rather than the exported one
|
||||
No more will we accidentally undo our changes!
|
||||
-->
|
||||
<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>
|
||||
|
||||
<!-- Run a script before and after building. -->
|
||||
<prebuild haxe="source/Prebuild.hx"/> -->
|
||||
<postbuild haxe="source/Postbuild.hx"/> -->
|
||||
|
||||
<!-- Enable this on platforms which do not support dropping files onto the window. -->
|
||||
<haxedef name="FILE_DROP_UNSUPPORTED" if="mac" />
|
||||
<section unless="FILE_DROP_UNSUPPORTED">
|
||||
<haxedef name="FILE_DROP_SUPPORTED" />
|
||||
</section>
|
||||
|
||||
<!-- Enable this on platforms which do not support the edsior views. -->
|
||||
<haxedef name="CHART_EDITOR_UNSUPPORTED" if="web" />
|
||||
<haxedef name="CHART_EDITOR_SUPPORTED" unless="web"/>
|
||||
|
||||
<!-- Options for Polymod -->
|
||||
<section if="polymod">
|
||||
<!-- Turns on additional debug logging. -->
|
||||
<haxedef name="POLYMOD_DEBUG" value="true" if="debug" />
|
||||
<!-- The file extension to use for script files. -->
|
||||
<haxedef name="POLYMOD_SCRIPT_EXT" value=".hscript" />
|
||||
<!-- Which asset library to use for scripts. -->
|
||||
<haxedef name="POLYMOD_SCRIPT_LIBRARY" value="scripts" />
|
||||
<!-- The base path from which scripts should be accessed. -->
|
||||
<haxedef name="POLYMOD_ROOT_PATH" value="scripts/" />
|
||||
<!-- Determines the subdirectory of the mod folder used for file appending. -->
|
||||
<haxedef name="POLYMOD_APPEND_FOLDER" value="_append" />
|
||||
<!-- Determines the subdirectory of the mod folder used for file merges. -->
|
||||
<haxedef name="POLYMOD_MERGE_FOLDER" value="_merge" />
|
||||
<!-- Determines the file in the mod folder used for metadata. -->
|
||||
<haxedef name="POLYMOD_MOD_METADATA_FILE" value="_polymod_meta.json" />
|
||||
<!-- Determines the file in the mod folder used for the icon. -->
|
||||
<haxedef name="POLYMOD_MOD_ICON_FILE" value="_polymod_icon.png" />
|
||||
</section>
|
||||
</project>
|
2
assets
2
assets
|
@ -1 +1 @@
|
|||
Subproject commit f986e52819c5fd2e47f4d8d3297f326351682675
|
||||
Subproject commit 8558bc906ecd48ba5d7b1c213982f15b5d033227
|
|
@ -83,7 +83,7 @@ apt-fast install -y --no-install-recommends \
|
|||
libc6-dev libffi-dev \
|
||||
libx11-dev libxi-dev libxext-dev libxinerama-dev libxrandr-dev \
|
||||
libgl-dev libgl1-mesa-dev \
|
||||
libasound2-dev \
|
||||
libasound2-dev libpulse-dev \
|
||||
libvlc-dev libvlccore-dev
|
||||
EOF
|
||||
|
||||
|
@ -137,8 +137,8 @@ ENV PATH="$HAXEPATH:$PATH"
|
|||
RUN <<EOF
|
||||
HOME=/etc haxelib setup "$HAXEPATH/lib"
|
||||
haxelib --global --never install haxelib $haxelib_version
|
||||
haxelib --global --never git haxelib https://github.com/HaxeFoundation/haxelib.git master
|
||||
haxelib --global --never install hmm
|
||||
haxelib --global --never git haxelib https://github.com/FunkinCrew/haxelib.git funkin-patches --skip-dependencies
|
||||
haxelib --global --never git hmm https://github.com/FunkinCrew/hmm funkin-patches
|
||||
EOF
|
||||
|
||||
# hxcpp
|
||||
|
|
34
hmm.json
34
hmm.json
|
@ -1,5 +1,12 @@
|
|||
{
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "FlxPartialSound",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "a1eab7b9bf507b87200a3341719054fe427f3b15",
|
||||
"url": "https://github.com/FunkinCrew/FlxPartialSound.git"
|
||||
},
|
||||
{
|
||||
"name": "discord_rpc",
|
||||
"type": "git",
|
||||
|
@ -11,7 +18,7 @@
|
|||
"name": "flixel",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "10c2a203c43a78ff1ff26b8368fd736576829d8d",
|
||||
"ref": "599f38eeb502a8ba6439784036c2cfdc7b485260",
|
||||
"url": "https://github.com/FunkinCrew/flixel"
|
||||
},
|
||||
{
|
||||
|
@ -32,7 +39,7 @@
|
|||
"name": "flixel-ui",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "d0afed7293c71ffdb1184751317fc709b44c9056",
|
||||
"ref": "27f1ba626f80a6282fa8a187115e79a4a2133dc2",
|
||||
"url": "https://github.com/HaxeFlixel/flixel-ui"
|
||||
},
|
||||
{
|
||||
|
@ -42,13 +49,6 @@
|
|||
"ref": "c3340af27d8e5bc11b42362d0ba33504bd2f3d35",
|
||||
"url": "https://github.com/Dot-Stuff/flxanimate"
|
||||
},
|
||||
{
|
||||
"name": "FlxPartialSound",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "a1eab7b9bf507b87200a3341719054fe427f3b15",
|
||||
"url": "https://github.com/FunkinCrew/FlxPartialSound.git"
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
"type": "haxelib",
|
||||
|
@ -58,7 +58,7 @@
|
|||
"name": "funkin.vis",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "38261833590773cb1de34ac5d11e0825696fc340",
|
||||
"ref": "22b1ce089dd924f15cdc4632397ef3504d464e90",
|
||||
"url": "https://github.com/FunkinCrew/funkVis"
|
||||
},
|
||||
{
|
||||
|
@ -89,8 +89,10 @@
|
|||
},
|
||||
{
|
||||
"name": "hscript",
|
||||
"type": "haxelib",
|
||||
"version": "2.5.0"
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "12785398e2f07082f05034cb580682e5671442a2",
|
||||
"url": "https://github.com/FunkinCrew/hscript"
|
||||
},
|
||||
{
|
||||
"name": "hxCodec",
|
||||
|
@ -103,8 +105,8 @@
|
|||
"name": "hxcpp",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"url": "https://github.com/HaxeFoundation/hxcpp",
|
||||
"ref": "8dc8020f8465027de6c2aaaed90718bc693651ed"
|
||||
"ref": "904ea40643b050a5a154c5e4c33a83fd2aec18b1",
|
||||
"url": "https://github.com/HaxeFoundation/hxcpp"
|
||||
},
|
||||
{
|
||||
"name": "hxcpp-debug-server",
|
||||
|
@ -143,7 +145,7 @@
|
|||
"name": "lime",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "f6153ffcb1ffcf733f91d531eac5fda4189e07f7",
|
||||
"ref": "e0b2339e02fff91168789dbd1a0dd019ea3dda39",
|
||||
"url": "https://github.com/FunkinCrew/lime"
|
||||
},
|
||||
{
|
||||
|
@ -185,7 +187,7 @@
|
|||
"name": "polymod",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "98945c6c7f5ecde01a32c4623d3515bf012a023a",
|
||||
"ref": "96cfc5fa693b017e47f7cb13b765cc68698fa6b6",
|
||||
"url": "https://github.com/larsiusprime/polymod"
|
||||
},
|
||||
{
|
||||
|
|
1109
project.hxp
Normal file
1109
project.hxp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -113,7 +113,7 @@ class Main extends Sprite
|
|||
|
||||
addChild(game);
|
||||
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
game.debugger.interaction.addTool(new funkin.util.TrackerToolButtonUtil());
|
||||
#end
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ import funkin.util.CLIUtil;
|
|||
import funkin.util.CLIUtil.CLIParams;
|
||||
import funkin.util.TimerUtil;
|
||||
import funkin.util.TrackerUtil;
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
import Discord.DiscordClient;
|
||||
#end
|
||||
|
||||
|
@ -123,7 +123,7 @@ class InitState extends FlxState
|
|||
//
|
||||
// DISCORD API SETUP
|
||||
//
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
DiscordClient.initialize();
|
||||
|
||||
Application.current.onExit.add(function(exitCode) {
|
||||
|
@ -144,7 +144,7 @@ class InitState extends FlxState
|
|||
// Plugins provide a useful interface for globally active Flixel objects,
|
||||
// that receive update events regardless of the current state.
|
||||
// TODO: Move scripted Module behavior to a Flixel plugin.
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
funkin.util.plugins.MemoryGCPlugin.initialize();
|
||||
#end
|
||||
funkin.util.plugins.EvacuateDebugPlugin.initialize();
|
||||
|
@ -374,11 +374,16 @@ class InitState extends FlxState
|
|||
//
|
||||
// FLIXEL DEBUG SETUP
|
||||
//
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
// Make errors and warnings less annoying.
|
||||
// Forcing this always since I have never been happy to have the debugger to pop up
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
trace('Initializing Flixel debugger...');
|
||||
|
||||
#if !debug
|
||||
// Make errors less annoying on release builds.
|
||||
LogStyle.ERROR.openConsole = false;
|
||||
LogStyle.ERROR.errorSound = null;
|
||||
#end
|
||||
|
||||
// Make errors and warnings less annoying.
|
||||
LogStyle.WARNING.openConsole = false;
|
||||
LogStyle.WARNING.errorSound = null;
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package funkin.api.discord;
|
||||
|
||||
import Sys.sleep;
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
import discord_rpc.DiscordRpc;
|
||||
#end
|
||||
|
||||
class DiscordClient
|
||||
{
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
public function new()
|
||||
{
|
||||
trace("Discord Client starting...");
|
||||
|
|
|
@ -24,7 +24,7 @@ class NGUnsafe
|
|||
NG.core.calls.event.logEvent(event).send();
|
||||
trace('should have logged: ' + event);
|
||||
#else
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
trace('event:$event - not logged, missing NG.io lib');
|
||||
#end
|
||||
#end
|
||||
|
@ -39,7 +39,7 @@ class NGUnsafe
|
|||
if (!medal.unlocked) medal.sendUnlock();
|
||||
}
|
||||
#else
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
trace('medal:$id - not unlocked, missing NG.io lib');
|
||||
#end
|
||||
#end
|
||||
|
@ -63,7 +63,7 @@ class NGUnsafe
|
|||
}
|
||||
}
|
||||
#else
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
trace('Song:$song, Score:$score - not posted, missing NG.io lib');
|
||||
#end
|
||||
#end
|
||||
|
|
|
@ -239,7 +239,7 @@ class NGio
|
|||
NG.core.calls.event.logEvent(event).send();
|
||||
trace('should have logged: ' + event);
|
||||
#else
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
trace('event:$event - not logged, missing NG.io lib');
|
||||
#end
|
||||
#end
|
||||
|
@ -254,7 +254,7 @@ class NGio
|
|||
if (!medal.unlocked) medal.sendUnlock();
|
||||
}
|
||||
#else
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
trace('medal:$id - not unlocked, missing NG.io lib');
|
||||
#end
|
||||
#end
|
||||
|
@ -278,7 +278,7 @@ class NGio
|
|||
}
|
||||
}
|
||||
#else
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
trace('Song:$song, Score:$score - not posted, missing NG.io lib');
|
||||
#end
|
||||
#end
|
||||
|
|
|
@ -38,6 +38,17 @@ class PlayerData
|
|||
@:optional
|
||||
public var freeplayDJ:Null<PlayerFreeplayDJData> = null;
|
||||
|
||||
/**
|
||||
* Data for displaying this character in the Character Select menu.
|
||||
* If null, exclude from Character Select.
|
||||
*/
|
||||
@:optional
|
||||
public var charSelect:Null<PlayerCharSelectData> = null;
|
||||
|
||||
/**
|
||||
* Data for displaying this character in the results screen.
|
||||
*/
|
||||
@:optional
|
||||
public var results:Null<PlayerResultsData> = null;
|
||||
|
||||
/**
|
||||
|
@ -97,6 +108,9 @@ class PlayerFreeplayDJData
|
|||
@:optional
|
||||
var cartoon:Null<PlayerFreeplayDJCartoonData>;
|
||||
|
||||
@:optional
|
||||
var fistPump:Null<PlayerFreeplayDJFistPumpData>;
|
||||
|
||||
public function new()
|
||||
{
|
||||
animationMap = new Map();
|
||||
|
@ -183,6 +197,58 @@ class PlayerFreeplayDJData
|
|||
{
|
||||
return cartoon?.channelChangeFrame ?? 60;
|
||||
}
|
||||
|
||||
public function getFistPumpIntroStartFrame():Int
|
||||
{
|
||||
return fistPump?.introStartFrame ?? 0;
|
||||
}
|
||||
|
||||
public function getFistPumpIntroEndFrame():Int
|
||||
{
|
||||
return fistPump?.introEndFrame ?? 0;
|
||||
}
|
||||
|
||||
public function getFistPumpLoopStartFrame():Int
|
||||
{
|
||||
return fistPump?.loopStartFrame ?? 0;
|
||||
}
|
||||
|
||||
public function getFistPumpLoopEndFrame():Int
|
||||
{
|
||||
return fistPump?.loopEndFrame ?? 0;
|
||||
}
|
||||
|
||||
public function getFistPumpIntroBadStartFrame():Int
|
||||
{
|
||||
return fistPump?.introBadStartFrame ?? 0;
|
||||
}
|
||||
|
||||
public function getFistPumpIntroBadEndFrame():Int
|
||||
{
|
||||
return fistPump?.introBadEndFrame ?? 0;
|
||||
}
|
||||
|
||||
public function getFistPumpLoopBadStartFrame():Int
|
||||
{
|
||||
return fistPump?.loopBadStartFrame ?? 0;
|
||||
}
|
||||
|
||||
public function getFistPumpLoopBadEndFrame():Int
|
||||
{
|
||||
return fistPump?.loopBadEndFrame ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
class PlayerCharSelectData
|
||||
{
|
||||
/**
|
||||
* A zero-indexed number for the character's preferred position in the grid.
|
||||
* 0 = top left, 4 = center, 8 = bottom right
|
||||
* In the event of a conflict, the first character alphabetically gets it,
|
||||
* and others get shifted over.
|
||||
*/
|
||||
@:optional
|
||||
public var position:Null<Int>;
|
||||
}
|
||||
|
||||
typedef PlayerResultsData =
|
||||
|
@ -242,3 +308,30 @@ typedef PlayerFreeplayDJCartoonData =
|
|||
var loopFrame:Int;
|
||||
var channelChangeFrame:Int;
|
||||
}
|
||||
|
||||
typedef PlayerFreeplayDJFistPumpData =
|
||||
{
|
||||
@:default(0)
|
||||
var introStartFrame:Int;
|
||||
|
||||
@:default(4)
|
||||
var introEndFrame:Int;
|
||||
|
||||
@:default(4)
|
||||
var loopStartFrame:Int;
|
||||
|
||||
@:default(-1)
|
||||
var loopEndFrame:Int;
|
||||
|
||||
@:default(0)
|
||||
var introBadStartFrame:Int;
|
||||
|
||||
@:default(4)
|
||||
var introBadEndFrame:Int;
|
||||
|
||||
@:default(4)
|
||||
var loopBadStartFrame:Int;
|
||||
|
||||
@:default(-1)
|
||||
var loopBadEndFrame:Int;
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@ package funkin.data.freeplay.player;
|
|||
import funkin.data.freeplay.player.PlayerData;
|
||||
import funkin.ui.freeplay.charselect.PlayableCharacter;
|
||||
import funkin.ui.freeplay.charselect.ScriptedPlayableCharacter;
|
||||
import funkin.save.Save;
|
||||
|
||||
class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData>
|
||||
{
|
||||
|
@ -53,6 +54,41 @@ class PlayerRegistry extends BaseRegistry<PlayableCharacter, PlayerData>
|
|||
log('Loaded ${countEntries()} playable characters with ${ownedCharacterIds.size()} associations.');
|
||||
}
|
||||
|
||||
public function countUnlockedCharacters():Int
|
||||
{
|
||||
var count = 0;
|
||||
|
||||
for (charId in listEntryIds())
|
||||
{
|
||||
var player = fetchEntry(charId);
|
||||
if (player == null) continue;
|
||||
|
||||
if (player.isUnlocked()) count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public function hasNewCharacter():Bool
|
||||
{
|
||||
var characters = Save.instance.charactersSeen.clone();
|
||||
|
||||
for (charId in listEntryIds())
|
||||
{
|
||||
var player = fetchEntry(charId);
|
||||
if (player == null) continue;
|
||||
|
||||
if (!player.isUnlocked()) continue;
|
||||
if (characters.contains(charId)) continue;
|
||||
|
||||
// This character is unlocked but we haven't seen them in Freeplay yet.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fallthrough case.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the playable character associated with a given stage character.
|
||||
* @param characterId The stage character ID.
|
||||
|
|
|
@ -93,8 +93,8 @@ class StageRegistry extends BaseRegistry<Stage, StageData>
|
|||
public function listBaseGameStageIds():Array<String>
|
||||
{
|
||||
return [
|
||||
"mainStage", "spookyMansion", "phillyTrain", "phillyTrainErect", "limoRide", "limoRideErect", "mallXmas", "mallEvil", "school", "schoolEvil",
|
||||
"tankmanBattlefield", "phillyStreets", "phillyBlazin",
|
||||
"mainStage", "mainStageErect", "spookyMansion", "phillyTrain", "phillyTrainErect", "limoRide", "limoRideErect", "mallXmas", "mallEvil", "school",
|
||||
"schoolEvil", "tankmanBattlefield", "phillyStreets", "phillyBlazin",
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -93,9 +93,9 @@ typedef LevelPropData =
|
|||
* The frequency to bop at, in beats.
|
||||
* 1 = every beat, 2 = every other beat, etc.
|
||||
* Supports up to 0.25 precision.
|
||||
* @default 0.0
|
||||
* @default 1.0
|
||||
*/
|
||||
@:default(0.0)
|
||||
@:default(1.0)
|
||||
@:optional
|
||||
var danceEvery:Float;
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ class FlxAtlasSprite extends FlxAnimate
|
|||
FrameRate: 24.0,
|
||||
Reversed: false,
|
||||
// ?OnComplete:Void -> Void,
|
||||
ShowPivot: #if debug false #else false #end,
|
||||
ShowPivot: false,
|
||||
Antialiasing: true,
|
||||
ScrollFactor: null,
|
||||
// Offset: new FlxPoint(0, 0), // This is just FlxSprite.offset
|
||||
|
|
|
@ -356,9 +356,10 @@ class Controls extends FlxActionSet
|
|||
|
||||
public function check(name:Action, trigger:FlxInputState = JUST_PRESSED, gamepadOnly:Bool = false):Bool
|
||||
{
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
if (!byName.exists(name)) throw 'Invalid name: $name';
|
||||
#end
|
||||
|
||||
var action = byName[name];
|
||||
if (gamepadOnly) return action.checkFiltered(trigger, GAMEPAD);
|
||||
else
|
||||
|
@ -367,7 +368,7 @@ class Controls extends FlxActionSet
|
|||
|
||||
public function getKeysForAction(name:Action):Array<FlxKey>
|
||||
{
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
if (!byName.exists(name)) throw 'Invalid name: $name';
|
||||
#end
|
||||
|
||||
|
@ -382,7 +383,7 @@ class Controls extends FlxActionSet
|
|||
|
||||
public function getButtonsForAction(name:Action):Array<FlxGamepadInputID>
|
||||
{
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
if (!byName.exists(name)) throw 'Invalid name: $name';
|
||||
#end
|
||||
|
||||
|
|
|
@ -28,11 +28,10 @@ class PolymodHandler
|
|||
{
|
||||
/**
|
||||
* The API version that mods should comply with.
|
||||
* Format this with Semantic Versioning; <MAJOR>.<MINOR>.<PATCH>.
|
||||
* Bug fixes increment the patch version, new features increment the minor version.
|
||||
* Changes that break old mods increment the major version.
|
||||
* Indicates which mods are compatible with this version of the game.
|
||||
* Minor updates rarely impact mods but major versions often do.
|
||||
*/
|
||||
static final API_VERSION:String = '0.1.0';
|
||||
static final API_VERSION:String = "0.5.0"; // Constants.VERSION;
|
||||
|
||||
/**
|
||||
* Where relative to the executable that mods are located.
|
||||
|
@ -178,7 +177,7 @@ class PolymodHandler
|
|||
loadedModIds.push(mod.id);
|
||||
}
|
||||
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
var fileList:Array<String> = Polymod.listModFiles(PolymodAssetType.IMAGE);
|
||||
trace('Installed mods have replaced ${fileList.length} images.');
|
||||
for (item in fileList)
|
||||
|
@ -258,8 +257,29 @@ class PolymodHandler
|
|||
// Unserializerr.DEFAULT_RESOLVER.resolveClass() can access blacklisted packages
|
||||
Polymod.blacklistImport('Unserializer');
|
||||
|
||||
// `lime.system.CFFI`
|
||||
// Can load and execute compiled binaries.
|
||||
Polymod.blacklistImport('lime.system.CFFI');
|
||||
|
||||
// `lime.system.JNI`
|
||||
// Can load and execute compiled binaries.
|
||||
Polymod.blacklistImport('lime.system.JNI');
|
||||
|
||||
// `lime.system.System`
|
||||
// System.load() can load malicious DLLs
|
||||
Polymod.blacklistImport('lime.system.System');
|
||||
|
||||
// `lime.utils.Assets`
|
||||
// Literally just has a private `resolveClass` function for some reason?
|
||||
Polymod.blacklistImport('lime.utils.Assets');
|
||||
Polymod.blacklistImport('openfl.utils.Assets');
|
||||
|
||||
// `openfl.desktop.NativeProcess`
|
||||
// Can load native processes on the host operating system.
|
||||
Polymod.blacklistImport('openfl.desktop.NativeProcess');
|
||||
|
||||
// `polymod.*`
|
||||
// You can probably unblacklist a module
|
||||
// Contains functions which may allow for un-blacklisting other modules.
|
||||
for (cls in ClassMacro.listClassesInPackage('polymod'))
|
||||
{
|
||||
if (cls == null) continue;
|
||||
|
@ -268,6 +288,7 @@ class PolymodHandler
|
|||
}
|
||||
|
||||
// `sys.*`
|
||||
// Access to system utilities such as the file system.
|
||||
for (cls in ClassMacro.listClassesInPackage('sys'))
|
||||
{
|
||||
if (cls == null) continue;
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
package funkin.modding.base;
|
||||
|
||||
/**
|
||||
* A script that can be tied to an FlxUIState.
|
||||
* Create a scripted class that extends FlxUIState to use this.
|
||||
*/
|
||||
@:hscriptClass
|
||||
class ScriptedFlxUIState extends flixel.addons.ui.FlxUIState implements HScriptedClass {}
|
|
@ -430,7 +430,7 @@ class PauseSubState extends MusicBeatSubState
|
|||
resume(this);
|
||||
}
|
||||
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
// to pause the game and get screenshots easy, press H on pause menu!
|
||||
if (FlxG.keys.justPressed.H)
|
||||
{
|
||||
|
|
|
@ -67,7 +67,7 @@ import lime.ui.Haptic;
|
|||
import openfl.display.BitmapData;
|
||||
import openfl.geom.Rectangle;
|
||||
import openfl.Lib;
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
import Discord.DiscordClient;
|
||||
#end
|
||||
|
||||
|
@ -445,7 +445,7 @@ class PlayState extends MusicBeatSubState
|
|||
*/
|
||||
public var vocals:VoicesGroup;
|
||||
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
// Discord RPC variables
|
||||
var storyDifficultyText:String = '';
|
||||
var iconRPC:String = '';
|
||||
|
@ -698,7 +698,7 @@ class PlayState extends MusicBeatSubState
|
|||
initStrumlines();
|
||||
initPopups();
|
||||
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
// Initialize Discord Rich Presence.
|
||||
initDiscord();
|
||||
#end
|
||||
|
@ -738,7 +738,7 @@ class PlayState extends MusicBeatSubState
|
|||
rightWatermarkText.cameras = [camHUD];
|
||||
|
||||
// Initialize some debug stuff.
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
// Display the version number (and git commit hash) in the bottom right corner.
|
||||
this.rightWatermarkText.text = Constants.VERSION;
|
||||
|
||||
|
@ -977,7 +977,7 @@ class PlayState extends MusicBeatSubState
|
|||
// boyfriendPos.put(); // TODO: Why is this here?
|
||||
}
|
||||
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
DiscordClient.changePresence(detailsPausedText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC);
|
||||
#end
|
||||
}
|
||||
|
@ -1036,7 +1036,7 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
// Disable updates, preventing animations in the background from playing.
|
||||
persistentUpdate = false;
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
if (FlxG.keys.pressed.THREE)
|
||||
{
|
||||
// TODO: Change the key or delete this?
|
||||
|
@ -1047,7 +1047,7 @@ class PlayState extends MusicBeatSubState
|
|||
{
|
||||
#end
|
||||
persistentDraw = false;
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
}
|
||||
#end
|
||||
|
||||
|
@ -1066,7 +1066,7 @@ class PlayState extends MusicBeatSubState
|
|||
moveToGameOver();
|
||||
}
|
||||
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
// Game Over doesn't get his own variable because it's only used here
|
||||
DiscordClient.changePresence('Game Over - ' + detailsText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC);
|
||||
#end
|
||||
|
@ -1278,7 +1278,7 @@ class PlayState extends MusicBeatSubState
|
|||
// Resume the countdown.
|
||||
Countdown.resumeCountdown();
|
||||
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
if (startTimer.finished)
|
||||
{
|
||||
DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC, true,
|
||||
|
@ -1311,7 +1311,7 @@ class PlayState extends MusicBeatSubState
|
|||
VideoCutscene.resumeVideo();
|
||||
#end
|
||||
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
if (health > Constants.HEALTH_MIN && !paused && FlxG.autoPause)
|
||||
{
|
||||
if (Conductor.instance.songPosition > 0.0) DiscordClient.changePresence(detailsText, currentSong.song
|
||||
|
@ -1337,7 +1337,7 @@ class PlayState extends MusicBeatSubState
|
|||
if (FlxG.autoPause) VideoCutscene.pauseVideo();
|
||||
#end
|
||||
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
if (health > Constants.HEALTH_MIN && !paused && FlxG.autoPause) DiscordClient.changePresence(detailsPausedText,
|
||||
currentSong.song + ' (' + storyDifficultyText + ')', iconRPC);
|
||||
#end
|
||||
|
@ -1558,7 +1558,7 @@ class PlayState extends MusicBeatSubState
|
|||
// Add the stage to the scene.
|
||||
this.add(currentStage);
|
||||
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
FlxG.console.registerObject('stage', currentStage);
|
||||
#end
|
||||
}
|
||||
|
@ -1661,7 +1661,7 @@ class PlayState extends MusicBeatSubState
|
|||
{
|
||||
currentStage.addCharacter(girlfriend, GF);
|
||||
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
FlxG.console.registerObject('gf', girlfriend);
|
||||
#end
|
||||
}
|
||||
|
@ -1670,7 +1670,7 @@ class PlayState extends MusicBeatSubState
|
|||
{
|
||||
currentStage.addCharacter(boyfriend, BF);
|
||||
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
FlxG.console.registerObject('bf', boyfriend);
|
||||
#end
|
||||
}
|
||||
|
@ -1681,7 +1681,7 @@ class PlayState extends MusicBeatSubState
|
|||
// Camera starts at dad.
|
||||
cameraFollowPoint.setPosition(dad.cameraFocusPoint.x, dad.cameraFocusPoint.y);
|
||||
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
FlxG.console.registerObject('dad', dad);
|
||||
#end
|
||||
}
|
||||
|
@ -1744,7 +1744,7 @@ class PlayState extends MusicBeatSubState
|
|||
*/
|
||||
function initDiscord():Void
|
||||
{
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
storyDifficultyText = difficultyString();
|
||||
iconRPC = currentSong.player2;
|
||||
|
||||
|
@ -1957,7 +1957,7 @@ class PlayState extends MusicBeatSubState
|
|||
vocals.pitch = playbackRate;
|
||||
resyncVocals();
|
||||
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
// Updating Discord Rich Presence (with Time Left)
|
||||
DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC, true, currentSongLengthMs);
|
||||
#end
|
||||
|
@ -1979,7 +1979,7 @@ class PlayState extends MusicBeatSubState
|
|||
if (vocals == null) return;
|
||||
|
||||
// Skip this if the music is paused (GameOver, Pause menu, start-of-song offset, etc.)
|
||||
if (!FlxG.sound.music.playing) return;
|
||||
if (!(FlxG.sound.music?.playing ?? false)) return;
|
||||
var timeToPlayAt:Float = Conductor.instance.songPosition - Conductor.instance.instrumentalOffset;
|
||||
FlxG.sound.music.pause();
|
||||
vocals.pause();
|
||||
|
@ -2221,10 +2221,14 @@ class PlayState extends MusicBeatSubState
|
|||
// Calling event.cancelEvent() skips all the other logic! Neat!
|
||||
if (event.eventCanceled) continue;
|
||||
|
||||
// Skip handling the miss in botplay!
|
||||
if (!isBotPlayMode)
|
||||
{
|
||||
// Judge the miss.
|
||||
// NOTE: This is what handles the scoring.
|
||||
trace('Missed note! ${note.noteData}');
|
||||
onNoteMiss(note, event.playSound, event.healthChange);
|
||||
}
|
||||
|
||||
note.handledMiss = true;
|
||||
}
|
||||
|
@ -2321,9 +2325,16 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
playerStrumline.pressKey(input.noteDirection);
|
||||
|
||||
// Don't credit or penalize inputs in Bot Play.
|
||||
if (isBotPlayMode) continue;
|
||||
|
||||
var notesInDirection:Array<NoteSprite> = notesByDirection[input.noteDirection];
|
||||
|
||||
if (!Constants.GHOST_TAPPING && notesInDirection.length == 0)
|
||||
#if FEATURE_GHOST_TAPPING
|
||||
if ((!playerStrumline.mayGhostTap()) && notesInDirection.length == 0)
|
||||
#else
|
||||
if (notesInDirection.length == 0)
|
||||
#end
|
||||
{
|
||||
// Pressed a wrong key with no notes nearby.
|
||||
// Perform a ghost miss (anti-spam).
|
||||
|
@ -2333,16 +2344,6 @@ class PlayState extends MusicBeatSubState
|
|||
playerStrumline.playPress(input.noteDirection);
|
||||
trace('PENALTY Score: ${songScore}');
|
||||
}
|
||||
else if (Constants.GHOST_TAPPING && (!playerStrumline.mayGhostTap()) && notesInDirection.length == 0)
|
||||
{
|
||||
// Pressed a wrong key with notes visible on-screen.
|
||||
// Perform a ghost miss (anti-spam).
|
||||
ghostNoteMiss(input.noteDirection, notesInRange.length > 0);
|
||||
|
||||
// Play the strumline animation.
|
||||
playerStrumline.playPress(input.noteDirection);
|
||||
trace('PENALTY Score: ${songScore}');
|
||||
}
|
||||
else if (notesInDirection.length == 0)
|
||||
{
|
||||
// Press a key with no penalty.
|
||||
|
@ -2552,7 +2553,7 @@ class PlayState extends MusicBeatSubState
|
|||
*/
|
||||
function debugKeyShit():Void
|
||||
{
|
||||
#if CHART_EDITOR_SUPPORTED
|
||||
#if FEATURE_CHART_EDITOR
|
||||
// Open the stage editor overlaying the current state.
|
||||
if (controls.DEBUG_STAGE)
|
||||
{
|
||||
|
@ -2582,7 +2583,7 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
#end
|
||||
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
// H: Hide the HUD.
|
||||
if (FlxG.keys.justPressed.H) camHUD.visible = !camHUD.visible;
|
||||
|
||||
|
@ -2599,7 +2600,7 @@ class PlayState extends MusicBeatSubState
|
|||
// 9: Toggle the old icon.
|
||||
if (FlxG.keys.justPressed.NINE) iconP1.toggleOldIcon();
|
||||
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
// PAGEUP: Skip forward two sections.
|
||||
// SHIFT+PAGEUP: Skip forward twenty sections.
|
||||
if (FlxG.keys.justPressed.PAGEUP) changeSection(FlxG.keys.pressed.SHIFT ? 20 : 2);
|
||||
|
@ -2918,11 +2919,16 @@ class PlayState extends MusicBeatSubState
|
|||
FunkinSound.playOnce(Paths.sound('Lights_Shut_off'), function() {
|
||||
// no camFollow so it centers on horror tree
|
||||
var targetSong:Song = SongRegistry.instance.fetchEntry(targetSongId);
|
||||
var targetVariation:String = currentVariation;
|
||||
if (!targetSong.hasDifficulty(PlayStatePlaylist.campaignDifficulty, currentVariation))
|
||||
{
|
||||
targetVariation = targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty) ?? Constants.DEFAULT_VARIATION;
|
||||
}
|
||||
LoadingState.loadPlayState(
|
||||
{
|
||||
targetSong: targetSong,
|
||||
targetDifficulty: PlayStatePlaylist.campaignDifficulty,
|
||||
targetVariation: currentVariation,
|
||||
targetVariation: targetVariation,
|
||||
cameraFollowPoint: cameraFollowPoint.getPosition(),
|
||||
});
|
||||
});
|
||||
|
@ -2930,11 +2936,16 @@ class PlayState extends MusicBeatSubState
|
|||
else
|
||||
{
|
||||
var targetSong:Song = SongRegistry.instance.fetchEntry(targetSongId);
|
||||
var targetVariation:String = currentVariation;
|
||||
if (!targetSong.hasDifficulty(PlayStatePlaylist.campaignDifficulty, currentVariation))
|
||||
{
|
||||
targetVariation = targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty) ?? Constants.DEFAULT_VARIATION;
|
||||
}
|
||||
LoadingState.loadPlayState(
|
||||
{
|
||||
targetSong: targetSong,
|
||||
targetDifficulty: PlayStatePlaylist.campaignDifficulty,
|
||||
targetVariation: currentVariation,
|
||||
targetVariation: targetVariation,
|
||||
cameraFollowPoint: cameraFollowPoint.getPosition(),
|
||||
});
|
||||
}
|
||||
|
@ -3315,7 +3326,7 @@ class PlayState extends MusicBeatSubState
|
|||
scrollSpeedTweens = [];
|
||||
}
|
||||
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
/**
|
||||
* Jumps forward or backward a number of sections in the song.
|
||||
* Accounts for BPM changes, does not prevent death from skipped notes.
|
||||
|
|
|
@ -129,7 +129,7 @@ class AnimateAtlasCharacter extends BaseCharacter
|
|||
{
|
||||
trace('[ATLASCHAR] Loading sprite atlas for ${characterId}.');
|
||||
|
||||
var sprite:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas(_data.assetPath, 'shared'));
|
||||
var sprite:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas(_data.assetPath));
|
||||
|
||||
// sprite.onAnimationComplete.removeAll();
|
||||
sprite.onAnimationComplete.add(this.onAnimationFinished);
|
||||
|
|
|
@ -366,12 +366,19 @@ class BaseCharacter extends Bopper
|
|||
// and Darnell (this keeps the flame on his lighter flickering).
|
||||
// Works for idle, singLEFT/RIGHT/UP/DOWN, alt singing animations, and anything else really.
|
||||
|
||||
if (!getCurrentAnimation().endsWith(Constants.ANIMATION_HOLD_SUFFIX)
|
||||
&& hasAnimation(getCurrentAnimation() + Constants.ANIMATION_HOLD_SUFFIX)
|
||||
&& isAnimationFinished())
|
||||
if (isAnimationFinished()
|
||||
&& !getCurrentAnimation().endsWith(Constants.ANIMATION_HOLD_SUFFIX)
|
||||
&& hasAnimation(getCurrentAnimation() + Constants.ANIMATION_HOLD_SUFFIX))
|
||||
{
|
||||
playAnimation(getCurrentAnimation() + Constants.ANIMATION_HOLD_SUFFIX);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isAnimationFinished())
|
||||
{
|
||||
trace('Not playing hold (${getCurrentAnimation()}) (${isAnimationFinished()}, ${getCurrentAnimation().endsWith(Constants.ANIMATION_HOLD_SUFFIX)}, ${hasAnimation(getCurrentAnimation() + Constants.ANIMATION_HOLD_SUFFIX)})');
|
||||
}
|
||||
}
|
||||
|
||||
// Handle character note hold time.
|
||||
if (isSinging())
|
||||
|
@ -411,7 +418,6 @@ class BaseCharacter extends Bopper
|
|||
else
|
||||
{
|
||||
// Play the idle animation.
|
||||
trace('${characterId}: attempting dance');
|
||||
dance(true);
|
||||
}
|
||||
}
|
||||
|
@ -594,7 +600,7 @@ class BaseCharacter extends Bopper
|
|||
/**
|
||||
* Every time a wrong key is pressed, play the miss animation if we are Boyfriend.
|
||||
*/
|
||||
public override function onNoteGhostMiss(event:GhostMissNoteScriptEvent)
|
||||
public override function onNoteGhostMiss(event:GhostMissNoteScriptEvent):Void
|
||||
{
|
||||
super.onNoteGhostMiss(event);
|
||||
|
||||
|
@ -628,12 +634,12 @@ class BaseCharacter extends Bopper
|
|||
var anim:String = 'sing${dir.nameUpper}${miss ? 'miss' : ''}${suffix != '' ? '-${suffix}' : ''}';
|
||||
|
||||
// restart even if already playing, because the character might sing the same note twice.
|
||||
trace('Playing ${anim}...');
|
||||
playAnimation(anim, true);
|
||||
}
|
||||
|
||||
public override function playAnimation(name:String, restart:Bool = false, ignoreOther:Bool = false, reversed:Bool = false):Void
|
||||
{
|
||||
// FlxG.watch.addQuick('playAnim(${characterName})', name);
|
||||
super.playAnimation(name, restart, ignoreOther, reversed);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -305,6 +305,8 @@ class CharacterDataParser
|
|||
icon = "darnell";
|
||||
case "senpai-angry":
|
||||
icon = "senpai";
|
||||
case "spooky-dark":
|
||||
icon = "spooky";
|
||||
case "tankman-atlas":
|
||||
icon = "tankman";
|
||||
}
|
||||
|
|
|
@ -62,11 +62,13 @@ class MultiSparrowCharacter extends BaseCharacter
|
|||
}
|
||||
}
|
||||
|
||||
var texture:FlxAtlasFrames = Paths.getSparrowAtlas(_data.assetPath, 'shared');
|
||||
var texture:FlxAtlasFrames = Paths.getSparrowAtlas(_data.assetPath);
|
||||
|
||||
if (texture == null)
|
||||
{
|
||||
trace('Multi-Sparrow atlas could not load PRIMARY texture: ${_data.assetPath}');
|
||||
FlxG.log.error('Multi-Sparrow atlas could not load PRIMARY texture: ${_data.assetPath}');
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -76,7 +78,7 @@ class MultiSparrowCharacter extends BaseCharacter
|
|||
|
||||
for (asset in assetList)
|
||||
{
|
||||
var subTexture:FlxAtlasFrames = Paths.getSparrowAtlas(asset, 'shared');
|
||||
var subTexture:FlxAtlasFrames = Paths.getSparrowAtlas(asset);
|
||||
// If we don't do this, the unused textures will be removed as soon as they're loaded.
|
||||
|
||||
if (subTexture == null)
|
||||
|
|
|
@ -30,7 +30,7 @@ class PackerCharacter extends BaseCharacter
|
|||
{
|
||||
trace('[PACKERCHAR] Loading spritesheet ${_data.assetPath} for ${characterId}');
|
||||
|
||||
var tex:FlxFramesCollection = Paths.getPackerAtlas(_data.assetPath, 'shared');
|
||||
var tex:FlxFramesCollection = Paths.getPackerAtlas(_data.assetPath);
|
||||
if (tex == null)
|
||||
{
|
||||
trace('Could not load Packer sprite: ${_data.assetPath}');
|
||||
|
|
|
@ -33,7 +33,7 @@ class SparrowCharacter extends BaseCharacter
|
|||
{
|
||||
trace('[SPARROWCHAR] Loading spritesheet ${_data.assetPath} for ${characterId}');
|
||||
|
||||
var tex:FlxFramesCollection = Paths.getSparrowAtlas(_data.assetPath, 'shared');
|
||||
var tex:FlxFramesCollection = Paths.getSparrowAtlas(_data.assetPath);
|
||||
if (tex == null)
|
||||
{
|
||||
trace('Could not load Sparrow sprite: ${_data.assetPath}');
|
||||
|
|
|
@ -114,7 +114,7 @@ class ZoomCameraSongEvent extends SongEvent
|
|||
name: 'zoom',
|
||||
title: 'Zoom Level',
|
||||
defaultValue: 1.0,
|
||||
step: 0.1,
|
||||
step: 0.05,
|
||||
type: SongEventFieldType.FLOAT,
|
||||
units: 'x'
|
||||
},
|
||||
|
|
|
@ -94,6 +94,10 @@ class Strumline extends FlxSpriteGroup
|
|||
|
||||
final noteStyle:NoteStyle;
|
||||
|
||||
#if FEATURE_GHOST_TAPPING
|
||||
var ghostTapTimer:Float = 0.0;
|
||||
#end
|
||||
|
||||
/**
|
||||
* The note data for the song. Should NOT be altered after the song starts,
|
||||
* so we can easily rewind.
|
||||
|
@ -179,21 +183,36 @@ class Strumline extends FlxSpriteGroup
|
|||
super.update(elapsed);
|
||||
|
||||
updateNotes();
|
||||
|
||||
#if FEATURE_GHOST_TAPPING
|
||||
updateGhostTapTimer(elapsed);
|
||||
#end
|
||||
}
|
||||
|
||||
#if FEATURE_GHOST_TAPPING
|
||||
/**
|
||||
* Returns `true` if no notes are in range of the strumline and the player can spam without penalty.
|
||||
*/
|
||||
public function mayGhostTap():Bool
|
||||
{
|
||||
// TODO: Refine this. Only querying "can be hit" is too tight but "is being rendered" is too loose.
|
||||
// Also, if you just hit a note, there should be a (short) period where this is off so you can't spam.
|
||||
|
||||
// If there are any notes on screen, we can't ghost tap.
|
||||
return notes.members.filter(function(note:NoteSprite) {
|
||||
return note != null && note.alive && !note.hasBeenHit;
|
||||
}).length == 0;
|
||||
// Any notes in range of the strumline.
|
||||
if (getNotesMayHit().length > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Any hold notes in range of the strumline.
|
||||
if (getHoldNotesHitOrMissed().length > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note has been hit recently.
|
||||
if (ghostTapTimer > 0.0) return false;
|
||||
|
||||
// **yippee**
|
||||
return true;
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
* Return notes that are within `Constants.HIT_WINDOW` ms of the strumline.
|
||||
|
@ -492,6 +511,32 @@ class Strumline extends FlxSpriteGroup
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return notes that are within, or way after, `Constants.HIT_WINDOW` ms of the strumline.
|
||||
* @return An array of `NoteSprite` objects.
|
||||
*/
|
||||
public function getNotesOnScreen():Array<NoteSprite>
|
||||
{
|
||||
return notes.members.filter(function(note:NoteSprite) {
|
||||
return note != null && note.alive && !note.hasBeenHit;
|
||||
});
|
||||
}
|
||||
|
||||
#if FEATURE_GHOST_TAPPING
|
||||
function updateGhostTapTimer(elapsed:Float):Void
|
||||
{
|
||||
// If it's still our turn, don't update the ghost tap timer.
|
||||
if (getNotesOnScreen().length > 0) return;
|
||||
|
||||
ghostTapTimer -= elapsed;
|
||||
|
||||
if (ghostTapTimer <= 0)
|
||||
{
|
||||
ghostTapTimer = 0;
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
* Called when the PlayState skips a large amount of time forward or backward.
|
||||
*/
|
||||
|
@ -563,6 +608,10 @@ class Strumline extends FlxSpriteGroup
|
|||
playStatic(dir);
|
||||
}
|
||||
resetScrollSpeed();
|
||||
|
||||
#if FEATURE_GHOST_TAPPING
|
||||
ghostTapTimer = 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function applyNoteData(data:Array<SongNoteData>):Void
|
||||
|
@ -602,6 +651,10 @@ class Strumline extends FlxSpriteGroup
|
|||
|
||||
note.holdNoteSprite.sustainLength = (note.holdNoteSprite.strumTime + note.holdNoteSprite.fullSustainLength) - conductorInUse.songPosition;
|
||||
}
|
||||
|
||||
#if FEATURE_GHOST_TAPPING
|
||||
ghostTapTimer = Constants.GHOST_TAP_DELAY;
|
||||
#end
|
||||
}
|
||||
|
||||
public function killNote(note:NoteSprite):Void
|
||||
|
|
|
@ -315,7 +315,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
|
|||
|
||||
public function isAnimationFinished():Bool
|
||||
{
|
||||
return this.animation.finished;
|
||||
return this.animation?.finished ?? false;
|
||||
}
|
||||
|
||||
public function setAnimationOffsets(name:String, xOffset:Float, yOffset:Float):Void
|
||||
|
|
|
@ -386,7 +386,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
|||
{
|
||||
if (character == null) return;
|
||||
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
// Temporary marker that shows where the character's location is relative to.
|
||||
// Should display at the stage position of the character (before any offsets).
|
||||
// TODO: Make this a toggle? It's useful to turn on from time to time.
|
||||
|
@ -436,8 +436,9 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
|||
// Start with the per-stage character position.
|
||||
// Subtracting the origin ensures characters are positioned relative to their feet.
|
||||
// Subtracting the global offset allows positioning on a per-character basis.
|
||||
character.x = stageCharData.position[0] - character.characterOrigin.x + character.globalOffsets[0];
|
||||
character.y = stageCharData.position[1] - character.characterOrigin.y + character.globalOffsets[1];
|
||||
// We previously applied the global offset here but that is now done elsewhere.
|
||||
character.x = stageCharData.position[0] - character.characterOrigin.x;
|
||||
character.y = stageCharData.position[1] - character.characterOrigin.y;
|
||||
|
||||
@:privateAccess(funkin.play.stage.Bopper)
|
||||
{
|
||||
|
@ -451,7 +452,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
|||
character.cameraFocusPoint.x += stageCharData.cameraOffsets[0];
|
||||
character.cameraFocusPoint.y += stageCharData.cameraOffsets[1];
|
||||
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
// Draw the debug icon at the character's feet.
|
||||
if (charType == BF || charType == DAD)
|
||||
{
|
||||
|
@ -468,7 +469,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
|||
|
||||
ScriptEventDispatcher.callEvent(character, new ScriptEvent(ADDED, false));
|
||||
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
debugIconGroup.add(debugIcon);
|
||||
debugIconGroup.add(debugIcon2);
|
||||
#end
|
||||
|
@ -769,39 +770,15 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
|||
* A function that gets called once per step in the song.
|
||||
* @param curStep The current step number.
|
||||
*/
|
||||
public function onStepHit(event:SongTimeScriptEvent):Void
|
||||
{
|
||||
// Override me in your scripted stage to perform custom behavior!
|
||||
// Make sure to call super.onStepHit(event) if you want to keep the boppers dancing.
|
||||
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onStepHit(event:SongTimeScriptEvent):Void {}
|
||||
|
||||
/**
|
||||
* A function that gets called once per beat in the song (once every four steps).
|
||||
* @param curStep The current beat number.
|
||||
*/
|
||||
public function onBeatHit(event:SongTimeScriptEvent):Void
|
||||
{
|
||||
// Override me in your scripted stage to perform custom behavior!
|
||||
// Make sure to call super.onBeatHit(event) if you want to keep the boppers dancing.
|
||||
public function onBeatHit(event:SongTimeScriptEvent):Void {}
|
||||
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
|
||||
public function onUpdate(event:UpdateScriptEvent)
|
||||
{
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onUpdate(event:UpdateScriptEvent) {}
|
||||
|
||||
public override function kill()
|
||||
{
|
||||
|
@ -883,129 +860,41 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
|||
|
||||
public function onScriptEvent(event:ScriptEvent)
|
||||
{
|
||||
// Ensure all custom events get broadcast to the elements of the stage.
|
||||
// If we do it here, we don't have to add a handler to EACH script event function.
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
|
||||
public function onPause(event:PauseScriptEvent)
|
||||
{
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onPause(event:PauseScriptEvent) {}
|
||||
|
||||
public function onResume(event:ScriptEvent)
|
||||
{
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onResume(event:ScriptEvent) {}
|
||||
|
||||
public function onSongStart(event:ScriptEvent)
|
||||
{
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onSongStart(event:ScriptEvent) {}
|
||||
|
||||
public function onSongEnd(event:ScriptEvent)
|
||||
{
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onSongEnd(event:ScriptEvent) {}
|
||||
|
||||
public function onGameOver(event:ScriptEvent)
|
||||
{
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onGameOver(event:ScriptEvent) {}
|
||||
|
||||
public function onCountdownStart(event:CountdownScriptEvent)
|
||||
{
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onCountdownStart(event:CountdownScriptEvent) {}
|
||||
|
||||
public function onCountdownStep(event:CountdownScriptEvent)
|
||||
{
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onCountdownStep(event:CountdownScriptEvent) {}
|
||||
|
||||
public function onCountdownEnd(event:CountdownScriptEvent)
|
||||
{
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onCountdownEnd(event:CountdownScriptEvent) {}
|
||||
|
||||
public function onNoteIncoming(event:NoteScriptEvent)
|
||||
{
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onNoteIncoming(event:NoteScriptEvent) {}
|
||||
|
||||
public function onNoteHit(event:HitNoteScriptEvent)
|
||||
{
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onNoteHit(event:HitNoteScriptEvent) {}
|
||||
|
||||
public function onNoteMiss(event:NoteScriptEvent)
|
||||
{
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onNoteMiss(event:NoteScriptEvent) {}
|
||||
|
||||
public function onSongEvent(event:SongEventScriptEvent)
|
||||
{
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onSongEvent(event:SongEventScriptEvent) {}
|
||||
|
||||
public function onNoteGhostMiss(event:GhostMissNoteScriptEvent)
|
||||
{
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onNoteGhostMiss(event:GhostMissNoteScriptEvent) {}
|
||||
|
||||
public function onSongLoaded(event:SongLoadScriptEvent)
|
||||
{
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onSongLoaded(event:SongLoadScriptEvent) {}
|
||||
|
||||
public function onSongRetry(event:ScriptEvent)
|
||||
{
|
||||
for (bopper in boppers)
|
||||
{
|
||||
ScriptEventDispatcher.callEvent(bopper, event);
|
||||
}
|
||||
}
|
||||
public function onSongRetry(event:ScriptEvent) {}
|
||||
}
|
||||
|
|
|
@ -121,6 +121,12 @@ class Save
|
|||
modOptions: [],
|
||||
},
|
||||
|
||||
unlocks:
|
||||
{
|
||||
// Default to having seen the default character.
|
||||
charactersSeen: ["bf"],
|
||||
},
|
||||
|
||||
optionsChartEditor:
|
||||
{
|
||||
// Reasonable defaults.
|
||||
|
@ -393,6 +399,22 @@ class Save
|
|||
return data.optionsChartEditor.playbackSpeed;
|
||||
}
|
||||
|
||||
public var charactersSeen(get, never):Array<String>;
|
||||
|
||||
function get_charactersSeen():Array<String>
|
||||
{
|
||||
return data.unlocks.charactersSeen;
|
||||
}
|
||||
|
||||
/**
|
||||
* When we've seen a character unlock, add it to the list of characters seen.
|
||||
* @param character
|
||||
*/
|
||||
public function addCharacterSeen(character:String):Void
|
||||
{
|
||||
data.unlocks.charactersSeen.push(character);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the score the user achieved for a given level on a given difficulty.
|
||||
*
|
||||
|
@ -471,11 +493,19 @@ class Save
|
|||
for (difficulty in difficultyList)
|
||||
{
|
||||
var score:Null<SaveScoreData> = getLevelScore(levelId, difficulty);
|
||||
// TODO: Do we need to check accuracy/score here?
|
||||
if (score != null)
|
||||
{
|
||||
if (score.score > 0)
|
||||
{
|
||||
// Level has score data, which means we cleared it!
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Level has score data, but the score is 0.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -630,11 +660,19 @@ class Save
|
|||
for (difficulty in difficultyList)
|
||||
{
|
||||
var score:Null<SaveScoreData> = getSongScore(songId, difficulty);
|
||||
// TODO: Do we need to check accuracy/score here?
|
||||
if (score != null)
|
||||
{
|
||||
if (score.score > 0)
|
||||
{
|
||||
// Level has score data, which means we cleared it!
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Level has score data, but the score is 0.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -956,6 +994,8 @@ typedef RawSaveData =
|
|||
*/
|
||||
var options:SaveDataOptions;
|
||||
|
||||
var unlocks:SaveDataUnlocks;
|
||||
|
||||
/**
|
||||
* The user's favorited songs in the Freeplay menu,
|
||||
* as a list of song IDs.
|
||||
|
@ -980,6 +1020,15 @@ typedef SaveApiNewgroundsData =
|
|||
var sessionId:Null<String>;
|
||||
}
|
||||
|
||||
typedef SaveDataUnlocks =
|
||||
{
|
||||
/**
|
||||
* Every time we see the unlock animation for a character,
|
||||
* add it to this list so that we don't show it again.
|
||||
*/
|
||||
var charactersSeen:Array<String>;
|
||||
}
|
||||
|
||||
/**
|
||||
* An anoymous structure containing options about the user's high scores.
|
||||
*/
|
||||
|
|
|
@ -22,14 +22,26 @@ class PixelatedIcon extends FlxSprite
|
|||
|
||||
switch (char)
|
||||
{
|
||||
case 'monster-christmas':
|
||||
charPath += 'monsterpixel';
|
||||
case 'mom-car':
|
||||
charPath += 'mommypixel';
|
||||
case 'darnell-blazin':
|
||||
charPath += 'darnellpixel';
|
||||
case 'senpai-angry':
|
||||
charPath += 'senpaipixel';
|
||||
case "bf-christmas" | "bf-car" | "bf-pixel" | "bf-holding-gf":
|
||||
charPath += "bfpixel";
|
||||
case "monster-christmas":
|
||||
charPath += "monsterpixel";
|
||||
case "mom" | "mom-car":
|
||||
charPath += "mommypixel";
|
||||
case "pico-blazin" | "pico-playable" | "pico-speaker":
|
||||
charPath += "picopixel";
|
||||
case "gf-christmas" | "gf-car" | "gf-pixel" | "gf-tankmen":
|
||||
charPath += "gfpixel";
|
||||
case "dad":
|
||||
charPath += "dadpixel";
|
||||
case "darnell-blazin":
|
||||
charPath += "darnellpixel";
|
||||
case "senpai-angry":
|
||||
charPath += "senpaipixel";
|
||||
case "spooky-dark":
|
||||
charPath += "spookypixel";
|
||||
case "tankman-atlas":
|
||||
charPath += "tankmanpixel";
|
||||
default:
|
||||
charPath += '${char}pixel';
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ class CharSelectGF extends FlxAtlasSprite
|
|||
|
||||
var analyzer:SpectralAnalyzer;
|
||||
|
||||
var curGF:GFChar = GF;
|
||||
|
||||
public function new()
|
||||
{
|
||||
super(0, 0, Paths.animateAtlas("charSelect/gfChill"));
|
||||
|
@ -35,7 +37,7 @@ class CharSelectGF extends FlxAtlasSprite
|
|||
switchGF("bf");
|
||||
}
|
||||
|
||||
override public function update(elapsed:Float)
|
||||
override public function update(elapsed:Float):Void
|
||||
{
|
||||
super.update(elapsed);
|
||||
|
||||
|
@ -103,7 +105,7 @@ class CharSelectGF extends FlxAtlasSprite
|
|||
* @param animInfo Should not be confused with animInInfo!
|
||||
* This is merely a local var for the function!
|
||||
*/
|
||||
function doFade(animInfo:FramesJSFLInfo)
|
||||
function doFade(animInfo:FramesJSFLInfo):Void
|
||||
{
|
||||
fadeTimer += FlxG.elapsed;
|
||||
if (fadeTimer >= 1 / 24)
|
||||
|
@ -141,27 +143,30 @@ class CharSelectGF extends FlxAtlasSprite
|
|||
fadeAnimIndex = 0;
|
||||
}
|
||||
|
||||
public function switchGF(str:String)
|
||||
/**
|
||||
* For switching between "GFs" such as gf, nene, etc
|
||||
* @param bf Which BF we are selecting, so that we know the accompyaning GF
|
||||
*/
|
||||
public function switchGF(bf:String):Void
|
||||
{
|
||||
str = switch (str)
|
||||
var prevGF:GFChar = curGF;
|
||||
switch (bf)
|
||||
{
|
||||
case "pico":
|
||||
"nene";
|
||||
curGF = NENE;
|
||||
case "bf":
|
||||
"gf";
|
||||
curGF = GF;
|
||||
default:
|
||||
"gf";
|
||||
curGF = GF;
|
||||
}
|
||||
|
||||
char = str;
|
||||
switch str
|
||||
{
|
||||
default:
|
||||
loadAtlas(Paths.animateAtlas("charSelect/" + str + "Chill"));
|
||||
}
|
||||
// We don't need to update any anims if we didn't change GF
|
||||
if (prevGF == curGF) return;
|
||||
|
||||
animInInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + str + "AnimInfo/" + str + "In.txt"));
|
||||
animOutInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + str + "AnimInfo/" + str + "Out.txt"));
|
||||
loadAtlas(Paths.animateAtlas("charSelect/" + curGF + "Chill"));
|
||||
|
||||
animInInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + curGF + "AnimInfo/" + curGF + "In.txt"));
|
||||
animOutInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + curGF + "AnimInfo/" + curGF + "Out.txt"));
|
||||
|
||||
playAnimation("idle", true, false, true);
|
||||
// addFrameCallback(getNextFrameLabel("idle"), () -> playAnimation("idle", true, false, false));
|
||||
|
@ -176,3 +181,9 @@ enum FadeStatus
|
|||
FADE_OUT;
|
||||
FADE_IN;
|
||||
}
|
||||
|
||||
enum abstract GFChar(String) from String to String
|
||||
{
|
||||
var GF = "gf";
|
||||
var NENE = "nene";
|
||||
}
|
||||
|
|
|
@ -1,29 +1,31 @@
|
|||
package funkin.ui.charSelect;
|
||||
|
||||
import funkin.ui.freeplay.FreeplayState;
|
||||
import flixel.text.FlxText;
|
||||
import funkin.ui.PixelatedIcon;
|
||||
import flixel.system.debug.watch.Tracker.TrackerProfile;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.tweens.FlxTween;
|
||||
import openfl.display.BlendMode;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.FlxObject;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.group.FlxGroup;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
import funkin.play.stage.Stage;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.sound.FlxSound;
|
||||
import flixel.system.debug.watch.Tracker.TrackerProfile;
|
||||
import flixel.text.FlxText;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.audio.FunkinSound;
|
||||
import funkin.data.freeplay.player.PlayerData;
|
||||
import funkin.data.freeplay.player.PlayerRegistry;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.graphics.FunkinCamera;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.modding.events.ScriptEventDispatcher;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import flixel.FlxObject;
|
||||
import openfl.display.BlendMode;
|
||||
import flixel.group.FlxGroup;
|
||||
import funkin.play.stage.Stage;
|
||||
import funkin.ui.freeplay.charselect.PlayableCharacter;
|
||||
import funkin.ui.freeplay.FreeplayState;
|
||||
import funkin.ui.PixelatedIcon;
|
||||
import funkin.util.MathUtil;
|
||||
import flixel.util.FlxTimer;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.sound.FlxSound;
|
||||
import funkin.audio.FunkinSound;
|
||||
import funkin.graphics.FunkinCamera;
|
||||
import funkin.vis.dsp.SpectralAnalyzer;
|
||||
import openfl.display.BlendMode;
|
||||
|
||||
class CharSelectSubState extends MusicBeatSubState
|
||||
{
|
||||
|
@ -73,8 +75,29 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
{
|
||||
super();
|
||||
|
||||
availableChars.set(4, "bf");
|
||||
availableChars.set(3, "pico");
|
||||
loadAvailableCharacters();
|
||||
}
|
||||
|
||||
function loadAvailableCharacters():Void
|
||||
{
|
||||
var playerIds:Array<String> = PlayerRegistry.instance.listEntryIds();
|
||||
|
||||
for (playerId in playerIds)
|
||||
{
|
||||
var player:Null<PlayableCharacter> = PlayerRegistry.instance.fetchEntry(playerId);
|
||||
if (player == null) continue;
|
||||
var playerData = player.getCharSelectData();
|
||||
if (playerData == null) continue;
|
||||
|
||||
var targetPosition:Int = playerData.position ?? 0;
|
||||
while (availableChars.exists(targetPosition))
|
||||
{
|
||||
targetPosition += 1;
|
||||
}
|
||||
|
||||
trace('Placing player ${playerId} at position ${targetPosition}');
|
||||
availableChars.set(targetPosition, playerId);
|
||||
}
|
||||
}
|
||||
|
||||
override public function create():Void
|
||||
|
@ -259,7 +282,7 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
cursorBlue.scrollFactor.set();
|
||||
cursorDarkBlue.scrollFactor.set();
|
||||
|
||||
FlxTween.color(cursor, 0.2, 0xFFFFFF00, 0xFFFFCC00, {type: FlxTween.PINGPONG});
|
||||
FlxTween.color(cursor, 0.2, 0xFFFFFF00, 0xFFFFCC00, {type: PINGPONG});
|
||||
|
||||
// FlxG.debugger.track(cursor);
|
||||
|
||||
|
@ -283,7 +306,6 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
}
|
||||
|
||||
var grpIcons:FlxSpriteGroup;
|
||||
|
||||
var grpXSpread(default, set):Float = 107;
|
||||
var grpYSpread(default, set):Float = 127;
|
||||
|
||||
|
|
|
@ -34,7 +34,13 @@ class CreditsState extends MusicBeatState
|
|||
* To use a font from the `assets` folder, use `Paths.font(...)`.
|
||||
* Choose something that will render Unicode properly.
|
||||
*/
|
||||
#if windows
|
||||
static final CREDITS_FONT = 'Consolas';
|
||||
#elseif mac
|
||||
static final CREDITS_FONT = 'Menlo';
|
||||
#else
|
||||
static final CREDITS_FONT = "Courier New";
|
||||
#end
|
||||
|
||||
/**
|
||||
* The size of the font.
|
||||
|
|
|
@ -54,7 +54,7 @@ class DebugMenuSubState extends MusicBeatSubState
|
|||
|
||||
// Create each menu item.
|
||||
// Call onMenuChange when the first item is created to move the camera .
|
||||
#if CHART_EDITOR_SUPPORTED
|
||||
#if FEATURE_CHART_EDITOR
|
||||
onMenuChange(createItem("CHART EDITOR", openChartEditor));
|
||||
#end
|
||||
// createItem("Input Offset Testing", openInputOffsetTesting);
|
||||
|
|
|
@ -1,33 +1,26 @@
|
|||
package funkin.ui.debug.anim;
|
||||
|
||||
import flixel.addons.display.FlxBackdrop;
|
||||
import flixel.addons.display.FlxGridOverlay;
|
||||
import flixel.addons.ui.FlxInputText;
|
||||
import flixel.addons.ui.FlxUIDropDownMenu;
|
||||
import flixel.FlxCamera;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.FlxState;
|
||||
import flixel.graphics.frames.FlxAtlasFrames;
|
||||
import flixel.graphics.frames.FlxFrame;
|
||||
import flixel.group.FlxGroup;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.text.FlxText;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxSpriteUtil;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.audio.FunkinSound;
|
||||
import funkin.input.Cursor;
|
||||
import funkin.play.character.BaseCharacter;
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.play.character.SparrowCharacter;
|
||||
import funkin.ui.mainmenu.MainMenuState;
|
||||
import funkin.util.MouseUtil;
|
||||
import funkin.util.SerializerUtil;
|
||||
import funkin.util.SortUtil;
|
||||
import haxe.ui.components.DropDown;
|
||||
import haxe.ui.core.Component;
|
||||
import haxe.ui.containers.dialogs.CollapsibleDialog;
|
||||
import haxe.ui.core.Screen;
|
||||
import haxe.ui.events.ItemEvent;
|
||||
import haxe.ui.events.UIEvent;
|
||||
import haxe.ui.RuntimeComponentBuilder;
|
||||
import lime.utils.Assets as LimeAssets;
|
||||
|
@ -36,9 +29,6 @@ import openfl.events.Event;
|
|||
import openfl.events.IOErrorEvent;
|
||||
import openfl.geom.Rectangle;
|
||||
import openfl.net.FileReference;
|
||||
import openfl.net.URLLoader;
|
||||
import openfl.net.URLRequest;
|
||||
import openfl.utils.ByteArray;
|
||||
|
||||
using flixel.util.FlxSpriteUtil;
|
||||
|
||||
|
@ -55,10 +45,10 @@ class DebugBoundingState extends FlxState
|
|||
TODAY'S TO-DO
|
||||
- Cleaner UI
|
||||
*/
|
||||
var bg:FlxSprite;
|
||||
var bg:FlxBackdrop;
|
||||
var fileInfo:FlxText;
|
||||
|
||||
var txtGrp:FlxGroup;
|
||||
var txtGrp:FlxTypedGroup<FlxText>;
|
||||
|
||||
var hudCam:FlxCamera;
|
||||
|
||||
|
@ -66,16 +56,23 @@ class DebugBoundingState extends FlxState
|
|||
|
||||
var spriteSheetView:FlxGroup;
|
||||
var offsetView:FlxGroup;
|
||||
var animDropDownMenu:FlxUIDropDownMenu;
|
||||
var dropDownSetup:Bool = false;
|
||||
|
||||
var onionSkinChar:FlxSprite;
|
||||
var txtOffsetShit:FlxText;
|
||||
|
||||
var uiStuff:Component;
|
||||
var offsetEditorDialog:CollapsibleDialog;
|
||||
var offsetAnimationDropdown:DropDown;
|
||||
|
||||
var haxeUIFocused(get, default):Bool = false;
|
||||
|
||||
var currentAnimationName(get, never):String;
|
||||
|
||||
function get_currentAnimationName():String
|
||||
{
|
||||
return offsetAnimationDropdown?.value?.id ?? "idle";
|
||||
}
|
||||
|
||||
function get_haxeUIFocused():Bool
|
||||
{
|
||||
// get the screen position, according to the HUD camera, temp default to FlxG.camera juuust in case?
|
||||
|
@ -87,46 +84,35 @@ class DebugBoundingState extends FlxState
|
|||
{
|
||||
Paths.setCurrentLevel('week1');
|
||||
|
||||
// lv.
|
||||
// lv.onChange = function(e:UIEvent)
|
||||
// {
|
||||
// trace(e.type);
|
||||
// // trace(e.data.curView);
|
||||
// // var item:haxe.ui.core.ItemRenderer = cast e.target;
|
||||
// trace(e.target);
|
||||
// // if (e.type == "change")
|
||||
// // {
|
||||
// // curView = cast e.data;
|
||||
// // }
|
||||
// };
|
||||
|
||||
hudCam = new FlxCamera();
|
||||
hudCam.bgColor.alpha = 0;
|
||||
|
||||
bg = FlxGridOverlay.create(10, 10);
|
||||
// bg = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.GREEN);
|
||||
|
||||
bg.scrollFactor.set();
|
||||
bg = new FlxBackdrop(FlxGridOverlay.createGrid(10, 10, FlxG.width, FlxG.height, true, 0xffe7e6e6, 0xffd9d5d5));
|
||||
add(bg);
|
||||
|
||||
// we are setting this as the default draw camera only temporarily, to trick haxeui
|
||||
FlxG.cameras.add(hudCam);
|
||||
|
||||
var str = Paths.xml('ui/animation-editor/offset-editor-view');
|
||||
uiStuff = RuntimeComponentBuilder.fromAsset(str);
|
||||
offsetEditorDialog = cast RuntimeComponentBuilder.fromAsset(str);
|
||||
|
||||
// uiStuff.findComponent("btnViewSpriteSheet").onClick = _ -> curView = SPRITESHEET;
|
||||
var dropdown:DropDown = cast uiStuff.findComponent("swapper");
|
||||
dropdown.onChange = function(e:UIEvent) {
|
||||
// offsetEditorDialog.findComponent("btnViewSpriteSheet").onClick = _ -> curView = SPRITESHEET;
|
||||
var viewDropdown:DropDown = offsetEditorDialog.findComponent("swapper", DropDown);
|
||||
viewDropdown.onChange = function(e:UIEvent) {
|
||||
trace(e.type);
|
||||
curView = cast e.data.curView;
|
||||
trace(e.data);
|
||||
// trace(e.data);
|
||||
};
|
||||
|
||||
uiStuff.cameras = [hudCam];
|
||||
offsetAnimationDropdown = offsetEditorDialog.findComponent("animationDropdown", DropDown);
|
||||
|
||||
add(uiStuff);
|
||||
offsetEditorDialog.cameras = [hudCam];
|
||||
|
||||
add(offsetEditorDialog);
|
||||
|
||||
// Anchor to the right side by default
|
||||
// offsetEditorDialog.x = FlxG.width - offsetEditorDialog.width;
|
||||
|
||||
// sets the default camera back to FlxG.camera, since we set it to hudCamera for haxeui stuf
|
||||
FlxG.cameras.setDefaultDrawTarget(FlxG.camera, true);
|
||||
|
@ -159,7 +145,7 @@ class DebugBoundingState extends FlxState
|
|||
|
||||
generateOutlines(tex.frames);
|
||||
|
||||
txtGrp = new FlxGroup();
|
||||
txtGrp = new FlxTypedGroup<FlxText>();
|
||||
txtGrp.cameras = [hudCam];
|
||||
spriteSheetView.add(txtGrp);
|
||||
|
||||
|
@ -168,64 +154,6 @@ class DebugBoundingState extends FlxState
|
|||
addInfo('Height', bf.height);
|
||||
|
||||
spriteSheetView.add(swagOutlines);
|
||||
|
||||
FlxG.stage.window.onDropFile.add(function(path:String) {
|
||||
// WACKY ASS TESTING SHIT FOR WEB FILE LOADING??
|
||||
#if web
|
||||
var swagList:FileList = cast path;
|
||||
|
||||
var objShit = js.html.URL.createObjectURL(swagList.item(0));
|
||||
trace(objShit);
|
||||
|
||||
var funnysound = new FunkinSound().loadStream('https://cdn.discordapp.com/attachments/767500676166451231/817821618251759666/Flutter.mp3', false, false,
|
||||
null, function() {
|
||||
trace('LOADED SHIT??');
|
||||
});
|
||||
|
||||
funnysound.volume = 1;
|
||||
funnysound.play();
|
||||
|
||||
var urlShit = new URLLoader(new URLRequest(objShit));
|
||||
|
||||
new FlxTimer().start(3, function(tmr:FlxTimer) {
|
||||
// music lol!
|
||||
if (urlShit.dataFormat == BINARY)
|
||||
{
|
||||
// var daSwagBytes:ByteArray = urlShit.data;
|
||||
|
||||
// FlxG.sound.playMusic();
|
||||
|
||||
// trace('is binary!!');
|
||||
}
|
||||
trace(urlShit.dataFormat);
|
||||
});
|
||||
|
||||
// remove(bf);
|
||||
// FlxG.bitmap.removeByKey(Paths.image('characters/temp'));
|
||||
// Assets.cache.clear();
|
||||
|
||||
// bf.loadGraphic(objShit);
|
||||
// add(bf);
|
||||
|
||||
// trace(swagList.item(0).name);
|
||||
// var urlShit = js.html.URL.createObjectURL(path);
|
||||
#end
|
||||
|
||||
#if sys
|
||||
trace("DROPPED FILE FROM: " + Std.string(path));
|
||||
var newPath = "./" + Paths.image('characters/temp');
|
||||
File.copy(path, newPath);
|
||||
|
||||
var swag = Paths.image('characters/temp');
|
||||
|
||||
if (bf != null) remove(bf);
|
||||
FlxG.bitmap.removeByKey(Paths.image('characters/temp'));
|
||||
Assets.cache.clear();
|
||||
|
||||
bf.loadGraphic(Paths.image('characters/temp'));
|
||||
add(bf);
|
||||
#end
|
||||
});
|
||||
}
|
||||
|
||||
function generateOutlines(frameShit:Array<FlxFrame>):Void
|
||||
|
@ -260,15 +188,9 @@ class DebugBoundingState extends FlxState
|
|||
txtOffsetShit = new FlxText(20, 20, 0, "", 20);
|
||||
txtOffsetShit.setFormat(Paths.font("vcr.ttf"), 26, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
|
||||
txtOffsetShit.cameras = [hudCam];
|
||||
txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height;
|
||||
offsetView.add(txtOffsetShit);
|
||||
|
||||
animDropDownMenu = new FlxUIDropDownMenu(0, 0, FlxUIDropDownMenu.makeStrIdLabelArray(['weed'], true));
|
||||
animDropDownMenu.cameras = [hudCam];
|
||||
// Move to bottom right corner
|
||||
animDropDownMenu.x = FlxG.width - animDropDownMenu.width - 20;
|
||||
animDropDownMenu.y = FlxG.height - animDropDownMenu.height - 20;
|
||||
offsetView.add(animDropDownMenu);
|
||||
|
||||
var characters:Array<String> = CharacterDataParser.listCharacterIds();
|
||||
characters = characters.filter(function(charId:String) {
|
||||
var char = CharacterDataParser.fetchCharacterData(charId);
|
||||
|
@ -276,7 +198,7 @@ class DebugBoundingState extends FlxState
|
|||
});
|
||||
characters.sort(SortUtil.alphabetically);
|
||||
|
||||
var charDropdown:DropDown = cast uiStuff.findComponent('characterDropdown');
|
||||
var charDropdown:DropDown = offsetEditorDialog.findComponent('characterDropdown', DropDown);
|
||||
for (char in characters)
|
||||
{
|
||||
charDropdown.dataSource.add({text: char});
|
||||
|
@ -289,32 +211,47 @@ class DebugBoundingState extends FlxState
|
|||
|
||||
public var mouseOffset:FlxPoint = FlxPoint.get(0, 0);
|
||||
public var oldPos:FlxPoint = FlxPoint.get(0, 0);
|
||||
public var movingCharacter:Bool = false;
|
||||
|
||||
function mouseOffsetMovement()
|
||||
{
|
||||
if (swagChar != null)
|
||||
{
|
||||
if (FlxG.mouse.justPressed)
|
||||
if (FlxG.mouse.justPressed && !haxeUIFocused)
|
||||
{
|
||||
movingCharacter = true;
|
||||
mouseOffset.set(FlxG.mouse.x - -swagChar.animOffsets[0], FlxG.mouse.y - -swagChar.animOffsets[1]);
|
||||
}
|
||||
|
||||
if (!movingCharacter) return;
|
||||
|
||||
if (FlxG.mouse.pressed)
|
||||
{
|
||||
swagChar.animOffsets = [(FlxG.mouse.x - mouseOffset.x) * -1, (FlxG.mouse.y - mouseOffset.y) * -1];
|
||||
|
||||
swagChar.animationOffsets.set(animDropDownMenu.selectedLabel, swagChar.animOffsets);
|
||||
swagChar.animationOffsets.set(offsetAnimationDropdown.value.id, swagChar.animOffsets);
|
||||
|
||||
txtOffsetShit.text = 'Offset: ' + swagChar.animOffsets;
|
||||
txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height;
|
||||
}
|
||||
|
||||
if (FlxG.mouse.justReleased)
|
||||
{
|
||||
movingCharacter = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addInfo(str:String, value:Dynamic)
|
||||
{
|
||||
var swagText:FlxText = new FlxText(10, 10 + (28 * txtGrp.length));
|
||||
var swagText:FlxText = new FlxText(10, FlxG.height - 32);
|
||||
swagText.setFormat(Paths.font("vcr.ttf"), 26, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
|
||||
swagText.scrollFactor.set();
|
||||
|
||||
for (text in txtGrp.members)
|
||||
{
|
||||
text.y -= swagText.height;
|
||||
}
|
||||
txtGrp.add(swagText);
|
||||
|
||||
swagText.text = str + ": " + Std.string(value);
|
||||
|
@ -345,14 +282,14 @@ class DebugBoundingState extends FlxState
|
|||
{
|
||||
if (FlxG.keys.justPressed.ONE)
|
||||
{
|
||||
var lv:DropDown = cast uiStuff.findComponent("swapper");
|
||||
var lv:DropDown = offsetEditorDialog.findComponent("swapper", DropDown);
|
||||
lv.selectedIndex = 0;
|
||||
curView = SPRITESHEET;
|
||||
}
|
||||
|
||||
if (FlxG.keys.justReleased.TWO)
|
||||
{
|
||||
var lv:DropDown = cast uiStuff.findComponent("swapper");
|
||||
var lv:DropDown = offsetEditorDialog.findComponent("swapper", DropDown);
|
||||
lv.selectedIndex = 1;
|
||||
curView = ANIMATIONS;
|
||||
if (swagChar != null)
|
||||
|
@ -368,12 +305,14 @@ class DebugBoundingState extends FlxState
|
|||
spriteSheetView.visible = true;
|
||||
offsetView.visible = false;
|
||||
offsetView.active = false;
|
||||
offsetAnimationDropdown.visible = false;
|
||||
case ANIMATIONS:
|
||||
spriteSheetView.visible = false;
|
||||
offsetView.visible = true;
|
||||
offsetView.active = true;
|
||||
offsetAnimationDropdown.visible = true;
|
||||
offsetControls();
|
||||
if (!haxeUIFocused) mouseOffsetMovement();
|
||||
mouseOffsetMovement();
|
||||
}
|
||||
|
||||
if (FlxG.keys.justPressed.H) hudCam.visible = !hudCam.visible;
|
||||
|
@ -395,24 +334,36 @@ class DebugBoundingState extends FlxState
|
|||
{
|
||||
if (FlxG.keys.justPressed.RBRACKET || FlxG.keys.justPressed.E)
|
||||
{
|
||||
if (Std.parseInt(animDropDownMenu.selectedId) + 1 <= animDropDownMenu.length)
|
||||
animDropDownMenu.selectedId = Std.string(Std.parseInt(animDropDownMenu.selectedId)
|
||||
+ 1);
|
||||
if (offsetAnimationDropdown.selectedIndex + 1 <= offsetAnimationDropdown.dataSource.size)
|
||||
{
|
||||
offsetAnimationDropdown.selectedIndex += 1;
|
||||
}
|
||||
else
|
||||
animDropDownMenu.selectedId = Std.string(0);
|
||||
playCharacterAnimation(animDropDownMenu.selectedId, true);
|
||||
{
|
||||
offsetAnimationDropdown.selectedIndex = 0;
|
||||
}
|
||||
trace(offsetAnimationDropdown.selectedIndex);
|
||||
trace(offsetAnimationDropdown.dataSource.size);
|
||||
trace(offsetAnimationDropdown.value);
|
||||
trace(currentAnimationName);
|
||||
playCharacterAnimation(currentAnimationName, true);
|
||||
}
|
||||
if (FlxG.keys.justPressed.LBRACKET || FlxG.keys.justPressed.Q)
|
||||
{
|
||||
if (Std.parseInt(animDropDownMenu.selectedId) - 1 >= 0) animDropDownMenu.selectedId = Std.string(Std.parseInt(animDropDownMenu.selectedId) - 1);
|
||||
if (offsetAnimationDropdown.selectedIndex - 1 >= 0)
|
||||
{
|
||||
offsetAnimationDropdown.selectedIndex -= 1;
|
||||
}
|
||||
else
|
||||
animDropDownMenu.selectedId = Std.string(animDropDownMenu.length - 1);
|
||||
playCharacterAnimation(animDropDownMenu.selectedId, true);
|
||||
{
|
||||
offsetAnimationDropdown.selectedIndex = offsetAnimationDropdown.dataSource.size - 1;
|
||||
}
|
||||
playCharacterAnimation(currentAnimationName, true);
|
||||
}
|
||||
|
||||
// Keyboards controls for general WASD "movement"
|
||||
// modifies the animDropDownMenu so that it's properly updated and shit
|
||||
// and then it's just played and updated from the animDropDownMenu callback, which is set in the loadAnimShit() function probabbly
|
||||
// modifies the animDrooffsetAnimationDropdownpDownMenu so that it's properly updated and shit
|
||||
// and then it's just played and updated from the offsetAnimationDropdown callback, which is set in the loadAnimShit() function probabbly
|
||||
if (FlxG.keys.justPressed.W || FlxG.keys.justPressed.S || FlxG.keys.justPressed.D || FlxG.keys.justPressed.A)
|
||||
{
|
||||
var suffix:String = '';
|
||||
|
@ -425,18 +376,19 @@ class DebugBoundingState extends FlxState
|
|||
if (FlxG.keys.justPressed.A) targetLabel = 'singLEFT$suffix';
|
||||
if (FlxG.keys.justPressed.D) targetLabel = 'singRIGHT$suffix';
|
||||
|
||||
if (targetLabel != animDropDownMenu.selectedLabel)
|
||||
if (targetLabel != currentAnimationName)
|
||||
{
|
||||
offsetAnimationDropdown.value = {id: targetLabel, text: targetLabel};
|
||||
|
||||
// Play the new animation if the IDs are the different.
|
||||
// Override the onion skin.
|
||||
animDropDownMenu.selectedLabel = targetLabel;
|
||||
playCharacterAnimation(animDropDownMenu.selectedId, true);
|
||||
playCharacterAnimation(currentAnimationName, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Replay the current animation if the IDs are the same.
|
||||
// Don't override the onion skin.
|
||||
playCharacterAnimation(animDropDownMenu.selectedId, false);
|
||||
playCharacterAnimation(currentAnimationName, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -448,16 +400,20 @@ class DebugBoundingState extends FlxState
|
|||
// Plays the idle animation
|
||||
if (FlxG.keys.justPressed.SPACE)
|
||||
{
|
||||
animDropDownMenu.selectedLabel = 'idle';
|
||||
playCharacterAnimation(animDropDownMenu.selectedId, true);
|
||||
offsetAnimationDropdown.value = {id: 'idle', text: 'idle'};
|
||||
|
||||
playCharacterAnimation(currentAnimationName, true);
|
||||
}
|
||||
|
||||
// Playback the animation
|
||||
if (FlxG.keys.justPressed.ENTER) playCharacterAnimation(animDropDownMenu.selectedId, false);
|
||||
if (FlxG.keys.justPressed.ENTER)
|
||||
{
|
||||
playCharacterAnimation(currentAnimationName, false);
|
||||
}
|
||||
|
||||
if (FlxG.keys.justPressed.RIGHT || FlxG.keys.justPressed.LEFT || FlxG.keys.justPressed.UP || FlxG.keys.justPressed.DOWN)
|
||||
{
|
||||
var animName = animDropDownMenu.selectedLabel;
|
||||
var animName = currentAnimationName;
|
||||
var coolValues:Array<Float> = swagChar.animationOffsets.get(animName).copy();
|
||||
|
||||
var multiplier:Int = 5;
|
||||
|
@ -471,10 +427,11 @@ class DebugBoundingState extends FlxState
|
|||
else if (FlxG.keys.justPressed.UP) coolValues[1] += 1 * multiplier;
|
||||
else if (FlxG.keys.justPressed.DOWN) coolValues[1] -= 1 * multiplier;
|
||||
|
||||
swagChar.animationOffsets.set(animDropDownMenu.selectedLabel, coolValues);
|
||||
swagChar.animationOffsets.set(currentAnimationName, coolValues);
|
||||
swagChar.playAnimation(animName);
|
||||
|
||||
txtOffsetShit.text = 'Offset: ' + coolValues;
|
||||
txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height;
|
||||
|
||||
trace(animName);
|
||||
}
|
||||
|
@ -529,7 +486,7 @@ class DebugBoundingState extends FlxState
|
|||
swagChar = CharacterDataParser.fetchCharacter(char);
|
||||
swagChar.x = 100;
|
||||
swagChar.y = 100;
|
||||
// swagChar.debugMode = true;
|
||||
swagChar.debug = true;
|
||||
offsetView.add(swagChar);
|
||||
|
||||
if (swagChar == null || swagChar.frames == null)
|
||||
|
@ -554,11 +511,25 @@ class DebugBoundingState extends FlxState
|
|||
trace(swagChar.animationOffsets[i]);
|
||||
}
|
||||
|
||||
animDropDownMenu.setData(FlxUIDropDownMenu.makeStrIdLabelArray(characterAnimNames, true));
|
||||
animDropDownMenu.callback = function(str:String) {
|
||||
playCharacterAnimation(str, true);
|
||||
};
|
||||
offsetAnimationDropdown.dataSource.clear();
|
||||
|
||||
for (charAnim in characterAnimNames)
|
||||
{
|
||||
trace('Adding ${charAnim} to HaxeUI dropdown');
|
||||
offsetAnimationDropdown.dataSource.add({id: charAnim, text: charAnim});
|
||||
}
|
||||
|
||||
offsetAnimationDropdown.selectedIndex = 0;
|
||||
|
||||
trace('Added ${offsetAnimationDropdown.dataSource.size} to HaxeUI dropdown');
|
||||
|
||||
offsetAnimationDropdown.onChange = function(event:UIEvent) {
|
||||
trace('Selected animation ${event?.data?.id}');
|
||||
playCharacterAnimation(event.data.id, true);
|
||||
}
|
||||
|
||||
txtOffsetShit.text = 'Offset: ' + swagChar.animOffsets;
|
||||
txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height;
|
||||
dropDownSetup = true;
|
||||
}
|
||||
|
||||
|
@ -575,11 +546,13 @@ class DebugBoundingState extends FlxState
|
|||
onionSkinChar.alpha = 0.6;
|
||||
}
|
||||
|
||||
var animName = characterAnimNames[Std.parseInt(str)];
|
||||
// var animName = characterAnimNames[Std.parseInt(str)];
|
||||
var animName = str;
|
||||
swagChar.playAnimation(animName, true); // trace();
|
||||
trace(swagChar.animationOffsets.get(animName));
|
||||
|
||||
txtOffsetShit.text = 'Offset: ' + swagChar.animOffsets;
|
||||
txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height;
|
||||
}
|
||||
|
||||
var _file:FileReference;
|
||||
|
|
|
@ -3327,7 +3327,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
handleTestKeybinds();
|
||||
handleHelpKeybinds();
|
||||
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
handleQuickWatch();
|
||||
#end
|
||||
|
||||
|
@ -5699,7 +5699,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
// TODO: Rework asset system so we can remove this jank.
|
||||
switch (currentSongStage)
|
||||
{
|
||||
case 'mainStage':
|
||||
case 'mainStage' | 'mainStageErect':
|
||||
PlayStatePlaylist.campaignId = 'week1';
|
||||
case 'spookyMansion' | 'spookyMansionErect':
|
||||
PlayStatePlaylist.campaignId = 'week2';
|
||||
|
|
|
@ -190,8 +190,8 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox
|
|||
var numberStepper:NumberStepper = new NumberStepper();
|
||||
numberStepper.id = field.name;
|
||||
numberStepper.step = field.step ?? 1.0;
|
||||
numberStepper.min = field.min ?? 0.0;
|
||||
numberStepper.max = field.max ?? 10.0;
|
||||
if (field.min != null) numberStepper.min = field.min;
|
||||
if (field.min != null) numberStepper.max = field.max;
|
||||
if (field.defaultValue != null) numberStepper.value = field.defaultValue;
|
||||
input = numberStepper;
|
||||
case FLOAT:
|
||||
|
|
|
@ -15,7 +15,7 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
{
|
||||
// Represents the sprite's current status.
|
||||
// Without state machines I would have driven myself crazy years ago.
|
||||
public var currentState:DJBoyfriendState = Intro;
|
||||
public var currentState:FreeplayDJState = Intro;
|
||||
|
||||
// A callback activated when the intro animation finishes.
|
||||
public var onIntroDone:FlxSignal = new FlxSignal();
|
||||
|
@ -99,7 +99,7 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
playFlashAnimation(animPrefix, true, false, true);
|
||||
}
|
||||
|
||||
if (getCurrentAnimation() == animPrefix && this.isLoopFinished())
|
||||
if (getCurrentAnimation() == animPrefix && this.isLoopComplete())
|
||||
{
|
||||
if (timeIdling >= IDLE_EGG_PERIOD && !seenIdleEasterEgg)
|
||||
{
|
||||
|
@ -111,18 +111,69 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
}
|
||||
}
|
||||
timeIdling += elapsed;
|
||||
case NewUnlock:
|
||||
var animPrefix = playableCharData.getAnimationPrefix('newUnlock');
|
||||
if (!hasAnimation(animPrefix))
|
||||
{
|
||||
currentState = Idle;
|
||||
}
|
||||
if (getCurrentAnimation() != animPrefix)
|
||||
{
|
||||
playFlashAnimation(animPrefix, true, false, true);
|
||||
}
|
||||
case Confirm:
|
||||
var animPrefix = playableCharData.getAnimationPrefix('confirm');
|
||||
if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, false);
|
||||
timeIdling = 0;
|
||||
case FistPumpIntro:
|
||||
var animPrefix = playableCharData.getAnimationPrefix('fistPump');
|
||||
if (getCurrentAnimation() != animPrefix) playFlashAnimation('Boyfriend DJ fist pump', false);
|
||||
if (getCurrentAnimation() == animPrefix && anim.curFrame >= 4)
|
||||
var animPrefixA = playableCharData.getAnimationPrefix('fistPump');
|
||||
var animPrefixB = playableCharData.getAnimationPrefix('loss');
|
||||
|
||||
if (getCurrentAnimation() == animPrefixA)
|
||||
{
|
||||
playAnimation("Boyfriend DJ fist pump", true, false, false, 0);
|
||||
var endFrame = playableCharData.getFistPumpIntroEndFrame();
|
||||
if (endFrame > -1 && anim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixA, true, false, false, playableCharData.getFistPumpIntroStartFrame());
|
||||
}
|
||||
}
|
||||
else if (getCurrentAnimation() == animPrefixB)
|
||||
{
|
||||
var endFrame = playableCharData.getFistPumpIntroBadEndFrame();
|
||||
if (endFrame > -1 && anim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixB, true, false, false, playableCharData.getFistPumpIntroBadStartFrame());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxG.log.warn("Unrecognized animation in FistPumpIntro: " + getCurrentAnimation());
|
||||
}
|
||||
|
||||
case FistPump:
|
||||
var animPrefixA = playableCharData.getAnimationPrefix('fistPump');
|
||||
var animPrefixB = playableCharData.getAnimationPrefix('loss');
|
||||
|
||||
if (getCurrentAnimation() == animPrefixA)
|
||||
{
|
||||
var endFrame = playableCharData.getFistPumpLoopEndFrame();
|
||||
if (endFrame > -1 && anim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixA, true, false, false, playableCharData.getFistPumpLoopStartFrame());
|
||||
}
|
||||
}
|
||||
else if (getCurrentAnimation() == animPrefixB)
|
||||
{
|
||||
var endFrame = playableCharData.getFistPumpLoopBadEndFrame();
|
||||
if (endFrame > -1 && anim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixB, true, false, false, playableCharData.getFistPumpLoopBadStartFrame());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxG.log.warn("Unrecognized animation in FistPump: " + getCurrentAnimation());
|
||||
}
|
||||
|
||||
case IdleEasterEgg:
|
||||
var animPrefix = playableCharData.getAnimationPrefix('idleEasterEgg');
|
||||
|
@ -148,7 +199,7 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
// I shit myself.
|
||||
}
|
||||
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
if (FlxG.keys.pressed.CONTROL)
|
||||
{
|
||||
if (FlxG.keys.justPressed.LEFT)
|
||||
|
@ -184,8 +235,15 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
// var name = anim.curSymbol.name;
|
||||
|
||||
if (name == playableCharData.getAnimationPrefix('intro'))
|
||||
{
|
||||
if (PlayerRegistry.instance.hasNewCharacter())
|
||||
{
|
||||
currentState = NewUnlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentState = Idle;
|
||||
}
|
||||
onIntroDone.dispatch();
|
||||
}
|
||||
else if (name == playableCharData.getAnimationPrefix('idle'))
|
||||
|
@ -225,9 +283,17 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
// runTvLogic();
|
||||
}
|
||||
trace('Replay idle: ${frame}');
|
||||
playAnimation(playableCharData.getAnimationPrefix('cartoon'), true, false, false, frame);
|
||||
playFlashAnimation(playableCharData.getAnimationPrefix('cartoon'), true, false, false, frame);
|
||||
// trace('Finished confirm');
|
||||
}
|
||||
else if (name == playableCharData.getAnimationPrefix('newUnlock'))
|
||||
{
|
||||
// Animation should loop.
|
||||
}
|
||||
else if (name == playableCharData.getAnimationPrefix('charSelect'))
|
||||
{
|
||||
onCharSelectComplete();
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Finished ${name}');
|
||||
|
@ -240,6 +306,15 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
seenIdleEasterEgg = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamic function, it's actually a variable you can reassign!
|
||||
* `dj.onCharSelectComplete = function() {};`
|
||||
*/
|
||||
public dynamic function onCharSelectComplete():Void
|
||||
{
|
||||
trace('onCharSelectComplete()');
|
||||
}
|
||||
|
||||
var offsetX:Float = 0.0;
|
||||
var offsetY:Float = 0.0;
|
||||
|
||||
|
@ -271,7 +346,7 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
function loadCartoon()
|
||||
{
|
||||
cartoonSnd = FunkinSound.load(Paths.sound(getRandomFlashToon()), 1.0, false, true, true, function() {
|
||||
playAnimation("Boyfriend DJ watchin tv OG", true, false, false, 60);
|
||||
playFlashAnimation(playableCharData.getAnimationPrefix('cartoon'), true, false, false, 60);
|
||||
});
|
||||
|
||||
// Fade out music to 40% volume over 1 second.
|
||||
|
@ -301,21 +376,48 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
currentState = Confirm;
|
||||
}
|
||||
|
||||
public function fistPump():Void
|
||||
public function toCharSelect():Void
|
||||
{
|
||||
if (hasAnimation('charSelect'))
|
||||
{
|
||||
currentState = CharSelect;
|
||||
var animPrefix = playableCharData.getAnimationPrefix('charSelect');
|
||||
playFlashAnimation(animPrefix, true, false, false, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentState = Confirm;
|
||||
// Call this immediately; otherwise, we get locked out of Character Select.
|
||||
onCharSelectComplete();
|
||||
}
|
||||
}
|
||||
|
||||
public function fistPumpIntro():Void
|
||||
{
|
||||
currentState = FistPumpIntro;
|
||||
var animPrefix = playableCharData.getAnimationPrefix('fistPump');
|
||||
playFlashAnimation(animPrefix, true, false, false, playableCharData.getFistPumpIntroStartFrame());
|
||||
}
|
||||
|
||||
public function pumpFist():Void
|
||||
public function fistPump():Void
|
||||
{
|
||||
currentState = FistPump;
|
||||
playAnimation("Boyfriend DJ fist pump", true, false, false, 4);
|
||||
var animPrefix = playableCharData.getAnimationPrefix('fistPump');
|
||||
playFlashAnimation(animPrefix, true, false, false, playableCharData.getFistPumpLoopStartFrame());
|
||||
}
|
||||
|
||||
public function pumpFistBad():Void
|
||||
public function fistPumpLossIntro():Void
|
||||
{
|
||||
currentState = FistPumpIntro;
|
||||
var animPrefix = playableCharData.getAnimationPrefix('loss');
|
||||
playFlashAnimation(animPrefix, true, false, false, playableCharData.getFistPumpIntroBadStartFrame());
|
||||
}
|
||||
|
||||
public function fistPumpLoss():Void
|
||||
{
|
||||
currentState = FistPump;
|
||||
playAnimation("Boyfriend DJ loss reaction 1", true, false, false, 4);
|
||||
var animPrefix = playableCharData.getAnimationPrefix('loss');
|
||||
playFlashAnimation(animPrefix, true, false, false, playableCharData.getFistPumpLoopBadStartFrame());
|
||||
}
|
||||
|
||||
override public function getCurrentAnimation():String
|
||||
|
@ -366,13 +468,53 @@ class FreeplayDJ extends FlxAtlasSprite
|
|||
}
|
||||
}
|
||||
|
||||
enum DJBoyfriendState
|
||||
enum FreeplayDJState
|
||||
{
|
||||
/**
|
||||
* Character enters the frame and transitions to Idle.
|
||||
*/
|
||||
Intro;
|
||||
|
||||
/**
|
||||
* Character loops in idle.
|
||||
*/
|
||||
Idle;
|
||||
Confirm;
|
||||
FistPumpIntro;
|
||||
FistPump;
|
||||
|
||||
/**
|
||||
* Plays an easter egg animation after a period in Idle, then reverts to Idle.
|
||||
*/
|
||||
IdleEasterEgg;
|
||||
|
||||
/**
|
||||
* Plays an elaborate easter egg animation. Does not revert until another animation is triggered.
|
||||
*/
|
||||
Cartoon;
|
||||
|
||||
/**
|
||||
* Player has selected a song.
|
||||
*/
|
||||
Confirm;
|
||||
|
||||
/**
|
||||
* Character preps to play the fist pump animation; plays after the Results screen.
|
||||
* The actual frame label that gets played may vary based on the player's success.
|
||||
*/
|
||||
FistPumpIntro;
|
||||
|
||||
/**
|
||||
* Character plays the fist pump animation.
|
||||
* The actual frame label that gets played may vary based on the player's success.
|
||||
*/
|
||||
FistPump;
|
||||
|
||||
/**
|
||||
* Plays an animation to indicate that the player has a new unlock in Character Select.
|
||||
* Overrides all idle animations as well as the fist pump. Only Confirm and CharSelect will override this.
|
||||
*/
|
||||
NewUnlock;
|
||||
|
||||
/**
|
||||
* Plays an animation to transition to the Character Select screen.
|
||||
*/
|
||||
CharSelect;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package funkin.ui.freeplay;
|
||||
|
||||
import flixel.addons.transition.FlxTransitionableState;
|
||||
import flixel.addons.ui.FlxInputText;
|
||||
import flixel.FlxCamera;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.group.FlxGroup;
|
||||
|
@ -178,9 +177,22 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
var stickerSubState:Null<StickerSubState> = null;
|
||||
|
||||
public static var rememberedDifficulty:Null<String> = Constants.DEFAULT_DIFFICULTY;
|
||||
/**
|
||||
* The difficulty we were on when this menu was last accessed.
|
||||
*/
|
||||
public static var rememberedDifficulty:String = Constants.DEFAULT_DIFFICULTY;
|
||||
|
||||
/**
|
||||
* The song we were on when this menu was last accessed.
|
||||
* NOTE: `null` if the last song was `Random`.
|
||||
*/
|
||||
public static var rememberedSongId:Null<String> = 'tutorial';
|
||||
|
||||
/**
|
||||
* The character we were on when this menu was last accessed.
|
||||
*/
|
||||
public static var rememberedCharacterId:String = Constants.DEFAULT_CHARACTER;
|
||||
|
||||
var funnyCam:FunkinCamera;
|
||||
var rankCamera:FunkinCamera;
|
||||
var rankBg:FunkinSprite;
|
||||
|
@ -210,14 +222,16 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
public function new(?params:FreeplayStateParams, ?stickers:StickerSubState)
|
||||
{
|
||||
currentCharacterId = params?.character ?? Constants.DEFAULT_CHARACTER;
|
||||
currentCharacterId = params?.character ?? rememberedCharacterId;
|
||||
var fetchPlayableCharacter = function():PlayableCharacter {
|
||||
var result = PlayerRegistry.instance.fetchEntry(params?.character ?? Constants.DEFAULT_CHARACTER);
|
||||
var result = PlayerRegistry.instance.fetchEntry(params?.character ?? rememberedCharacterId);
|
||||
if (result == null) throw 'No valid playable character with id ${params?.character}';
|
||||
return result;
|
||||
};
|
||||
currentCharacter = fetchPlayableCharacter();
|
||||
|
||||
rememberedCharacterId = currentCharacter?.id ?? Constants.DEFAULT_CHARACTER;
|
||||
|
||||
fromResultsParams = params?.fromResults;
|
||||
|
||||
if (fromResultsParams?.playRankAnim == true)
|
||||
|
@ -292,14 +306,14 @@ class FreeplayState extends MusicBeatSubState
|
|||
stickerSubState.degenStickers();
|
||||
}
|
||||
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
// Updating Discord Rich Presence
|
||||
DiscordClient.changePresence('In the Menus', null);
|
||||
#end
|
||||
|
||||
var isDebug:Bool = false;
|
||||
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
isDebug = true;
|
||||
#end
|
||||
|
||||
|
@ -630,8 +644,8 @@ class FreeplayState extends MusicBeatSubState
|
|||
speed: 0.3
|
||||
});
|
||||
|
||||
var diffSelLeft:DifficultySelector = new DifficultySelector(20, grpDifficulties.y - 10, false, controls);
|
||||
var diffSelRight:DifficultySelector = new DifficultySelector(325, grpDifficulties.y - 10, true, controls);
|
||||
var diffSelLeft:DifficultySelector = new DifficultySelector(this, 20, grpDifficulties.y - 10, false, controls);
|
||||
var diffSelRight:DifficultySelector = new DifficultySelector(this, 325, grpDifficulties.y - 10, true, controls);
|
||||
diffSelLeft.visible = false;
|
||||
diffSelRight.visible = false;
|
||||
add(diffSelLeft);
|
||||
|
@ -747,10 +761,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
var tempSongs:Array<Null<FreeplaySongData>> = songs;
|
||||
|
||||
// Remember just the difficulty because it's important for song sorting.
|
||||
if (rememberedDifficulty != null)
|
||||
{
|
||||
currentDifficulty = rememberedDifficulty;
|
||||
}
|
||||
|
||||
if (filterStuff != null) tempSongs = sortSongs(tempSongs, filterStuff);
|
||||
|
||||
|
@ -905,7 +916,15 @@ class FreeplayState extends MusicBeatSubState
|
|||
changeSelection();
|
||||
changeDiff();
|
||||
|
||||
if (dj != null) dj.fistPump();
|
||||
if (fromResultsParams?.newRank == SHIT)
|
||||
{
|
||||
if (dj != null) dj.fistPumpLossIntro();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dj != null) dj.fistPumpIntro();
|
||||
}
|
||||
|
||||
// rankCamera.fade(FlxColor.BLACK, 0.5, true);
|
||||
rankCamera.fade(0xFF000000, 0.5, true, null, true);
|
||||
if (FlxG.sound.music != null) FlxG.sound.music.volume = 0;
|
||||
|
@ -1087,11 +1106,11 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
if (fromResultsParams?.newRank == SHIT)
|
||||
{
|
||||
if (dj != null) dj.pumpFistBad();
|
||||
if (dj != null) dj.fistPumpLoss();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dj != null) dj.pumpFist();
|
||||
if (dj != null) dj.fistPump();
|
||||
}
|
||||
|
||||
rankCamera.zoom = 0.8;
|
||||
|
@ -1194,7 +1213,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
/**
|
||||
* If true, disable interaction with the interface.
|
||||
*/
|
||||
var busy:Bool = false;
|
||||
public var busy:Bool = false;
|
||||
|
||||
var originalPos:FlxPoint = new FlxPoint();
|
||||
|
||||
|
@ -1202,7 +1221,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
{
|
||||
super.update(elapsed);
|
||||
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
if (FlxG.keys.justPressed.T)
|
||||
{
|
||||
rankAnimStart(fromResultsParams ??
|
||||
|
@ -1219,7 +1238,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
FlxG.switchState(FreeplayState.build(
|
||||
{
|
||||
{
|
||||
character: currentCharacterId == "pico" ? "bf" : "pico",
|
||||
character: currentCharacterId == "pico" ? Constants.DEFAULT_CHARACTER : "pico",
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
@ -1237,8 +1256,33 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
if (controls.FREEPLAY_CHAR_SELECT && !busy)
|
||||
{
|
||||
// Check if we have ACCESS to character select!
|
||||
trace('Is Pico unlocked? ${PlayerRegistry.instance.fetchEntry('pico')?.isUnlocked()}');
|
||||
trace('Number of characters: ${PlayerRegistry.instance.countUnlockedCharacters()}');
|
||||
|
||||
if (PlayerRegistry.instance.countUnlockedCharacters() > 1)
|
||||
{
|
||||
if (dj != null)
|
||||
{
|
||||
busy = true;
|
||||
// Transition to character select after animation
|
||||
dj.onCharSelectComplete = function() {
|
||||
FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState());
|
||||
}
|
||||
dj.toCharSelect();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Transition to character select immediately
|
||||
FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Not enough characters unlocked to open character select!');
|
||||
FunkinSound.playOnce(Paths.sound('cancelMenu'));
|
||||
}
|
||||
}
|
||||
|
||||
if (controls.FREEPLAY_FAVORITE && !busy)
|
||||
{
|
||||
|
@ -1330,6 +1374,8 @@ class FreeplayState extends MusicBeatSubState
|
|||
}
|
||||
|
||||
handleInputs(elapsed);
|
||||
|
||||
if (dj != null) FlxG.watch.addQuick('dj-anim', dj.getCurrentAnimation());
|
||||
}
|
||||
|
||||
function handleInputs(elapsed:Float):Void
|
||||
|
@ -1487,7 +1533,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
generateSongList(currentFilter, true);
|
||||
}
|
||||
|
||||
if (controls.BACK)
|
||||
if (controls.BACK && !busy)
|
||||
{
|
||||
busy = true;
|
||||
FlxTween.globalManager.clear();
|
||||
|
@ -1776,7 +1822,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
var targetInstId:String = baseInstrumentalId;
|
||||
|
||||
// TODO: Make this a UI element.
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
if (altInstrumentalIds.length > 0 && FlxG.keys.pressed.CONTROL)
|
||||
{
|
||||
targetInstId = altInstrumentalIds[0];
|
||||
|
@ -1837,7 +1883,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
practiceMode: false,
|
||||
minimalMode: false,
|
||||
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
botPlayMode: FlxG.keys.pressed.SHIFT,
|
||||
#else
|
||||
botPlayMode: false,
|
||||
|
@ -1895,7 +1941,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
intendedCompletion = 0.0;
|
||||
diffIdsCurrent = diffIdsTotal;
|
||||
rememberedSongId = null;
|
||||
rememberedDifficulty = null;
|
||||
rememberedDifficulty = Constants.DEFAULT_DIFFICULTY;
|
||||
albumRoll.albumId = null;
|
||||
}
|
||||
|
||||
|
@ -1946,7 +1992,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
var instSuffix:String = baseInstrumentalId;
|
||||
|
||||
// TODO: Make this a UI element.
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
if (altInstrumentalIds.length > 0 && FlxG.keys.pressed.CONTROL)
|
||||
{
|
||||
instSuffix = altInstrumentalIds[0];
|
||||
|
@ -2004,10 +2050,13 @@ class DifficultySelector extends FlxSprite
|
|||
var controls:Controls;
|
||||
var whiteShader:PureColor;
|
||||
|
||||
public function new(x:Float, y:Float, flipped:Bool, controls:Controls)
|
||||
var parent:FreeplayState;
|
||||
|
||||
public function new(parent:FreeplayState, x:Float, y:Float, flipped:Bool, controls:Controls)
|
||||
{
|
||||
super(x, y);
|
||||
|
||||
this.parent = parent;
|
||||
this.controls = controls;
|
||||
|
||||
frames = Paths.getSparrowAtlas('freeplay/freeplaySelector');
|
||||
|
@ -2023,8 +2072,8 @@ class DifficultySelector extends FlxSprite
|
|||
|
||||
override function update(elapsed:Float):Void
|
||||
{
|
||||
if (flipX && controls.UI_RIGHT_P) moveShitDown();
|
||||
if (!flipX && controls.UI_LEFT_P) moveShitDown();
|
||||
if (flipX && controls.UI_RIGHT_P && !parent.busy) moveShitDown();
|
||||
if (!flipX && controls.UI_LEFT_P && !parent.busy) moveShitDown();
|
||||
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
@ -2153,8 +2202,14 @@ class FreeplaySongData
|
|||
|
||||
function updateValues(variations:Array<String>):Void
|
||||
{
|
||||
this.songDifficulties = song.listSuffixedDifficulties(variations, false, false);
|
||||
if (!this.songDifficulties.contains(currentDifficulty)) currentDifficulty = Constants.DEFAULT_DIFFICULTY;
|
||||
this.songDifficulties = song.listDifficulties(null, variations, false, false);
|
||||
if (!this.songDifficulties.contains(currentDifficulty))
|
||||
{
|
||||
currentDifficulty = Constants.DEFAULT_DIFFICULTY;
|
||||
// This method gets called again by the setter-method
|
||||
// or the difficulty didn't change, so there's no need to continue.
|
||||
return;
|
||||
}
|
||||
|
||||
var songDifficulty:SongDifficulty = song.getDifficulty(currentDifficulty, null, variations);
|
||||
if (songDifficulty == null) return;
|
||||
|
|
|
@ -88,6 +88,11 @@ class PlayableCharacter implements IRegistryEntry<PlayerData>
|
|||
return _data.freeplayDJ.getFreeplayDJText(index);
|
||||
}
|
||||
|
||||
public function getCharSelectData():PlayerCharSelectData
|
||||
{
|
||||
return _data.charSelect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rank Which rank to get info for
|
||||
* @return An array of animations. For example, BF Great has two animations, one for BF and one for GF
|
||||
|
|
|
@ -27,7 +27,7 @@ import funkin.ui.title.TitleState;
|
|||
import funkin.ui.story.StoryMenuState;
|
||||
import funkin.ui.Prompt;
|
||||
import funkin.util.WindowUtil;
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
import Discord.DiscordClient;
|
||||
#end
|
||||
#if newgrounds
|
||||
|
@ -54,7 +54,7 @@ class MainMenuState extends MusicBeatState
|
|||
|
||||
override function create():Void
|
||||
{
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
// Updating Discord Rich Presence
|
||||
DiscordClient.changePresence("In the Menus", null);
|
||||
#end
|
||||
|
@ -98,14 +98,7 @@ class MainMenuState extends MusicBeatState
|
|||
add(menuItems);
|
||||
menuItems.onChange.add(onMenuItemChange);
|
||||
menuItems.onAcceptPress.add(function(_) {
|
||||
if (_.name == 'freeplay')
|
||||
{
|
||||
magenta.visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxFlicker.flicker(magenta, 1.1, 0.15, false, true);
|
||||
}
|
||||
});
|
||||
|
||||
menuItems.enabled = true; // can move on intro
|
||||
|
@ -117,10 +110,7 @@ class MainMenuState extends MusicBeatState
|
|||
FlxTransitionableState.skipNextTransIn = true;
|
||||
FlxTransitionableState.skipNextTransOut = true;
|
||||
|
||||
openSubState(new FreeplayState(
|
||||
{
|
||||
character: FlxG.keys.pressed.SHIFT ? 'pico' : 'bf',
|
||||
}));
|
||||
openSubState(new FreeplayState());
|
||||
});
|
||||
|
||||
#if CAN_OPEN_LINKS
|
||||
|
@ -344,7 +334,7 @@ class MainMenuState extends MusicBeatState
|
|||
}
|
||||
}
|
||||
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
// Open the debug menu, defaults to ` / ~
|
||||
if (controls.DEBUG_MENU)
|
||||
{
|
||||
|
@ -355,6 +345,7 @@ class MainMenuState extends MusicBeatState
|
|||
|
||||
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.W)
|
||||
{
|
||||
FunkinSound.playOnce(Paths.sound('confirmMenu'));
|
||||
// Give the user a score of 1 point on Weekend 1 story mode.
|
||||
// This makes the level count as cleared and displays the songs in Freeplay.
|
||||
funkin.save.Save.instance.setLevelScore('weekend1', 'easy',
|
||||
|
@ -375,6 +366,29 @@ class MainMenuState extends MusicBeatState
|
|||
});
|
||||
}
|
||||
|
||||
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.L)
|
||||
{
|
||||
FunkinSound.playOnce(Paths.sound('confirmMenu'));
|
||||
// Give the user a score of 0 points on Weekend 1 story mode.
|
||||
// This makes the level count as uncleared and no longer displays the songs in Freeplay.
|
||||
funkin.save.Save.instance.setLevelScore('weekend1', 'easy',
|
||||
{
|
||||
score: 1,
|
||||
tallies:
|
||||
{
|
||||
sick: 0,
|
||||
good: 0,
|
||||
bad: 0,
|
||||
shit: 0,
|
||||
missed: 0,
|
||||
combo: 0,
|
||||
maxCombo: 0,
|
||||
totalNotesHit: 0,
|
||||
totalNotes: 0,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.R)
|
||||
{
|
||||
// Give the user a hypothetical overridden score,
|
||||
|
|
|
@ -16,7 +16,7 @@ class LevelProp extends Bopper
|
|||
this.propData = value;
|
||||
|
||||
this.visible = this.propData != null;
|
||||
danceEvery = this.propData?.danceEvery ?? 0.0;
|
||||
danceEvery = this.propData?.danceEvery ?? 1.0;
|
||||
|
||||
applyData();
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ class LevelProp extends Bopper
|
|||
|
||||
public function playConfirm():Void
|
||||
{
|
||||
playAnimation('confirm', true, true);
|
||||
if (hasAnimation('confirm')) playAnimation('confirm', true, true);
|
||||
}
|
||||
|
||||
function applyData():Void
|
||||
|
|
|
@ -216,7 +216,7 @@ class StoryMenuState extends MusicBeatState
|
|||
changeLevel();
|
||||
refresh();
|
||||
|
||||
#if discord_rpc
|
||||
#if FEATURE_DISCORD_RPC
|
||||
// Updating Discord Rich Presence
|
||||
DiscordClient.changePresence('In the Menus', null);
|
||||
#end
|
||||
|
|
|
@ -174,7 +174,7 @@ class LoadingState extends MusicBeatSubState
|
|||
FlxG.watch.addQuick('percentage?', callbacks.numRemaining / callbacks.length);
|
||||
}
|
||||
|
||||
#if debug
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
if (FlxG.keys.justPressed.SPACE) trace('fired: ' + callbacks.getFired() + ' unfired:' + callbacks.getUnfired());
|
||||
#end
|
||||
}
|
||||
|
@ -314,25 +314,28 @@ class LoadingState extends MusicBeatSubState
|
|||
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num7'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num8'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num9'));
|
||||
|
||||
FunkinSprite.cacheTexture(Paths.image('notes', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('noteSplashes', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('noteStrumline', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('NOTE_hold_assets'));
|
||||
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/ready', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/set', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/go', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/ready', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/set', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/go', 'shared'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/sick'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/good'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/bad'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/shit'));
|
||||
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/sick'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/good'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/bad'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/shit'));
|
||||
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/sick'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/good'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/bad'));
|
||||
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/shit'));
|
||||
FunkinSprite.cacheTexture(Paths.image('miss', 'shared')); // TODO: remove this
|
||||
|
||||
// List all image assets in the level's library.
|
||||
// This is crude and I want to remove it when we have a proper asset caching system.
|
||||
|
|
|
@ -41,9 +41,9 @@ class Constants
|
|||
* A suffix to add to the game version.
|
||||
* Add a suffix to prototype builds and remove it for releases.
|
||||
*/
|
||||
public static final VERSION_SUFFIX:String = #if (DEBUG || FORCE_DEBUG_VERSION) ' PROTOTYPE' #else '' #end;
|
||||
public static final VERSION_SUFFIX:String = #if FEATURE_DEBUG_FUNCTIONS ' PROTOTYPE' #else '' #end;
|
||||
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
static function get_VERSION():String
|
||||
{
|
||||
return 'v${Application.current.meta.get('version')} (${GIT_BRANCH} : ${GIT_HASH}${GIT_HAS_LOCAL_CHANGES ? ' : MODIFIED' : ''})' + VERSION_SUFFIX;
|
||||
|
@ -384,11 +384,7 @@ class Constants
|
|||
* 1 = The preloader waits for 1 second before moving to the next step.
|
||||
* The progress bare is automatically rescaled to match.
|
||||
*/
|
||||
#if debug
|
||||
public static final PRELOADER_MIN_STAGE_TIME:Float = 0.0;
|
||||
#else
|
||||
public static final PRELOADER_MIN_STAGE_TIME:Float = 0.1;
|
||||
#end
|
||||
|
||||
/**
|
||||
* HEALTH VALUES
|
||||
|
@ -528,12 +524,16 @@ class Constants
|
|||
* OTHER
|
||||
*/
|
||||
// ==============================
|
||||
#if FEATURE_GHOST_TAPPING
|
||||
// Hey there, Eric here.
|
||||
// This feature is currently still in development. You can test it out by creating a special debug build!
|
||||
// lime build windows -DFEATURE_GHOST_TAPPING
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Duration, in seconds, after the player's section ends before the player can spam without penalty.
|
||||
*/
|
||||
public static final GHOST_TAPPING:Bool = false;
|
||||
public static final GHOST_TAP_DELAY:Float = 3 / 8;
|
||||
#end
|
||||
|
||||
/**
|
||||
* The maximum number of previous file paths for the Chart Editor to remember.
|
||||
|
|
|
@ -265,9 +265,10 @@ class CrashHandler
|
|||
|
||||
static function renderMethod():String
|
||||
{
|
||||
try
|
||||
var outputStr:String = 'UNKNOWN';
|
||||
outputStr = try
|
||||
{
|
||||
return switch (FlxG.renderMethod)
|
||||
switch (FlxG.renderMethod)
|
||||
{
|
||||
case FlxRenderMethod.DRAW_TILES: 'DRAW_TILES';
|
||||
case FlxRenderMethod.BLITTING: 'BLITTING';
|
||||
|
@ -276,7 +277,9 @@ class CrashHandler
|
|||
}
|
||||
catch (e)
|
||||
{
|
||||
return 'ERROR ON QUERY RENDER METHOD: ${e}';
|
||||
}
|
||||
'ERROR ON QUERY RENDER METHOD: ${e}';
|
||||
}
|
||||
|
||||
return outputStr;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue