diff --git a/data/font/Lucida Console.fnt b/data/font/Lucida Console.fnt
new file mode 100644
index 00000000..a3836141
--- /dev/null
+++ b/data/font/Lucida Console.fnt
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/data/font/LucidaConsole.png b/data/font/LucidaConsole.png
new file mode 100644
index 00000000..3e188bce
Binary files /dev/null and b/data/font/LucidaConsole.png differ
diff --git a/src/AudioManager.hx b/src/AudioManager.hx
index 108bb753..9fc59ea8 100644
--- a/src/AudioManager.hx
+++ b/src/AudioManager.hx
@@ -10,6 +10,7 @@ import src.Settings;
import hxd.snd.ChannelGroup;
import src.Resource;
import src.ResourceLoaderWorker;
+import src.Console;
class AudioManager {
static var manager:hxd.snd.Manager;
@@ -23,6 +24,7 @@ class AudioManager {
static var currentMusicResource:Resource;
public static function init() {
+ Console.log("Initializing Audio System");
AudioManager.manager = hxd.snd.Manager.get();
AudioManager.soundGroup = new SoundGroup("sound");
soundGroup.volume = Settings.optionsSettings.soundVolume;
diff --git a/src/Console.hx b/src/Console.hx
new file mode 100644
index 00000000..186f5186
--- /dev/null
+++ b/src/Console.hx
@@ -0,0 +1,88 @@
+package src;
+
+@:publicFields
+class ConsoleEntry {
+ var time:Float;
+ var type:String;
+ var text:String;
+
+ public function new(time:Float, type:String, text:String) {
+ this.time = time;
+ this.type = type;
+ this.text = text;
+ }
+}
+
+class Console {
+ public static var instance:Console;
+
+ public var entries:Array;
+
+ var consumers:ArrayVoid>;
+ var timeSinceStart:Float;
+
+ public function new() {
+ if (instance == null) {
+ instance = this;
+ }
+ entries = [];
+ consumers = [];
+ timeSinceStart = haxe.Timer.stamp();
+ }
+
+ public function clear() {
+ entries = [];
+ }
+
+ function getTime() {
+ return Std.int((haxe.Timer.stamp() - timeSinceStart) * 1000) / 1000;
+ }
+
+ function addEntry(type:String, msg:String) {
+ var e = new ConsoleEntry(getTime(), type, msg);
+ entries.push(e);
+ for (c in consumers) {
+ c(e);
+ }
+ }
+
+ function _log(t:String) {
+ addEntry("log", t);
+ }
+
+ function _warn(t:String) {
+ addEntry("warn", t);
+ }
+
+ function _error(t:String) {
+ addEntry("error", t);
+ }
+
+ function _debug(t:String) {
+ addEntry("debug", t);
+ }
+
+ public static function log(t:String) {
+ instance._log(t);
+ }
+
+ public static function warn(t:String) {
+ instance._warn(t);
+ }
+
+ public static function error(t:String) {
+ instance._error(t);
+ }
+
+ public static function debug(t:String) {
+ instance._debug(t);
+ }
+
+ public static function addConsumer(c:ConsoleEntry->Void) {
+ instance.consumers.push(c);
+ }
+
+ public static function removeConsumer(c:ConsoleEntry->Void) {
+ instance.consumers.remove(c);
+ }
+}
diff --git a/src/Main.hx b/src/Main.hx
index 88303fa9..1034247c 100644
--- a/src/Main.hx
+++ b/src/Main.hx
@@ -1,5 +1,6 @@
package;
+import src.Console;
import hxd.Key;
import src.Util;
import src.ResourceLoader;
@@ -23,6 +24,8 @@ class Main extends hxd.App {
var loaded:Bool = false;
+ var frameByFrame:Bool = false;
+
override function init() {
super.init();
@@ -56,6 +59,12 @@ class Main extends hxd.App {
});
#end
+ new Console(); // Singleton
+ Console.log("Initializing MBHaxe");
+ #if hl
+ Console.log("System: " + Sys.systemName());
+ #end
+
Settings.init();
ResourceLoader.init(s2d, () -> {
AudioManager.init();
diff --git a/src/MarbleGame.hx b/src/MarbleGame.hx
index f7b028b3..01297ca3 100644
--- a/src/MarbleGame.hx
+++ b/src/MarbleGame.hx
@@ -1,5 +1,6 @@
package src;
+import gui.ConsoleDlg;
import src.Replay;
import touch.TouchInput;
import src.ResourceLoader;
@@ -17,6 +18,7 @@ import gui.Canvas;
import src.Util;
import src.ProfilerUI;
import src.Settings;
+import src.Console;
@:publicFields
class MarbleGame {
@@ -36,7 +38,11 @@ class MarbleGame {
var touchInput:TouchInput;
+ var consoleShown:Bool = false;
+ var console:ConsoleDlg;
+
public function new(scene2d:h2d.Scene, scene:h3d.scene.Scene) {
+ Console.log("Initializing the game...");
canvas = new Canvas(scene2d, cast this);
this.scene = scene;
this.scene2d = scene2d;
@@ -183,6 +189,19 @@ class MarbleGame {
}
}
if (canvas != null) {
+ if (Key.isPressed(Key.QWERTY_TILDE)) {
+ consoleShown = !consoleShown;
+ if (consoleShown) {
+ if (console == null)
+ console = new ConsoleDlg();
+ @:privateAccess console.isShowing = true;
+ canvas.pushDialog(console);
+ } else {
+ @:privateAccess console.isShowing = false;
+ canvas.popDialog(console, false);
+ }
+ }
+
var mouseState:MouseState = {
position: new Vector(canvas.scene2d.mouseX, canvas.scene2d.mouseY)
}
@@ -193,6 +212,7 @@ class MarbleGame {
public function handlePauseGame() {
if (paused && world._ready) {
+ Console.log("Game paused");
world.setCursorLock(false);
exitGameDlg = new ExitGameDlg((sender) -> {
canvas.popDialog(exitGameDlg);
@@ -210,6 +230,7 @@ class MarbleGame {
canvas.pushDialog(exitGameDlg);
} else {
if (world._ready) {
+ Console.log("Game unpaused");
if (exitGameDlg != null)
canvas.popDialog(exitGameDlg);
world.setCursorLock(true);
@@ -218,6 +239,7 @@ class MarbleGame {
}
public function quitMission() {
+ Console.log("Quitting mission");
world.setCursorLock(false);
paused = false;
var pmg = new PlayMissionGui();
diff --git a/src/MarbleWorld.hx b/src/MarbleWorld.hx
index 6d6cb97b..8859c8b0 100644
--- a/src/MarbleWorld.hx
+++ b/src/MarbleWorld.hx
@@ -87,6 +87,7 @@ import src.Resource;
import src.ProfilerUI;
import src.ResourceLoaderWorker;
import haxe.io.Path;
+import src.Console;
class MarbleWorld extends Scheduler {
public var collisionWorld:CollisionWorld;
@@ -197,6 +198,7 @@ class MarbleWorld extends Scheduler {
}
public function initLoading() {
+ Console.log("*** LOADING MISSION: " + mission.path);
this.loadingGui = new LoadingGui(this.mission.title, this.mission.game);
MarbleGame.canvas.setContent(this.loadingGui);
@@ -260,6 +262,7 @@ class MarbleWorld extends Scheduler {
}
public function initScene(onFinish:Void->Void) {
+ Console.log("Starting scene");
this.collisionWorld = new CollisionWorld();
this.playGui = new PlayGui();
this.instanceManager = new InstanceManager(scene);
@@ -330,6 +333,7 @@ class MarbleWorld extends Scheduler {
}
public function initMarble(onFinish:Void->Void) {
+ Console.log("Initializing marble");
var worker = new ResourceLoaderWorker(onFinish);
var marblefiles = [
"particles/star.png",
@@ -390,6 +394,7 @@ class MarbleWorld extends Scheduler {
}
public function start() {
+ Console.log("LEVEL START");
restart(true);
for (interior in this.interiors)
interior.onLevelStart();
@@ -502,20 +507,25 @@ class MarbleWorld extends Scheduler {
AudioManager.playSound(ResourceLoader.getResource('data/sound/spawn.wav', ResourceLoader.getAudio, this.soundResources));
+ Console.log("State Start");
this.clearSchedule();
this.schedule(0.5, () -> {
// setCenterText('ready');
+ Console.log("State Ready");
AudioManager.playSound(ResourceLoader.getResource('data/sound/ready.wav', ResourceLoader.getAudio, this.soundResources));
return 0;
});
this.schedule(2, () -> {
// setCenterText('set');
+ Console.log("State Set");
AudioManager.playSound(ResourceLoader.getResource('data/sound/set.wav', ResourceLoader.getAudio, this.soundResources));
return 0;
});
this.schedule(3.5, () -> {
// setCenterText('go');
+ Console.log("State Go");
AudioManager.playSound(ResourceLoader.getResource('data/sound/go.wav', ResourceLoader.getAudio, this.soundResources));
+ Console.log("State Play");
return 0;
});
@@ -570,6 +580,7 @@ class MarbleWorld extends Scheduler {
this.addPathedInterior(pathedInterior, () -> {
if (pathedInterior == null) {
fwd();
+ Console.error("Unable to load pathed interior");
return;
}
@@ -736,6 +747,7 @@ class MarbleWorld extends Scheduler {
else if (["clear", "cloudy", "dusk", "wintry"].contains(dataBlockLowerCase))
shape = new shapes.Sky(dataBlockLowerCase);
else {
+ Console.error("Unable to create static shape with data block '" + element.datablock + "'");
onFinish();
return;
}
@@ -849,6 +861,7 @@ class MarbleWorld extends Scheduler {
else if (["clear", "cloudy", "dusk", "wintry"].contains(dataBlockLowerCase))
shape = new shapes.Sky(dataBlockLowerCase);
else {
+ Console.error("Unknown item: " + element.datablock);
onFinish();
return;
}
@@ -920,12 +933,14 @@ class MarbleWorld extends Scheduler {
var shapeName = element.shapename;
var index = shapeName.indexOf('data/');
if (index == -1) {
+ Console.error("Unable to parse shape path: " + shapeName);
onFinish();
return;
}
var dtsPath = 'data/' + shapeName.substring(index + 'data/'.length);
if (ResourceLoader.getProperFilepath(dtsPath) == "") {
+ Console.error("DTS path does not exist: " + dtsPath);
onFinish();
return;
}
@@ -969,6 +984,7 @@ class MarbleWorld extends Scheduler {
}
public function addParticleEmitterNode(element:MissionElementParticleEmitterNode) {
+ Console.warn("Unimplemented method addParticleEmitterNode");
// TODO THIS SHIT
}
@@ -1265,6 +1281,7 @@ class MarbleWorld extends Scheduler {
return;
if (!_ready && !postInited) {
postInited = true;
+ Console.log("Finished loading, starting mission");
postInit();
}
}
@@ -1623,6 +1640,7 @@ class MarbleWorld extends Scheduler {
}
function showFinishScreen() {
+ Console.log("State End");
var egg:EndGameGui = null;
#if js
var pointercontainer = js.Browser.document.querySelector("#pointercontainer");
diff --git a/src/Mission.hx b/src/Mission.hx
index e1003ccf..86ff1361 100644
--- a/src/Mission.hx
+++ b/src/Mission.hx
@@ -14,6 +14,7 @@ import src.ResourceLoader;
import hxd.res.Image;
import src.Resource;
import src.Util;
+import src.Console;
class Mission {
public var root:MissionElementSimGroup;
@@ -146,11 +147,13 @@ class Mission {
});
return imgFileEntry.path;
}
+ Console.error("Preview image not found for " + this.path);
var img = new BitmapData(1, 1);
img.setPixel(0, 0, 0);
onLoaded(Tile.fromBitmap(img));
return null;
} else {
+ Console.error("Preview image not found for " + this.path);
var img = new BitmapData(1, 1);
img.setPixel(0, 0, 0);
onLoaded(Tile.fromBitmap(img));
@@ -181,6 +184,7 @@ class Mission {
return path;
if (ResourceLoader.fileSystem.exists(dirpath + fname))
return dirpath + fname;
+ Console.error("Interior resource not found: " + rawElementPath);
return "";
}
diff --git a/src/Resource.hx b/src/Resource.hx
index aa191680..14292ee6 100644
--- a/src/Resource.hx
+++ b/src/Resource.hx
@@ -1,5 +1,7 @@
package src;
+import src.Console;
+
class Resource {
public var resource:T;
public var identifier:String;
@@ -23,6 +25,7 @@ class Resource {
public function release() {
this.referenceCount--;
if (this.referenceCount == 0) {
+ Console.log("Freeing: " + identifier);
disposeFunc(this.resource);
this.resourceMap.remove(this.identifier);
// trace('Releasing Resource ${this.identifier}');
diff --git a/src/ResourceLoader.hx b/src/ResourceLoader.hx
index 471296b1..13688eb1 100644
--- a/src/ResourceLoader.hx
+++ b/src/ResourceLoader.hx
@@ -19,6 +19,7 @@ import src.Resource;
import src.ResourceLoaderWorker;
import fs.TorqueFileSystem;
import src.Settings;
+import src.Console;
class ResourceLoader {
#if (hl && !android)
@@ -46,6 +47,10 @@ class ResourceLoader {
// static var threadPool:FixedThreadPool = new FixedThreadPool(4);
public static function init(scene2d:h2d.Scene, onLoadedFunc:Void->Void) {
+ Console.log("Initializing filesystem");
+ #if hl
+ Console.log("Filesystem Path: " + @:privateAccess cast(fileSystem, TorqueFileSystem).baseDir);
+ #end
hxd.res.Resource.LIVE_UPDATE = false; // Disable live update to save frames
@:privateAccess hxd.res.Image.ENABLE_AUTO_WATCH = false;
@:privateAccess hxd.res.Sound.ENABLE_AUTO_WATCH = false;
@@ -275,6 +280,7 @@ class ResourceLoader {
if (interiorResources.exists(path))
return interiorResources.get(path);
else {
+ Console.log("Load Interior: " + path);
var itr:Dif;
// var lock = new Lock();
// threadPool.run(() -> {
@@ -293,6 +299,7 @@ class ResourceLoader {
if (dtsResources.exists(path))
return dtsResources.get(path);
else {
+ Console.log("Load DTS: " + path);
var dts = new DtsFile();
// var lock = new Lock();
// threadPool.run(() -> {
@@ -311,6 +318,7 @@ class ResourceLoader {
if (textureCache.exists(path))
return textureCache.get(path);
if (fileSystem.exists(path)) {
+ Console.log("Load Texture: " + path);
var img = loader.load(path).toImage();
Image.setupTextureFlags = (texObj) -> {
texObj.flags.set(MipMapped);
@@ -333,6 +341,7 @@ class ResourceLoader {
if (imageCache.exists(path))
return imageCache.get(path);
if (fileSystem.exists(path)) {
+ Console.log("Load Image: " + path);
var tex = loader.load(path).toImage();
var imageresource = new Resource(tex, path, imageCache, img -> {});
imageCache.set(path, imageresource);
@@ -348,6 +357,7 @@ class ResourceLoader {
if (audioCache.exists(path))
return audioCache.get(path);
if (fileSystem.exists(path)) {
+ Console.log("Load Audio: " + path);
var snd = loader.load(path).toSound();
// @:privateAccess snd.watchCallb();
var audioresource = new Resource(snd, path, audioCache, snd -> snd.dispose());
diff --git a/src/Settings.hx b/src/Settings.hx
index c4187a6f..9454a892 100644
--- a/src/Settings.hx
+++ b/src/Settings.hx
@@ -16,6 +16,7 @@ import haxe.io.Path;
import src.ResourceLoader;
import haxe.Json;
import src.Util;
+import src.Console;
typedef Score = {
var name:String;
@@ -268,6 +269,7 @@ class Settings {
}
public static function load() {
+ Console.log("Loading settings");
var settingsExists = false;
#if hl
settingsExists = FileSystem.exists(Path.join([settingsDir, "settings.json"]));
@@ -334,6 +336,7 @@ class Settings {
#end
highscoreName = json.highscoreName;
} else {
+ Console.warn("Settings file does not exist");
save();
}
#if hl
@@ -376,6 +379,9 @@ class Settings {
canvasElement.style.height = "100%";
#end
+ Console.log("Window resized to " + Settings.optionsSettings.screenWidth + "x" + Settings.optionsSettings.screenHeight + " (Zoom " + zoomRatio +
+ ")");
+
MarbleGame.canvas.scene2d.scaleMode = Zoom(zoomRatio);
MarbleGame.canvas.render(MarbleGame.canvas.scene2d);
diff --git a/src/gui/Canvas.hx b/src/gui/Canvas.hx
index f9be47b6..9414e126 100644
--- a/src/gui/Canvas.hx
+++ b/src/gui/Canvas.hx
@@ -1,5 +1,6 @@
package gui;
+import src.Console;
import src.MarbleGame;
import h3d.Vector;
import h2d.Scene;
@@ -12,6 +13,7 @@ class Canvas extends GuiControl {
public function new(scene, marbleGame:MarbleGame) {
super();
+ Console.log("Creating canvas");
this.scene2d = scene;
this.marbleGame = marbleGame;
diff --git a/src/gui/ConsoleDlg.hx b/src/gui/ConsoleDlg.hx
new file mode 100644
index 00000000..94ec1e68
--- /dev/null
+++ b/src/gui/ConsoleDlg.hx
@@ -0,0 +1,108 @@
+package gui;
+
+import src.Console.ConsoleEntry;
+import h2d.Graphics;
+import h2d.Tile;
+import h3d.mat.Texture;
+import hxd.res.BitmapFont;
+import src.ResourceLoader;
+import h3d.Vector;
+import src.Settings;
+import src.Console;
+
+class ConsoleDlg extends GuiControl {
+ var onConsoleEntry:(e:ConsoleEntry) -> Void;
+ var isShowing = false;
+
+ public function new() {
+ super();
+ this.position = new Vector(0, 0);
+ this.extent = new Vector(640, 370);
+
+ var white = Tile.fromColor(0xFFFFFF);
+ var black = Tile.fromColor(0x000000);
+ var consoleWhite = new GuiImage(white);
+ consoleWhite.position = new Vector(0, 0);
+ consoleWhite.extent = new Vector(640, 350);
+ consoleWhite.horizSizing = Width;
+ consoleWhite.vertSizing = Top;
+ this.addChild(consoleWhite);
+
+ var scroll = new GuiConsoleScrollCtrl(ResourceLoader.getResource("data/ui/common/darkscroll.png", ResourceLoader.getImage, this.imageResources)
+ .toTile());
+ scroll.position = new Vector(0, 0);
+ scroll.extent = new Vector(640, 350);
+ scroll.horizSizing = Width;
+ scroll.vertSizing = Height;
+
+ consoleWhite.addChild(scroll);
+
+ var consolefontdata = ResourceLoader.getFileEntry("data/font/Lucida Console.fnt");
+ var consoleb = new BitmapFont(consolefontdata.entry);
+ @:privateAccess consoleb.loader = ResourceLoader.loader;
+ var consoleb = consoleb.toSdfFont(cast 11.7 * Settings.uiScale, MultiChannel);
+
+ function mlFontLoader(text:String) {
+ return null;
+ }
+
+ var consoleContent = new GuiMLText(consoleb, mlFontLoader);
+ consoleContent.position = new Vector(0, 0);
+ consoleContent.extent = new Vector(640, 350);
+ consoleContent.horizSizing = Width;
+ consoleContent.vertSizing = Top;
+ consoleContent.text.textColor = 0;
+ consoleContent.scrollable = true;
+ scroll.addChild(consoleContent);
+
+ consoleContent.text.text = "";
+
+ // Generate console text
+ for (entry in Console.instance.entries) {
+ var txt = '[${entry.time}] ${StringTools.htmlEscape(entry.text)}
';
+ consoleContent.text.text += txt;
+ }
+
+ scroll.setScrollMax(consoleContent.text.textHeight);
+ scroll.updateScrollVisual();
+
+ var arial14fontdata = ResourceLoader.getFileEntry("data/font/arial.fnt");
+ var arial14b = new BitmapFont(arial14fontdata.entry);
+ @:privateAccess arial14b.loader = ResourceLoader.loader;
+ var arial14 = arial14b.toSdfFont(cast 14 * Settings.uiScale, MultiChannel);
+
+ var bord = new GuiImage(black);
+ bord.position = new Vector(0, 350);
+ bord.extent = new Vector(640, 18);
+ bord.horizSizing = Width;
+ consoleContent.addChild(bord);
+
+ var consoleInput = new GuiTextInput(arial14);
+ consoleInput.position = new Vector(1, 351);
+ consoleInput.extent = new Vector(638, 20);
+ consoleInput.horizSizing = Width;
+ consoleInput.vertSizing = Top;
+ consoleInput.text.textColor = 0;
+ consoleInput.text.backgroundColor = 0xFFFFFFFF;
+ consoleInput.text.selectionColor.set(1, 1, 1);
+ consoleInput.text.selectionTile = h2d.Tile.fromColor(0x808080, 0, hxd.Math.ceil(consoleInput.text.font.lineHeight));
+
+ consoleContent.addChild(consoleInput);
+
+ onConsoleEntry = (e) -> {
+ var txt = '[${e.time}] ${StringTools.htmlEscape(e.text)}
';
+ consoleContent.text.text += txt;
+ if (isShowing) {
+ scroll.setScrollMax(consoleContent.text.textHeight);
+ haxe.Timer.delay(() -> scroll.setScrollPercentage(1), 1);
+ }
+ };
+
+ Console.addConsumer(onConsoleEntry);
+ }
+
+ override function dispose() {
+ super.dispose();
+ Console.removeConsumer(onConsoleEntry);
+ }
+}
diff --git a/src/gui/GuiConsoleScrollCtrl.hx b/src/gui/GuiConsoleScrollCtrl.hx
new file mode 100644
index 00000000..14653fae
--- /dev/null
+++ b/src/gui/GuiConsoleScrollCtrl.hx
@@ -0,0 +1,273 @@
+package gui;
+
+import h2d.Bitmap;
+import h3d.Vector;
+import src.Settings;
+import gui.GuiControl.MouseState;
+import h2d.Interactive;
+import h2d.Scene;
+import h2d.Tile;
+import h2d.Graphics;
+import src.MarbleGame;
+import src.Util;
+
+class GuiConsoleScrollCtrl extends GuiControl {
+ public var scrollY:Float = 0;
+
+ var maxScrollY:Float;
+
+ var scrollBarY:Graphics;
+ var scrollTrack:Bitmap;
+
+ var scrollTopTile:Tile;
+ var scrollBottomTile:Tile;
+ var scrollFillTile:Tile;
+
+ var scrollTopPressedTile:Tile;
+ var scrollBottomPressedTile:Tile;
+ var scrollFillPressedTile:Tile;
+
+ var scrollTrackTile:Tile;
+
+ var clickInteractive:Interactive;
+
+ var pressed:Bool = false;
+ var dirty:Bool = true;
+ var prevMousePos:Vector;
+
+ var scrollUpButton:GuiButton;
+ var scrollDownButton:GuiButton;
+
+ public function new(scrollBar:Tile) {
+ super();
+ this.scrollTopTile = scrollBar.sub(0, 37, 18, 8);
+ this.scrollBottomTile = scrollBar.sub(0, 55, 18, 8);
+ this.scrollFillTile = scrollBar.sub(0, 46, 18, 1);
+ this.scrollTopPressedTile = scrollBar.sub(19, 37, 18, 8);
+ this.scrollBottomPressedTile = scrollBar.sub(19, 46, 18, 8);
+ this.scrollFillPressedTile = scrollBar.sub(19, 55, 18, 1);
+ this.scrollTrackTile = scrollBar.sub(0, 64, 18, 1);
+ var scrollUpTile = scrollBar.sub(0, 1, 18, 17);
+ var scrollDownTile = scrollBar.sub(0, 19, 18, 17);
+ var scrollUpPressedTile = scrollBar.sub(19, 1, 18, 17);
+ var scrollDownPressedTile = scrollBar.sub(19, 19, 18, 17);
+ var scrollUpDisabledTile = scrollBar.sub(38, 1, 18, 17);
+ var scrollDownDisabledTile = scrollBar.sub(38, 19, 18, 17);
+
+ scrollUpButton = new GuiButton([scrollUpTile, scrollUpTile, scrollUpPressedTile, scrollUpDisabledTile]);
+ scrollUpButton.position = new Vector(0, 0);
+ scrollUpButton.extent = new Vector(18, 17);
+ scrollUpButton.horizSizing = Right;
+ scrollUpButton.pressedAction = (e) -> {
+ this.scrollY -= 10;
+ this.updateScrollVisual();
+ }
+ this.addChild(scrollUpButton);
+
+ scrollDownButton = new GuiButton([scrollDownTile, scrollDownTile, scrollDownPressedTile, scrollDownDisabledTile]);
+ scrollDownButton.position = new Vector(0, 0);
+ scrollDownButton.extent = new Vector(18, 17);
+ scrollDownButton.horizSizing = Right;
+ scrollDownButton.pressedAction = (e) -> {
+ this.scrollY += 10;
+ this.updateScrollVisual();
+ }
+ this.addChild(scrollDownButton);
+
+ this.scrollBarY = new Graphics();
+ this.scrollBarY.scale(Settings.uiScale);
+ this.clickInteractive = new Interactive(10 * Settings.uiScale, 1);
+ this.clickInteractive.onPush = (e) -> {
+ if (!this.pressed) {
+ this.pressed = true;
+ this.dirty = true;
+ this.updateScrollVisual();
+
+ var prevEY:Null = null;
+
+ this.clickInteractive.startCapture(e2 -> {
+ if (e2.kind == ERelease) {
+ this.clickInteractive.stopCapture();
+ }
+ if (e2.kind == EMove) {
+ if (prevEY == null) {
+ prevEY = e2.relY;
+ } else {
+ this.scrollY += (e2.relY - prevEY);
+ prevEY = e2.relY;
+ this.updateScrollVisual();
+ }
+ }
+ }, () -> {
+ if (this.pressed) {
+ this.pressed = false;
+ this.dirty = true;
+ this.updateScrollVisual();
+ }
+ });
+ }
+ };
+ this.scrollTrack = new Bitmap(scrollTrackTile);
+ }
+
+ public function setScrollMax(max:Float) {
+ this.scrollY = 0;
+ this.maxScrollY = max;
+ this.dirty = true;
+ this.updateScrollVisual();
+ }
+
+ public override function getRenderRectangle():Rect {
+ var rrec = super.getRenderRectangle();
+ rrec.scroll.y = scrollY * this.maxScrollY / (rrec.extent.y - 34 * Settings.uiScale);
+ return rrec;
+ }
+
+ public override function render(scene2d:Scene) {
+ this.dirty = true;
+
+ scrollUpButton.position = new Vector(this.extent.x - 18, 0);
+ scrollDownButton.position = new Vector(this.extent.x - 18, this.extent.y - 17);
+
+ if (scene2d.contains(scrollTrack))
+ scene2d.removeChild(scrollTrack);
+
+ if (scene2d.contains(scrollBarY))
+ scene2d.removeChild(scrollBarY);
+
+ if (scene2d.contains(clickInteractive))
+ scene2d.removeChild(clickInteractive);
+
+ scene2d.addChild(scrollTrack);
+ scene2d.addChild(scrollBarY);
+ scene2d.addChild(clickInteractive);
+
+ updateScrollVisual();
+
+ super.render(scene2d);
+ }
+
+ public function updateScrollVisual() {
+ var renderRect = this.getRenderRectangle();
+
+ if (maxScrollY < renderRect.extent.y) {
+ scrollBarY.clear();
+ return;
+ }
+
+ this.scrollTrack.setPosition(renderRect.position.x + renderRect.extent.x - 18 * Settings.uiScale, renderRect.position.y);
+
+ var scrollExtentY = renderRect.extent.y - 34 * Settings.uiScale;
+
+ var scrollBarYSize = (scrollExtentY * scrollExtentY / (maxScrollY * Settings.uiScale - 34 * Settings.uiScale));
+
+ this.scrollTrack.scaleY = renderRect.extent.y;
+
+ this.scrollY = Util.clamp(scrollY, 0, scrollExtentY - scrollBarYSize * Settings.uiScale);
+
+ this.scrollBarY.setPosition(renderRect.position.x
+ + renderRect.extent.x
+ - 18 * Settings.uiScale,
+ 18 * Settings.uiScale
+ + renderRect.position.y
+ + scrollY);
+
+ this.clickInteractive.setPosition(renderRect.position.x + renderRect.extent.x - 18 * Settings.uiScale, 18 * Settings.uiScale + renderRect.position.y);
+
+ this.clickInteractive.height = scrollExtentY;
+
+ if (this.dirty) {
+ if (scrollBarYSize > scrollExtentY) {
+ scrollBarYSize = scrollExtentY;
+ scrollBarY.clear();
+ return;
+ }
+
+ scrollBarY.clear();
+
+ scrollBarY.tileWrap = true;
+
+ scrollBarY.drawTile(0, 0, pressed ? scrollTopPressedTile : scrollTopTile);
+
+ // :skull:
+ for (i in 0...cast(scrollBarYSize - 12)) {
+ scrollBarY.drawTile(0, i + 8, pressed ? scrollFillPressedTile : scrollFillTile);
+ }
+
+ scrollBarY.drawTile(0, scrollBarYSize - 8, pressed ? scrollBottomPressedTile : scrollBottomTile);
+
+ this.dirty = false;
+ }
+
+ for (c in this.children) {
+ c.onScroll(0, scrollY * (this.maxScrollY - 34 * Settings.uiScale) / scrollExtentY);
+ }
+ }
+
+ public override function dispose() {
+ super.dispose();
+ this.scrollBarY.remove();
+ this.scrollTrack.remove();
+ this.clickInteractive.remove();
+ }
+
+ public function setScrollPercentage(f:Float) {
+ var renderRect = this.getRenderRectangle();
+ var scrollExtentY = renderRect.extent.y - 34 * Settings.uiScale;
+ var scrollBarYSize = (scrollExtentY * scrollExtentY / (maxScrollY * Settings.uiScale - 34 * Settings.uiScale));
+
+ this.scrollY = Util.lerp(0, scrollExtentY - scrollBarYSize * Settings.uiScale, f);
+ updateScrollVisual();
+ }
+
+ public override function onRemove() {
+ super.onRemove();
+ if (MarbleGame.canvas.scene2d.contains(scrollTrack)) {
+ MarbleGame.canvas.scene2d.removeChild(scrollTrack); // Refresh "layer"
+ }
+ if (MarbleGame.canvas.scene2d.contains(scrollBarY)) {
+ MarbleGame.canvas.scene2d.removeChild(scrollBarY); // Refresh "layer"
+ }
+ if (MarbleGame.canvas.scene2d.contains(clickInteractive)) {
+ MarbleGame.canvas.scene2d.removeChild(clickInteractive); // Refresh "layer"
+ }
+ }
+
+ public override function onMousePress(mouseState:MouseState) {
+ if (Util.isTouchDevice()) {
+ this.pressed = true;
+ this.dirty = true;
+ this.updateScrollVisual();
+ this.prevMousePos = mouseState.position;
+ }
+ }
+
+ public override function onMouseRelease(mouseState:MouseState) {
+ if (Util.isTouchDevice()) {
+ this.pressed = false;
+ this.dirty = true;
+ this.updateScrollVisual();
+ }
+ }
+
+ public override function onMouseMove(mouseState:MouseState) {
+ if (Util.isTouchDevice()) {
+ super.onMouseMove(mouseState);
+ if (this.pressed) {
+ var dy = mouseState.position.y - this.prevMousePos.y;
+ this.scrollY -= dy;
+ this.prevMousePos = mouseState.position;
+ this.updateScrollVisual();
+ }
+ }
+ }
+
+ // public override function onMouseDown(mouseState:MouseState) {
+ // var renderRect = this.getHitTestRect();
+ // if (mouseState.position.x >= renderRect.position.x + renderRect.extent.x - 10) {
+ // this.scrollY = mouseState.position.y - renderRect.position.y;
+ // this.updateScrollVisual();
+ // }
+ // super.onMouseDown(mouseState);
+ // }
+}
diff --git a/src/mis/MisParser.hx b/src/mis/MisParser.hx
index c719e585..9bc6977b 100644
--- a/src/mis/MisParser.hx
+++ b/src/mis/MisParser.hx
@@ -23,6 +23,7 @@ import mis.MissionElement.MissionElementScriptObject;
import src.Util;
import h3d.Vector;
import h3d.Quat;
+import src.Console;
final elementHeadRegEx = ~/new (\w+)\((\w*)\) *{/g;
final blockCommentRegEx = ~/\/\*(.|\n)*?\*\//g;
@@ -175,7 +176,7 @@ class MisParser {
case "ParticleEmitterNode":
MisParserMacros.parseObject(name, MissionElementParticleEmitterNode, MissionElementType.ParticleEmitterNode);
default:
- trace("Unknown element type! " + type);
+ Console.warn("Unknown element type! " + type);
// Still advance the index
var endingBraceIndex = Util.indexOfIgnoreStringLiterals(this.text, '};', this.index);
if (endingBraceIndex == -1)