1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2024-11-04 13:54:22 +00:00

Additional fixes for health icons and camera positioning.

This commit is contained in:
Eric Myllyoja 2022-03-26 22:18:26 -04:00
parent 3634346072
commit 4b7744f4e3
13 changed files with 353 additions and 110 deletions

View file

@ -1,14 +1,10 @@
package funkin;
import funkin.util.Constants;
import funkin.modding.events.ScriptEvent.UpdateScriptEvent;
import funkin.modding.module.ModuleHandler;
import funkin.NGio;
import flixel.addons.transition.FlxTransitionableState;
import flixel.effects.FlxFlicker;
import flixel.FlxObject;
import flixel.FlxSprite;
import flixel.FlxState;
import flixel.addons.transition.FlxTransitionableState;
import flixel.effects.FlxFlicker;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.input.touch.FlxTouch;
@ -18,14 +14,19 @@ import flixel.tweens.FlxTween;
import flixel.ui.FlxButton;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import lime.app.Application;
import openfl.filters.ShaderFilter;
import funkin.modding.events.ScriptEvent.UpdateScriptEvent;
import funkin.modding.module.ModuleHandler;
import funkin.NGio;
import funkin.shaderslmfao.ScreenWipeShader;
import funkin.ui.AtlasMenuList;
import funkin.ui.MenuList;
import funkin.ui.OptionsState;
import funkin.ui.PreferencesMenu;
import funkin.ui.Prompt;
import funkin.util.Constants;
import funkin.util.WindowUtil;
import lime.app.Application;
import openfl.filters.ShaderFilter;
using StringTools;
@ -185,17 +186,7 @@ class MainMenuState extends MusicBeatState
#if CAN_OPEN_LINKS
function selectDonate()
{
#if linux
// Sys.command('/usr/bin/xdg-open', ["https://ninja-muffin24.itch.io/funkin", "&"]);
Sys.command('/usr/bin/xdg-open', [
"https://www.kickstarter.com/projects/funkin/friday-night-funkin-the-full-ass-game/",
"&"
]);
#else
// FlxG.openURL('https://ninja-muffin24.itch.io/funkin');
FlxG.openURL('https://www.kickstarter.com/projects/funkin/friday-night-funkin-the-full-ass-game/');
#end
WindowUtil.openURL(Constants.URL_KICKSTARTER);
}
#end

View file

@ -95,10 +95,14 @@ class MusicBeatState extends FlxUIState
function debug_refreshModules()
{
// Forcibly clear scripts so that scripts can be edited.
ModuleHandler.clearModuleCache();
// Forcibly reload scripts so that scripted stages can be edited.
polymod.hscript.PolymodScriptClass.clearScriptClasses();
// Forcibly reload Polymod so it finds any new files.
polymod.Polymod.reload();
// Reload scripted classes so stages and modules will update.
polymod.hscript.PolymodScriptClass.registerAllScriptClasses();
// Reload the stages in cache. This might cause a lag spike but who cares this is a debug utility.
@ -106,7 +110,7 @@ class MusicBeatState extends FlxUIState
CharacterDataParser.loadCharacterCache();
ModuleHandler.loadModuleCache();
// Create a new instance of the current state class.
// Restart the current state, so old data is cleared.
FlxG.resetState();
}

View file

@ -46,7 +46,7 @@ class StoryMenuState extends MusicBeatState
['mom', 'bf', 'gf'],
['parents-christmas', 'bf', 'gf'],
['senpai', 'bf', 'gf'],
['tankman', 'bf', 'gf']
['tankman', 'bf', 'gf'],
];
var weekNames:Array<String> = [

View file

@ -27,7 +27,7 @@ typedef AnimationData =
* Offset the character's position by this amount when playing this animation.
* @default [0, 0]
*/
var offsets:Null<Array<Int>>;
var offsets:Null<Array<Float>>;
/**
* Whether the animation should loop when it finishes.

View file

@ -1,7 +1,9 @@
package funkin.play;
import funkin.play.character.CharacterData.CharacterDataParser;
import flixel.math.FlxMath;
import flixel.FlxSprite;
import flixel.math.FlxPoint;
import funkin.play.character.CharacterData.CharacterDataParser;
import openfl.utils.Assets;
/**
@ -31,10 +33,25 @@ class HealthIcon extends FlxSprite
/**
* 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
* Note that turning this off means you have to manually do the following:
* - Bumping the icon on the beat.
* - Switching between winning/losing/idle animations.
* - Repositioning the icon as health changes.
*/
public var autoUpdate:Bool = true;
/**
* Since the `scale` of the sprite dynamically changes over time,
* this value allows you to set a relative scale for the icon.
* @default 1x scale
*/
public var size:FlxPoint = new FlxPoint(1, 1);
/**
* Apply the "bump" animation once every X steps.
*/
public var bumpEvery:Int = 4;
/**
* The player the health icon is attached to.
*/
@ -46,6 +63,11 @@ class HealthIcon extends FlxSprite
*/
var isPixel:Bool = false;
/**
* Whether this is a legacy icon or not.
*/
var isLegacyStyle:Bool = false;
/**
* At this amount of health, play the Winning animation instead of the idle.
*/
@ -127,10 +149,57 @@ class HealthIcon extends FlxSprite
switch (playerId)
{
case 0: // Boyfriend
// Update the animation based on the current state.
updateHealthIcon(PlayState.instance.health);
// Update the position to match the health bar.
this.x = PlayState.instance.healthBar.x
+ (PlayState.instance.healthBar.width * (FlxMath.remapToRange(PlayState.instance.healthBar.value, 0, 2, 100, 0) * 0.01));
case 1: // Dad
// Update the animation based on the current state.
updateHealthIcon(MAXIMUM_HEALTH - PlayState.instance.health);
// Update the position to match the health bar.
this.x = PlayState.instance.healthBar.x
+ (PlayState.instance.healthBar.width * (FlxMath.remapToRange(PlayState.instance.healthBar.value, 0, 2, 100, 0) * 0.01))
- (this.width);
}
// Lerp the health icon back to its normal size,
// while maintaining aspect ratio.
if (this.width > this.height)
{
// Apply linear interpolation while accounting for frame rate.
var targetSize = Std.int(CoolUtil.coolLerp(this.width, 150 * this.size.x, 0.15));
setGraphicSize(targetSize, 0);
}
else
{
var targetSize = Std.int(CoolUtil.coolLerp(this.height, 150 * this.size.y, 0.15));
setGraphicSize(0, targetSize);
}
this.updateHitbox();
}
}
public function onStepHit(curStep:Int)
{
if (curStep % bumpEvery == 0 && isLegacyStyle)
{
// Make the health icons bump (the update function causes them to lerp back down).
if (this.width > this.height)
{
var targetSize = Std.int(CoolUtil.coolLerp(this.width + 30, 150, 0.15));
setGraphicSize(targetSize, 0);
}
else
{
var targetSize = Std.int(CoolUtil.coolLerp(this.height + 30, 150, 0.15));
setGraphicSize(0, targetSize);
}
this.updateHitbox();
}
}
@ -169,6 +238,10 @@ class HealthIcon extends FlxSprite
case FROM_LOSING | FROM_WINNING:
if (isAnimationFinished())
playAnimation(IDLE);
case "":
playAnimation(IDLE);
default:
playAnimation(IDLE);
}
}
@ -200,7 +273,7 @@ class HealthIcon extends FlxSprite
function correctCharacterId(charId:String):String
{
if (!Assets.exists(Paths.image('icons/icon-' + charId)))
if (!Assets.exists(Paths.image('icons/icon-$charId')))
{
FlxG.log.warn('No icon for character: $charId : using default placeholder face instead!');
return "face";
@ -211,7 +284,7 @@ class HealthIcon extends FlxSprite
function isNewSpritesheet(charId:String):Bool
{
return Assets.exists(Paths.xml('icons/icon-' + characterId));
return Assets.exists(Paths.file('images/icons/icon-$characterId.xml'));
}
function fetchIsPixel(charId:String):Bool
@ -235,7 +308,9 @@ class HealthIcon extends FlxSprite
isPixel = fetchIsPixel(charId);
if (isNewSpritesheet(charId))
isLegacyStyle = !isNewSpritesheet(charId);
if (!isLegacyStyle)
{
frames = Paths.getSparrowAtlas('icons/icon-$charId');
@ -290,11 +365,17 @@ class HealthIcon extends FlxSprite
{
// Attempt to play the animation
if (hasAnimation(name))
{
this.animation.play(name, restart, false, 0);
return;
}
// Play the fallback animation if the requested animation was not found
if (fallback != null && hasAnimation(fallback))
{
this.animation.play(fallback, restart, false, 0);
return;
}
// If we don't have an animation, we're done.
}

View file

@ -185,7 +185,7 @@ class PlayState extends MusicBeatState implements IHook
* The bar which displays the player's health.
* Dynamically updated based on the value of `healthLerp` (which is based on `health`).
*/
private var healthBar:FlxBar;
public var healthBar:FlxBar;
/**
* The background image used for the health bar.
@ -193,6 +193,16 @@ class PlayState extends MusicBeatState implements IHook
*/
public var healthBarBG:FlxSprite;
/**
* The health icon representing the player.
*/
public var iconP1:HealthIcon;
/**
* The health icon representing the opponent.
*/
public var iconP2:HealthIcon;
/**
* The sprite group containing active player's strumline notes.
*/
@ -247,8 +257,6 @@ class PlayState extends MusicBeatState implements IHook
private var combo:Int = 0;
private var generatedMusic:Bool = false;
private var startingSong:Bool = false;
private var iconP1:HealthIcon;
private var iconP2:HealthIcon;
var dialogue:Array<String>;
var talking:Bool = true;
@ -275,8 +283,9 @@ class PlayState extends MusicBeatState implements IHook
instance = this;
// TEMP: For testing
cameraFollowPoint.makeGraphic(8, 8, 0xFFFF00FF);
// Displays the camera follow point as a sprite for debug purposes.
// TODO: Put this on a toggle?
cameraFollowPoint.makeGraphic(8, 8, 0xFF00FF00);
cameraFollowPoint.zIndex = 1000000;
// Reduce physics accuracy (who cares!!!) to improve animation quality.
@ -326,6 +335,18 @@ class PlayState extends MusicBeatState implements IHook
// Once the song is loaded, we can continue and initialize the stage.
var healthBarYPos:Float = PreferencesMenu.getPref('downscroll') ? FlxG.height * 0.1 : FlxG.height * 0.9;
healthBarBG = new FlxSprite(0, healthBarYPos).loadGraphic(Paths.image('healthBar'));
healthBarBG.screenCenter(X);
healthBarBG.scrollFactor.set(0, 0);
add(healthBarBG);
healthBar = new FlxBar(healthBarBG.x + 4, healthBarBG.y + 4, RIGHT_TO_LEFT, Std.int(healthBarBG.width - 8), Std.int(healthBarBG.height - 8), this,
'healthLerp', 0, 2);
healthBar.scrollFactor.set();
healthBar.createFilledBar(Constants.HEALTH_BAR_RED, Constants.HEALTH_BAR_GREEN);
add(healthBar);
initStage();
initCharacters();
#if discord_rpc
@ -357,31 +378,11 @@ class PlayState extends MusicBeatState implements IHook
FlxG.worldBounds.set(0, 0, FlxG.width, FlxG.height);
var healthBarYPos:Float = PreferencesMenu.getPref('downscroll') ? FlxG.height * 0.1 : FlxG.height * 0.9;
healthBarBG = new FlxSprite(0, healthBarYPos).loadGraphic(Paths.image('healthBar'));
healthBarBG.screenCenter(X);
healthBarBG.scrollFactor.set(0, 0);
add(healthBarBG);
healthBar = new FlxBar(healthBarBG.x + 4, healthBarBG.y + 4, RIGHT_TO_LEFT, Std.int(healthBarBG.width - 8), Std.int(healthBarBG.height - 8), this,
'healthLerp', 0, 2);
healthBar.scrollFactor.set();
healthBar.createFilledBar(Constants.HEALTH_BAR_RED, Constants.HEALTH_BAR_GREEN);
add(healthBar);
scoreText = new FlxText(healthBarBG.x + healthBarBG.width - 190, healthBarBG.y + 30, 0, "", 20);
scoreText.setFormat(Paths.font("vcr.ttf"), 16, FlxColor.WHITE, RIGHT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
scoreText.scrollFactor.set();
add(scoreText);
iconP1 = new HealthIcon(currentSong.player1, 0);
iconP1.y = healthBar.y - (iconP1.height / 2);
add(iconP1);
iconP2 = new HealthIcon(currentSong.player2, 1);
iconP2.y = healthBar.y - (iconP2.height / 2);
add(iconP2);
// Attach the groups to the HUD camera so they are rendered independent of the stage.
grpNoteSplashes.cameras = [camHUD];
activeNotes.cameras = [camHUD];
@ -475,6 +476,9 @@ class PlayState extends MusicBeatState implements IHook
currentStageId = 'schoolEvil';
case 'guns' | 'stress' | 'ugh':
currentStageId = 'tankmanBattlefield';
case 'experimental-phase' | 'perfection':
// SERIOUSLY REVAMP THE CHART FORMAT ALREADY
currentStageId = "breakout";
default:
currentStageId = "mainStage";
}
@ -484,6 +488,14 @@ class PlayState extends MusicBeatState implements IHook
function initCharacters()
{
iconP1 = new HealthIcon(currentSong.player1, 0);
iconP1.y = healthBar.y - (iconP1.height / 2);
add(iconP1);
iconP2 = new HealthIcon(currentSong.player2, 1);
iconP2.y = healthBar.y - (iconP2.height / 2);
add(iconP2);
//
// GIRLFRIEND
//
@ -501,6 +513,9 @@ class PlayState extends MusicBeatState implements IHook
gfVersion = 'gf-pixel';
case 'tankmanBattlefield':
gfVersion = 'gf-tankmen';
case 'breakout':
// SERIOUSLY PUT THIS SHIT IN THE CHART
gfVersion = '';
}
if (currentSong.player1 == "pico")
@ -585,8 +600,8 @@ 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(boyfriend, BF);
currentStage.addCharacter(girlfriend, GF);
currentStage.addCharacter(boyfriend, BF);
currentStage.addCharacter(dad, DAD);
// Redo z-indexes.
@ -1048,17 +1063,6 @@ class PlayState extends MusicBeatState implements IHook
if (FlxG.keys.justPressed.NINE)
iconP1.toggleOldIcon();
iconP1.setGraphicSize(Std.int(CoolUtil.coolLerp(iconP1.width, 150, 0.15)));
iconP2.setGraphicSize(Std.int(CoolUtil.coolLerp(iconP2.width, 150, 0.15)));
iconP1.updateHitbox();
iconP2.updateHitbox();
var iconOffset:Int = 26;
iconP1.x = healthBar.x + (healthBar.width * (FlxMath.remapToRange(healthBar.value, 0, 2, 100, 0) * 0.01) - iconOffset);
iconP2.x = healthBar.x + (healthBar.width * (FlxMath.remapToRange(healthBar.value, 0, 2, 100, 0) * 0.01)) - (iconP2.width - iconOffset);
if (health > 2)
health = 2;
@ -1293,7 +1297,7 @@ class PlayState extends MusicBeatState implements IHook
function killCombo():Void
{
// Girlfriend gets sad if you combo break after hitting 5 notes.
if (currentStage.getGirlfriend() != null)
if (currentStage != null && currentStage.getGirlfriend() != null)
if (combo > 5 && currentStage.getGirlfriend().hasAnimation('sad'))
currentStage.getGirlfriend().playAnimation('sad');
@ -1727,6 +1731,9 @@ class PlayState extends MusicBeatState implements IHook
resyncVocals();
}
iconP1.onStepHit(curStep);
iconP2.onStepHit(curStep);
dispatchEvent(new SongTimeScriptEvent(ScriptEvent.SONG_STEP_HIT, curBeat, curStep));
}
@ -1765,12 +1772,6 @@ class PlayState extends MusicBeatState implements IHook
}
}
// Make the health icons bump (the update function causes them to lerp back down).
iconP1.setGraphicSize(Std.int(iconP1.width + 30));
iconP2.setGraphicSize(Std.int(iconP2.width + 30));
iconP1.updateHitbox();
iconP2.updateHitbox();
// Make the characters dance on the beat
danceOnBeat();

View file

@ -61,21 +61,21 @@ class BaseCharacter extends Bopper
*/
public var cameraFocusPoint(default, null):FlxPoint = new FlxPoint(0, 0);
override function set_animOffset(value:Array<Float>)
override function set_animOffsets(value:Array<Float>)
{
if (animOffset == null)
animOffset = [0, 0];
if (animOffset == value)
if (animOffsets == null)
animOffsets = [0, 0];
if (animOffsets == value)
return value;
var xDiff = animOffset[0] - value[0];
var yDiff = animOffset[1] - value[1];
var xDiff = animOffsets[0] - value[0];
var yDiff = animOffsets[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;
return animOffsets = value;
}
/**
@ -122,6 +122,7 @@ class BaseCharacter extends Bopper
{
this.characterName = _data.name;
this.singTimeCrochet = _data.singTime;
this.globalOffsets = _data.offsets;
}
}
@ -139,12 +140,43 @@ class BaseCharacter extends Bopper
this.updateHitbox();
}
/**
* The per-character camera offset.
*/
var characterCameraOffsets(get, null):Array<Float>;
function get_characterCameraOffsets():Array<Float>
{
return _data.cameraOffsets;
}
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]);
// 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]);
super.onCreate(event);
}
public function initHealthIcon(isOpponent:Bool):Void
{
if (!isOpponent)
{
PlayState.instance.iconP1.characterId = _data.healthIcon.id;
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];
}
else
{
PlayState.instance.iconP2.characterId = _data.healthIcon.id;
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];
}
}
public override function onUpdate(event:UpdateScriptEvent):Void
{
super.onUpdate(event);

View file

@ -290,11 +290,11 @@ class CharacterDataParser
static final DEFAULT_ISPIXEL:Bool = false;
static final DEFAULT_LOOP:Bool = false;
static final DEFAULT_NAME:String = "Untitled Character";
static final DEFAULT_OFFSETS:Array<Int> = [0, 0];
static final DEFAULT_OFFSETS:Array<Float> = [0, 0];
static final DEFAULT_HEALTHICON_OFFSETS:Array<Int> = [0, 25];
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";
/**
@ -341,6 +341,40 @@ class CharacterDataParser
return null;
}
if (input.offsets == null)
{
input.offsets = DEFAULT_OFFSETS;
}
if (input.cameraOffsets == null)
{
input.cameraOffsets = DEFAULT_OFFSETS;
}
if (input.healthIcon == null)
{
input.healthIcon = {
id: null,
scale: null,
offsets: null
};
}
if (input.healthIcon.id == null)
{
input.healthIcon.id = id;
}
if (input.healthIcon.scale == null)
{
input.healthIcon.scale = DEFAULT_SCALE;
}
if (input.healthIcon.offsets == null)
{
input.healthIcon.offsets = DEFAULT_OFFSETS;
}
if (input.startingAnimation == null)
{
input.startingAnimation = DEFAULT_STARTINGANIM;
@ -351,11 +385,6 @@ class CharacterDataParser
input.scale = DEFAULT_SCALE;
}
if (input.cameraOffset == null)
{
input.cameraOffset = DEFAULT_CAMERAOFFSET;
}
if (input.isPixel == null)
{
input.isPixel = DEFAULT_ISPIXEL;
@ -467,12 +496,23 @@ typedef CharacterData =
*/
var scale:Null<Float>;
/**
* Optional data about the health icon for the character.
*/
var healthIcon:Null<HealthIconData>;
/**
* The global offset to the character's position, in pixels.
* @default [0, 0]
*/
var offsets:Null<Array<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>;
var cameraOffsets:Array<Float>;
/**
* Setting this to true disables anti-aliasing for the character.
@ -510,3 +550,23 @@ typedef CharacterData =
*/
var startingAnimation:Null<String>;
};
typedef HealthIconData =
{
/**
* The ID to use for the health icon.
* @default The character's ID
*/
var id:Null<String>;
/**
* The scale of the health icon.
*/
var scale:Null<Float>;
/**
* The offset of the health icon, in pixels.
* @default [0, 25]
*/
var offsets:Null<Array<Float>>;
}

