Merge branch 'new-editors' into zoro-charterstuffs

This commit is contained in:
TheZoroForce240
2024-10-14 22:06:43 +01:00
65 changed files with 1706 additions and 225 deletions
+36
View File
@@ -0,0 +1,36 @@
# You need to run the following command in your repository:
#
# git config blame.ignoreRevsFile .git-blame-ignore-revs
#
# afterwards, revisions specified in this file will be ignored in git blame
# any revision needs to be in a unabbreviated sha1 format
# see https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revs-fileltfilegt for more information
# A good format to use is:
# # reason for ignoring the revisions
# <rev>
# Removed unused imports & reordered imports
d17ea72c74bbbadac0bb00e11868dddf6aebd346
# ScriptPack revert
8a2ac9e1d4e51e688cd2339bdccd6b39ed7d52a3
# Spaces to tabs
a63e5f5958ed0643fab92e9d5708a4c26e5238a1
# Removed useless indentation and more spaces to tabs
f8b20774e3f035eeddb9211a28a391b20db74456
# Revert a revert
75f6bec8294e5b783368cfd7ae6e45698c326cb3
4aa95838d7df9c307bcac6456e937081645a0f4a
# Revert a revert (note types use int)
17e177a69554f4d798f5acdd3799687dc9f01a93
008ad05bf1407d9772805ed69985cfbcde79e495
# Revert a revert (empty script)
392416e00cbd82e1c49e82a9fafdcf2e069a654e
159467de832a172681200c463d244a46e295c3c8
+2 -2
View File
@@ -38,12 +38,12 @@ jobs:
- name: Tar files
run: tar -zcvf CodenameEngine.tar.gz -C export/release/linux/bin .
- name: Uploading artifact (entire build)
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: Codename Engine
path: CodenameEngine.tar.gz
# - name: Uploading artifact (executable)
# uses: actions/upload-artifact@v2
# uses: actions/upload-artifact@v4
# with:
# name: Update-Linux
# path: export/release/linux/bin/CodenameEngine
+2 -2
View File
@@ -35,12 +35,12 @@ jobs:
- name: Tar files
run: tar -zcvf CodenameEngine.tar.gz -C export/release/macos/bin .
- name: Uploading artifact (entire build)
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: Codename Engine
path: CodenameEngine.tar.gz
# - name: Uploading artifact (executable)
# uses: actions/upload-artifact@v2
# uses: actions/upload-artifact@v4
# with:
# name: Update-Mac
# path: export/release/macos/bin/CodenameEngine
+2 -2
View File
@@ -33,12 +33,12 @@ jobs:
run: |
haxelib run lime build windows
- name: Uploading artifact (entire build)
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: Codename Engine
path: export/release/windows/bin
# - name: Uploading artifact (executable)
# uses: actions/upload-artifact@v2
# uses: actions/upload-artifact@v4
# with:
# name: Update-Windows
# path: export/release/windows/bin/CodenameEngine.exe
+1 -1
View File
@@ -35,7 +35,7 @@ Shoutout to the people who stuck with us throughout the alpha and supported us!
- ### New Features
- Opponent & Co-op modes
- New volume change SFX (no more loud flixel beep, customizable)
- New FPS counter allowing you to see advanced info by pressing F3.
- New FPS counter allowing you to see advanced info by pressing F3 (infos can be selected and copied too when in advanced).
- (Windows) FNF is no longer blurry on 125-150% DPI
- FNF auto fixes audio on state change when you plug in/out your headphones.
- FNF auto applies dark titlebar
+1 -1
View File
@@ -17,7 +17,7 @@ _**QOL = Quality of Life**_
- Usage of [flxanimate](https://github.com/Dot-Stuff/flxanimate) on big sprites, such as Girlfriend to save memory.
- You can further optimize it on certain stages by enabling `Low Memory Mode` in `Options > Appearance`.
- New volume change SFX (no more loud flixel beep, customizable)
- New FPS counter allowing you to see advanced info by pressing F3.
- New FPS counter allowing you to see advanced info by pressing F3 (infos can be selected and copied too when in advanced).
- Simple but advanced modding system (press TAB on main menu)
- **Windows only:**
- (Windows) FNF is no longer blurry on 125-150% DPI
+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;
+14 -10
View File
@@ -1,39 +1,43 @@
var fastCarCanDrive:Bool = true;
function create() {
function create()
resetFastCar();
}
function update(elapsed:Float) {
function update(elapsed:Float)
{
if (!fastCarCanDrive && fastCar.x > 2000) {
resetFastCar();
fastCarCanDrive = true;
}
var offset = Math.sin(curBeatFloat / 4) * Math.cos(curBeatFloat / 16) * 85;
// back limo drive
if (Options.lowMemoryMode) {
bgLimo.frameOffset.x = Math.sin(curBeatFloat / 4) * Math.cos(curBeatFloat / 16) * 85;
bgLimo.frameOffset.x = offset;
} else {
for(e in [bgLimo, dancer1, dancer2, dancer3, dancer4, dancer5]) {
e.frameOffset.x = Math.sin(curBeatFloat / 4) * Math.cos(curBeatFloat / 16) * 85;
e.frameOffset.x = offset;
}
}
}
function beatHit(curBeat:Int) {
function beatHit(curBeat:Int)
if (FlxG.random.bool(10) && fastCarCanDrive)
fastCarDrive();
}
function resetFastCar() {
function resetFastCar()
{
fastCar.x = -12600;
fastCar.y = FlxG.random.int(140, 250);
fastCar.velocity.x = 0;
fastCar.moves = false;
fastCarCanDrive = true;
}
function fastCarDrive()
{
FlxG.sound.play(Paths.soundRandom('carPass', 0, 1), 0.7);
fastCar.velocity.x = (FlxG.random.int(170, 220) / FlxG.elapsed) * 3;
fastCar.velocity.x = FlxG.random.int(170, 220) * 60 * 3;
fastCar.moves = true;
fastCarCanDrive = false;
}
+1
View File
@@ -1,3 +1,4 @@
//
import funkin.game.PlayState;
// phillyTrain is automatically added by Stage.hx
-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

+7 -7
View File
@@ -18,17 +18,17 @@ void main() {
return;
vec4 color = getColor(camPos);
float fsteps = steps;
for(float inside = 1.0; inside < stepsInside+1.0; inside++) {
float fsteps = float(steps);
for(float inside = 1.0; inside < float(stepsInside)+1.0; inside++) {
for(int i = 0; i < steps; i++) {
float fi = i;
float fi = float(i);
color += getColor(camPos + vec2(
strength * (inside / stepsInside) * cos(fi / fsteps * (PI * 2.0)),
strength * (inside / stepsInside) * sin(fi / fsteps * (PI * 2.0))
strength * (inside / float(stepsInside)) * cos(fi / fsteps * (PI * 2.0)),
strength * (inside / float(stepsInside)) * sin(fi / fsteps * (PI * 2.0))
));
}
}
color /= steps * stepsInside;
color /= vec4(steps * stepsInside);
gl_FragColor = color;
}
}
+4 -4
View File
@@ -55,10 +55,10 @@ class Setup {
case "lib" | "git":
final lib:Library = {
name: libNode.att.name,
type: libNode.name
type: libNode.name,
skipDeps: libNode.has.skipDeps ? libNode.att.skipDeps == "true" : false,
};
if (libNode.has.global) lib.global = libNode.att.global;
if (libNode.has.skipDeps) lib.skipDeps = libNode.att.skipDeps;
switch (lib.type) {
case "lib":
if (libNode.has.version) lib.version = libNode.att.version;
@@ -143,7 +143,7 @@ class Setup {
case INSTALL:
var lib:Library = event.data;
var globalSuffix:Null<String> = lib.global == "true" ? " --global" : "";
var skipDeps = lib.skipDeps == "true" ? " --skip-dependencies" : "";
var skipDeps = lib.skipDeps ? " --skip-dependencies" : "";
var commandPrefix = commandSuffix + globalSuffix + skipDeps;// + " --no-timeout";
switch(lib.type) {
case "lib":
@@ -278,8 +278,8 @@ class Setup {
typedef Library = {
var name:String;
var type:String;
var skipDeps:Bool;
var ?global:String;
var ?skipDeps:String;
var ?recursive:String;
var ?version:String;
var ?ref:String;
+1 -1
View File
@@ -46,7 +46,7 @@
<print pretty="true">Rebuilding Lime...</print>
<!- - Build lime - ->
<cmd inLib="lime" cmd="haxelib run lime rebuild . $PLATFORM -clean" />
<cmd inLib="lime" cmd="haxelib run lime rebuild . $PLATFORM -release -clean" />
<cmd inLib="lime" dir="tools" cmd="haxe tools.hxml" />
</if> -->
+1
View File
@@ -195,6 +195,7 @@
<haxedef name="HXCPP_CHECK_POINTER" if="release" />
<haxedef name="HXCPP_STACK_LINE" if="release" />
<haxedef name="HXCPP_DEBUG_LINK" if="release" />
<haxedef name="HXCPP_CATCH_SEGV" />
<haxedef name="hscriptPos" /> <!-- for logging -->
+7 -4
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;
@@ -25,10 +25,13 @@ class FlxAnimate extends flxanimate.FlxAnimate {
if (limb != _pivot) {
if (frameOffsetAngle != null && frameOffsetAngle != angle)
{
var angleOff = (-angle + frameOffsetAngle) * FlxAngle.TO_RAD;
rMatrix.rotate(-angleOff);
var angleOff = (frameOffsetAngle - angle) * FlxAngle.TO_RAD;
var cos = Math.cos(angleOff);
var sin = Math.sin(angleOff);
// cos doesnt need to be negated
rMatrix.rotateWithTrig(cos, -sin);
rMatrix.translate(-frameOffset.x, -frameOffset.y);
rMatrix.rotate(angleOff);
rMatrix.rotateWithTrig(cos, sin);
}
else
{
+7 -2
View File
@@ -112,7 +112,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
if (!debugMode && isAnimFinished()) {
var name = getAnimName() + '-loop';
if (hasAnimation(name))
playAnim(name, false, lastAnimContext);
playAnim(name, null, lastAnimContext);
}
}
@@ -290,11 +290,16 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
#if REGION
public var lastAnimContext:PlayAnimContext = DANCE;
public function playAnim(AnimName:String, Force:Bool = false, Context:PlayAnimContext = NONE, Reversed:Bool = false, Frame:Int = 0):Void
public function playAnim(AnimName:String, ?Force:Null<Bool>, Context:PlayAnimContext = NONE, Reversed:Bool = false, Frame:Int = 0):Void
{
if (AnimName == null)
return;
if (Force == null) {
var anim = animDatas.get(AnimName);
Force = anim != null && anim.forced;
}
if (animateAtlas != null)
{
@:privateAccess
+6 -4
View File
@@ -76,14 +76,16 @@ class Paths
inline static public function voices(song:String, difficulty:String = "normal", ?prefix:String = "")
{
var diff = getPath('songs/${song.toLowerCase()}/song/Voices$prefix-${difficulty.toLowerCase()}.$SOUND_EXT', null);
return OpenFlAssets.exists(diff) ? diff : getPath('songs/${song.toLowerCase()}/song/Voices$prefix.$SOUND_EXT', null);
song = song.toLowerCase();
var diff = getPath('songs/${song}/song/Voices$prefix-${difficulty.toLowerCase()}.$SOUND_EXT', null);
return OpenFlAssets.exists(diff) ? diff : getPath('songs/${song}/song/Voices$prefix.$SOUND_EXT', null);
}
inline static public function inst(song:String, difficulty:String = "normal", ?prefix:String = "")
{
var diff = getPath('songs/${song.toLowerCase()}/song/Inst$prefix-${difficulty.toLowerCase()}.$SOUND_EXT', null);
return OpenFlAssets.exists(diff) ? diff : getPath('songs/${song.toLowerCase()}/song/Inst$prefix.$SOUND_EXT', null);
song = song.toLowerCase();
var diff = getPath('songs/${song}/song/Inst$prefix-${difficulty.toLowerCase()}.$SOUND_EXT', null);
return OpenFlAssets.exists(diff) ? diff : getPath('songs/${song}/song/Inst$prefix.$SOUND_EXT', null);
}
static public function image(key:String, ?library:String, checkForAtlas:Bool = false, ?ext:String = "png")
+23 -6
View File
@@ -69,7 +69,11 @@ final class EventsData {
hscriptInterp.variables.set("String", TString);
hscriptInterp.variables.set("StrumLine", TStrumLine);
hscriptInterp.variables.set("ColorWheel", TColorWheel);
hscriptInterp.variables.set("DropDown", Reflect.makeVarArgs((args) -> {return args.length > 0 ? TDropDown([for (arg in args) Std.string(arg)]) : TDropDown(["null"]);}));
hscriptInterp.variables.set("DropDown", Reflect.makeVarArgs(function(args:Array<Dynamic>):EventParamType {
var flatArgs = CoolUtil.deepFlatten(args);
if(flatArgs.length == 0) return TDropDown(["null"]);
return TDropDown([for (arg in flatArgs) Std.string(arg)]);
}));
hscriptInterp.variables.set("Character", TCharacter);
hscriptInterp.variables.set("Stage", TStage);
@@ -77,11 +81,12 @@ final class EventsData {
hscriptParser.allowJSON = hscriptParser.allowMetadata = false;
for (file in Paths.getFolderContent('data/events/', true, BOTH)) {
if (Path.extension(file) != "json" && Path.extension(file) != "pack") continue;
var ext = Path.extension(file);
if (ext != "json" && ext != "pack") continue;
var eventName:String = CoolUtil.getFilename(file);
var fileTxt:String = Assets.getText(file);
if (Path.extension(file) == "pack") {
if (ext == "pack") {
var arr = fileTxt.split("________PACKSEP________");
eventName = Path.withoutExtension(arr[0]);
fileTxt = arr[2];
@@ -93,13 +98,17 @@ final class EventsData {
eventsParams.set(eventName, []);
try {
var data:Dynamic = Json.parse(fileTxt);
var data:EventInfoFile = cast Json.parse(fileTxt);
if (data == null || data.params == null) continue;
var finalParams:Array<EventParamInfo> = [];
for (paramData in cast(data.params, Array<Dynamic>)) {
for (paramData in data.params) {
try {
finalParams.push({name: paramData.name, type: hscriptInterp.expr(hscriptParser.parseString(paramData.type)), defValue: paramData.defaultValue});
finalParams.push({
name: paramData.name,
type: hscriptInterp.expr(hscriptParser.parseString(paramData.type)),
defValue: paramData.defaultValue
});
} catch (e) {trace('Error parsing event param ${paramData.name} - ${eventName}: $e'); finalParams.push(null);}
}
eventsParams.set(eventName, finalParams);
@@ -110,6 +119,14 @@ final class EventsData {
}
}
typedef EventInfoFile = {
var params:Array<{
var name:String;
var type:String;
var defaultValue:Dynamic;
}>;
}
typedef EventInfo = {
var params:Array<EventParamInfo>;
var paramValues:Array<Dynamic>;
@@ -226,7 +226,7 @@ typedef SwagSong =
typedef SwagSection =
{
var sectionNotes:Array<Dynamic>;
var sectionNotes:Array<Array<Dynamic>>;
var lengthInSteps:Int;
var ?sectionBeats:Float;
var ?typeOfSection:Int;
+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)
*/
@@ -26,7 +26,7 @@ final class DirectionAnimEvent extends CancellableEvent {
**/
public var frame:Int;
/**
Force the animation to replay even if it's already playing.
Force the animation to replay even if it's already playing (if it's null it will be forced based on the sprite's data xml, if it has one).
**/
public var force:Bool = true;
public var force:Null<Bool>;
}
@@ -100,9 +100,9 @@ final class NoteHitEvent extends CancellableEvent {
*/
public var ratingAntialiasing:Bool = true;
/**
* Whenever the animation should be forced to play.
* Whenever the animation should be forced to play (if it's null it will be forced based on the sprite's data xml, if it has one).
*/
public var forceAnim:Bool = true;
public var forceAnim:Null<Bool> = true;
/**
* Prevents the default sing animation from being played.
@@ -29,9 +29,9 @@ final class NoteMissEvent extends CancellableEvent {
public var gfSadAnim:String;
public var forceGfAnim:Bool;
/**
* Whenever the animation should be forced to play.
* Whenever the animation should be forced to play (if it's null it will be forced based on the sprite's data xml, if it has one).
*/
public var forceAnim:Bool;
public var forceAnim:Null<Bool>;
/**
* Suffix of the animation. "miss" for miss notes, "-alt" for alt notes, "" for normal ones.
*/
@@ -7,9 +7,9 @@ final class PlayAnimEvent extends CancellableEvent {
public var animName:String;
/**
Whenever the animation will be forced or not.
Whenever the animation will be forced or not (if it's null it will be forced based on the sprite's data xml, if it has one).
**/
public var force:Bool;
public var force:Null<Bool>;
/**
Whenever the animation will play in reverse or not
+28 -38
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) {
@@ -484,9 +483,7 @@ vec4 textureCam(sampler2D bitmap, vec2 pos) {
return flixel_texture2D(bitmap, camToOg(pos));
}";
public static final fragBody:String = "vec4 color = flixel_texture2D (bitmap, openfl_TextureCoordv);
gl_FragColor = color;
";
public static final fragBody:String = "gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv);";
public static final vertHeader:String = "attribute float openfl_Alpha;
attribute vec4 openfl_ColorMultiplier;
attribute vec4 openfl_ColorOffset;
@@ -500,16 +497,26 @@ varying vec2 openfl_TextureCoordv;
uniform mat4 openfl_Matrix;
uniform bool openfl_HasColorTransform;
uniform vec2 openfl_TextureSize;";
uniform vec2 openfl_TextureSize;
attribute float alpha;
attribute vec4 colorMultiplier;
attribute vec4 colorOffset;
uniform bool hasColorTransform;";
public static final vertBody:String = "openfl_Alphav = openfl_Alpha;
openfl_TextureCoordv = openfl_TextureCoord;
if (openfl_HasColorTransform) {
if(openfl_HasColorTransform) {
openfl_ColorMultiplierv = openfl_ColorMultiplier;
openfl_ColorOffsetv = openfl_ColorOffset / 255.0;
}
openfl_Alphav = openfl_Alpha * alpha;
if(hasColorTransform) {
openfl_ColorOffsetv = colorOffset / 255.0;
openfl_ColorMultiplierv = colorMultiplier;
}
gl_Position = openfl_Matrix * openfl_Position;";
@@ -517,31 +524,14 @@ gl_Position = openfl_Matrix * openfl_Position;";
public static final defaultVertexSource:String = "#pragma header
attribute float alpha;
attribute vec4 colorMultiplier;
attribute vec4 colorOffset;
uniform bool hasColorTransform;
void main(void)
{
void main(void) {
#pragma body
openfl_Alphav = openfl_Alpha * alpha;
if (hasColorTransform)
{
openfl_ColorOffsetv = colorOffset / 255.0;
openfl_ColorMultiplierv = colorMultiplier;
}
}";
// TODO: camera stuff
public static final defaultFragmentSource:String = "#pragma header
void main(void)
{
gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv);
void main(void) {
#pragma body
}";
}
@@ -125,9 +125,16 @@ class Framerate extends Sprite {
bgSprite.scaleX = width;
bgSprite.scaleY = height;
var y:Float = height + 4;
var selectable = debugMode == 2;
{ // idk i tried to make it more lookable:sob: - Nex
memoryCounter.memoryText.selectable = memoryCounter.memoryPeakText.selectable =
fpsCounter.fpsNum.selectable = fpsCounter.fpsLabel.selectable =
#if SHOW_BUILD_ON_FPS codenameBuildField.selectable = #end selectable;
}
var y:Float = height + 4;
for(c in categories) {
c.title.selectable = c.text.selectable = selectable;
c.alpha = debugAlpha;
c.x = FlxMath.lerp(-c.width - offset.x, 0, debugAlpha);
c.y = y;
@@ -29,6 +29,7 @@ class FramerateCategory extends Sprite {
label.x = 0;
label.y = 0;
label.defaultTextFormat = new TextFormat(Framerate.fontName, label == this.title ? 18 : 12, -1);
label.selectable = false;
addChild(label);
}
this.title.text = title;
@@ -51,4 +52,4 @@ class FramerateCategory extends Sprite {
bgSprite.scaleX = width;
bgSprite.scaleY = height;
}
}
}
@@ -22,6 +22,7 @@ class FramerateCounter extends Sprite {
label.text = "FPS";
label.multiline = label.wordWrap = false;
label.defaultTextFormat = new TextFormat(Framerate.fontName, label == fpsNum ? 18 : 12, -1);
label.selectable = false;
addChild(label);
}
}
@@ -37,4 +38,4 @@ class FramerateCounter extends Sprite {
fpsLabel.x = fpsNum.x + fpsNum.width;
fpsLabel.y = (fpsNum.y + fpsNum.height) - fpsLabel.height;
}
}
}
@@ -24,6 +24,7 @@ class MemoryCounter extends Sprite {
label.text = "FPS";
label.multiline = label.wordWrap = false;
label.defaultTextFormat = new TextFormat(Framerate.fontName, 12, -1);
label.selectable = false;
addChild(label);
}
memoryPeakText.alpha = 0.5;
@@ -42,4 +43,4 @@ class MemoryCounter extends Sprite {
memoryPeakText.x = memoryText.x + memoryText.width;
}
}
}
@@ -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);
+21 -12
View File
@@ -20,7 +20,7 @@ final class BitmapUtil {
for(y in 0...bmap.height) {
for(x in 0...bmap.width) {
color = bmap.getPixel32(x, y);
fixedColor = 0xFF000000 + (color % 0x1000000);
fixedColor = 0xFF000000 | (color & 0xFFFFFF);
if (!colorMap.exists(fixedColor))
colorMap[fixedColor] = 0;
colorMap[fixedColor] += color.alphaFloat;
@@ -51,7 +51,7 @@ final class BitmapUtil {
for(y in 0...bmap.height) {
for(x in 0...bmap.width) {
color = bmap.getPixel32(x, y);
fixedColor = 0xFF000000 + (color % 0x1000000);
fixedColor = 0xFF000000 | (color & 0xFFFFFF);
if (!colorMap.exists(fixedColor))
colorMap[fixedColor] = 0;
colorMap[fixedColor] += color.alphaFloat * 0.33 + (0.67 * (color.saturation * (2 * (color.lightness > 0.5 ? 0.5 - (color.lightness) : color.lightness))));
@@ -75,25 +75,33 @@ final class BitmapUtil {
*/
public static function crop(bitmap:BitmapData) {
var bitmapBounds:Rectangle = BitmapUtil.bounds(bitmap);
var croppedBitmap:BitmapData = new BitmapData(Std.int(bitmapBounds.width), Std.int(bitmapBounds.height), true, 0x00000000);
croppedBitmap.copyPixels(bitmap, bitmapBounds, new Point(0,0));
return croppedBitmap;
}
/**
* Get bounds of non empty pixels in the bitmap
* @param bitmap
* @param bitmap
* @return
*/
public static function bounds(bitmap:BitmapData, ?limit:Rectangle = null):Rectangle {
var minX:Int = limit != null ? Std.int(limit.width) : bitmap.width;
var minY:Int = limit != null ? Std.int(limit.height) : bitmap.height;
var maxX:Int = limit != null ? Std.int(limit.x) : 0;
var maxY:Int = limit != null ? Std.int(limit.y) : 0;
for (y in (limit != null ? Std.int(limit.x) : 0)...(limit != null ? Std.int(limit.height) : bitmap.height))
for (x in (limit != null ? Std.int(limit.y) : 0)...(limit != null ? Std.int(limit.width) : bitmap.width)) {
var hasLimit:Bool = limit != null;
// Searching bounds
var startX:Int = hasLimit ? Std.int(limit.x) : 0;
var startY:Int = hasLimit ? Std.int(limit.y) : 0;
var endX:Int = hasLimit ? Std.int(limit.width) : bitmap.width;
var endY:Int = hasLimit ? Std.int(limit.height) : bitmap.height;
// Detected bounds
var minX:Int = endX;
var minY:Int = endY;
var maxX:Int = startX;
var maxY:Int = startY;
for (y in startY...endY) {
for (x in startX...endX) {
if (bitmap.getPixel32(x, y) != 0x00000000) {
if (x < minX) minX = x;
if (y < minY) minY = y;
@@ -101,6 +109,7 @@ final class BitmapUtil {
if (y > maxY) maxY = y;
}
}
}
return new Rectangle(minX, minY, maxX-minX+1, maxY-minY+1);
}
+14 -1
View File
@@ -1010,6 +1010,19 @@ final class CoolUtil
return result.join(seperator);
}
public static function deepFlatten(arr:Array<Dynamic>, ?result:Array<Dynamic>):Array<Dynamic> {
if(arr == null) return [];
if(result == null) result = [];
for (e in arr) {
if (Std.isOfType(e, Array)) {
deepFlatten(e, result);
} else {
result.push(e);
}
}
return result;
}
}
/**
@@ -1022,4 +1035,4 @@ enum abstract CoolSfx(Int) from Int {
var CHECKED = 3;
var UNCHECKED = 4;
var WARNING = 5;
}
}
+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;
+11 -8
View File
@@ -4,6 +4,7 @@ import flixel.util.FlxColor;
import flixel.util.typeLimit.OneOfTwo;
import funkin.backend.FunkinSprite.XMLAnimType;
import funkin.backend.FunkinSprite;
import funkin.game.Character;
import funkin.backend.system.ErrorCode;
import funkin.backend.system.interfaces.IOffsetCompatible;
import haxe.xml.Access;
@@ -175,8 +176,7 @@ final class XMLUtil {
animType: spr.spriteAnimType,
x: 0,
y: 0,
indices: [for(i in 0...spr.frames.frames.length) i],
forced: (node.has.forced && node.att.forced == "true") || (!node.has.forced && spr.spriteAnimType == BEAT)
indices: [for(i in 0...spr.frames.frames.length) i]
});
}
}
@@ -202,8 +202,7 @@ final class XMLUtil {
animType: animType,
x: 0,
y: 0,
indices: [],
forced: false,
indices: []
};
if (anim.has.name) animData.name = anim.att.name;
@@ -259,24 +258,28 @@ final class XMLUtil {
if (sprite is FunkinSprite) {
var xmlSpr = cast(sprite, FunkinSprite);
var name = animData.name;
switch(animData.animType) {
case BEAT:
xmlSpr.beatAnims.push({
name: animData.name,
forced: animData.forced.getDefault(false)
name: name,
forced: animData.forced.getDefault(defaultForcedCheck(name, xmlSpr))
});
case LOOP:
xmlSpr.playAnim(animData.name, animData.forced.getDefault(false));
xmlSpr.playAnim(name, animData.forced.getDefault(defaultForcedCheck(name, xmlSpr)));
default:
// nothing
}
xmlSpr.animDatas.set(animData.name, animData);
xmlSpr.animDatas.set(name, animData);
}
return OK;
}
return MISSING_PROPERTY;
}
public static inline function defaultForcedCheck(animName:String, sprite:FunkinSprite):Bool
return sprite is Character && (animName.startsWith("idle") || animName.startsWith("danceLeft") || animName.startsWith("danceRight")) ? false : sprite.spriteAnimType == BEAT;
public static inline function fixXMLText(text:String) {
var v:String;
return [for(l in text.split("\n")) if ((v = l.trim()) != "") v].join("\n");
+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);
@@ -1,12 +1,12 @@
package funkin.editors.character;
import flxanimate.animate.FlxAnim.FlxSymbolAnimation;
import flxanimate.animate.FlxSymbol;
import flxanimate.animate.FlxElement;
import flxanimate.animate.SymbolParameters;
import flxanimate.animate.FlxKeyFrame;
import flxanimate.animate.FlxTimeline;
import flixel.graphics.frames.FlxFrame;
import flxanimate.animate.FlxAnim.SymbolStuff;
import flixel.animation.FlxAnimation;
import openfl.geom.Rectangle;
import funkin.backend.utils.XMLUtil.AnimData;
@@ -215,7 +215,7 @@ class CharacterAnimButton extends UIButton {
}
if (parent.character.animateAtlas != null) {
var animSymbol:SymbolStuff = parent.character.animateAtlas.anim.animsMap[anim];
var animSymbol:FlxSymbolAnimation = parent.character.animateAtlas.anim.animsMap[anim];
parent.character.animateAtlas.anim.animsMap.remove(anim);
parent.character.animateAtlas.anim.animsMap.set(newName, animSymbol);
@@ -251,7 +251,7 @@ class CharacterAnimButton extends UIButton {
animData.anim = newAnim;
if (parent.character.animateAtlas != null) {
var animSymbol:SymbolStuff = parent.character.animateAtlas.anim.animsMap[anim];
var animSymbol:FlxSymbolAnimation = parent.character.animateAtlas.anim.animsMap[anim];
refreshSymbolKeyFrames(animSymbol, animData);
} else {
var flxAnimation:FlxAnimation = parent.character.animation._animations[anim];
@@ -287,7 +287,7 @@ class CharacterAnimButton extends UIButton {
animData.fps = newFPS;
if (parent.character.animateAtlas != null) {
var animSymbol:SymbolStuff = parent.character.animateAtlas.anim.animsMap[anim];
var animSymbol:FlxSymbolAnimation = parent.character.animateAtlas.anim.animsMap[anim];
animSymbol.frameRate = newFPS;
} else {
var flxAnimation:FlxAnimation = parent.character.animation._animations[anim];
@@ -303,7 +303,7 @@ class CharacterAnimButton extends UIButton {
animData.loop = newLooping;
if (parent.character.animateAtlas != null) {
var animSymbol:SymbolStuff = parent.character.animateAtlas.anim.animsMap[anim];
var animSymbol:FlxSymbolAnimation = parent.character.animateAtlas.anim.animsMap[anim];
animSymbol.instance.symbol.loop = animData.loop ? Loop : PlayOnce;
} else {
var flxAnimation:FlxAnimation = parent.character.animation._animations[anim];
@@ -319,7 +319,7 @@ class CharacterAnimButton extends UIButton {
animData.indices = indicies;
if (parent.character.animateAtlas != null) {
var animSymbol:SymbolStuff = parent.character.animateAtlas.anim.animsMap[anim];
var animSymbol:FlxSymbolAnimation = parent.character.animateAtlas.anim.animsMap[anim];
refreshSymbolKeyFrames(animSymbol, animData);
} else {
var flxAnimation:FlxAnimation = parent.character.animation._animations[anim];
@@ -351,7 +351,7 @@ class CharacterAnimButton extends UIButton {
}
}
public inline function refreshSymbolKeyFrames(symbol:SymbolStuff, animData:AnimData) @:privateAccess {
public inline function refreshSymbolKeyFrames(symbol:FlxSymbolAnimation, animData:AnimData) @:privateAccess {
if (animData.indices.length > 0) {
// keeps on crashing, look at flxanimate FlxAnim.hx for refrence
} else {
+5 -6
View File
@@ -111,10 +111,10 @@ class Charter extends UIState {
{
label: "File",
childs: [
{
/*{
label: "New"
},
null,
null,*/
{
label: "Save",
keybind: [CONTROL, S],
@@ -378,9 +378,9 @@ class Charter extends UIState {
onSelect: _playback_metronome,
icon: Options.charterMetronomeEnabled ? 1 : 0
},
{
/*{
label: "Visual metronome"
},
},*/
]
}
];
@@ -436,7 +436,7 @@ class Charter extends UIState {
};
uiGroup.add(scrollBar);
songPosInfo = new UIText(FlxG.width - 30 - 400, scrollBar.y + 10, 400, "00:00\nBeat: 0\nStep: 0\nMeasure: 0\nBPM: 0\nTime Signature: 4/4");
songPosInfo = new UIText(FlxG.width - 30 - 400, scrollBar.y + 10, 400, "00:00 / 00:00\nBeat: 0\nStep: 0\nMeasure: 0\nBPM: 0\nTime Signature: 4/4");
songPosInfo.alignment = RIGHT;
uiGroup.add(songPosInfo);
@@ -1184,7 +1184,6 @@ class Charter extends UIState {
var snapButton:UITopMenuButton = cast topMenuSpr.members[snapIndex];
var lastButtonX = playBackButton.x-10;
var buttonI:Int = 0;
for (button in quantButtons) {
button.visible = ((button.quant == quant) ||
(button.quant == quants[FlxMath.wrap(quants.indexOf(quant)-1, 0, quants.length-1)]) ||
+4 -3
View File
@@ -41,7 +41,7 @@ class CharterNote extends UISprite implements ICharterSelectable {
this.setUnstretchedGraphicSize(40, 40, false);
sustainSpr = new UISprite(10, 20);
sustainSpr.makeSolid(1, 1, -1);
sustainSpr.makeSolid(1, 1, -1, false, "note-sustain");
sustainSpr.scale.set(10, 0);
members.push(sustainSpr);
@@ -166,8 +166,9 @@ class CharterNote extends UISprite implements ICharterSelectable {
}
if (strumLine != null) {
sustainSpr.alpha = alpha = !strumLine.strumLine.visible ? (__passed ? 0.2 : 0.4) : (__passed ? 0.6 : 1);
typeAlpha = !strumLine.strumLine.visible ? (__passed ? 0.4 : 0.6) : (__passed ? 0.8 : 1);
var isVisible = strumLine.strumLine.visible;
sustainSpr.alpha = alpha = !isVisible ? (__passed ? 0.2 : 0.4) : (__passed ? 0.6 : 1);
typeAlpha = !isVisible ? (__passed ? 0.4 : 0.6) : (__passed ? 0.8 : 1);
}
colorTransform.redMultiplier = colorTransform.greenMultiplier = colorTransform.blueMultiplier = selected ? 0.75 : 1;
@@ -60,14 +60,14 @@ class CharterNoteGroup extends FlxTypedGroup<CharterNote> {
public override function update(elapsed:Float) @:privateAccess {
var oldDefaultCameras = FlxCamera._defaultCameras;
if (cameras != null) FlxCamera._defaultCameras = cameras;
if (_cameras != null) FlxCamera._defaultCameras = _cameras;
if (autoSort && members.length != __lastSort)
sortNotes();
forEach((n) -> {
if(n.exists && n.active) {
n.cameras = n.__lastDrawCameras = cameras;
n._cameras = n.__lastDrawCameras = cameras;
n.update(elapsed);
}
});
@@ -46,7 +46,7 @@ class CharterStrumline extends UISprite {
}
return selectedWaveform = value;
}
public var waveformShader:CustomShader;
public var waveformShader:CustomShader;
public function new(strumLine:ChartStrumLine) {
super();
+2 -2
View File
@@ -75,8 +75,8 @@ class UIState extends MusicBeatState {
var pos = FlxG.mouse.getScreenPosition(camera, __point);
__rect.copyFrom(rect);
__rect.x -= camera.scroll.x * spr.scrollFactor.x;
__rect.y -= camera.scroll.y * spr.scrollFactor.y;
__rect.x = __rect.x - camera.scroll.x * spr.scrollFactor.x;
__rect.y = __rect.y - camera.scroll.y * spr.scrollFactor.y;
if (((pos.x > __rect.x) && (pos.x < __rect.x + __rect.width)) && ((pos.y > __rect.y) && (pos.y < __rect.y + __rect.height))) {
return true;
+4 -2
View File
@@ -4,10 +4,12 @@ import flixel.input.FlxInput.FlxInputState;
import flixel.input.keyboard.FlxKey;
import funkin.editors.ui.UIContextMenu.UIContextMenuOption;
@:access(flixel.FlxSprite)
class UIUtil {
public static function follow(spr:FlxSprite, target:FlxSprite, x:Float = 0, y:Float = 0) {
spr.cameras = target is UISprite ? cast(target, UISprite).__lastDrawCameras : target.cameras;
spr.setPosition(target.x + x, target.y + y);
spr._cameras = target is UISprite ? cast(target, UISprite).__lastDrawCameras : target.cameras;
spr.x = target.x + x;
spr.y = target.y + y;
spr.scrollFactor.set(target.scrollFactor.x, target.scrollFactor.y);
}
+3 -2
View File
@@ -291,7 +291,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
/**
* Like `playSingAnimUnsafe` but checks if the character has the animation with the suffix part, otherwise it plays the animation without the suffix part.
*/
public function playSingAnim(direction:Int, suffix:String = "", Context:PlayAnimContext = SING, Force:Bool = true, Reversed:Bool = false, Frame:Int = 0)
public function playSingAnim(direction:Int, suffix:String = "", Context:PlayAnimContext = SING, ?Force:Null<Bool> = null, Reversed:Bool = false, Frame:Int = 0)
{
var event = EventManager.get(DirectionAnimEvent).recycle(getSingAnim(direction, suffix), direction, suffix, Context, Reversed, Frame, Force);
script.call("onPlaySingAnim", [event]);
@@ -308,7 +308,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
playAnim(event.animName, event.force, event.context, event.reversed, event.frame);
}
public override function playAnim(AnimName:String, Force:Bool = false, Context:PlayAnimContext = NONE, Reversed:Bool = false, Frame:Int = 0) {
public override function playAnim(AnimName:String, ?Force:Bool, Context:PlayAnimContext = NONE, Reversed:Bool = false, Frame:Int = 0) {
var event = EventManager.get(PlayAnimEvent).recycle(AnimName, Force, Reversed, Frame, Context);
script.call("onPlayAnim", [event]);
if (event.cancelled) return;
@@ -380,6 +380,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
public inline function buildCharacter(xml:Access) {
this.xml = xml; // Modders wassup :D
sprite = curCharacter;
spriteAnimType = BEAT;
if (xml.x.exists("isPlayer")) playerOffsets = (xml.x.get("isPlayer") == "true");
if (xml.x.exists("x")) globalOffset.x = Std.parseFloat(xml.x.get("x"));
+13 -7
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) {
@@ -49,16 +55,16 @@ class NoteGroup extends FlxTypedGroup<Note> {
}
}
public override function draw() {
@:privateAccess var oldDefaultCameras = FlxCamera._defaultCameras;
@:privateAccess if (cameras != null) FlxCamera._defaultCameras = cameras;
public override function draw() @:privateAccess {
var oldDefaultCameras = FlxCamera._defaultCameras;
if (_cameras != null) FlxCamera._defaultCameras = _cameras;
var oldCur = __currentlyLooping;
__currentlyLooping = true;
i = length-1;
__loopSprite = null;
__time = Conductor.songPosition;
__time = __getSongPos();
while(i >= 0) {
__loopSprite = members[i--];
if (__loopSprite == null || !__loopSprite.exists || !__loopSprite.visible)
@@ -68,7 +74,7 @@ class NoteGroup extends FlxTypedGroup<Note> {
}
__currentlyLooping = oldCur;
@:privateAccess FlxCamera._defaultCameras = oldDefaultCameras;
FlxCamera._defaultCameras = oldDefaultCameras;
}
/**
@@ -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;
+10 -2
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 = [];
@@ -1933,7 +1941,7 @@ class PlayState extends MusicBeatState
}
}
class ComboRating {
final class ComboRating {
public var percent:Float;
public var rating:String;
public var color:FlxColor;
+3 -2
View File
@@ -107,8 +107,9 @@ class SplashGroup extends FlxTypedGroup<FunkinSprite> {
public function getSplashAnim(id:Int):String {
if (animationNames.length <= 0) return null;
id %= animationNames.length;
if (animationNames[id] == null || animationNames[id].length <= 0) return null;
return animationNames[id][FlxG.random.int(0, animationNames[id].length - 1)];
var animNames = animationNames[id];
if (animNames == null || animNames.length <= 0) return null;
return animNames[FlxG.random.int(0, animNames.length - 1)];
}
var __splash:FunkinSprite;
+1 -1
View File
@@ -42,7 +42,7 @@ 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(SplashShowEvent).recycle(name, __grp.showOnStrum(strum), strum, __grp);
var event = EventManager.get(SplashShowEvent).recycle(name, __grp.showOnStrum(strum), strum, __grp);
event = PlayState.instance.scripts.event("onSplashShown", event);
if (!event.cancelled)
add(event.splash);
+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;
}
}
+1 -1
View File
@@ -80,7 +80,7 @@ class Strum extends FlxSprite {
super.draw();
}
@:noCompletion public static final PIX180:Float = 565.4866776461628; // 180 * Math.PI
@:noCompletion public static inline final PIX180:Float = 565.4866776461628; // 180 * Math.PI
@:noCompletion public static final N_WIDTHDIV2:Float = Note.swagWidth / 2;
public function updateNotePosition(daNote:Note) {
+4 -2
View File
@@ -152,8 +152,10 @@ class StrumLine extends FlxTypedGroup<Strum> {
var scrollSpeed = strumLine.scrollSpeed;
if(scrollSpeed == null) if (PlayState.instance != null) scrollSpeed = PlayState.instance.scrollSpeed;
if(scrollSpeed == null) scrollSpeed = 1;
var limit = FlxG.height / 0.45;
notes.limit = limit / scrollSpeed;
// TODOD: Make this work by accounting zoom and scrollspeed changes - Nex
/*var limit = FlxG.height / 0.45;
notes.limit = limit / scrollSpeed;*/
}
public override function update(elapsed:Float) {
@@ -94,7 +94,7 @@ class DialogueBox extends FunkinSprite {
dialogueBoxScript.call("postCreate");
}
public override function playAnim(AnimName:String, Force:Bool = false, Context:PlayAnimContext = NONE, Reversed:Bool = false, Frame:Int = 0) {
public override function playAnim(AnimName:String, ?Force:Bool, Context:PlayAnimContext = NONE, Reversed:Bool = false, Frame:Int = 0) {
var event = EventManager.get(PlayAnimEvent).recycle(AnimName, Force, Reversed, Frame, Context);
dialogueBoxScript.call("onPlayAnim", [event]);
if(event.cancelled) return;
@@ -61,7 +61,7 @@ class DialogueCharacter extends FunkinSprite {
dialogueCharScript.call("postCreate");
}
public override function playAnim(AnimName:String, Force:Bool = false, Context:PlayAnimContext = NONE, Reversed:Bool = false, Frame:Int = 0) {
public override function playAnim(AnimName:String, ?Force:Bool, Context:PlayAnimContext = NONE, Reversed:Bool = false, Frame:Int = 0) {
var event = EventManager.get(PlayAnimEvent).recycle(AnimName, Force, Reversed, Frame, Context);
dialogueCharScript.call("playAnim", [event]);
if(event.cancelled) return;
+8 -6
View File
@@ -281,12 +281,13 @@ class FreeplayState extends MusicBeatState
function updateCoopModes() {
__opponentMode = false;
__coopMode = false;
if (songs[curSelected].coopAllowed && songs[curSelected].opponentModeAllowed) {
var curSong = songs[curSelected];
if (curSong.coopAllowed && curSong.opponentModeAllowed) {
__opponentMode = curCoopMode % 2 == 1;
__coopMode = curCoopMode >= 2;
} else if (songs[curSelected].coopAllowed) {
} else if (curSong.coopAllowed) {
__coopMode = curCoopMode == 1;
} else if (songs[curSelected].opponentModeAllowed) {
} else if (curSong.opponentModeAllowed) {
__opponentMode = curCoopMode == 1;
}
}
@@ -315,9 +316,10 @@ class FreeplayState extends MusicBeatState
}
public function convertChart() {
trace('Converting ${songs[curSelected].name} (${songs[curSelected].difficulties[curDifficulty]}) to Codename format...');
var chart = Chart.parse(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty]);
Chart.save('${Main.pathBack}assets/songs/${songs[curSelected].name}', chart, songs[curSelected].difficulties[curDifficulty].toLowerCase());
var curSong = songs[curSelected];
trace('Converting ${curSong.name} (${curSong.difficulties[curDifficulty]}) to Codename format...');
var chart = Chart.parse(curSong.name, curSong.difficulties[curDifficulty]);
Chart.save('${Main.pathBack}assets/songs/${curSong.name}', chart, curSong.difficulties[curDifficulty].toLowerCase());
}
/**
+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());
}
+297
View File
@@ -0,0 +1,297 @@
package haxe;
#if !lime_cffi
// Original haxe.Timer class
/*
* Copyright (C)2005-2018 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
/**
The Timer class allows you to create asynchronous timers on platforms that
support events.
The intended usage is to create an instance of the Timer class with a given
interval, set its run() method to a custom function to be invoked and
eventually call stop() to stop the Timer.
Note that a running Timer may or may not prevent the program to exit
automatically when main() returns.
It is also possible to extend this class and override its run() method in
the child class.
**/
class Timer
{
#if (flash || js)
private var id:Null<Int>;
#elseif java
private var timer:java.util.Timer;
private var task:java.util.TimerTask;
#elseif (haxe_ver >= "3.4.0")
private var event:MainLoop.MainEvent;
#end
/**
Creates a new timer that will run every `time_ms` milliseconds.
After creating the Timer instance, it calls `this.run` repeatedly,
with delays of `time_ms` milliseconds, until `this.stop` is called.
The first invocation occurs after `time_ms` milliseconds, not
immediately.
The accuracy of this may be platform-dependent.
**/
public function new(time_ms:Int)
{
#if flash
var me = this;
id = untyped __global__["flash.utils.setInterval"](function()
{
me.run();
}, time_ms);
#elseif js
var me = this;
id = untyped setInterval(function() me.run(), time_ms);
#elseif java
timer = new java.util.Timer();
timer.scheduleAtFixedRate(task = new TimerTask(this), haxe.Int64.ofInt(time_ms), haxe.Int64.ofInt(time_ms));
#elseif (haxe_ver >= "3.4.0")
var dt = time_ms / 1000;
event = MainLoop.add(function()
{
@:privateAccess event.nextRun += dt;
run();
});
event.delay(dt);
#end
}
/**
Stops `this` Timer.
After calling this method, no additional invocations of `this.run`
will occur.
It is not possible to restart `this` Timer once stopped.
**/
public function stop()
{
#if (flash || js)
if (id == null) return;
#if flash
untyped __global__["flash.utils.clearInterval"](id);
#elseif js
untyped clearInterval(id);
#end
id = null;
#elseif java
if (timer != null)
{
timer.cancel();
timer = null;
}
task = null;
#elseif (haxe_ver >= "3.4.0")
if (event != null)
{
event.stop();
event = null;
}
#end
}
/**
This method is invoked repeatedly on `this` Timer.
It can be overridden in a subclass, or rebound directly to a custom
function:
var timer = new haxe.Timer(1000); // 1000ms delay
timer.run = function() { ... }
Once bound, it can still be rebound to different functions until `this`
Timer is stopped through a call to `this.stop`.
**/
public dynamic function run() {}
/**
Invokes `f` after `time_ms` milliseconds.
This is a convenience function for creating a new Timer instance with
`time_ms` as argument, binding its run() method to `f` and then stopping
`this` Timer upon the first invocation.
If `f` is null, the result is unspecified.
**/
public static function delay(f:Void->Void, time_ms:Int)
{
var t = new haxe.Timer(time_ms);
t.run = function()
{
t.stop();
f();
};
return t;
}
/**
Measures the time it takes to execute `f`, in seconds with fractions.
This is a convenience function for calculating the difference between
Timer.stamp() before and after the invocation of `f`.
The difference is passed as argument to Log.trace(), with "s" appended
to denote the unit. The optional `pos` argument is passed through.
If `f` is null, the result is unspecified.
**/
public static function measure<T>(f:Void->T, ?pos:PosInfos):T
{
var t0 = stamp();
var r = f();
Log.trace((stamp() - t0) + "s", pos);
return r;
}
/**
Returns a timestamp, in seconds with fractions.
The value itself might differ depending on platforms, only differences
between two values make sense.
**/
public static inline function stamp():Float
{
#if flash
return flash.Lib.getTimer() / 1000;
#elseif (neko || php)
return Sys.time();
#elseif js
return Date.now().getTime() / 1000;
#elseif cpp
return untyped __global__.__time_stamp();
#elseif python
return Sys.cpuTime();
#elseif sys
return Sys.time();
#else
return 0;
#end
}
}
#if java
@:nativeGen
private class TimerTask extends java.util.TimerTask
{
var timer:Timer;
public function new(timer:Timer):Void
{
super();
this.timer = timer;
}
@:overload override public function run():Void
{
timer.run();
}
}
#end
#else
import lime.system.System;
class Timer
{
private static var sRunningTimers:Array<Timer> = [];
private var mTime:Float;
private var mFireAt:Float;
private var mRunning:Bool;
public function new(time:Float)
{
mTime = time;
sRunningTimers.push(this);
mFireAt = getMS() + mTime;
mRunning = true;
}
public static function delay(f:Void->Void, time:Int)
{
var t = new Timer(time);
t.run = function()
{
t.stop();
f();
};
return t;
}
private static function getMS():Float
{
return System.getTimer();
}
public static function measure<T>(f:Void->T, ?pos:PosInfos):T
{
var t0 = stamp();
var r = f();
Log.trace((stamp() - t0) + "s", pos);
return r;
}
dynamic public function run() {}
public static inline function stamp():Float
{
var timer = System.getTimer();
return (timer > 0 ? timer / 1000 : 0);
}
public function stop():Void
{
/*if (mRunning)
{
for (i in 0...sRunningTimers.length)
{
if (sRunningTimers[i] == this)
{
sRunningTimers[i] = null;
break;
}
}
}*/
mRunning = false;
}
@:noCompletion private function __check(inTime:Float)
{
if (inTime >= mFireAt)
{
mFireAt += mTime;
run();
}
}
}
#end
+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
@@ -0,0 +1,983 @@
package lime._internal.backend.native;
import haxe.Timer;
import lime._internal.backend.native.NativeCFFI;
import lime.app.Application;
import lime.graphics.opengl.GL;
import lime.graphics.OpenGLRenderContext;
import lime.graphics.RenderContext;
import lime.math.Rectangle;
import lime.media.AudioManager;
import lime.system.Clipboard;
import lime.system.Display;
import lime.system.DisplayMode;
import lime.system.JNI;
import lime.system.Sensor;
import lime.system.SensorType;
import lime.system.System;
import lime.ui.Gamepad;
import lime.ui.Joystick;
import lime.ui.JoystickHatPosition;
import lime.ui.KeyCode;
import lime.ui.KeyModifier;
import lime.ui.Touch;
import lime.ui.Window;
#if !lime_debug
@:fileXml('tags="haxe,release"')
@:noDebug
#end
@:access(haxe.Timer)
@:access(lime._internal.backend.native.NativeCFFI)
@:access(lime._internal.backend.native.NativeOpenGLRenderContext)
@:access(lime._internal.backend.native.NativeWindow)
@:access(lime.app.Application)
@:access(lime.graphics.opengl.GL)
@:access(lime.graphics.OpenGLRenderContext)
@:access(lime.graphics.Renderer)
@:access(lime.system.Clipboard)
@:access(lime.system.Sensor)
@:access(lime.ui.Gamepad)
@:access(lime.ui.Joystick)
@:access(lime.ui.Window)
class NativeApplication
{
private var applicationEventInfo = new ApplicationEventInfo(UPDATE);
private var clipboardEventInfo = new ClipboardEventInfo();
private var currentTouches = new Map<Int, Touch>();
private var dropEventInfo = new DropEventInfo();
private var gamepadEventInfo = new GamepadEventInfo();
private var joystickEventInfo = new JoystickEventInfo();
private var keyEventInfo = new KeyEventInfo();
private var mouseEventInfo = new MouseEventInfo();
private var renderEventInfo = new RenderEventInfo(RENDER);
private var sensorEventInfo = new SensorEventInfo();
private var textEventInfo = new TextEventInfo();
private var touchEventInfo = new TouchEventInfo();
private var unusedTouchesPool = new List<Touch>();
private var windowEventInfo = new WindowEventInfo();
public var handle:Dynamic;
private var pauseTimer:Int;
private var parent:Application;
private var toggleFullscreen:Bool;
private static function __init__()
{
#if (lime_cffi && !macro)
var init = NativeCFFI;
#end
}
public function new(parent:Application):Void
{
this.parent = parent;
pauseTimer = -1;
toggleFullscreen = true;
AudioManager.init();
#if (ios || android || tvos)
Sensor.registerSensor(SensorType.ACCELEROMETER, 0);
#end
#if (!macro && lime_cffi)
handle = NativeCFFI.lime_application_create();
#end
}
private function advanceTimer():Void
{
#if lime_cffi
if (pauseTimer > -1)
{
var offset = System.getTimer() - pauseTimer;
for(timer in Timer.sRunningTimers) {
if(timer != null && timer.mRunning) timer.mFireAt += offset;
}
pauseTimer = -1;
}
#end
}
public function exec():Int
{
#if !macro
#if lime_cffi
NativeCFFI.lime_application_event_manager_register(handleApplicationEvent, applicationEventInfo);
NativeCFFI.lime_clipboard_event_manager_register(handleClipboardEvent, clipboardEventInfo);
NativeCFFI.lime_drop_event_manager_register(handleDropEvent, dropEventInfo);
NativeCFFI.lime_gamepad_event_manager_register(handleGamepadEvent, gamepadEventInfo);
NativeCFFI.lime_joystick_event_manager_register(handleJoystickEvent, joystickEventInfo);
NativeCFFI.lime_key_event_manager_register(handleKeyEvent, keyEventInfo);
NativeCFFI.lime_mouse_event_manager_register(handleMouseEvent, mouseEventInfo);
NativeCFFI.lime_render_event_manager_register(handleRenderEvent, renderEventInfo);
NativeCFFI.lime_text_event_manager_register(handleTextEvent, textEventInfo);
NativeCFFI.lime_touch_event_manager_register(handleTouchEvent, touchEventInfo);
NativeCFFI.lime_window_event_manager_register(handleWindowEvent, windowEventInfo);
#if (ios || android || tvos)
NativeCFFI.lime_sensor_event_manager_register(handleSensorEvent, sensorEventInfo);
#end
#end
#if (nodejs && lime_cffi)
NativeCFFI.lime_application_init(handle);
var eventLoop = function()
{
var active = NativeCFFI.lime_application_update(handle);
if (!active)
{
untyped process.exitCode = NativeCFFI.lime_application_quit(handle);
parent.onExit.dispatch(untyped process.exitCode);
}
else
{
untyped setImmediate(eventLoop);
}
}
untyped setImmediate(eventLoop);
return 0;
#elseif lime_cffi
var result = NativeCFFI.lime_application_exec(handle);
#if (!webassembly && !ios && !nodejs)
parent.onExit.dispatch(result);
#end
return result;
#end
#end
return 0;
}
public function exit():Void
{
AudioManager.shutdown();
#if (!macro && lime_cffi)
NativeCFFI.lime_application_quit(handle);
#end
}
private function handleApplicationEvent():Void
{
switch (applicationEventInfo.type)
{
case UPDATE:
updateTimer();
parent.onUpdate.dispatch(applicationEventInfo.deltaTime);
default:
}
}
private function handleClipboardEvent():Void
{
Clipboard.__update();
}
private function handleDropEvent():Void
{
for (window in parent.windows)
{
window.onDropFile.dispatch(#if hl @:privateAccess String.fromUTF8(dropEventInfo.file) #else dropEventInfo.file #end);
}
}
private function handleGamepadEvent():Void
{
switch (gamepadEventInfo.type)
{
case AXIS_MOVE:
var gamepad = Gamepad.devices.get(gamepadEventInfo.id);
if (gamepad != null) gamepad.onAxisMove.dispatch(gamepadEventInfo.axis, gamepadEventInfo.axisValue);
case BUTTON_DOWN:
var gamepad = Gamepad.devices.get(gamepadEventInfo.id);
if (gamepad != null) gamepad.onButtonDown.dispatch(gamepadEventInfo.button);
case BUTTON_UP:
var gamepad = Gamepad.devices.get(gamepadEventInfo.id);
if (gamepad != null) gamepad.onButtonUp.dispatch(gamepadEventInfo.button);
case CONNECT:
Gamepad.__connect(gamepadEventInfo.id);
case DISCONNECT:
Gamepad.__disconnect(gamepadEventInfo.id);
}
}
private function handleJoystickEvent():Void
{
switch (joystickEventInfo.type)
{
case AXIS_MOVE:
var joystick = Joystick.devices.get(joystickEventInfo.id);
if (joystick != null) joystick.onAxisMove.dispatch(joystickEventInfo.index, joystickEventInfo.x);
case HAT_MOVE:
var joystick = Joystick.devices.get(joystickEventInfo.id);
if (joystick != null) joystick.onHatMove.dispatch(joystickEventInfo.index, joystickEventInfo.eventValue);
case TRACKBALL_MOVE:
var joystick = Joystick.devices.get(joystickEventInfo.id);
if (joystick != null) joystick.onTrackballMove.dispatch(joystickEventInfo.index, joystickEventInfo.x, joystickEventInfo.y);
case BUTTON_DOWN:
var joystick = Joystick.devices.get(joystickEventInfo.id);
if (joystick != null) joystick.onButtonDown.dispatch(joystickEventInfo.index);
case BUTTON_UP:
var joystick = Joystick.devices.get(joystickEventInfo.id);
if (joystick != null) joystick.onButtonUp.dispatch(joystickEventInfo.index);
case CONNECT:
Joystick.__connect(joystickEventInfo.id);
case DISCONNECT:
Joystick.__disconnect(joystickEventInfo.id);
}
}
private function handleKeyEvent():Void
{
var window = parent.__windowByID.get(keyEventInfo.windowID);
if (window != null)
{
var type:KeyEventType = keyEventInfo.type;
var int32:Float = keyEventInfo.keyCode;
var keyCode:KeyCode = Std.int(int32);
var modifier:KeyModifier = keyEventInfo.modifier;
switch (type)
{
case KEY_DOWN:
window.onKeyDown.dispatch(keyCode, modifier);
case KEY_UP:
window.onKeyUp.dispatch(keyCode, modifier);
}
#if (windows || linux)
if (keyCode == RETURN)
{
if (type == KEY_DOWN)
{
if (toggleFullscreen && modifier.altKey && (!modifier.ctrlKey && !modifier.shiftKey && !modifier.metaKey))
{
toggleFullscreen = false;
if (!window.onKeyDown.canceled)
{
window.fullscreen = !window.fullscreen;
}
}
}
else
{
toggleFullscreen = true;
}
}
#if rpi
if (keyCode == ESCAPE && modifier == KeyModifier.NONE && type == KEY_UP && !window.onKeyUp.canceled)
{
System.exit(0);
}
#end
#elseif mac
if (keyCode == F)
{
if (type == KEY_DOWN)
{
if (toggleFullscreen && (modifier.ctrlKey && modifier.metaKey) && (!modifier.altKey && !modifier.shiftKey))
{
toggleFullscreen = false;
if (!window.onKeyDown.canceled)
{
window.fullscreen = !window.fullscreen;
}
}
}
else
{
toggleFullscreen = true;
}
}
#elseif android
if (keyCode == APP_CONTROL_BACK && modifier == KeyModifier.NONE && type == KEY_UP && !window.onKeyUp.canceled)
{
var mainActivity = JNI.createStaticField("org/haxe/extension/Extension", "mainActivity", "Landroid/app/Activity;");
var moveTaskToBack = JNI.createMemberMethod("android/app/Activity", "moveTaskToBack", "(Z)Z");
moveTaskToBack(mainActivity.get(), true);
}
#end
}
}
private function handleMouseEvent():Void
{
var window = parent.__windowByID.get(mouseEventInfo.windowID);
if (window != null)
{
switch (mouseEventInfo.type)
{
case MOUSE_DOWN:
window.clickCount = mouseEventInfo.clickCount;
window.onMouseDown.dispatch(mouseEventInfo.x, mouseEventInfo.y, mouseEventInfo.button);
window.clickCount = 0;
case MOUSE_UP:
window.clickCount = mouseEventInfo.clickCount;
window.onMouseUp.dispatch(mouseEventInfo.x, mouseEventInfo.y, mouseEventInfo.button);
window.clickCount = 0;
case MOUSE_MOVE:
window.onMouseMove.dispatch(mouseEventInfo.x, mouseEventInfo.y);
window.onMouseMoveRelative.dispatch(mouseEventInfo.movementX, mouseEventInfo.movementY);
case MOUSE_WHEEL:
window.onMouseWheel.dispatch(mouseEventInfo.x, mouseEventInfo.y, UNKNOWN);
default:
}
}
}
private function handleRenderEvent():Void
{
// TODO: Allow windows to render independently
for (window in parent.__windows)
{
if (window == null) continue;
// parent.renderer = renderer;
switch (renderEventInfo.type)
{
case RENDER:
if (window.context != null)
{
window.__backend.render();
window.onRender.dispatch(window.context);
if (!window.onRender.canceled)
{
window.__backend.contextFlip();
}
}
case RENDER_CONTEXT_LOST:
if (window.__backend.useHardware && window.context != null)
{
switch (window.context.type)
{
case OPENGL, OPENGLES, WEBGL:
#if (lime_cffi && (lime_opengl || lime_opengles) && !display)
var gl = window.context.gl;
(gl : NativeOpenGLRenderContext).__contextLost();
if (GL.context == gl) GL.context = null;
#end
default:
}
window.context = null;
window.onRenderContextLost.dispatch();
}
case RENDER_CONTEXT_RESTORED:
if (window.__backend.useHardware)
{
// GL.context = new OpenGLRenderContext ();
// window.context.gl = GL.context;
window.onRenderContextRestored.dispatch(window.context);
}
}
}
}
private function handleSensorEvent():Void
{
var sensor = Sensor.sensorByID.get(sensorEventInfo.id);
if (sensor != null)
{
sensor.onUpdate.dispatch(sensorEventInfo.x, sensorEventInfo.y, sensorEventInfo.z);
}
}
private function handleTextEvent():Void
{
var window = parent.__windowByID.get(textEventInfo.windowID);
if (window != null)
{
switch (textEventInfo.type)
{
case TEXT_INPUT:
window.onTextInput.dispatch(#if hl @:privateAccess String.fromUTF8(textEventInfo.text) #else textEventInfo.text #end);
case TEXT_EDIT:
window.onTextEdit.dispatch(#if hl @:privateAccess String.fromUTF8(textEventInfo.text) #else textEventInfo.text #end, textEventInfo.start,
textEventInfo.length);
default:
}
}
}
private function handleTouchEvent():Void
{
switch (touchEventInfo.type)
{
case TOUCH_START:
var touch = unusedTouchesPool.pop();
if (touch == null)
{
touch = new Touch(touchEventInfo.x, touchEventInfo.y, touchEventInfo.id, touchEventInfo.dx, touchEventInfo.dy, touchEventInfo.pressure,
touchEventInfo.device);
}
else
{
touch.x = touchEventInfo.x;
touch.y = touchEventInfo.y;
touch.id = touchEventInfo.id;
touch.dx = touchEventInfo.dx;
touch.dy = touchEventInfo.dy;
touch.pressure = touchEventInfo.pressure;
touch.device = touchEventInfo.device;
}
currentTouches.set(touch.id, touch);
Touch.onStart.dispatch(touch);
case TOUCH_END:
var touch = currentTouches.get(touchEventInfo.id);
if (touch != null)
{
touch.x = touchEventInfo.x;
touch.y = touchEventInfo.y;
touch.dx = touchEventInfo.dx;
touch.dy = touchEventInfo.dy;
touch.pressure = touchEventInfo.pressure;
Touch.onEnd.dispatch(touch);
currentTouches.remove(touchEventInfo.id);
unusedTouchesPool.add(touch);
}
case TOUCH_MOVE:
var touch = currentTouches.get(touchEventInfo.id);
if (touch != null)
{
touch.x = touchEventInfo.x;
touch.y = touchEventInfo.y;
touch.dx = touchEventInfo.dx;
touch.dy = touchEventInfo.dy;
touch.pressure = touchEventInfo.pressure;
Touch.onMove.dispatch(touch);
}
default:
}
}
private function handleWindowEvent():Void
{
var window = parent.__windowByID.get(windowEventInfo.windowID);
if (window != null)
{
switch (windowEventInfo.type)
{
case WINDOW_ACTIVATE:
advanceTimer();
window.onActivate.dispatch();
AudioManager.resume();
case WINDOW_CLOSE:
window.close();
case WINDOW_DEACTIVATE:
window.onDeactivate.dispatch();
AudioManager.suspend();
pauseTimer = System.getTimer();
case WINDOW_ENTER:
window.onEnter.dispatch();
case WINDOW_EXPOSE:
window.onExpose.dispatch();
case WINDOW_FOCUS_IN:
window.onFocusIn.dispatch();
case WINDOW_FOCUS_OUT:
window.onFocusOut.dispatch();
case WINDOW_LEAVE:
window.onLeave.dispatch();
case WINDOW_MAXIMIZE:
window.__maximized = true;
window.__fullscreen = false;
window.__minimized = false;
window.onMaximize.dispatch();
case WINDOW_MINIMIZE:
window.__minimized = true;
window.__maximized = false;
window.__fullscreen = false;
window.onMinimize.dispatch();
case WINDOW_MOVE:
window.__x = windowEventInfo.x;
window.__y = windowEventInfo.y;
window.onMove.dispatch(windowEventInfo.x, windowEventInfo.y);
case WINDOW_RESIZE:
window.__width = windowEventInfo.width;
window.__height = windowEventInfo.height;
window.onResize.dispatch(windowEventInfo.width, windowEventInfo.height);
case WINDOW_RESTORE:
window.__fullscreen = false;
window.__minimized = false;
window.onRestore.dispatch();
case WINDOW_SHOW:
window.onShow.dispatch();
case WINDOW_HIDE:
window.onHide.dispatch();
}
}
}
private function updateTimer():Void
{
#if lime_cffi
if (Timer.sRunningTimers.length > 0)
{
var currentTime = System.getTimer();
var foundNull = false;
var timer;
for (i in 0...Timer.sRunningTimers.length)
{
timer = Timer.sRunningTimers[i];
if (timer != null && timer.mRunning)
{
if (currentTime >= timer.mFireAt)
{
timer.mFireAt += timer.mTime;
timer.run();
}
}
else
{
foundNull = true;
}
}
if (foundNull)
{
Timer.sRunningTimers = Timer.sRunningTimers.filter(function(val)
{
return val != null && val.mRunning;
});
}
}
#if (haxe_ver >= 4.2)
#if target.threaded
sys.thread.Thread.current().events.progress();
#else
// Duplicate code required because Haxe 3 can't handle
// #if (haxe_ver >= 4.2 && target.threaded)
@:privateAccess haxe.EntryPoint.processEvents();
#end
#else
@:privateAccess haxe.EntryPoint.processEvents();
#end
#end
}
}
@:keep /*private*/ class ApplicationEventInfo
{
public var deltaTime:Int;
public var type:ApplicationEventType;
public function new(type:ApplicationEventType = null, deltaTime:Int = 0)
{
this.type = type;
this.deltaTime = deltaTime;
}
public function clone():ApplicationEventInfo
{
return new ApplicationEventInfo(type, deltaTime);
}
}
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract ApplicationEventType(Int)
{
var UPDATE = 0;
var EXIT = 1;
}
@:keep /*private*/ class ClipboardEventInfo
{
public var type:ClipboardEventType;
public function new(type:ClipboardEventType = null)
{
this.type = type;
}
public function clone():ClipboardEventInfo
{
return new ClipboardEventInfo(type);
}
}
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract ClipboardEventType(Int)
{
var UPDATE = 0;
}
@:keep /*private*/ class DropEventInfo
{
public var file:#if hl hl.Bytes #else String #end;
public var type:DropEventType;
public function new(type:DropEventType = null, file = null)
{
this.type = type;
this.file = file;
}
public function clone():DropEventInfo
{
return new DropEventInfo(type, file);
}
}
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract DropEventType(Int)
{
var DROP_FILE = 0;
}
@:keep /*private*/ class GamepadEventInfo
{
public var axis:Int;
public var button:Int;
public var id:Int;
public var type:GamepadEventType;
public var axisValue:Float;
public function new(type:GamepadEventType = null, id:Int = 0, button:Int = 0, axis:Int = 0, value:Float = 0)
{
this.type = type;
this.id = id;
this.button = button;
this.axis = axis;
this.axisValue = value;
}
public function clone():GamepadEventInfo
{
return new GamepadEventInfo(type, id, button, axis, axisValue);
}
}
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract GamepadEventType(Int)
{
var AXIS_MOVE = 0;
var BUTTON_DOWN = 1;
var BUTTON_UP = 2;
var CONNECT = 3;
var DISCONNECT = 4;
}
@:keep /*private*/ class JoystickEventInfo
{
public var id:Int;
public var index:Int;
public var type:JoystickEventType;
public var eventValue:Int;
public var x:Float;
public var y:Float;
public function new(type:JoystickEventType = null, id:Int = 0, index:Int = 0, value:Int = 0, x:Float = 0, y:Float = 0)
{
this.type = type;
this.id = id;
this.index = index;
this.eventValue = value;
this.x = x;
this.y = y;
}
public function clone():JoystickEventInfo
{
return new JoystickEventInfo(type, id, index, eventValue, x, y);
}
}
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract JoystickEventType(Int)
{
var AXIS_MOVE = 0;
var HAT_MOVE = 1;
var TRACKBALL_MOVE = 2;
var BUTTON_DOWN = 3;
var BUTTON_UP = 4;
var CONNECT = 5;
var DISCONNECT = 6;
}
@:keep /*private*/ class KeyEventInfo
{
public var keyCode: Float;
public var modifier:Int;
public var type:KeyEventType;
public var windowID:Int;
public function new(type:KeyEventType = null, windowID:Int = 0, keyCode: Float = 0, modifier:Int = 0)
{
this.type = type;
this.windowID = windowID;
this.keyCode = keyCode;
this.modifier = modifier;
}
public function clone():KeyEventInfo
{
return new KeyEventInfo(type, windowID, keyCode, modifier);
}
}
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract KeyEventType(Int)
{
var KEY_DOWN = 0;
var KEY_UP = 1;
}
@:keep /*private*/ class MouseEventInfo
{
public var button:Int;
public var movementX:Float;
public var movementY:Float;
public var type:MouseEventType;
public var windowID:Int;
public var x:Float;
public var y:Float;
public var clickCount:Int;
public function new(type:MouseEventType = null, windowID:Int = 0, x:Float = 0, y:Float = 0, button:Int = 0, movementX:Float = 0, movementY:Float = 0, clickCount:Int = 0)
{
this.type = type;
this.windowID = 0;
this.x = x;
this.y = y;
this.button = button;
this.movementX = movementX;
this.movementY = movementY;
this.clickCount = clickCount;
}
public function clone():MouseEventInfo
{
return new MouseEventInfo(type, windowID, x, y, button, movementX, movementY, clickCount);
}
}
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract MouseEventType(Int)
{
var MOUSE_DOWN = 0;
var MOUSE_UP = 1;
var MOUSE_MOVE = 2;
var MOUSE_WHEEL = 3;
}
@:keep /*private*/ class RenderEventInfo
{
public var type:RenderEventType;
public function new(type:RenderEventType = null)
{
this.type = type;
}
public function clone():RenderEventInfo
{
return new RenderEventInfo(type);
}
}
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract RenderEventType(Int)
{
var RENDER = 0;
var RENDER_CONTEXT_LOST = 1;
var RENDER_CONTEXT_RESTORED = 2;
}
@:keep /*private*/ class SensorEventInfo
{
public var id:Int;
public var x:Float;
public var y:Float;
public var z:Float;
public var type:SensorEventType;
public function new(type:SensorEventType = null, id:Int = 0, x:Float = 0, y:Float = 0, z:Float = 0)
{
this.type = type;
this.id = id;
this.x = x;
this.y = y;
this.z = z;
}
public function clone():SensorEventInfo
{
return new SensorEventInfo(type, id, x, y, z);
}
}
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract SensorEventType(Int)
{
var ACCELEROMETER = 0;
}
@:keep /*private*/ class TextEventInfo
{
public var id:Int;
public var length:Int;
public var start:Int;
public var text:#if hl hl.Bytes #else String #end;
public var type:TextEventType;
public var windowID:Int;
public function new(type:TextEventType = null, windowID:Int = 0, text = null, start:Int = 0, length:Int = 0)
{
this.type = type;
this.windowID = windowID;
this.text = text;
this.start = start;
this.length = length;
}
public function clone():TextEventInfo
{
return new TextEventInfo(type, windowID, text, start, length);
}
}
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract TextEventType(Int)
{
var TEXT_INPUT = 0;
var TEXT_EDIT = 1;
}
@:keep /*private*/ class TouchEventInfo
{
public var device:Int;
public var dx:Float;
public var dy:Float;
public var id:Int;
public var pressure:Float;
public var type:TouchEventType;
public var x:Float;
public var y:Float;
public function new(type:TouchEventType = null, x:Float = 0, y:Float = 0, id:Int = 0, dx:Float = 0, dy:Float = 0, pressure:Float = 0, device:Int = 0)
{
this.type = type;
this.x = x;
this.y = y;
this.id = id;
this.dx = dx;
this.dy = dy;
this.pressure = pressure;
this.device = device;
}
public function clone():TouchEventInfo
{
return new TouchEventInfo(type, x, y, id, dx, dy, pressure, device);
}
}
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract TouchEventType(Int)
{
var TOUCH_START = 0;
var TOUCH_END = 1;
var TOUCH_MOVE = 2;
}
@:keep /*private*/ class WindowEventInfo
{
public var height:Int;
public var type:WindowEventType;
public var width:Int;
public var windowID:Int;
public var x:Int;
public var y:Int;
public function new(type:WindowEventType = null, windowID:Int = 0, width:Int = 0, height:Int = 0, x:Int = 0, y:Int = 0)
{
this.type = type;
this.windowID = windowID;
this.width = width;
this.height = height;
this.x = x;
this.y = y;
}
public function clone():WindowEventInfo
{
return new WindowEventInfo(type, windowID, width, height, x, y);
}
}
#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract WindowEventType(Int)
{
var WINDOW_ACTIVATE = 0;
var WINDOW_CLOSE = 1;
var WINDOW_DEACTIVATE = 2;
var WINDOW_ENTER = 3;
var WINDOW_EXPOSE = 4;
var WINDOW_FOCUS_IN = 5;
var WINDOW_FOCUS_OUT = 6;
var WINDOW_LEAVE = 7;
var WINDOW_MAXIMIZE = 8;
var WINDOW_MINIMIZE = 9;
var WINDOW_MOVE = 10;
var WINDOW_RESIZE = 11;
var WINDOW_RESTORE = 12;
var WINDOW_SHOW = 13;
var WINDOW_HIDE = 14;
}