Merge branch 'main' into new-editors

This commit is contained in:
⍚~Nex
2024-09-20 14:40:25 +02:00
23 changed files with 201 additions and 78 deletions
+3 -1
View File
@@ -21,6 +21,8 @@ Make sure to read the wiki and join the Discord server linked there!<br>
Latest builds for the engine can be found in the [Actions](https://github.com/YoshiCrafter29/CodenameEngine/actions) tab.<br>
In the future (when the engine won't be a WIP anymore) we're gonna also publish the engine on platforms like Gamebanana; stay tuned!
If you don't have a github account please go onto https://fnf-cne-devs.github.io/ and click the download button for the respective operating system.
<details>
<summary><h2>How to build</h2></summary>
@@ -76,7 +78,7 @@ In the future (when the engine won't be a WIP anymore) we're gonna also publish
- Steal code from Codename Engine for another different project that is not Codename Engine related (Codename Engine mods excluded) without properly crediting
- Release the entire Codename Engine on platforms (Mods that use Codename Engine as source are fine, if it's specified even better)
#### *If you need more info or feel like asking to do something which is not listed here, ask us directly on our discord (linked in the wiki)!*
#### *If you need more info or feel like asking to do something which is not listed here, ask us directly on our discord (linked on the website)!*
</details>
<details>
Binary file not shown.
+5 -4
View File
@@ -1,7 +1,8 @@
@ECHO OFF
cd ..
echo Building Game...
lime build windows --haxeflag="--macro include('scripting')" --haxeflag="-xml docs/doc.xml"
echo Generating Documentation...
haxelib run dox -i docs --include funkin --include scripting
echo Done!
lime build windows --haxeflag="--macro include('scripting')" --haxeflag="-xml docs/doc.xml" -D DOCUMENTATION --no-output
echo art
echo Generated the api xml file at docs/doc.xml
echo Please put this in FNF-CNE-Devs.github.io/api-generator/api/doc.xml
+1
View File
@@ -1,3 +1,4 @@
//
import funkin.game.HudCamera;
import funkin.backend.scripting.events.NoteHitEvent;
-1
View File
@@ -1,5 +1,4 @@
function create() {
importScript("data/scripts/pixel");
isSpooky = true;
// Make transition work between school types
+1
View File
@@ -1,5 +1,6 @@
<!DOCTYPE codename-engine-stage>
<stage zoom="1" name="school-evil" folder="stages/school/" startCamPosY="550" startCamPosX="600">
<use-extension script="pixel"/>
<sprite name="bg" x="400" y="200" sprite="animatedEvilSchool"
antialiasing="false" scale="6" scrollx="0.8" scrolly="0.9" type="loop"/>
</stage>
+1 -1
View File
@@ -1,5 +1,5 @@
//
function create() {
importScript("data/scripts/pixel");
if (PlayState.SONG.meta.name.toLowerCase() == "roses") {
bgGirls.animation.remove("danceLeft");
bgGirls.animation.remove("danceRight");
+2
View File
@@ -1,5 +1,7 @@
<!DOCTYPE codename-engine-stage>
<stage zoom="1" folder="stages/school/" startCamPosY="550" startCamPosX="600">
<use-extension script="pixel"/>
<sprite name="bgSky" x="-12" y="0" sprite="weebSky"
antialiasing="false" scale="6" scroll="0.1" updateHitbox="true"/>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 44 KiB

+1 -1
View File
@@ -15,7 +15,7 @@ class FlxAnimate extends flxanimate.FlxAnimate {
for (camera in cameras)
{
rMatrix.identity();
rMatrix.translate(-limb.offset.x, -limb.offset.y);
limb.prepareMatrix(rMatrix, FlxFrameAngle.ANGLE_0, _checkFlipX() != camera.flipX, _checkFlipY() != camera.flipY);
rMatrix.concat(_rMatrix);
if (!camera.visible || !camera.exists || !limbOnScreen(limb, _rMatrix, camera))
return;
+1 -1
View File
@@ -64,7 +64,7 @@ class HScript extends Script {
}
private function importFailedCallback(cl:Array<String>):Bool {
if(_importFailedCallback(cl, "") || _importFailedCallback(cl, "source/")) {
if(_importFailedCallback(cl, "source/") || _importFailedCallback(cl, "")) {
return true;
}
return false;
+1 -1
View File
@@ -283,7 +283,7 @@ class Script extends FlxBasic implements IFlxDestroyable {
public function get(variable:String):Dynamic {return null;}
/**
* Gets the variable `variable` from the script's variables.
* Sets the variable `variable` from the script's variables.
* @param variable Name of the variable.
* @return Variable (or null if it doesn't exists)
*/
@@ -0,0 +1,23 @@
package funkin.backend.scripting.events;
import funkin.game.SplashGroup;
import funkin.game.Strum;
final class SplashShowEvent extends CancellableEvent {
/**
Splash name/type
**/
public var splashName:String;
/**
Splash that is shown
**/
public var splash:FunkinSprite;
/**
Strum that the splash is shown on
**/
public var strum:Strum;
/**
Splash group that the splash is from
**/
public var group:SplashGroup;
}
+11 -12
View File
@@ -438,34 +438,33 @@ uniform sampler2D bitmap;
uniform bool hasTransform;
uniform bool hasColorTransform;
vec4 flixel_texture2D(sampler2D bitmap, vec2 coord)
{
vec4 color = texture2D(bitmap, coord);
if (!hasTransform)
{
vec4 applyFlixelEffects(vec4 color) {
if (!hasTransform) {
return color;
}
if (color.a == 0.0)
{
if (color.a == 0.0) {
return vec4(0.0, 0.0, 0.0, 0.0);
}
if (!hasColorTransform)
{
if (!hasColorTransform) {
return color * openfl_Alphav;
}
color = vec4(color.rgb / color.a, color.a);
color.rgb = color.rgb / color.a;
color = clamp(openfl_ColorOffsetv + (color * openfl_ColorMultiplierv), 0.0, 1.0);
if (color.a > 0.0)
{
if (color.a > 0.0) {
return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
}
return vec4(0.0, 0.0, 0.0, 0.0);
}
vec4 flixel_texture2D(sampler2D bitmap, vec2 coord) {
vec4 color = texture2D(bitmap, coord);
return applyFlixelEffects(color);
}
uniform vec4 _camSize;
float map(float value, float min1, float max1, float min2, float max2) {
@@ -75,34 +75,13 @@ class EventMacro {
// add a "set this" expr for each variable
for(v in values) {
var name = v.name;
exprs.push({
pos: Context.currentPos(),
expr: EBinop(OpAssign, {
pos: Context.currentPos(),
expr: EField({
pos: Context.currentPos(),
expr: EConst(CIdent("this"))
}, name)
}, {
pos: Context.currentPos(),
expr: EConst(CIdent(name))
})
});
exprs.push(macro this.$name = $i{name});
}
// add a "set this" expr to reset each private/hidden variables
for(v in hiddenValues) {
var name = v.name;
exprs.push({
pos: Context.currentPos(),
expr: EBinop(OpAssign, {
pos: Context.currentPos(),
expr: EField({
pos: Context.currentPos(),
expr: EConst(CIdent("this"))
}, name)
}, v.expr)
});
exprs.push(macro this.$name = ${v.expr});
}
exprs.push(macro return this);
+11 -1
View File
@@ -117,7 +117,17 @@ final class MemoryUtil {
22 => "DDR2 FB-DIMM",
24 => "DDR3",
25 => "FBD2",
26 => "DDR4"
26 => "DDR4",
27 => "LPDDR",
28 => "LPDDR2",
29 => "LPDDR3",
30 => "LPDDR4",
31 => "Logical Non-volatile device",
32 => "HBM",
33 => "HBM2",
34 => "DDR5",
35 => "LPDDR5",
36 => "HBM3",
];
var memoryOutput:Int = -1;
+14 -2
View File
@@ -22,11 +22,21 @@ class EditorPicker extends MusicBeatSubstate {
iconID: 2,
state: funkin.editors.stage.StageSelection
},
#if debug
{
name: "UI Debug State",
iconID: 3,
state: UIDebugState
},
#end
{
name: "Wiki",
iconID: 5,
state: null,
onClick: function() {
CoolUtil.openURL("https://fnf-cne-devs.github.io/");
}
},
{
name: "Debug Options",
iconID: 4,
@@ -94,7 +104,9 @@ class EditorPicker extends MusicBeatSubstate {
}
if (controls.ACCEPT || FlxG.mouse.justReleased) {
if (options[curSelected].state != null) {
if(options[curSelected].onClick != null)
options[curSelected].onClick();
else if (options[curSelected].state != null) {
selected = true;
CoolUtil.playMenuSFX(CONFIRM);
@@ -145,6 +157,7 @@ typedef Editor = {
var name:String;
var iconID:Int;
var state:Class<MusicBeatState>;
var ?onClick:Void->Void;
}
class EditorPickerOption extends FlxTypedSpriteGroup<FlxSprite> {
@@ -161,7 +174,6 @@ class EditorPickerOption extends FlxTypedSpriteGroup<FlxSprite> {
public function new(name:String, iconID:Int, height:Float) {
super();
FlxG.mouse.visible = true;
iconSpr = new FlxSprite();
iconSpr.loadGraphic(Paths.image('editors/icons'), true, 128, 128);
+9 -3
View File
@@ -34,10 +34,16 @@ class NoteGroup extends FlxTypedGroup<Note> {
return FlxSort.byValues(FlxSort.DESCENDING, n1.strumTime, n2.strumTime);
});
}
@:dox(hide) public var __forcedSongPos:Null<Float> = null;
@:dox(hide) private inline function __getSongPos()
return __forcedSongPos == null ? Conductor.songPosition : __forcedSongPos;
public override function update(elapsed:Float) {
i = length-1;
__loopSprite = null;
__time = Conductor.songPosition;
__time = __getSongPos();
while(i >= 0) {
__loopSprite = members[i--];
if (__loopSprite == null || !__loopSprite.exists || !__loopSprite.active) {
@@ -58,7 +64,7 @@ class NoteGroup extends FlxTypedGroup<Note> {
i = length-1;
__loopSprite = null;
__time = Conductor.songPosition;
__time = __getSongPos();
while(i >= 0) {
__loopSprite = members[i--];
if (__loopSprite == null || !__loopSprite.exists || !__loopSprite.visible)
@@ -81,7 +87,7 @@ class NoteGroup extends FlxTypedGroup<Note> {
public override function forEach(noteFunc:Note->Void, recursive:Bool = false) {
i = length-1;
__loopSprite = null;
__time = Conductor.songPosition;
__time = __getSongPos();
var oldCur = __currentlyLooping;
__currentlyLooping = true;
+9 -1
View File
@@ -242,7 +242,7 @@ class PlayState extends MusicBeatState
/**
* Current stage name
*/
public var curStage:String = "";
public var curStage(get, set):String;
/**
* Interval at which Girlfriend dances.
@@ -525,6 +525,14 @@ class PlayState extends MusicBeatState
return this.maxHealth = v;
}
private inline function get_curStage()
return stage == null ? "" : stage.stageName;
private inline function set_curStage(name:String) {
if (stage != null) stage.stageName = name;
return name;
}
@:dox(hide) override public function create()
{
Note.__customNoteTypeExists = [];
+4 -2
View File
@@ -1,5 +1,7 @@
package funkin.game;
import funkin.backend.scripting.events.SplashShowEvent;
class SplashHandler extends FlxTypedGroup<FunkinSprite> {
/**
* Map containing all of the splashes group.
@@ -40,10 +42,10 @@ class SplashHandler extends FlxTypedGroup<FunkinSprite> {
var __grp:SplashGroup;
public function showSplash(name:String, strum:Strum) {
__grp = getSplashGroup(name);
var event = funkin.backend.scripting.EventManager.get(funkin.backend.scripting.events.DynamicEvent).recycle(__grp.showOnStrum(strum), strum);
var event = EventManager.get(SplashShowEvent).recycle(name, __grp.showOnStrum(strum), strum, __grp);
event = PlayState.instance.scripts.event("onSplashShown", event);
if (!event.cancelled)
add(event.value1);
add(event.splash);
// max 8 rendered splashes
while(members.length > 8)
+94 -24
View File
@@ -3,6 +3,7 @@ package funkin.game;
import flixel.util.typeLimit.OneOfTwo;
import flixel.FlxState;
import flixel.math.FlxPoint;
import funkin.backend.scripting.DummyScript;
import funkin.backend.scripting.Script;
import funkin.backend.scripting.events.StageNodeEvent;
import funkin.backend.scripting.events.StageXMLEvent;
@@ -23,6 +24,7 @@ class Stage extends FlxBasic implements IBeatReceiver {
public var stageScript:Script;
public var state:FlxState;
public var characterPoses:Map<String, StageCharPos> = [];
public var xmlImportedScripts:Array<XMLImportedScriptInfo> = [];
public var defaultZoom:Float = 1.05;
public var startCam = new FlxPoint();
@@ -31,12 +33,15 @@ class Stage extends FlxBasic implements IBeatReceiver {
public var onNodeLoaded:(Access, Dynamic)->Dynamic = null;
public var onNodeFinished:(Access, Dynamic)->Void = null;
public var onAddSprite:(FlxObject)->Void = null;
public var onXMLPostLoaded:(Access, Array<Access>)->Array<Access> = null;
public var spritesParentFolder = "";
public inline function getSprite(name:String) {
public inline function getSprite(name:String)
return stageSprites[name];
}
public function setStagesSprites(script:Script)
for (k=>e in stageSprites) script.set(k, e);
public function new(stage:String, ?state:FlxState, autoLoad:Bool = true) {
super();
@@ -46,13 +51,9 @@ class Stage extends FlxBasic implements IBeatReceiver {
this.state = state;
stageFile = stage;
stagePath = Paths.xml('stages/$stageFile');
try {
if (Assets.exists(stagePath)) stageXML = new Access(Xml.parse(Assets.getText(stagePath)).firstElement());
} catch(e) {
Logs.trace('Couldn\'t load stage "$stageFile": ${e.message}', ERROR);
}
try if (Assets.exists(stagePath)) stageXML = new Access(Xml.parse(Assets.getText(stagePath)).firstElement())
catch(e) Logs.trace('Couldn\'t load stage "$stageFile": ${e.message}', ERROR);
if (autoLoad) loadXml(stageXML);
}
@@ -66,6 +67,8 @@ class Stage extends FlxBasic implements IBeatReceiver {
stageScript.load();
}
var event = null;
var elems:Array<Access> = [];
if (xml != null) {
var parsed:Null<Float>;
if((parsed = Std.parseFloat(xml.getAtt("startCamPosX"))).isNotNull()) startCam.x = parsed;
@@ -78,7 +81,6 @@ class Stage extends FlxBasic implements IBeatReceiver {
if(xml.has.startCamPosX) PlayState.instance.camFollow.x = startCam.x;
if(xml.has.startCamPosY) PlayState.instance.camFollow.y = startCam.y;
if(xml.has.zoom) PlayState.instance.defaultCamZoom = defaultZoom;
PlayState.instance.curStage = stageName;
}
if (xml.has.folder) {
spritesParentFolder = xml.att.folder;
@@ -90,22 +92,16 @@ class Stage extends FlxBasic implements IBeatReceiver {
if(!DEFAULT_ATTRIBUTES.contains(att))
extra.set(att, xml.x.get(att));
var elems:Array<Access> = [];
// some way to tag that the sprites are from the group
for(node in xml.elements) {
if (node.name == "high-memory" && (!Options.lowMemoryMode || forceLoadAll))
for(e in node.elements)
elems.push(e);
else if (node.name == "low-memory" && (Options.lowMemoryMode || forceLoadAll))
for(e in node.elements)
elems.push(e);
else
elems.push(node);
if (node.name == "high-memory" && (!Options.lowMemoryMode || forceLoadAll)) for(e in node.elements) __pushNcheckNode(elems, e);
else if (node.name == "low-memory" && (Options.lowMemoryMode || forceLoadAll)) for(e in node.elements) __pushNcheckNode(elems, e);
else __pushNcheckNode(elems, node);
}
if (PlayState.instance == state) {
var event = PlayState.instance.scripts.event("onStageXMLParsed", EventManager.get(StageXMLEvent).recycle(this, xml, elems));
elems = event.elems;
event = EventManager.get(StageXMLEvent).recycle(this, xml, elems);
elems = PlayState.instance.scripts.event("onStageXMLParsed", event).elems;
}
if(onXMLLoaded != null) {
elems = onXMLLoaded(xml, elems);
@@ -121,7 +117,7 @@ class Stage extends FlxBasic implements IBeatReceiver {
stageSprites.set(spr.name, spr);
addSprite(spr);
case "box" | "solid":
if ( !node.has.name || !node.has.width || !node.has.height) continue;
if (!node.has.name || !node.has.width || !node.has.height) continue;
var spr = new FunkinSprite(
(node.has.x) ? Std.parseFloat(node.att.x).getDefault(0) : 0,
@@ -154,6 +150,9 @@ class Stage extends FlxBasic implements IBeatReceiver {
);
PlayState.instance.add(PlayState.instance.comboGroup);
PlayState.instance.comboGroup;
case "use-extension" | "extension" | "ext":
if (__shouldLoadBefore(node) || XMLImportedScriptInfo.prepareInfos(node, this) == null) continue;
null;
default: null;
}
@@ -184,10 +183,31 @@ class Stage extends FlxBasic implements IBeatReceiver {
if (characterPoses["boyfriend"] == null)
addCharPos("boyfriend", null, getDefaultPos("boyfriend"));
if (PlayState.instance != state) return;
for(k=>e in stageSprites) {
stageScript.set(k, e);
if (PlayState.instance == state) {
setStagesSprites(stageScript);
// i know this for gets run twice under, but its better like this in case a script modifies the short lived ones, i dont wanna save them in an array; more dynamic like this - Nex
for (info in xmlImportedScripts) if (info.importStageSprites) {
var script = info.getScript();
if (script != null) setStagesSprites(script);
}
// idk lemme check anyways just in case scripts did smth - Nex
if (event != null) PlayState.instance.scripts.event("onPostStageCreation", event);
// shortlived scripts destroy when the stage finishes setting up - Nex
for (info in xmlImportedScripts) if (info.shortLived) {
var script = info.getScript();
if (script == null) continue;
PlayState.instance.scripts.remove(script);
script.destroy();
}
}
if(onXMLPostLoaded != null) {
elems = onXMLPostLoaded(xml, elems);
}
}
public static function getDefaultPos(name:String):StageCharPosInfo {
@@ -219,6 +239,15 @@ class Stage extends FlxBasic implements IBeatReceiver {
}
}
@:dox(hide) private function __pushNcheckNode(array:Array<Access>, node:Access) {
array.push(node);
if ((node.name == "use-extension" || node.name == "extension" || node.name == "ext") && __shouldLoadBefore(node))
XMLImportedScriptInfo.prepareInfos(node, this);
}
@:dox(hide) private static inline function __shouldLoadBefore(node:Access):Bool
return node.getAtt("loadBefore") != "false";
public function addCharPos(name:String, node:Access, ?nonXMLInfo:StageCharPosInfo):StageCharPos {
var charPos = new StageCharPos();
charPos.visible = charPos.active = false;
@@ -410,4 +439,45 @@ typedef OldCharInfo = {
var alpha:Float;
var zoomFactor:Float;
var angle:Float;
}
class XMLImportedScriptInfo {
public var path:String;
public var shortLived:Bool = false;
public var loadBefore:Bool = true;
public var importStageSprites:Bool = false;
public function new(path:String)
this.path = path;
public function getScript():Script
return PlayState.instance == null ? null : PlayState.instance.scripts.getByPath(path);
public static function prepareInfos(node:Access, ?stage:Stage):XMLImportedScriptInfo {
if (!node.has.script || PlayState.instance == null) return null;
var folder = node.getAtt("folder").getDefault("data/scripts/");
if (!folder.endsWith("/")) folder += "/";
var path = Paths.script(folder + node.getAtt("script"));
var daScript = Script.create(path);
if (daScript is DummyScript) {
var msg = 'Script Extension at ${path} does not exist';
if (stage != null) msg += ' through stage "${stage.stageName}"';
Logs.trace(msg + ".", ERROR);
return null;
}
var infos = new XMLImportedScriptInfo(daScript.path);
infos.shortLived = node.getAtt("isShortLived") == "true";
infos.importStageSprites = node.getAtt("importStageSprites") == "true";
@:privateAccess infos.loadBefore = Stage.__shouldLoadBefore(node);
if (stage != null) stage.xmlImportedScripts.push(infos);
PlayState.instance.scripts.add(daScript);
daScript.set("scriptInfo", infos);
daScript.load();
return infos;
}
}
+4
View File
@@ -8,6 +8,7 @@ import funkin.backend.scripting.Script;
import funkin.backend.scripting.events.MenuChangeEvent;
import funkin.backend.scripting.events.NameEvent;
import funkin.backend.scripting.events.PauseCreationEvent;
import funkin.backend.system.Conductor;
import funkin.backend.utils.FunkinParentDisabler;
import funkin.editors.charter.Charter;
import funkin.menus.StoryMenuState;
@@ -162,6 +163,9 @@ class PauseSubState extends MusicBeatSubstate
PlayState.resetSongInfos();
if (Charter.instance != null) Charter.instance.__clearStatics();
// prevents certain notes to disappear early when exiting - Nex
game.strumLines.forEachAlive(function(grp) grp.notes.__forcedSongPos = Conductor.songPosition);
CoolUtil.playMenuSong();
FlxG.switchState(PlayState.isStoryMode ? new StoryMenuState() : new FreeplayState());
}
+4
View File
@@ -3,19 +3,23 @@ package hscript;
class Config {
// Runs support for custom classes in these
public static final ALLOWED_CUSTOM_CLASSES = [
#if !DOCUMENTATION
"flixel",
"funkin",
#end
];
// Runs support for abstract support in these
public static final ALLOWED_ABSTRACT_AND_ENUM = [
#if !DOCUMENTATION
"flixel",
"openfl",
"haxe.xml",
"haxe.CallStack",
"funkin",
#end
];
// Incase any of your files fail