mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2024-12-23 21:56:46 +00:00
Work in progress on Week 8 stuff (animation offsets, death animation)
This commit is contained in:
parent
05148ee4f0
commit
ee3e1750df
|
@ -54,7 +54,7 @@
|
|||
<library name="week5" preload="true" />
|
||||
<library name="week6" preload="true" />
|
||||
<library name="week7" preload="true" />
|
||||
<library name="week8" preload="true" />
|
||||
<library name="weekend1" preload="true" />
|
||||
</section>
|
||||
|
||||
<section if="NO_PRELOAD_ALL">
|
||||
|
@ -68,7 +68,7 @@
|
|||
<library name="week5" preload="false" />
|
||||
<library name="week6" preload="false" />
|
||||
<library name="week7" preload="false" />
|
||||
<library name="week8" preload="false" />
|
||||
<library name="weekend1" preload="false" />
|
||||
</section>
|
||||
|
||||
<assets path="assets/songs" library="songs" exclude="*.fla|*.ogg" if="web" />
|
||||
|
@ -91,8 +91,8 @@
|
|||
<assets path="assets/week6" library="week6" exclude="*.fla|*.mp3" unless="web" />
|
||||
<assets path="assets/week7" library="week7" exclude="*.fla|*.ogg" if="web" />
|
||||
<assets path="assets/week7" library="week7" exclude="*.fla|*.mp3" unless="web" />
|
||||
<assets path="assets/week8" library="week8" exclude="*.fla|*.ogg" if="web" />
|
||||
<assets path="assets/week8" library="week8" exclude="*.fla|*.mp3" unless="web" />
|
||||
<assets path="assets/weekend1" library="weekend1" exclude="*.fla|*.ogg" if="web" />
|
||||
<assets path="assets/weekend1" library="weekend1" exclude="*.fla|*.mp3" unless="web" />
|
||||
|
||||
<!-- <assets path='example_mods' rename='mods' embed='false'/> -->
|
||||
|
||||
|
@ -128,6 +128,7 @@
|
|||
<!--haxelib name="newgrounds" unless="switch"/> -->
|
||||
<haxelib name="faxe" if='switch' />
|
||||
<haxelib name="polymod" />
|
||||
<haxelib name="flxanimate" />
|
||||
|
||||
<haxelib name="thx.semver" />
|
||||
|
||||
|
|
7
hmm.json
7
hmm.json
|
@ -24,6 +24,13 @@
|
|||
"type": "haxelib",
|
||||
"version": "2.4.0"
|
||||
},
|
||||
{
|
||||
"name": "flxanimate",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "master",
|
||||
"url": "https://github.com/Dot-Stuff/flxanimate"
|
||||
},
|
||||
{
|
||||
"name": "hscript",
|
||||
"type": "git",
|
||||
|
|
|
@ -118,7 +118,6 @@ class FreeplayState extends MusicBeatSubstate
|
|||
addWeek(['Ugh', 'Guns', 'Stress'], 7, ['tankman']);
|
||||
|
||||
addWeek(["Darnell", "lit-up", "2hot"], 8, ['darnell']);
|
||||
addWeek(["bro"], 1, ['gf']);
|
||||
|
||||
// LOAD MUSIC
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package funkin;
|
||||
|
||||
import flixel.FlxSprite;
|
||||
import flixel.FlxObject;
|
||||
import flixel.system.FlxSound;
|
||||
import flixel.util.FlxColor;
|
||||
|
@ -50,7 +51,11 @@ class GameOverSubstate extends MusicBeatSubstate
|
|||
public function new()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
override public function create()
|
||||
{
|
||||
super.create();
|
||||
FlxG.sound.list.add(gameOverMusic);
|
||||
gameOverMusic.stop();
|
||||
|
||||
|
@ -73,13 +78,22 @@ class GameOverSubstate extends MusicBeatSubstate
|
|||
}
|
||||
}
|
||||
|
||||
// By adding a background we can make it transparent for testing.
|
||||
var bg = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK);
|
||||
bg.alpha = 0.25;
|
||||
bg.scrollFactor.set();
|
||||
add(bg);
|
||||
|
||||
// We have to remove boyfriend from the stage. Then we can add him back at the end.
|
||||
boyfriend = PlayState.instance.currentStage.getBoyfriend(true);
|
||||
boyfriend.isDead = true;
|
||||
boyfriend.playAnimation('firstDeath');
|
||||
add(boyfriend);
|
||||
boyfriend.resetCharacter();
|
||||
boyfriend.playAnimation('firstDeath');
|
||||
|
||||
cameraFollowPoint = new FlxObject(PlayState.instance.cameraFollowPoint.x, PlayState.instance.cameraFollowPoint.y, 1, 1);
|
||||
cameraFollowPoint.x = boyfriend.getGraphicMidpoint().x;
|
||||
cameraFollowPoint.y = boyfriend.getGraphicMidpoint().y;
|
||||
add(cameraFollowPoint);
|
||||
|
||||
// FlxG.camera.scroll.set();
|
||||
|
@ -124,11 +138,10 @@ class GameOverSubstate extends MusicBeatSubstate
|
|||
|
||||
// Start panning the camera to BF after 12 frames.
|
||||
// TODO: Should this be de-hardcoded?
|
||||
if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && boyfriend.animation.curAnim.curFrame == 12)
|
||||
{
|
||||
cameraFollowPoint.x = boyfriend.getGraphicMidpoint().x;
|
||||
cameraFollowPoint.y = boyfriend.getGraphicMidpoint().y;
|
||||
}
|
||||
//if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && boyfriend.animation.curAnim.curFrame == 12)
|
||||
//{
|
||||
//
|
||||
//}
|
||||
|
||||
if (gameOverMusic.playing)
|
||||
{
|
||||
|
|
|
@ -171,7 +171,7 @@ class InitState extends FlxTransitionableState
|
|||
#elseif FREEPLAY
|
||||
FlxG.switchState(new FreeplayState());
|
||||
#elseif ANIMATE
|
||||
FlxG.switchState(new animate.AnimTestStage());
|
||||
FlxG.switchState(new funkin.animate.dotstuff.DotStuffTestStage());
|
||||
#elseif CHARTING
|
||||
FlxG.switchState(new ChartingState());
|
||||
#elseif STAGEBUILD
|
||||
|
@ -179,11 +179,7 @@ class InitState extends FlxTransitionableState
|
|||
#elseif FIGHT
|
||||
FlxG.switchState(new PicoFight());
|
||||
#elseif ANIMDEBUG
|
||||
<<<<<<< HEAD
|
||||
FlxG.switchState(new funkin.ui.animDebugShit.DebugBoundingState());
|
||||
=======
|
||||
FlxG.switchState(new DebugBoundingState());
|
||||
>>>>>>> origin/feature/scripted-modules
|
||||
#elseif NETTEST
|
||||
FlxG.switchState(new netTest.NetTest());
|
||||
#else
|
||||
|
|
|
@ -188,8 +188,10 @@ class LoadingState extends MusicBeatState
|
|||
{
|
||||
Paths.setCurrentLevel('tutorial');
|
||||
}
|
||||
else
|
||||
{
|
||||
else if (PlayState.storyWeek == 8) {
|
||||
// TODO: Refactor this code.
|
||||
Paths.setCurrentLevel("weekend1");
|
||||
} else {
|
||||
Paths.setCurrentLevel("week" + PlayState.storyWeek);
|
||||
}
|
||||
#if NO_PRELOAD_ALL
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package funkin;
|
||||
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.FlxSubState;
|
||||
import funkin.Conductor.BPMChangeEvent;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
|
@ -10,9 +11,9 @@ import funkin.modding.module.ModuleHandler;
|
|||
*/
|
||||
class MusicBeatSubstate extends FlxSubState
|
||||
{
|
||||
public function new()
|
||||
public function new(bgColor:FlxColor = FlxColor.TRANSPARENT)
|
||||
{
|
||||
super();
|
||||
super(bgColor);
|
||||
}
|
||||
|
||||
private var curStep:Int = 0;
|
||||
|
|
|
@ -283,7 +283,9 @@ class Note extends FlxSprite
|
|||
|
||||
static public function fromData(data:NoteData, prevNote:Note, isSustainNote = false)
|
||||
{
|
||||
return new Note(data.strumTime, data.noteData, prevNote, isSustainNote);
|
||||
var result = new Note(data.strumTime, data.noteData, prevNote, isSustainNote);
|
||||
result.data = data;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,20 +294,18 @@ typedef RawNoteData =
|
|||
var strumTime:Float;
|
||||
var noteData:NoteType;
|
||||
var sustainLength:Float;
|
||||
var altNote:String;
|
||||
var noteKind:NoteKind;
|
||||
}
|
||||
|
||||
@:forward
|
||||
abstract NoteData(RawNoteData)
|
||||
{
|
||||
public function new(strumTime = 0.0, noteData:NoteType = 0, sustainLength = 0.0, altNote = "", noteKind = NORMAL)
|
||||
public function new(strumTime = 0.0, noteData:NoteType = 0, sustainLength = 0.0, noteKind = NORMAL)
|
||||
{
|
||||
this = {
|
||||
strumTime: strumTime,
|
||||
noteData: noteData,
|
||||
sustainLength: sustainLength,
|
||||
altNote: altNote,
|
||||
noteKind: noteKind
|
||||
}
|
||||
}
|
||||
|
@ -470,11 +470,5 @@ enum abstract NoteKind(String) from String to String
|
|||
* The default note type.
|
||||
*/
|
||||
var NORMAL = "normal";
|
||||
|
||||
// Testing shiz
|
||||
var PYRO_LIGHT = "pyro_light";
|
||||
var PYRO_KICK = "pyro_kick";
|
||||
var PYRO_TOSS = "pyro_toss";
|
||||
var PYRO_COCK = "pyro_cock"; // lol
|
||||
var PYRO_SHOOT = "pyro_shoot";
|
||||
var ALT = "alt";
|
||||
}
|
||||
|
|
|
@ -191,11 +191,7 @@ class SongLoad
|
|||
noteStuff[sectionIndex].sectionNotes[noteIndex].sustainLength = arrayDipshit[2];
|
||||
if (arrayDipshit.length > 3)
|
||||
{
|
||||
noteStuff[sectionIndex].sectionNotes[noteIndex].altNote = arrayDipshit[3];
|
||||
}
|
||||
if (arrayDipshit.length > 4)
|
||||
{
|
||||
noteStuff[sectionIndex].sectionNotes[noteIndex].noteKind = arrayDipshit[4];
|
||||
noteStuff[sectionIndex].sectionNotes[noteIndex].noteKind = arrayDipshit[3];
|
||||
}
|
||||
}
|
||||
else if (noteDataArray != null)
|
||||
|
@ -227,7 +223,7 @@ class SongLoad
|
|||
noteTypeDefShit.strumTime,
|
||||
noteTypeDefShit.noteData,
|
||||
noteTypeDefShit.sustainLength,
|
||||
noteTypeDefShit.altNote
|
||||
noteTypeDefShit.noteKind
|
||||
];
|
||||
|
||||
noteStuff[sectionIndex].sectionNotes[noteIndex] = cast dipshitArray;
|
||||
|
@ -252,7 +248,15 @@ class SongLoad
|
|||
|
||||
public static function parseJSONshit(rawJson:String):SwagSong
|
||||
{
|
||||
var songParsed:Dynamic = Json.parse(rawJson);
|
||||
var songParsed:Dynamic;
|
||||
try {
|
||||
songParsed = Json.parse(rawJson);
|
||||
} catch (e) {
|
||||
FlxG.log.warn("Error parsing JSON: " + e.message);
|
||||
trace("Error parsing JSON: " + e.message);
|
||||
return null;
|
||||
}
|
||||
|
||||
var swagShit:SwagSong = cast songParsed.song;
|
||||
swagShit.difficulties = []; // reset it to default before load
|
||||
swagShit.noteMap = new Map();
|
||||
|
|
|
@ -1016,8 +1016,8 @@ class ChartingState extends MusicBeatState
|
|||
if (curSelectedNote != null)
|
||||
{
|
||||
trace('ALT NOTE SHIT');
|
||||
curSelectedNote.altNote = (curSelectedNote.altNote == "alt") ? "" : "alt";
|
||||
trace(curSelectedNote.altNote);
|
||||
curSelectedNote.noteKind = (curSelectedNote.noteKind == "alt") ? "" : "alt";
|
||||
trace(curSelectedNote.noteKind);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1358,7 +1358,7 @@ class ChartingState extends MusicBeatState
|
|||
var noteStrum = getStrumTime(dummyArrow.y) + sectionStartTime();
|
||||
var noteData = Math.floor(FlxG.mouse.x / GRID_SIZE);
|
||||
var noteSus = 0;
|
||||
var noteAlt = "";
|
||||
var noteKind = "";
|
||||
|
||||
justPlacedNote = true;
|
||||
|
||||
|
@ -1399,7 +1399,7 @@ class ChartingState extends MusicBeatState
|
|||
|
||||
var daNewNote:Note = new Note(noteStrum, noteData);
|
||||
daNewNote.data.sustainLength = noteSus;
|
||||
daNewNote.data.altNote = noteAlt;
|
||||
daNewNote.data.noteKind = noteKind;
|
||||
SongLoad.getSong()[curSection].sectionNotes.push(daNewNote.data);
|
||||
|
||||
curSelectedNote = SongLoad.getSong()[curSection].sectionNotes[SongLoad.getSong()[curSection].sectionNotes.length - 1];
|
||||
|
|
|
@ -44,7 +44,7 @@ interface INoteScriptedClass extends IScriptedClass
|
|||
*
|
||||
* I previously considered adding events for onKeyDown, onKeyUp, mouse events, etc.
|
||||
* However, I realized that you can simply call something like the following within a module:
|
||||
* `FlxG.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);`
|
||||
* `FlxG.state.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);`
|
||||
* This is more efficient than adding an entire event handler for every key press.
|
||||
*
|
||||
* -Eric
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package funkin.modding;
|
||||
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.modding.module.ModuleHandler;
|
||||
import funkin.play.stage.StageData;
|
||||
import polymod.Polymod;
|
||||
|
@ -157,7 +158,7 @@ class PolymodHandler
|
|||
return {
|
||||
assetLibraryPaths: [
|
||||
"songs" => "songs", "shared" => "", "tutorial" => "tutorial", "scripts" => "scripts", "week1" => "week1", "week2" => "week2",
|
||||
"week3" => "week3", "week4" => "week4", "week5" => "week5", "week6" => "week6", "week7" => "week7", "week8" => "week8",
|
||||
"week3" => "week3", "week4" => "week4", "week5" => "week5", "week6" => "week6", "week7" => "week7", "weekend1" => "weekend1",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -227,9 +228,10 @@ class PolymodHandler
|
|||
// Reload scripted classes so stages and modules will update.
|
||||
polymod.hscript.PolymodScriptClass.registerAllScriptClasses();
|
||||
|
||||
// Reload the stages in cache.
|
||||
// TODO: Currently this causes lag since you're reading a lot of files, how to fix?
|
||||
// Reload everything that is cached.
|
||||
// Currently this freezes the game for a second but I guess that's tolerable?
|
||||
StageDataParser.loadStageCache();
|
||||
CharacterDataParser.loadCharacterCache();
|
||||
ModuleHandler.loadModuleCache();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,7 +142,7 @@ class ScriptEvent
|
|||
public static inline final GAME_OVER:ScriptEventType = "GAME_OVER";
|
||||
|
||||
/**
|
||||
* Called when the player presses a key to restart the game.
|
||||
* Called after the player presses a key to restart the game.
|
||||
* This can happen from the pause menu or the game over screen.
|
||||
*
|
||||
* This event IS cancelable! Canceling this event will prevent the game from restarting.
|
||||
|
|
|
@ -110,8 +110,6 @@ class HealthIcon extends FlxSprite
|
|||
|
||||
this.antialiasing = !isPixel;
|
||||
|
||||
this.flipX = playerId == 0;
|
||||
|
||||
initTargetSize();
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ class PicoFight extends MusicBeatState
|
|||
|
||||
override function create()
|
||||
{
|
||||
Paths.setCurrentLevel("week8");
|
||||
Paths.setCurrentLevel("weekend1");
|
||||
|
||||
var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height);
|
||||
bg.scrollFactor.set();
|
||||
|
|
|
@ -28,6 +28,7 @@ import funkin.play.Strumline.StrumlineArrow;
|
|||
import funkin.play.Strumline.StrumlineStyle;
|
||||
import funkin.play.character.BaseCharacter;
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.play.scoring.Scoring;
|
||||
import funkin.play.stage.Stage;
|
||||
import funkin.play.stage.StageData;
|
||||
import funkin.ui.PopUpStuff;
|
||||
|
@ -279,6 +280,14 @@ class PlayState extends MusicBeatState implements IHook
|
|||
{
|
||||
super.create();
|
||||
|
||||
if (currentSong == null) {
|
||||
lime.app.Application.current.window.alert(
|
||||
"There was a critical error while accessing the selected song. Click OK to return to the main menu.",
|
||||
"Error loading PlayState"
|
||||
);
|
||||
FlxG.switchState(new MainMenuState());
|
||||
}
|
||||
|
||||
instance = this;
|
||||
|
||||
// Displays the camera follow point as a sprite for debug purposes.
|
||||
|
@ -465,12 +474,13 @@ class PlayState extends MusicBeatState implements IHook
|
|||
currentStageId = 'mallXmas';
|
||||
case 'winter-horrorland':
|
||||
currentStageId = 'mallEvil';
|
||||
case 'senpai' | 'roses':
|
||||
currentStageId = 'school';
|
||||
case "darnell" | "lit-up" | "2hot":
|
||||
// currentStageId = 'phillyStreets';
|
||||
currentStageId = 'pyro';
|
||||
case 'pyro':
|
||||
currentStageId = 'pyro';
|
||||
case 'senpai' | 'roses':
|
||||
currentStageId = 'school';
|
||||
case "darnell":
|
||||
currentStageId = 'phillyStreets';
|
||||
case 'thorns':
|
||||
currentStageId = 'schoolEvil';
|
||||
case 'guns' | 'stress' | 'ugh':
|
||||
|
@ -504,6 +514,8 @@ class PlayState extends MusicBeatState implements IHook
|
|||
|
||||
switch (currentStageId)
|
||||
{
|
||||
case 'pyro' | 'phillyStreets':
|
||||
gfVersion = 'nene';
|
||||
case 'limoRide':
|
||||
gfVersion = 'gf-car';
|
||||
case 'mallXmas' | 'mallEvil':
|
||||
|
@ -580,12 +592,19 @@ class PlayState extends MusicBeatState implements IHook
|
|||
{
|
||||
// We're using Eric's stage handler.
|
||||
// Characters get added to the stage, not the main scene.
|
||||
currentStage.addCharacter(girlfriend, GF);
|
||||
currentStage.addCharacter(boyfriend, BF);
|
||||
currentStage.addCharacter(dad, DAD);
|
||||
if (girlfriend != null) {
|
||||
currentStage.addCharacter(girlfriend, GF);
|
||||
}
|
||||
|
||||
// Camera starts at dad.
|
||||
cameraFollowPoint.setPosition(dad.cameraFocusPoint.x, dad.cameraFocusPoint.y);
|
||||
if (boyfriend != null) {
|
||||
currentStage.addCharacter(boyfriend, BF);
|
||||
}
|
||||
|
||||
if (dad != null) {
|
||||
currentStage.addCharacter(dad, DAD);
|
||||
// Camera starts at dad.
|
||||
cameraFollowPoint.setPosition(dad.cameraFocusPoint.x, dad.cameraFocusPoint.y);
|
||||
}
|
||||
|
||||
// Redo z-indexes.
|
||||
currentStage.refresh();
|
||||
|
@ -867,7 +886,7 @@ class PlayState extends MusicBeatState implements IHook
|
|||
var swagNote:Note = new Note(daStrumTime, daNoteData, oldNote, false, strumlineStyle);
|
||||
// swagNote.data = songNotes;
|
||||
swagNote.data.sustainLength = songNotes.sustainLength;
|
||||
swagNote.data.altNote = songNotes.altNote;
|
||||
swagNote.data.noteKind = songNotes.noteKind;
|
||||
swagNote.scrollFactor.set(0, 0);
|
||||
|
||||
var susLength:Float = swagNote.data.sustainLength;
|
||||
|
@ -881,6 +900,7 @@ class PlayState extends MusicBeatState implements IHook
|
|||
|
||||
var sustainNote:Note = new Note(daStrumTime + (Conductor.stepCrochet * susNote) + Conductor.stepCrochet, daNoteData, oldNote, true,
|
||||
strumlineStyle);
|
||||
sustainNote.data.noteKind = songNotes.noteKind;
|
||||
sustainNote.scrollFactor.set();
|
||||
inactiveNotes.push(sustainNote);
|
||||
|
||||
|
@ -976,6 +996,8 @@ class PlayState extends MusicBeatState implements IHook
|
|||
|
||||
FlxG.sound.music.time = 0;
|
||||
|
||||
currentStage.resetStage();
|
||||
|
||||
regenNoteData(); // loads the note data from start
|
||||
health = 1;
|
||||
songScore = 0;
|
||||
|
@ -1037,7 +1059,9 @@ class PlayState extends MusicBeatState implements IHook
|
|||
|
||||
if (!event.eventCanceled)
|
||||
{
|
||||
// Pause updates while the substate is open, preventing the game state from advancing.
|
||||
persistentUpdate = false;
|
||||
// Enable drawing while the substate is open, allowing the game state to be shown behind the pause menu.
|
||||
persistentDraw = true;
|
||||
|
||||
// There is a 1/1000 change to use a special pause menu.
|
||||
|
@ -1062,6 +1086,21 @@ class PlayState extends MusicBeatState implements IHook
|
|||
}
|
||||
}
|
||||
|
||||
#if debug
|
||||
// 1: End the song immediately.
|
||||
if (FlxG.keys.justPressed.ONE)
|
||||
endSong();
|
||||
|
||||
// 2: Gain 10% health.
|
||||
if (FlxG.keys.justPressed.TWO)
|
||||
health += 0.1 * 2.0;
|
||||
|
||||
// 3: Lose 5% health.
|
||||
if (FlxG.keys.justPressed.THREE)
|
||||
health -= 0.05 * 2.0;
|
||||
#end
|
||||
|
||||
// 7: Move to the charter.
|
||||
if (FlxG.keys.justPressed.SEVEN)
|
||||
{
|
||||
FlxG.switchState(new ChartingState());
|
||||
|
@ -1071,25 +1110,26 @@ class PlayState extends MusicBeatState implements IHook
|
|||
#end
|
||||
}
|
||||
|
||||
// 8: Move to the offset editor.
|
||||
if (FlxG.keys.justPressed.EIGHT)
|
||||
FlxG.switchState(new funkin.ui.animDebugShit.DebugBoundingState());
|
||||
|
||||
// 9: Toggle the old icon.
|
||||
if (FlxG.keys.justPressed.NINE)
|
||||
iconP1.toggleOldIcon();
|
||||
|
||||
if (health > 2)
|
||||
health = 2;
|
||||
|
||||
#if debug
|
||||
if (FlxG.keys.justPressed.ONE)
|
||||
endSong();
|
||||
|
||||
if (FlxG.keys.justPressed.PAGEUP)
|
||||
changeSection(1);
|
||||
if (FlxG.keys.justPressed.PAGEDOWN)
|
||||
changeSection(-1);
|
||||
#end
|
||||
|
||||
if (health > 2.0)
|
||||
health = 2.0;
|
||||
if (health < 0.0)
|
||||
health = 0.0;
|
||||
|
||||
if (camZooming && subState == null)
|
||||
{
|
||||
FlxG.camera.zoom = FlxMath.lerp(defaultCameraZoom, FlxG.camera.zoom, 0.95);
|
||||
|
@ -1139,9 +1179,6 @@ class PlayState extends MusicBeatState implements IHook
|
|||
|
||||
if (health <= 0 && !isPracticeMode)
|
||||
{
|
||||
persistentUpdate = false;
|
||||
persistentDraw = false;
|
||||
|
||||
vocals.pause();
|
||||
FlxG.sound.music.pause();
|
||||
|
||||
|
@ -1149,7 +1186,22 @@ class PlayState extends MusicBeatState implements IHook
|
|||
|
||||
dispatchEvent(new ScriptEvent(ScriptEvent.GAME_OVER));
|
||||
|
||||
openSubState(new GameOverSubstate());
|
||||
// Disable updates, preventing animations in the background from playing.
|
||||
persistentUpdate = false;
|
||||
#if debug
|
||||
if (FlxG.keys.pressed.THREE) {
|
||||
// TODO: Change the key or delete this?
|
||||
// In debug builds, pressing 3 to kill the player makes the background transparent.
|
||||
persistentDraw = true;
|
||||
} else {
|
||||
#end
|
||||
persistentDraw = false;
|
||||
#if debug
|
||||
}
|
||||
#end
|
||||
|
||||
var gameOverSubstate = new GameOverSubstate();
|
||||
openSubState(gameOverSubstate);
|
||||
|
||||
#if discord_rpc
|
||||
// Game Over doesn't get his own variable because it's only used here
|
||||
|
@ -1307,6 +1359,11 @@ class PlayState extends MusicBeatState implements IHook
|
|||
}
|
||||
|
||||
#if debug
|
||||
/**
|
||||
* Jumps forward or backward a number of sections in the song.
|
||||
* Accounts for BPM changes, does not prevent death from skipped notes.
|
||||
* @param sec
|
||||
*/
|
||||
function changeSection(sec:Int):Void
|
||||
{
|
||||
FlxG.sound.music.pause();
|
||||
|
@ -1432,30 +1489,22 @@ class PlayState extends MusicBeatState implements IHook
|
|||
// boyfriend.playAnimation('hey');
|
||||
vocals.volume = 1;
|
||||
|
||||
var score:Int = 350;
|
||||
var daRating:String = "sick";
|
||||
var isSick:Bool = false;
|
||||
var healthMulti:Float = 1;
|
||||
|
||||
healthMulti *= daNote.lowStakes ? 0.002 : 0.033;
|
||||
var score = Scoring.scoreNote(noteDiff, PBOT1);
|
||||
var daRating = Scoring.judgeNote(noteDiff, PBOT1);
|
||||
var healthMulti:Float = daNote.lowStakes ? 0.002 : 0.033;
|
||||
|
||||
if (noteDiff > Note.HIT_WINDOW * Note.BAD_THRESHOLD)
|
||||
{
|
||||
healthMulti *= 0; // no health on shit note
|
||||
daRating = 'shit';
|
||||
score = 50;
|
||||
}
|
||||
else if (noteDiff > Note.HIT_WINDOW * Note.GOOD_THRESHOLD)
|
||||
{
|
||||
healthMulti *= 0.2;
|
||||
daRating = 'bad';
|
||||
score = 100;
|
||||
}
|
||||
else if (noteDiff > Note.HIT_WINDOW * Note.SICK_THRESHOLD)
|
||||
{
|
||||
healthMulti *= 0.78;
|
||||
daRating = 'good';
|
||||
score = 200;
|
||||
}
|
||||
else
|
||||
isSick = true;
|
||||
|
@ -1795,6 +1844,7 @@ class PlayState extends MusicBeatState implements IHook
|
|||
|
||||
// bruh this var is bonkers i thot it was a function lmfaooo
|
||||
|
||||
|
||||
var shouldShowComboText:Bool = (curBeat % 8 == 7) // End of measure. TODO: Is this always the correct time?
|
||||
&& (SongLoad.getSong()[Std.int(curStep / 16)].mustHitSection) // Current section is BF's.
|
||||
&& (combo > 5) // Don't want to show on small combos.
|
||||
|
@ -1832,11 +1882,13 @@ class PlayState extends MusicBeatState implements IHook
|
|||
if (currentStage == null)
|
||||
return;
|
||||
|
||||
// TODO: Move this to a song event.
|
||||
if (curBeat % 8 == 7 && currentSong.song == 'Bopeebo')
|
||||
{
|
||||
currentStage.getBoyfriend().playAnimation('hey', true);
|
||||
}
|
||||
|
||||
// TODO: Move this to a song event.
|
||||
if (curBeat % 16 == 15
|
||||
&& currentSong.song == 'Tutorial'
|
||||
&& currentStage.getDad().characterId == 'gf'
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
package funkin.play;
|
||||
|
||||
/**
|
||||
* A static class which holds any functions related to scoring.
|
||||
*/
|
||||
class Scoring {}
|
|
@ -92,7 +92,7 @@ class BaseCharacter extends Bopper
|
|||
override function set_animOffsets(value:Array<Float>)
|
||||
{
|
||||
if (animOffsets == null)
|
||||
animOffsets = [0, 0];
|
||||
value = [0, 0];
|
||||
if (animOffsets == value)
|
||||
return value;
|
||||
|
||||
|
@ -157,6 +157,15 @@ class BaseCharacter extends Bopper
|
|||
shouldBop = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of flipX from the character data.
|
||||
* `!getFlipX()` is the direction Boyfriend should face.
|
||||
*/
|
||||
public function getDataFlipX():Bool
|
||||
{
|
||||
return _data.flipX;
|
||||
}
|
||||
|
||||
function findCountAnimations(prefix:String):Array<Int> {
|
||||
var animNames:Array<String> = this.animation.getNameList();
|
||||
|
||||
|
@ -176,6 +185,27 @@ class BaseCharacter extends Bopper
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the character so it can be used at the start of the level.
|
||||
* Call this when restarting the level.
|
||||
*/
|
||||
public function resetCharacter(resetCamera:Bool = true):Void {
|
||||
// Reset the animation offsets. This will modify x and y to be the absolute position of the character.
|
||||
this.animOffsets = [0, 0];
|
||||
|
||||
// Now we can set the x and y to be their original values without having to account for animOffsets.
|
||||
this.resetPosition();
|
||||
|
||||
// Make sure we are playing the idle animation (to reapply animOffsets)...
|
||||
this.dance();
|
||||
// ...then update the hitbox so that this.width and this.height are correct.
|
||||
this.updateHitbox();
|
||||
|
||||
// Reset the camera focus point while we're at it.
|
||||
if (resetCamera)
|
||||
this.resetCameraFocusPoint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sprite scale to the appropriate value.
|
||||
* @param scale
|
||||
|
@ -206,10 +236,14 @@ class BaseCharacter extends Bopper
|
|||
|
||||
override function onCreate(event:ScriptEvent):Void
|
||||
{
|
||||
// Camera focus point
|
||||
var charCenterX = this.x + this.width / 2;
|
||||
var charCenterY = this.y + this.height / 2;
|
||||
this.cameraFocusPoint = new FlxPoint(charCenterX + _data.cameraOffsets[0], charCenterY + _data.cameraOffsets[1]);
|
||||
// Make sure we are playing the idle animation...
|
||||
this.dance();
|
||||
// ...then update the hitbox so that this.width and this.height are correct.
|
||||
this.updateHitbox();
|
||||
// Without the above code, width and height (and therefore character position)
|
||||
// will be based on the first animation in the sheet rather than the default animation.
|
||||
|
||||
this.resetCameraFocusPoint();
|
||||
|
||||
// Child class should have created animations by now,
|
||||
// so we can query which ones are available.
|
||||
|
@ -218,10 +252,18 @@ class BaseCharacter extends Bopper
|
|||
trace('${this.animation.getNameList()}');
|
||||
trace('Combo note counts: ' + this.comboNoteCounts);
|
||||
trace('Drop note counts: ' + this.dropNoteCounts);
|
||||
|
||||
|
||||
super.onCreate(event);
|
||||
}
|
||||
|
||||
function resetCameraFocusPoint():Void
|
||||
{
|
||||
// Calculate the camera focus point
|
||||
var charCenterX = this.x + this.width / 2;
|
||||
var charCenterY = this.y + this.height / 2;
|
||||
this.cameraFocusPoint = new FlxPoint(charCenterX + _data.cameraOffsets[0], charCenterY + _data.cameraOffsets[1]);
|
||||
}
|
||||
|
||||
public function initHealthIcon(isOpponent:Bool):Void
|
||||
{
|
||||
if (!isOpponent)
|
||||
|
@ -230,6 +272,7 @@ class BaseCharacter extends Bopper
|
|||
PlayState.instance.iconP1.size.set(_data.healthIcon.scale, _data.healthIcon.scale);
|
||||
PlayState.instance.iconP1.offset.x = _data.healthIcon.offsets[0];
|
||||
PlayState.instance.iconP1.offset.y = _data.healthIcon.offsets[1];
|
||||
PlayState.instance.iconP1.flipX = !_data.healthIcon.flipX;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -237,6 +280,7 @@ class BaseCharacter extends Bopper
|
|||
PlayState.instance.iconP2.size.set(_data.healthIcon.scale, _data.healthIcon.scale);
|
||||
PlayState.instance.iconP2.offset.x = _data.healthIcon.offsets[0];
|
||||
PlayState.instance.iconP2.offset.y = _data.healthIcon.offsets[1];
|
||||
PlayState.instance.iconP1.flipX = _data.healthIcon.flipX;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,16 +299,16 @@ class BaseCharacter extends Bopper
|
|||
playDeathAnimation();
|
||||
}
|
||||
|
||||
if (hasAnimation('idle-end') && getCurrentAnimation() == "idle" && isAnimationFinished())
|
||||
playAnimation('idle-end');
|
||||
if (hasAnimation('singLEFT-end') && getCurrentAnimation() == "singLEFT" && isAnimationFinished())
|
||||
playAnimation('singLEFT-end');
|
||||
if (hasAnimation('singDOWN-end') && getCurrentAnimation() == "singDOWN" && isAnimationFinished())
|
||||
playAnimation('singDOWN-end');
|
||||
if (hasAnimation('singUP-end') && getCurrentAnimation() == "singUP" && isAnimationFinished())
|
||||
playAnimation('singUP-end');
|
||||
if (hasAnimation('singRIGHT-end') && getCurrentAnimation() == "singRIGHT" && isAnimationFinished())
|
||||
playAnimation('singRIGHT-end');
|
||||
if (hasAnimation('idle-hold') && getCurrentAnimation() == "idle" && isAnimationFinished())
|
||||
playAnimation('idle-hold');
|
||||
if (hasAnimation('singLEFT-hold') && getCurrentAnimation() == "singLEFT" && isAnimationFinished())
|
||||
playAnimation('singLEFT-hold');
|
||||
if (hasAnimation('singDOWN-hold') && getCurrentAnimation() == "singDOWN" && isAnimationFinished())
|
||||
playAnimation('singDOWN-hold');
|
||||
if (hasAnimation('singUP-hold') && getCurrentAnimation() == "singUP" && isAnimationFinished())
|
||||
playAnimation('singUP-hold');
|
||||
if (hasAnimation('singRIGHT-hold') && getCurrentAnimation() == "singRIGHT" && isAnimationFinished())
|
||||
playAnimation('singRIGHT-hold');
|
||||
|
||||
// Handle character note hold time.
|
||||
if (getCurrentAnimation().startsWith("sing"))
|
||||
|
@ -276,9 +320,8 @@ class BaseCharacter extends Bopper
|
|||
var shouldStopSinging:Bool = (this.characterType == BF) ? !isHoldingNote() : true;
|
||||
|
||||
FlxG.watch.addQuick('singTimeMs-${characterId}', singTimeMs);
|
||||
if (holdTimer > singTimeMs && shouldStopSinging && !getCurrentAnimation().endsWith("miss"))
|
||||
if (holdTimer > singTimeMs && shouldStopSinging) // && !getCurrentAnimation().endsWith("miss")
|
||||
{
|
||||
trace(getCurrentAnimation());
|
||||
// trace('holdTimer reached ${holdTimer}sec (> ${singTimeMs}), stopping sing animation');
|
||||
holdTimer = 0;
|
||||
dance(true);
|
||||
|
@ -401,14 +444,14 @@ class BaseCharacter extends Bopper
|
|||
if (event.note.mustPress && characterType == BF)
|
||||
{
|
||||
// If the note is from the same strumline, play the sing animation.
|
||||
this.playSingAnimation(event.note.data.dir, false, event.note.data.altNote);
|
||||
this.playSingAnimation(event.note.data.dir, false);
|
||||
}
|
||||
else if (!event.note.mustPress && characterType == DAD)
|
||||
{
|
||||
// If the note is from the same strumline, play the sing animation.
|
||||
this.playSingAnimation(event.note.data.dir, false, event.note.data.altNote);
|
||||
this.playSingAnimation(event.note.data.dir, false);
|
||||
} else if (characterType == GF) {
|
||||
if (this.comboNoteCounts.contains(event.comboCount)) {
|
||||
if (event.note.mustPress && this.comboNoteCounts.contains(event.comboCount)) {
|
||||
trace('Playing GF combo animation: combo${event.comboCount}');
|
||||
this.playAnimation('combo${event.comboCount}', true, true);
|
||||
}
|
||||
|
@ -426,12 +469,12 @@ class BaseCharacter extends Bopper
|
|||
if (event.note.mustPress && characterType == BF)
|
||||
{
|
||||
// If the note is from the same strumline, play the sing animation.
|
||||
this.playSingAnimation(event.note.data.dir, true, event.note.data.altNote);
|
||||
this.playSingAnimation(event.note.data.dir, true);
|
||||
}
|
||||
else if (!event.note.mustPress && characterType == DAD)
|
||||
{
|
||||
// If the note is from the same strumline, play the sing animation.
|
||||
this.playSingAnimation(event.note.data.dir, true, event.note.data.altNote);
|
||||
this.playSingAnimation(event.note.data.dir, true);
|
||||
} else if (event.note.mustPress && characterType == GF) {
|
||||
var dropAnim = '';
|
||||
|
||||
|
@ -466,9 +509,9 @@ class BaseCharacter extends Bopper
|
|||
|
||||
if (characterType == BF)
|
||||
{
|
||||
trace('Playing ghost miss animation...');
|
||||
// If the note is from the same strumline, play the sing animation.
|
||||
this.playSingAnimation(event.dir, true, null);
|
||||
// trace('Playing ghost miss animation...');
|
||||
this.playSingAnimation(event.dir, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ class CharacterDataParser
|
|||
trace(' Instantiating ${scriptedCharClassNames3.length} (Multi-Sparrow) scripted characters...');
|
||||
for (charCls in scriptedCharClassNames3)
|
||||
{
|
||||
var character = ScriptedBaseCharacter.init(charCls, DEFAULT_CHAR_ID);
|
||||
var character = ScriptedMultiSparrowCharacter.init(charCls, DEFAULT_CHAR_ID);
|
||||
if (character == null)
|
||||
{
|
||||
trace(' Failed to instantiate scripted character: ${charCls}');
|
||||
|
@ -362,6 +362,7 @@ class CharacterDataParser
|
|||
input.healthIcon = {
|
||||
id: null,
|
||||
scale: null,
|
||||
flipX: null,
|
||||
offsets: null
|
||||
};
|
||||
}
|
||||
|
@ -376,6 +377,11 @@ class CharacterDataParser
|
|||
input.healthIcon.scale = DEFAULT_SCALE;
|
||||
}
|
||||
|
||||
if (input.healthIcon.flipX == null)
|
||||
{
|
||||
input.healthIcon.flipX = DEFAULT_FLIPX;
|
||||
}
|
||||
|
||||
if (input.healthIcon.offsets == null)
|
||||
{
|
||||
input.healthIcon.offsets = DEFAULT_OFFSETS;
|
||||
|
@ -583,6 +589,12 @@ typedef HealthIconData =
|
|||
*/
|
||||
var scale:Null<Float>;
|
||||
|
||||
/**
|
||||
* Whether to flip the health icon horizontally.
|
||||
* @default false
|
||||
*/
|
||||
var flipX:Null<Bool>;
|
||||
|
||||
/**
|
||||
* The offset of the health icon, in pixels.
|
||||
* @default [0, 25]
|
||||
|
|
|
@ -50,8 +50,6 @@ class MultiSparrowCharacter extends BaseCharacter
|
|||
buildSpritesheets();
|
||||
buildAnimations();
|
||||
|
||||
playAnimation(_data.startingAnimation);
|
||||
|
||||
if (_data.isPixel)
|
||||
{
|
||||
this.antialiasing = false;
|
||||
|
@ -124,7 +122,7 @@ class MultiSparrowCharacter extends BaseCharacter
|
|||
if (members.exists(assetPath))
|
||||
{
|
||||
// Switch to a new set of sprites.
|
||||
trace('Loading frames from asset path: ${assetPath}');
|
||||
// trace('Loading frames from asset path: ${assetPath}');
|
||||
this.frames = members.get(assetPath);
|
||||
this.activeMember = assetPath;
|
||||
this.setScale(_data.scale);
|
||||
|
@ -176,7 +174,9 @@ class MultiSparrowCharacter extends BaseCharacter
|
|||
|
||||
public override function playAnimation(name:String, restart:Bool = false, ?ignoreOther:Bool = false):Void
|
||||
{
|
||||
if (!this.canPlayOtherAnims)
|
||||
// Make sure we ignore other animations if we're currently playing a forced one,
|
||||
// unless we're forcing a new animation.
|
||||
if (!this.canPlayOtherAnims && !ignoreOther)
|
||||
return;
|
||||
|
||||
loadFramesByAnimName(name);
|
||||
|
|
|
@ -23,8 +23,6 @@ class PackerCharacter extends BaseCharacter
|
|||
loadSpritesheet();
|
||||
loadAnimations();
|
||||
|
||||
playAnimation(_data.startingAnimation);
|
||||
|
||||
super.onCreate(event);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,8 +25,6 @@ class SparrowCharacter extends BaseCharacter
|
|||
loadSpritesheet();
|
||||
loadAnimations();
|
||||
|
||||
playAnimation(_data.startingAnimation);
|
||||
|
||||
super.onCreate(event);
|
||||
}
|
||||
|
||||
|
|
198
source/funkin/play/scoring/Scoring.hx
Normal file
198
source/funkin/play/scoring/Scoring.hx
Normal file
|
@ -0,0 +1,198 @@
|
|||
package funkin.play.scoring;
|
||||
|
||||
enum abstract ScoringSystem(String) {
|
||||
/**
|
||||
* The scoring system used in versions of the game Week 6 and older.
|
||||
* Scores the player based on judgement, represented by a step function.
|
||||
*/
|
||||
var LEGACY;
|
||||
/**
|
||||
* The scoring system used in Week 7. It has tighter scoring windows than Legacy.
|
||||
* Scores the player based on judgement, represented by a step function.
|
||||
*/
|
||||
var WEEK7;
|
||||
/**
|
||||
* Points Based On Timing scoring system, version 1
|
||||
* Scores the player based on the offset based on timing, represented by a sigmoid function.
|
||||
*/
|
||||
var PBOT1;
|
||||
// WIFE1
|
||||
// WIFE3
|
||||
}
|
||||
|
||||
/**
|
||||
* A static class which holds any functions related to scoring.
|
||||
*/
|
||||
class Scoring {
|
||||
/**
|
||||
* Determine the score a note receives under a given scoring system.
|
||||
* @param msTiming The difference between the note's time and when it was hit.
|
||||
* @param scoringSystem The scoring system to use.
|
||||
* @return The score the note receives.
|
||||
*/
|
||||
public static function scoreNote(msTiming:Float, scoringSystem:ScoringSystem = PBOT1) {
|
||||
switch (scoringSystem) {
|
||||
case LEGACY:
|
||||
return scoreNote_LEGACY(msTiming);
|
||||
case WEEK7:
|
||||
return scoreNote_WEEK7(msTiming);
|
||||
case PBOT1:
|
||||
return scoreNote_PBOT1(msTiming);
|
||||
default:
|
||||
trace('ERROR: Unknown scoring system: ' + scoringSystem);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the judgement a note receives under a given scoring system.
|
||||
* @param msTiming The difference between the note's time and when it was hit.
|
||||
* @return The judgement the note receives.
|
||||
*/
|
||||
public static function judgeNote(msTiming:Float, scoringSystem:ScoringSystem = PBOT1):String {
|
||||
switch (scoringSystem) {
|
||||
case LEGACY:
|
||||
return judgeNote_LEGACY(msTiming);
|
||||
case WEEK7:
|
||||
return judgeNote_WEEK7(msTiming);
|
||||
case PBOT1:
|
||||
return judgeNote_PBOT1(msTiming);
|
||||
default:
|
||||
trace('ERROR: Unknown scoring system: ' + scoringSystem);
|
||||
return 'miss';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum score received.
|
||||
*/
|
||||
public static var PBOT1_MAX_SCORE = 350;
|
||||
/**
|
||||
* The minimum score received.
|
||||
*/
|
||||
public static var PBOT1_MIN_SCORE = 0;
|
||||
/**
|
||||
* The threshold at which a note hit is considered perfect and always given the max score.
|
||||
**/
|
||||
public static var PBOT1_PERFECT_THRESHOLD = 5.0; // 5ms.
|
||||
/**
|
||||
* The threshold at which a note hit is considered missed and always given the min score.
|
||||
**/
|
||||
public static var PBOT1_MISS_THRESHOLD = (10/60) * 1000; // ~166ms
|
||||
|
||||
// Magic numbers used to tweak the shape of the scoring function.
|
||||
public static var PBOT1_SCORING_SLOPE:Float = 0.052;
|
||||
public static var PBOT1_SCORING_OFFSET:Float = 80.0;
|
||||
|
||||
static function scoreNote_PBOT1(msTiming:Float):Int {
|
||||
// Absolute value because otherwise late hits are always given the max score.
|
||||
var absTiming = Math.abs(msTiming);
|
||||
if (absTiming > PBOT1_MISS_THRESHOLD) {
|
||||
return PBOT1_MIN_SCORE;
|
||||
} else if (absTiming < PBOT1_PERFECT_THRESHOLD) {
|
||||
return PBOT1_MAX_SCORE;
|
||||
} else {
|
||||
// Calculate the score based on the timing using a sigmoid function.
|
||||
var factor:Float = 1.0 - (1.0 / (1.0 + Math.exp(-PBOT1_SCORING_SLOPE * (absTiming - PBOT1_SCORING_OFFSET))));
|
||||
|
||||
var score = Std.int(PBOT1_MAX_SCORE * factor);
|
||||
|
||||
return score;
|
||||
}
|
||||
}
|
||||
|
||||
static function judgeNote_PBOT1(msTiming:Float):String {
|
||||
return judgeNote_WEEK7(msTiming);
|
||||
}
|
||||
|
||||
/**
|
||||
* The window of time in which a note is considered to be hit, on the Funkin Legacy scoring system.
|
||||
* Currently equal to 10 frames at 60fps, or ~166ms.
|
||||
*/
|
||||
public static var LEGACY_HIT_WINDOW:Float = (10 / 60) * 1000; // 166.67 ms hit window (10 frames at 60fps)
|
||||
/**
|
||||
* The threshold at which a note is considered a "Bad" hit rather than a "Shit" hit.
|
||||
* Represented as a percentage of the total hit window.
|
||||
*/
|
||||
public static var LEGACY_BAD_THRESHOLD:Float = 0.9;
|
||||
public static var LEGACY_GOOD_THRESHOLD:Float = 0.75;
|
||||
public static var LEGACY_SICK_THRESHOLD:Float = 0.2;
|
||||
public static var LEGACY_SHIT_SCORE = 50;
|
||||
public static var LEGACY_BAD_SCORE = 100;
|
||||
public static var LEGACY_GOOD_SCORE = 200;
|
||||
public static var LEGACY_SICK_SCORE = 350;
|
||||
|
||||
static function scoreNote_LEGACY(msTiming:Float):Int {
|
||||
var absTiming = Math.abs(msTiming);
|
||||
if (absTiming < LEGACY_HIT_WINDOW * LEGACY_SICK_THRESHOLD) {
|
||||
return LEGACY_SICK_SCORE;
|
||||
} else if (absTiming < LEGACY_HIT_WINDOW * LEGACY_GOOD_THRESHOLD) {
|
||||
return LEGACY_GOOD_SCORE;
|
||||
} else if (absTiming < LEGACY_HIT_WINDOW * LEGACY_BAD_THRESHOLD) {
|
||||
return LEGACY_BAD_SCORE;
|
||||
} else if (absTiming < LEGACY_HIT_WINDOW) {
|
||||
return LEGACY_SHIT_SCORE;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static function judgeNote_LEGACY(msTiming:Float):String {
|
||||
var absTiming = Math.abs(msTiming);
|
||||
if (absTiming < LEGACY_HIT_WINDOW * LEGACY_SICK_THRESHOLD) {
|
||||
return 'sick';
|
||||
} else if (absTiming < LEGACY_HIT_WINDOW * LEGACY_GOOD_THRESHOLD) {
|
||||
return 'good';
|
||||
} else if (absTiming < LEGACY_HIT_WINDOW * LEGACY_BAD_THRESHOLD) {
|
||||
return 'bad';
|
||||
} else if (absTiming < LEGACY_HIT_WINDOW) {
|
||||
return 'shit';
|
||||
} else {
|
||||
return 'miss';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The window of time in which a note is considered to be hit, on the Funkin Classic scoring system.
|
||||
* Same as L 10 frames at 60fps, or ~166ms.
|
||||
*/
|
||||
public static var WEEK7_HIT_WINDOW = LEGACY_HIT_WINDOW;
|
||||
public static var WEEK7_BAD_THRESHOLD = 0.8; // 80% of the hit window, or ~125ms
|
||||
public static var WEEK7_GOOD_THRESHOLD = 0.55; // 55% of the hit window, or ~91ms
|
||||
public static var WEEK7_SICK_THRESHOLD = 0.2; // 20% of the hit window, or ~33ms
|
||||
public static var WEEK7_SHIT_SCORE = 50;
|
||||
public static var WEEK7_BAD_SCORE = 100;
|
||||
public static var WEEK7_GOOD_SCORE = 200;
|
||||
public static var WEEK7_SICK_SCORE = 350;
|
||||
|
||||
static function scoreNote_WEEK7(msTiming:Float):Int {
|
||||
var absTiming = Math.abs(msTiming);
|
||||
if (absTiming < WEEK7_HIT_WINDOW * WEEK7_SICK_THRESHOLD) {
|
||||
return WEEK7_SICK_SCORE;
|
||||
} else if (absTiming < WEEK7_HIT_WINDOW * WEEK7_GOOD_THRESHOLD) {
|
||||
return WEEK7_GOOD_SCORE;
|
||||
} else if (absTiming < WEEK7_HIT_WINDOW * WEEK7_BAD_THRESHOLD) {
|
||||
return WEEK7_BAD_SCORE;
|
||||
} else if (absTiming < WEEK7_HIT_WINDOW) {
|
||||
return WEEK7_SHIT_SCORE;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static function judgeNote_WEEK7(msTiming:Float):String {
|
||||
var absTiming = Math.abs(msTiming);
|
||||
if (absTiming < WEEK7_HIT_WINDOW * WEEK7_SICK_THRESHOLD) {
|
||||
return 'sick';
|
||||
} else if (absTiming < WEEK7_HIT_WINDOW * WEEK7_GOOD_THRESHOLD) {
|
||||
return 'good';
|
||||
} else if (absTiming < WEEK7_HIT_WINDOW * WEEK7_BAD_THRESHOLD) {
|
||||
return 'bad';
|
||||
} else if (absTiming < WEEK7_HIT_WINDOW) {
|
||||
return 'shit';
|
||||
} else {
|
||||
return 'miss';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -104,11 +104,36 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
|
|||
{
|
||||
super();
|
||||
this.danceEvery = danceEvery;
|
||||
this.animation.finishCallback = function(name)
|
||||
{
|
||||
if (finishCallbackMap[name] != null)
|
||||
finishCallbackMap[name]();
|
||||
};
|
||||
|
||||
this.animation.callback = this.onAnimationFrame;
|
||||
this.animation.finishCallback = this.onAnimationFinished;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an animation finishes.
|
||||
* @param name The name of the animation that just finished.
|
||||
*/
|
||||
function onAnimationFinished(name:String) {
|
||||
if (!canPlayOtherAnims) {
|
||||
canPlayOtherAnims = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the current animation's frame changes.
|
||||
* @param name The name of the current animation.
|
||||
* @param frameNumber The number of the current frame.
|
||||
* @param frameIndex The index of the current frame.
|
||||
*
|
||||
* For example, if an animation was defined as having the indexes [3, 0, 1, 2],
|
||||
* then the first callback would have frameNumber = 0 and frameIndex = 3.
|
||||
*/
|
||||
function onAnimationFrame(name:String = "", frameNumber:Int = -1, frameIndex:Int = -1) {
|
||||
// Do nothing by default.
|
||||
// This can be overridden by, for example, scripted characters.
|
||||
// Try not to do anything expensive here, it runs many times a second.
|
||||
|
||||
// Sometimes this gets called with empty values? IDK why but adding defaults keeps it from crashing.
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -236,13 +261,6 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
|
|||
if (ignoreOther)
|
||||
{
|
||||
canPlayOtherAnims = false;
|
||||
|
||||
// doing it with this funny map, since overriding the animation.finishCallback is a bit messier IMO
|
||||
finishCallbackMap[name] = function()
|
||||
{
|
||||
canPlayOtherAnims = true;
|
||||
finishCallbackMap[name] = null;
|
||||
};
|
||||
}
|
||||
|
||||
applyAnimationOffsets(correctName);
|
||||
|
|
|
@ -64,6 +64,36 @@ class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScripte
|
|||
this.refresh();
|
||||
}
|
||||
|
||||
public function resetStage():Void {
|
||||
// Reset positions of characters.
|
||||
if (getBoyfriend() != null) {
|
||||
getBoyfriend().resetCharacter(false);
|
||||
} else {
|
||||
trace('STAGE RESET: No boyfriend found.');
|
||||
}
|
||||
if (getGirlfriend() != null) {
|
||||
getGirlfriend().resetCharacter(false);
|
||||
}
|
||||
if (getDad() != null) {
|
||||
getDad().resetCharacter(false);
|
||||
}
|
||||
|
||||
// Reset positions of named props.
|
||||
for (dataProp in _data.props) {
|
||||
// Fetch the prop.
|
||||
var prop:FlxSprite = getNamedProp(dataProp.name);
|
||||
|
||||
if (prop != null) {
|
||||
// Reset the position.
|
||||
prop.x = dataProp.position[0];
|
||||
prop.y = dataProp.position[1];
|
||||
prop.zIndex = dataProp.zIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// We can assume unnamed props are not moving.
|
||||
}
|
||||
|
||||
/**
|
||||
* The default stage construction routine. Called when the stage is going to be played in.
|
||||
* Instantiates each prop and adds it to the stage, while setting its parameters.
|
||||
|
@ -253,9 +283,13 @@ class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScripte
|
|||
// 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.
|
||||
var debugIcon:FlxSprite = new FlxSprite(0, 0);
|
||||
var debugIcon2:FlxSprite = new FlxSprite(0, 0);
|
||||
debugIcon.makeGraphic(8, 8, 0xffff00ff);
|
||||
debugIcon.visible = false;
|
||||
debugIcon2.makeGraphic(8, 8, 0xff00ffff);
|
||||
debugIcon.visible = true;
|
||||
debugIcon2.visible = true;
|
||||
debugIcon.zIndex = 1000000;
|
||||
debugIcon2.zIndex = 1000000;
|
||||
#end
|
||||
|
||||
// Apply position and z-index.
|
||||
|
@ -265,20 +299,25 @@ class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScripte
|
|||
case BF:
|
||||
this.characters.set("bf", character);
|
||||
charData = _data.characters.bf;
|
||||
character.flipX = !character.flipX;
|
||||
// flip offsets if flipX
|
||||
character.flipX = !character.getDataFlipX();
|
||||
character.initHealthIcon(false);
|
||||
case GF:
|
||||
this.characters.set("gf", character);
|
||||
charData = _data.characters.gf;
|
||||
character.flipX = character.getDataFlipX();
|
||||
case DAD:
|
||||
this.characters.set("dad", character);
|
||||
charData = _data.characters.dad;
|
||||
// flip offsets if flipX
|
||||
character.flipX = character.getDataFlipX();
|
||||
character.initHealthIcon(true);
|
||||
default:
|
||||
this.characters.set(character.characterId, character);
|
||||
}
|
||||
|
||||
// Reset the character before adding it to the stage.
|
||||
// This ensures positioning is based on the idle animation.
|
||||
character.resetCharacter(true);
|
||||
|
||||
if (charData != null)
|
||||
{
|
||||
character.zIndex = charData.zIndex;
|
||||
|
@ -289,17 +328,28 @@ class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScripte
|
|||
character.x = charData.position[0] - character.characterOrigin.x + character.globalOffsets[0];
|
||||
character.y = charData.position[1] - character.characterOrigin.y + character.globalOffsets[1];
|
||||
|
||||
character.originalPosition.x = character.x;
|
||||
character.originalPosition.y = character.y;
|
||||
|
||||
character.cameraFocusPoint.x += charData.cameraOffsets[0];
|
||||
character.cameraFocusPoint.y += charData.cameraOffsets[1];
|
||||
|
||||
#if debug
|
||||
// Draw the debug icon at the character's feet.
|
||||
debugIcon.x = charData.position[0];
|
||||
debugIcon.y = charData.position[1];
|
||||
if (charType == BF || charType == DAD)
|
||||
{
|
||||
debugIcon.x = charData.position[0];
|
||||
debugIcon.y = charData.position[1];
|
||||
debugIcon2.x = character.x;
|
||||
debugIcon2.y = character.y;
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
// Add the character to the scene.
|
||||
this.add(character);
|
||||
this.add(debugIcon);
|
||||
this.add(debugIcon2);
|
||||
}
|
||||
|
||||
public inline function getGirlfriendPosition():FlxPoint
|
||||
|
|
77
source/funkin/util/BezierUtil.hx
Normal file
77
source/funkin/util/BezierUtil.hx
Normal file
|
@ -0,0 +1,77 @@
|
|||
package funkin.util;
|
||||
|
||||
import flixel.math.FlxPoint;
|
||||
|
||||
class BezierUtil {
|
||||
/**
|
||||
* Linearly interpolate between two values.
|
||||
* Depending on p, 0 = a, 1 = b, 0.5 = halfway between a and b.
|
||||
*/
|
||||
static inline function mix2(p:Float, a:Float, b:Float):Float {
|
||||
return a * (1 - p) + (b * p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Linearly interpolate between three values.
|
||||
* Depending on p, 0 = a, 0.5 = b, 1 = c, 0.25 = halfway between a and c, etc.
|
||||
*/
|
||||
static inline function mix3(p:Float, a:Float, b:Float, c:Float):Float {
|
||||
return mix2(p, mix2(p, a, b), mix2(p, b, c));
|
||||
}
|
||||
|
||||
static inline function mix4(p:Float, a:Float, b:Float, c:Float, d:Float):Float {
|
||||
return mix2(p, mix3(p, a, b, c), mix3(p, b, c, d));
|
||||
}
|
||||
|
||||
static inline function mix5(p:Float, a:Float, b:Float, c:Float, d:Float, e:Float):Float {
|
||||
return mix2(p, mix4(p, a, b, c, d), mix4(p, b, c, d, e));
|
||||
}
|
||||
|
||||
static inline function mix6(p:Float, a:Float, b:Float, c:Float, d:Float, e:Float, f:Float):Float {
|
||||
return mix2(p, mix5(p, a, b, c, d, e), mix5(p, b, c, d, e, f));
|
||||
}
|
||||
|
||||
/**
|
||||
* A bezier curve with two points.
|
||||
* This is really just linear interpolation but whatever.
|
||||
*/
|
||||
public static function bezier2(p:Float, a:FlxPoint, b:FlxPoint):FlxPoint {
|
||||
return new FlxPoint(mix2(p, a.x, b.x), mix2(p, a.y, b.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* A bezier curve with three points.
|
||||
* @param p The percentage of the way through the curve.
|
||||
* @param a The start point.
|
||||
* @param b The control point.
|
||||
* @param c The end point.
|
||||
*/
|
||||
public static function bezier3(p:Float, a:FlxPoint, b:FlxPoint, c:FlxPoint):FlxPoint {
|
||||
return new FlxPoint(mix3(p, a.x, b.x, c.x), mix3(p, a.y, b.y, c.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* A bezier curve with four points.
|
||||
* @param p The percentage of the way through the curve.
|
||||
* @param a The start point.
|
||||
* @param b The first control point.
|
||||
* @param c The second control point.
|
||||
* @param d The end point.
|
||||
*/
|
||||
public static function bezier4(p:Float, a:FlxPoint, b:FlxPoint, c:FlxPoint, d:FlxPoint):FlxPoint {
|
||||
return new FlxPoint(mix4(p, a.x, b.x, c.x, d.x), mix4(p, a.y, b.y, c.y, d.y));
|
||||
}
|
||||
|
||||
/**
|
||||
* A bezier curve with four points.
|
||||
* @param p The percentage of the way through the curve.
|
||||
* @param a The start point.
|
||||
* @param b The first control point.
|
||||
* @param c The second control point.
|
||||
* @param c The third control point.
|
||||
* @param d The end point.
|
||||
*/
|
||||
public static function bezier5(p:Float, a:FlxPoint, b:FlxPoint, c:FlxPoint, d:FlxPoint, e:FlxPoint):FlxPoint {
|
||||
return new FlxPoint(mix5(p, a.x, b.x, c.x, d.x, e.x), mix5(p, a.y, b.y, c.y, d.y, e.y));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue