Skip to content

Commit

Permalink
ghost/onion skin WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
KoloInDaCrib authored Oct 31, 2024
1 parent 74abe1e commit dad258a
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 8 deletions.
121 changes: 121 additions & 0 deletions source/funkin/ui/debug/char/components/dialogs/GhostSettingsDialog.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package funkin.ui.debug.char.components.dialogs;

import haxe.ui.core.Screen;
import haxe.ui.containers.Grid;
import haxe.ui.containers.menus.Menu;
import funkin.play.character.CharacterData;
import funkin.play.character.CharacterData.CharacterDataParser;
import funkin.util.SortUtil;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;

@:build(haxe.ui.macros.ComponentMacros.build("assets/exclude/data/ui/char-creator/dialogs/ghost-dialog.xml"))
class GhostSettingsDialog extends DefaultPageDialog
{
var attachedMenu:GhostCharacterMenu;

override public function new(daPage:CharCreatorDefaultPage)
{
super(daPage);

var charData = CharacterDataParser.fetchCharacterData(Constants.DEFAULT_CHARACTER);
ghostTypeButton.icon = (charData == null ? null : CharacterDataParser.getCharPixelIconAsset(Constants.DEFAULT_CHARACTER));
ghostTypeButton.text = (charData == null ? "None" : charData.name.length > 6 ? '${charData.name.substr(0, 6)}.' : '${charData.name}');

// callbacks
ghostEnable.onChange = function(_) {
ghostDataBox.disabled = !ghostEnable.selected;
}

ghostCurChar.onChange = function(_) {
ghostTypeButton.disabled = ghostCurChar.selected; // no need to check for the other one thankfully
}

ghostTypeButton.onClick = function(_) {
attachedMenu = new GhostCharacterMenu(daPage, this);
Screen.instance.addComponent(attachedMenu);
}

ghostAnimDropdown.onChange = function(_) {
if (ghostAnimDropdown.selectedIndex == -1) return;
cast(daPage, CharCreatorGameplayPage).ghostCharacter.playAnimation(ghostAnimDropdown.selectedItem.text);
}
}

function releaseTheGhouls() {}
}

