diff --git a/src/Mission.hx b/src/Mission.hx index 53205d76..141d3f02 100644 --- a/src/Mission.hx +++ b/src/Mission.hx @@ -1,13 +1,56 @@ package src; +import mis.MisParser; +import mis.MissionElement.MissionElementScriptObject; +import mis.MissionElement.MissionElementType; +import mis.MisFile; import mis.MissionElement.MissionElementSimGroup; import src.ResourceLoader; class Mission { public var root:MissionElementSimGroup; + public var title:String; + public var artist:String; + public var description:String; + public var qualifyTime = Math.POSITIVE_INFINITY; + public var goldTime:Float = 0; + public var type:String; + public var path:String; + public var missionInfo:MissionElementScriptObject; public function new() {} + public static function fromMissionInfo(path:String, mInfo:MissionElementScriptObject) { + var mission = new Mission(); + mission.path = path; + mission.missionInfo = mInfo; + + var missionInfo = mInfo; + + mission.title = missionInfo.name; + mission.artist = missionInfo.artist == null ? '' : missionInfo.artist; + mission.description = missionInfo.desc == null ? '' : missionInfo.desc; + if (missionInfo.time != null && missionInfo.time != "0") + mission.qualifyTime = MisParser.parseNumber(missionInfo.time) / 1000; + if (missionInfo.goldtime != null) { + mission.goldTime = MisParser.parseNumber(missionInfo.goldtime) / 1000; + } + mission.type = missionInfo.type.toLowerCase(); + mission.missionInfo = missionInfo; + return mission; + } + + public function getPreviewImage() { + var basename = haxe.io.Path.withoutExtension(this.path); + if (ResourceLoader.fileSystem.exists(basename + ".png")) { + return ResourceLoader.getImage(basename + ".png").toTile(); + } + if (ResourceLoader.fileSystem.exists(basename + ".jpg")) { + return ResourceLoader.getImage(basename + ".jpg").toTile(); + } + return null; + } + public function getDifPath(rawElementPath:String) { rawElementPath = rawElementPath.toLowerCase(); var path = StringTools.replace(rawElementPath.substring(rawElementPath.indexOf('data/')), "\"", ""); diff --git a/src/MissionList.hx b/src/MissionList.hx new file mode 100644 index 00000000..6562f7ce --- /dev/null +++ b/src/MissionList.hx @@ -0,0 +1,39 @@ +import mis.MisParser; +import src.ResourceLoader; +import src.Mission; + +@:publicFields +class MissionList { + static var beginnerMissions:Array; + static var intermediateMissions:Array; + static var advancedMissions:Array; + + static var _build:Bool = false; + + public function new() {} + + public static function buildMissionList() { + if (_build) + return; + function parseDifficulty(difficulty:String) { + var difficultyFiles = ResourceLoader.fileSystem.dir("data/missions/" + difficulty); + var difficultyMissions = []; + for (file in difficultyFiles) { + if (file.extension == "mis") { + var misParser = new MisParser(file.getText()); + var mInfo = misParser.parseMissionInfo(); + var mission = Mission.fromMissionInfo(file.path, mInfo); + difficultyMissions.push(mission); + } + } + difficultyMissions.sort((a, b) -> Std.parseInt(a.missionInfo.level) - Std.parseInt(b.missionInfo.level)); + return difficultyMissions; + } + + beginnerMissions = parseDifficulty("beginner"); + intermediateMissions = parseDifficulty("intermediate"); + advancedMissions = parseDifficulty("advanced"); + + _build = true; + } +} diff --git a/src/Util.hx b/src/Util.hx index 8da95356..7c655c2e 100644 --- a/src/Util.hx +++ b/src/Util.hx @@ -112,7 +112,8 @@ class Util { '\\0' => '\x00', '\\f' => '\x0C', '\\n' => '\n', - '\\r' => '\r' + '\\r' => '\r', + "\\'" => "'" ]; for (obj => esc in specialCases) { diff --git a/src/gui/Canvas.hx b/src/gui/Canvas.hx index bcea05e0..6569de69 100644 --- a/src/gui/Canvas.hx +++ b/src/gui/Canvas.hx @@ -12,7 +12,7 @@ class Canvas extends GuiControl { this.scene2d = scene; this.position = new Vector(); - this.extent = new Vector(scene.width, scene.height); + this.extent = new Vector(640, 480); this.horizSizing = Width; this.vertSizing = Height; } diff --git a/src/gui/GuiAnim.hx b/src/gui/GuiAnim.hx index d89c7a3d..9f2b47dd 100644 --- a/src/gui/GuiAnim.hx +++ b/src/gui/GuiAnim.hx @@ -19,8 +19,10 @@ class GuiAnim extends GuiControl { anim.setPosition(renderRect.position.x, renderRect.position.y); anim.scaleX = renderRect.extent.x / anim.getFrame().width; anim.scaleY = renderRect.extent.y / anim.getFrame().height; - if (!scene2d.contains(anim)) - scene2d.addChild(anim); + if (scene2d.contains(anim)) { + scene2d.removeChild(anim); // Refresh "layer" + } + scene2d.addChild(anim); super.render(scene2d); } diff --git a/src/gui/GuiButton.hx b/src/gui/GuiButton.hx index 73296dd8..2cdcb6a8 100644 --- a/src/gui/GuiButton.hx +++ b/src/gui/GuiButton.hx @@ -12,26 +12,28 @@ class GuiButton extends GuiAnim { // 3 is disabled public var pressedAction:GuiControl->Void = null; + public var disabled:Bool = false; + public function new(anim:Array) { super(anim); } public override function update(dt:Float, mouseState:MouseState) { var renderRect = getRenderRectangle(); - if (renderRect.inRect(mouseState.position)) { + if (renderRect.inRect(mouseState.position) && !disabled) { if (Key.isDown(Key.MOUSE_LEFT)) { this.anim.currentFrame = 2; } else { this.anim.currentFrame = 1; } } else - this.anim.currentFrame = 0; + this.anim.currentFrame = disabled ? 3 : 0; super.update(dt, mouseState); } public override function onMouseRelease(mouseState:MouseState) { super.onMouseRelease(mouseState); - if (this.pressedAction != null) { + if (this.pressedAction != null && !disabled) { this.pressedAction(this); } } diff --git a/src/gui/GuiControl.hx b/src/gui/GuiControl.hx index 25dbd045..49a19bfd 100644 --- a/src/gui/GuiControl.hx +++ b/src/gui/GuiControl.hx @@ -89,13 +89,13 @@ class GuiControl { } if (this.horizSizing == HorizSizing.Width) { if (this.parent != null) - rect.extent.x = parentRect.extent.x; + rect.extent.x = parentRect.extent.x * (this.extent.x / parent.extent.x); else rect.extent.x = Window.getInstance().width; } if (this.vertSizing == VertSizing.Height) { if (this.parent != null) - rect.extent.y = parentRect.extent.y; + rect.extent.y = parentRect.extent.y * (this.extent.y / parent.extent.y); else rect.extent.y = Window.getInstance().height; } @@ -138,6 +138,11 @@ class GuiControl { ctrl.parent = this; } + public function removeChild(ctrl:GuiControl) { + this.children.remove(ctrl); + ctrl.parent = null; + } + public function removeChildren() { for (c in this.children) { c.parent = null; diff --git a/src/gui/GuiImage.hx b/src/gui/GuiImage.hx index 0626b699..d7e531c3 100644 --- a/src/gui/GuiImage.hx +++ b/src/gui/GuiImage.hx @@ -1,5 +1,6 @@ package gui; +import gui.GuiControl.MouseState; import h2d.Scene; import h2d.Tile; import h2d.Bitmap; @@ -8,6 +9,8 @@ import h2d.Bitmap; class GuiImage extends GuiControl { var bmp:Bitmap; + public var pressedAction:GuiControl->Void = null; + public function new(texture:Tile) { super(); this.bmp = new Bitmap(texture); @@ -16,10 +19,14 @@ class GuiImage extends GuiControl { public override function render(scene2d:Scene) { var renderRect = this.getRenderRectangle(); bmp.setPosition(renderRect.position.x, renderRect.position.y); - bmp.scaleX = renderRect.extent.x / bmp.tile.width; - bmp.scaleY = renderRect.extent.y / bmp.tile.height; - if (!scene2d.contains(bmp)) - scene2d.addChild(bmp); + // bmp.scaleX = renderRect.extent.x / bmp.tile.width; + // bmp.scaleY = renderRect.extent.y / bmp.tile.height; + bmp.width = renderRect.extent.x; + bmp.height = renderRect.extent.y; + if (scene2d.contains(bmp)) { + scene2d.removeChild(bmp); // Refresh "layer" + } + scene2d.addChild(bmp); super.render(scene2d); } @@ -27,4 +34,11 @@ class GuiImage extends GuiControl { super.dispose(); this.bmp.remove(); } + + public override function onMouseRelease(mouseState:MouseState) { + super.onMouseRelease(mouseState); + if (this.pressedAction != null) { + this.pressedAction(this); + } + } } diff --git a/src/gui/GuiMLText.hx b/src/gui/GuiMLText.hx new file mode 100644 index 00000000..9bee64f7 --- /dev/null +++ b/src/gui/GuiMLText.hx @@ -0,0 +1,45 @@ +package gui; + +import gui.GuiText.Justification; +import h2d.HtmlText; +import h2d.Scene; +import hxd.res.BitmapFont; +import h2d.Text; + +@:publicFields +class GuiMLText extends GuiControl { + var text:HtmlText; + var justify:Justification = Left; + + public function new(font:BitmapFont, loadFontFunc:String->h2d.Font) { + super(); + this.text = new HtmlText(font.toFont()); + this.text.loadFont = loadFontFunc; + } + + public override function render(scene2d:Scene) { + var renderRect = this.getRenderRectangle(); + if (justify == Left) { + text.setPosition(renderRect.position.x, renderRect.position.y); + text.textAlign = Left; + } + if (justify == Right) { + text.setPosition(renderRect.position.x + renderRect.extent.x, renderRect.position.y); + text.textAlign = Right; + } + if (justify == Center) { + text.setPosition(renderRect.position.x + renderRect.extent.x / 2, renderRect.position.y); + text.textAlign = Center; + } + if (scene2d.contains(text)) + scene2d.removeChild(text); + scene2d.addChild(text); + text.maxWidth = renderRect.extent.x; + super.render(scene2d); + } + + public override function dispose() { + super.dispose(); + this.text.remove(); + } +} diff --git a/src/gui/GuiText.hx b/src/gui/GuiText.hx index 0b66a867..a022efc5 100644 --- a/src/gui/GuiText.hx +++ b/src/gui/GuiText.hx @@ -34,8 +34,9 @@ class GuiText extends GuiControl { text.setPosition(renderRect.position.x + renderRect.extent.x / 2, renderRect.position.y); text.textAlign = Center; } - if (!scene2d.contains(text)) - scene2d.addChild(text); + if (scene2d.contains(text)) + scene2d.removeChild(text); + scene2d.addChild(text); super.render(scene2d); } diff --git a/src/gui/PlayMissionGui.hx b/src/gui/PlayMissionGui.hx index a5bfde57..816afd05 100644 --- a/src/gui/PlayMissionGui.hx +++ b/src/gui/PlayMissionGui.hx @@ -1,9 +1,21 @@ package gui; +import haxe.io.Path; +import h2d.Scene; +import h2d.Text; +import src.Mission; +import hxd.res.BitmapFont; import src.ResourceLoader; import h3d.Vector; +import src.Util; class PlayMissionGui extends GuiImage { + var currentSelection:Int = 0; + var currentCategory:String = "beginner"; + var currentList:Array; + + var setSelectedFunc:Int->Void; + public function new() { super(ResourceLoader.getImage("data/ui/background.jpg").toTile()); @@ -12,6 +24,8 @@ class PlayMissionGui extends GuiImage { this.extent = new Vector(640, 480); this.position = new Vector(0, 0); + MissionList.buildMissionList(); + var localContainer = new GuiControl(); localContainer.horizSizing = Center; localContainer.vertSizing = Center; @@ -23,17 +37,32 @@ class PlayMissionGui extends GuiImage { var normal = ResourceLoader.getImage('${path}_n.png').toTile(); var hover = ResourceLoader.getImage('${path}_h.png').toTile(); var pressed = ResourceLoader.getImage('${path}_d.png').toTile(); - return [normal, hover, pressed]; + var disabled = ResourceLoader.getImage('${path}_i.png').toTile(); + return [normal, hover, pressed, disabled]; } + var setCategoryFunc:String->Void = null; + var tabAdvanced = new GuiImage(ResourceLoader.getImage("data/ui/play/tab_adv.png").toTile()); tabAdvanced.position = new Vector(410, 21); tabAdvanced.extent = new Vector(166, 43); + tabAdvanced.pressedAction = (sender) -> { + currentList = MissionList.advancedMissions; + currentCategory = "advanced"; + setSelectedFunc(0); + setCategoryFunc("advanced"); + } localContainer.addChild(tabAdvanced); var tabIntermediate = new GuiImage(ResourceLoader.getImage("data/ui/play/tab_inter.png").toTile()); tabIntermediate.position = new Vector(213, 4); tabIntermediate.extent = new Vector(205, 58); + tabIntermediate.pressedAction = (sender) -> { + currentList = MissionList.intermediateMissions; + currentCategory = "intermediate"; + setSelectedFunc(0); + setCategoryFunc("intermediate"); + } localContainer.addChild(tabIntermediate); var tabCustom = new GuiImage(ResourceLoader.getImage("data/ui/play/cust_tab.png").toTile()); @@ -44,13 +73,13 @@ class PlayMissionGui extends GuiImage { var pmBox = new GuiImage(ResourceLoader.getImage("data/ui/play/playgui.png").toTile()); pmBox.position = new Vector(0, 42); pmBox.extent = new Vector(610, 351); - pmBox.horizSizing = Right; - pmBox.vertSizing = Bottom; + pmBox.horizSizing = Width; + pmBox.vertSizing = Height; localContainer.addChild(pmBox); var textWnd = new GuiImage(ResourceLoader.getImage("data/ui/play/text_window.png").toTile()); - textWnd.horizSizing = Right; - textWnd.vertSizing = Bottom; + textWnd.horizSizing = Width; + textWnd.vertSizing = Height; textWnd.position = new Vector(31, 29); textWnd.extent = new Vector(276, 229); pmBox.addChild(textWnd); @@ -65,8 +94,46 @@ class PlayMissionGui extends GuiImage { levelWnd.extent = new Vector(258, 194); pmPreview.addChild(levelWnd); + var domcasual24fontdata = ResourceLoader.loader.load("data/font/DomCasual24px.fnt"); + var domcasual24 = new BitmapFont(domcasual24fontdata.entry); + @:privateAccess domcasual24.loader = ResourceLoader.loader; + + var domcasual32fontdata = ResourceLoader.loader.load("data/font/DomCasual24px.fnt"); + var domcasual32 = new BitmapFont(domcasual32fontdata.entry); + @:privateAccess domcasual32.loader = ResourceLoader.loader; + + var arial14fontdata = ResourceLoader.loader.load("data/font/Arial14.fnt"); + var arial14 = new BitmapFont(arial14fontdata.entry); + @:privateAccess arial14.loader = ResourceLoader.loader; + + var arialBold14fontdata = ResourceLoader.loader.load("data/font/ArialBold14px.fnt"); + var arialBold14 = new BitmapFont(arialBold14fontdata.entry); + @:privateAccess arialBold14.loader = ResourceLoader.loader; + // TODO texts - // var levelBkgnd = new GuiText() + var levelBkgnd = new GuiText(domcasual24); + levelBkgnd.position = new Vector(5, 156); + levelBkgnd.extent = new Vector(254, 24); + levelBkgnd.text.textColor = 0x000000; + levelBkgnd.justify = Center; + levelBkgnd.text.text = "Beginner Level 3"; + levelWnd.addChild(levelBkgnd); + + var levelFgnd = new GuiText(domcasual24); + levelFgnd.position = new Vector(4, 155); + levelFgnd.extent = new Vector(254, 24); + levelFgnd.text.textColor = 0xFFFFFF; + levelFgnd.justify = Center; + levelFgnd.text.text = "Beginner Level 3"; + levelWnd.addChild(levelFgnd); + + var noQualText = new GuiText(domcasual32); + noQualText.position = new Vector(0, 84); + noQualText.extent = new Vector(254, 32); + noQualText.text.textColor = 0xCCCCCC; + noQualText.justify = Center; + noQualText.text.text = "Not qualified!"; + levelWnd.addChild(noQualText); var pmPlay = new GuiButton(loadButtonImages("data/ui/play/play")); pmPlay.position = new Vector(391, 257); @@ -76,11 +143,17 @@ class PlayMissionGui extends GuiImage { var pmPrev = new GuiButton(loadButtonImages("data/ui/play/prev")); pmPrev.position = new Vector(321, 260); pmPrev.extent = new Vector(77, 58); + pmPrev.pressedAction = (sender) -> { + setSelectedFunc(currentSelection - 1); + } pmBox.addChild(pmPrev); var pmNext = new GuiButton(loadButtonImages("data/ui/play/next")); pmNext.position = new Vector(507, 262); pmNext.extent = new Vector(75, 60); + pmNext.pressedAction = (sender) -> { + setSelectedFunc(currentSelection + 1); + } pmBox.addChild(pmNext); var pmBack = new GuiButton(loadButtonImages("data/ui/play/back")); @@ -91,13 +164,162 @@ class PlayMissionGui extends GuiImage { }; pmBox.addChild(pmBack); + function mlFontLoader(text:String) { + switch (text) { + case "DomCasual24": + return domcasual32.toFont(); + case "Arial14": + return arial14.toFont(); + case "ArialBold14": + return arialBold14.toFont(); + default: + return null; + } + } + // TODO Pm description + var pmDescription = new GuiMLText(arial14, mlFontLoader); + pmDescription.position = new Vector(61, 52); + pmDescription.extent = new Vector(215, 174); + pmDescription.text.textColor = 0x000000; + // We're gonna use  to align shit lmao, its too hacky i know + var descText = 'Learn The Super Speed

' + 'ÂTest Align'; + descText += '

Best Times:
'; + for (i in 0...3) { + descText += '
ÂÂ${i + 1}. Nardo Polo'; + } + pmDescription.text.text = descText; + pmBox.addChild(pmDescription); + + // Oh god this is yet another hack cause I cant do that tab thing torque does so thats bruh + var pmDescriptionOther = new GuiMLText(arial14, mlFontLoader); + pmDescriptionOther.position = new Vector(61, 52); + pmDescriptionOther.extent = new Vector(215, 174); + pmDescriptionOther.text.textColor = 0x000000; + var descText2 = '

' + 'ÂTest Align'; + descText2 += '


'; + for (i in 0...3) { + descText2 += '
ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ00:00.000'; + } + pmDescriptionOther.text.text = descText2; + pmBox.addChild(pmDescriptionOther); var tabBeginner = new GuiImage(ResourceLoader.getImage("data/ui/play/tab_begin.png").toTile()); tabBeginner.position = new Vector(29, 2); tabBeginner.extent = new Vector(184, 55); + tabBeginner.pressedAction = (sender) -> { + currentList = MissionList.beginnerMissions; + currentCategory = "beginner"; + setSelectedFunc(0); + setCategoryFunc("beginner"); + } localContainer.addChild(tabBeginner); + currentList = MissionList.beginnerMissions; + // TODO actual tab buttons + + setCategoryFunc = function(category:String) { + localContainer.removeChild(tabBeginner); + localContainer.removeChild(tabIntermediate); + localContainer.removeChild(tabAdvanced); + localContainer.removeChild(tabCustom); + localContainer.removeChild(pmBox); + if (category == "beginner") { + localContainer.addChild(tabIntermediate); + localContainer.addChild(tabAdvanced); + localContainer.addChild(tabCustom); + localContainer.addChild(pmBox); + localContainer.addChild(tabBeginner); + } + if (category == "intermediate") { + localContainer.addChild(tabBeginner); + localContainer.addChild(tabAdvanced); + localContainer.addChild(tabCustom); + localContainer.addChild(pmBox); + localContainer.addChild(tabIntermediate); + } + if (category == "advanced") { + localContainer.addChild(tabBeginner); + localContainer.addChild(tabIntermediate); + localContainer.addChild(tabCustom); + localContainer.addChild(pmBox); + localContainer.addChild(tabAdvanced); + } + this.render(cast(this.parent, Canvas).scene2d); + } + + function splitTextWithPadding(textElement:Text, textStr:String) { + var maxWidth = textElement.maxWidth; + textElement.maxWidth = null; + var splits = []; + var currentText = "Â"; + var textSplit = textStr.split(" "); + for (i in 0...textSplit.length) { + var prevText = currentText; + currentText += textSplit[i]; + if (i != textSplit.length - 1) + currentText += " "; + textElement.text = currentText; + if (textElement.textWidth > maxWidth) { + splits.push(StringTools.trim(prevText)); + currentText = "Â" + textSplit[i]; + if (i != textSplit.length - 1) + currentText += " "; + } + } + textElement.maxWidth = maxWidth; + splits.push(currentText); + return splits.join('\n'); + } + + setSelectedFunc = function setSelected(index:Int) { + if (index > currentList.length - 1) { + index = currentList.length - 1; + } + if (index < 0) { + index = 0; + } + + currentSelection = index; + + if (index == 0) { + pmPrev.disabled = true; + } else + pmPrev.disabled = false; + if (index == currentList.length - 1) { + pmNext.disabled = true; + } else + pmNext.disabled = false; + + var currentMission = currentList[currentSelection]; + var descText = '${currentMission.title}

' + + splitTextWithPadding(pmDescription.text, Util.unescape(currentMission.description)); + descText += '

Best Times:
'; + for (i in 0...3) { + descText += '
ÂÂ${i + 1}. Nardo Polo'; + } + pmDescription.text.text = descText; + + var descText2 = '

' + + '${splitTextWithPadding(pmDescriptionOther.text, Util.unescape(currentMission.description))}'; + descText2 += '


'; + for (i in 0...3) { + descText2 += '
ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ00:00.000'; + } + pmDescriptionOther.text.text = descText2; + + pmPreview.bmp.tile = currentMission.getPreviewImage(); + + levelBkgnd.text.text = currentCategory.charAt(0).toUpperCase() + currentCategory.substr(1) + ' Level ${currentSelection + 1}'; + levelFgnd.text.text = currentCategory.charAt(0).toUpperCase() + currentCategory.substr(1) + ' Level ${currentSelection + 1}'; + + noQualText.text.visible = false; + } + } + + public override function render(scene2d:Scene) { + super.render(scene2d); + setSelectedFunc(0); } } diff --git a/src/mis/MisParser.hx b/src/mis/MisParser.hx index e48fd0cd..ace186cd 100644 --- a/src/mis/MisParser.hx +++ b/src/mis/MisParser.hx @@ -115,7 +115,6 @@ class MisParser { if (elements.length != 1) { // We expect there to be only one outer element; the MissionGroup SimGroup. - trace(elements); throw new Exception("Mission file doesn't have exactly 1 outer element!"); } @@ -126,6 +125,13 @@ class MisParser { return mf; } + public function parseMissionInfo() { + var missionInfoIndex = this.text.indexOf("new ScriptObject(MissionInfo) {"); + this.index = missionInfoIndex; + var mInfo:MissionElementScriptObject = cast readElement(); + return mInfo; + } + function readElement() { // Get information about the head // elementHeadRegEx.lastIndex = this.index;