Skip to content

Add modcharting features (using FunkinModchart) #526

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,5 @@ _**QOL = Quality of Life**_
- Features not found in other editors!
- Every single state & substate can be modified via HScript (`data/states/StateName.hx`)
- **Instances launched via `lime test windows` will automatically use assets from source.**
</details>
- Modcharting features powered by [FunkinModchart](https://lib.haxe.org/p/funkin-modchart/).
</details>
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,5 @@ In the future (when the engine won't be a WIP anymore) we're gonna also publish
- Credits to the [FlxAnimate](https://github.com/Dot-Stuff/flxanimate) team for the Animate Atlas support
- Credits to Smokey555 for the backup Animate Atlas to spritesheet code
- Credits to MAJigsaw77 for [hxvlc](https://github.com/MAJigsaw77/hxvlc) (video cutscene/mp4 support) and [hxdiscord_rpc](https://github.com/MAJigsaw77/hxdiscord_rpc) (discord rpc integration)
- Credits to [TheoDev](https://github.com/TheoDevelops) for [FunkinModchart](https://lib.haxe.org/p/funkin-modchart/). ***(library used for modcharting features)***
</details>
1 change: 1 addition & 0 deletions libs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<git name="flxanimate" url="https://github.com/CodenameCrew/cne-flxanimate" />
<git name="hxdiscord_rpc" url="https://github.com/CodenameCrew/cne-hxdiscord_rpc" />
<lib name="hxvlc" version="1.9.3" skipDeps="true" />
<lib name="funkin-modchart" skipDeps="true" />

<!-- Documentation and other features -->
<git name="away3d" url="https://github.com/CodenameCrew/away3d" />
Expand Down
11 changes: 11 additions & 0 deletions project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@
<!-- Comment this out to disable dark mode windows -->
<define name="DARK_MODE_WINDOW"/>

<!-- Comment this out to disable modcharting features -->
<define name="MODCHARTING_FEATURES"/>

<!-- Disable a optimization, to allow reflection to use more functions -->
<define name="FLX_NO_GENERIC" />

Expand Down Expand Up @@ -195,6 +198,14 @@

<!-- _________________________________ Additional stuff _______________________________ -->

<section if="MODCHARTING_FEATURES">
<haxedef name="FM_ENGINE" value="CODENAME"/>
<haxedef name="FM_ENGINE_VERSION" value="1.0"/>

<haxelib name="funkin-modchart" />
<haxeflag name="--macro" value="modchart.core.macros.Macro.includeFiles()"/>
</section>

<section if="COMPILE_ALL_CLASSES">
<haxeflag name="-dce" value="no" />
<haxeflag name="--macro" value="funkin.backend.system.macros.Macros.addAdditionalClasses()" />
Expand Down
11 changes: 11 additions & 0 deletions source/funkin/game/Note.hx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ class Note extends FlxSprite
*/
public var nextSustain:Note;

/**
* The parent of the sustain.
*
* If this note is not sustain, this will be null.
*/
public var sustainParent:Null<Note>;

/**
* Name of the splash.
*/
Expand Down Expand Up @@ -128,6 +135,10 @@ class Note extends FlxSprite
}
}

// work around to set the `sustainParent`
if (isSustainNote)
sustainParent = prevNote.isSustainNote ? prevNote.sustainParent : prevNote;

x += 50;
// MAKE SURE ITS DEFINITELY OFF SCREEN?
y -= 2000;
Expand Down
5 changes: 5 additions & 0 deletions source/funkin/game/Strum.hx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ class Strum extends FlxSprite {
*/
public var animSuffix:String = "";

/**
* This strum's StrumLine
*/
public var strumLine:StrumLine;

public var cpu = false; // Unused
public var lastHit:Float = -5000;

Expand Down
1 change: 1 addition & 0 deletions source/funkin/game/StrumLine.hx
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ class StrumLine extends FlxTypedGroup<Strum> {
animPrefix = strumAnimPrefix[i % strumAnimPrefix.length];
var babyArrow:Strum = new Strum(startingPos.x + ((Note.swagWidth * strumScale) * i), startingPos.y);
babyArrow.ID = i;
babyArrow.strumLine = this;

if(data.scrollSpeed != null)
babyArrow.scrollSpeed = data.scrollSpeed;
Expand Down
3 changes: 3 additions & 0 deletions source/funkin/options/Options.hx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ class Options
public static var songOffset:Float = 0;
public static var framerate:Int = 120;
public static var gpuOnlyBitmaps:Bool = #if (mac || web) false #else true #end; // causes issues on mac and web
#if MODCHARTING_FEATURES
public static var modchartHoldSubdivisions:Int = 4;
#end

public static var lastLoadedMod:String = null;

Expand Down
7 changes: 7 additions & 0 deletions source/funkin/options/OptionsMenu.hx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ class OptionsMenu extends TreeMenu {
desc: 'Change Appearance options such as Flashing menus...',
state: AppearanceOptions
},
#if MODCHARTING_FEATURES
{
name: 'Modchart Settings >',
desc: 'Customize your modcharting experience...',
state: ModchartingOptions
},
#end
{
name: 'Miscellaneous >',
desc: 'Use this menu to reset save data or engine settings.',
Expand Down
2 changes: 1 addition & 1 deletion source/funkin/options/categories/AppearanceOptions.hx
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@ class AppearanceOptions extends OptionsScreen {
else
FlxG.updateFramerate = FlxG.drawFramerate = Std.int(change);
}
}
}
17 changes: 17 additions & 0 deletions source/funkin/options/categories/ModchartingOptions.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package funkin.options.categories;

#if MODCHARTING_FEATURES
class ModchartingOptions extends OptionsScreen {
public override function new() {
super("Modcharting Options", "Customize your modcharting experience.");
add(new NumOption(
"Hold Subdivisions",
"Softens the tail/hold/sustain of the arrows by subdividing it, giving them greater quality. By higher the subdivisions number is, performance will be affected.",
1, // minimum
128, // maximum
1, // change
"modchartHoldSubdivisions" // save name
)); // callback
}
}
#end
163 changes: 163 additions & 0 deletions source/modchart/standalone/adapters/codename/Codename.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package modchart.standalone.adapters.codename;

import flixel.FlxCamera;
import flixel.FlxSprite;
import funkin.backend.system.Conductor;
import funkin.game.Note;
import funkin.game.PlayState;
import funkin.game.Strum;
import funkin.options.Options;
import modchart.standalone.IAdapter;

class Codename implements IAdapter {
private var beatCrochet:Float = 0;

public function onModchartingInitialization() {
beatCrochet = Conductor.crochet;
}

public function isTapNote(sprite:FlxSprite) {
return sprite is Note;
}

// Song related
public function getSongPosition():Float {
return Conductor.songPosition;
}

public function getCurrentBeat():Float {
return Conductor.curBeatFloat;
}

public function getStaticCrochet():Float {
return beatCrochet;
}

public function getBeatFromStep(step:Float):Float {
return step * Conductor.stepsPerBeat;
}

public function arrowHit(arrow:FlxSprite) {
if (arrow is Note) {
final note:Note = cast arrow;
return note.wasGoodHit;
}
return false;
}

public function isHoldEnd(arrow:FlxSprite) {
if (arrow is Note) {
final note:Note = cast arrow;
return note.nextSustain == null;
}
return false;
}

public function getLaneFromArrow(arrow:FlxSprite) {
if (arrow is Note) {
final note:Note = cast arrow;
return note.strumID;
} else if (arrow is Strum) {
final strum:Strum = cast arrow;
return strum.ID;
}
return 0;
}

public function getPlayerFromArrow(arrow:FlxSprite) {
if (arrow is Note) {
final note:Note = cast arrow;
return note.strumLine.ID;
} else if (arrow is Strum) {
final strum:Strum = cast arrow;
return strum.strumLine.ID;
}

return 0;
}

public function getHoldParentTime(arrow:FlxSprite) {
final note:Note = cast arrow;
return note.sustainParent.strumTime;
}

// im so fucking sorry for those conditionals
public function getKeyCount(?player:Int = 0):Int {
return PlayState.instance != null
&& PlayState.instance.strumLines != null
&& PlayState.instance.strumLines.members != null
&& PlayState.instance.strumLines.members[player] != null
&& PlayState.instance.strumLines.members[player].members != null ? PlayState.instance.strumLines.members[player].members.length : 4;
}

public function getPlayerCount():Int {
return PlayState.instance != null && PlayState.instance.strumLines != null ? PlayState.instance.strumLines.length : 2;
}

public function getTimeFromArrow(arrow:FlxSprite) {
if (arrow is Note) {
final note:Note = cast arrow;
return note.strumTime;
}

return 0;
}

public function getHoldSubdivisions():Int
return Options.modchartHoldSubdivisions;

public function getDownscroll():Bool
return Options.downscroll;

public function getDefaultReceptorX(lane:Int, player:Int):Float {
@:privateAccess
return PlayState.instance.strumLines.members[player].members[lane].x;
}

public function getDefaultReceptorY(lane:Int, player:Int):Float {
@:privateAccess
return PlayState.instance.strumLines.members[player].members[lane].y;
}

public function getArrowCamera():Array<FlxCamera>
return [PlayState.instance.camHUD];

public function getCurrentScrollSpeed():Float {
return PlayState.instance.scrollSpeed;
}

public function getArrowItems() {
var drawMembers:Array<Array<Array<FlxSprite>>> = [];
var strumLineMembers = PlayState.instance.strumLines.members;

for (i in 0...strumLineMembers.length) {
final sl = strumLineMembers[i];

if (!sl.visible)
continue;

// setup list
drawMembers[i] = [
cast sl.members.copy(),
[],
[]
];

// preallocating first
var st = 0;
var nt = 0;
sl.notes.forEachAlive((spr) -> {
spr.isSustainNote ? st++ : nt++;
});

drawMembers[i][1].resize(nt);
drawMembers[i][2].resize(st);

var si = 0;
var ni = 0;
sl.notes.forEachAlive((spr) -> drawMembers[i][spr.isSustainNote ? 2 : 1][spr.isSustainNote ? si++ : ni++] = spr);
}

return drawMembers;
}
}