Skip to content
Draft
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
File renamed without changes
File renamed without changes
207 changes: 207 additions & 0 deletions scripts/MenuSwitchState.hxc
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package kade.menucore;

import funkin.ui.FullScreenScaleMode;
import funkin.graphics.FunkinSprite;
import funkin.ui.title.TitleState;
import funkin.group.FunkinGroup;
import funkin.audio.FunkinSound;
import funkin.ui.MusicBeatState;
import funkin.util.MathUtil;
import funkin.ui.AtlasText;

#if FEATURE_TOUCH_CONTROLS
import funkin.util.TouchUtil;
import funkin.util.SwipeUtil;
#end

import flixel.addons.transition.FlxTransitionableState;
import flixel.util.FlxColor;
import flixel.math.FlxMath;
import flixel.text.FlxText;

class MenuSwitchState extends MusicBeatState
{
var menuItems:Array<String>;
var menuGroup:FunkinGroup;
var curSelected:Int = 0;

var arrowLeft:FunkinSprite;
var arrowRight:FunkinSprite;

var topRightText:FlxText;
var nameText:AtlasText;

override public function create():Void
{
menuItems = MenuRegistry.instance.listEntryIds();
curSelected = Math.max(menuItems.indexOf(BaseMenu.currentMenu), 0);

FunkinSound.playMusic('menu-switch', {
startingVolume: 0.0,
overrideExisting: true,
restartTrack: false
});
FlxG.sound.music.fadeIn(3);

FlxTransitionableState.skipNextTransIn = true;
FlxTransitionableState.skipNextTransOut = true;

var bg:FunkinSprite = FunkinSprite.create(0, 0, 'mc_bg');
bg.setGraphicSize(Std.int(FlxG.width * 1.2));
bg.updateHitbox();
bg.screenCenter();
bg.zIndex = 0;
add(bg);

var chooseBlackBar:FunkinSprite = new FunkinSprite(0, 0);
chooseBlackBar.makeSolidColor(FlxG.width, 60, FlxColor.BLACK);
chooseBlackBar.zIndex = 10;
add(chooseBlackBar);

var blackBar:FunkinSprite = new FunkinSprite(0, 0);
blackBar.makeSolidColor(FlxG.width, 185, FlxColor.BLACK);
blackBar.screenCenter(0x10);
blackBar.zIndex = 10;
add(blackBar);

topRightText = new FlxText(0, 15, FlxG.width / 2, "WEED");
topRightText.setFormat(Paths.font("vcr.ttf"), 35, FlxColor.WHITE, "right");
topRightText.x = (FlxG.width - topRightText.width) - 6;
topRightText.zIndex = 20;
add(topRightText);

final textX:Float = Math.max(6, FullScreenScaleMode.gameNotchSize.x);
var choose:FlxText = new FlxText(textX, 2, FlxG.width, "CHOOSE THE VERSION");
choose.setFormat(Paths.font("vcr.ttf"), 55, FlxColor.WHITE);
choose.zIndex = 20;
add(choose);

menuGroup = new FunkinGroup(0, 0, 0, false, false);
menuGroup.scale.set(0.8, 0.8);
menuGroup.zIndex = 30;
add(menuGroup);

nameText = new AtlasText(0, 0, 'cock', 'bold');
nameText.x = (FlxG.width - nameText.getWidth()) / 2;
nameText.y = FlxG.height - nameText.height - 25;
nameText.zIndex = 40;
add(nameText);

arrowLeft = FunkinSprite.createSparrow(FlxG.width / 8, 0, 'freeplay/freeplaySelector/freeplaySelector');
arrowLeft.animation.addByPrefix('shine', 'arrow pointer loop', 24);
arrowLeft.animation.play('shine');
arrowLeft.x -= arrowLeft.width / 2;
arrowLeft.screenCenter(0x10);
arrowLeft.zIndex = 50;
add(arrowLeft);

arrowRight = FunkinSprite.createSparrow(FlxG.width - (FlxG.width / 8), 0, 'freeplay/freeplaySelector/freeplaySelector');
arrowRight.animation.addByPrefix('shine', 'arrow pointer loop', 24);
arrowRight.animation.play('shine');
arrowRight.x -= arrowRight.width / 2;
arrowRight.screenCenter(0x10);
arrowRight.flipX = true;
arrowRight.zIndex = 50;
add(arrowRight);

super.create();

#if FEATURE_TOUCH_CONTROLS
addBackButton(FlxG.width - 230, FlxG.height - 200, FlxColor.WHITE, () -> back(), 0.3);
#end

populateMenuItems();

changeSelection(0);
menuGroup.x = menuGroup.localX;
}

function populateMenuItems():Void
{
for (i in 0...menuItems.length)
{
var menu:BaseMenu = MenuRegistry.instance.fetchEntry(menuItems[i]);

var sprite:FunkinSprite = new FunkinSprite(0, 0);
sprite.loadGraphic(menu.getImage());
sprite.updateHitbox();

sprite.localX = (FlxG.width - sprite.width) / 2;
sprite.localY = (FlxG.height - sprite.height) / 2;
sprite.localX += FlxG.width * i;

menuGroup.add(sprite);
}
}

override public function update(elapsed:Float):Void
{
var leftP:Bool = controls.UI_LEFT_P;
var rightP:Bool = controls.UI_RIGHT_P;
var acceptP:Bool = controls.ACCEPT_P;
var backP:Bool = controls.BACK_P;

#if FEATURE_TOUCH_CONTROLS
if (TouchUtil.pressAction(arrowLeft) || SwipeUtil.justSwipedLeft)
{
leftP = true;
}
else if (TouchUtil.pressAction(arrowRight) || SwipeUtil.justSwipedRight)
{
rightP = true;
}
else if (TouchUtil.pressAction(menuGroup.getChildAt(curSelected)))
{
acceptP = true;
}
#end

if (leftP)
{
changeSelection(-1);
}
else if (rightP)
{
changeSelection(1);
}
else if (acceptP)
{
select();
}
else if (backP)
{
back();
}

menuGroup.x = MathUtil.smoothLerpPrecision(menuGroup.x, menuGroup.localX, elapsed, 0.307);
super.update(elapsed);
}

public function changeSelection(change:Int = 0):Void
{
curSelected = FlxMath.wrap(curSelected + change, 0, menuItems.length - 1);
menuGroup.localX = -FlxG.width * curSelected;

if (change != 0) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);

var menu:BaseMenu = MenuRegistry.instance.fetchEntry(menuItems[curSelected]);

nameText.text = menu.getName();
nameText.x = (FlxG.width - nameText.getWidth()) / 2;

topRightText.text = menu.getDescription();
}