View file

@ -47,22 +47,27 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
return value;
}
private var animOffset(default, set):Array<Float> = [0, 0];
/**
* The offset of the character relative to the position specified by the stage.
*/
public var globalOffsets(default, null):Array<Float> = [0, 0];
function set_animOffset(value:Array<Float>)
private var animOffsets(default, set):Array<Float> = [0, 0];
function set_animOffsets(value:Array<Float>)
{
if (animOffset == null)
animOffset = [0, 0];
if (animOffset == value)
if (animOffsets == null)
animOffsets = [0, 0];
if (animOffsets == value)
return value;
var xDiff = animOffset[0] - value[0];
var yDiff = animOffset[1] - value[1];
var xDiff = animOffsets[0] - value[0];
var yDiff = animOffsets[1] - value[1];
this.x += xDiff;
this.y += yDiff;
return animOffset = value;
return animOffsets = value;
}
/**
@ -192,11 +197,11 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
var offsets = animationOffsets.get(name);
if (offsets != null)
{
this.animOffset = offsets;
this.animOffsets = [offsets[0] + globalOffsets[0], offsets[1] + globalOffsets[1]];
}
else
{
this.animOffset = [0, 0];
this.animOffsets = globalOffsets;
}
}
@ -205,7 +210,7 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
return this.animation.finished;
}
public function setAnimationOffsets(name:String, xOffset:Int, yOffset:Int):Void
public function setAnimationOffsets(name:String, xOffset:Float, yOffset:Float):Void
{
animationOffsets.set(name, [xOffset, yOffset]);
applyAnimationOffsets(name);

View file

@ -241,33 +241,70 @@ class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScripte
if (character == null)
return;
#if debug
// Temporary marker that shows where the character's location is relative to.
// Should display at the stage position of the character (before any offsets).
// TODO: Make this a toggle? It's useful to turn on from time to time.
var debugIcon:FlxSprite = new FlxSprite(0, 0);
debugIcon.makeGraphic(8, 8, 0xffff00ff);
debugIcon.zIndex = 1000000;
#end
// Apply position and z-index.
switch (charType)
{
case BF:
this.characters.set("bf", character);
character.zIndex = _data.characters.bf.zIndex;
// Start with the per-stage character position.
// Subtracting the origin ensures characters are positioned relative to their feet.
character.x = _data.characters.bf.position[0] - character.characterOrigin.x;
character.y = _data.characters.bf.position[1] - character.characterOrigin.y;
// Subtracting the global offset allows positioning on a per-character basis.
character.x = _data.characters.bf.position[0] - character.characterOrigin.x + character.globalOffsets[0];
character.y = _data.characters.bf.position[1] - character.characterOrigin.y + character.globalOffsets[1];
character.cameraFocusPoint.x += _data.characters.bf.cameraOffsets[0];
character.cameraFocusPoint.y += _data.characters.bf.cameraOffsets[1];
debugIcon.x = _data.characters.bf.position[0];
debugIcon.y = _data.characters.bf.position[1];
character.initHealthIcon(false);
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;
character.x = _data.characters.gf.position[0] - character.characterOrigin.x + character.globalOffsets[0];
character.y = _data.characters.gf.position[1] - character.characterOrigin.y + character.globalOffsets[1];
character.cameraFocusPoint.x += _data.characters.gf.cameraOffsets[0];
character.cameraFocusPoint.y += _data.characters.gf.cameraOffsets[1];
// Draw the debug icon at the character's feet.
debugIcon.x = _data.characters.gf.position[0];
debugIcon.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;
character.x = _data.characters.dad.position[0] - character.characterOrigin.x + character.globalOffsets[0];
character.y = _data.characters.dad.position[1] - character.characterOrigin.y + character.globalOffsets[1];
character.cameraFocusPoint.x += _data.characters.dad.cameraOffsets[0];
character.cameraFocusPoint.y += _data.characters.dad.cameraOffsets[1];
// Draw the debug icon at the character's feet.
debugIcon.x = _data.characters.dad.position[0];
debugIcon.y = _data.characters.dad.position[1];
character.initHealthIcon(true);
default:
this.characters.set(character.characterId, character);
}
// Add the character to the scene.
this.add(character);
this.add(debugIcon);
}
public inline function getGirlfriendPosition():FlxPoint

View file

@ -174,7 +174,7 @@ class StageDataParser
static final DEFAULT_DANCEEVERY:Int = 0;
static final DEFAULT_ISPIXEL:Bool = false;
static final DEFAULT_NAME:String = "Untitled Stage";
static final DEFAULT_OFFSETS:Array<Int> = [0, 0];
static final DEFAULT_OFFSETS:Array<Float> = [0, 0];
static final DEFAULT_POSITION:Array<Float> = [0, 0];
static final DEFAULT_SCALE:Float = 1.0;
static final DEFAULT_SCROLL:Array<Float> = [0, 0];
@ -183,6 +183,7 @@ class StageDataParser
static final DEFAULT_CHARACTER_DATA:StageDataCharacter = {
zIndex: DEFAULT_ZINDEX,
position: DEFAULT_POSITION,
cameraOffsets: DEFAULT_OFFSETS,
}
/**
@ -358,6 +359,10 @@ class StageDataParser
{
inputCharacter.position = [0, 0];
}
if (inputCharacter.cameraOffsets == null || inputCharacter.cameraOffsets.length != 2)
{
inputCharacter.cameraOffsets = [0, 0];
}
}
// All good!
@ -475,5 +480,11 @@ typedef StageDataCharacter =
/**
* The position to render the character at.
*/
position:Array<Float>
position:Array<Float>,
/**
* The camera offsets to apply when focusing on the character on this stage.
* @default [0, 0]
*/
cameraOffsets:Array<Float>,
};

View file

@ -31,4 +31,7 @@ class Constants
return 'v${Application.current.meta.get('version')}' + VERSION_SUFFIX;
}
#end
public static final URL_KICKSTARTER:String = "https://www.kickstarter.com/projects/funkin/friday-night-funkin-the-full-ass-game/";
public static final URL_ITCH:String = "https://ninja-muffin24.itch.io/funkin";
}

View file

@ -0,0 +1,18 @@
package funkin.util;
class WindowUtil
{
public static function openURL(targetUrl:String)
{
#if CAN_OPEN_LINKS
#if linux
// Sys.command('/usr/bin/xdg-open', [, "&"]);
Sys.command('/usr/bin/xdg-open', [targetUrl, "&"]);
#else
FlxG.openURL(targetUrl);
#end
#else
trace('Cannot open')
#end
}
}