1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2025-03-23 02:19:46 +00:00

Stable, only position adjustments needed.

This commit is contained in:
Eric Myllyoja 2022-03-25 22:30:37 -04:00
parent 386e8db830
commit 3634346072
26 changed files with 998 additions and 356 deletions

View file

@ -25,6 +25,7 @@ import funkin.freeplayStuff.BGScrollingText;
import funkin.freeplayStuff.DJBoyfriend;
import funkin.freeplayStuff.FreeplayScore;
import funkin.freeplayStuff.SongMenuItem;
import funkin.play.HealthIcon;
import lime.app.Future;
import lime.utils.Assets;
import funkin.shaderslmfao.AngleMask;
@ -295,7 +296,7 @@ class FreeplayState extends MusicBeatSubstate
// grpSongs.add(songText);
var icon:HealthIcon = new HealthIcon(songs[i].songCharacter);
icon.sprTracker = songText;
// icon.sprTracker = songText;
// using a FlxGroup is too much fuss!
iconArray.push(icon);

View file

@ -1,88 +0,0 @@
package funkin;
import flixel.FlxSprite;
import openfl.utils.Assets;
import funkin.play.PlayState;
using StringTools;
class HealthIcon extends FlxSprite
{
/**
* Used for FreeplayState! If you use it elsewhere, prob gonna annoying
*/
public var sprTracker:FlxSprite;
public var char:String = '';
var isPlayer:Bool = false;
public function new(char:String = 'bf', isPlayer:Bool = false)
{
super();
this.isPlayer = isPlayer;
antialiasing = true;
changeIcon(char);
scrollFactor.set();
}
public var isOldIcon:Bool = false;
public function swapOldIcon():Void
{
isOldIcon = !isOldIcon;
if (isOldIcon)
changeIcon('bf-old');
else
changeIcon(PlayState.currentSong.player1);
}
var pixelArrayFunny:Array<String> = CoolUtil.coolTextFile(Paths.file('images/icons/pixelIcons.txt'));
public function changeIcon(newChar:String):Void
{
if (newChar != 'bf-pixel' && newChar != 'bf-old')
newChar = newChar.split('-')[0].trim();
if (!Assets.exists(Paths.image('icons/icon-' + newChar)))
{
FlxG.log.warn('No icon with data: $newChar : using default placeholder face instead!');
newChar = "face";
}
if (newChar != char)
{
if (animation.getByName(newChar) == null)
{
var imgSize:Int = 150;
if (newChar.endsWith('pixel') || pixelArrayFunny.contains(newChar))
imgSize = 32;
loadGraphic(Paths.image('icons/icon-' + newChar), true, imgSize, imgSize);
animation.add(newChar, [0, 1], 0, false, isPlayer);
}
animation.play(newChar);
char = newChar;
if (newChar.endsWith('pixel') || pixelArrayFunny.contains(newChar))
antialiasing = false;
else
antialiasing = true;
setGraphicSize(150);
updateHitbox();
}
}
override function update(elapsed:Float)
{
super.update(elapsed);
if (sprTracker != null)
setPosition(sprTracker.x + sprTracker.width + 10, sprTracker.y - 30);
}
}

View file

@ -46,9 +46,6 @@ class MusicBeatState extends FlxUIState
{
super.create();
if (transIn != null)
trace('reg ' + transIn.region);
createWatermarkText();
}

View file

@ -189,8 +189,11 @@ class SongLoad
noteStuff[sectionIndex].sectionNotes[noteIndex].strumTime = arrayDipshit[0];
noteStuff[sectionIndex].sectionNotes[noteIndex].noteData = arrayDipshit[1];
noteStuff[sectionIndex].sectionNotes[noteIndex].sustainLength = arrayDipshit[2];
noteStuff[sectionIndex].sectionNotes[noteIndex].altNote = arrayDipshit[3];
if (arrayDipshit.length >= 5)
if (arrayDipshit.length > 3)
{
noteStuff[sectionIndex].sectionNotes[noteIndex].altNote = arrayDipshit[3];
}
if (arrayDipshit.length > 4)
{
noteStuff[sectionIndex].sectionNotes[noteIndex].noteKind = arrayDipshit[4];
}

View file

@ -116,8 +116,6 @@ class StoryMenuState extends MusicBeatState
grpLocks = new FlxTypedGroup<FlxSprite>();
add(grpLocks);
trace("Line 70");
#if discord_rpc
// Updating Discord Rich Presence
DiscordClient.changePresence("In the Menus", null);
@ -147,8 +145,6 @@ class StoryMenuState extends MusicBeatState
}
}
trace("Line 96");
for (char in 0...3)
{
var weekCharacterThing:MenuCharacter = new MenuCharacter((FlxG.width * 0.25) * (1 + char) - 150, weekCharacters[curWeek][char]);
@ -180,8 +176,6 @@ class StoryMenuState extends MusicBeatState
difficultySelectors = new FlxGroup();
add(difficultySelectors);
trace("Line 124");
leftArrow = new FlxSprite(grpWeekText.members[0].x + grpWeekText.members[0].width + 10, grpWeekText.members[0].y + 10);
leftArrow.frames = ui_tex;
leftArrow.animation.addByPrefix('idle', "arrow left");
@ -206,8 +200,6 @@ class StoryMenuState extends MusicBeatState
rightArrow.animation.play('idle');
difficultySelectors.add(rightArrow);
trace("Line 150");
add(yellowBG);
add(grpWeekCharacters);
@ -222,8 +214,6 @@ class StoryMenuState extends MusicBeatState
updateText();
trace("Line 165");
super.create();
}

View file

@ -189,8 +189,6 @@ class TitleState extends MusicBeatState
gfDance.antialiasing = true;
add(gfDance);
trace('MACRO TEST: ${gfDance.zIndex}');
// alphaShader.shader.funnyShit.input = gfDance.pixels; // old shit
logoBl.shader = alphaShader.shader;

View file

@ -0,0 +1,78 @@
package funkin.api.newgrounds;
import flixel.util.FlxSignal;
import flixel.util.FlxTimer;
import lime.app.Application;
import openfl.display.Stage;
#if newgrounds
import io.newgrounds.NG;
import io.newgrounds.NGLite;
import io.newgrounds.components.ScoreBoardComponent.Period;
import io.newgrounds.objects.Error;
import io.newgrounds.objects.Medal;
import io.newgrounds.objects.Score;
import io.newgrounds.objects.ScoreBoard;
import io.newgrounds.objects.events.Response;
import io.newgrounds.objects.events.Result.GetCurrentVersionResult;
import io.newgrounds.objects.events.Result.GetVersionResult;
#end
using StringTools;
/**
* Contains any script functions which should be BLOCKED from use by modded scripts.
*/
class NGUnsafe
{
static public function logEvent(event:String)
{
#if newgrounds
NG.core.calls.event.logEvent(event).send();
trace('should have logged: ' + event);
#else
#if debug
trace('event:$event - not logged, missing NG.io lib');
#end
#end
}
static public function unlockMedal(id:Int)
{
#if newgrounds
if (isLoggedIn)
{
var medal = NG.core.medals.get(id);
if (!medal.unlocked)
medal.sendUnlock();
}
#else
#if debug
trace('medal:$id - not unlocked, missing NG.io lib');
#end
#end
}
static public function postScore(score:Int = 0, song:String)
{
#if newgrounds
if (isLoggedIn)
{
for (id in NG.core.scoreBoards.keys())
{
var board = NG.core.scoreBoards.get(id);
if (song == board.name)
{
board.postScore(score, "Uhh meow?");
}
// trace('loaded scoreboard id:$id, name:${board.name}');
}
}
#else
#if debug
trace('Song:$song, Score:$score - not posted, missing NG.io lib');
#end
#end
}
}