public function select():Void
{
var menu:BaseMenu = MenuRegistry.instance.fetchEntry(menuItems[curSelected]);
menu.onSelected();

BaseMenu.currentMenu = menu.id;
}

public function back():Void
{
FlxG.switchState(() -> new TitleState());
}
}
51 changes: 51 additions & 0 deletions scripts/data/BaseMenu.hxc
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package kade.menucore.data;

import funkin.ui.mainmenu.MainMenuState;
import funkin.modding.ModStore;

class BaseMenu
{
public static var currentMenu(get, set):String;

static function get_currentMenu():String
{
return ModStore.get('MC_CurrentMenu');
}

static function set_currentMenu(value:String):String
{
ModStore.stores.set('MC_CurrentMenu', value);
return value;
}

/**
* The internal ID for this menu.
*/
public final id:String;

public function new(ID:String)
{
id = ID;
}

public function getName():String
{
throw 'getName() must be overriden!';
}

public function getDescription():String
{
throw 'getDescription() must be overriden!';
}

public function getImage():FlxGraphicAsset
{
throw 'getImage() must be overriden!';
}

public function onSelected():Void
{
// By default, in case the custom menus come later!
FlxG.switchState(() -> new MainMenuState());
}
}
106 changes: 106 additions & 0 deletions scripts/data/MenuRegistry.hxc
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package kade.menucore.data;

import funkin.util.SortUtil;
import haxe.ds.StringMap;

using funkin.util.AnsiUtil;

class MenuRegistry
{
public static var instance:MenuRegistry;

var entries:StringMap<BaseMenu>;

public function new()
{
entries = new StringMap();
}

public function loadEntries():Void
{
var scriptedClasses:Array<String> = BaseMenu.listScriptClasses();
scriptedClasses.remove('kade.menucore.menus.LegacyMenu');

log(' INFO '.info() + ' Parsing ${scriptedClasses.length} scripted entries...');

for (entryCls in scriptedClasses)
{
var menu:BaseMenu = null;

try
{
menu = BaseMenu.scriptInit(entryCls, 'unknown');
}
catch(e:Exception) {}

if (menu != null)
{
registerEntry(menu);
}
else
{
log('Failed to create scripted entry (${entryCls})');
}
}
}

public function registerEntry(menu:BaseMenu):Void
{
entries.set(menu.id, menu);
log('Successfully registered entry (${menu.toString()} = ${menu.id})');
}

/**
* Retrieve a list of all entry IDs in this registry.
* @return The list of entry IDs.
*/
public function listEntryIds():Array<String>
{
var toReturn:Array<String> = [for (i in entries.keys()) i];
toReturn.sort((a, b) -> SortUtil.defaultsThenAlphabetically(listBaseGameEntryIds(), a, b));
return toReturn;
}

/**
* Count the number of entries in this registry.
* @return The number of entries.
*/
public function countEntries():Int
{
return listEntryIds().length;
}

/**
* Return whether the registry has successfully parsed an entry with the given ID.
* @param id The ID of the entry.
* @return `true` if the entry exists, `false` otherwise.
*/
public function hasEntry(id:String):Bool
{
return entries.exists(id);
}

/**
* Fetch an entry by its ID.
* @param id The ID of the entry to fetch.
* @return The entry, or `null` if it does not exist.
*/
public function fetchEntry(id:String):BaseMenu
{
return entries.get(id);
}

/**
* A list of all entries included in the base game.
* @return Array<String>
*/
public function listBaseGameEntryIds():Array<String>
{
return ['funkin'];
}

static function log(message:String):Void
{
trace(' MENUCORE '.bold().bg_orange() + ' $message');
}
}
26 changes: 26 additions & 0 deletions scripts/menus/FunkinMenu.hxc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package kade.menucore.menus;

import funkin.Paths;

class FunkinMenu extends BaseMenu
{
public function new()
{
super('funkin');
}

override public function getName():String
{
return 'Funkin';
}

override public function getDescription():String
{
return "Friday Night Funkin'";
}

override public function getImage():FlxGraphicAsset
{
return Paths.image('mc_base');
}
}
Loading