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)