View file

@ -0,0 +1,260 @@
package funkin.api.newgrounds;
import flixel.util.FlxSignal;
import flixel.util.FlxTimer;
import lime.app.Application;
import openfl.display.Stage;
#if newgrounds
import io.newgrounds.NG;
import io.newgrounds.NGLite;
import io.newgrounds.components.ScoreBoardComponent.Period;
import io.newgrounds.objects.Error;
import io.newgrounds.objects.Medal;
import io.newgrounds.objects.Score;
import io.newgrounds.objects.ScoreBoard;
import io.newgrounds.objects.events.Response;
import io.newgrounds.objects.events.Result.GetCurrentVersionResult;
import io.newgrounds.objects.events.Result.GetVersionResult;
#end
using StringTools;
/**
* Contains any script functions which should be ALLOWD for use by modded scripts.
*/
class NGUtil
{
#if newgrounds
/**
* True, if the saved sessionId was used in the initial login, and failed to connect.
* Used in MainMenuState to show a popup to establish a new connection
*/
public static var savedSessionFailed(default, null):Bool = false;
public static var scoreboardsLoaded:Bool = false;
public static var isLoggedIn(get, never):Bool;
inline static function get_isLoggedIn()
{
return NG.core != null && NG.core.loggedIn;
}
public static var scoreboardArray:Array<Score> = [];
public static var ngDataLoaded(default, null):FlxSignal = new FlxSignal();
public static var ngScoresLoaded(default, null):FlxSignal = new FlxSignal();
public static var GAME_VER:String = "";
static public function checkVersion(callback:String->Void)
{
trace('checking NG.io version');
GAME_VER = "v" + Application.current.meta.get('version');
NG.core.calls.app.getCurrentVersion(GAME_VER).addDataHandler(function(response)
{
GAME_VER = response.result.data.currentVersion;
trace('CURRENT NG VERSION: ' + GAME_VER);
callback(GAME_VER);
}).send();
}
static public function init()
{
var api = APIStuff.API;
if (api == null || api.length == 0)
{
trace("Missing Newgrounds API key, aborting connection");
return;
}
trace("connecting to newgrounds");
#if NG_FORCE_EXPIRED_SESSION
var sessionId:String = "fake_session_id";
function onSessionFail(error:Error)
{
trace("Forcing an expired saved session. " + "To disable, comment out NG_FORCE_EXPIRED_SESSION in Project.xml");
savedSessionFailed = true;
}
#else
var sessionId:String = NGLite.getSessionId();
if (sessionId != null)
trace("found web session id");
#if (debug)
if (sessionId == null && APIStuff.SESSION != null)
{
trace("using debug session id");
sessionId = APIStuff.SESSION;
}
#end
var onSessionFail:Error->Void = null;
if (sessionId == null && FlxG.save.data.sessionId != null)
{
trace("using stored session id");
sessionId = FlxG.save.data.sessionId;
onSessionFail = function(error) savedSessionFailed = true;
}
#end
NG.create(api, sessionId, #if NG_DEBUG true #else false #end, onSessionFail);
#if NG_VERBOSE
NG.core.verbose = true;
#end
// Set the encryption cipher/format to RC4/Base64. AES128 and Hex are not implemented yet
NG.core.initEncryption(APIStuff.EncKey); // Found in you NG project view
if (NG.core.attemptingLogin)
{
/* a session_id was found in the loadervars, this means the user is playing on newgrounds.com
* and we should login shortly. lets wait for that to happen
*/
trace("attempting login");
NG.core.onLogin.add(onNGLogin);
}
// GK: taking out auto login, adding a login button to the main menu
// else
// {
// /* They are NOT playing on newgrounds.com, no session id was found. We must start one manually, if we want to.
// * Note: This will cause a new browser window to pop up where they can log in to newgrounds
// */
// NG.core.requestLogin(onNGLogin);
// }
}
/**
* Attempts to log in to newgrounds by requesting a new session ID, only call if no session ID was found automatically
* @param popupLauncher The function to call to open the login url, must be inside
* a user input event or the popup blocker will block it.
* @param onComplete A callback with the result of the connection.
*/
static public function login(?popupLauncher:(Void->Void)->Void, onComplete:ConnectionResult->Void)
{
trace("Logging in manually");
var onPending:Void->Void = null;
if (popupLauncher != null)
{
onPending = function() popupLauncher(NG.core.openPassportUrl);
}
var onSuccess:Void->Void = onNGLogin;
var onFail:Error->Void = null;
var onCancel:Void->Void = null;
if (onComplete != null)
{
onSuccess = function()
{
onNGLogin();
onComplete(Success);
}
onFail = function(e) onComplete(Fail(e.message));
onCancel = function() onComplete(Cancelled);
}
NG.core.requestLogin(onSuccess, onPending, onFail, onCancel);
}
inline static public function cancelLogin():Void
{
NG.core.cancelLoginRequest();
}
static function onNGLogin():Void
{
trace('logged in! user:${NG.core.user.name}');
FlxG.save.data.sessionId = NG.core.sessionId;
FlxG.save.flush();
// Load medals then call onNGMedalFetch()
NG.core.requestMedals(onNGMedalFetch);
// Load Scoreboards hten call onNGBoardsFetch()
NG.core.requestScoreBoards(onNGBoardsFetch);
ngDataLoaded.dispatch();
}
static public function logout()
{
NG.core.logOut();
FlxG.save.data.sessionId = null;
FlxG.save.flush();
}
// --- MEDALS
static function onNGMedalFetch():Void
{
/*
// Reading medal info
for (id in NG.core.medals.keys())
{
var medal = NG.core.medals.get(id);
trace('loaded medal id:$id, name:${medal.name}, description:${medal.description}');
}
// Unlocking medals
var unlockingMedal = NG.core.medals.get(54352);// medal ids are listed in your NG project viewer
if (!unlockingMedal.unlocked)
unlockingMedal.sendUnlock();
*/
}
// --- SCOREBOARDS
static function onNGBoardsFetch():Void
{
/*
// Reading medal info
for (id in NG.core.scoreBoards.keys())
{
var board = NG.core.scoreBoards.get(id);
trace('loaded scoreboard id:$id, name:${board.name}');
}
*/
// var board = NG.core.scoreBoards.get(8004);// ID found in NG project view
// Posting a score thats OVER 9000!
// board.postScore(FlxG.random.int(0, 1000));
// --- To view the scores you first need to select the range of scores you want to see ---
// add an update listener so we know when we get the new scores
// board.onUpdate.add(onNGScoresFetch);
trace("shoulda got score by NOW!");
// board.requestScores(20);// get the best 10 scores ever logged
// more info on scores --- http://www.newgrounds.io/help/components/#scoreboard-getscores
}
static function onNGScoresFetch():Void
{
scoreboardsLoaded = true;
ngScoresLoaded.dispatch();
/*
for (score in NG.core.scoreBoards.get(8737).scores)
{
trace('score loaded user:${score.user.name}, score:${score.formatted_value}');
}
*/
// var board = NG.core.scoreBoards.get(8004);// ID found in NG project view
// board.postScore(HighScore.score);
// NGUtil.scoreboardArray = NG.core.scoreBoards.get(8004).scores;
}
#end
}
enum ConnectionResult
{
/** Log in successful */
Success;
/** Could not login */
Fail(msg:String);
/** User cancelled the login */
Cancelled;
}

View file

@ -0,0 +1,104 @@
package funkin.api.newgrounds;
#if newgrounds
import funkin.NGio;
import funkin.ui.Prompt;
class NgPrompt extends Prompt
{
public function new(text:String, style:ButtonStyle = Yes_No)
{
super(text, style);
}
static public function showLogin()
{
return showLoginPrompt(true);
}
static public function showSavedSessionFailed()
{
return showLoginPrompt(false);
}
static function showLoginPrompt(fromUi:Bool)
{
var prompt = new NgPrompt("Talking to server...", None);
prompt.openCallback = NGUtil.login.bind(function popupLauncher(openPassportUrl)
{
var choiceMsg = fromUi ? #if web "Log in to Newgrounds?" #else null #end // User-input needed to allow popups
: "Your session has expired.\n Please login again.";
if (choiceMsg != null)
{
prompt.setText(choiceMsg);
prompt.setButtons(Yes_No);
#if web
prompt.buttons.getItem("yes").fireInstantly = true;
#end
prompt.onYes = function()
{
prompt.setText("Connecting..." #if web + "\n(check your popup blocker)" #end);
prompt.setButtons(None);
openPassportUrl();
};
prompt.onNo = function()
{
prompt.close();
prompt = null;
NGio.cancelLogin();
};
}
else
{
prompt.setText("Connecting...");
openPassportUrl();
}
}, function onLoginComplete(result:ConnectionResult)
{
switch (result)
{
case Success:
{
prompt.setText("Login Successful");
prompt.setButtons(Ok);
prompt.onYes = prompt.close;
}
case Fail(msg):
{
trace("Login Error:" + msg);
prompt.setText("Login failed");
prompt.setButtons(Ok);
prompt.onYes = prompt.close;
}
case Cancelled:
{
if (prompt != null)
{
prompt.setText("Login cancelled by user");
prompt.setButtons(Ok);
prompt.onYes = prompt.close;
}
else
trace("Login cancelled via prompt");
}
}
});
return prompt;
}
static public function showLogout()
{
var user = io.newgrounds.NG.core.user.name;
var prompt = new NgPrompt('Log out of $user?', Yes_No);
prompt.onYes = function()
{
NGio.logout();
prompt.close();
};
prompt.onNo = prompt.close;
return prompt;
}
}
#end

View file

@ -0,0 +1,9 @@
# funkin.api.newgrounds
This package contains two main classes:
- `NGUtil` contains utility functions for interacting with the Newgrounds API.
- This includes any functions which scripts should be able to use,
such as retrieving achievement status.
- `NGUnsafe` contains sensitive utility functions for interacting with the Newgrounds API.
- This includes any functions which scripts should not be able to use,
such as writing high scores or posting achievements.

View file

@ -8,6 +8,7 @@ import funkin.audiovis.ABotVis;
import funkin.audiovis.PolygonSpectogram;
import funkin.audiovis.SpectogramSprite;
import flixel.FlxSprite;
import funkin.play.HealthIcon;
import flixel.addons.display.FlxGridOverlay;
import flixel.addons.transition.FlxTransitionableState;
import flixel.addons.ui.FlxInputText;
@ -705,7 +706,7 @@ class ChartingState extends MusicBeatState
{
if (FlxG.mouse.overlaps(leftIcon))
{
if (leftIcon.char == _song.player1)
if (leftIcon.characterId == _song.player1)
{
p1Muted = !p1Muted;
leftIcon.animation.curAnim.curFrame = p1Muted ? 1 : 0;
@ -727,7 +728,7 @@ class ChartingState extends MusicBeatState
// sloppy copypaste lol deal with it!
if (FlxG.mouse.overlaps(rightIcon))
{
if (rightIcon.char == _song.player1)
if (rightIcon.characterId == _song.player1)
{
p1Muted = !p1Muted;
rightIcon.animation.curAnim.curFrame = p1Muted ? 1 : 0;
@ -1129,16 +1130,16 @@ class ChartingState extends MusicBeatState
{
if (check_mustHitSection.checked)
{
leftIcon.changeIcon(_song.player1);
rightIcon.changeIcon(_song.player2);
leftIcon.characterId = (_song.player1);
rightIcon.characterId = (_song.player2);
leftIcon.animation.curAnim.curFrame = p1Muted ? 1 : 0;
rightIcon.animation.curAnim.curFrame = p2Muted ? 1 : 0;
}
else
{
leftIcon.changeIcon(_song.player2);
rightIcon.changeIcon(_song.player1);
leftIcon.characterId = (_song.player2);
rightIcon.characterId = (_song.player1);
leftIcon.animation.curAnim.curFrame = p2Muted ? 1 : 0;
rightIcon.animation.curAnim.curFrame = p1Muted ? 1 : 0;

View file

@ -136,6 +136,8 @@ class PolymodHandler
// Ensure script files have merge support.
output.addType("hscript", TextFileFormat.PLAINTEXT);
output.addType("hxs", TextFileFormat.PLAINTEXT);
output.addType("hxc", TextFileFormat.PLAINTEXT);
output.addType("hx", TextFileFormat.PLAINTEXT);
// You can specify the format of a specific file, with file extension.
// output.addFile("data/introText.txt", TextFileFormat.LINES)
@ -146,8 +148,8 @@ 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",
"songs" => "songs", "shared" => "", "tutorial" => "tutorial", "scripts" => "scripts", "week1" => "week1", "week2" => "week2",
"week3" => "week3", "week4" => "week4", "week5" => "week5", "week6" => "week6", "week7" => "week7", "week8" => "week8",
]
}
}

View file

@ -0,0 +1,346 @@
package funkin.play;
import funkin.play.character.CharacterData.CharacterDataParser;
import flixel.FlxSprite;
import openfl.utils.Assets;
/**
* This is a rework of the health icon with the following changes:
* - The health icon now owns its own state logic. It queries health and updates the sprite itself,
* rather than relying on PlayState to command it.
* - The health icon now supports animations.
* - The health icon will now search for a SparrowV2 (XML) spritesheet, and use that for rendering if it can.
* - If it can't find a spritesheet, it will the old format; a two-frame 300x150 image.
* - If the spritesheet is found, the health icon will attempt to load and use the following animations as appropriate:
* - `idle`, `winning`, `losing`, `toWinning`, `fromWinning`, `toLosing`, `fromLosing`
* - The health icon is now easier to control via scripts.
* - Set `autoUpdate` to false to prevent the health icon from changing its own animations.
* - Once `autoUpdate` is false, you can manually call `playAnimation()` to play a specific animation.
* - i.e. `PlayState.instance.iconP1.playAnimation("losing")`
* - Scripts can also utilize all functionality that a normal FlxSprite would have access to, such as adding supplimental animations.
* - i.e. `PlayState.instance.iconP1.animation.addByPrefix("jumpscare", "jumpscare", 24, false);`
* @author MasterEric
*/
class HealthIcon extends FlxSprite
{
/**
* The character this icon is representing.
* Setting this variable will automatically update the graphic.
*/
public var characterId(default, set):String;
/**
* Whether this health icon should automatically update its state based on the character's health.
* You can set this to false if you want to manually forc
*/
public var autoUpdate:Bool = true;
/**
* The player the health icon is attached to.
*/
var playerId:Int = 0;
/**
* Whether the sprite is pixel art or not.
* Calculated when loading an icon.
*/
var isPixel:Bool = false;
/**
* At this amount of health, play the Winning animation instead of the idle.
*/
static final WINNING_THRESHOLD = 0.8 * 2;
/**
* At this amount of health, play the Losing animation instead of the idle.
*/
static final LOSING_THRESHOLD = 0.2 * 2;
/**
* The maximum health of the player.
*/
static final MAXIMUM_HEALTH = 2;
/**
* The size of a non-pixel icon when using the legacy format.
* Remember, modern icons can be any size.
*/
static final LEGACY_ICON_SIZE = 150;
/**
* The size of a pixel icon when using the legacy format.
* Remember, modern icons can be any size.
*/
static final LEGACY_PIXEL_SIZE = 32;
public function new(char:String = 'bf', playerId:Int = 0)
{
super(0, 0);
this.playerId = playerId;
this.scrollFactor.set();
this.characterId = char;
this.antialiasing = !isPixel;
this.flipX = playerId == 0;
}
function set_characterId(value:String):String
{
if (value == characterId)
return value;
characterId = value;
loadCharacter(characterId);
return value;
}
/**
* Easter egg; press 9 in the PlayState to use the old player icon.
*/
public function toggleOldIcon():Void
{
if (characterId == 'bf-old')
{
characterId = PlayState.currentSong.player1;
}
else
{
characterId = 'bf-old';
}
}
/**
* Called by Flixel every frame. Includes logic to manage the currently playing animation.
*/
override function update(elapsed:Float):Void
{
super.update(elapsed);
if (PlayState.instance == null)
return;
// Auto-update the state of the icon based on the player's health.
if (autoUpdate)
{
switch (playerId)
{
case 0: // Boyfriend
updateHealthIcon(PlayState.instance.health);
case 1: // Dad
updateHealthIcon(MAXIMUM_HEALTH - PlayState.instance.health);
}
}
}
function updateHealthIcon(health:Float)
{
// We want to efficiently handle animation playback
// Here, we use the current animation name to track the current state
// of a simple state machine. Neat!
switch (getCurrentAnimation())
{
case IDLE:
if (health < LOSING_THRESHOLD)
playAnimation(TO_LOSING, LOSING);
else if (health > WINNING_THRESHOLD)
playAnimation(TO_WINNING, WINNING);
else
playAnimation(IDLE);
case WINNING:
if (health < WINNING_THRESHOLD)
playAnimation(FROM_WINNING, IDLE);
else
playAnimation(WINNING, IDLE);
case LOSING:
if (health > LOSING_THRESHOLD)
playAnimation(FROM_LOSING, IDLE);
else
playAnimation(LOSING, IDLE);
case TO_LOSING:
if (isAnimationFinished())
playAnimation(LOSING, IDLE);
case TO_WINNING:
if (isAnimationFinished())
playAnimation(WINNING, IDLE);
case FROM_LOSING | FROM_WINNING:
if (isAnimationFinished())
playAnimation(IDLE);
}
}
/**
* Load
* @param charId
*/
function loadAnimationNew(charId:String):Void
{
this.animation.addByPrefix(IDLE, IDLE, 24, true);
this.animation.addByPrefix(WINNING, WINNING, 24, true);
this.animation.addByPrefix(LOSING, LOSING, 24, true);
this.animation.addByPrefix(TO_WINNING, TO_WINNING, 24, true);
this.animation.addByPrefix(TO_LOSING, TO_LOSING, 24, true);
this.animation.addByPrefix(FROM_WINNING, FROM_WINNING, 24, true);
this.animation.addByPrefix(FROM_LOSING, FROM_LOSING, 24, true);
}
/**
* Load health icon animations using the legacy format.
* Simply assumes two icons, one on
* @param charId
*/
function loadAnimationOld(charId:String):Void
{
this.animation.add(IDLE, [0], 0, false, this.playerId == 0);
this.animation.add(LOSING, [1], 0, false, this.playerId == 0);
}
function correctCharacterId(charId:String):String
{
if (!Assets.exists(Paths.image('icons/icon-' + charId)))
{
FlxG.log.warn('No icon for character: $charId : using default placeholder face instead!');
return "face";
}
return charId;
}
function isNewSpritesheet(charId:String):Bool
{
return Assets.exists(Paths.xml('icons/icon-' + characterId));
}
function fetchIsPixel(charId:String):Bool
{
var charData = CharacterDataParser.fetchCharacterData(charId);
if (charData == null)
{
FlxG.log.warn('No character data found for character: $charId');
return false;
}
return charData.isPixel;
}
function loadCharacter(charId:String):Void
{
if (correctCharacterId(charId) != charId)
{
characterId = correctCharacterId(charId);
return;
}
isPixel = fetchIsPixel(charId);
if (isNewSpritesheet(charId))
{
frames = Paths.getSparrowAtlas('icons/icon-$charId');
loadAnimationNew(charId);
}
else
{
loadGraphic(Paths.image('icons/icon-$charId'), true, isPixel ? LEGACY_PIXEL_SIZE : LEGACY_ICON_SIZE,
isPixel ? LEGACY_PIXEL_SIZE : LEGACY_ICON_SIZE);
loadAnimationOld(charId);
}
}
/**
* @return Name of the current animation being played by this health icon.
*/
public function getCurrentAnimation():String
{
if (this.animation == null || this.animation.curAnim == null)
return "";
return this.animation.curAnim.name;
}
/**
* @return Whether this sprite posesses the given animation.
* Only true if the animation was successfully loaded from the XML.
*/
public function hasAnimation(id:String):Bool
{
if (this.animation == null)
return false;
return this.animation.getByName(id) != null;
}
/**
* @return Whether the current animation is in the finished state.
*/
public function isAnimationFinished():Bool
{
return this.animation.finished;
}
/**
* Plays the animation with the given name.
* @param name The name of the animation to play.
* @param fallback The fallback animation to play if the given animation is not found.
* @param restart Whether to forcibly restart the animation if it is already playing.
*/
public function playAnimation(name:String, fallback:String = null, restart = false):Void
{
// Attempt to play the animation
if (hasAnimation(name))
this.animation.play(name, restart, false, 0);
// Play the fallback animation if the requested animation was not found
if (fallback != null && hasAnimation(fallback))
this.animation.play(fallback, restart, false, 0);
// If we don't have an animation, we're done.
}
}
enum abstract HealthIconState(String) to String from String
{
/**
* Indicates the health icon is in the default animation.
* Plays as long as health is between 20% and 80%.
*/
var IDLE = "idle";
/**
* Indicates the health icon is playing the Winning animation.
* Plays as long as health is above 80%.
*/
var WINNING = "winning";
/**
* Indicates the health icon is playing the Losing animation.
* Plays as long as health is below 20%.
*/
var LOSING = "losing";
/**
* Indicates that the health icon is transitioning between `idle` and `winning`.
* The next animation will play once the current animation finishes.
*/
var TO_WINNING = "toWinning";
/**
* Indicates that the health icon is transitioning between `idle` and `losing`.
* The next animation will play once the current animation finishes.
*/
var TO_LOSING = "toLosing";
/**
* Indicates that the health icon is transitioning between `winning` and `idle`.
* The next animation will play once the current animation finishes.
*/
var FROM_WINNING = "fromWinning";
/**
* Indicates that the health icon is transitioning between `losing` and `idle`.
* The next animation will play once the current animation finishes.
*/
var FROM_LOSING = "fromLosing";
}

View file

@ -1,7 +1,6 @@
package funkin.play;
import funkin.play.character.BaseCharacter;
import flixel.addons.effects.FlxTrail;
import flixel.addons.transition.FlxTransitionableState;
import flixel.FlxCamera;
import flixel.FlxObject;
@ -29,6 +28,7 @@ import funkin.modding.module.ModuleHandler;
import funkin.Note;
import funkin.play.character.CharacterData;
import funkin.play.stage.Stage;
import funkin.play.HealthIcon;
import funkin.play.stage.StageData;
import funkin.play.Strumline.StrumlineArrow;
import funkin.play.Strumline.StrumlineStyle;
@ -143,7 +143,7 @@ class PlayState extends MusicBeatState implements IHook
* NOTE: This must be an FlxObject, not an FlxPoint, because it needs to be added to the scene.
* Once it's added to the scene, the camera can be configured to follow it.
*/
public var cameraFollowPoint:FlxObject = new FlxObject(0, 0, 1, 1);
public var cameraFollowPoint:FlxSprite = new FlxSprite(0, 0);
/**
* PRIVATE INSTANCE VARIABLES
@ -275,6 +275,10 @@ class PlayState extends MusicBeatState implements IHook
instance = this;
// TEMP: For testing
cameraFollowPoint.makeGraphic(8, 8, 0xFFFF00FF);
cameraFollowPoint.zIndex = 1000000;
// Reduce physics accuracy (who cares!!!) to improve animation quality.
FlxG.fixedTimestep = false;
@ -370,11 +374,11 @@ class PlayState extends MusicBeatState implements IHook
scoreText.scrollFactor.set();
add(scoreText);
iconP1 = new HealthIcon(currentSong.player1, true);
iconP1 = new HealthIcon(currentSong.player1, 0);
iconP1.y = healthBar.y - (iconP1.height / 2);
add(iconP1);
iconP2 = new HealthIcon(currentSong.player2, false);
iconP2 = new HealthIcon(currentSong.player2, 1);
iconP2.y = healthBar.y - (iconP2.height / 2);
add(iconP2);
@ -536,51 +540,17 @@ class PlayState extends MusicBeatState implements IHook
if (dad != null)
{
dad.characterType = CharacterType.DAD;
cameraFollowPoint.setPosition(dad.getGraphicMidpoint().x, dad.getGraphicMidpoint().y);
cameraFollowPoint.setPosition(dad.cameraFocusPoint.x, dad.cameraFocusPoint.y);
}
switch (currentSong.player2)
{
case 'gf':
var gfPoint:FlxPoint = currentStage.getGirlfriendPosition();
dad.setPosition(gfPoint.x, gfPoint.y);
// girlfriend.visible = false;
if (isStoryMode)
{
cameraFollowPoint.x += 600;
tweenCamIn();
}
case "spooky":
dad.y += 200;
case "monster":
dad.y += 100;
case 'monster-christmas':
dad.y += 130;
case 'dad':
cameraFollowPoint.x += 400;
case 'pico':
cameraFollowPoint.x += 600;
dad.y += 300;
case 'parents-christmas':
dad.x -= 500;
case 'senpai' | 'senpai-angry':
dad.x += 150;
dad.y += 360;
cameraFollowPoint.setPosition(dad.getGraphicMidpoint().x + 300, dad.getGraphicMidpoint().y);
case 'spirit':
dad.x -= 150;
dad.y += 100;
cameraFollowPoint.setPosition(dad.getGraphicMidpoint().x + 300, dad.getGraphicMidpoint().y);
case 'tankman':
dad.y += 180;
}
if (currentSong.player1 == "pico")
{
dad.x -= 100;
dad.y -= 100;
}
//
@ -596,11 +566,6 @@ class PlayState extends MusicBeatState implements IHook
// REPOSITIONING PER STAGE
switch (currentStageId)
{
case 'schoolEvil':
var evilTrail = new FlxTrail(dad, null, 4, 24, 0.3, 0.069);
// Go behind Spirit.
evilTrail.zIndex = 190;
add(evilTrail);
case "tank":
girlfriend.y += 10;
girlfriend.x -= 30;
@ -1081,7 +1046,7 @@ class PlayState extends MusicBeatState implements IHook
FlxG.switchState(new funkin.ui.animDebugShit.DebugBoundingState());
if (FlxG.keys.justPressed.NINE)
iconP1.swapOldIcon();
iconP1.toggleOldIcon();
iconP1.setGraphicSize(Std.int(CoolUtil.coolLerp(iconP1.width, 150, 0.15)));
iconP2.setGraphicSize(Std.int(CoolUtil.coolLerp(iconP2.width, 150, 0.15)));
@ -1097,16 +1062,6 @@ class PlayState extends MusicBeatState implements IHook
if (health > 2)
health = 2;
if (healthBar.percent < 20)
iconP1.animation.curAnim.curFrame = 1;
else
iconP1.animation.curAnim.curFrame = 0;
if (healthBar.percent > 80)
iconP2.animation.curAnim.curFrame = 1;
else
iconP2.animation.curAnim.curFrame = 0;
#if debug
if (FlxG.keys.justPressed.ONE)
endSong();
@ -1121,7 +1076,7 @@ class PlayState extends MusicBeatState implements IHook
{
cameraRightSide = SongLoad.getSong()[Std.int(curStep / 16)].mustHitSection;
cameraMovement();
controlCamera();
}
if (camZooming)
@ -1195,7 +1150,7 @@ class PlayState extends MusicBeatState implements IHook
inactiveNotes.shift();
}
if (generatedMusic)
if (generatedMusic && playerStrumline != null)
{
activeNotes.forEachAlive(function(daNote:Note)
{
@ -1522,57 +1477,35 @@ class PlayState extends MusicBeatState implements IHook
comboPopUps.displayCombo(combo);
}
function cameraMovement()
function controlCamera()
{
if (currentStage == null)
return;
if (cameraFollowPoint.x != currentStage.getDad().getMidpoint().x + 150 && !cameraRightSide)
var isFocusedOnDad = cameraFollowPoint.x == currentStage.getDad().cameraFocusPoint.x;
var isFocusedOnBF = cameraFollowPoint.x == currentStage.getBoyfriend().cameraFocusPoint.x;
if (cameraRightSide && !isFocusedOnBF)
{
cameraFollowPoint.setPosition(currentStage.getDad().getMidpoint().x + 150, currentStage.getDad().getMidpoint().y - 100);
// camFollow.setPosition(lucky.getMidpoint().x - 120, lucky.getMidpoint().y + 210);
switch (currentStage.getDad().characterId)
{
case 'mom':
cameraFollowPoint.y = currentStage.getDad().getMidpoint().y;
case 'senpai' | 'senpai-angry':
cameraFollowPoint.y = currentStage.getDad().getMidpoint().y - 430;
cameraFollowPoint.x = currentStage.getDad().getMidpoint().x - 100;
}
if (currentStage.getDad().characterId == 'mom')
vocals.volume = 1;
if (currentSong.song.toLowerCase() == 'tutorial')
tweenCamIn();
}
if (cameraRightSide && cameraFollowPoint.x != currentStage.getBoyfriend().getMidpoint().x - 100)
{
cameraFollowPoint.setPosition(currentStage.getBoyfriend().getMidpoint().x - 100, currentStage.getBoyfriend().getMidpoint().y - 100);
switch (currentStageId)
{
case 'limo':
cameraFollowPoint.x = currentStage.getBoyfriend().getMidpoint().x - 300;
case 'mall':
cameraFollowPoint.y = currentStage.getBoyfriend().getMidpoint().y - 200;
case 'school' | 'schoolEvil':
cameraFollowPoint.x = currentStage.getBoyfriend().getMidpoint().x - 200;
cameraFollowPoint.y = currentStage.getBoyfriend().getMidpoint().y - 200;
}
// Focus the camera on the player.
cameraFollowPoint.setPosition(currentStage.getBoyfriend().cameraFocusPoint.x, currentStage.getBoyfriend().cameraFocusPoint.y);
// TODO: Un-hardcode this.
if (currentSong.song.toLowerCase() == 'tutorial')
FlxTween.tween(FlxG.camera, {zoom: 1 * FlxCamera.defaultZoom}, (Conductor.stepCrochet * 4 / 1000), {ease: FlxEase.elasticInOut});
}
}
else if (!cameraRightSide && !isFocusedOnDad)
{
// Focus the camera on the opponent.
cameraFollowPoint.setPosition(currentStage.getDad().cameraFocusPoint.x, currentStage.getDad().cameraFocusPoint.y);
public var test:(PlayState) -> Void = function(instance:PlayState)
{
trace('test');
trace(instance.currentStageId);
};
// TODO: Un-hardcode this stuff.
if (currentStage.getDad().characterId == 'mom')
vocals.volume = 1;
if (currentSong.song.toLowerCase() == 'tutorial')
tweenCamIn();
}
}
public function keyShit(test:Bool):Void
{
@ -1684,6 +1617,8 @@ class PlayState extends MusicBeatState implements IHook
for (keyId => isPressed in pressArray)
{
if (playerStrumline == null)
continue;
var arrow:StrumlineArrow = PlayState.instance.playerStrumline.getArrow(keyId);
if (isPressed && arrow.animation.curAnim.name != 'confirm')
@ -2072,7 +2007,6 @@ class PlayState extends MusicBeatState implements IHook
public function refresh()
{
sort(SortUtil.byZIndex, FlxSort.ASCENDING);
trace('Stage sorted by z-index');
}
/**

View file

@ -2,14 +2,13 @@ package funkin.play;
import flixel.util.FlxTimer;
import flixel.tweens.FlxTween;
import flixel.tweens.FlxTween;
import flixel.tweens.FlxEase;
import flixel.util.FlxColor;
import flixel.FlxSprite;
/**
* Static methods for playing cutscenes in the PlayState.
* TODO: Softcode this shit!!!!!1!
* TODO: Un-hardcode this shit!!!!!1!
*/
class VanillaCutscenes
{
@ -64,7 +63,7 @@ class VanillaCutscenes
@:privateAccess
PlayState.instance.startCountdown();
@:privateAccess
PlayState.instance.cameraMovement();
PlayState.instance.controlCamera();
}
public static function playHorrorStartCutscene()

View file

@ -1,5 +1,6 @@
package funkin.play.character;
import funkin.play.character.CharacterData.CharacterDataParser;
import flixel.math.FlxPoint;
import funkin.modding.events.ScriptEvent;
import funkin.modding.events.ScriptEvent.UpdateScriptEvent;
@ -40,7 +41,8 @@ class BaseCharacter extends Bopper
final singTimeCrochet:Float;
/**
* The x and y position to subtract from the stage's X value to get the character's proper rendering position.
* Character position in stage file is at the bottom center of the character.
* Position from stage file - character origin is at the top left corner of the character.
*/
public var characterOrigin(get, null):FlxPoint;
@ -48,15 +50,64 @@ class BaseCharacter extends Bopper
{
var xPos = (width / 2); // Horizontal center
var yPos = (height); // Vertical bottom
trace('Origin: ${characterId} (${xPos}, ${yPos})');
return new FlxPoint(xPos, yPos);
}
/**
* Returns the point the camera should focus on.
* Should be approximately centered on the character, and should not move based on the current animation.
*
* Set the position of this rather than reassigning it, so that anything referencing it will not be affected.
*/
public var cameraFocusPoint(default, null):FlxPoint = new FlxPoint(0, 0);
override function set_animOffset(value:Array<Float>)
{
if (animOffset == null)
animOffset = [0, 0];
if (animOffset == value)
return value;
var xDiff = animOffset[0] - value[0];
var yDiff = animOffset[1] - value[1];
// Call the super function so that camera focus point is not affected.
super.set_x(this.x + xDiff);
super.set_y(this.y + yDiff);
return animOffset = value;
}
/**
* If the x position changes, other than via changing the animation offset,
* then we need to update the camera focus point.
*/
override function set_x(value:Float):Float
{
if (value == this.x)
return value;
var xDiff = value - this.x;
this.cameraFocusPoint.x += xDiff;
return super.set_x(value);
}
/**
* If the y position changes, other than via changing the animation offset,
* then we need to update the camera focus point.
*/
override function set_y(value:Float):Float
{
if (value == this.y)
return value;
var yDiff = value - this.y;
this.cameraFocusPoint.y += yDiff;
return super.set_y(value);
}
public function new(id:String)
{
super();
@ -74,6 +125,26 @@ class BaseCharacter extends Bopper
}
}
/**
* Set the sprite scale to the appropriate value.
* @param scale
*/
function setScale(scale:Null<Float>):Void
{
if (scale == null)
scale = 1.0;
this.scale.x = scale;
this.scale.y = scale;
this.updateHitbox();
}
override function onCreate(event:ScriptEvent):Void
{
this.cameraFocusPoint = new FlxPoint(this.x + this.width / 2 + _data.cameraOffset[0], this.y + this.height / 2 + _data.cameraOffset[0]);
super.onCreate(event);
}
public override function onUpdate(event:UpdateScriptEvent):Void
{
super.onUpdate(event);
@ -112,7 +183,7 @@ class BaseCharacter extends Bopper
FlxG.watch.addQuick('singTimeMs-${characterId}', singTimeMs);
if (holdTimer > singTimeMs && shouldStopSinging)
{
trace('holdTimer reached ${holdTimer}sec (> ${singTimeMs}), stopping sing animation');
// trace('holdTimer reached ${holdTimer}sec (> ${singTimeMs}), stopping sing animation');
holdTimer = 0;
dance(true);
}
@ -229,8 +300,6 @@ class BaseCharacter extends Bopper
{
super.onNoteHit(event);
trace('HIT NOTE: ${event.note.data.dir} : ${event.note.isSustainNote}');
holdTimer = 0;
if (event.note.mustPress && characterType == BF)

View file

@ -135,7 +135,11 @@ class CharacterDataParser
trace(' Failed to instantiate scripted character: ${charCls}');
continue;
}
characterScriptedClass.set(character.characterId, charCls);
else
{
trace(' Successfully instantiated scripted character: ${charCls}');
characterScriptedClass.set(character.characterId, charCls);
}
}
trace(' Successfully loaded ${Lambda.count(characterCache)} stages.');
@ -290,6 +294,7 @@ class CharacterDataParser
static final DEFAULT_RENDERTYPE:CharacterRenderType = CharacterRenderType.SPARROW;
static final DEFAULT_SCALE:Float = 1;
static final DEFAULT_SCROLL:Array<Float> = [0, 0];
static final DEFAULT_CAMERAOFFSET:Array<Float> = [0, 0];
static final DEFAULT_STARTINGANIM:String = "idle";
/**
@ -303,7 +308,7 @@ class CharacterDataParser
{
if (input == null)
{
trace('[CHARDATA] ERROR: Could not parse character data for "${id}".');
// trace('[CHARDATA] ERROR: Could not parse character data for "${id}".');
return null;
}
@ -346,6 +351,11 @@ class CharacterDataParser
input.scale = DEFAULT_SCALE;
}
if (input.cameraOffset == null)
{
input.cameraOffset = DEFAULT_CAMERAOFFSET;
}
if (input.isPixel == null)
{
input.isPixel = DEFAULT_ISPIXEL;
@ -451,11 +461,18 @@ typedef CharacterData =
var assetPath:String;
/**
* Either the scale of the graphic as a float, or the [w, h] scale as an array of two floats.
* The scale of the graphic as a float.
* Pro tip: On pixel-art levels, save the sprites small and set this value to 6 or so to save memory.
* @default 1
*/
var scale:OneOfTwo<Float, Array<Float>>;
var scale:Null<Float>;
/**
* The amount to offset the camera by while focusing on this character.
* Default value focuses on the character directly.
* @default [0, 0]
*/
var cameraOffset:Array<Float>;
/**
* Setting this to true disables anti-aliasing for the character.

View file

@ -41,16 +41,17 @@ class MultiSparrowCharacter extends BaseCharacter
{
trace('Creating MULTI SPARROW CHARACTER: ' + this.characterId);
buildSprite();
playAnimation(_data.startingAnimation);
buildSprites();
super.onCreate(event);
}
function buildSprite()
function buildSprites()
{
buildSpritesheets();
buildAnimations();
playAnimation(_data.startingAnimation);
if (_data.isPixel)
{
this.antialiasing = false;
@ -59,12 +60,6 @@ class MultiSparrowCharacter extends BaseCharacter
{
this.antialiasing = true;
}
if (_data.scale != null)
{
this.setGraphicSize(Std.int(this.width * this.scale.x));
this.updateHitbox();
}
}
function buildSpritesheets()
@ -115,26 +110,28 @@ class MultiSparrowCharacter extends BaseCharacter
}
if (assetPath == null)
{
trace('Asset path is null, falling back to default. This is normal!');
// trace('Asset path is null, falling back to default. This is normal!');
loadFramesByAssetPath(_data.assetPath);
return;
}
if (this.activeMember == assetPath)
{
trace('Already using this asset path: ${assetPath}');
// trace('Already using this asset path: ${assetPath}');
return;
}
if (members.exists(assetPath))
{
// Switch to a new set of sprites.
trace('Loading frames from asset path: ${assetPath}');
this.frames = members.get(assetPath);
this.activeMember = assetPath;
this.setScale(_data.scale);
}
else
{
trace('Multi-Sparrow could not find asset path: ${assetPath}');
trace('[WARN] MultiSparrow character ${characterId} could not find asset path: ${assetPath}');
}
}
@ -149,20 +146,18 @@ class MultiSparrowCharacter extends BaseCharacter
}
else
{
trace('Multi-Sparrow could not find animation: ${animName}');
trace('[WARN] MultiSparrow character ${characterId} could not find animation: ${animName}');
}
}
function buildAnimations()
{
trace('[SPARROWCHAR] Loading ${_data.animations.length} animations for ${characterId}');
trace('[MULTISPARROWCHAR] Loading ${_data.animations.length} animations for ${characterId}');
// We need to swap to the proper frame collection before adding the animations, I think?
for (anim in _data.animations)
{
trace('Using frames: ${anim.name}');
loadFramesByAnimName(anim.name);
trace('Adding animation');
FlxAnimationUtil.addAtlasAnimation(this, anim);
if (anim.offsets == null)
@ -176,7 +171,7 @@ class MultiSparrowCharacter extends BaseCharacter
}
var animNames = this.animation.getNameList();
trace('[SPARROWCHAR] Successfully loaded ${animNames.length} animations for ${characterId}');
trace('[MULTISPARROWCHAR] Successfully loaded ${animNames.length} animations for ${characterId}');
}
public override function playAnimation(name:String, restart:Bool = false):Void

View file

@ -24,6 +24,8 @@ class PackerCharacter extends BaseCharacter
loadAnimations();
playAnimation(_data.startingAnimation);
super.onCreate(event);
}
function loadSpritesheet()
@ -48,11 +50,7 @@ class PackerCharacter extends BaseCharacter
this.antialiasing = true;
}
if (_data.scale != null)
{
this.setGraphicSize(Std.int(this.width * this.scale.x));
this.updateHitbox();
}
this.setScale(_data.scale);
}
function loadAnimations()

View file

@ -26,6 +26,8 @@ class SparrowCharacter extends BaseCharacter
loadAnimations();
playAnimation(_data.startingAnimation);
super.onCreate(event);
}
function loadSpritesheet()
@ -50,11 +52,7 @@ class SparrowCharacter extends BaseCharacter
this.antialiasing = true;
}
if (_data.scale != null)
{
this.setGraphicSize(Std.int(this.width * this.scale.x));
this.updateHitbox();
}
this.setScale(_data.scale);
}
function loadAnimations()

View file

@ -32,7 +32,7 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
/**
* Offset the character's sprite by this much when playing each animation.
*/
public var animationOffsets:Map<String, Array<Int>> = new Map<String, Array<Int>>();
public var animationOffsets:Map<String, Array<Float>> = new Map<String, Array<Float>>();
/**
* Add a suffix to the `idle` animation (or `danceLeft` and `danceRight` animations)
@ -47,23 +47,22 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
return value;
}
/**
* Set this value to define an additional vertical offset to this sprite's position.
*/
public var yOffset:Float = 0;
private var animOffset(default, set):Array<Float> = [0, 0];
override function set_y(value:Float):Float
function set_animOffset(value:Array<Float>)
{
this.y = this.yOffset + value;
return this.y;
}
if (animOffset == null)
animOffset = [0, 0];
if (animOffset == value)
return value;
function set_yOffset(value:Float):Float
{
var diff = value - this.yOffset;
this.yOffset = value;
this.y += diff;
return value;
var xDiff = animOffset[0] - value[0];
var yDiff = animOffset[1] - value[1];
this.x += xDiff;
this.y += yDiff;
return animOffset = value;
}
/**
@ -193,11 +192,11 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
var offsets = animationOffsets.get(name);
if (offsets != null)
{
this.offset.set(offsets[0], offsets[1]);
this.animOffset = offsets;
}
else
{
this.offset.set(0, 0);
this.animOffset = [0, 0];
}
}

View file

@ -211,7 +211,6 @@ class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScripte
public function refresh()
{
sort(SortUtil.byZIndex, FlxSort.ASCENDING);
trace('Stage sorted by z-index');
}
/**
@ -242,8 +241,6 @@ class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScripte
if (character == null)
return;
var debugMarker:FlxSprite = new FlxSprite(0, 0);
// Apply position and z-index.
switch (charType)
{
@ -251,40 +248,24 @@ class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScripte
this.characters.set("bf", character);
character.zIndex = _data.characters.bf.zIndex;
// Subtracting the origin ensures characters are positioned relative to their feet.
trace('Data: ' + _data.characters.bf.position[0] + ', ' + _data.characters.bf.position[1]);
character.x = _data.characters.bf.position[0] - character.characterOrigin.x;
character.y = _data.characters.bf.position[1] - character.characterOrigin.y;
trace('Character position: ' + character.x + ', ' + character.y);
debugMarker.x = _data.characters.bf.position[0];
debugMarker.y = _data.characters.bf.position[1];
trace('Debug marker position: ' + debugMarker.x + ', ' + debugMarker.y);
case GF:
this.characters.set("gf", character);
character.zIndex = _data.characters.gf.zIndex;
// Subtracting the origin ensures characters are positioned relative to their feet.
character.x = _data.characters.gf.position[0] - character.characterOrigin.x;
character.y = _data.characters.gf.position[1] - character.characterOrigin.y;
debugMarker.x = _data.characters.gf.position[0];
debugMarker.y = _data.characters.gf.position[1];
case DAD:
this.characters.set("dad", character);
character.zIndex = _data.characters.dad.zIndex;
// Subtracting the origin ensures characters are positioned relative to their feet.
character.x = _data.characters.dad.position[0] - character.characterOrigin.x;
character.y = _data.characters.dad.position[1] - character.characterOrigin.y;
debugMarker.x = _data.characters.dad.position[0];
debugMarker.y = _data.characters.dad.position[1];
default:
this.characters.set(character.characterId, character);
}
#if debug
// Add a DEBUG marker for the character's origin.
debugMarker.makeGraphic(10, 10, 0xFFFF00FF);
debugMarker.zIndex = 10000;
this.add(debugMarker);
#end
// Add the character to the scene.
this.add(character);
}

View file

@ -223,10 +223,9 @@ class StageDataParser
input.cameraZoom = DEFAULT_CAMERAZOOM;
}
if (input.props == null || input.props.length == 0)
if (input.props == null)
{
trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing props');
return null;
input.props = [];
}
for (inputProp in input.props)

View file

@ -4,79 +4,28 @@ import flixel.system.FlxAssets.FlxShader;
class BuildingShaders
{
public var shader(default, null):BuildingShaderLegacy;
public var shader(default, null):BuildingShader;
public var daAlpha:Float = 1;
public function new():Void
{
shader = new BuildingShaderLegacy();
shader.buildingAlpha = 0;
shader = new BuildingShader();
shader.alphaShit.value = [0];
}
public function update(elapsed:Float):Void
{
shader.buildingAlpha += elapsed;
shader.alphaShit.value[0] += elapsed;
}
public function reset()
{
shader.buildingAlpha = 0;
shader.alphaShit.value[0] = 0;
}
}
class BuildingShader extends FlxRuntimeShader
class BuildingShader extends FlxShader
{
public var buildingAlpha(get, set):Float;
function get_buildingAlpha():Float
{
return getFloat('alphaShit');
}
function set_buildingAlpha(value:Float):Float
{
// Every time buildingAlpha is changed, update the property of the shader.
setFloat('alphaShit', value);
return value;
}
static final FRAGMENT_SHADER = "
#pragma header
uniform float alphaShit;
void main()
{
vec4 color = flixel_texture2D(bitmap, openfl_TextureCoordv);
if (color.a > 0.0)
color -= alphaShit;
gl_FragColor = color;
}
";
public function new()
{
super(FRAGMENT_SHADER, null, true);
}
}
class BuildingShaderLegacy extends FlxShader
{
public var buildingAlpha(get, set):Float;
function get_buildingAlpha():Float
{
return alphaShit.value[0];
}
function set_buildingAlpha(value:Float):Float
{
// Every time buildingAlpha is changed, update the property of the shader.
alphaShit.value = [value];
return value;
}
@:glFragmentSource('
#pragma header

View file

@ -157,16 +157,17 @@ class PreferencesMenu extends Page
});
}
private static function preferenceCheck(prefString:String, prefValue:Dynamic):Void
private static function preferenceCheck(prefString:String, defaultValue:Dynamic):Void
{
if (preferences.get(prefString) == null)
{
preferences.set(prefString, prefValue);
trace('set preference!');
// Set the value to default.
preferences.set(prefString, defaultValue);
trace('Set preference to default: ${prefString} = ${defaultValue}');
}
else
{
trace('found preference: ' + preferences.get(prefString));
trace('Found preference: ${prefString} = ${preferences.get(prefString)}');
}
}
}

View file

@ -1,5 +1,7 @@
package funkin.ui.animDebugShit;
import funkin.play.character.CharacterData.CharacterDataParser;
import funkin.play.character.SparrowCharacter;
import flixel.addons.display.FlxGridOverlay;
import flixel.addons.ui.FlxInputText;
import flixel.addons.ui.FlxUIDropDownMenu;
@ -392,7 +394,7 @@ class DebugBoundingState extends FlxState
if (FlxG.keys.justPressed.RIGHT || FlxG.keys.justPressed.LEFT || FlxG.keys.justPressed.UP || FlxG.keys.justPressed.DOWN)
{
var animName = animDropDownMenu.selectedLabel;
var coolValues:Array<Int> = swagChar.animationOffsets.get(animName);
var coolValues:Array<Float> = swagChar.animationOffsets.get(animName);
var multiplier:Int = 5;
@ -443,7 +445,7 @@ class DebugBoundingState extends FlxState
swagChar.destroy();
}
swagChar = new BaseCharacter(char);
swagChar = CharacterDataParser.fetchCharacter(char);
swagChar.x = 100;
swagChar.y = 100;
// swagChar.debugMode = true;