/**
* Maybe it would be nice to move the character menus to it's own group at some point - this is the third state to use it.
*/
@:xml('
<menu id="iconSelector" width="410" height="185" padding="8">
<vbox width="100%" height="100%">
<scrollview id="ghostSelectScroll" width="390" height="150" contentWidth="100%" />
<label id="ghostIconName" text="(choose a character)" />
</vbox>
</menu>
')
class GhostCharacterMenu extends Menu
{
override public function new(page:CharCreatorDefaultPage, parent:GhostSettingsDialog)
{
super();

this.x = Screen.instance.currentMouseX;
this.y = Screen.instance.currentMouseY;

var charGrid = new Grid();
charGrid.columns = 5;
charGrid.width = this.width;
ghostSelectScroll.addComponent(charGrid);

var charIds = CharacterDataParser.listCharacterIds();
charIds.sort(SortUtil.alphabetically);

var defaultText:String = '(choose a character)';

for (charIndex => charId in charIds)
{
var charData:CharacterData = CharacterDataParser.fetchCharacterData(charId);

var charButton = new haxe.ui.components.Button();
charButton.width = 70;
charButton.height = 70;
charButton.padding = 8;
charButton.iconPosition = "top";

/*if (charId == state.selectedChar.characterId)
{
// Scroll to the character if it is already selected.
ghostSelectScroll.hscrollPos = Math.floor(charIndex / 5) * 80;
charButton.selected = true;
defaultText = '${charData.name} [${charId}]';
}*/

var LIMIT = 6;
charButton.icon = CharacterDataParser.getCharPixelIconAsset(charId);
charButton.text = charData.name.length > LIMIT ? '${charData.name.substr(0, LIMIT)}.' : '${charData.name}';

charButton.onClick = _ ->
{
// kill and replace
};

charButton.onMouseOver = _ -> {
ghostIconName.text = '${charData.name} [${charId}]';
};
charButton.onMouseOut = _ -> {
ghostIconName.text = defaultText;
};
charGrid.addComponent(charButton);
}

ghostIconName.text = defaultText;

this.alpha = 0;
this.y -= 10;
FlxTween.tween(this, {alpha: 1, y: this.y + 10}, 0.2, {ease: FlxEase.quartOut});
}
}
2 changes: 1 addition & 1 deletion source/funkin/ui/debug/char/components/dialogs/import.hx
Original file line number Diff line number Diff line change
@@ -1 +1 @@
import funkin.ui.debug.char.pages.CharCreatorDefaultPage;
import funkin.ui.debug.char.pages.*;
47 changes: 40 additions & 7 deletions source/funkin/ui/debug/char/pages/CharCreatorGameplayPage.hx
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ class CharCreatorGameplayPage extends CharCreatorDefaultPage

// char
public var currentCharacter:CharCreatorCharacter;
public var characterScale:Float = 1.0;
public var characterFlipX:Bool = false;

// dialogs
public var dialogMap:Map<CharDialogType, DefaultPageDialog> = [];

// onion skin/ghost
public var ghostCharacter:CharCreatorCharacter;

override public function new(daState:CharCreatorState, wizardParams:WizardGenerateParams)
{
super(daState);
Expand All @@ -42,18 +43,29 @@ class CharCreatorGameplayPage extends CharCreatorDefaultPage

currentCharacter = new CharCreatorCharacter(wizardParams);
add(currentCharacter);

ghostCharacter = new CharCreatorCharacter(wizardParams);
add(ghostCharacter);

updateCharPerStageData();

dialogMap.set(Animation, new AddAnimDialog(this, currentCharacter));
dialogMap.set(Ghost, new GhostSettingsDialog(this));
}

override public function onDialogUpdate(dialog:DefaultPageDialog)
{
if (dialog == dialogMap[Animation])
{
labelAnimName.text = cast(dialogMap[Animation], AddAnimDialog).charAnimDropdown.selectedItem.text;
var animDialog = cast(dialogMap[Animation], AddAnimDialog);
var ghostDialog = cast(dialogMap[Ghost], GhostSettingsDialog);

labelAnimName.text = animDialog.charAnimDropdown.selectedItem.text;
labelAnimOffsetX.text = "" + currentCharacter.getAnimationData(labelAnimName.text).offsets[0];
labelAnimOffsetY.text = "" + currentCharacter.getAnimationData(labelAnimName.text).offsets[1];

GhostUtil.copyFromCharacter(ghostCharacter, currentCharacter);
ghostDialog.ghostAnimDropdown.dataSource = animDialog.charAnimDropdown.dataSource;
}
}

Expand Down Expand Up @@ -193,6 +205,10 @@ class CharCreatorGameplayPage extends CharCreatorDefaultPage
currentCharacter.setAnimationOffsets(currentCharacter.animations[drop.selectedIndex].name, newOffsets[0], newOffsets[1]); // todo: probs merge there two lol
currentCharacter.playAnimation(currentCharacter.animations[drop.selectedIndex].name, true, true);

// GhostUtil.copyFromCharacter(ghostCharacter, currentCharacter); very costly for memory! we're just gonna update the offsets
ghostCharacter.animations[drop.selectedIndex].offsets = newOffsets;
ghostCharacter.setAnimationOffsets(ghostCharacter.animations[drop.selectedIndex].name, newOffsets[0], newOffsets[1]); // todo: probs merge there two lol

// might as well update the text
labelAnimOffsetX.text = "" + newOffsets[0];
labelAnimOffsetY.text = "" + newOffsets[1];
Expand All @@ -206,24 +222,40 @@ class CharCreatorGameplayPage extends CharCreatorDefaultPage
dialogMap[Animation].hidden = !checkAnim.selected;
}
item.addComponent(checkAnim);

var checkGhost = new MenuCheckBox();
checkGhost.text = "Ghost Settings";
checkGhost.onChange = function(_) {
dialogMap[Ghost].hidden = !checkGhost.selected;
}
item.addComponent(checkGhost);
}

override public function performCleanup()
{
Conductor.instance.onBeatHit.remove(stageBeatHit);
}

function updateCharPerStageData(type:CharacterType = BF)
static inline final GHOST_SKIN_ALPHA:Float = 0.3;

