Compare commits
No commits in common. "master" and "changes_laptop" have entirely different histories.
master
...
changes_la
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,7 +1,3 @@
|
||||
.vscode
|
||||
/hGameTest/bin
|
||||
/hGameTest/node_modules
|
||||
|
||||
hGameTest/exp/
|
||||
pp
|
||||
bla
|
||||
@ -1,56 +0,0 @@
|
||||
import lime.tools.HXProject;
|
||||
import lime.tools.Platform;
|
||||
import hxp.*;
|
||||
|
||||
class BuildTool extends Script{
|
||||
public static function main() {
|
||||
// Get command line arguments
|
||||
var args = Sys.args();
|
||||
var target = "linux"; // Default
|
||||
|
||||
trace(args);
|
||||
// Check if a platform was specified
|
||||
if (args.length > 0) {
|
||||
target = args[0].toLowerCase();
|
||||
}
|
||||
|
||||
// Create the project
|
||||
var project = new Project();
|
||||
|
||||
// Set target platform
|
||||
switch (target) {
|
||||
case "windows": project.target = Platform.WINDOWS;
|
||||
case "mac": project.target = Platform.MAC;
|
||||
case "linux": project.target = Platform.LINUX;
|
||||
case "android": project.target = Platform.ANDROID;
|
||||
case "ios": project.target = Platform.IOS;
|
||||
case "html5": project.target = Platform.HTML5;
|
||||
default: Sys.println('Unknown platform: $target, using linux');
|
||||
project.target = Platform.LINUX;
|
||||
}
|
||||
|
||||
// Create build configuration
|
||||
var command = "build";
|
||||
if (args.length > 1) {
|
||||
command = args[1].toLowerCase();
|
||||
}
|
||||
|
||||
// Additional flags
|
||||
var flags = [];
|
||||
if (args.length > 2) {
|
||||
flags = args.slice(2);
|
||||
}
|
||||
|
||||
// Use Lime's internal CommandLineTools
|
||||
var commandLineArgs = [command, target].concat(flags);
|
||||
// var lime = new CommandLineTools();
|
||||
|
||||
// Pass the project directly to lime
|
||||
// lime.project = project;
|
||||
|
||||
HXProject.main();
|
||||
|
||||
// Execute the command
|
||||
// lime.process(commandLineArgs);
|
||||
}
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
package;
|
||||
|
||||
import lime.tools.*;
|
||||
import openfl.tools.*;
|
||||
import hxp.*;
|
||||
|
||||
class Project extends HXProject {
|
||||
public function new() {
|
||||
super();
|
||||
|
||||
// Meta data
|
||||
meta.title = "hgame";
|
||||
meta.packageName = "FLWork";
|
||||
meta.version = "1.0.0";
|
||||
meta.company = "";
|
||||
|
||||
// App settings
|
||||
app.main = "Main";
|
||||
app.file = "hgame";
|
||||
app.path = "bin";
|
||||
|
||||
|
||||
// Window config
|
||||
window.background = 0xFFFFFF;
|
||||
window.fps = 60;
|
||||
window.width = 1280;
|
||||
window.height = 960;
|
||||
window.vsync = false;
|
||||
window.antialiasing = 0;
|
||||
|
||||
// Source path
|
||||
sources.push("src");
|
||||
sources.push("."); // we need this to enable code completion for this file
|
||||
|
||||
// Libraries
|
||||
haxelibs.push(new Haxelib("lime", "8.2.2"));
|
||||
haxelibs.push(new Haxelib("openfl", "9.4.1"));
|
||||
haxelibs.push(new Haxelib("actuate"));
|
||||
haxelibs.push(new Haxelib("hxp"));
|
||||
|
||||
|
||||
// Assets
|
||||
includeAssets("res/textures", "textures");
|
||||
includeAssets("res/fonts", "fonts");
|
||||
includeAssets("res/data", "data");
|
||||
// assets.push(new lime.tools.Asset("res/textures", "textures"));
|
||||
// assets.push(new lime.tools.Asset("res/fonts", "fonts"));
|
||||
// assets.push(new lime.tools.Asset("res/data", "data"));
|
||||
|
||||
// Haxe flags
|
||||
haxeflags.push("--macro addGlobalMetadata('', '@:build(engine.macros.ConVarDecorators.build())')");
|
||||
haxeflags.push("--macro keep('Main')");
|
||||
haxeflags.push("-dce full");
|
||||
|
||||
// target = Platform.LINUX;
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
-lib actuate
|
||||
-lib lime
|
||||
-lib openfl
|
||||
-lib hxp
|
||||
|
||||
--main ApplicationMain
|
||||
#-main App
|
||||
#-js src/app.js
|
||||
#-cp node_modules/openfl/lib
|
||||
#-cp src
|
||||
#-D buildhxml
|
||||
#-D source-map
|
||||
|
||||
--main
|
||||
-hl exp/out.hl
|
||||
-cp src
|
||||
|
||||
--cpp bin/cpp
|
||||
@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
haxe -lib lime -lib hxp --run BuildTool.hx
|
||||
@ -1,11 +0,0 @@
|
||||
-L lime
|
||||
-L openfl
|
||||
-L hxp
|
||||
|
||||
--class-path src
|
||||
|
||||
--class-path bin/hl/haxe
|
||||
|
||||
--main ApplicationMain
|
||||
|
||||
--interp
|
||||
2
hGameTest/dist/index.html
vendored
2
hGameTest/dist/index.html
vendored
@ -1,7 +1,7 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>test</title>
|
||||
<title>nigger</title>
|
||||
<link rel="shortcut icon" type="image/png" href="./favicon.png">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
# @install: lix --silent download "haxelib:/actuate#1.9.0" into actuate/1.9.0/haxelib
|
||||
-cp ${HAXE_LIBCACHE}/actuate/1.9.0/haxelib/src
|
||||
-D actuate=1.9.0
|
||||
@ -1,4 +0,0 @@
|
||||
# @install: lix --silent download "haxelib:/hxcpp-debug-server#1.2.4" into hxcpp-debug-server/1.2.4/haxelib
|
||||
-cp ${HAXE_LIBCACHE}/hxcpp-debug-server/1.2.4/haxelib/
|
||||
-D hxcpp-debug-server=1.2.4
|
||||
--macro hxcpp.debug.jsonrpc.Macro.injectServer()
|
||||
@ -1,4 +0,0 @@
|
||||
# @install: lix --silent download "haxelib:/hxp#1.3.0" into hxp/1.3.0/haxelib
|
||||
# @run: haxelib run-dir hxp "${HAXE_LIBCACHE}/hxp/1.3.0/haxelib"
|
||||
-cp ${HAXE_LIBCACHE}/hxp/1.3.0/haxelib/src
|
||||
-D hxp=1.3.0
|
||||
@ -1,6 +0,0 @@
|
||||
# @install: lix --silent download "haxelib:/lime#8.2.2" into lime/8.2.2/haxelib
|
||||
# @run: haxelib run-dir lime "${HAXE_LIBCACHE}/lime/8.2.2/haxelib"
|
||||
-cp ${HAXE_LIBCACHE}/lime/8.2.2/haxelib/src
|
||||
-D lime=8.2.2
|
||||
--macro lime._internal.macros.DefineMacro.run()
|
||||
-lib ndll:${HAXE_LIBCACHE}/lime/8.2.2/haxelib/ndll/
|
||||
@ -1,5 +0,0 @@
|
||||
# @install: lix --silent download "haxelib:/openfl#9.4.1" into openfl/9.4.1/haxelib
|
||||
# @run: haxelib run-dir openfl "${HAXE_LIBCACHE}/openfl/9.4.1/haxelib"
|
||||
-cp ${HAXE_LIBCACHE}/openfl/9.4.1/haxelib/src
|
||||
-D openfl=9.4.1
|
||||
--macro openfl.utils._internal.ExtraParamsMacro.include()
|
||||
2
hGameTest/package-lock.json
generated
2
hGameTest/package-lock.json
generated
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "test",
|
||||
"name": "nigger",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "test",
|
||||
"name": "nigger",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"config": {
|
||||
|
||||
@ -1 +0,0 @@
|
||||
Project.hx
|
||||
29
hGameTest/project.xml
Normal file
29
hGameTest/project.xml
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<project>
|
||||
<!-- NMML reference: https://gist.github.com/1763850 -->
|
||||
|
||||
<!-- metadata, make sure 'package' is at least 3 segments (ie. com.mycompany.myproject) -->
|
||||
<meta title="hgame" package="FLWork" version="1.0.0" company="" />
|
||||
|
||||
<!-- output -->
|
||||
<app main="Main" file="hgame" path="bin" />
|
||||
|
||||
<window background="#FFFFFF" fps="60" />
|
||||
<!--<window width="320" height="480" unless="mobile" />-->
|
||||
<window width="1280" height="960" unless="mobile || html5" />
|
||||
<window orientation="landscape" vsync="false" antialiasing="0" if="cpp" />
|
||||
|
||||
<!-- classpath, haxe libs -->
|
||||
<source path="src" />
|
||||
<assets path="./res/textures" rename="textures" />
|
||||
<assets path="./res/fonts" rename="fonts" />
|
||||
<assets path="./res/data" rename="data"/>
|
||||
<haxelib name="openfl" />
|
||||
<haxelib name="actuate" />
|
||||
<!-- assets -->
|
||||
<!--<icon path="assets/openfl.svg" /> -->
|
||||
<!-- <assets path="assets/img" rename="img" /> -->
|
||||
|
||||
<!-- optimize output
|
||||
<haxeflag name="-dce full" /> -->
|
||||
</project>
|
||||
@ -1,369 +0,0 @@
|
||||
package;
|
||||
|
||||
#if macro
|
||||
import haxe.macro.Compiler;
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Expr;
|
||||
#end
|
||||
|
||||
@:access(lime.app.Application)
|
||||
@:access(lime.system.System)
|
||||
@:access(openfl.display.Stage)
|
||||
@:access(openfl.events.UncaughtErrorEvents)
|
||||
@:dox(hide)
|
||||
class ApplicationMain
|
||||
{
|
||||
#if !macro
|
||||
public static function main()
|
||||
{
|
||||
lime.system.System.__registerEntryPoint("::APP_FILE::", create);
|
||||
|
||||
#if (js && html5)
|
||||
#if (munit || (utest && openfl_enable_utest_legacy_mode))
|
||||
lime.system.System.embed("::APP_FILE::", null, ::WIN_WIDTH::, ::WIN_HEIGHT::);
|
||||
#end
|
||||
#else
|
||||
create(null);
|
||||
#end
|
||||
}
|
||||
|
||||
public static function create(config):Void
|
||||
{
|
||||
var app = new openfl.display.Application();
|
||||
|
||||
#if !disable_preloader_assets
|
||||
ManifestResources.init(config);
|
||||
#end
|
||||
|
||||
app.meta["build"] = "::meta.buildNumber::";
|
||||
app.meta["company"] = "::meta.company::";
|
||||
app.meta["file"] = "::APP_FILE::";
|
||||
app.meta["name"] = "::meta.title::";
|
||||
app.meta["packageName"] = "::meta.packageName::";
|
||||
app.meta["version"] = "::meta.version::";
|
||||
|
||||
::if (config.hxtelemetry != null)::#if hxtelemetry
|
||||
app.meta["hxtelemetry-allocations"] = "::config.hxtelemetry.allocations::";
|
||||
app.meta["hxtelemetry-host"] = "::config.hxtelemetry.host::";
|
||||
#end::end::
|
||||
|
||||
#if !flash
|
||||
::foreach windows::
|
||||
var attributes:lime.ui.WindowAttributes = {
|
||||
allowHighDPI: ::allowHighDPI::,
|
||||
alwaysOnTop: ::alwaysOnTop::,
|
||||
borderless: ::borderless::,
|
||||
// display: ::display::,
|
||||
element: null,
|
||||
frameRate: ::fps::,
|
||||
#if !web fullscreen: ::fullscreen::, #end
|
||||
height: ::height::,
|
||||
hidden: #if munit true #else ::hidden:: #end,
|
||||
maximized: ::maximized::,
|
||||
minimized: ::minimized::,
|
||||
parameters: ::parameters::,
|
||||
resizable: ::resizable::,
|
||||
title: "::title::",
|
||||
width: ::width::,
|
||||
x: ::x::,
|
||||
y: ::y::,
|
||||
};
|
||||
|
||||
attributes.context = {
|
||||
antialiasing: ::antialiasing::,
|
||||
background: ::background::,
|
||||
colorDepth: ::colorDepth::,
|
||||
depth: ::depthBuffer::,
|
||||
hardware: ::hardware::,
|
||||
stencil: ::stencilBuffer::,
|
||||
type: null,
|
||||
vsync: ::vsync::
|
||||
};
|
||||
|
||||
if (app.window == null)
|
||||
{
|
||||
if (config != null)
|
||||
{
|
||||
for (field in Reflect.fields(config))
|
||||
{
|
||||
if (Reflect.hasField(attributes, field))
|
||||
{
|
||||
Reflect.setField(attributes, field, Reflect.field(config, field));
|
||||
}
|
||||
else if (Reflect.hasField(attributes.context, field))
|
||||
{
|
||||
Reflect.setField(attributes.context, field, Reflect.field(config, field));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if sys
|
||||
lime.system.System.__parseArguments(attributes);
|
||||
#end
|
||||
}
|
||||
|
||||
app.createWindow(attributes);
|
||||
::end::
|
||||
#elseif air
|
||||
app.window.title = "::meta.title::";
|
||||
#else
|
||||
app.window.context.attributes.background = ::WIN_BACKGROUND::;
|
||||
app.window.frameRate = ::WIN_FPS::;
|
||||
#end
|
||||
|
||||
var preloader = getPreloader();
|
||||
app.preloader.onProgress.add (function(loaded, total)
|
||||
{
|
||||
@:privateAccess preloader.update(loaded, total);
|
||||
});
|
||||
app.preloader.onComplete.add(function()
|
||||
{
|
||||
@:privateAccess preloader.start();
|
||||
});
|
||||
|
||||
preloader.onComplete.add(start.bind((cast app.window:openfl.display.Window).stage));
|
||||
|
||||
#if !disable_preloader_assets
|
||||
for (library in ManifestResources.preloadLibraries)
|
||||
{
|
||||
app.preloader.addLibrary(library);
|
||||
}
|
||||
|
||||
for (name in ManifestResources.preloadLibraryNames)
|
||||
{
|
||||
app.preloader.addLibraryName(name);
|
||||
}
|
||||
#end
|
||||
|
||||
app.preloader.load();
|
||||
|
||||
var result = app.exec();
|
||||
|
||||
#if (sys && !ios && !nodejs && !emscripten)
|
||||
lime.system.System.exit(result);
|
||||
#end
|
||||
}
|
||||
|
||||
public static function start(stage:openfl.display.Stage):Void
|
||||
{
|
||||
#if flash
|
||||
ApplicationMain.getEntryPoint();
|
||||
#else
|
||||
if (stage.__uncaughtErrorEvents.__enabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
ApplicationMain.getEntryPoint();
|
||||
|
||||
stage.dispatchEvent(new openfl.events.Event(openfl.events.Event.RESIZE, false, false));
|
||||
|
||||
if (stage.window.fullscreen)
|
||||
{
|
||||
stage.dispatchEvent(new openfl.events.FullScreenEvent(openfl.events.FullScreenEvent.FULL_SCREEN, false, false, true, true));
|
||||
}
|
||||
}
|
||||
catch (e:Dynamic)
|
||||
{
|
||||
#if !display
|
||||
stage.__handleError(e);
|
||||
#end
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplicationMain.getEntryPoint();
|
||||
|
||||
stage.dispatchEvent(new openfl.events.Event(openfl.events.Event.RESIZE, false, false));
|
||||
|
||||
if (stage.window.fullscreen)
|
||||
{
|
||||
stage.dispatchEvent(new openfl.events.FullScreenEvent(openfl.events.FullScreenEvent.FULL_SCREEN, false, false, true, true));
|
||||
}
|
||||
}
|
||||
#end
|
||||
}
|
||||
#end
|
||||
|
||||
macro public static function getEntryPoint()
|
||||
{
|
||||
var hasMain = false;
|
||||
|
||||
switch (Context.follow(Context.getType("::APP_MAIN::")))
|
||||
{
|
||||
case TInst(t, params):
|
||||
|
||||
var type = t.get();
|
||||
for (method in type.statics.get())
|
||||
{
|
||||
if (method.name == "main")
|
||||
{
|
||||
hasMain = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasMain)
|
||||
{
|
||||
return Context.parse("@:privateAccess ::APP_MAIN::.main()", Context.currentPos());
|
||||
}
|
||||
else if (type.constructor != null)
|
||||
{
|
||||
return macro
|
||||
{
|
||||
var current = stage.getChildAt (0);
|
||||
|
||||
if (current == null || !(current is openfl.display.DisplayObjectContainer))
|
||||
{
|
||||
current = new openfl.display.MovieClip();
|
||||
stage.addChild(current);
|
||||
}
|
||||
|
||||
new DocumentClass(cast current);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.fatalError("Main class \"::APP_MAIN::\" has neither a static main nor a constructor.", Context.currentPos());
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
Context.fatalError("Main class \"::APP_MAIN::\" isn't a class.", Context.currentPos());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
macro public static function getPreloader()
|
||||
{
|
||||
::if (PRELOADER_NAME != "")::
|
||||
var type = Context.getType("::PRELOADER_NAME::");
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case TInst(classType, _):
|
||||
|
||||
var searchTypes = classType.get();
|
||||
|
||||
while (searchTypes != null)
|
||||
{
|
||||
if (searchTypes.pack.length == 2 && searchTypes.pack[0] == "openfl" && searchTypes.pack[1] == "display" && searchTypes.name == "Preloader")
|
||||
{
|
||||
return macro
|
||||
{
|
||||
new ::PRELOADER_NAME::();
|
||||
};
|
||||
}
|
||||
|
||||
if (searchTypes.superClass != null)
|
||||
{
|
||||
searchTypes = searchTypes.superClass.t.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
searchTypes = null;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
return macro
|
||||
{
|
||||
new openfl.display.Preloader(new ::PRELOADER_NAME::());
|
||||
}
|
||||
::else::
|
||||
return macro
|
||||
{
|
||||
new openfl.display.Preloader(new openfl.display.Preloader.DefaultPreloader());
|
||||
};
|
||||
::end::
|
||||
}
|
||||
|
||||
#if !macro
|
||||
@:noCompletion @:dox(hide) public static function __init__()
|
||||
{
|
||||
var init = lime.app.Application;
|
||||
|
||||
#if neko
|
||||
// Copy from https://github.com/HaxeFoundation/haxe/blob/development/std/neko/_std/Sys.hx#L164
|
||||
// since Sys.programPath () isn't available in __init__
|
||||
var sys_program_path = {
|
||||
var m = neko.vm.Module.local().name;
|
||||
try
|
||||
{
|
||||
sys.FileSystem.fullPath(m);
|
||||
}
|
||||
catch (e:Dynamic)
|
||||
{
|
||||
// maybe the neko module name was supplied without .n extension...
|
||||
if (!StringTools.endsWith(m, ".n"))
|
||||
{
|
||||
try
|
||||
{
|
||||
sys.FileSystem.fullPath(m + ".n");
|
||||
}
|
||||
catch (e:Dynamic)
|
||||
{
|
||||
m;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var loader = new neko.vm.Loader(untyped $loader);
|
||||
loader.addPath(haxe.io.Path.directory(#if (haxe_ver >= 3.3) sys_program_path #else Sys.executablePath() #end));
|
||||
loader.addPath("./");
|
||||
loader.addPath("@executable_path/");
|
||||
#end
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
#if !macro
|
||||
@:build(DocumentClass.build())
|
||||
@:keep @:dox(hide) class DocumentClass extends ::APP_MAIN:: {}
|
||||
#else
|
||||
class DocumentClass
|
||||
{
|
||||
macro public static function build():Array<Field>
|
||||
{
|
||||
var classType = Context.getLocalClass().get();
|
||||
var searchTypes = classType;
|
||||
|
||||
while (searchTypes != null)
|
||||
{
|
||||
if (searchTypes.module == "openfl.display.DisplayObject" || searchTypes.module == "flash.display.DisplayObject")
|
||||
{
|
||||
var fields = Context.getBuildFields();
|
||||
|
||||
var method = macro
|
||||
{
|
||||
current.addChild(this);
|
||||
super();
|
||||
dispatchEvent(new openfl.events.Event(openfl.events.Event.ADDED_TO_STAGE, false, false));
|
||||
}
|
||||
|
||||
fields.push({ name: "new", access: [ APublic ], kind: FFun({ args: [ { name: "current", opt: false, type: macro :openfl.display.DisplayObjectContainer, value: null } ], expr: method, params: [], ret: macro :Void }), pos: Context.currentPos() });
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
if (searchTypes.superClass != null)
|
||||
{
|
||||
searchTypes = searchTypes.superClass.t.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
searchTypes = null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#end
|
||||
@ -1,4 +1,3 @@
|
||||
import openfl.display.Window;
|
||||
import assets.Scanner;
|
||||
import game.video.Mode;
|
||||
import openfl.events.KeyboardEvent;
|
||||
@ -13,37 +12,28 @@ class Main extends Sprite {
|
||||
public function new () {
|
||||
super ();
|
||||
this.addEventListener(Event.ADDED_TO_STAGE, onInit);
|
||||
//stage.application.window.resize(1920, 1080);
|
||||
//stage.application.window.title = "Kanker";
|
||||
}
|
||||
private function onInit(e:Event)
|
||||
{
|
||||
//Scanner.scanTextureDir();
|
||||
trace("heyy");
|
||||
stage.frameRate = 1000;
|
||||
var game:Game = new Game(stage);
|
||||
trace("kokk");
|
||||
stage.addEventListener(Event.ENTER_FRAME, game.onEnterFrame);
|
||||
stage.addEventListener(KeyboardEvent.KEY_DOWN,Input.onKeyIsDown);
|
||||
stage.addEventListener(KeyboardEvent.KEY_UP,Input.onKeyIsUp);
|
||||
stage.addEventListener(Event.RESIZE,onResize);
|
||||
game.onInit();
|
||||
|
||||
//Mode.setVideoMode(1280, 960);
|
||||
}
|
||||
private function onResize (event:Event):Void {
|
||||
|
||||
trace("yeet");
|
||||
//Here we can do shit with window scaling
|
||||
//stage.stageWidth;
|
||||
//stage.stageHeight;
|
||||
}
|
||||
|
||||
// static function main () {
|
||||
// // Bootstrap the OpenFL project
|
||||
// var application = new lime.app.Application();
|
||||
// application.createWindow({
|
||||
// hidden: false,
|
||||
// x: 0,
|
||||
// y: 0,
|
||||
// width: 800,
|
||||
// height: 600,
|
||||
// title: "DSTEngine Window"
|
||||
// });
|
||||
// }
|
||||
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
package engine;
|
||||
|
||||
import game.video.Mode;
|
||||
import haxe.macro.Expr.Field;
|
||||
import engine.typedefs.console.CVar;
|
||||
import engine.typedefs.console.CCmd;
|
||||
import engine.enums.console.CVarType;
|
||||
import engine.enums.console.CVarFlag;
|
||||
import engine.macros.ConVarDecorators;
|
||||
import game.ui.console.Console;
|
||||
|
||||
@:keep
|
||||
// @:build(engine.macros.CCmdDecorator.build()) // No longer needed because we added the build macro to the project.xml
|
||||
class ConVars_Engine {
|
||||
|
||||
@:concmd("list")
|
||||
public static function listCommands(args:Array<String>) {
|
||||
var CVarMap:Map<String, CVar> = ConVar.getCVarMap();
|
||||
var keys:Array<String> = ConVar.getCVarNames();
|
||||
for(key in keys){
|
||||
if(CVarMap[key] != null){
|
||||
Console.devMsg(key+" "+ CVarMap[key].value);
|
||||
}
|
||||
else{
|
||||
Console.devMsg(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@:concmd("test")
|
||||
public static function testCommand() {
|
||||
// Command implementation
|
||||
trace("Test Output");
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
package engine;
|
||||
|
||||
import game.ui.console.Console;
|
||||
import Sys;
|
||||
|
||||
#if sys
|
||||
class HProfiler{
|
||||
public static var profilerList:Array<HProfiler> = [];
|
||||
public static var profilerMap:Map<String, HProfiler> = [];
|
||||
private var tStart:Float;
|
||||
private var tEnd:Float;
|
||||
public var tDelta:Float;
|
||||
private var name:String;
|
||||
private function new(_name:String){
|
||||
name = _name;
|
||||
profilerMap[_name] = this;
|
||||
}
|
||||
public function start(){
|
||||
tStart = Sys.time() * 1000.0;
|
||||
}
|
||||
public function stop(){
|
||||
tEnd = Sys.time() * 1000.0;
|
||||
tDelta = (tEnd-tStart);
|
||||
}
|
||||
//public static var
|
||||
public static function startProfiling(name:String):Void
|
||||
{
|
||||
if(profilerMap[name] == null)
|
||||
profilerList.push(new HProfiler(name));
|
||||
profilerMap[name].start();
|
||||
}
|
||||
public static function stopProfiling(name:String):Void
|
||||
{
|
||||
profilerMap[name].stop();
|
||||
}
|
||||
|
||||
public static var ccmd_debug_tracetimes = ConVar.registerCCmd("debug_tracetimes", (args:Array<String>) -> {
|
||||
for(profiler in profilerList){
|
||||
Console.devMsg(profiler.name + ": " + profiler.tDelta + "ms");
|
||||
}
|
||||
});
|
||||
}
|
||||
#else
|
||||
class HProfiler{
|
||||
public static function startProfiling(name:String):Void{}
|
||||
public static function stopProfiling(name:String):Void{}
|
||||
}
|
||||
#end
|
||||
@ -1,7 +1,6 @@
|
||||
package engine.enums.console;
|
||||
package engine.enums;
|
||||
|
||||
enum CVarFlag{
|
||||
FCVAR_NONE;
|
||||
FCVAR_ARCHIVE;
|
||||
FCVAR_CHEAT;
|
||||
FVCAR_REPLICATED;
|
||||
@ -1,9 +1,8 @@
|
||||
package engine.enums.console;
|
||||
package engine.enums;
|
||||
|
||||
enum CVarType {
|
||||
CInt;
|
||||
CFloat;
|
||||
CString;
|
||||
CBool;
|
||||
//CCmd;
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
package engine.geom;
|
||||
|
||||
|
||||
typedef P2d = {
|
||||
x:Float,
|
||||
y:Float
|
||||
}
|
||||
@ -1,270 +0,0 @@
|
||||
package engine.macros;
|
||||
|
||||
#if macro
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Type;
|
||||
import haxe.macro.Expr.Field;
|
||||
import haxe.macro.Expr.Function;
|
||||
import haxe.macro.Expr.Access;
|
||||
|
||||
private typedef CVarMetadata = {
|
||||
name:Expr,
|
||||
type:String,
|
||||
value:Expr,
|
||||
flags:Expr,
|
||||
help:Expr,
|
||||
?callback:Expr
|
||||
}
|
||||
|
||||
class ConVarDecorators {
|
||||
static function extractCVarMetadata(params:Array<Expr>):CVarMetadata {
|
||||
if (params == null || params.length == 0) return null;
|
||||
|
||||
var metadata:CVarMetadata = {
|
||||
name: macro "",
|
||||
type: "CString",
|
||||
value: macro 0,
|
||||
flags: macro "FCVAR_NONE",
|
||||
help: macro "",
|
||||
callback: macro null
|
||||
};
|
||||
|
||||
switch (params[0].expr) {
|
||||
case EObjectDecl(fields):
|
||||
var fieldMap = [
|
||||
"name" => function(expr) metadata.name = expr,
|
||||
"type" => function(expr) metadata.type = switch (expr.expr) {
|
||||
case EConst(CString(s)): s;
|
||||
case _: "CString";
|
||||
},
|
||||
"value" => function(expr) metadata.value = expr,
|
||||
"flags" => function(expr) metadata.flags = expr,
|
||||
"help" => function(expr) metadata.help = expr,
|
||||
"callback" => function(expr) metadata.callback = expr
|
||||
];
|
||||
|
||||
for (field in fields) {
|
||||
if (fieldMap.exists(field.field)) {
|
||||
fieldMap[field.field](field.expr);
|
||||
} else {
|
||||
Context.error("Unexpected field: " + field.field, field.expr.pos);
|
||||
}
|
||||
}
|
||||
case _:
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
// Process command fields
|
||||
private static function processCommandField(field:Field, cmdMeta:MetadataEntry, extraFields:Array<Field>):Void {
|
||||
|
||||
if (!isFieldKind(field, "FFun")) {
|
||||
Context.error("The @:concmd metadata can only be applied to functions", field.pos);
|
||||
return;
|
||||
}
|
||||
|
||||
var pos = Context.currentPos();
|
||||
var functionReference = getFunctionFromField(field);
|
||||
if (functionReference == null) return;
|
||||
|
||||
var name = cmdMeta.params.length > 0 ? getStringFromExpr(cmdMeta.params[0]) ?? field.name : field.name;
|
||||
var functionExpression = macro function(args:Array<String>) ${functionReference.expr};
|
||||
|
||||
var initExpr = macro {
|
||||
var cmd = engine.ConVar.registerCCmd($v{name}, $functionExpression);
|
||||
cmd;
|
||||
};
|
||||
|
||||
var initName = "__init__cmd_" + name;
|
||||
extraFields.push(createStaticInitField(initName, initExpr, pos));
|
||||
}
|
||||
|
||||
// Process convar fields
|
||||
private static function processConVarField(field:Field, cvarMeta:MetadataEntry, extraFields:Array<Field>):Void {
|
||||
if (!isFieldKind(field, "FVar")) {
|
||||
Context.error("The @:convar metadata can only be applied to variables", field.pos);
|
||||
return;
|
||||
}
|
||||
|
||||
var pos = Context.currentPos();
|
||||
var meta = extractCVarMetadata(cvarMeta.params);
|
||||
if (meta == null) {
|
||||
Context.error("Metadata extraction failed for field: " + field.name, field.pos);
|
||||
return;
|
||||
}
|
||||
|
||||
var cvarName = getStringFromMetaExpr(meta.name) ?? field.name;
|
||||
var fieldName = field.name;
|
||||
var isStatic = hasAccess(field, AStatic);
|
||||
|
||||
var varType = getVarType(field);
|
||||
|
||||
if (isStatic) {
|
||||
// Handle static fields
|
||||
var staticInitExpr = macro {
|
||||
var cvar = $i{fieldName} = engine.ConVar.registerCVar(
|
||||
${meta.name},
|
||||
${Context.parse('engine.enums.console.CVarType.' + meta.type, pos)},
|
||||
${meta.value},
|
||||
${Context.parse('engine.enums.console.CVarFlag.' + switch (meta.flags.expr) {
|
||||
case EConst(CString(s)): s;
|
||||
case _: "FCVAR_NONE";
|
||||
}, pos)},
|
||||
${meta.help},
|
||||
${meta.callback ?? macro null}
|
||||
);
|
||||
trace($v{"Registered CVar: "} + $e{meta.name});
|
||||
cvar;
|
||||
};
|
||||
|
||||
extraFields.push(createStaticInitField("__init__" + cvarName, staticInitExpr, pos));
|
||||
} else {
|
||||
// Handle instance fields
|
||||
updateFieldInitialValue(field, macro null);
|
||||
|
||||
var instanceInitializerExpression = macro {
|
||||
if (Reflect.field(this, $v{fieldName}) == null) {
|
||||
Reflect.setField(this, $v{fieldName}, engine.ConVar.registerCVar(
|
||||
${meta.name},
|
||||
${Context.parse('engine.enums.console.CVarType.' + meta.type, pos)},
|
||||
${meta.value},
|
||||
${Context.parse('engine.enums.console.CVarFlag.' + switch (meta.flags.expr) {
|
||||
case EConst(CString(s)): s;
|
||||
case _: "FCVAR_NONE";
|
||||
}, pos)},
|
||||
${meta.help},
|
||||
${meta.callback ?? macro null}
|
||||
));
|
||||
trace($v{"Registered CVar: "} + $e{meta.name});
|
||||
}
|
||||
};
|
||||
|
||||
ensureConstructorWithInit(extraFields, instanceInitializerExpression, pos);
|
||||
}
|
||||
}
|
||||
|
||||
// Map to check field kinds
|
||||
private static final fieldKindMap:Map<String, haxe.macro.FieldType->Bool> = [
|
||||
"FFun" => function(ft) return switch(ft) { case FFun(_): true; case _: false; },
|
||||
"FVar" => function(ft) return switch(ft) { case FVar(_, _): true; case _: false; },
|
||||
"FProp" => function(ft) return switch(ft) { case FProp(_, _, _, _): true; case _: false; }
|
||||
];
|
||||
|
||||
// Utility functions
|
||||
private static function isFieldKind(field:Field, kind:String):Bool {
|
||||
return fieldKindMap.exists(kind) ? fieldKindMap.get(kind)(field.kind) : false;
|
||||
}
|
||||
|
||||
private static function getFunctionFromField(field:Field):Function {
|
||||
return switch (field.kind) {
|
||||
case FFun(f): f;
|
||||
case _: null;
|
||||
}
|
||||
}
|
||||
|
||||
private static function getVarType(field:Field):ComplexType {
|
||||
return switch (field.kind) {
|
||||
case FVar(t, _): t;
|
||||
case _: null;
|
||||
}
|
||||
}
|
||||
|
||||
private static function updateFieldInitialValue(field:Field, expr:Expr):Void {
|
||||
switch (field.kind) {
|
||||
case FVar(t, _): field.kind = FVar(t, expr);
|
||||
case _:
|
||||
}
|
||||
}
|
||||
|
||||
private static function getStringFromExpr(expr:Expr):String {
|
||||
return switch (expr.expr) {
|
||||
case EConst(CString(s)): s;
|
||||
case _: null;
|
||||
}
|
||||
}
|
||||
|
||||
private static function getStringFromMetaExpr(expr:Expr):String {
|
||||
return switch (expr.expr) {
|
||||
case EConst(CString(s)): s;
|
||||
case _: null;
|
||||
}
|
||||
}
|
||||
|
||||
private static function hasAccess(field:Field, access:Access):Bool {
|
||||
return field.access != null && field.access.indexOf(access) >= 0;
|
||||
}
|
||||
|
||||
private static function createStaticInitField(name:String, initExpr:Expr, pos:Position):Field {
|
||||
return {
|
||||
name: name,
|
||||
access: [AStatic],
|
||||
kind: FVar(null, initExpr),
|
||||
pos: pos,
|
||||
doc: null,
|
||||
meta: [{ name: ":keep", params: [], pos: pos }]
|
||||
};
|
||||
}
|
||||
|
||||
private static function ensureConstructorWithInit(fields:Array<Field>, initExpr:Expr, pos:Position):Void {
|
||||
var cls = Context.getLocalClass().get();
|
||||
var hasSuperClass = cls.superClass != null;
|
||||
|
||||
var constructor = Lambda.find(fields, function(f) return f.name == "new");
|
||||
if (constructor == null) {
|
||||
constructor = {
|
||||
name: "new",
|
||||
access: [APublic],
|
||||
kind: FFun({
|
||||
args: [],
|
||||
expr: if (hasSuperClass) macro {
|
||||
super();
|
||||
$initExpr;
|
||||
} else macro {
|
||||
$initExpr;
|
||||
},
|
||||
ret: null
|
||||
}),
|
||||
pos: pos,
|
||||
meta: []
|
||||
};
|
||||
fields.push(constructor);
|
||||
} else {
|
||||
switch (constructor.kind) {
|
||||
case FFun(f):
|
||||
switch(f.expr.expr) {
|
||||
case EBlock(exprs):
|
||||
exprs.push(initExpr);
|
||||
case _:
|
||||
f.expr = macro {
|
||||
${f.expr};
|
||||
$initExpr;
|
||||
}
|
||||
}
|
||||
case _:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static macro function build():Array<Field> {
|
||||
|
||||
var fields = Context.getBuildFields(); // Get all fields in the class
|
||||
var extraFields:Array<Field> = []; // Extra fields to be added
|
||||
|
||||
for (field in fields) {
|
||||
if (field.meta == null) continue; // Skip fields without metadata
|
||||
|
||||
var cmdDecorator = Lambda.find(field.meta, function(m) return m.name == ":concmd"); // Check for command decorator
|
||||
var cvarDecorator = Lambda.find(field.meta, function(m) return m.name == ":convar"); // Check for convar decorator
|
||||
|
||||
if (cmdDecorator != null) {
|
||||
processCommandField(field, cmdDecorator, extraFields);
|
||||
} else if (cvarDecorator != null && cvarDecorator.params.length > 0) {
|
||||
processConVarField(field, cvarDecorator, extraFields);
|
||||
}
|
||||
}
|
||||
|
||||
return fields.concat(extraFields);
|
||||
}
|
||||
}
|
||||
#end
|
||||
@ -1,34 +0,0 @@
|
||||
package engine.tools;
|
||||
|
||||
import lime.ui.WindowAttributes;
|
||||
import openfl.Lib;
|
||||
import openfl.display.Sprite;
|
||||
import openfl.display.Window;
|
||||
|
||||
class HWindow extends Sprite{
|
||||
public var window:Window;
|
||||
public var attribs:WindowAttributes;
|
||||
public static var windows:Array<Window> = [];
|
||||
public function new(){
|
||||
super();
|
||||
window = Lib.application.createWindow(attribs);
|
||||
windows.push(window);
|
||||
}
|
||||
public function initStage(spr:Sprite){
|
||||
window.stage.addChild(this);
|
||||
}
|
||||
public function close(){
|
||||
window.close();
|
||||
}
|
||||
public static function createEditorWindow()
|
||||
{
|
||||
|
||||
}
|
||||
//public function open()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -1,105 +0,0 @@
|
||||
package engine.tools.ui;
|
||||
|
||||
import game.ui.text.TextFormats;
|
||||
import openfl.text.TextFormat;
|
||||
import openfl.text.TextField;
|
||||
import engine.ui.UIPane;
|
||||
import lime.app.Event;
|
||||
import openfl.display.Sprite;
|
||||
import openfl.Lib;
|
||||
import lime.ui.Window;
|
||||
import engine.ConVar;
|
||||
import game.ui.console.Console;
|
||||
//import Lib.application.
|
||||
|
||||
|
||||
typedef UINode = {
|
||||
object:UIPane,
|
||||
?children:Array<UINode>
|
||||
}
|
||||
|
||||
|
||||
class UITool{
|
||||
public static var ccmd_dev_uitool = ConVar.registerCCmd("dev_uitool", (args:Array<String>) -> { open();});
|
||||
public static var uiEditorWindow:openfl.display.Window;
|
||||
public static var uiEditorSprite:Sprite;
|
||||
private static var tf:TextField;
|
||||
public static function spawnUIEditorWindow(){
|
||||
var secondWindow = Lib.application.createWindow({title: "UI Outliner"});
|
||||
if(uiEditorSprite == null){
|
||||
uiEditorSprite = new Sprite();
|
||||
uiEditorSprite.graphics.beginFill(0x00ff00);
|
||||
uiEditorSprite.graphics.drawRect(0,0,1280,960);
|
||||
}
|
||||
secondWindow.stage.addChild(uiEditorSprite);
|
||||
uiEditorWindow = secondWindow;
|
||||
secondWindow.onClose.add(()->{
|
||||
uiEditorWindow = null;
|
||||
});
|
||||
tf = new TextField();
|
||||
TextFormats.getFormats();
|
||||
var tformat = TextFormats.formats.cInputFmt;
|
||||
tf.setTextFormat(tformat);
|
||||
tf.autoSize = LEFT;
|
||||
uiEditorSprite.addChild(tf);
|
||||
update([]);
|
||||
}
|
||||
|
||||
|
||||
public static var ccmd_dev_uitool_refresh = ConVar.registerCCmd("dev_uitool_refresh", update);
|
||||
|
||||
public static var uiNodes:Map<UIPane, UINode> = [];
|
||||
public static var rootNodes:Array<UINode> = [];
|
||||
public static function collect(){
|
||||
for(pane in UIPane.panelist){
|
||||
var node:UINode = null;
|
||||
if(uiNodes[pane] == null){
|
||||
node = {
|
||||
object: pane,
|
||||
children: []
|
||||
};
|
||||
}
|
||||
else{
|
||||
node = uiNodes[pane];
|
||||
}
|
||||
var parentNode:UINode = null;
|
||||
if(uiNodes[pane.parent] == null){
|
||||
parentNode = {
|
||||
object:pane.parent,
|
||||
children: []
|
||||
}
|
||||
}
|
||||
else{
|
||||
parentNode = uiNodes[pane.parent];
|
||||
}
|
||||
parentNode.children.push(node);
|
||||
if(pane.parent == null){
|
||||
rootNodes.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function update(args:Array<String>){
|
||||
tf.text = "";
|
||||
for(pane in UIPane.panelist){
|
||||
//check if pane is toplevel
|
||||
Console.devMsg("Pane: "+ pane.name);
|
||||
//Get all Root UI Panes
|
||||
if(pane.parent == null) {
|
||||
tf.appendText("-");
|
||||
tf.appendText(pane.name+"\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function open(){
|
||||
if(uiEditorWindow == null){
|
||||
spawnUIEditorWindow();
|
||||
}
|
||||
|
||||
}
|
||||
public static function close(){
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
package engine.typedefs.console;
|
||||
package engine.typedefs;
|
||||
|
||||
import engine.typedefs.console.CVar;
|
||||
import engine.typedefs.CVar;
|
||||
|
||||
|
||||
typedef CCmd = {
|
||||
@ -1,7 +1,7 @@
|
||||
package engine.typedefs.console;
|
||||
package engine.typedefs;
|
||||
|
||||
import engine.enums.console.CVarType;
|
||||
import engine.enums.console.CVarFlag;
|
||||
import engine.enums.CVarType;
|
||||
import engine.enums.CVarFlag;
|
||||
|
||||
typedef CVar = {
|
||||
var name:String;
|
||||
@ -1,30 +0,0 @@
|
||||
package engine.ui;
|
||||
|
||||
import engine.geom.Geom.P2d;
|
||||
import engine.ui.UIPane.PaneDimensions;
|
||||
import openfl.display.DisplayObject;
|
||||
|
||||
class UIElement extends UIPane
|
||||
{
|
||||
public var dispObj:DisplayObject;
|
||||
public var padding:P2d;
|
||||
public function new(_name:String, _dimensions:PaneDimensions, _displayObject:DisplayObject)
|
||||
{
|
||||
super(_name, _dimensions);
|
||||
dispObj = _displayObject;
|
||||
sprite.addChild(dispObj);
|
||||
}
|
||||
override public function onResize(){
|
||||
super.onResize();
|
||||
resizeDisplayObject();
|
||||
}
|
||||
public function resizeDisplayObject(){
|
||||
if(padding == null)
|
||||
padding = {x:0,y:0};
|
||||
dispObj.width = this.dimensions.width - padding.x;
|
||||
dispObj.height = this.dimensions.height - padding.y;
|
||||
dispObj.x = padding.x / 2;
|
||||
dispObj.y = padding.y / 2;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,334 +0,0 @@
|
||||
package engine.ui;
|
||||
|
||||
// __ __ _____ ______
|
||||
// | | | | / ___ \ | ____|
|
||||
// | |____| | | / _\_| | |____
|
||||
// | ____ | | | |_ \ | ____|
|
||||
// | | | | | \__/ | | |____
|
||||
// |__| |__| \______/ |______|
|
||||
|
||||
/*
|
||||
/ file: UIPane.hx
|
||||
/ author: and.schaafsma@gmail.com
|
||||
/ purpose: Class for resizable and scalable UI Panels
|
||||
/ My hope is that this code is so awful I'm never allowed to write UI code again.
|
||||
*/
|
||||
import game.ui.console.Console;
|
||||
import openfl.display.Sprite;
|
||||
import openfl.display.BitmapData;
|
||||
import openfl.display.DisplayObject;
|
||||
import openfl.text.TextField;
|
||||
import engine.ConVar;
|
||||
import engine.geom.Geom.P2d;
|
||||
|
||||
enum abstract PaneLayout(Int) {
|
||||
var HORIZONTAL = 0;
|
||||
var VERTICAL = 1;
|
||||
}
|
||||
|
||||
enum PaneAnchor {
|
||||
LEFT;
|
||||
TOPLEFT;
|
||||
TOP;
|
||||
TOPRIGHT;
|
||||
RIGHT;
|
||||
BOTTOMLEFT;
|
||||
BOTTOM;
|
||||
BOTTOMRIGHT;
|
||||
}
|
||||
|
||||
enum ExpandBehavior {
|
||||
// ABSOLUTE;
|
||||
FACTOR(f:Float);
|
||||
// CONTENT;
|
||||
STRETCH;
|
||||
// FILL;
|
||||
// FIT;
|
||||
NONE;
|
||||
}
|
||||
|
||||
enum PaneAlign {
|
||||
START;
|
||||
END;
|
||||
}
|
||||
|
||||
typedef PaneDimensions = {
|
||||
var width:Float;
|
||||
var height:Float;
|
||||
@:optional var expandBehavior:ExpandBehavior;
|
||||
@:optional var minWidth:Float;
|
||||
@:optional var minHeight:Float;
|
||||
@:optional var maxWidth:Float;
|
||||
@:optional var maxHeight:Float;
|
||||
}
|
||||
|
||||
typedef PaneProps = {
|
||||
dimensions:PaneDimensions,
|
||||
layout:PaneLayout
|
||||
}
|
||||
|
||||
class UIPane {
|
||||
public var name:String;
|
||||
public var sprite:Sprite;
|
||||
public var x(get, set):Float;
|
||||
public var y(get, set):Float;
|
||||
|
||||
function get_x() {
|
||||
return sprite.x;
|
||||
}
|
||||
|
||||
function set_x(x) {
|
||||
return sprite.x = x;
|
||||
}
|
||||
|
||||
function get_y() {
|
||||
return sprite.y;
|
||||
}
|
||||
|
||||
function set_y(y) {
|
||||
return sprite.y = y;
|
||||
}
|
||||
|
||||
public var dimensions:PaneDimensions;
|
||||
public var width(get, set):Float;
|
||||
|
||||
function get_width() {
|
||||
return dimensions.width;
|
||||
}
|
||||
|
||||
function set_width(width) {
|
||||
dimensions.width = width;
|
||||
onResize();
|
||||
return width;
|
||||
}
|
||||
|
||||
public var height(get, set):Float;
|
||||
|
||||
function get_height() {
|
||||
return dimensions.height;
|
||||
}
|
||||
|
||||
function set_height(height) {
|
||||
dimensions.height = height;
|
||||
onResize();
|
||||
return height;
|
||||
}
|
||||
|
||||
public var layout:PaneLayout;
|
||||
public var autoArrange:Bool = true;
|
||||
public var autoArrangeChildren = true;
|
||||
public var expand:Bool;
|
||||
public var children:Array<UIPane> = [];
|
||||
public var parent:UIPane = null;
|
||||
public var align:PaneAlign = START;
|
||||
public var maskSprite:Sprite;
|
||||
|
||||
public static var panelist:Array<UIPane> = [];
|
||||
|
||||
public function new(_name:String, _dimensions:PaneDimensions) {
|
||||
// Set name
|
||||
name = _name;
|
||||
// Set dimensions
|
||||
if (_dimensions != null) {
|
||||
dimensions = _dimensions;
|
||||
} else {
|
||||
dimensions = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
minWidth: 0,
|
||||
minHeight: 0,
|
||||
maxWidth: 0,
|
||||
maxHeight: 0
|
||||
};
|
||||
}
|
||||
if (name == null || name == "") {
|
||||
Console.devMsg("UI Pane name init error");
|
||||
}
|
||||
// Initialize Sprite
|
||||
initSprite();
|
||||
// Draw debug pane for visualizing
|
||||
drawDebugPane();
|
||||
panelist.push(this);
|
||||
}
|
||||
|
||||
public static var ccmd_ui_redraw = ConVar.registerCCmd("ui_redraw", (args:Array<String>) -> {
|
||||
redrawUIPanes();
|
||||
});
|
||||
|
||||
public static function redrawUIPanes() {
|
||||
for (pane in panelist) {
|
||||
pane.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
public function getAnchorOffset(anchor:PaneAnchor):P2d {
|
||||
switch (anchor) {
|
||||
case LEFT:
|
||||
return {x: 0, y: (height / 2)};
|
||||
case TOPLEFT:
|
||||
return {x: 0, y: 0};
|
||||
case TOP:
|
||||
return {x: width / 2, y: 0};
|
||||
case TOPRIGHT:
|
||||
return {x: width, y: 0};
|
||||
case RIGHT:
|
||||
return {x: width, y: height / 2}
|
||||
case BOTTOMRIGHT:
|
||||
return {x: width, y: height};
|
||||
case BOTTOM:
|
||||
return {x: width / 2, y: height};
|
||||
case BOTTOMLEFT:
|
||||
return {x: 0, y: height};
|
||||
}
|
||||
}
|
||||
|
||||
public function initSprite():Void {
|
||||
// Construct Sprite object
|
||||
sprite = new Sprite();
|
||||
|
||||
// Draw mask
|
||||
maskSprite = new Sprite();
|
||||
drawMask();
|
||||
sprite.addChild(maskSprite);
|
||||
sprite.mask = maskSprite;
|
||||
}
|
||||
|
||||
public function drawMask():Void {
|
||||
maskSprite.graphics.clear();
|
||||
maskSprite.graphics.beginFill(0xffffff);
|
||||
maskSprite.graphics.drawRect(0, 0, width, height);
|
||||
}
|
||||
|
||||
public function drawDebugPane() {
|
||||
// Clear graphics
|
||||
sprite.graphics.clear();
|
||||
// Create objects we need to draw.
|
||||
var label:TextField = new TextField();
|
||||
var bmp = new BitmapData(Std.int(width), Std.int(height), true, 0x00000000);
|
||||
// Set textfield text to the panel name
|
||||
label.text = name;
|
||||
// Draw textfield to bitmap
|
||||
bmp.draw(label);
|
||||
// Draw graphics
|
||||
sprite.graphics.beginFill(Std.int(Math.random() * 0xffffff), 0.5);
|
||||
sprite.graphics.drawRect(0, 0, dimensions.width, dimensions.height);
|
||||
sprite.graphics.beginBitmapFill(bmp, null);
|
||||
sprite.graphics.drawRect(0, 0, dimensions.width, dimensions.height);
|
||||
// Visualize Anchor Points
|
||||
if (false) {
|
||||
sprite.graphics.beginFill(Std.int(Math.random() * 0xffffff), 0.5);
|
||||
sprite.graphics.drawCircle(getAnchorOffset(TOPLEFT).x, getAnchorOffset(TOPLEFT).y, 5);
|
||||
sprite.graphics.drawCircle(getAnchorOffset(TOP).x, getAnchorOffset(TOP).y, 5);
|
||||
sprite.graphics.drawCircle(getAnchorOffset(TOPRIGHT).x, getAnchorOffset(TOPRIGHT).y, 5);
|
||||
sprite.graphics.drawCircle(getAnchorOffset(RIGHT).x, getAnchorOffset(RIGHT).y, 5);
|
||||
sprite.graphics.drawCircle(getAnchorOffset(BOTTOMRIGHT).x, getAnchorOffset(BOTTOMRIGHT).y, 5);
|
||||
sprite.graphics.drawCircle(getAnchorOffset(BOTTOM).x, getAnchorOffset(BOTTOM).y, 5);
|
||||
sprite.graphics.drawCircle(getAnchorOffset(BOTTOMLEFT).x, getAnchorOffset(BOTTOMLEFT).y, 5);
|
||||
sprite.graphics.drawCircle(getAnchorOffset(LEFT).x, getAnchorOffset(LEFT).y, 5);
|
||||
}
|
||||
}
|
||||
|
||||
public var endOffset:Float = 0;
|
||||
public var startOffset:Float = 0;
|
||||
|
||||
public function addChild(child:UIPane) {
|
||||
if (child.parent != null)
|
||||
throw "Attempting to attach child UIPane that already has a parent";
|
||||
children.push(child);
|
||||
sprite.addChild(child.sprite);
|
||||
child.parent = this;
|
||||
if (child.autoArrange && autoArrangeChildren) {
|
||||
arrangeChild(child);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
public function redraw():Void {
|
||||
drawDebugPane();
|
||||
drawMask();
|
||||
}
|
||||
|
||||
public function onResize() {
|
||||
redraw();
|
||||
if (autoArrangeChildren) {
|
||||
arrangeChildren();
|
||||
}
|
||||
}
|
||||
|
||||
private function arrangeChild(child:UIPane) {
|
||||
// We do not arrange the
|
||||
if (!child.autoArrange) {
|
||||
return;
|
||||
}
|
||||
if (endOffset == 0) {
|
||||
switch (layout) {
|
||||
case HORIZONTAL:
|
||||
endOffset = width;
|
||||
case VERTICAL:
|
||||
endOffset = height;
|
||||
}
|
||||
}
|
||||
var offsetDiff:Float = endOffset - startOffset;
|
||||
// Set child dimensions
|
||||
switch (child.dimensions.expandBehavior) {
|
||||
case STRETCH:
|
||||
switch (layout) {
|
||||
case HORIZONTAL:
|
||||
child.width = offsetDiff;
|
||||
child.height = height;
|
||||
case VERTICAL:
|
||||
child.width = width;
|
||||
child.height = offsetDiff;
|
||||
}
|
||||
case FACTOR(f):
|
||||
switch (layout) {
|
||||
case HORIZONTAL:
|
||||
child.width = width * f;
|
||||
child.height = height;
|
||||
case VERTICAL:
|
||||
child.width = width;
|
||||
child.height = height * f;
|
||||
}
|
||||
default:
|
||||
switch (layout) {
|
||||
case HORIZONTAL:
|
||||
// do some shit
|
||||
child.height = height;
|
||||
case VERTICAL:
|
||||
// do some other shit
|
||||
child.width = width;
|
||||
default:
|
||||
child.height = child.height;
|
||||
}
|
||||
}
|
||||
// Set child position
|
||||
switch (child.align) {
|
||||
case START:
|
||||
switch (layout) {
|
||||
case HORIZONTAL:
|
||||
child.x = startOffset;
|
||||
startOffset += child.width;
|
||||
case VERTICAL:
|
||||
child.y = startOffset;
|
||||
startOffset += child.height;
|
||||
}
|
||||
case END:
|
||||
switch (layout) {
|
||||
case HORIZONTAL:
|
||||
child.x = endOffset -= child.width;
|
||||
case VERTICAL:
|
||||
child.y = endOffset -= child.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function arrangeChildren() {
|
||||
// reset offsets
|
||||
startOffset = 0;
|
||||
endOffset = 0;
|
||||
// arrance each child
|
||||
for (child in children) {
|
||||
arrangeChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
10
hGameTest/src/game/Bind.hx
Normal file
10
hGameTest/src/game/Bind.hx
Normal file
@ -0,0 +1,10 @@
|
||||
package game;
|
||||
|
||||
class Bind{
|
||||
public var key:Int;
|
||||
public var action:String;
|
||||
public function new(key:Int, action:String){
|
||||
this.key = key;
|
||||
this.action = action;
|
||||
}
|
||||
}
|
||||
@ -1,22 +1,14 @@
|
||||
package game;
|
||||
|
||||
import engine.typedefs.CVar;
|
||||
import game.entities.Player;
|
||||
import openfl.display.Stage;
|
||||
import openfl.display.Sprite;
|
||||
import openfl.events.Event;
|
||||
|
||||
import engine.HProfiler;
|
||||
import engine.ConVar;
|
||||
import engine.ui.UIPane;
|
||||
import engine.typedefs.console.CVar;
|
||||
import engine.tools.ui.UITool;
|
||||
|
||||
import game.ui.console.ConsolePane;
|
||||
import game.entities.Player;
|
||||
import game.ui.console.Console;
|
||||
|
||||
|
||||
import assets.HTex;
|
||||
import assets.Scanner;
|
||||
import assets.HTex;
|
||||
import game.ui.ConVar;
|
||||
import game.ui.console.Console;
|
||||
|
||||
class Game
|
||||
{
|
||||
@ -25,12 +17,12 @@ class Game
|
||||
public var uiLayer:Sprite;
|
||||
public function new(_stage:Stage){
|
||||
stage = _stage;
|
||||
#if PRECACHE_ASSETS & sys
|
||||
/*
|
||||
Scanner.scanTextureDir();
|
||||
for(tex in Scanner.textures){
|
||||
HTex.createTextureObjectFromJSON(tex);
|
||||
}
|
||||
#end
|
||||
*/
|
||||
gameLayer = new Sprite();
|
||||
uiLayer = new Sprite();
|
||||
trace("wattafak");
|
||||
@ -40,23 +32,32 @@ class Game
|
||||
public function loadData():Void
|
||||
{
|
||||
new game.ui.text.TextFormats();
|
||||
|
||||
}
|
||||
public function onInit():Void
|
||||
{
|
||||
|
||||
gameLayer = new Sprite();
|
||||
uiLayer = new Sprite();
|
||||
stage.addChild(gameLayer);
|
||||
stage.addChild(uiLayer);
|
||||
var console2:ConsolePane = new ConsolePane();
|
||||
uiLayer.addChild(console2);
|
||||
//player.graphics.lineStyle(2,0xFF0000);
|
||||
//player.graphics.drawRect(0,0,16,16);
|
||||
// var bitmapData:BitmapData = Assets.getBitmapData("textures/sprites/character.png");
|
||||
//player = new Player();
|
||||
//gameLayer.addChild(player.sprite);
|
||||
uiLayer.addChild(console);
|
||||
HProfiler.startProfiling("frametime");
|
||||
//UITool.spawnUIEditorWindow();
|
||||
var cvar_cl_kankerlow:CVar = ConVar.registerCVar("cl_kankerlow", CInt, 10, FCVAR_ARCHIVE, "is de speler een kankerlow?",null,false,false,0,0,false);
|
||||
//var sheet:SpriteSheet = new SpriteSheet(sheetData);
|
||||
//var playerBitmap:Bitmap = new Bitmap(Tileset.tilesetMap["testsheet"].tileMap["testTile5"]);
|
||||
//var someotherbitmap:Bitmap = new Bitmap(Tileset.tilesetMap["testsheet"].tileMap["testTile3"]);
|
||||
//playerBitmap.scaleX = playerBitmap.scaleY = 10;
|
||||
//player.addChild(playerBitmap);
|
||||
//TextureData.parseConfig();
|
||||
//Sys.
|
||||
}
|
||||
public function onEnterFrame(e:Event):Void
|
||||
{
|
||||
HProfiler.stopProfiling("frametime");
|
||||
HProfiler.startProfiling("frametime");
|
||||
Input.onEnterFrame();
|
||||
}
|
||||
}
|
||||
@ -1,36 +1,25 @@
|
||||
package game;
|
||||
|
||||
import openfl.text.TextField;
|
||||
import engine.enums.CVarFlag;
|
||||
import engine.typedefs.CCmd;
|
||||
import game.ui.console.Console;
|
||||
import game.video.Mode;
|
||||
import openfl.events.KeyboardEvent;
|
||||
import openfl.ui.Keyboard in Kb;
|
||||
import openfl.Lib;
|
||||
import engine.enums.CVarType;
|
||||
import engine.typedefs.CVar;
|
||||
import game.ui.ConVar;
|
||||
|
||||
import engine.enums.console.CVarFlag;
|
||||
import engine.enums.console.CVarType;
|
||||
import engine.typedefs.console.CCmd;
|
||||
import engine.typedefs.console.CVar;
|
||||
import engine.ConVar;
|
||||
|
||||
import game.ui.console.Console;
|
||||
import game.video.Mode;
|
||||
|
||||
class Input{
|
||||
public static var keys:Map<Int,Bool> = [];
|
||||
public static var keysLast:Map<Int,Bool> = [];
|
||||
public static var bindMap:Map<String, String> = ["~" => "toggleconsole", "\\" => "toggleconsole", "1" => "echo kak", "2" => "+attack"];
|
||||
@:convar({
|
||||
name: "cl_debuginput",
|
||||
type: CVarType.CInt,
|
||||
value: 0,
|
||||
flags: CVarFlag.None,
|
||||
help: "print debug messages related to input to console",
|
||||
callback: function() {
|
||||
trace("Debug input changed");
|
||||
}
|
||||
})
|
||||
public static var cv_debugKeys:CVar;
|
||||
|
||||
// public static var cv_debugKeys = ConVar.registerCVar("cl_debuginput", CInt, 0, null, "print debug messages related to input to console", null, false, true, 0, 0, false);
|
||||
public static var bindMap:Map<String, String> = [];
|
||||
//public static var bind:CVar = Convar.registerCVar("bind",CVarType.cCmd, null, ()->{
|
||||
//
|
||||
//});
|
||||
public static var cv_debugKeys = ConVar.registerCVar("cl_debuginput", CInt, 0, null, "print debug messages related to input to console", null, false, true, 0, 0, false);
|
||||
public static var keyNameMap:Map<Int, String> = [
|
||||
Kb.HOME => "HOME", Kb.END => "END", Kb.INSERT => "INSERT", Kb.DELETE => "DELETE", Kb.PAGE_UP => "PGUP", Kb.PAGE_DOWN => "PGDN",
|
||||
Kb.ESCAPE => "ESC", Kb.F1 => "F1", Kb.F2 => "F2", Kb.F3 => "F3", Kb.F4 => "F4", Kb.F5 => "F5",
|
||||
@ -57,16 +46,9 @@ class Input{
|
||||
Console.devMsg(Std.string(keycode));
|
||||
});
|
||||
public static function onKeyIsDown(e:KeyboardEvent){
|
||||
if(!Std.isOfType(Lib.current.stage.focus,TextField)){
|
||||
// Check if not null so we don't end up cooming all over unallocated memory;
|
||||
if(bindMap[keyNameMap[e.keyCode]] != null)
|
||||
Console.consoleInstance.parseCmd(bindMap[keyNameMap[e.keyCode]]);
|
||||
}
|
||||
if(!keys[e.keyCode]){
|
||||
if(cv_debugKeys.value == 1)
|
||||
if(cv_debugKeys.value > 0)
|
||||
Console.devMsg("triggered key: "+keyNameMap[e.keyCode]);
|
||||
else if(cv_debugKeys.value > 1 )
|
||||
Console.devMsg(""+e.keyCode);
|
||||
}
|
||||
keys[e.keyCode] = true;
|
||||
var key:String = keyNameMap[e.keyCode];
|
||||
@ -74,46 +56,28 @@ class Input{
|
||||
|
||||
}
|
||||
public static function onKeyIsUp(e:KeyboardEvent){
|
||||
if(!Std.isOfType(Lib.current.stage.focus,TextField)){
|
||||
// Make sure we're not checking a null value (this can cause a crash)
|
||||
if(bindMap[keyNameMap[e.keyCode]] != null){
|
||||
if(bindMap[keyNameMap[e.keyCode]].indexOf("+") == 0){
|
||||
Console.consoleInstance.parseCmd(StringTools.replace(bindMap[keyNameMap[e.keyCode]],"+","-"));
|
||||
}
|
||||
}
|
||||
}
|
||||
keys[e.keyCode] = false;
|
||||
}
|
||||
// public static function resolveKeyName(key:Int):String
|
||||
// {
|
||||
// return keyNameMap[key];
|
||||
// }
|
||||
public static function bindKey(input:String, action:String):Void
|
||||
public static function resolveKeyName(key:Int):String
|
||||
{
|
||||
bindMap[input] = action;
|
||||
return keyNameMap[key];
|
||||
}
|
||||
@:concmd("bind")
|
||||
private static function bind(args:Array<String>):Void
|
||||
{
|
||||
args[0] = args[0].toUpperCase();
|
||||
if(args.length == 2){
|
||||
if(keyCodeMap[args[0]]!= null) {
|
||||
if(args[1].indexOf('"') == 0 && args[1].lastIndexOf('"') == args[1].length-1){
|
||||
bindKey(args[0], args[1].substring(1,args[1].length-1));
|
||||
}
|
||||
else{
|
||||
bindKey(args[0], args[1]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if(args.length == 1){
|
||||
Console.devMsg(bindMap[args[0]]);
|
||||
}
|
||||
else{
|
||||
Console.devMsg("usage: bind <key> <command>");
|
||||
}
|
||||
};
|
||||
// public static function bind(input:Dynamic, action:String):Void
|
||||
// {
|
||||
// var key:Int;
|
||||
// if(Std.is(input,Int)){
|
||||
// key = input;
|
||||
// }
|
||||
// else if(Std.is(input, String)){
|
||||
// var value:String = input;
|
||||
// key = keyCodeMap[value.toUpperCase()];
|
||||
// }
|
||||
|
||||
// }
|
||||
private static var bind:CCmd = ConVar.registerCCmd("bind", (cArgs:Array<String>)->{
|
||||
|
||||
ConVar.runCmd("echo",cArgs);
|
||||
});
|
||||
public static function onEnterFrame()
|
||||
{
|
||||
if( keys[Kb.BACKQUOTE]){
|
||||
@ -135,12 +99,6 @@ class Input{
|
||||
if (keys[Kb.DOWN] && !keysLast[Kb.DOWN]){
|
||||
Console.histNext();
|
||||
}
|
||||
|
||||
}
|
||||
if(Console.consoleInstance.visible){
|
||||
if (keys[Kb.ESCAPE]){
|
||||
Console.toggle();
|
||||
}
|
||||
}
|
||||
keysLast = keys.copy();
|
||||
}
|
||||
|
||||
9
hGameTest/src/game/ui/CVar.hx
Normal file
9
hGameTest/src/game/ui/CVar.hx
Normal file
@ -0,0 +1,9 @@
|
||||
package game.ui;
|
||||
|
||||
|
||||
typedef CVar = {
|
||||
var name:String;
|
||||
var type:CVarType;
|
||||
var value:Dynamic;
|
||||
@:optional var callback:Void -> Void;
|
||||
};
|
||||
9
hGameTest/src/game/ui/CVarType.hx
Normal file
9
hGameTest/src/game/ui/CVarType.hx
Normal file
@ -0,0 +1,9 @@
|
||||
package game.ui;
|
||||
|
||||
enum CVarType {
|
||||
CInt;
|
||||
CFloat;
|
||||
CString;
|
||||
CBool;
|
||||
CCmd;
|
||||
}
|
||||
@ -1,37 +1,33 @@
|
||||
package engine;
|
||||
package game.ui;
|
||||
|
||||
import engine.typedefs.console.CVar;
|
||||
import engine.typedefs.console.CCmd;
|
||||
import engine.enums.console.CVarType;
|
||||
import engine.enums.console.CVarFlag;
|
||||
import haxe.Constraints.Function;
|
||||
import engine.typedefs.CVar;
|
||||
import engine.typedefs.CCmd;
|
||||
import engine.enums.CVarType;
|
||||
import engine.enums.CVarFlag;
|
||||
import game.ui.console.Console;
|
||||
|
||||
import engine.ConVars_Engine;
|
||||
|
||||
class ConVar{
|
||||
// Hush the compiler about unused import
|
||||
// static var ConVars_Engine:Class<ConVars_Engine> = ConVars_Engine;
|
||||
|
||||
static var CVarMap:Map<String, CVar> = [];
|
||||
static var CCmdMap:Map<String, CCmd> = [];
|
||||
|
||||
/** Registers a new CVar
|
||||
@param _name The name of the CVar. This is the way it is called in the console or referred to in code.
|
||||
@param _type CVar type as defined in the CVarType enum.
|
||||
@param _value The default value for this CVar. This needs to be in line with its CVarType.
|
||||
@param _flags CVar flags as defined in the CVarFlags enum.
|
||||
@param _helpString Help string that gets printed out in console when no value is specified.
|
||||
@param _callback The function that gets called when the CVar is set. Calls empty Void when left undefined.
|
||||
@param _callOnCreate Whether the callback function gets called or not after registering.
|
||||
@param _bMin Specifies if the CVar has a minimum numeric value.
|
||||
@param _fMin Specifies the minimum numeric value.
|
||||
@param _fMax Specifies the maximum numeric value.
|
||||
@param _bMax Specifies if the CVar has a maximum numeric value.
|
||||
@return The registered CVar object.
|
||||
/**
|
||||
* Registers a new CVar
|
||||
* @param _name The name of the CVar. This is the way it is called in the console or referred to in code.
|
||||
* @param _type CVar type as defined in the CVarType enum.
|
||||
* @param _value The default value for this CVar. This needs to be in line with its CVarType.
|
||||
* @param _flags CVar flags as defined in the CVarFlags enum.
|
||||
* @param _helpString Help string that gets printed out in console when no value is specified.
|
||||
* @param _callback The function that gets called when the CVar is set. Calls empty Void when left undefined.
|
||||
* @param _callOnCreate Whether the callback function gets called or not after registering.
|
||||
* @param _bMin Specifies if the CVar has a minimum numeric value.
|
||||
* @param _fMin Specifies the minimum numeric value.
|
||||
* @param _fMax Specifies the maximum numeric value.
|
||||
* @param _bMax Specifies if the CVar has a maximum numeric value.
|
||||
*/
|
||||
public static inline function registerCVar(_name:String, _type:CVarType, _value:Dynamic, ?_flags:CVarFlag, ?_helpString:String = "", ?_callback:Void->Void, ?_callOnCreate:Bool=false, _bMin:Bool=false, _fMin:Float=0, _fMax:Float=0, _bMax:Bool = false):CVar
|
||||
{
|
||||
if(CVarMap[_name]!=null || CCmdMap[_name]!= null){
|
||||
if(CVarMap[_name]!=null || CCmdMap[_name]!=null){
|
||||
Console.devMsg("Tried setting already defined convar: " + _name + ", returning null instead");
|
||||
return null;
|
||||
}
|
||||
@ -68,10 +64,6 @@ class ConVar{
|
||||
}
|
||||
public static inline function setCVar(_name:String, _value:Dynamic):Void
|
||||
{
|
||||
if(CVarMap == null){
|
||||
Console.devMsg("CVarMap is null");
|
||||
return;
|
||||
}
|
||||
var cv = getCVar(_name);
|
||||
if(cv != null){
|
||||
switch(cv.type){
|
||||
@ -151,13 +143,20 @@ class ConVar{
|
||||
});
|
||||
return keys;
|
||||
}
|
||||
|
||||
public static var cmdList:CCmd = ConVar.registerCCmd("list", (cArgs:Array<String>)->{
|
||||
var keys:Array<String> = getCVarNames();
|
||||
for(key in keys){
|
||||
if(CVarMap[key] != null){
|
||||
Console.devMsg(key+" "+CVarMap[key].value);
|
||||
}
|
||||
else{
|
||||
Console.devMsg(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
public static inline function getCVar(_name:String):CVar
|
||||
{
|
||||
return CVarMap[_name];
|
||||
}
|
||||
public static inline function getCVarMap():Map<String, CVar>
|
||||
{
|
||||
return CVarMap;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
package game.ui;
|
||||
|
||||
import openfl.geom.Point;
|
||||
import openfl.display.Sprite;
|
||||
|
||||
|
||||
class UIContianer extends Sprite{
|
||||
public static var UIDrawList:Array<UIElement>=[];
|
||||
public var anchor:Point;
|
||||
public function new(panchor,scale){
|
||||
super();
|
||||
}
|
||||
public function onResize(){
|
||||
|
||||
}
|
||||
public function hide(){
|
||||
|
||||
}
|
||||
public function show(){
|
||||
|
||||
}
|
||||
public function getParent()
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
|
||||
}
|
||||
258
hGameTest/src/game/ui/UIPane.hx
Normal file
258
hGameTest/src/game/ui/UIPane.hx
Normal file
@ -0,0 +1,258 @@
|
||||
package game.ui;
|
||||
|
||||
import openfl.display.Sprite;
|
||||
import openfl.display.BitmapData;
|
||||
import openfl.display.DisplayObject;
|
||||
import openfl.text.TextField;
|
||||
|
||||
typedef P2d = {
|
||||
x:Float,
|
||||
y:Float
|
||||
}
|
||||
enum PaneLayout{
|
||||
HORIZONTAL;
|
||||
VERTICAL;
|
||||
}
|
||||
enum PaneAnchor{
|
||||
LEFT;
|
||||
TOPLEFT;
|
||||
TOP;
|
||||
TOPRIGHT;
|
||||
RIGHT;
|
||||
BOTTOMLEFT;
|
||||
BOTTOM;
|
||||
BOTTOMRIGHT;
|
||||
}
|
||||
enum ExpandBehavior{
|
||||
FACTOR;
|
||||
ABSOLUTE;
|
||||
STRETCH;
|
||||
FILL;
|
||||
FIT;
|
||||
NONE;
|
||||
}
|
||||
enum PaneAlign{
|
||||
START;
|
||||
END;
|
||||
}
|
||||
|
||||
typedef PaneDimensions = {
|
||||
var width:Float;
|
||||
var height:Float;
|
||||
@:optional var expandBehavior:ExpandBehavior;
|
||||
@:optional var minWidth:Float;
|
||||
@:optional var minHeight:Float;
|
||||
@:optional var maxWidth:Float;
|
||||
@:optional var maxHeight:Float;
|
||||
}
|
||||
|
||||
typedef PaneProps = {
|
||||
dimensions:PaneDimensions,
|
||||
layout:PaneLayout
|
||||
}
|
||||
|
||||
class UIPane{
|
||||
public var name:String;
|
||||
public var sprite:Sprite;
|
||||
public var x(get, set):Float;
|
||||
public var y(get, set):Float;
|
||||
function get_x(){ return sprite.x; }
|
||||
function set_x(x){ return sprite.x = x; }
|
||||
function get_y(){ return sprite.y; }
|
||||
function set_y(y){ return sprite.y = y; }
|
||||
public var dimensions:PaneDimensions;
|
||||
public var width(get,set):Float;
|
||||
function get_width(){
|
||||
return dimensions.width;
|
||||
}
|
||||
function set_width(width){
|
||||
dimensions.width = width;
|
||||
onResize();
|
||||
return width;
|
||||
}
|
||||
public var height(get,set):Float;
|
||||
function get_height(){
|
||||
return dimensions.height;
|
||||
}
|
||||
function set_height(height){
|
||||
dimensions.height = height;
|
||||
onResize();
|
||||
return height;
|
||||
}
|
||||
public var layout:PaneLayout;
|
||||
public var autoArrange:Bool = true;
|
||||
public var autoArrangeChildren = true;
|
||||
public var expand:Bool;
|
||||
public var children:Array<UIPane> = [];
|
||||
public var parent:UIPane = null;
|
||||
public var align:PaneAlign = START;
|
||||
public var maskSprite:Sprite;
|
||||
public function new(_name:String, _dimensions:PaneDimensions)
|
||||
{
|
||||
// Set name
|
||||
name = _name;
|
||||
// Set dimensions
|
||||
if(_dimensions != null){
|
||||
dimensions = _dimensions;
|
||||
}
|
||||
else{
|
||||
dimensions = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
minWidth: 0,
|
||||
minHeight: 0,
|
||||
maxWidth: 0,
|
||||
maxHeight: 0
|
||||
};
|
||||
}
|
||||
|
||||
// Initialize Sprite
|
||||
initSprite();
|
||||
// Draw debug pane for visualizing
|
||||
//drawDebugPane();
|
||||
}
|
||||
public function getAnchorOffset(anchor:PaneAnchor):P2d
|
||||
{
|
||||
switch(anchor){
|
||||
case LEFT:
|
||||
return {x:0,y:(height/2)};
|
||||
case TOPLEFT:
|
||||
return {x:0, y:0};
|
||||
case TOP:
|
||||
return {x:width/2,y:0};
|
||||
case TOPRIGHT:
|
||||
return {x:width, y:0};
|
||||
case RIGHT:
|
||||
return {x:width, y:height/2}
|
||||
case BOTTOMRIGHT:
|
||||
return {x:width, y:height};
|
||||
case BOTTOM:
|
||||
return {x:width/2, y:height};
|
||||
case BOTTOMLEFT:
|
||||
return {x:0, y:height};
|
||||
}
|
||||
}
|
||||
public function initSprite():Void
|
||||
{
|
||||
|
||||
// Construct Sprite object
|
||||
sprite = new Sprite();
|
||||
|
||||
// Draw mask
|
||||
//maskSprite = new Sprite();
|
||||
//drawMask();
|
||||
//sprite.addChild(maskSprite);
|
||||
//sprite.mask = maskSprite;
|
||||
}
|
||||
|
||||
public function drawMask():Void
|
||||
{
|
||||
maskSprite.graphics.clear();
|
||||
maskSprite.graphics.beginFill(0xffffff);
|
||||
maskSprite.graphics.drawRect(0,0,width,height);
|
||||
}
|
||||
|
||||
|
||||
public function drawDebugPane()
|
||||
{
|
||||
// Clear graphics
|
||||
sprite.graphics.clear();
|
||||
// Create objects we need to draw.
|
||||
var label:TextField = new TextField();
|
||||
var bmp = new BitmapData(Std.int(width),Std.int(height),true,0x00000000);
|
||||
// Set textfield text to the panel name
|
||||
label.text = name;
|
||||
// Draw textfield to bitmap
|
||||
bmp.draw(label);
|
||||
// Draw graphics
|
||||
sprite.graphics.beginFill(Std.int(Math.random()*0xffffff),0.5);
|
||||
sprite.graphics.drawRect(0,0,dimensions.width,dimensions.height);
|
||||
sprite.graphics.beginBitmapFill(bmp,null);
|
||||
sprite.graphics.drawRect(0,0,dimensions.width,dimensions.height);
|
||||
|
||||
if(true){
|
||||
sprite.graphics.beginFill(Std.int(Math.random()*0xffffff),0.5);
|
||||
sprite.graphics.drawCircle(getAnchorOffset(TOPLEFT).x,getAnchorOffset(TOPLEFT).y,5);
|
||||
sprite.graphics.drawCircle(getAnchorOffset(TOP).x,getAnchorOffset(TOP).y,5);
|
||||
sprite.graphics.drawCircle(getAnchorOffset(TOPRIGHT).x,getAnchorOffset(TOPRIGHT).y,5);
|
||||
sprite.graphics.drawCircle(getAnchorOffset(RIGHT).x,getAnchorOffset(RIGHT).y,5);
|
||||
sprite.graphics.drawCircle(getAnchorOffset(BOTTOMRIGHT).x,getAnchorOffset(BOTTOMRIGHT).y,5);
|
||||
sprite.graphics.drawCircle(getAnchorOffset(BOTTOM).x,getAnchorOffset(BOTTOM).y,5);
|
||||
sprite.graphics.drawCircle(getAnchorOffset(BOTTOMLEFT).x,getAnchorOffset(BOTTOMLEFT).y,5);
|
||||
sprite.graphics.drawCircle(getAnchorOffset(LEFT).x,getAnchorOffset(LEFT).y,5);
|
||||
}
|
||||
|
||||
}
|
||||
public var endOffset:Float = 0;
|
||||
public var startOffset:Float = 0;
|
||||
public function addChild(child:UIPane)
|
||||
{
|
||||
children.push(child);
|
||||
sprite.addChild(child.sprite);
|
||||
child.parent = this;
|
||||
if(child.autoArrange && autoArrangeChildren){
|
||||
arrangeChild(child);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
public function redraw():Void
|
||||
{
|
||||
//drawDebugPane();
|
||||
//drawMask();
|
||||
}
|
||||
public function onResize(){
|
||||
redraw();
|
||||
if(autoArrangeChildren){
|
||||
arrangeChildren();
|
||||
}
|
||||
}
|
||||
private function arrangeChild(child:UIPane){
|
||||
if(child.autoArrange){
|
||||
switch(layout){
|
||||
case HORIZONTAL:
|
||||
if(endOffset == 0) endOffset = width;
|
||||
switch(child.dimensions.expandBehavior){
|
||||
case STRETCH:
|
||||
child.width = endOffset-startOffset;
|
||||
child.height = height;
|
||||
default:
|
||||
child.height = height;
|
||||
//
|
||||
}
|
||||
switch(child.align){
|
||||
case START:
|
||||
child.x = startOffset;
|
||||
startOffset += child.width;
|
||||
case END:
|
||||
child.x = endOffset-= child.width;
|
||||
}
|
||||
case VERTICAL:
|
||||
if(endOffset == 0) endOffset = height;
|
||||
switch(child.dimensions.expandBehavior){
|
||||
case STRETCH:
|
||||
child.height = endOffset-startOffset;
|
||||
child.width = width;
|
||||
default:
|
||||
child.width = width;
|
||||
//
|
||||
}
|
||||
switch(child.align){
|
||||
case START:
|
||||
child.y = startOffset;
|
||||
startOffset += child.height;
|
||||
case END:
|
||||
child.y = endOffset-=child.height;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public function arrangeChildren(){
|
||||
startOffset = 0;
|
||||
endOffset = 0;
|
||||
for(child in children){
|
||||
arrangeChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,9 +1,7 @@
|
||||
package game.ui.console;
|
||||
|
||||
import game.ui.text.TextFormats;
|
||||
import assets.Fonts;
|
||||
import openfl.Lib;
|
||||
import engine.ui.UIPane.PaneLayout;
|
||||
import openfl.events.Event;
|
||||
import openfl.text.TextFieldAutoSize;
|
||||
import openfl.text.TextField;
|
||||
@ -11,391 +9,373 @@ import openfl.display.Sprite;
|
||||
import openfl.Assets;
|
||||
import openfl.text.TextFormat;
|
||||
import openfl.text.TextFieldType;
|
||||
import engine.ConVar;
|
||||
import engine.enums.console.CVarFlag;
|
||||
import engine.typedefs.console.CVar;
|
||||
import engine.typedefs.console.CCmd;
|
||||
// Engine Imports
|
||||
import engine.enums.CVarFlag;
|
||||
import engine.typedefs.CVar;
|
||||
import engine.typedefs.CCmd;
|
||||
// Game imports
|
||||
import game.ui.ConVar;
|
||||
import game.ui.text.TextFormats;
|
||||
import game.ui.console.elements.ConsoleInput;
|
||||
import engine.ui.UIPane;
|
||||
import game.ui.UIPane;
|
||||
|
||||
class Console extends Sprite {
|
||||
public var textFormat:TextFormat;
|
||||
public var cOut:TextField;
|
||||
public var cIn:TextField;
|
||||
public var cInput:ConsoleInput;
|
||||
public var cAutoComp:TextField;
|
||||
|
||||
public static var consoleInstance:Console;
|
||||
|
||||
@:convar({
|
||||
name: "mat_consolebg",
|
||||
type: "CString",
|
||||
value: "0x222222",
|
||||
help: "console background color",
|
||||
callback: () -> {
|
||||
if (Console.consoleInstance != null){
|
||||
Console.consoleInstance.cOut.backgroundColor = Std.parseInt(Console.consoleInstance.cvar_mat_consolebg.value);
|
||||
Console.consoleInstance.cIn.backgroundColor = Std.parseInt(Console.consoleInstance.cvar_mat_consolebg.value);
|
||||
|
||||
class Console extends Sprite{
|
||||
public var textFormat:TextFormat;
|
||||
public var cOut:TextField;
|
||||
public var cIn:TextField;
|
||||
public var cInput:ConsoleInput;
|
||||
public var cAutoComp:TextField;
|
||||
public static var consoleInstance:Console;
|
||||
public var cvar_mat_consolebg:CVar;
|
||||
public function new(){
|
||||
super();
|
||||
|
||||
consoleInstance = this;
|
||||
|
||||
var consolePane:UIPane = new UIPane("console pane",{width: 800, height: 600});
|
||||
var topBar:UIPane = new UIPane("top bar", {width: 800, height: 32});
|
||||
var bottomBar:UIPane = new UIPane("input bar", {width: 800, height: 32});
|
||||
var outputPane:UIPane = new UIPane("output pane", {width: 800, height: 1});
|
||||
outputPane.dimensions.expandBehavior = STRETCH;
|
||||
bottomBar.align = END;
|
||||
outputPane.align = END;
|
||||
consolePane.layout = VERTICAL;
|
||||
topBar.align = START;
|
||||
consolePane.addChild(topBar);
|
||||
consolePane.addChild(bottomBar);
|
||||
consolePane.addChild(outputPane);
|
||||
addChild(consolePane.sprite);
|
||||
//graphics.beginFill(0x555555);
|
||||
graphics.drawRect(0,0,800,600);
|
||||
|
||||
cInput = new ConsoleInput();
|
||||
cIn = cInput.tf;
|
||||
|
||||
//cIn.setTextFormat(textFormat);
|
||||
cInput.y = 600-cInput.height-12;
|
||||
cInput.x = 12;
|
||||
|
||||
cvar_mat_consolebg = ConVar.registerCVar("mat_consolebg",CString,"0x222222",null,"console background color", ()->{
|
||||
cOut.backgroundColor = Std.parseInt(cvar_mat_consolebg.value);
|
||||
cIn.backgroundColor = Std.parseInt(cvar_mat_consolebg.value);
|
||||
},false,false,0,0,false);
|
||||
|
||||
cOut = new TextField();
|
||||
cOut.text = "hConsole Initialized\n";
|
||||
cOut.defaultTextFormat = TextFormats.getFormats().cOutputFmt;
|
||||
cOut.wordWrap = true;
|
||||
//cOut.autoSize = TextFieldAutoSize.LEFT;
|
||||
cOut.multiline = true;
|
||||
cOut.background = true;
|
||||
cOut.backgroundColor = Std.parseInt(cvar_mat_consolebg.value);
|
||||
cOut.width = outputPane.dimensions.width;
|
||||
cOut.height = outputPane.dimensions.height;
|
||||
cOut.y = 0;
|
||||
cOut.x = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
cAutoComp = new TextField();
|
||||
cAutoComp.text = "";
|
||||
cAutoComp.defaultTextFormat = TextFormats.getFormats().cInputFmt;
|
||||
cAutoComp.wordWrap = false;
|
||||
cAutoComp.multiline = true;
|
||||
cAutoComp.background = true;
|
||||
cAutoComp.backgroundColor = 0x11111100;
|
||||
cAutoComp.border = true;
|
||||
cAutoComp.borderColor = 0x55555500;
|
||||
cAutoComp.selectable = false;
|
||||
cAutoComp.width = 400;
|
||||
cAutoComp.height = 32*5;
|
||||
cAutoComp.visible = false;
|
||||
cAutoComp.x = 0;
|
||||
cAutoComp.y = 600;
|
||||
|
||||
|
||||
|
||||
cOut.addEventListener(Event.CHANGE, onOutputTextChange);
|
||||
cIn.addEventListener(Event.CHANGE, onInputTextChange);
|
||||
//cOut.addEventListener()
|
||||
outputPane.sprite.addChild(cOut);
|
||||
this.addChild(cInput);
|
||||
this.addChild(cAutoComp);
|
||||
//ConVar.registerCVar("echo", CVarType.CCmd, null, devMsg())
|
||||
ConVar.registerCCmd("echo", (args:Array<String>) -> { Console.devMsg(args.join(" ").split('"').join(""));});
|
||||
ConVar.registerCCmd("quit", (args:Array<String>) -> { Lib.application.window.close();});
|
||||
}
|
||||
public function parseCmd(cmd:String){
|
||||
history.push(cmd);
|
||||
cmd = cmd.split(";").join(" ; ");
|
||||
var subStrings = [];
|
||||
var startQuoteIndex:Int = cmd.indexOf('"');
|
||||
var endQuoteIndex:Int;
|
||||
if(startQuoteIndex != -1){
|
||||
while((startQuoteIndex = cmd.indexOf('"')) > -1){
|
||||
//push start of the cmd up until the quotes start
|
||||
subStrings.push(cmd.substring(0,startQuoteIndex));
|
||||
//find next quote
|
||||
endQuoteIndex = cmd.indexOf('"',startQuoteIndex+1)+1;
|
||||
if(endQuoteIndex == 0){
|
||||
cmd+='"';
|
||||
endQuoteIndex = cmd.length;
|
||||
|
||||
}
|
||||
//push quote content
|
||||
subStrings.push(cmd.substring(startQuoteIndex,endQuoteIndex));
|
||||
cmd = cmd.substr(endQuoteIndex);
|
||||
}
|
||||
}
|
||||
})
|
||||
public static var scvar_mat_consolebg:CVar;
|
||||
|
||||
public var cvar_mat_consolebg:CVar;
|
||||
// public function cCmdToggleConsole(args:Array<String>) {
|
||||
// toggle();
|
||||
// }
|
||||
@:concmd("toggleconsole")
|
||||
public function ccmd_toggleconsole(cArgs:Array<String>) {
|
||||
toggle();
|
||||
};
|
||||
subStrings.push(cmd);
|
||||
//Split args
|
||||
var newSubStrings = [];
|
||||
for(subString in subStrings){
|
||||
if(subString.indexOf('"') == -1){
|
||||
// split spaced args
|
||||
for(str in subString.split(" ")){
|
||||
//we want to discard empty strings
|
||||
if(str != "")
|
||||
newSubStrings.push(str);
|
||||
}
|
||||
|
||||
}
|
||||
else{
|
||||
newSubStrings.push(subString);
|
||||
}
|
||||
}
|
||||
var commands = [];
|
||||
//split off additional commands
|
||||
while(newSubStrings.length > 0){
|
||||
for(i in 0...newSubStrings.length){
|
||||
if(newSubStrings[i] == ";"){
|
||||
commands.push(newSubStrings.splice(0,i));
|
||||
newSubStrings.shift();
|
||||
break;
|
||||
}
|
||||
if(newSubStrings.length-1 == i){
|
||||
commands.push(newSubStrings);
|
||||
newSubStrings = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
trace(commands);
|
||||
execCommands(commands);
|
||||
}
|
||||
public function execCommands(commands:Array<Array<String>>){
|
||||
for(command in commands){
|
||||
var cName:String = command[0];
|
||||
var cValue:String = command[1];
|
||||
var cArgs:Array<String> = command.slice(1);
|
||||
if(ConVar.isCVar(cName)){
|
||||
var cv:CVar = ConVar.getCVar(command[0]);
|
||||
if(command.length == 1){
|
||||
devMsg(cv.name+" - "+cv.helpString);
|
||||
devMsg(cv.name+" = "+cv.value);
|
||||
}
|
||||
else{
|
||||
switch(cv.type){
|
||||
case CInt:
|
||||
ConVar.setCVar(cName,Std.parseInt(cValue));
|
||||
trace(cValue);
|
||||
break;
|
||||
case CFloat:
|
||||
ConVar.setCVar(cName,Std.parseFloat(cValue));
|
||||
break;
|
||||
case CBool:
|
||||
cValue = cValue.toLowerCase();
|
||||
if(cValue == "1" || cValue == "true"){
|
||||
ConVar.setCVar(cName,true);
|
||||
}
|
||||
else if(cValue == "0" || cValue == "false"){
|
||||
ConVar.setCVar(cName,false);
|
||||
}
|
||||
break;
|
||||
case CString:
|
||||
ConVar.setCVar(cName,cValue);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else if(ConVar.isCmd(cName)){
|
||||
ConVar.runCmd(cName,cArgs);
|
||||
}
|
||||
else{
|
||||
devMsg("unkown command: "+command[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
public function submitInput(){
|
||||
cOut.appendText(">"+cIn.text+"\n");
|
||||
parseCmd(cIn.text);
|
||||
cIn.text = "";
|
||||
cAutoComp.visible = false;
|
||||
cOut.scrollV = cOut.maxScrollV;
|
||||
histSelect = -1;
|
||||
}
|
||||
public function onOutputTextChange(e:Event){
|
||||
cOut.scrollV = cOut.maxScrollV;
|
||||
}
|
||||
public function onInputTextChange(e:Event){
|
||||
if(cIn.text != ""){
|
||||
cAutoComp.text = "";
|
||||
for(string in (autocompleteList = getCompList())){
|
||||
cAutoComp.text += string+"\n";
|
||||
trace(string);
|
||||
}
|
||||
cAutoComp.visible = true;
|
||||
}
|
||||
else{
|
||||
cAutoComp.visible = false;
|
||||
autocompleteList = history;
|
||||
}
|
||||
}
|
||||
public static function toggle():Void
|
||||
{
|
||||
consoleInstance.visible = !consoleInstance.visible;
|
||||
}
|
||||
public static function devMsg(msg:String):Void
|
||||
{
|
||||
consoleInstance.cOut.appendText(msg+"\n");
|
||||
consoleInstance.cOut.scrollV = consoleInstance.cOut.maxScrollV;
|
||||
}
|
||||
public static var history:Array<String> = [];
|
||||
public static var histSelect:Int = -1;
|
||||
public static var tmpInput:String = "";
|
||||
public static var autocompleteList:Array<String> = [];
|
||||
public static var compSelect:Int = -1;
|
||||
public static function getCompList():Array<String>
|
||||
{
|
||||
// Split words
|
||||
var inp:Array<String> = consoleInstance.cIn.text.split(" ");
|
||||
var inpStripped:Array<String> = [
|
||||
for(word in inp){
|
||||
if(word != "" && word != " "){
|
||||
word;
|
||||
}
|
||||
}
|
||||
];
|
||||
// Stop if input is empty
|
||||
if(inp.length == 0) return [];
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
|
||||
cvar_mat_consolebg = scvar_mat_consolebg;
|
||||
consoleInstance = this;
|
||||
|
||||
var consolePane = new UIPane("yeeto", {width: 800, height: 600, minWidth: 400, minHeight: 300, maxWidth: 1200, maxHeight: 800});
|
||||
|
||||
consolePane.layout = VERTICAL;
|
||||
consolePane.layout = VERTICAL;
|
||||
|
||||
// graphics.beginFill(0x555555);
|
||||
// graphics.drawRect(0,0,800,600);
|
||||
|
||||
cInput = new ConsoleInput();
|
||||
cIn = cInput.tf;
|
||||
|
||||
// cIn.setTextFormat(textFormat);
|
||||
cInput.y = 600 - cInput.height - 12;
|
||||
cInput.x = 12;
|
||||
|
||||
|
||||
cOut = new TextField();
|
||||
cAutoComp = new TextField();
|
||||
|
||||
drawFields(cOut, cAutoComp);
|
||||
|
||||
cOut.addEventListener(Event.CHANGE, onOutputTextChange);
|
||||
cIn.addEventListener(Event.CHANGE, onInputTextChange);
|
||||
this.addChild(cOut);
|
||||
this.addChild(cInput);
|
||||
this.addChild(cAutoComp);
|
||||
ConVar.registerCCmd("echo", (args:Array<String>) -> {
|
||||
Console.devMsg(args.join(" ").split('"').join(""));
|
||||
});
|
||||
ConVar.registerCCmd("quit", (args:Array<String>) -> {
|
||||
Lib.application.window.close();
|
||||
});
|
||||
}
|
||||
|
||||
public function drawFields(cOut:TextField, cAutoComp:TextField) {
|
||||
cOut.text = "hConsole Initialized\n";
|
||||
cOut.defaultTextFormat = TextFormats.getFormats().cOutputFmt;
|
||||
cOut.wordWrap = true;
|
||||
// cOut.autoSize = TextFieldAutoSize.LEFT;
|
||||
cOut.multiline = true;
|
||||
cOut.background = true;
|
||||
trace(cvar_mat_consolebg);
|
||||
cOut.backgroundColor = Std.parseInt(cvar_mat_consolebg.value);
|
||||
cOut.width = 800 - 24;
|
||||
cOut.height = 600 - cIn.height - 38;
|
||||
cOut.y = 12;
|
||||
cOut.x = 12;
|
||||
|
||||
cAutoComp.text = "";
|
||||
cAutoComp.defaultTextFormat = TextFormats.getFormats().cInputFmt;
|
||||
cAutoComp.wordWrap = false;
|
||||
cAutoComp.multiline = true;
|
||||
cAutoComp.background = true;
|
||||
cAutoComp.backgroundColor = 0x11111100;
|
||||
cAutoComp.border = true;
|
||||
cAutoComp.borderColor = 0x55555500;
|
||||
cAutoComp.selectable = false;
|
||||
cAutoComp.width = 400;
|
||||
cAutoComp.height = 32 * 5;
|
||||
cAutoComp.visible = false;
|
||||
cAutoComp.x = 0;
|
||||
cAutoComp.y = 600;
|
||||
}
|
||||
|
||||
public function parseCmd(cmd:String, bNoHist:Bool = false) {
|
||||
if (!bNoHist)
|
||||
history.push(cmd);
|
||||
|
||||
cmd = cmd.split(";").join(" ; ");
|
||||
var subStrings = [];
|
||||
var startQuoteIndex:Int = cmd.indexOf('"');
|
||||
var endQuoteIndex:Int;
|
||||
if (startQuoteIndex != -1) {
|
||||
while ((startQuoteIndex = cmd.indexOf('"')) > -1) {
|
||||
// push start of the cmd up until the quotes start
|
||||
subStrings.push(cmd.substring(0, startQuoteIndex));
|
||||
// find next quote
|
||||
endQuoteIndex = cmd.indexOf('"', startQuoteIndex + 1) + 1;
|
||||
if (endQuoteIndex == 0) {
|
||||
cmd += '"';
|
||||
endQuoteIndex = cmd.length;
|
||||
}
|
||||
// push quote content
|
||||
subStrings.push(cmd.substring(startQuoteIndex, endQuoteIndex));
|
||||
cmd = cmd.substr(endQuoteIndex);
|
||||
}
|
||||
}
|
||||
subStrings.push(cmd);
|
||||
// Split args
|
||||
var newSubStrings = [];
|
||||
for (subString in subStrings) {
|
||||
if (subString.indexOf('"') == -1) {
|
||||
// split spaced args
|
||||
for (str in subString.split(" ")) {
|
||||
// we want to discard empty strings
|
||||
if (str != "")
|
||||
newSubStrings.push(str);
|
||||
}
|
||||
} else {
|
||||
newSubStrings.push(subString);
|
||||
}
|
||||
}
|
||||
var commands = [];
|
||||
// split off additional commands
|
||||
while (newSubStrings.length > 0) {
|
||||
for (i in 0...newSubStrings.length) {
|
||||
if (newSubStrings[i] == ";") {
|
||||
commands.push(newSubStrings.splice(0, i));
|
||||
newSubStrings.shift();
|
||||
break;
|
||||
}
|
||||
if (newSubStrings.length - 1 == i) {
|
||||
commands.push(newSubStrings);
|
||||
newSubStrings = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
trace(commands);
|
||||
execCommands(commands);
|
||||
}
|
||||
|
||||
public function execCommands(commands:Array<Array<String>>) {
|
||||
for (command in commands) {
|
||||
// Store command name, value and args
|
||||
var cName:String = command[0];
|
||||
var cValue:String = command[1];
|
||||
var cArgs:Array<String> = command.slice(1);
|
||||
// Check if convar with that name actually exists
|
||||
if (ConVar.isCVar(cName)) {
|
||||
// Get reference to convar object
|
||||
var cv:CVar = ConVar.getCVar(command[0]);
|
||||
// Check number of params
|
||||
if (command.length == 1) {
|
||||
// No params after the cmd, print help string
|
||||
devMsg(cv.name + " - " + cv.helpString);
|
||||
devMsg(cv.name + " = " + cv.value);
|
||||
} else {
|
||||
// Check for convar type and set value accordingly
|
||||
switch (cv.type) {
|
||||
case CInt:
|
||||
ConVar.setCVar(cName, Std.parseInt(cValue));
|
||||
trace(cValue);
|
||||
break;
|
||||
case CFloat:
|
||||
ConVar.setCVar(cName, Std.parseFloat(cValue));
|
||||
break;
|
||||
case CBool:
|
||||
cValue = cValue.toLowerCase();
|
||||
if (cValue == "1" || cValue == "true") {
|
||||
ConVar.setCVar(cName, true);
|
||||
} else if (cValue == "0" || cValue == "false") {
|
||||
ConVar.setCVar(cName, false);
|
||||
}
|
||||
break;
|
||||
case CString:
|
||||
ConVar.setCVar(cName, cValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// convar is actually a command, run it.
|
||||
else if (ConVar.isCmd(cName)) {
|
||||
ConVar.runCmd(cName, cArgs);
|
||||
}
|
||||
// convar doesn't exist
|
||||
else {
|
||||
devMsg("unkown command: " + command[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function submitInput() {
|
||||
cOut.appendText(">" + cInput.getText() + "\n");
|
||||
parseCmd(cInput.getText());
|
||||
cInput.setText("");
|
||||
cAutoComp.visible = false;
|
||||
cOut.scrollV = cOut.maxScrollV;
|
||||
histSelect = -1;
|
||||
}
|
||||
|
||||
public function onOutputTextChange(e:Event) {
|
||||
cOut.scrollV = cOut.maxScrollV;
|
||||
}
|
||||
|
||||
public function onInputTextChange(e:Event) {
|
||||
if (cInput.getText() != "") {
|
||||
cAutoComp.text = "";
|
||||
for (string in (autocompleteList = getCompList())) {
|
||||
cAutoComp.text += string + "\n";
|
||||
trace(string);
|
||||
}
|
||||
cAutoComp.visible = true;
|
||||
} else {
|
||||
cAutoComp.visible = false;
|
||||
autocompleteList = history;
|
||||
}
|
||||
}
|
||||
|
||||
public static function toggle():Void {
|
||||
consoleInstance.visible = !consoleInstance.visible;
|
||||
if (!consoleInstance.visible) {
|
||||
if (Lib.current.stage.focus == consoleInstance.cIn) {
|
||||
Lib.current.stage.focus = null;
|
||||
}
|
||||
} else {
|
||||
Lib.current.stage.focus = consoleInstance.cIn;
|
||||
}
|
||||
}
|
||||
|
||||
public static function devMsg(msg:String):Void {
|
||||
consoleInstance.cOut.appendText(msg + "\n");
|
||||
consoleInstance.cOut.scrollV = consoleInstance.cOut.maxScrollV;
|
||||
}
|
||||
|
||||
public static var history:Array<String> = [];
|
||||
public static var histSelect:Int = -1;
|
||||
public static var tmpInput:String = "";
|
||||
public static var autocompleteList:Array<String> = [];
|
||||
public static var compSelect:Int = -1;
|
||||
|
||||
public static function getCompList():Array<String> {
|
||||
// Split words
|
||||
var inp:Array<String> = consoleInstance.cInput.getText().split(" ");
|
||||
var inpStripped:Array<String> = [
|
||||
for (word in inp) {
|
||||
if (word != "" && word != " ") {
|
||||
word;
|
||||
}
|
||||
}
|
||||
];
|
||||
// Stop if input is empty
|
||||
if (inp.length == 0)
|
||||
return [];
|
||||
|
||||
var cVars:Array<String> = ConVar.getCVarNames();
|
||||
var cVarFiltered:Array<String> = [];
|
||||
// Loop through convars
|
||||
for (cVar in cVars) {
|
||||
// if there's one word just check if the convar starts with the input string
|
||||
if (inp.length == 1) {
|
||||
// Check if the cvar starts with the input
|
||||
if (cVar.indexOf(inp[0]) == 0) {
|
||||
cVarFiltered.push(cVar);
|
||||
}
|
||||
}
|
||||
// User pressed space at least once but entered no other words. Check if first word occurs at any position
|
||||
else if (inpStripped.length == 1 && inp.length > 1) {
|
||||
if (cVar.indexOf(inp[0]) > -1) {
|
||||
cVarFiltered.push(cVar);
|
||||
}
|
||||
}
|
||||
// Multiple words, check for cvars that contain all of them
|
||||
else if (inpStripped.length > 1) {
|
||||
var bWordNotPresent:Bool = false;
|
||||
for (word in inpStripped) {
|
||||
if (cVar.indexOf(word) == -1) {
|
||||
bWordNotPresent = true;
|
||||
}
|
||||
}
|
||||
if (!bWordNotPresent)
|
||||
cVarFiltered.push(cVar);
|
||||
}
|
||||
}
|
||||
return cVarFiltered;
|
||||
}
|
||||
|
||||
public static function histPrev():Void {
|
||||
// Only complete if input field is empty or scrolling through hist
|
||||
if (consoleInstance.cInput.getText() == "" || histSelect != -1) {
|
||||
// Store current input in tmpInput
|
||||
if (histSelect == -1)
|
||||
tmpInput = consoleInstance.cInput.getText();
|
||||
// Only go through history if history is not empty
|
||||
if (history.length != 0) {
|
||||
// Check if currently selecting a history entry
|
||||
if (histSelect >= 0) {
|
||||
histSelect--;
|
||||
}
|
||||
// Not currently selecting a history entry
|
||||
else {
|
||||
// Put selector to the newest
|
||||
histSelect = history.length - 1;
|
||||
}
|
||||
if (histSelect != -1) {
|
||||
// Put correct history entry in the input field
|
||||
consoleInstance.cInput.setText(history[histSelect]);
|
||||
} else {
|
||||
// Restore tmp input
|
||||
consoleInstance.cInput.setText(tmpInput);
|
||||
}
|
||||
}
|
||||
// History is empty
|
||||
else {
|
||||
// Do nothing
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Now we need to descend through the autocomplete list
|
||||
else {
|
||||
if (autocompleteList.length == 0) {
|
||||
return;
|
||||
} else {
|
||||
// Check if currently selecting an autocomplete entry
|
||||
if (compSelect > -1) {}
|
||||
// Not currently selecting an autocomplete entry
|
||||
else {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function histNext():Void {
|
||||
// Only complete if input field is empty or scrolling through hist
|
||||
if (consoleInstance.cIn.text == "" || histSelect != -1) {
|
||||
// Store current input in tmpInput
|
||||
if (histSelect == -1)
|
||||
tmpInput = consoleInstance.cIn.text;
|
||||
// Only go through history if history is not empty
|
||||
if (history.length != 0) {
|
||||
// Check if currently selecting a history entry
|
||||
if (histSelect < history.length - 1) {
|
||||
histSelect++;
|
||||
}
|
||||
// Otherwise wrap around
|
||||
else {
|
||||
// Put selector to no selection
|
||||
histSelect = -1;
|
||||
}
|
||||
if (histSelect != -1) {
|
||||
// Put correct history entry in the input field
|
||||
consoleInstance.cIn.text = history[histSelect];
|
||||
} else {
|
||||
// Restore tmp input
|
||||
consoleInstance.cIn.text = tmpInput;
|
||||
}
|
||||
}
|
||||
// History is empty
|
||||
else {
|
||||
// Do nothing
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var cVars:Array<String> = ConVar.getCVarNames();
|
||||
var cVarFiltered:Array<String> = [];
|
||||
// Loop through convars
|
||||
for(cVar in cVars){
|
||||
//if there's one word just check if the convar starts with the input string
|
||||
if(inp.length == 1)
|
||||
{
|
||||
// Check if the cvar starts with the input
|
||||
if(cVar.indexOf(inp[0]) == 0){
|
||||
cVarFiltered.push(cVar);
|
||||
}
|
||||
}
|
||||
// User pressed space at least once but entered no other words. Check if first word occurs at any position
|
||||
else if(inpStripped.length == 1 && inp.length > 1){
|
||||
if(cVar.indexOf(inp[0]) > -1){
|
||||
cVarFiltered.push(cVar);
|
||||
}
|
||||
}
|
||||
// Multiple words, check for cvars that contain all of them
|
||||
else if(inpStripped.length > 1)
|
||||
{
|
||||
var bWordNotPresent:Bool = false;
|
||||
for(word in inpStripped){
|
||||
if(cVar.indexOf(word) == -1){
|
||||
bWordNotPresent = true;
|
||||
}
|
||||
}
|
||||
if(!bWordNotPresent)
|
||||
cVarFiltered.push(cVar);
|
||||
}
|
||||
}
|
||||
return cVarFiltered;
|
||||
}
|
||||
public static function histPrev():Void
|
||||
{
|
||||
// Only complete if input field is empty or scrolling through hist
|
||||
if(consoleInstance.cIn.text == "" || histSelect != -1){
|
||||
// Store current input in tmpInput
|
||||
if(histSelect == -1) tmpInput = consoleInstance.cIn.text;
|
||||
// Only go through history if history is not empty
|
||||
if(history.length != 0){
|
||||
// Check if currently selecting a history entry
|
||||
if(histSelect >= 0){
|
||||
histSelect--;
|
||||
}
|
||||
// Not currently selecting a history entry
|
||||
else{
|
||||
// Put selector to the newest
|
||||
histSelect = history.length -1;
|
||||
}
|
||||
if(histSelect != -1){
|
||||
// Put correct history entry in the input field
|
||||
consoleInstance.cIn.text = history[histSelect];
|
||||
}
|
||||
else{
|
||||
// Restore tmp input
|
||||
consoleInstance.cIn.text = tmpInput;
|
||||
}
|
||||
}
|
||||
// History is empty
|
||||
else{
|
||||
// Do nothing
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Now we need to descend through the autocomplete list
|
||||
else{
|
||||
if(autocompleteList.length == 0){
|
||||
return;
|
||||
}
|
||||
else{
|
||||
// Check if currently selecting an autocomplete entry
|
||||
if(compSelect > -1){
|
||||
|
||||
}
|
||||
// Not currently selecting an autocomplete entry
|
||||
else{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
public static function histNext():Void
|
||||
{
|
||||
// Only complete if input field is empty or scrolling through hist
|
||||
if(consoleInstance.cIn.text == "" || histSelect != -1){
|
||||
// Store current input in tmpInput
|
||||
if(histSelect == -1) tmpInput = consoleInstance.cIn.text;
|
||||
// Only go through history if history is not empty
|
||||
if(history.length != 0){
|
||||
// Check if currently selecting a history entry
|
||||
if(histSelect < history.length -1){
|
||||
histSelect++;
|
||||
}
|
||||
// Otherwise wrap around
|
||||
else{
|
||||
// Put selector to no selection
|
||||
histSelect = -1;
|
||||
}
|
||||
if(histSelect != -1){
|
||||
// Put correct history entry in the input field
|
||||
consoleInstance.cIn.text = history[histSelect];
|
||||
}
|
||||
else{
|
||||
// Restore tmp input
|
||||
consoleInstance.cIn.text = tmpInput;
|
||||
}
|
||||
}
|
||||
// History is empty
|
||||
else{
|
||||
// Do nothing
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,65 +0,0 @@
|
||||
package game.ui.console;
|
||||
|
||||
import engine.HProfiler;
|
||||
import engine.ui.UIPane;
|
||||
import engine.ui.UIElement;
|
||||
import openfl.text.TextField;
|
||||
import openfl.display3D.textures.Texture;
|
||||
import openfl.display.Sprite;
|
||||
|
||||
class ConsolePane extends Sprite{
|
||||
public function new(){
|
||||
super();
|
||||
var testPane:UIPane = new UIPane("test",{height:800,width:600});
|
||||
testPane.layout = VERTICAL;
|
||||
|
||||
var titlebarPane:UIPane = new UIPane("titlebar", {height:32, width: 600});
|
||||
titlebarPane.align = START;
|
||||
titlebarPane.layout = HORIZONTAL;
|
||||
|
||||
var inputPane:UIPane = new UIPane("inputbar", {height:32, width: 600});
|
||||
inputPane.align = END;
|
||||
inputPane.layout = HORIZONTAL;
|
||||
|
||||
var outputPane:UIPane = new UIPane("output pane", {height: 32, width: 600});
|
||||
outputPane.align = START;
|
||||
outputPane.dimensions.expandBehavior = STRETCH;
|
||||
HProfiler.startProfiling("ui_resize");
|
||||
testPane.width = 1000;
|
||||
HProfiler.stopProfiling("ui_resize");
|
||||
|
||||
testPane.addChild(titlebarPane);
|
||||
testPane.addChild(inputPane);
|
||||
testPane.addChild(outputPane);
|
||||
|
||||
|
||||
|
||||
var textField:TextField = new TextField();
|
||||
textField.background = true;
|
||||
textField.backgroundColor = 0x00ff00;
|
||||
|
||||
var testInputElement:UIElement = new UIElement("inputelem", {height: 32, width: 300},textField);
|
||||
testInputElement.dimensions.expandBehavior = STRETCH;
|
||||
testInputElement.padding = {x: 8, y: 8};
|
||||
|
||||
var inputButtonPane:UIPane = new UIPane("submit button", {height: 32, width: 32});
|
||||
inputButtonPane.align = END;
|
||||
|
||||
var inputFieldPane:UIPane = new UIPane("input field", {height: 32, width: 3});
|
||||
inputFieldPane.align = START;
|
||||
inputFieldPane.dimensions.expandBehavior = STRETCH;
|
||||
|
||||
inputPane.addChild(inputButtonPane);
|
||||
inputPane.addChild(inputFieldPane);
|
||||
//inputFieldPane.addChild(testInputElement);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
addChild(testPane.sprite);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
package game.video;
|
||||
|
||||
import game.ui.console.Console;
|
||||
import engine.ConVar;
|
||||
import engine.typedefs.console.CCmd;
|
||||
import game.ui.ConVar;
|
||||
import openfl.Lib;
|
||||
import openfl.display.StageDisplayState;
|
||||
|
||||
@ -12,29 +10,26 @@ class Mode
|
||||
{
|
||||
return Lib.application.window;
|
||||
}
|
||||
|
||||
public static function setVideoMode(width:Int, height:Int, fs:Int = null){
|
||||
getWindow().resize(width,height);
|
||||
if(fs == null){
|
||||
return;
|
||||
if(fs != null){
|
||||
switchFsMode(fs);
|
||||
}
|
||||
switchFsMode(fs);
|
||||
}
|
||||
@:concmd("mat_setvideomode")
|
||||
public static function cCmdMatSetVideoMode(args:Array<String>){
|
||||
Console.devMsg("Setting video mode to: "+args[0]+"x"+args[1]+"x"+args[2]);
|
||||
Mode.setVideoMode(Std.parseInt(args[0]), Std.parseInt(args[1]), Std.parseInt(args[2]));
|
||||
}
|
||||
public static var cvMatSetVideoMode:engine.typedefs.CCmd = ConVar.registerCCmd("mat_setvideomode",(args:Array<String>)->{
|
||||
setVideoMode(Std.parseInt(args[0]), Std.parseInt(args[1]), Std.parseInt(args[2]));
|
||||
});
|
||||
public static function switchFsMode(toState:Int = 0){
|
||||
Console.devMsg("Switching fullscreen mode to: "+toState);
|
||||
if(toState == 0){
|
||||
Lib.current.stage.displayState = StageDisplayState.FULL_SCREEN;
|
||||
}
|
||||
else if(toState == 1){
|
||||
Lib.current.stage.displayState = StageDisplayState.FULL_SCREEN_INTERACTIVE;
|
||||
if(Lib.current.stage.displayState != StageDisplayState.FULL_SCREEN_INTERACTIVE){
|
||||
Lib.current.stage.displayState = StageDisplayState.FULL_SCREEN_INTERACTIVE;
|
||||
}
|
||||
else{
|
||||
Lib.current.stage.displayState = StageDisplayState.NORMAL;
|
||||
}
|
||||
}
|
||||
else{
|
||||
Lib.current.stage.displayState = StageDisplayState.NORMAL;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
6
package-lock.json
generated
6
package-lock.json
generated
@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "DSTEngine",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
{}
|
||||
9
standalone-openfl-app/.gitignore
vendored
9
standalone-openfl-app/.gitignore
vendored
@ -1,9 +0,0 @@
|
||||
**/bin/*
|
||||
**/Export/*
|
||||
**/lime-repo/**/*
|
||||
**/openfl-repo/**/*
|
||||
|
||||
bin
|
||||
Export
|
||||
lime-repo
|
||||
openfl-repo
|
||||
@ -1,23 +0,0 @@
|
||||
# Never violate the following rules:
|
||||
- Make the code work with the .hxml based workflow
|
||||
- don't use compiletime `#if lime` or `#if openfl` as this relies on the openfl and lime build system. We don't use their buildsystems but try to do the same thing they do for the hxcpp based targets. (Native Windows/ Linux compilation)
|
||||
- Adhere to the spec outlined in OpenFL_Build_Chain_Asset_System_Deep_dive.md and IMPORTANT_INSTRUCTIONS.md
|
||||
- There is sample code in the Export/haxe directory. We want our own src/ManifestResources.hx to mirror the implementation in Export/haxe/ManifestResources.hx
|
||||
- Mirror preload logic from the Export/haxe directory into our own files.
|
||||
- We also want to preload our assets using a similar approach using the ManifestResource and doing related initialization in ApplicationMain.hx
|
||||
|
||||
# Implementation details:
|
||||
|
||||
- macros/AssetMacro.hx
|
||||
- Implement a @:assets decorator that allows specifying assets with the same parameters that openfl/lime use in project.xml
|
||||
- These assets will be defined in Assets.hx
|
||||
- Generate an asset manifest `default.json` in a folder `manifest` in the hxcpp output directory (the same path the built binary gets put into) (get the path for this from the haxe compiler at build time) as defined with the `-cpp` flag in build.hxml
|
||||
- Copy assets into the hxcpp output directory similarly to how lime and openfl do this
|
||||
- Assets.hx
|
||||
- as stated previously, the @:asset decorator will be used here to specify assets for the compiler to copy to the build output directory at compile time
|
||||
- Main.hx
|
||||
- This is the entrypoint for game specific code, no need to touch this yet.
|
||||
- ApplicationMain.hx
|
||||
- This is the actual entrypoint of the application. This sets up the lime/openfl application, window and stage. Also this is responsible for preloading assets.
|
||||
- ManifestResources.hx
|
||||
- This will be left the same as the sample in Export/haxe/ManifestResources.hx
|
||||
@ -1,37 +0,0 @@
|
||||
## Important Development Instructions
|
||||
|
||||
In this repository you will find 3 projects.
|
||||
|
||||
One of them is in the root and src directories of this repo, this is the project we're actively working on.
|
||||
|
||||
The other 2 are located at
|
||||
|
||||
- ./lime-repo
|
||||
- ./openfl-repo
|
||||
|
||||
Instructions:
|
||||
|
||||
Analyze the openfl repo, this project contains a build chain based on HXP that can be called from the commandline. We need to document how this buildchain operates when the following command is ran.
|
||||
|
||||
`openfl test linux`
|
||||
|
||||
We already know that it makes some calls to lime under the hood which has a similar build chain.
|
||||
|
||||
The current project is built to create an openfl app using the hxml build system and bypass lime's/ openfl's build system entirely.
|
||||
|
||||
We are currently working on asset bundling, this is proving to be a difficult task. Please document how openfl does this in a file called OpenFL_Build_Chain_Asset_System_Deep_Dive.md
|
||||
|
||||
## What we need:
|
||||
- `src/Assets.hx`
|
||||
- define asset paths in a similar way to how openfl does this in the project.xml file. This will be done using a custom meta tag `@:Assets`
|
||||
- We will have a macro in `src/macros/AssetMacro.hx` to process these meta tags, build a manifest, and copy the assets into the `--cpp` output directory specified in `build.hxml`
|
||||
- `src/macros/AssetMacro.hx`
|
||||
- Process `@:Assets` metatags defined in `src/Assets.hx`
|
||||
- Build manifest in a `manifest` subdirectory the `--cpp` output directory specified in `build.hxml`
|
||||
- copy the assets into the `assets` subdirectory in the `--cpp` output directory
|
||||
- `src/ManifestResources.hx`
|
||||
- initialize the Asset Libraries from the manifest files
|
||||
- `src/ApplicationMain.hx`
|
||||
- Initialize `src/ManifestResources.hx`
|
||||
- bootstrap the openfl application
|
||||
- add `src/Main.hx` to the application
|
||||
@ -1,734 +0,0 @@
|
||||
---
|
||||
title: XML Format
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Usually a simple project file is all you need to build projects using Lime or OpenFL, but it can be extended based on your needs. Here is a reference of what you can do.
|
||||
|
||||
### XML Schema
|
||||
|
||||
If you are using an XSD aware XML editor you can use the following declaration to enable XML validation and type hinting/auto completion.
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<project
|
||||
xmlns="http://lime.openfl.org/project/1.0.4"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://lime.openfl.org/project/1.0.4 http://lime.openfl.org/xsd/project-1.0.4.xsd">
|
||||
</project>
|
||||
```
|
||||
|
||||
### Conditionals
|
||||
|
||||
First, every node in the project file format supports `if` and `unless` attributes. These are conditional values to help you customize your build process, based upon a number of values. Here are some of the default values:
|
||||
|
||||
- mobile, desktop or web
|
||||
- ios, android, windows, mac, linux or html5
|
||||
- cpp, neko, flash or js
|
||||
|
||||
You can [`<set />`](#set) or [`<define />`](#define) values for conditional logic:
|
||||
|
||||
```xml
|
||||
<set name="red" />
|
||||
<window background="#FF0000" if="red" />
|
||||
|
||||
<define name="fullscreen" />
|
||||
<window fullscreen="true" if="fullscreen" />
|
||||
```
|
||||
|
||||
Both also support a `value` attribute, which can be retrieved later using the `${}` syntax.
|
||||
|
||||
```xml
|
||||
<set name="color" value="#FF0000" if="red" />
|
||||
<define name="color" value="#0000FF" if="blue" />
|
||||
|
||||
<window background="${color}" if="color" />
|
||||
```
|
||||
|
||||
You can also check values that were defined on the command line:
|
||||
|
||||
```sh
|
||||
lime test flash -Dblue
|
||||
```
|
||||
|
||||
```xml
|
||||
<window background="#0000FF" if="blue" />
|
||||
```
|
||||
|
||||
If you need to have multiple values in a conditional, spaces imply an "and" and vertical bars imply an "or", like this:
|
||||
|
||||
```xml
|
||||
<window width="640" height="480" if="define define2" unless="define3 || define4" />
|
||||
```
|
||||
|
||||
If a value isn't found, either because it was never set or because of [`<unset />`](#unset)/[`<undefine />`](#undefine), it will be treated as `false`.
|
||||
|
||||
### Build info
|
||||
|
||||
The `${}` syntax can get information about the build. You can use this data in a condition, print it, or pass it along to Haxe:
|
||||
|
||||
```xml
|
||||
<set name="buildOrTest" if="${project.command == build} || ${project.command == test}" />
|
||||
<echo value="Building for ${project.target} on a ${project.host} machine" if="buildOrTest" />
|
||||
<define name="projectDirectory" value="${project.workingDirectory}" />
|
||||
```
|
||||
|
||||
### Duplicates
|
||||
|
||||
You can include more than one copy of each tag, so do not worry about putting it all in one place. All of the values you define will be used. If you define the same value more than once, the last definition will be used.
|
||||
|
||||
```xml
|
||||
<window width="640" />
|
||||
<window height="480" />
|
||||
```
|
||||
|
||||
### include.xml
|
||||
|
||||
If you create a haxelib, you can add _include.xml_ to the top-level directory. The build tools will automatically add the contents of the file to the user's project. You can use this to add binary dependencies, additional classpaths, etc.
|
||||
|
||||
## XML Tag Glossary
|
||||
|
||||
### app
|
||||
|
||||
The `<app />` tag sets values important to building your project, including the entry point (main class), the output file directory, or if you want to customize the executable filename or define a custom preloader for a web platform:
|
||||
|
||||
```xml
|
||||
<app main="com.example.MyApplication" file="MyApplication" path="Export" preloader="CustomPreloader" />
|
||||
<app swf-version="11" />
|
||||
```
|
||||
|
||||
You can retrieve this information later:
|
||||
|
||||
```xml
|
||||
<echo value="App info: ${app.file} ${app.path}" />
|
||||
```
|
||||
|
||||
### architecture
|
||||
|
||||
Use `<architecture />` tags to set or exclude target-specific architectures. Depending on the target, values can include `arm64`, `armv7`, `armv7s`, `armv6`, `armv5`, `x86`, and `x64`.
|
||||
|
||||
By default, the only architecture built for Android will be `armv7`. For example, if you want to enable `armv6` and disable `armv7` you would set the `<architecture />` tag to:
|
||||
|
||||
```xml
|
||||
<architecture name="armv6" exclude="armv7" if="android" />
|
||||
```
|
||||
|
||||
### assets
|
||||
|
||||
Use asset nodes to add resources to your project, available using `lime.Assets`.
|
||||
|
||||
The path attribute can point to either a file or a directory. These files will be copied (or embedded) in your final project, and can be accessed using the `lime.Assets` class.
|
||||
|
||||
For example, if you include the following node in your project file:
|
||||
|
||||
```xml
|
||||
<assets path="images/MyImage.jpg" />
|
||||
```
|
||||
|
||||
You can access it in your application like this:
|
||||
|
||||
```haxe
|
||||
var bitmapData = Assets.getBitmapData ("images/MyImage.png");
|
||||
```
|
||||
|
||||
The target path will mirror the source path by default, but you also can include a rename attribute, if you wish to use a different target path. The `lime.Assets` class will use the _target_ path by default, so using the rename attribute will alter the names you use to reference your files.
|
||||
|
||||
If you would prefer to set the ID for your asset file yourself, use an "id" attribute. This only applies to asset nodes which point to a file, not a directory path.
|
||||
|
||||
When pointing to a directory, you can use the include or exclude attributes to specify patterns for including files automatically. Wildcards are supported. To include all the files under the directory, for example, use an include value of "*". You can separate multiple patterns using "\|" characters.
|
||||
|
||||
You can nest assets nodes inside of each other. If you specify a directory in the top assets node, its path will be appended to the paths you specify in subsequent nodes.
|
||||
|
||||
The type for each file will be determined automatically, based on each file extension, but you can use the type attribute to set it for the file or directory yourself. If you are nesting a node inside of another assets node, you can also use the name of the type as the name of your node.
|
||||
|
||||
These are the current types:
|
||||
|
||||
- binary
|
||||
- font
|
||||
- image
|
||||
- music
|
||||
- sound
|
||||
- template
|
||||
- text
|
||||
|
||||
Some targets can only support playing one music file at a time. You should use "music" for files which are designed to play as background music, and "sound" for all other audio. "binary" and "text" are generic types which are available as a ByteArray or String in your application. Most targets can use them interchangeably.
|
||||
|
||||
If an asset is specified as "template", it will not be copied/embedded as an ordinary asset, but instead will be copied to the root directory of your project, so you can replace any of the template HX, HXML or platform-specific files for the target.
|
||||
|
||||
```xml
|
||||
<assets path="assets" include="*" />
|
||||
<assets path="../../assets" rename="assets" include="*" />
|
||||
<assets path="assets/images" rename="images" include="*.jpg|*.png" exclude="example.jpg" />
|
||||
<assets path="assets">
|
||||
<assets path="images" include="*" type="image" />
|
||||
<assets path="assets">
|
||||
<sound path="sound/MySound.wav" id="MySound" />
|
||||
<music path="sound/BackgroundMusic.ogg" />
|
||||
</assets>
|
||||
```
|
||||
|
||||
### certificate
|
||||
|
||||
Use `<certificate />` tags to add a keystore for release signing on certain platforms.
|
||||
|
||||
If you do not include the password attribute, you will be prompted for your certificate password at the command-line.
|
||||
|
||||
For Android, the alias will be set to the file name of your certificate by default, without the extension. If the alias name is different, you can use the alias attribute.
|
||||
|
||||
If you have set the password attribute, the alias_password attribute will default to the same value. Otherwise you can add an alias-password attribute to specify a different value.
|
||||
|
||||
```xml
|
||||
<certificate path="to/certificate.crt" password="1234" alias="my-alias" alias-password="4321" />
|
||||
```
|
||||
|
||||
iOS does not use a certificate `path` and `password`, but instead uses a `team-id` attribute matching the ID provided in the Apple Developer portal for your team:
|
||||
|
||||
```xml
|
||||
<certificate team-id="SK12FH34" />
|
||||
```
|
||||
|
||||
You can retrieve this information later:
|
||||
|
||||
```xml
|
||||
<echo value="Certificate info: ${keystore.path} ${keystore.alias}" />
|
||||
```
|
||||
|
||||
### classpath
|
||||
|
||||
Same as [`<source/>`](#source).
|
||||
|
||||
### compilerflag
|
||||
|
||||
Same as [`<haxeflag/>`](#haxeflag).
|
||||
|
||||
### config
|
||||
|
||||
Use `<config />` tags to set platform-specific values. See [config options](config) for the full list.
|
||||
|
||||
```xml
|
||||
<config:android install-location="preferExternal" />
|
||||
<config:android permission="android.permission.INTERNET" />
|
||||
<config:android target-sdk-version="16" />
|
||||
<config:ios deployment="5.1" />
|
||||
<config:ios prerendered-icon="false" />
|
||||
```
|
||||
|
||||
You can retrieve this information later:
|
||||
|
||||
```xml
|
||||
<echo value="Android config info: ${config.android.target-sdk-version} ${config.android.minimum-sdk-version}" />
|
||||
<echo value="iOS config info: ${config.ios.compiler} ${config.ios.deployment}" />
|
||||
```
|
||||
|
||||
### define
|
||||
|
||||
Use `<define />` tags to set variables for conditional logic. See the [Conditionals](#conditionals) section above, and see [`<undefine />`](#undefine) to remove a defined value.
|
||||
|
||||
```xml
|
||||
<define name="color" value="blue" />
|
||||
<define name="custom-value" />
|
||||
```
|
||||
|
||||
These values will be available in Haxe and in [templates](#template). For templates, use the syntax `::DEFINE_CUSTOM_VALUE::`. Hyphens are converted to underscores in both cases.
|
||||
|
||||
### dependency
|
||||
|
||||
Use `<dependency />` tags to specify native frameworks or references that are required to compile your project, as well as additional libraries you need copied.
|
||||
|
||||
When targeting [iOS](../../advanced-setup/ios/), it may be used to add a native framework.
|
||||
|
||||
```xml
|
||||
<dependency name="GameKit.framework" if="ios" />
|
||||
```
|
||||
|
||||
When targeting [Android](../../advanced-setup/android/), it may be used to add a native library.
|
||||
|
||||
```xml
|
||||
<dependency name="example-sdk" path="dependencies/android/example-sdk" if="android" />
|
||||
```
|
||||
|
||||
When targeting [Adobe AIR](../../advanced-setup/air/), it may be used to add a native extension.
|
||||
|
||||
```xml
|
||||
<dependency name="com.example.MyNativeExtension" path="path/to/com.example.MyNativeExtension.ane" if="air"/>
|
||||
```
|
||||
|
||||
### echo
|
||||
|
||||
Prints a specified message to the console.
|
||||
|
||||
```xml
|
||||
<echo value="Some output message" />
|
||||
```
|
||||
|
||||
The `<echo/>` element is often combined with `if` or `unless`.
|
||||
|
||||
```xml
|
||||
<echo value="You are using advanced mode. Proceed with caution!" if="advanced_mode"/>
|
||||
```
|
||||
|
||||
Or it may be added inside a `<section/>` element:
|
||||
|
||||
```xml
|
||||
<section if="advanced_mode">
|
||||
<error value="You are using advanced mode. Proceed with caution!" />
|
||||
</section>
|
||||
```
|
||||
|
||||
### error
|
||||
|
||||
Logs an error with `lime.utils.Log.error()` which by default throws `value` message and stops compilation (if `lime.utils.Log.throwErrors` is set to `true`).
|
||||
|
||||
The `<error/>` element is often combined with `if` or `unless`.
|
||||
|
||||
```xml
|
||||
<error value="html5 isn't supported!" if="html5 />
|
||||
```
|
||||
|
||||
Or it may be added inside a `<section/>` element:
|
||||
|
||||
```xml
|
||||
<section if="html5">
|
||||
<error value="html5 isn't supported!" />
|
||||
</section>
|
||||
```
|
||||
|
||||
### haxedef
|
||||
|
||||
Use `<haxedef />` tags to add Haxe defines (similar to using a [`<haxeflag />`](#haxeflag) with "-D"):
|
||||
|
||||
```xml
|
||||
<haxedef name="define" />
|
||||
```
|
||||
|
||||
### haxeflag
|
||||
|
||||
Use `<haxeflag />` tags to add additional [Haxe compiler arguments](https://haxe.org/manual/compiler-usage.html):
|
||||
|
||||
```xml
|
||||
<haxeflag name="-dce" value="std" />
|
||||
```
|
||||
|
||||
### haxelib
|
||||
|
||||
Use `<haxelib />` tags to include [Haxe libraries](https://lib.haxe.org):
|
||||
|
||||
```xml
|
||||
<haxelib name="actuate" />
|
||||
```
|
||||
|
||||
You can specify a version or path, if you prefer:
|
||||
|
||||
```xml
|
||||
<haxelib name="actuate" version="1.0.0" /> <!-- Use Actuate 1.0.0, even if a newer version is installed. -->
|
||||
<haxelib name="actuate" path="../alt-haxelibs/actuate" /> <!-- Use a version of Actuate from a specific folder, even a folder that Haxelib doesn't know about. -->
|
||||
```
|
||||
|
||||
By default, an error will be thrown if the requested library isn't installed. You can set `optional` to continue without the library instead.
|
||||
|
||||
```xml
|
||||
<haxelib name="actuate" optional="true" /> <!-- Use Actuate if available, and don't if not. Haxe code can use `#if actuate` to avoid errors. -->
|
||||
```
|
||||
|
||||
If you have multiple Haxelib repository folders, you can select one using the `repository` attribute. The folder you select must include a copy of Lime (and OpenFL, if applicable).
|
||||
|
||||
```xml
|
||||
<haxelib repository="../alt-haxelibs" />
|
||||
```
|
||||
|
||||
### icon
|
||||
|
||||
Use `<icon />` nodes to add icon files to your project. When the command-line tools request icons for a target platform, it will either use an exact size match you have provided, or it will attempt to find the closest match possible and resize. If you include an SVG vector icon, it should prefer this file over resizing bitmap files.
|
||||
|
||||
```xml
|
||||
<icon path="icon.png" size="64" />
|
||||
<icon path="icon.png" width="96" height="96" />
|
||||
<icon path="icon.svg" />
|
||||
```
|
||||
|
||||
### include
|
||||
|
||||
Use `<include />` tags to add the tags found in another project file, or to find an _include.xml_ file in the target directory:
|
||||
|
||||
```xml
|
||||
<include path="to/another/project.xml" />
|
||||
<include path="to/shared/library" />
|
||||
```
|
||||
|
||||
### java
|
||||
|
||||
Use `<java />` tags to add Java classes to the project when targeting Android:
|
||||
|
||||
```xml
|
||||
<java path="to/classes" />
|
||||
```
|
||||
|
||||
### language
|
||||
|
||||
Adds a language to the list of supported languages (by default the list is empty).
|
||||
|
||||
```xml
|
||||
<language name="en-US" />
|
||||
```
|
||||
|
||||
### launchImage
|
||||
|
||||
Sets the path to launch image of the app (image which will be shown at application start up)
|
||||
|
||||
```xml
|
||||
<launchImage path="launchImage.png" />
|
||||
```
|
||||
|
||||
### launchStoryboard
|
||||
|
||||
Sets the launch screen storyboard (useful only for iOS development).
|
||||
|
||||
```xml
|
||||
<launchStoryboard path="image.png" />
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```xml
|
||||
<launchStoryboard name="image.png" />
|
||||
```
|
||||
|
||||
You can also use `template` for this purpose.
|
||||
|
||||
#### The default template
|
||||
|
||||
The launch storyboard feature uses Haxe templates and your _project.xml_ configuration to generate an Xcode storyboard file. Lime includes one default storyboard template `splash.storyboard`, which can be configured with the following options.
|
||||
|
||||
```xml
|
||||
<launchStoryboard template="splash.storyboard" assetsPath="Images/splash"
|
||||
bg:RGB="0xFFFFFF"
|
||||
contentMode="center"
|
||||
ipad="ipad-splash-sb.png"
|
||||
portrait="iphone-splash-sb.png">
|
||||
|
||||
<imageset name="ipad-splash-sb.png" />
|
||||
<imageset name="iphone-splash-sb.png" />
|
||||
|
||||
</launchStoryboard>
|
||||
```
|
||||
|
||||
`template` is the specific storyboard template you’re using, chosen from the templates available at `templates/ios/storyboards`.
|
||||
|
||||
`assetsPath` is the path from your openfl/lime project root to where your launch storyboard images are saved.
|
||||
|
||||
`bg` is a hexadecimal formatted color to use as the background, in case you’re not using a full-screen content mode.
|
||||
|
||||
`contentMode` determines how the image scales to fit the screen.
|
||||
|
||||
- `center` is unscaled
|
||||
- `scaleAspectFill` scales to fill the entire screen, with original aspect ratio
|
||||
- `scaleAspectFit` scales to fit inside the screen (letterbox), with original aspect ratio
|
||||
- `scaleToFill` stretches the image to fill the screen
|
||||
|
||||
There are three optional attributes, (`portrait`, `landscape` and `ipad`) which you can choose from based on the target devices and orientation of your game. (The above example includes just ipad and portrait.)
|
||||
|
||||
- `ipad` determines the image to use for iPads.
|
||||
- `portrait` determines the image to use for iPods and iPhones in portrait orientation.
|
||||
- `landscape` determines the image to use for iPods and iPhones in landscape orientation.
|
||||
|
||||
Finally, you have the `imageset` child elements. For each imageset, lime looks for `{name}.png`, `{name}@2x.png`, and `{name}@3x.png` in the specified `assetsPath`, copying any of those that exist into an Xcode imageset. Most iPhones these days require the @2x or @3x image, while iPads require @2x.
|
||||
|
||||
#### About templates and custom storyboard
|
||||
|
||||
`template`, `assetsPath`, and the `imageset` elements are standard parts of the configuration. All the other attributes listed above are available based on the chosen default template, `splash.storyboard`.
|
||||
|
||||
If the default template doesn’t meet your needs, you have two options, but both require you to open your exported iOS project in Xcode and create your own storyboard.
|
||||
|
||||
1. Take the storyboard you built in Xcode and put it into your project. In this case, you would use the name or path attribute instead of template, and you would have no template-specific attributes to set, but you would still need to provide the appropriate imagesets.
|
||||
|
||||
```xml
|
||||
<launchStoryboard path="custom.storyboard" assetsPath="Images/splash">
|
||||
<imageset name="ipad-splash-sb.png" />
|
||||
</launchStoryboard>
|
||||
```
|
||||
|
||||
2. Using the default template as an example, take the storyboard you built in Xcode and add it to your lime templates. You would use it in the same manner as the default template, but use any attributes you specified instead.
|
||||
|
||||
### library
|
||||
|
||||
All assets go into the "default" library, but by adding `<library>` tags it is possible to modify the default library and also define additional libraries and load/unload them as needed.
|
||||
|
||||
To disable preloading on the default library:
|
||||
|
||||
```xml
|
||||
<library name="default" preload="false" />
|
||||
```
|
||||
|
||||
To load assets at runtime:
|
||||
|
||||
```haxe
|
||||
Assets.loadLibrary ("default").onComplete (function (library) {
|
||||
|
||||
var bitmapData = Assets.getBitmapData ("default:image.png");
|
||||
// or
|
||||
var bitmapData = Assets.getBitmapData ("image.png");
|
||||
// "default:" prefix is implied, if no library prefix is included
|
||||
});
|
||||
```
|
||||
|
||||
#### Using Additional Asset Libraries
|
||||
|
||||
You can easily add assets to libraries other than the "default" library. These are not preloaded by default, unless you add:
|
||||
|
||||
```xml
|
||||
<library name="myOtherLibrary" preload="true" />
|
||||
```
|
||||
|
||||
Then to have certain assets allocated to the above library:
|
||||
|
||||
```xml
|
||||
<assets path="assets/other" library="myOtherLibrary" />
|
||||
```
|
||||
|
||||
Be sure to specify the correct library when retrieving the assets in your code. See the above example for using the library prefix.
|
||||
|
||||
You can also use `Assets.unloadLibrary` when you are doing using those resources.
|
||||
|
||||
### log
|
||||
|
||||
Logs an error (see [`<error/>`](#error)), warning or info message.
|
||||
|
||||
Examples:
|
||||
|
||||
```xml
|
||||
<log error="error message" />
|
||||
<log warn="warn message" />
|
||||
<log info="info message" />
|
||||
<log value="your message" />
|
||||
<log verbose="verbose message" />
|
||||
```
|
||||
|
||||
The `<log/>` element is often combined with `if` or `unless`.
|
||||
|
||||
```xml
|
||||
<log info="You are using advanced mode. Proceed with caution!" if="advanced_mode"/>
|
||||
```
|
||||
|
||||
```xml
|
||||
<log error="Missing this_is_required" unless="this_is_required"/>
|
||||
```
|
||||
|
||||
Or it may be added inside a `<section/>` element:
|
||||
|
||||
```xml
|
||||
<section if="advanced_mode">
|
||||
<log info="You are using advanced mode. Proceed with caution!" />
|
||||
</section>
|
||||
```
|
||||
|
||||
### meta
|
||||
|
||||
Use `<meta />` tags to add information about your application, which usually will not affect how the application runs, but how it is identified to the target operating system or on an application store:
|
||||
|
||||
```xml
|
||||
<meta title="My Application" package="com.example.myapplication" version="1.0.0" company="My Company" />
|
||||
```
|
||||
|
||||
You can retrieve this information later:
|
||||
|
||||
```xml
|
||||
<echo value="Metadata: ${meta.title} ${meta.version}" />
|
||||
```
|
||||
|
||||
### module
|
||||
|
||||
More to come.
|
||||
|
||||
### ndll
|
||||
|
||||
You can use `<ndll />` tags to include native libraries. These are usually located under an "ndll" directory, with additional directories based upon the target platform. Usually an `<ndll />` tag will be included as a part of an extension, and is rare to be used directly:
|
||||
|
||||
```xml
|
||||
<ndll name="std" haxelib="hxcpp" />
|
||||
```
|
||||
|
||||
### output
|
||||
|
||||
**Deprecated!** Use the [`<app/>`](#app) element instead.
|
||||
|
||||
Can be used for setting app file name, app path and app swf-version:
|
||||
|
||||
```xml
|
||||
<output name="app file name" />
|
||||
<output name="app path" />
|
||||
<output swf-version="11" />
|
||||
```
|
||||
|
||||
### path
|
||||
|
||||
Use `<path />` tags to add directories to your system's PATH environment variable.
|
||||
|
||||
```xml
|
||||
<path value="path/to/add/to/system/PATH" />
|
||||
```
|
||||
|
||||
### postbuild
|
||||
|
||||
Lets you set post-build commands such as Haxe code (interpeted by the Haxe interpreter), a run file command, or a console command.
|
||||
|
||||
```xml
|
||||
<postbuild haxe="Haxe code"/>
|
||||
<postbuild open="file to run"/>
|
||||
<postbuild command="command to run"/>
|
||||
<postbuild cmd="command to run"/>
|
||||
```
|
||||
|
||||
### prebuild
|
||||
|
||||
Lets you set pre-build commands such as Haxe code (interpeted by the Haxe interpreter), a run file command, or a console command:
|
||||
|
||||
```xml
|
||||
<prebuild haxe="Haxe code"/>
|
||||
<prebuild open="file to run"/>
|
||||
<prebuild command="command to run"/>
|
||||
<prebuild cmd="command to run"/>
|
||||
```
|
||||
|
||||
### preloader
|
||||
|
||||
**Deprecated!** Use [`<app preloader="preloaderClass" />`](#app) instead.
|
||||
|
||||
```xml
|
||||
<preloader name="preloaderClass" />
|
||||
```
|
||||
|
||||
### sample
|
||||
|
||||
In a library that targets Lime, makes a sample available using the `lime create` command.
|
||||
|
||||
```xml
|
||||
<sample path="${haxelib:flixel-demos}/Arcade" />
|
||||
```
|
||||
|
||||
Then you can use:
|
||||
|
||||
```sh
|
||||
lime create flixel:Arcade
|
||||
```
|
||||
|
||||
### section
|
||||
|
||||
The `<section />` tag is used to group other tags together. This is usually most valuable when combined with "if" and/or "unless" logic:
|
||||
|
||||
```xml
|
||||
<section if="html5">
|
||||
<source path="extra/src/html5" />
|
||||
</section>
|
||||
```
|
||||
|
||||
### set
|
||||
|
||||
Use `<set />` tags to set variables for conditional logic. See the [Conditionals](#conditionals) section above, and see [`<unset />`](#unset) to remove a set value.
|
||||
|
||||
```xml
|
||||
<set name="color" value="red" />
|
||||
<set name="custom-value" />
|
||||
```
|
||||
|
||||
These values will be available in [templates](#template) using the syntax `::SET_CUSTOM_VALUE::`. (Hyphens are converted to underscores.)
|
||||
|
||||
### setenv
|
||||
|
||||
Use `<setenv />` tags to set environment variables. See the [Conditionals](#conditionals) section above.
|
||||
|
||||
```xml
|
||||
<setenv name="GLOBAL_DEFINE" />
|
||||
```
|
||||
|
||||
### source
|
||||
|
||||
Use `<source />` tags to add Haxe class paths:
|
||||
|
||||
```xml
|
||||
<source path="Source" />
|
||||
```
|
||||
|
||||
If you are using `@:file`, `@:bitmap`, `@:sound` or `@:file` tags in your project, be sure that the asset files are available within your Haxe source paths.
|
||||
|
||||
### splashscreen
|
||||
|
||||
Same as [`<launchimage/>`](#launchimage).
|
||||
|
||||
### ssl
|
||||
|
||||
Not implemented
|
||||
|
||||
### swf
|
||||
|
||||
See [`<library/>`](#library).
|
||||
|
||||
### target
|
||||
|
||||
Lets you redefine the build process for a specific target by running a custom haxelib command. This might be useful if you want to use your own library for building your project, i.e. you know what you're doing and you know how the Lime build system works.
|
||||
|
||||
```xml
|
||||
<target name="customTarget" handler="yourHandler" />
|
||||
```
|
||||
|
||||
### template
|
||||
|
||||
Use `<template />` tags to add paths which can override the templates used by the command-line tools.
|
||||
|
||||
You can add a full template path like this:
|
||||
|
||||
```xml
|
||||
<template path="templates" />
|
||||
```
|
||||
|
||||
Otherwise, you can override a single file like this:
|
||||
|
||||
```xml
|
||||
<template path="Assets/index.html" rename="index.php" />
|
||||
```
|
||||
|
||||
### templatepath
|
||||
|
||||
See [`<template/>`](#template).
|
||||
|
||||
```xml
|
||||
<templatepath name="path"/>
|
||||
```
|
||||
|
||||
### undefine
|
||||
|
||||
Unsets a previously defined flag. See entry for [`<define/>`](#define).
|
||||
|
||||
```xml
|
||||
<undefine name="red" />
|
||||
```
|
||||
|
||||
### unset
|
||||
|
||||
Unsets a previously set value. See entry for [`<set/>`](#set).
|
||||
|
||||
```xml
|
||||
<set name="red" value="0xff0000" />
|
||||
<unset name="red" />
|
||||
```
|
||||
|
||||
### window
|
||||
|
||||
You can use `<window />` tags to control how an application will be initialized. This includes the screen resolution and background color, as well as other options, such as whether hardware should be allowed or display mode flags.
|
||||
|
||||
By default, mobile platforms use a window width and height of 0, which is a special value that uses the resolution of the current display. This is available on desktop platforms, but usually it is recommended to enable the `fullscreen` flag instead, and to set the `width` and `height` values to a good windowed resolution. There is a special `fps="0"` value for HTML5, which is default, which uses "requestAnimationFrame" instead of forcing a frame rate.
|
||||
|
||||
```xml
|
||||
<window width="640" height="480" background="#FFFFFF" fps="30" />
|
||||
<window hardware="true" allow-shaders="true" require-shaders="true" depth-buffer="false" stencil-buffer="false" />
|
||||
<window fullscreen="false" resizable="true" borderless="false" vsync="false" />
|
||||
<window orientation="portrait" />
|
||||
```
|
||||
|
||||
The `orientation` value expects either "portrait" or "landscape" ... the default is "auto" which allows the operating system to decide which orientation to use.
|
||||
|
||||
You can retrieve this information later:
|
||||
|
||||
```xml
|
||||
<echo value="Window info: ${window.fps} ${window.allowShaders}" />
|
||||
```
|
||||
@ -1,24 +0,0 @@
|
||||
# Makefile for standalone OpenFL application
|
||||
|
||||
# Default target
|
||||
.PHONY: all
|
||||
all: build
|
||||
|
||||
# Build the application
|
||||
.PHONY: build
|
||||
build:
|
||||
haxe build.hxml
|
||||
|
||||
# Run the compiled application
|
||||
.PHONY: run
|
||||
run:
|
||||
bash -c "cd bin/cpp && ./ApplicationMain"
|
||||
|
||||
# Build and run the application
|
||||
.PHONY: test
|
||||
test: build run
|
||||
|
||||
# Clean build artifacts
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf bin/cpp
|
||||
@ -1,558 +0,0 @@
|
||||
# OpenFL Build Chain: Asset System Deep Dive
|
||||
|
||||
This document provides an in-depth analysis of OpenFL's asset handling system, focusing on asset processing, the runtime access layer, and resource management. This information is intended to help implement a custom asset bundling system that bypasses the standard OpenFL/Lime build toolchain.
|
||||
|
||||
## 1. Asset Processing System
|
||||
|
||||
OpenFL's asset processing pipeline transforms source files into platform-optimized assets through several stages:
|
||||
|
||||
### 1.1 Asset Identification
|
||||
|
||||
**Implementation:**
|
||||
- The asset processing begins with scanning directories specified in `project.xml`
|
||||
- For each asset, OpenFL records:
|
||||
- **Source path**: Location in the project directory
|
||||
- **Target path**: Location in the output bundle
|
||||
- **ID**: Unique identifier (typically the target path)
|
||||
- **Type**: Determined by file extension or explicit definition
|
||||
|
||||
```haxe
|
||||
// Simplified representation of how OpenFL identifies assets
|
||||
var assets = [];
|
||||
for (assetPath in FileSystem.readDirectory(sourcePath)) {
|
||||
if (filterPattern.match(assetPath)) {
|
||||
var id = targetPath + assetPath.substr(sourcePath.length);
|
||||
var type = determineAssetType(assetPath);
|
||||
assets.push({ id: id, path: assetPath, type: type });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 1.2 Type-Specific Processing
|
||||
|
||||
OpenFL processes different asset types with specialized handlers:
|
||||
|
||||
#### Images (BitmapData)
|
||||
- **Formats**: PNG, JPG, GIF, BMP, etc.
|
||||
- **Processing**:
|
||||
- Conversion to platform-specific formats
|
||||
- Optional compression
|
||||
- Resolution handling for multi-DPI targets
|
||||
- For embedded assets, images are encoded as Base64 or binary data within generated code
|
||||
|
||||
```haxe
|
||||
// Example of image processing logic
|
||||
function processImage(path, targetPath, options) {
|
||||
var image = Image.fromFile(path);
|
||||
|
||||
// Apply transformations based on options
|
||||
if (options.compress) image.compress(options.quality);
|
||||
if (options.resize) image.resize(options.width, options.height);
|
||||
|
||||
// Output in appropriate format
|
||||
if (options.embed) {
|
||||
return encodeImageForEmbedding(image, options.format);
|
||||
} else {
|
||||
image.save(targetPath, options.format);
|
||||
return { path: targetPath };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Audio
|
||||
- **Formats**: MP3, OGG, WAV
|
||||
- **Processing**:
|
||||
- Format conversion based on platform support
|
||||
- Optional compression
|
||||
- Stream vs. complete load settings
|
||||
|
||||
#### Fonts
|
||||
- **Processing**:
|
||||
- Registration with the text engine
|
||||
- Subsetting when specified
|
||||
- Conversion to platform-specific formats
|
||||
|
||||
#### Text/Data Files
|
||||
- **Processing**:
|
||||
- Optional minification for JSON/XML
|
||||
- Encoding conversion if necessary
|
||||
- Direct embedding or file copying
|
||||
|
||||
### 1.3 Asset Transformation Chain
|
||||
|
||||
For complex asset types, OpenFL applies a chain of transformations:
|
||||
|
||||
1. **Loading**: Reading the source asset
|
||||
2. **Validation**: Checking format compatibility
|
||||
3. **Transformation**: Applying format-specific conversions
|
||||
4. **Optimization**: Platform-specific optimizations
|
||||
5. **Output**: Writing to target location or embedding
|
||||
|
||||
### 1.4 Embedding Process
|
||||
|
||||
For embedded assets, OpenFL:
|
||||
|
||||
1. Reads the asset content
|
||||
2. Converts it to a code representation (usually a byte array)
|
||||
3. Generates code to reconstruct the asset at runtime
|
||||
4. Includes this code in the compiled application
|
||||
|
||||
```haxe
|
||||
// Example of how embedding is implemented
|
||||
var content = File.getBytes(assetPath);
|
||||
var assetCode = 'var ${safeName}_bytes = new haxe.io.Bytes(${content.length}, "${Base64.encode(content)}");';
|
||||
assetCode += 'assets.byteData.set("${assetId}", ${safeName}_bytes);';
|
||||
```
|
||||
|
||||
### 1.5 Manifest Generation
|
||||
|
||||
The final step creates a manifest that catalogs all assets:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "default",
|
||||
"assets": [
|
||||
{
|
||||
"id": "assets/images/logo.png",
|
||||
"path": "./assets/images/logo.png",
|
||||
"type": "image"
|
||||
},
|
||||
{
|
||||
"id": "assets/data/levels.json",
|
||||
"path": "./assets/data/levels.json",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 2. Runtime Access Layer
|
||||
|
||||
The runtime access layer is responsible for loading assets during application execution.
|
||||
|
||||
### 2.1 Assets Class Architecture
|
||||
|
||||
OpenFL's asset system is built around several key classes:
|
||||
|
||||
- **Assets** (`openfl.utils.Assets`): Static interface for accessing assets
|
||||
- **AssetLibrary** (`openfl.utils.AssetLibrary`): Container for a collection of assets
|
||||
- **AssetCache** (`openfl.utils.AssetCache`): Temporary storage for loaded assets
|
||||
|
||||
```haxe
|
||||
// Core Assets class simplified structure
|
||||
class Assets {
|
||||
private static var libraries:Map<String, AssetLibrary>;
|
||||
private static var cache:AssetCache;
|
||||
|
||||
public static function getBitmapData(id:String, useCache:Bool = true):BitmapData;
|
||||
public static function getBytes(id:String):Bytes;
|
||||
public static function getFont(id:String):Font;
|
||||
public static function getSound(id:String):Sound;
|
||||
public static function getText(id:String):String;
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 Asset Resolution System
|
||||
|
||||
When requesting an asset:
|
||||
|
||||
1. The Assets class parses the ID to determine the library and asset path
|
||||
2. It checks if the asset is already cached (if caching is enabled)
|
||||
3. If not cached, it delegates loading to the appropriate AssetLibrary
|
||||
4. The loaded asset is optionally cached before being returned
|
||||
|
||||
```haxe
|
||||
// Asset resolution pseudocode
|
||||
function getAsset(id:String, type:AssetType) {
|
||||
var libraryName = parseLibraryName(id);
|
||||
var assetPath = parseAssetPath(id);
|
||||
var library = libraries.get(libraryName);
|
||||
|
||||
if (useCache && cache.has(id)) {
|
||||
return cache.get(id);
|
||||
}
|
||||
|
||||
var asset = library.loadAsset(assetPath, type);
|
||||
|
||||
if (useCache) {
|
||||
cache.set(id, asset);
|
||||
}
|
||||
|
||||
return asset;
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 Library Loading Process
|
||||
|
||||
Asset libraries are typically loaded at application startup:
|
||||
|
||||
```haxe
|
||||
// How OpenFL initializes the asset system
|
||||
public static function initializeAssets():Void {
|
||||
// Load embedded manifest(s)
|
||||
var manifest = AssetManifest.fromBytes(getEmbeddedManifest());
|
||||
var library = AssetLibrary.fromManifest(manifest);
|
||||
|
||||
// Register the library
|
||||
registerLibrary("default", library);
|
||||
|
||||
// Additional libraries can be loaded dynamically
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 Asset Loading Methods
|
||||
|
||||
OpenFL supports both synchronous and asynchronous loading:
|
||||
|
||||
- **Synchronous**: `Assets.getBitmapData(id)` - Blocks until the asset is loaded
|
||||
- **Asynchronous**: `Assets.loadBitmapData(id).onComplete(callback)` - Non-blocking load
|
||||
|
||||
```haxe
|
||||
// Example of async loading implementation
|
||||
public static function loadBitmapData(id:String):Future<BitmapData> {
|
||||
var libraryName = parseLibraryName(id);
|
||||
var assetPath = parseAssetPath(id);
|
||||
var library = libraries.get(libraryName);
|
||||
|
||||
return library.loadBitmapData(assetPath);
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Resource Management
|
||||
|
||||
Resource management focuses on efficiently handling assets during runtime.
|
||||
|
||||
### 3.1 Asset Caching System
|
||||
|
||||
OpenFL's caching system:
|
||||
|
||||
1. Stores loaded assets by ID in type-specific caches
|
||||
2. Enables efficient reuse of frequently accessed assets
|
||||
3. Provides cache control methods to manage memory usage
|
||||
|
||||
```haxe
|
||||
// AssetCache implementation outline
|
||||
class AssetCache {
|
||||
private var bitmapData:Map<String, BitmapData>;
|
||||
private var font:Map<String, Font>;
|
||||
private var sound:Map<String, Sound>;
|
||||
|
||||
public function set(id:String, asset:Dynamic, type:AssetType):Void {
|
||||
switch (type) {
|
||||
case AssetType.IMAGE:
|
||||
bitmapData.set(id, asset);
|
||||
case AssetType.FONT:
|
||||
font.set(id, asset);
|
||||
// Other types...
|
||||
}
|
||||
}
|
||||
|
||||
public function get(id:String, type:AssetType):Dynamic {
|
||||
switch (type) {
|
||||
case AssetType.IMAGE:
|
||||
return bitmapData.get(id);
|
||||
// Other types...
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function clear(type:AssetType = null):Void {
|
||||
// Clear specific or all caches
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Memory Management
|
||||
|
||||
OpenFL implements several strategies for efficient memory usage:
|
||||
|
||||
#### Reference Counting
|
||||
- Certain assets implement reference counting to track usage
|
||||
- When references drop to zero, assets can be unloaded
|
||||
|
||||
```haxe
|
||||
class ManagedAsset {
|
||||
private var referenceCount:Int = 0;
|
||||
|
||||
public function retain():Void {
|
||||
referenceCount++;
|
||||
}
|
||||
|
||||
public function release():Void {
|
||||
referenceCount--;
|
||||
if (referenceCount <= 0) {
|
||||
dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private function dispose():Void {
|
||||
// Resource-specific cleanup
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Cache Eviction Policies
|
||||
- Time-based: Assets unused for a period are removed
|
||||
- Size-based: Cache enforces a maximum memory footprint
|
||||
- Priority-based: Critical assets remain cached longer
|
||||
|
||||
#### Manual Control
|
||||
- `Assets.cache.clear()` - Clears all cached assets
|
||||
- `Assets.cache.removeBitmapData(id)` - Removes specific cached assets
|
||||
- `Assets.unloadLibrary(name)` - Unloads entire libraries
|
||||
|
||||
### 3.3 Event System
|
||||
|
||||
The asset system uses events to communicate state changes:
|
||||
|
||||
```haxe
|
||||
// Event system for asset loading
|
||||
class AssetLibrary {
|
||||
private var eventDispatcher:EventDispatcher;
|
||||
|
||||
public function loadBitmapData(id:String):Future<BitmapData> {
|
||||
var future = new Future<BitmapData>();
|
||||
|
||||
// Start loading process
|
||||
var loader = new BitmapDataLoader(id);
|
||||
loader.onComplete = function(bitmapData) {
|
||||
eventDispatcher.dispatchEvent(new AssetEvent(AssetEvent.ASSET_LOADED, id));
|
||||
future.complete(bitmapData);
|
||||
};
|
||||
loader.onError = function(error) {
|
||||
eventDispatcher.dispatchEvent(new AssetEvent(AssetEvent.ASSET_ERROR, id));
|
||||
future.error(error);
|
||||
};
|
||||
loader.load();
|
||||
|
||||
return future;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. Implementation Guidelines for Custom Asset System
|
||||
|
||||
Based on OpenFL's approach, here are guidelines for implementing your own asset bundling system:
|
||||
|
||||
### 4.1 Asset Processing Implementation
|
||||
|
||||
1. **Asset Scanner**:
|
||||
- Create a recursive directory scanner that identifies assets
|
||||
- Implement pattern matching for inclusion/exclusion rules
|
||||
- Generate unique IDs based on file paths
|
||||
|
||||
```haxe
|
||||
function scanAssets(directory:String, patterns:Array<String>):Array<AssetInfo> {
|
||||
var assets = [];
|
||||
|
||||
for (file in FileSystem.readRecursive(directory)) {
|
||||
if (matchesAnyPattern(file, patterns)) {
|
||||
assets.push({
|
||||
id: generateAssetId(file, directory),
|
||||
path: file,
|
||||
type: determineType(file)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return assets;
|
||||
}
|
||||
```
|
||||
|
||||
2. **Asset Processors**:
|
||||
- Create processor classes for each asset type
|
||||
- Implement transformation pipelines
|
||||
- Support embedding and non-embedding modes
|
||||
|
||||
```haxe
|
||||
interface AssetProcessor {
|
||||
function process(source:String, target:String, options:Map<String, Dynamic>):ProcessedAsset;
|
||||
function embed(source:String, options:Map<String, Dynamic>):EmbeddedCode;
|
||||
}
|
||||
|
||||
class ImageProcessor implements AssetProcessor {
|
||||
public function process(source, target, options) {
|
||||
// Image-specific processing
|
||||
}
|
||||
|
||||
public function embed(source, options) {
|
||||
// Generate code for embedded image
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **Manifest Generator**:
|
||||
- Create a serializable manifest structure
|
||||
- Support JSON and binary formats
|
||||
- Include asset metadata for runtime access
|
||||
|
||||
```haxe
|
||||
function generateManifest(assets:Array<AssetInfo>):AssetManifest {
|
||||
var manifest = new AssetManifest();
|
||||
|
||||
for (asset in assets) {
|
||||
manifest.addAsset(
|
||||
asset.id,
|
||||
asset.path,
|
||||
asset.type,
|
||||
asset.parameters
|
||||
);
|
||||
}
|
||||
|
||||
return manifest;
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 Runtime Access Layer Implementation
|
||||
|
||||
1. **Assets Class**:
|
||||
- Create a static API similar to OpenFL's Assets class
|
||||
- Implement type-specific getters (getBitmapData, getText, etc.)
|
||||
- Support both synchronous and asynchronous loading
|
||||
|
||||
```haxe
|
||||
class Assets {
|
||||
private static var libraries:Map<String, AssetLibrary> = new Map();
|
||||
private static var cache:AssetCache = new AssetCache();
|
||||
|
||||
public static function registerLibrary(name:String, library:AssetLibrary):Void {
|
||||
libraries.set(name, library);
|
||||
}
|
||||
|
||||
public static function getBitmapData(id:String, useCache:Bool = true):BitmapData {
|
||||
// Implementation
|
||||
}
|
||||
|
||||
public static function loadBitmapData(id:String):Future<BitmapData> {
|
||||
// Async implementation
|
||||
}
|
||||
|
||||
// Other asset type methods
|
||||
}
|
||||
```
|
||||
|
||||
2. **Asset Library**:
|
||||
- Implement a library class to manage asset collections
|
||||
- Support manifest loading
|
||||
- Handle asset type detection and loading
|
||||
|
||||
```haxe
|
||||
class AssetLibrary {
|
||||
private var assets:Map<String, AssetInfo> = new Map();
|
||||
|
||||
public static function fromManifest(manifest:AssetManifest):AssetLibrary {
|
||||
var library = new AssetLibrary();
|
||||
|
||||
for (asset in manifest.assets) {
|
||||
library.addAsset(asset.id, asset);
|
||||
}
|
||||
|
||||
return library;
|
||||
}
|
||||
|
||||
public function getBitmapData(id:String):BitmapData {
|
||||
var asset = assets.get(id);
|
||||
if (asset != null && asset.type == AssetType.IMAGE) {
|
||||
return loadBitmapDataSync(asset.path);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Other loading methods
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 Resource Management Implementation
|
||||
|
||||
1. **Asset Cache**:
|
||||
- Implement a multi-type caching system
|
||||
- Support cache control operations
|
||||
- Add memory usage tracking
|
||||
|
||||
```haxe
|
||||
class AssetCache {
|
||||
private var bitmapData:Map<String, BitmapData> = new Map();
|
||||
private var text:Map<String, String> = new Map();
|
||||
// Other asset type caches
|
||||
|
||||
public function has(id:String, type:AssetType):Bool {
|
||||
switch (type) {
|
||||
case AssetType.IMAGE:
|
||||
return bitmapData.exists(id);
|
||||
// Other types
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function set(id:String, asset:Dynamic, type:AssetType):Void {
|
||||
// Implementation
|
||||
}
|
||||
|
||||
public function get(id:String, type:AssetType):Dynamic {
|
||||
// Implementation
|
||||
}
|
||||
|
||||
public function clear(type:AssetType = null):Void {
|
||||
// Implementation
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **Memory Management**:
|
||||
- Implement reference counting for disposable assets
|
||||
- Add cache size limits
|
||||
- Create cache eviction policies
|
||||
|
||||
```haxe
|
||||
class MemoryMonitor {
|
||||
private static var memoryUsage:Map<String, Int> = new Map();
|
||||
private static var maxMemory:Int = 100 * 1024 * 1024; // 100MB example limit
|
||||
|
||||
public static function trackAsset(id:String, size:Int):Void {
|
||||
memoryUsage.set(id, size);
|
||||
enforceMemoryLimits();
|
||||
}
|
||||
|
||||
public static function untrackAsset(id:String):Void {
|
||||
memoryUsage.remove(id);
|
||||
}
|
||||
|
||||
private static function enforceMemoryLimits():Void {
|
||||
var totalUsage = calculateTotalUsage();
|
||||
|
||||
if (totalUsage > maxMemory) {
|
||||
evictCache(totalUsage - maxMemory);
|
||||
}
|
||||
}
|
||||
|
||||
private static function evictCache(bytesToFree:Int):Void {
|
||||
// Implement LRU or other eviction strategy
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5. Critical Implementation Considerations
|
||||
|
||||
When implementing your custom asset system, keep these factors in mind:
|
||||
|
||||
### Performance Optimization
|
||||
- Implement asynchronous loading for large assets
|
||||
- Use streaming for audio/video when appropriate
|
||||
- Implement progressive loading for large textures
|
||||
|
||||
### Platform Compatibility
|
||||
- Handle platform-specific file format requirements
|
||||
- Implement fallback loading mechanisms
|
||||
- Consider thread safety for multi-threaded platforms
|
||||
|
||||
### Memory Management
|
||||
- Monitor memory usage of cached assets
|
||||
- Implement automatic cache cleanup
|
||||
- Provide manual unloading API for memory-constrained environments
|
||||
|
||||
### Asset Versioning
|
||||
- Implement asset versioning to handle updates
|
||||
- Consider cache invalidation strategies
|
||||
- Support hot-reloading during development
|
||||
|
||||
By following these implementation guidelines, you can create a robust asset management system that provides functionality similar to OpenFL's built-in system while giving you complete control over the asset bundling and loading process.
|
||||
@ -1,49 +0,0 @@
|
||||
# Standalone OpenFL Application
|
||||
|
||||
This project is a standalone OpenFL application that serves as a template for building your own applications using the OpenFL framework. Below are the details regarding the structure and usage of the project.
|
||||
|
||||
## Project Structure
|
||||
|
||||
- **src/**: Contains the source code for the application.
|
||||
- **Main.hx**: Entry point for the application. Initializes the application and sets up the main logic.
|
||||
- **ApplicationMain.hx**: Manages the application lifecycle, creates the application instance, and handles preloader functionality.
|
||||
- **DocumentClass.hx**: Extends the main application class, managing display and event handling.
|
||||
- **ManifestResources.hx**: Manages application resources, including asset loading and preloader resources.
|
||||
|
||||
- **assets/**: Contains asset files for the application.
|
||||
- **img/**: Directory for image assets. Currently contains a placeholder file.
|
||||
- **placeholder.txt**: Placeholder file that can be replaced with actual image files.
|
||||
|
||||
- **build.hxml**: Build configuration file for the Haxe compiler. Specifies source files, output directory, and additional compiler flags or libraries.
|
||||
|
||||
- **compile.sh**: Shell script to automate the build process. Runs the Haxe compiler with the specified build.hxml configuration.
|
||||
|
||||
- **project.xml**: Project configuration file for OpenFL, containing metadata such as title, version, and dependencies.
|
||||
|
||||
- **README.md**: Documentation for the project, including setup instructions and usage information.
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
1. **Install Haxe and OpenFL**: Ensure you have Haxe and OpenFL installed on your system. You can find installation instructions on the [Haxe website](https://haxe.org/download/) and the [OpenFL website](https://openfl.org/download/).
|
||||
|
||||
2. **Replace Placeholders**: Open the `src/ApplicationMain.hx` file and replace the `::` placeholders with actual values relevant to your application:
|
||||
- `::APP_FILE::`: Path to your main application file (e.g., "src/Main.hx").
|
||||
- `::meta.buildNumber::`: Build number of your application.
|
||||
- `::meta.company::`: Your company name.
|
||||
- `::meta.title::`: Title of your application.
|
||||
- `::meta.packageName::`: Package name of your application.
|
||||
- `::meta.version::`: Version of your application.
|
||||
- `::WIN_WIDTH::` and `::WIN_HEIGHT::`: Desired window dimensions.
|
||||
- Other boolean values like `::allowHighDPI::`, `::alwaysOnTop::`, etc., with appropriate settings.
|
||||
|
||||
3. **Adjust Build Configuration**: Modify the `build.hxml` file to include necessary libraries and source files for your application.
|
||||
|
||||
4. **Build the Application**: Run the `compile.sh` script to build your OpenFL application. This will compile the source files and generate the executable.
|
||||
|
||||
5. **Run the Application**: After building, you can run the generated executable to see your application in action.
|
||||
|
||||
## Usage Information
|
||||
|
||||
This template provides a solid foundation for developing OpenFL applications. You can expand upon the provided classes and resources to create your own unique application. Be sure to refer to the OpenFL documentation for additional guidance on using the framework effectively.
|
||||
|
||||
Happy coding!
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
This file is intentionally left blank.
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 547 B |
@ -1,28 +0,0 @@
|
||||
-main ApplicationMain
|
||||
|
||||
-lib openfl
|
||||
-lib lime
|
||||
-lib actuate
|
||||
|
||||
-cp src
|
||||
-D lime-harfbuzz
|
||||
-D lime-openal
|
||||
-D lime-threads
|
||||
-D tools=8.2.2
|
||||
-D lime-cairo
|
||||
-D lime-opengl
|
||||
# -D no-compilation
|
||||
-D native
|
||||
-D lime-curl
|
||||
-D lime-native
|
||||
-D lime-vorbis
|
||||
-D openfl-native
|
||||
-D lime-cffi
|
||||
-D linux
|
||||
-D desktop
|
||||
--remap flash:openfl
|
||||
-cpp bin/cpp
|
||||
--macro keep("Main")
|
||||
--macro keep("Assets")
|
||||
--macro keep("AssetMacro")
|
||||
# --macro macros.MacroLoader.run()
|
||||
@ -1,7 +0,0 @@
|
||||
src/macros/AssetMacro.hx:106: Added asset: data/img/placeholder.txt
|
||||
src/macros/AssetMacro.hx:48: DEBUG - JSON starts with: {
|
||||
"name": "default",
|
||||
"rootPath": "../",
|
||||
"ver...
|
||||
src/macros/AssetMacro.hx:55: Asset manifest saved to: bin/cpp/manifest/default.json
|
||||
src/macros/AssetMacro.hx:55: Asset manifest saved to: manifest/default.json
|
||||
@ -1,15 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Compile the project using the Haxe compiler with the specified build.hxml configuration
|
||||
haxe build.hxml
|
||||
|
||||
# Check if the compilation was successful
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Compilation successful!"
|
||||
else
|
||||
echo "Compilation failed!"
|
||||
fi
|
||||
|
||||
# Run the compiled project
|
||||
# We need to run ApplicationMain-debug within the bin/cpp directory as the current working directory
|
||||
(cd ./bin/cpp; ./ApplicationMain-debug)
|
||||
@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- <project>
|
||||
<meta>
|
||||
<title>Your Application Title</title>
|
||||
<version>1.0.0</version>
|
||||
<company>Your Company Name</company>
|
||||
<package>com.yourcompany.yourapp</package>
|
||||
<buildNumber>1</buildNumber>
|
||||
</meta>
|
||||
<source>
|
||||
<path>src/Main.hx</path>
|
||||
<path>src/ApplicationMain.hx</path>
|
||||
<path>src/DocumentClass.hx</path>
|
||||
<path>src/ManifestResources.hx</path>
|
||||
</source>
|
||||
<assets>
|
||||
<path>assets/img/</path>
|
||||
</assets>
|
||||
<dependencies>
|
||||
<dependency>openfl</dependency>
|
||||
<dependency>lime</dependency>
|
||||
</dependencies>
|
||||
</project> -->
|
||||
<project>
|
||||
|
||||
<meta title="MinimalApplication" package="com.sample.minimalapplication" version="1.0.0" company="Company Name" />
|
||||
<app main="Main" path="Export" file="MinimalApplication" />
|
||||
|
||||
<source path="src" />
|
||||
|
||||
<haxelib name="openfl" />
|
||||
|
||||
<assets path="assets" rename="assets" />
|
||||
|
||||
</project>
|
||||
@ -1,331 +0,0 @@
|
||||
package;
|
||||
|
||||
#if macro
|
||||
import haxe.macro.Compiler;
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Expr;
|
||||
#end
|
||||
|
||||
|
||||
@:access(lime.app.Application)
|
||||
@:access(lime.system.System)
|
||||
@:access(openfl.display.Stage)
|
||||
@:access(openfl.events.UncaughtErrorEvents)
|
||||
@:dox(hide)
|
||||
class ApplicationMain
|
||||
{
|
||||
#if !macro
|
||||
public static function main()
|
||||
{
|
||||
lime.system.System.__registerEntryPoint("MinimalApplication", create);
|
||||
|
||||
#if (js && html5)
|
||||
#if (munit || (utest && openfl_enable_utest_legacy_mode))
|
||||
lime.system.System.embed("MinimalApplication", null, 800, 600);
|
||||
#end
|
||||
#else
|
||||
create(null);
|
||||
#end
|
||||
}
|
||||
|
||||
public static function create(config):Void
|
||||
{
|
||||
var app = new openfl.display.Application();
|
||||
|
||||
#if !disable_preloader_assets
|
||||
ManifestResources.init(config);
|
||||
#end
|
||||
|
||||
app.meta["build"] = "48";
|
||||
app.meta["company"] = "Company Name";
|
||||
app.meta["file"] = "MinimalApplication";
|
||||
app.meta["name"] = "MinimalApplication";
|
||||
app.meta["packageName"] = "com.sample.minimalapplication";
|
||||
app.meta["version"] = "1.0.0";
|
||||
|
||||
|
||||
|
||||
#if !flash
|
||||
|
||||
var attributes:lime.ui.WindowAttributes = {
|
||||
allowHighDPI: false,
|
||||
alwaysOnTop: false,
|
||||
borderless: false,
|
||||
// display: 0,
|
||||
element: null,
|
||||
frameRate: 30,
|
||||
#if !web fullscreen: false, #end
|
||||
height: 600,
|
||||
hidden: #if munit true #else false #end,
|
||||
maximized: false,
|
||||
minimized: false,
|
||||
parameters: {},
|
||||
resizable: true,
|
||||
title: "MinimalApplication",
|
||||
width: 800,
|
||||
x: null,
|
||||
y: null,
|
||||
};
|
||||
|
||||
attributes.context = {
|
||||
antialiasing: 0,
|
||||
background: 16777215,
|
||||
colorDepth: 32,
|
||||
depth: true,
|
||||
hardware: true,
|
||||
stencil: true,
|
||||
type: null,
|
||||
vsync: false
|
||||
};
|
||||
|
||||
if (app.window == null)
|
||||
{
|
||||
if (config != null)
|
||||
{
|
||||
for (field in Reflect.fields(config))
|
||||
{
|
||||
if (Reflect.hasField(attributes, field))
|
||||
{
|
||||
Reflect.setField(attributes, field, Reflect.field(config, field));
|
||||
}
|
||||
else if (Reflect.hasField(attributes.context, field))
|
||||
{
|
||||
Reflect.setField(attributes.context, field, Reflect.field(config, field));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if sys
|
||||
lime.system.System.__parseArguments(attributes);
|
||||
#end
|
||||
}
|
||||
|
||||
app.createWindow(attributes);
|
||||
|
||||
#elseif air
|
||||
app.window.title = "MinimalApplication";
|
||||
#else
|
||||
app.window.context.attributes.background = 16777215;
|
||||
app.window.frameRate = 30;
|
||||
#end
|
||||
|
||||
var preloader = getPreloader();
|
||||
app.preloader.onProgress.add (function(loaded, total)
|
||||
{
|
||||
@:privateAccess preloader.update(loaded, total);
|
||||
});
|
||||
app.preloader.onComplete.add(function()
|
||||
{
|
||||
@:privateAccess preloader.start();
|
||||
});
|
||||
|
||||
preloader.onComplete.add(start.bind((cast app.window:openfl.display.Window).stage));
|
||||
|
||||
#if !disable_preloader_assets
|
||||
for (library in ManifestResources.preloadLibraries)
|
||||
{
|
||||
app.preloader.addLibrary(library);
|
||||
}
|
||||
|
||||
for (name in ManifestResources.preloadLibraryNames)
|
||||
{
|
||||
app.preloader.addLibraryName(name);
|
||||
}
|
||||
#end
|
||||
|
||||
app.preloader.load();
|
||||
|
||||
var result = app.exec();
|
||||
|
||||
#if (sys && !ios && !nodejs && !emscripten)
|
||||
lime.system.System.exit(result);
|
||||
#end
|
||||
}
|
||||
|
||||
public static function start(stage:openfl.display.Stage):Void
|
||||
{
|
||||
#if flash
|
||||
ApplicationMain.getEntryPoint();
|
||||
#else
|
||||
if (stage.__uncaughtErrorEvents.__enabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
ApplicationMain.getEntryPoint();
|
||||
|
||||
stage.dispatchEvent(new openfl.events.Event(openfl.events.Event.RESIZE, false, false));
|
||||
|
||||
if (stage.window.fullscreen)
|
||||
{
|
||||
stage.dispatchEvent(new openfl.events.FullScreenEvent(openfl.events.FullScreenEvent.FULL_SCREEN, false, false, true, true));
|
||||
}
|
||||
}
|
||||
catch (e:Dynamic)
|
||||
{
|
||||
#if !display
|
||||
stage.__handleError(e);
|
||||
#end
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplicationMain.getEntryPoint();
|
||||
|
||||
stage.dispatchEvent(new openfl.events.Event(openfl.events.Event.RESIZE, false, false));
|
||||
|
||||
if (stage.window.fullscreen)
|
||||
{
|
||||
stage.dispatchEvent(new openfl.events.FullScreenEvent(openfl.events.FullScreenEvent.FULL_SCREEN, false, false, true, true));
|
||||
}
|
||||
}
|
||||
#end
|
||||
}
|
||||
#end
|
||||
|
||||
macro public static function getEntryPoint()
|
||||
{
|
||||
var hasMain = false;
|
||||
|
||||
switch (Context.follow(Context.getType("Main")))
|
||||
{
|
||||
case TInst(t, params):
|
||||
|
||||
var type = t.get();
|
||||
for (method in type.statics.get())
|
||||
{
|
||||
if (method.name == "main")
|
||||
{
|
||||
hasMain = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasMain)
|
||||
{
|
||||
return Context.parse("@:privateAccess Main.main()", Context.currentPos());
|
||||
}
|
||||
else if (type.constructor != null)
|
||||
{
|
||||
return macro
|
||||
{
|
||||
var current = stage.getChildAt (0);
|
||||
|
||||
if (current == null || !(current is openfl.display.DisplayObjectContainer))
|
||||
{
|
||||
current = new openfl.display.MovieClip();
|
||||
stage.addChild(current);
|
||||
}
|
||||
|
||||
new DocumentClass(cast current);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.fatalError("Main class \"Main\" has neither a static main nor a constructor.", Context.currentPos());
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
Context.fatalError("Main class \"Main\" isn't a class.", Context.currentPos());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
macro public static function getPreloader()
|
||||
{
|
||||
|
||||
return macro
|
||||
{
|
||||
new openfl.display.Preloader(new openfl.display.Preloader.DefaultPreloader());
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#if !macro
|
||||
@:noCompletion @:dox(hide) public static function __init__()
|
||||
{
|
||||
var init = lime.app.Application;
|
||||
|
||||
#if neko
|
||||
// Copy from https://github.com/HaxeFoundation/haxe/blob/development/std/neko/_std/Sys.hx#L164
|
||||
// since Sys.programPath () isn't available in __init__
|
||||
var sys_program_path = {
|
||||
var m = neko.vm.Module.local().name;
|
||||
try
|
||||
{
|
||||
sys.FileSystem.fullPath(m);
|
||||
}
|
||||
catch (e:Dynamic)
|
||||
{
|
||||
// maybe the neko module name was supplied without .n extension...
|
||||
if (!StringTools.endsWith(m, ".n"))
|
||||
{
|
||||
try
|
||||
{
|
||||
sys.FileSystem.fullPath(m + ".n");
|
||||
}
|
||||
catch (e:Dynamic)
|
||||
{
|
||||
m;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var loader = new neko.vm.Loader(untyped $loader);
|
||||
loader.addPath(haxe.io.Path.directory(#if (haxe_ver >= 3.3) sys_program_path #else Sys.executablePath() #end));
|
||||
loader.addPath("./");
|
||||
loader.addPath("@executable_path/");
|
||||
#end
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
#if !macro
|
||||
@:build(DocumentClass.build())
|
||||
@:keep @:dox(hide) class DocumentClass extends Main {}
|
||||
#else
|
||||
class DocumentClass
|
||||
{
|
||||
macro public static function build():Array<Field>
|
||||
{
|
||||
var classType = Context.getLocalClass().get();
|
||||
var searchTypes = classType;
|
||||
|
||||
while (searchTypes != null)
|
||||
{
|
||||
if (searchTypes.module == "openfl.display.DisplayObject" || searchTypes.module == "flash.display.DisplayObject")
|
||||
{
|
||||
var fields = Context.getBuildFields();
|
||||
|
||||
var method = macro
|
||||
{
|
||||
current.addChild(this);
|
||||
super();
|
||||
dispatchEvent(new openfl.events.Event(openfl.events.Event.ADDED_TO_STAGE, false, false));
|
||||
}
|
||||
|
||||
fields.push({ name: "new", access: [ APublic ], kind: FFun({ args: [ { name: "current", opt: false, type: macro :openfl.display.DisplayObjectContainer, value: null } ], expr: method, params: [], ret: macro :Void }), pos: Context.currentPos() });
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
if (searchTypes.superClass != null)
|
||||
{
|
||||
searchTypes = searchTypes.superClass.t.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
searchTypes = null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#end
|
||||
@ -1,29 +0,0 @@
|
||||
package;
|
||||
|
||||
/**
|
||||
* Asset definition class using meta tags instead of project.xml
|
||||
* The AssetMacro will process these declarations at compile time
|
||||
*/
|
||||
@:keep
|
||||
@:build(macros.AssetMacro.build()) // Fix: use fully qualified path with package name
|
||||
class Assets {
|
||||
// Define image assets
|
||||
@:Assets("assets/images/", "images")
|
||||
public static var images:String;
|
||||
|
||||
// Define sound assets
|
||||
@:Assets("assets/sounds/", "sounds")
|
||||
public static var sounds:String;
|
||||
|
||||
// Define font assets
|
||||
@:Assets("assets/fonts/", "fonts")
|
||||
public static var fonts:String;
|
||||
|
||||
// Define data assets
|
||||
@:Assets("assets/data/", "data")
|
||||
public static var data:String;
|
||||
|
||||
// You can also define individual files if needed
|
||||
// @:Assets("assets/images/logo.png", "images", "logo.png")
|
||||
// public static var logo:String;
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
package;
|
||||
|
||||
import openfl.display.Sprite;
|
||||
import openfl.Lib;
|
||||
import sys.FileSystem;
|
||||
import Assets;
|
||||
|
||||
class Main extends Sprite {
|
||||
public function new() {
|
||||
super();
|
||||
|
||||
Lib.current.addChild(this);
|
||||
|
||||
// Draw background to verify app is running
|
||||
graphics.beginFill(0x3498db, 1);
|
||||
graphics.drawRect(0, 0, 800, 600);
|
||||
graphics.endFill();
|
||||
|
||||
|
||||
// Get data asset library
|
||||
var dataLibrary = openfl.utils.Assets.getLibrary("data");
|
||||
var imageLibrary = openfl.utils.Assets.getLibrary("images");
|
||||
if (dataLibrary == null) {
|
||||
trace("Data library not found");
|
||||
return;
|
||||
}
|
||||
if (imageLibrary == null) {
|
||||
trace("Image library not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// test a data asset
|
||||
var testAsset = dataLibrary.getText("img/placeholder.txt");
|
||||
trace("Successfully loaded asset: " + testAsset);
|
||||
// test an image asset
|
||||
var testImage = imageLibrary.getImage("logo.png");
|
||||
trace("Successfully loaded image asset: " + testImage);
|
||||
var bitmapData = openfl.display.BitmapData.fromImage(testImage);
|
||||
this.addChild(new openfl.display.Bitmap(bitmapData));
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
package;
|
||||
|
||||
#if sys
|
||||
import haxe.io.Path;
|
||||
import openfl.utils.AssetLibrary;
|
||||
import openfl.utils.AssetManifest;
|
||||
import openfl.utils.Assets as LimeAssets;
|
||||
import sys.FileSystem;
|
||||
|
||||
@:access(lime.utils.Assets)
|
||||
@:keep class ManifestResources {
|
||||
public static var preloadLibraries:Array<AssetLibrary> = [];
|
||||
public static var preloadLibraryNames:Array<String> = [];
|
||||
public static var rootPath:String;
|
||||
|
||||
public static function init(config:Dynamic):Void {
|
||||
rootPath = "";
|
||||
|
||||
var manifestDirPath = Path.join(["manifest"]);
|
||||
var manifests = FileSystem.readDirectory(manifestDirPath);
|
||||
for (manifest in manifests) {
|
||||
trace(manifest);
|
||||
if (StringTools.endsWith(manifest, ".json")) {
|
||||
var manifestPath = Path.join([manifestDirPath, manifest]);
|
||||
loadManifest(manifestPath, manifest.split(".json")[0]);
|
||||
}
|
||||
else{
|
||||
throw("we eating shit tonight");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function loadManifest(manifestPath, manifestName) {
|
||||
if (!FileSystem.exists(manifestPath)) {
|
||||
trace("Manifest file not found: " + manifestPath);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var manifest = AssetManifest.fromFile(manifestPath);
|
||||
if (manifest != null) {
|
||||
var library = AssetLibrary.fromManifest(manifest);
|
||||
if (library != null) {
|
||||
trace("Library: `"+library+"`");
|
||||
trace("manifestName: `"+manifestName+"`");
|
||||
preloadLibraries.push(library);
|
||||
preloadLibraryNames.push(manifestName);
|
||||
// LimeAssets.registerLibrary(manifestName, library);
|
||||
openfl.utils.Assets.registerLibrary(manifestName, library);
|
||||
}
|
||||
}
|
||||
} catch (e:Dynamic) {
|
||||
trace("Error loading asset manifest: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
#end
|
||||
@ -1,269 +0,0 @@
|
||||
package macros;
|
||||
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Type;
|
||||
import sys.FileSystem;
|
||||
import sys.io.File;
|
||||
import haxe.Json;
|
||||
using StringTools;
|
||||
|
||||
/**
|
||||
* Macro for processing @:Assets meta tags and building asset manifests
|
||||
*/
|
||||
@:keep
|
||||
class AssetMacro {
|
||||
// Track all assets processed
|
||||
private static var assetMap:Map<String, Array<{id:String, path:String, size:Int, type:String}>> = new Map();
|
||||
|
||||
/**
|
||||
* Entry point for the macro - called during compilation
|
||||
*/
|
||||
public static function build():Array<Field> {
|
||||
#if macro
|
||||
Sys.println("AssetMacro.build() called - macro is running");
|
||||
Context.warning("AssetMacro is running", Context.currentPos());
|
||||
#end
|
||||
|
||||
// Get current build context
|
||||
var fields = Context.getBuildFields();
|
||||
var localClass = Context.getLocalClass().get();
|
||||
|
||||
#if macro
|
||||
Sys.println("Processing class: " + localClass.name);
|
||||
#end
|
||||
|
||||
// Get the output directory from build.hxml
|
||||
var outputDir = getOutputDir();
|
||||
var manifestDir = outputDir + "/manifest";
|
||||
var assetsOutputDir = outputDir + "/assets";
|
||||
|
||||
// Create directories if they don't exist
|
||||
createDirectory(manifestDir);
|
||||
createDirectory(assetsOutputDir);
|
||||
|
||||
// Process asset fields
|
||||
for (field in fields) {
|
||||
processField(field, assetsOutputDir);
|
||||
}
|
||||
|
||||
// Build the manifest files
|
||||
buildManifests(manifestDir);
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a field for @:Assets meta
|
||||
*/
|
||||
private static function processField(field:Field, assetsOutputDir:String) {
|
||||
// Look for our custom meta
|
||||
var assetsMeta = findAssetsMeta(field.meta);
|
||||
if (assetsMeta == null) return;
|
||||
|
||||
var params = assetsMeta.params;
|
||||
if (params.length < 2) {
|
||||
Context.error("@:Assets meta requires at least 2 parameters: source path and library name", assetsMeta.pos);
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract parameters
|
||||
var sourcePath = extractString(params[0]);
|
||||
var libraryName = extractString(params[1]);
|
||||
var targetPath = params.length > 2 ? extractString(params[2]) : null;
|
||||
|
||||
// Process directory or single file
|
||||
if (FileSystem.exists(sourcePath)) {
|
||||
if (FileSystem.isDirectory(sourcePath)) {
|
||||
processDirectory(sourcePath, libraryName, assetsOutputDir);
|
||||
} else {
|
||||
processSingleFile(sourcePath, libraryName, targetPath, assetsOutputDir);
|
||||
}
|
||||
} else {
|
||||
Context.warning('Asset source not found: ${sourcePath}', field.pos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a directory of assets
|
||||
*/
|
||||
private static function processDirectory(sourcePath:String, libraryName:String, assetsOutputDir:String) {
|
||||
if (!sourcePath.endsWith("/")) sourcePath += "/";
|
||||
var targetDir = assetsOutputDir + "/" + libraryName + "/";
|
||||
createDirectory(targetDir);
|
||||
|
||||
function scanDir(path:String, relPath:String = "") {
|
||||
if (!FileSystem.exists(path)) return;
|
||||
|
||||
for (entry in FileSystem.readDirectory(path)) {
|
||||
var fullPath = path + entry;
|
||||
var relAssetPath = relPath + entry;
|
||||
|
||||
if (FileSystem.isDirectory(fullPath)) {
|
||||
var newRelPath = relPath + entry + "/";
|
||||
var newTargetDir = targetDir + newRelPath;
|
||||
createDirectory(newTargetDir);
|
||||
scanDir(fullPath + "/", newRelPath);
|
||||
} else {
|
||||
// Copy file and register in asset map
|
||||
var targetPath = targetDir + relAssetPath;
|
||||
try {
|
||||
File.copy(fullPath, targetPath);
|
||||
|
||||
// Register asset
|
||||
if (!assetMap.exists(libraryName)) {
|
||||
assetMap.set(libraryName, []);
|
||||
}
|
||||
|
||||
assetMap.get(libraryName).push({
|
||||
id: relAssetPath,
|
||||
path: relAssetPath,
|
||||
size: FileSystem.stat(fullPath).size,
|
||||
type: getAssetType(fullPath)
|
||||
});
|
||||
} catch (e) {
|
||||
Context.warning('Failed to copy asset: ${fullPath} -> ${targetPath}. Error: ${e}', Context.currentPos());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scanDir(sourcePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single file asset
|
||||
*/
|
||||
private static function processSingleFile(sourcePath:String, libraryName:String, targetPath:String, assetsOutputDir:String) {
|
||||
var targetDir = assetsOutputDir + "/" + libraryName + "/";
|
||||
createDirectory(targetDir);
|
||||
|
||||
var fileName = targetPath != null ? targetPath : sourcePath.split("/").pop();
|
||||
var destPath = targetDir + fileName;
|
||||
|
||||
try {
|
||||
File.copy(sourcePath, destPath);
|
||||
|
||||
// Register asset
|
||||
if (!assetMap.exists(libraryName)) {
|
||||
assetMap.set(libraryName, []);
|
||||
}
|
||||
|
||||
assetMap.get(libraryName).push({
|
||||
id: fileName,
|
||||
path: fileName,
|
||||
size: FileSystem.stat(sourcePath).size,
|
||||
type: getAssetType(sourcePath)
|
||||
});
|
||||
} catch (e) {
|
||||
Context.warning('Failed to copy asset: ${sourcePath} -> ${destPath}. Error: ${e}', Context.currentPos());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build manifest JSON files for each library
|
||||
*/
|
||||
private static function buildManifests(manifestDir:String) {
|
||||
// Create an array to hold all assets for the default manifest
|
||||
var allAssets = [];
|
||||
|
||||
// First, process individual library manifests
|
||||
for (library in assetMap.keys()) {
|
||||
var assets = assetMap.get(library);
|
||||
var manifest = {
|
||||
name: library,
|
||||
assets: assets,
|
||||
rootPath: "../assets/" + library + "/"
|
||||
};
|
||||
|
||||
// Add assets to combined collection with library prefix
|
||||
for (asset in assets) {
|
||||
allAssets.push({
|
||||
id: library + "/" + asset.id,
|
||||
path: library + "/" + asset.path,
|
||||
size: asset.size,
|
||||
type: asset.type
|
||||
});
|
||||
}
|
||||
|
||||
var manifestPath = manifestDir + "/" + library + ".json";
|
||||
try {
|
||||
var content = Json.stringify(manifest, null, " ");
|
||||
File.saveContent(manifestPath, content);
|
||||
Context.info('Built manifest: ${manifestPath} with ${assets.length} assets', Context.currentPos());
|
||||
} catch (e) {
|
||||
Context.warning('Failed to write manifest: ${manifestPath}. Error: ${e}', Context.currentPos());
|
||||
}
|
||||
}
|
||||
|
||||
// Create the default manifest with all assets
|
||||
var defaultManifest = {
|
||||
name: "default",
|
||||
assets: allAssets,
|
||||
rootPath: "../assets/"
|
||||
};
|
||||
|
||||
// Save the default manifest
|
||||
var defaultManifestPath = manifestDir + "/default.json";
|
||||
try {
|
||||
var content = Json.stringify(defaultManifest, null, " ");
|
||||
File.saveContent(defaultManifestPath, content);
|
||||
Context.info('Built default manifest: ${defaultManifestPath} with ${allAssets.length} assets', Context.currentPos());
|
||||
} catch (e) {
|
||||
Context.warning('Failed to write default manifest: ${defaultManifestPath}. Error: ${e}', Context.currentPos());
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
private static function getOutputDir():String {
|
||||
// Try to parse the --cpp argument from the build.hxml
|
||||
var args = Sys.args();
|
||||
for (i in 0...args.length) {
|
||||
if (args[i] == "--cpp" && i < args.length - 1) {
|
||||
return args[i + 1];
|
||||
}
|
||||
}
|
||||
return "bin/cpp"; // Default output directory
|
||||
}
|
||||
|
||||
private static function createDirectory(path:String) {
|
||||
if (!FileSystem.exists(path)) {
|
||||
try {
|
||||
FileSystem.createDirectory(path);
|
||||
} catch (e) {
|
||||
Context.warning('Failed to create directory: ${path}. Error: ${e}', Context.currentPos());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function findAssetsMeta(metaAccess:Array<MetadataEntry>):MetadataEntry {
|
||||
if (metaAccess == null) return null;
|
||||
|
||||
for (meta in metaAccess) {
|
||||
if (meta.name == ":Assets" || meta.name == "Assets") {
|
||||
return meta;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function extractString(expr:Expr):String {
|
||||
switch (expr.expr) {
|
||||
case EConst(CString(s)): return s;
|
||||
default: Context.error("Expected string literal", expr.pos);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function getAssetType(path:String):String {
|
||||
var ext = path.split(".").pop().toLowerCase();
|
||||
return (switch (ext) {
|
||||
case "jpg", "jpeg", "png", "gif", "bmp": "image";
|
||||
case "mp3", "ogg", "wav": "sound";
|
||||
case "ttf", "otf": "font";
|
||||
case "json", "xml", "txt": "text";
|
||||
default: "binary";
|
||||
}).toUpperCase();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user