Scripted stages are done (weeks 1-7)

This commit is contained in:
Eric Myllyoja 2022-03-06 02:37:38 -05:00
parent d50a82cdf6
commit b19478cd1e
24 changed files with 189 additions and 271 deletions

10
.vscode/launch.json vendored
View file

@ -1,16 +1,6 @@
{
"version": "0.2.0",
"configurations": [
{
// Launch in browser
"name": "HTML5 Debug",
"type": "chrome",
"request": "launch",
"url": "http://127.0.0.1:3001",
"sourceMaps": true,
"webRoot": "${workspaceFolder}",
"preLaunchTask": "debug: html"
},
{
// Launch in native/CPP
"name": "Lime",

View file

@ -125,7 +125,7 @@
<haxelib name="flixel-ui" />
<!--haxelib name="newgrounds" unless="switch"/> -->
<haxelib name="faxe" if='switch'/>
<haxelib name="polymod" if="desktop"/>
<haxelib name="polymod" />
<haxelib name="firetongue" />
<!-- <haxelib name="colyseus"/> -->
@ -184,8 +184,9 @@
<!--Enable this for Nape release builds for a serious peformance improvement-->
<haxedef name="NAPE_RELEASE_BUILD" unless="debug" />
<haxeflag name="--no-traces" unless="debug" />
<haxeflag name="--dce full" if="release" />
<haxeflag name="-no-traces" unless="debug" />
<!-- HScript relies heavily on Reflection, which means we can't use DCE. -->
<haxeflag name="--dce no" />
<haxedef name="HXCPP_CHECK_POINTER" />
<haxedef name="HXCPP_STACK_LINE" />
@ -203,6 +204,7 @@
took me a while to get this working but turns out the classname comes SECOND
-->
<haxeflag name="--macro" value="addMetadata('@:build(util.macro.FlxMacro.buildFlxBasic())', 'flixel.FlxBasic')" />
<haxeflag name="--macro" value="include('modding.base')" />
<!-- <haxedef name="SKIP_TO_PLAYSTATE" if="debug" /> -->

View file

@ -1,5 +1,7 @@
package;
import Note.NoteData;
import SongLoad.SwagSong;
import Section.SwagSection;
import flixel.FlxSprite;
import flixel.animation.FlxBaseAnimation;
@ -19,7 +21,7 @@ class Character extends FlxSprite
public var holdTimer:Float = 0;
public var animationNotes:Array<Dynamic> = [];
public var animationNotes:Array<NoteData> = [];
public function new(x:Float, y:Float, ?character:String = "bf", ?isPlayer:Bool = false)
{
@ -584,27 +586,25 @@ class Character extends FlxSprite
public function loadMappedAnims()
{
var swagshit = SongLoad.loadFromJson('picospeaker', 'stress');
var swagshit:SwagSong = SongLoad.loadFromJson('stress', 'stress');
var notes = swagshit.extraNotes.get('picospeaker');
var notes:Array<SwagSection> = swagshit.noteMap.get('picospeaker');
for (section in notes)
{
for (idk in section.sectionNotes)
for (noteData in section.sectionNotes)
{
animationNotes.push(idk);
animationNotes.push(noteData);
}
}
TankmenBG.animationNotes = animationNotes;
trace(animationNotes);
animationNotes.sort(sortAnims);
}
function sortAnims(val1:Array<Dynamic>, val2:Array<Dynamic>):Int
function sortAnims(val1:NoteData, val2:NoteData):Int
{
return FlxSort.byValues(FlxSort.ASCENDING, val1[0], val2[0]);
return FlxSort.byValues(FlxSort.ASCENDING, val1.strumTime, val2.strumTime);
}
function quickAnimAdd(name:String, prefix:String)
@ -659,13 +659,13 @@ class Character extends FlxSprite
// for pico??
if (animationNotes.length > 0)
{
if (Conductor.songPosition > animationNotes[0][0])
if (Conductor.songPosition > animationNotes[0].strumTime)
{
trace('played shoot anim' + animationNotes[0][1]);
trace('played shoot anim' + animationNotes[0].noteData);
var shootAnim:Int = 1;
if (animationNotes[0][1] >= 2)
if ((cast animationNotes[0].noteData) >= 2)
shootAnim = 3;
shootAnim += FlxG.random.int(0, 1);

View file

@ -98,9 +98,11 @@ class Main extends Sprite
#if debug
fpsCounter = new FPS(10, 3, 0xFFFFFF);
addChild(fpsCounter);
#if !html5
memoryCounter = new MemoryCounter(10, 13, 0xFFFFFF);
addChild(memoryCounter);
#end
#end
/*
video = new Video();

View file

@ -10,9 +10,7 @@ import ui.PreferencesMenu;
using StringTools;
#if polymod
import polymod.format.ParseRules.TargetSignatureElement;
#end
class Note extends FlxSprite
{

View file

@ -111,8 +111,6 @@ class PlayState extends MusicBeatState
var foregroundSprites:FlxTypedGroup<BGSprite>;
var tankmanRun:FlxTypedGroup<TankmenBG>;
var talking:Bool = true;
var songScore:Int = 0;
var scoreTxt:FlxText;
@ -429,13 +427,13 @@ class PlayState extends MusicBeatState
switch (curStageId)
{
case 'limo':
case 'limoRide':
gfVersion = 'gf-car';
case 'mall' | 'mallEvil':
case 'mallXmas' | 'mallEvil':
gfVersion = 'gf-christmas';
case 'school' | 'schoolEvil':
gfVersion = 'gf-pixel';
case 'tank':
case 'tankmanBattlefield':
gfVersion = 'gf-tankmen';
}
@ -455,23 +453,6 @@ class PlayState extends MusicBeatState
case 'pico-speaker':
gf.x -= 50;
gf.y -= 200;
var tempTankman:TankmenBG = new TankmenBG(20, 500, true);
tempTankman.strumTime = 10;
tempTankman.resetShit(20, 600, true);
tankmanRun.add(tempTankman);
for (i in 0...TankmenBG.animationNotes.length)
{
if (FlxG.random.bool(16))
{
var tankman:TankmenBG = tankmanRun.recycle(TankmenBG);
// new TankmenBG(500, 200 + FlxG.random.int(50, 100), TankmenBG.animationNotes[i][1] < 2);
tankman.strumTime = TankmenBG.animationNotes[i][0];
tankman.resetShit(500, 200 + FlxG.random.int(50, 100), TankmenBG.animationNotes[i][1] < 2);
tankmanRun.add(tankman);
}
}
}
dad = new Character(100, 100, SONG.player2);
@ -2020,7 +2001,11 @@ class PlayState extends MusicBeatState
curStage.onNoteMiss(daNote);
}
health -= 0.0775;
vocals.volume = 0;
// Practice mode doesn't mute the vocals on miss.
if (!practiceMode)
{
vocals.volume = 0;
}
killCombo();
}
@ -2512,8 +2497,12 @@ class PlayState extends MusicBeatState
if (!practiceMode)
songScore -= 10;
vocals.volume = 0;
FlxG.sound.play(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2));
// Practice mode doesn't mute the vocals on miss.
if (!practiceMode)
{
vocals.volume = 0;
FlxG.sound.play(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2));
}
/* boyfriend.stunned = true;

View file

@ -21,7 +21,7 @@ class StoryMenuState extends MusicBeatState
{
var scoreText:FlxText;
var weekData:Array<Dynamic> = [
var weekData:Array<Array<String>> = [
['Tutorial'],
['Bopeebo', 'Fresh', 'Dadbattle'],
['Spookeez', 'South', "Monster"],
@ -430,11 +430,10 @@ class StoryMenuState extends MusicBeatState
// grpWeekCharacters.members[0].updateHitbox();
}
var stringThing:Array<String> = weekData[curWeek];
for (i in stringThing)
var trackNames:Array<String> = weekData[curWeek];
for (i in trackNames)
{
txtTracklist.text += "\n" + i;
txtTracklist.text += '\n${i}';
}
txtTracklist.text = txtTracklist.text.toUpperCase();

View file

@ -1,89 +0,0 @@
package;
import flixel.FlxSprite;
import haxe.display.Display.Package;
class TankmenBG extends FlxSprite
{
public static var animationNotes:Array<Dynamic> = [];
public var strumTime:Float = 0;
public var goingRight:Bool = false;
public var tankSpeed:Float = 0.7;
public var endingOffset:Float;
public function new(x:Float, y:Float, isGoingRight:Bool)
{
super(x, y);
// makeGraphic(200, 200);
frames = Paths.getSparrowAtlas('tankmanKilled1');
antialiasing = true;
animation.addByPrefix('run', 'tankman running', 24, true);
animation.addByPrefix('shot', 'John Shot ' + FlxG.random.int(1, 2), 24, false);
animation.play('run');
animation.curAnim.curFrame = FlxG.random.int(0, animation.curAnim.numFrames - 1);
updateHitbox();
setGraphicSize(Std.int(width * 0.8));
updateHitbox();
}
public function resetShit(x:Float, y:Float, isGoingRight:Bool)
{
setPosition(x, y);
goingRight = isGoingRight;
endingOffset = FlxG.random.float(50, 200);
tankSpeed = FlxG.random.float(0.6, 1);
if (goingRight)
flipX = true;
}
override function update(elapsed:Float)
{
super.update(elapsed);
if (x >= FlxG.width * 1.2 || x <= FlxG.width * -0.5)
visible = false;
else
visible = true;
if (animation.curAnim.name == 'run')
{
var endDirection:Float = (FlxG.width * 0.74) + endingOffset;
if (goingRight)
{
endDirection = (FlxG.width * 0.02) - endingOffset;
x = (endDirection + (Conductor.songPosition - strumTime) * tankSpeed);
}
else
{
x = (endDirection - (Conductor.songPosition - strumTime) * tankSpeed);
}
}
if (Conductor.songPosition > strumTime)
{
// kill();
animation.play('shot');
if (goingRight)
{
offset.y = 200;
offset.x = 300;
}
}
if (animation.curAnim.name == 'shot' && animation.curAnim.curFrame >= animation.curAnim.frames.length - 1)
{
kill();
}
}
}

View file

@ -351,17 +351,6 @@ class TitleState extends MusicBeatState
FlxG.switchState(new CutsceneAnimTestState());
#end
/*
if (FlxG.keys.justPressed.R)
{
#if polymod
polymod.Polymod.init({modRoot: "mods", dirs: ['introMod']});
trace('reinitialized');
#end
}
*/
if (FlxG.sound.music != null)
Conductor.songPosition = FlxG.sound.music.time;
if (FlxG.keys.justPressed.F)

View file

@ -1,3 +0,0 @@
BF: 770, 450
GF: 400, 130
DD: 100, 100

View file

@ -1,8 +1,6 @@
package modding;
#if polymod
import polymod.hscript.HScriptable;
#end
/**
* Add this interface to a class to make it a scriptable object.
@ -12,4 +10,4 @@ import polymod.hscript.HScriptable;
// ALL of these values are added to ALL scripts in the child classes.
context: [FlxG, FlxSprite, Math, Paths, Std]
})
interface IHook #if polymod extends HScriptable #end {}
interface IHook extends HScriptable {}

View file

@ -0,0 +1,71 @@
package modding;
import polymod.Polymod;
class PolymodErrorHandler
{
/**
* Show a popup with the given text.
* This displays a system popup, it WILL interrupt the game.
* Make sure to only use this when it's important, like when there's a script error.
*
* @param name The name at the top of the popup.
* @param desc The body text of the popup.
*/
public static function showAlert(name:String, desc:String):Void
{
lime.app.Application.current.window.alert(desc, name);
}
public static function onPolymodError(error:PolymodError):Void
{
// Perform an action based on the error code.
switch (error.code)
{
case MOD_LOAD_PREPARE:
logInfo('[POLYMOD]: ${error.message}');
case MOD_LOAD_DONE:
logInfo('[POLYMOD]: ${error.message}');
case MISSING_ICON:
logWarn('[POLYMOD]: A mod is missing an icon. Please add one.');
case SCRIPT_PARSE_ERROR:
// A syntax error when parsing a script.
logError('[POLYMOD]: ${error.message}');
showAlert('Polymod Script Parsing Error', error.message);
case SCRIPT_EXCEPTION:
// A runtime error when running a script.
logError('[POLYMOD]: ${error.message}');
showAlert('Polymod Script Execution Error', error.message);
case SCRIPT_CLASS_NOT_FOUND:
// A scripted class tried to reference an unknown superclass.
logError('[POLYMOD]: ${error.message}');
showAlert('Polymod Script Parsing Error', error.message);
default:
// Log the message based on its severity.
switch (error.severity)
{
case NOTICE:
logInfo('[POLYMOD]: ${error.message}');
case WARNING:
logWarn('[POLYMOD]: ${error.message}');
case ERROR:
logError('[POLYMOD]: ${error.message}');
}
}
}
static function logInfo(message:String):Void
{
trace('[INFO ] ${message}');
}
static function logError(message:String):Void
{
trace('[ERROR] ${message}');
}
static function logWarn(message:String):Void
{
trace('[WARN ] ${message}');
}
}

View file

@ -1,13 +1,11 @@
package modding;
#if polymod
import polymod.Polymod.ModMetadata;
import polymod.Polymod;
import polymod.backends.OpenFLBackend;
import polymod.backends.PolymodAssets.PolymodAssetType;
import polymod.format.ParseRules.LinesParseFormat;
import polymod.format.ParseRules.TextFileFormat;
#end
class PolymodHandler
{
@ -29,12 +27,8 @@ class PolymodHandler
*/
public static function loadAllMods()
{
#if polymod
trace("Initializing Polymod (using all mods)...");
loadModsById(getAllModIds());
#else
trace("Polymod not initialized; not supported on this platform.");
#end
}
/**
@ -43,17 +37,12 @@ class PolymodHandler
public static function loadNoMods()
{
// We still need to configure the debug print calls etc.
#if polymod
trace("Initializing Polymod (using no mods)...");
loadModsById([]);
#else
trace("Polymod not initialized; not supported on this platform.");
#end
}
public static function loadModsById(ids:Array<String>)
{
#if polymod
if (ids.length == 0)
{
trace('You attempted to load zero mods.');
@ -72,7 +61,7 @@ class PolymodHandler
// The current version of our API.
apiVersion: API_VERSION,
// Call this function any time an error occurs.
errorCallback: onPolymodError,
errorCallback: PolymodErrorHandler.onPolymodError,
// Enforce semantic version patterns for each mod.
// modVersions: null,
// A map telling Polymod what the asset type is for unfamiliar file extensions.
@ -107,7 +96,9 @@ class PolymodHandler
}
for (mod in loadedModList)
{
trace(' * ${mod.title} v${mod.modVersion} [${mod.id}]');
}
#if debug
var fileList = Polymod.listModFiles("IMAGE");
@ -130,12 +121,8 @@ class PolymodHandler
for (item in fileList)
trace(' * $item');
#end
#else
trace("[POLYMOD] Mods are not supported on this platform.");
#end
}
#if polymod
static function buildParseRules():polymod.format.ParseRules
{
var output = polymod.format.ParseRules.getDefault();
@ -160,42 +147,12 @@ class PolymodHandler
}
}
static function onPolymodError(error:PolymodError):Void
public static function getAllMods():Array<ModMetadata>
{
// Perform an action based on the error code.
switch (error.code)
{
case MOD_LOAD_PREPARE:
trace('[POLYMOD] ${error.message}');
case MOD_LOAD_DONE:
trace('[POLYMOD] ${error.message}');
case MISSING_ICON:
trace('[POLYMOD] A mod is missing an icon. Please add one.');
default:
// Log the message based on its severity.
switch (error.severity)
{
case NOTICE:
trace('[POLYMOD] ${error.message}');
case WARNING:
trace('[POLYMOD] ${error.message}');
case ERROR:
trace('[POLYMOD] ${error.message}');
}
}
}
#end
public static function getAllMods()
{
#if polymod
trace('Scanning the mods folder...');
var modMetadata = Polymod.scan(MOD_FOLDER);
trace('Found ${modMetadata.length} mods when scanning.');
return modMetadata;
#else
return new Array<Dynamic>();
#end
}
public static function getAllModIds():Array<String>

View file

@ -0,0 +1,5 @@
# modding.base
This package is used to allow modders to create scripted classes which extend these base classes.
For example, one script can extend FlxSprite and another can call `ScriptedFlxSprite.init('ClassName')`.
Most of these scripted class stubs are not used by the game itself, so this package has been explicitly marked to be ignored by DCE.

View file

@ -0,0 +1,10 @@
package modding.base;
import flixel.FlxSprite;
import modding.IHook;
@:hscriptClass
class ScriptedFlxSprite extends FlxSprite implements IHook
{
// No body needed for this class, it's magic ;)
}

View file

@ -0,0 +1,10 @@
package modding.base;
import flixel.group.FlxSpriteGroup;
import modding.IHook;
@:hscriptClass
class ScriptedFlxSpriteGroup extends FlxSpriteGroup implements IHook
{
// No body needed for this class, it's magic ;)
}

View file

@ -0,0 +1,12 @@
package modding.base;
import flixel.FlxState;
import modding.IHook;
// TODO: Polymod is having trouble with this one.
// Maybe there's a type parameter that's nested too much?
// @:hscriptClass
// class ScriptedFlxState extends FlxState implements IHook
// {
// // No body needed for this class, it's magic ;)
// }

View file

@ -0,0 +1,12 @@
package modding.base;
import flixel.FlxSubState;
import modding.IHook;
// TODO: Polymod is having trouble with this one.
// Maybe there's a type parameter that's nested too much?
// @:hscriptClass
// class ScriptedFlxSubState extends FlxSubState implements IHook
// {
// // No body needed for this class, it's magic ;)
// }

View file

@ -0,0 +1,10 @@
package play.stage;
import modding.IHook;
@:hscriptClass
@:keep
class ScriptedBopper extends Bopper implements IHook
{
// No body needed for this class, it's magic ;)
}

View file

@ -2,14 +2,6 @@ package play.stage;
import modding.IHook;
/**
* NOTE: Turns out one of the few edge case that scripted classes are broken with,
* that being generic classes with a constrained type argument, applies to FlxSpriteGroup.
* Will have to find a fix for the issue before stages can have scripting enabled.
*
* In the meantime though, I want to get stages working just with JSON.
* -Eric
*/
@:hscriptClass
class ScriptedStage extends Stage implements IHook
{

View file

@ -34,7 +34,6 @@ class StageDataParser
clearStageCache();
trace("[STAGEDATA] Loading stage cache...");
#if polymod
//
// SCRIPTED STAGES
//
@ -58,21 +57,15 @@ class StageDataParser
trace(' Failed to instantiate scripted stage class: ${stageCls}');
}
}
#end
//
// UNSCRIPTED STAGES
//
var stageIdList:Array<String> = DataAssets.listDataFilesInPath('stages/');
var unscriptedStageIds:Array<String> =
#if polymod
stageIdList.filter(function(stageId:String):Bool
{
return !stageCache.exists(stageId);
});
#else
stageIdList;
#end
var unscriptedStageIds:Array<String> = stageIdList.filter(function(stageId:String):Bool
{
return !stageCache.exists(stageId);
});
trace(' Instantiating ${unscriptedStageIds.length} non-scripted stages...');
for (stageId in unscriptedStageIds)
{

View file

@ -16,7 +16,6 @@ class BuildingShaders
public function update(elapsed:Float):Void
{
shader.alphaShit.value[0] += elapsed;
trace(shader.alphaShit.value[0]);
}
public function reset()

View file

@ -1,16 +1,16 @@
package ui;
import modding.PolymodHandler;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import polymod.Polymod;
import sys.FileSystem;
class ModMenu extends ui.OptionsState.Page
{
var grpMods:FlxTypedGroup<ModMenuItem>;
var enabledMods:Array<String> = [];
var modFolders:Array<String> = [];
var enabledMods:Array<ModMetadata> = [];
var detectedMods:Array<ModMetadata> = [];
var curSelected:Int = 0;
@ -62,10 +62,10 @@ class ModMenu extends ui.OptionsState.Page
{
curSelected += change;
if (curSelected >= modFolders.length)
if (curSelected >= detectedMods.length)
curSelected = 0;
if (curSelected < 0)
curSelected = modFolders.length - 1;
curSelected = detectedMods.length - 1;
for (txt in 0...grpMods.length)
{
@ -80,8 +80,6 @@ class ModMenu extends ui.OptionsState.Page
organizeByY();
}
inline static var MOD_PATH = "./mods";
private function refreshModList():Void
{
while (grpMods.members.length > 0)
@ -90,36 +88,17 @@ class ModMenu extends ui.OptionsState.Page
}
#if desktop
var modList = [];
modFolders = [];
detectedMods = PolymodHandler.getAllMods();
trace("mods path:" + FileSystem.absolutePath(MOD_PATH));
if (!FileSystem.exists(MOD_PATH))
trace('ModMenu: Detected ${detectedMods.length} mods');
for (index in 0...detectedMods.length)
{
FlxG.log.warn("missing mods folder, expected: " + FileSystem.absolutePath(MOD_PATH));
return;
}
for (file in FileSystem.readDirectory(MOD_PATH))
{
if (FileSystem.isDirectory(MOD_PATH + file))
modFolders.push(file);
}
enabledMods = [];
modList = Polymod.scan(MOD_PATH);
trace(modList);
var loopNum:Int = 0;
for (i in modFolders)
{
var txt:ModMenuItem = new ModMenuItem(0, 10 + (40 * loopNum), 0, i, 32);
txt.text = i;
var modMetadata:ModMetadata = detectedMods[index];
var modName:String = modMetadata.title;
var txt:ModMenuItem = new ModMenuItem(0, 10 + (40 * index), 0, modName, 32);
txt.text = modName;
grpMods.add(txt);
loopNum++;
}
#end
}

View file

@ -33,9 +33,7 @@ class OptionsState extends MusicBeatState
var controls = addPage(Controls, new ControlsMenu());
// var colors = addPage(Colors, new ColorsMenu());
#if polymod
var mods = addPage(Mods, new ModMenu());
#end
if (options.hasMultipleOptions())
{
@ -43,10 +41,7 @@ class OptionsState extends MusicBeatState
controls.onExit.add(switchPage.bind(Options));
// colors.onExit.add(switchPage.bind(Options));
preferences.onExit.add(switchPage.bind(Options));
#if polymod
mods.onExit.add(switchPage.bind(Options));
#end
}
else
{
@ -180,9 +175,7 @@ class OptionsMenu extends Page
createItem(t("PREFERENCES"), function() switchPage(Preferences));
createItem(t("CONTROLS"), function() switchPage(Controls));
// createItem(t("COLORS"), function() switchPage(Colors));
#if polymod
createItem(t("MODS"), function() switchPage(Mods));
#end
#if CAN_OPEN_LINKS
if (showDonate)