public function updateCharPerStageData(type:CharacterType = BF)
{
if (charStageDatas[type] == null) return;

currentCharacter.zIndex = charStageDatas[type].zIndex;
currentCharacter.x = charStageDatas[type].position[0] - currentCharacter.characterOrigin.x;
currentCharacter.y = charStageDatas[type].position[1] - currentCharacter.characterOrigin.y;
currentCharacter.characterScale = characterScale * charStageDatas[type].scale;
currentCharacter.flipX = (type == BF ? !characterFlipX : characterFlipX);
currentCharacter.totalScale = currentCharacter.characterScale * charStageDatas[type].scale;
currentCharacter.flipX = (type == BF ? !currentCharacter.characterFlipX : currentCharacter.characterFlipX);

ghostCharacter.characterType = currentCharacter.characterType = type;

currentCharacter.characterType = type;
ghostCharacter.alpha = GHOST_SKIN_ALPHA;
ghostCharacter.zIndex = currentCharacter.zIndex - 1; // should onion skin be behind or in front?
ghostCharacter.x = charStageDatas[type].position[0] - ghostCharacter.characterOrigin.x;
ghostCharacter.y = charStageDatas[type].position[1] - ghostCharacter.characterOrigin.y;
ghostCharacter.totalScale = ghostCharacter.characterScale * charStageDatas[type].scale;
ghostCharacter.flipX = (type == BF ? !ghostCharacter.characterFlipX : ghostCharacter.characterFlipX);

sortAssets();
}
Expand Down Expand Up @@ -361,4 +393,5 @@ enum CharDialogType
{
Animation;
Data;
Ghost;
}
59 changes: 59 additions & 0 deletions source/funkin/ui/debug/char/util/GhostUtil.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package funkin.ui.debug.char.util;

import funkin.ui.debug.char.animate.CharSelectAtlasSprite;
import funkin.ui.debug.char.pages.CharCreatorGameplayPage;
import flixel.graphics.frames.FlxAtlasFrames;
import openfl.display.BitmapData;

// utilities for the onion skin/ghost character
class GhostUtil
{
public static function copyFromCharacter(ghost:CharCreatorCharacter, player:CharCreatorCharacter)
{
ghost.generatedParams = player.generatedParams;
ghost.atlasCharacter = null;

switch (player.renderType)
{
case "sparrow" | "multisparrow":
if (ghost.generatedParams.files.length != 2) return; // img and data

var img = BitmapData.fromBytes(ghost.generatedParams.files[0].bytes);
var data = ghost.generatedParams.files[1].bytes.toString();
ghost.frames = FlxAtlasFrames.fromSparrow(img, data);

case "packer":
if (ghost.generatedParams.files.length != 2) return; // img and data

var img = BitmapData.fromBytes(ghost.generatedParams.files[0].bytes);
var data = ghost.generatedParams.files[1].bytes.toString();
ghost.frames = FlxAtlasFrames.fromSpriteSheetPacker(img, data);

case "atlas": // todo
if (ghost.generatedParams.files.length != 1) return; // zip file with all the data
ghost.atlasCharacter = new CharSelectAtlasSprite(0, 0, ghost.generatedParams.files[0].bytes);

ghost.atlasCharacter.alpha = 0.0001;
ghost.atlasCharacter.draw();
ghost.atlasCharacter.alpha = 1.0;

ghost.atlasCharacter.x = ghost.x;
ghost.atlasCharacter.y = ghost.y;
ghost.atlasCharacter.alpha *= ghost.alpha;
ghost.atlasCharacter.flipX = ghost.flipX;
ghost.atlasCharacter.flipY = ghost.flipY;
ghost.atlasCharacter.scrollFactor.copyFrom(ghost.scrollFactor);
@:privateAccess ghost.atlasCharacter.cameras = ghost._cameras; // _cameras instead of cameras because get_cameras() will not return null

default: // nothing, what the fuck are you even doing
}

ghost.globalOffsets = player.globalOffsets.copy();

for (anim in player.animations)
{
ghost.addAnimation(anim.name, anim.prefix, anim.offsets, anim.frameIndices, anim.assetPath, anim.frameRate, anim.looped, anim.flipX, anim.flipY);
ghost.setAnimationOffsets(anim.name, anim.offsets[0], anim.offsets[1]);
}
}
}

0 comments on commit dad258a

Please sign in to comment.