diff --git a/.vscode/settings.json b/.vscode/settings.json index e45b72390..2f795d56e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,5 +9,6 @@ } }, - "haxe.enableExtendedIndentation": true + "haxe.enableExtendedIndentation": true, + "restructuredtext.confPath": "${workspaceFolder}\\docs" } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 16a77646f..3daafd90a 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,10 +4,23 @@ { "type": "lime", "command": "test", + "targetConfiguration": "Windows", "group": { - "kind": "build", + "kind": "test", "isDefault": true } + }, + { + "type": "lime", + "command": "test", + "targetConfiguration": "Windows", + "problemMatcher": [ + "$haxe-absolute", + "$haxe", + "$haxe-error", + "$haxe-trace" + ], + "label": "lime: test windows" } ] } diff --git a/assets/images/dialogue/boxes/dialogueBox-pixel/dialogueBox-pixel.json b/assets/images/dialogue/boxes/dialogueBox-pixel/dialogueBox-pixel.json new file mode 100644 index 000000000..3991cb19a --- /dev/null +++ b/assets/images/dialogue/boxes/dialogueBox-pixel/dialogueBox-pixel.json @@ -0,0 +1,15 @@ +{ + "position": [515, 370], + "textPos": [200, 480], + "scale": 5.4, + "antialiasing": false, + "singleFrame": true, + "doFlip": false, + "bgColor": [255, 255, 255], + "states": { + "normal": { + "open": ["Text Box Appear instance 1"], + "default": ["Text Box Appear instance 1"] + } + } +} \ No newline at end of file diff --git a/assets/images/dialogue/boxes/dialogueBox-pixel/dialogueBox-pixel.png b/assets/images/dialogue/boxes/dialogueBox-pixel/dialogueBox-pixel.png new file mode 100644 index 000000000..c7d88ff5f Binary files /dev/null and b/assets/images/dialogue/boxes/dialogueBox-pixel/dialogueBox-pixel.png differ diff --git a/assets/images/dialogue/boxes/dialogueBox-pixel/dialogueBox-pixel.xml b/assets/images/dialogue/boxes/dialogueBox-pixel/dialogueBox-pixel.xml new file mode 100644 index 000000000..e0a1070a5 --- /dev/null +++ b/assets/images/dialogue/boxes/dialogueBox-pixel/dialogueBox-pixel.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/images/dialogue/boxes/speech_bubble_talking/speech_bubble_talking.json b/assets/images/dialogue/boxes/speech_bubble_talking/speech_bubble_talking.json new file mode 100644 index 000000000..8f1094cb8 --- /dev/null +++ b/assets/images/dialogue/boxes/speech_bubble_talking/speech_bubble_talking.json @@ -0,0 +1,13 @@ +{ + "position": [20, 370], + "states": { + "normal": { + "open": ["Speech Bubble Normal Open"], + "default": ["speech bubble normal", [-30, 0]] + }, + "yell": { + "open": ["speech bubble loud open", [80, 140]], + "default": ["AHH speech bubble", [0, 80]] + } + } +} \ No newline at end of file diff --git a/assets/images/dialogue/boxes/speech_bubble_talking/speech_bubble_talking.png b/assets/images/dialogue/boxes/speech_bubble_talking/speech_bubble_talking.png new file mode 100644 index 000000000..b10d26bce Binary files /dev/null and b/assets/images/dialogue/boxes/speech_bubble_talking/speech_bubble_talking.png differ diff --git a/assets/images/dialogue/boxes/speech_bubble_talking/speech_bubble_talking.xml b/assets/images/dialogue/boxes/speech_bubble_talking/speech_bubble_talking.xml new file mode 100644 index 000000000..367e31a45 --- /dev/null +++ b/assets/images/dialogue/boxes/speech_bubble_talking/speech_bubble_talking.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/dialogue/portraits/bf-pixel/bf-pixel.json b/assets/images/dialogue/portraits/bf-pixel/bf-pixel.json new file mode 100644 index 000000000..38d46412a --- /dev/null +++ b/assets/images/dialogue/portraits/bf-pixel/bf-pixel.json @@ -0,0 +1,14 @@ +{ + "name": "bf-pixel", + "expressions": { + "normal": "Boyfriend portrait enter" + }, + "position": [630, 370], + "scale": 5.4, + "antialiasing": false, + "loop": false, + + "sounds": ["pixelText"], + "soundChance": 100, + "soundPath": "sounds/" +} \ No newline at end of file diff --git a/assets/images/dialogue/portraits/bf-pixel/bf-pixel.png b/assets/images/dialogue/portraits/bf-pixel/bf-pixel.png new file mode 100644 index 000000000..303914d98 Binary files /dev/null and b/assets/images/dialogue/portraits/bf-pixel/bf-pixel.png differ diff --git a/assets/images/dialogue/portraits/bf-pixel/bf-pixel.xml b/assets/images/dialogue/portraits/bf-pixel/bf-pixel.xml new file mode 100644 index 000000000..4a56a9a2e --- /dev/null +++ b/assets/images/dialogue/portraits/bf-pixel/bf-pixel.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/dialogue/portraits/bf/BF_1.ogg b/assets/images/dialogue/portraits/bf/BF_1.ogg new file mode 100644 index 000000000..12660f145 Binary files /dev/null and b/assets/images/dialogue/portraits/bf/BF_1.ogg differ diff --git a/assets/images/dialogue/portraits/bf/BF_2.ogg b/assets/images/dialogue/portraits/bf/BF_2.ogg new file mode 100644 index 000000000..44fc87db7 Binary files /dev/null and b/assets/images/dialogue/portraits/bf/BF_2.ogg differ diff --git a/assets/images/dialogue/portraits/bf/BF_3.ogg b/assets/images/dialogue/portraits/bf/BF_3.ogg new file mode 100644 index 000000000..a6c8e3a8c Binary files /dev/null and b/assets/images/dialogue/portraits/bf/BF_3.ogg differ diff --git a/assets/images/dialogue/portraits/bf/bf.json b/assets/images/dialogue/portraits/bf/bf.json new file mode 100644 index 000000000..473e728f7 --- /dev/null +++ b/assets/images/dialogue/portraits/bf/bf.json @@ -0,0 +1,11 @@ +{ + "name": "bf", + "expressions": { + "normal": "Normal Talk", + "mad": "Angry Talk", + "smug": "Smug Talk" + }, + "position": "right", + + "sounds": ["BF_1", "BF_2", "BF_3"] +} \ No newline at end of file diff --git a/assets/images/dialogue/portraits/bf/bf.png b/assets/images/dialogue/portraits/bf/bf.png new file mode 100644 index 000000000..b162efcd6 Binary files /dev/null and b/assets/images/dialogue/portraits/bf/bf.png differ diff --git a/assets/images/dialogue/portraits/bf/bf.xml b/assets/images/dialogue/portraits/bf/bf.xml new file mode 100644 index 000000000..d1a946d92 --- /dev/null +++ b/assets/images/dialogue/portraits/bf/bf.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/dialogue/portraits/senpai/senpai.json b/assets/images/dialogue/portraits/senpai/senpai.json new file mode 100644 index 000000000..b458fec0d --- /dev/null +++ b/assets/images/dialogue/portraits/senpai/senpai.json @@ -0,0 +1,16 @@ +{ + "name": "senpai", + "expressions": { + "normal": "Senpai Portrait Enter", + "mad": "SENPAI ANGRY IMPACT SPEECH" + }, + "position": [420, 370], + "scale": 5.4, + "flipX": true, + "antialiasing": false, + "loop": false, + + "sounds": ["pixelText"], + "soundChance": 100, + "soundPath": "sounds/" +} \ No newline at end of file diff --git a/assets/images/dialogue/portraits/senpai/senpai.png b/assets/images/dialogue/portraits/senpai/senpai.png new file mode 100644 index 000000000..27deb9f1b Binary files /dev/null and b/assets/images/dialogue/portraits/senpai/senpai.png differ diff --git a/assets/images/dialogue/portraits/senpai/senpai.xml b/assets/images/dialogue/portraits/senpai/senpai.xml new file mode 100644 index 000000000..bfd2468ed --- /dev/null +++ b/assets/images/dialogue/portraits/senpai/senpai.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/songs/dadbattle/dialogue.json b/assets/songs/dadbattle/dialogue.json new file mode 100644 index 000000000..60623d63b --- /dev/null +++ b/assets/songs/dadbattle/dialogue.json @@ -0,0 +1,58 @@ +{ + "box": "speech_bubble_talking", + "boxState": "normal", + "dialogue": [ + { + "portrait": "kade", + "expression": "normal", + "text": "I have no idea what Psych engine is." + }, + { + "portrait": "bf2", + "text": "vine boom" + }, + { + "portrait": "kade", + "text": "I really wish I didn't make engines.\nBecause I made fucking the worse\ndecision of my life." + }, + { + "text": "Because all these engines either\nfucking suck." + }, + { + "portrait": "bf2", + "text": "vine boom" + }, + { + "portrait": "kade", + "text": "They copy me." + }, + { + "portrait": "bf2", + "text": "vine boom" + }, + { + "portrait": "kade", + "text": "Or they copy somebody else." + }, + { + "portrait": "bf2", + "text": "vine boom" + }, + { + "portrait": "kade", + "text": "Nobodys original." + }, + { + "portrait": "bf2", + "text": "vine boom" + }, + { + "portrait": "kade", + "text": "Its fucking really stupid." + }, + { + "portrait": "bf2", + "text": "kade\nwtf dude" + } + ] +} \ No newline at end of file diff --git a/assets/songs/senpai/dialogue.json b/assets/songs/senpai/dialogue.json new file mode 100644 index 000000000..1f370dddc --- /dev/null +++ b/assets/songs/senpai/dialogue.json @@ -0,0 +1,18 @@ +{ + "box": "dialogueBox-pixel", + "boxState": "normal", + "dialogue": [ + { + "portrait": "senpai", + "expression": "normal", + "text": "Ah, a new fair maiden has come in search of true love!" + }, + { + "text": "A serenade between gentlemen shall decide where her beautiful heart shall reside." + }, + { + "portrait": "bf-pixel", + "text": "Beep bo bop" + } + ] +} \ No newline at end of file diff --git a/assets/songs/test/dialogue.json b/assets/songs/test/dialogue.json new file mode 100644 index 000000000..a4893f047 --- /dev/null +++ b/assets/songs/test/dialogue.json @@ -0,0 +1,68 @@ +{ + "box": "speech_bubble_talking", + "boxState": "normal", + "dialogue": [ + { + "portrait": "bf", + "expression": "normal", + "text": "hey" + }, + { + "portrait": "bf", + "expression": "normal", + "text": "do you know sawcon" + }, + { + "portrait": "bf", + "expression": "normal", + "text": "whats sawcon lmao" + }, + { + "portrait": "bf", + "expression": "smug", + "text": "sawcon deez nuts" + }, + { + "portrait": "bf", + "expression": "smug", + "text": "idiot" + }, + { + "portrait": "bf", + "expression": "mad", + "text": "I fucking hate you." + }, + { + "portrait": "bf-pixel", + "expression": "normal", + "text": "I am going to kick your ass.", + "boxState": "yell", + "scale": 1.4 + }, + { + "portrait": "bf", + "expression": "mad", + "text": "Bet.", + "boxState": "normal", + "scale": 0.8 + }, + { + "portrait": "bf", + "expression": "smug", + "text": "'lololololololol' .'`?!\nline break test.`?!\ncool", + "speed": 10 + }, + { + "portrait": "bf", + "expression": "mad", + "text": "Shut the fuck up...\nShut the fuck up...... !!'? .''?!", + "boxState": "yell", + "speed": 0.5 + }, + { + "portrait": "bf", + "expression": "mad", + "text": "Seriously!\nI cant believe I hate you!" + } + ] +} \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 48a3e1047..9d639ec98 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -16,6 +16,7 @@ We hope you learn something new! :caption: Tutorials tutorials/1-getting-started + tutorials/dialogue .. Indices and tables diff --git a/docs/tutorials/dialogue.rst b/docs/tutorials/dialogue.rst new file mode 100644 index 000000000..16d804076 --- /dev/null +++ b/docs/tutorials/dialogue.rst @@ -0,0 +1,83 @@ +Dialogue +============ + +================== +Creating Dialogue +================== + +To add dialogue to a song, create a dialogue.json file in the songs directory. +(``songs/tutorial/dialogue.json``) + +The example dialogue.json file has the following structure. + +.. code-block:: json + + { + "box": "speech_bubble_talking", + "boxState": "normal", + "dialogue": [ + { + "events": [], + "portrait": "", + "expression": "", + "text": "" + }, + { + etc... + } + } + +================== +dialogue.json +================== + +A list of each value that can be used to create dialogue. + +:box: + The box to be used for the entire dialogue. *CANNOT BE CHANGED MID DIALOGUE* ``"text": ""`` + +:boxState: + The *default* animation state to use for the current box. ``"boxState": ""`` + + +The ``dialogue`` object is where each page of the dialogue is stored. + +All values that can be assigned are the following: + +:text: + The text to be used for the current page of dialogue. ``"text": ""`` + +:portrait: + The name of the portrait to use for the current dialogue. ``"portrait": ""`` + +:expression: + The expression to use for the current portrait. ``"expression": ""`` + +:events: + Events have different values depending on what event it is. ``"events": [["type", arguments], etc...]`` + + List of current event types: + + image: + :file: + The location of the image file used. + *If you would like the image to be animated, this needs to be an array. + Formatted as follows.* ``[images/..., animation, loops]`` + + :x: + The images X position. + + :y: + The images Y position. + + :x scale: + The images X scale factor. + + :y scale: + The images Y scale factor. + + sound: + :file: + The location of the sound file. +:boxState: + The animation state to use for the current box. ``"boxState": ""`` \ No newline at end of file diff --git a/source/Init.hx b/source/Init.hx index e49e83efe..c7bcc43e1 100644 --- a/source/Init.hx +++ b/source/Init.hx @@ -113,6 +113,7 @@ class Init extends FlxState "Enables the custom Forever Engine titlescreen! (only effective with a restart)", FORCED ], + 'Skip Cutscenes' => [false, 0, 'Skip the cutscenes in story mode. (Includes Dialogue)'], 'SM-like Judgements' => [ false, 0, @@ -123,6 +124,7 @@ class Init extends FlxState 0, "When enabled, displays the amount of combo breaks you have in a song." ], + ]; public static var trueSettings:Map = []; diff --git a/source/Paths.hx b/source/Paths.hx index 079fb0d84..9eaa77c02 100644 --- a/source/Paths.hx +++ b/source/Paths.hx @@ -113,7 +113,7 @@ class Paths inline static public function json(key:String, ?library:String) { - return getPath('data/$key.json', TEXT, library); + return getPath('songs/$key.json', TEXT, library); } inline static public function songJson(song:String, secondSong:String, ?library:String) diff --git a/source/gameFolder/gameObjects/userInterface/DialogueBox.hx b/source/gameFolder/gameObjects/userInterface/DialogueBox.hx index bd3f36815..009c0d3fa 100644 --- a/source/gameFolder/gameObjects/userInterface/DialogueBox.hx +++ b/source/gameFolder/gameObjects/userInterface/DialogueBox.hx @@ -1,6 +1,65 @@ package gameFolder.gameObjects.userInterface; +import flixel.FlxBasic; +import flixel.FlxG; +import flixel.FlxSprite; import flixel.group.FlxSpriteGroup; +import flixel.math.FlxPoint; +import flixel.text.FlxText; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; +import flixel.util.FlxColor; +import flixel.util.FlxTimer; +import gameFolder.meta.data.dependency.FNFSprite; +import gameFolder.meta.data.font.Alphabet; + +typedef PortraitDataDef = +{ + var name:String; + var expressions:Array; + var position:Null; + var offset:Null>; + var scale:Null; + var antialiasing:Null; + var flipX:Null; + var loop:Null; + + var sounds:Null>; + var soundChance:Null; + var soundPath:Null; +} + +typedef DialogueDataDef = +{ + var events:Array>; + var portrait:String; + var expression:String; + var text:Null; + var boxState:Null; + + var speed:Null; + var scale:Null; +} + +typedef BoxDataDef = +{ + var position:Null>; + var textPos:Null>; + var scale:Null; + var antialiasing:Null; + var singleFrame:Null; + var doFlip:Null; + var bgColor:Null>; + + var states:Null; +} + +typedef DialogueFileDataDef = +{ + var box:String; + var boxState:Null; + var dialogue:Array; +} class DialogueBox extends FlxSpriteGroup { @@ -10,15 +69,477 @@ class DialogueBox extends FlxSpriteGroup nothing yet :P */ - public static function createDialogue(thisDialogue:Array):DialogueBox + var box:FNFSprite; + var bgFade:FlxSprite; + var portrait:FNFSprite; + var text:FlxText; + var alphabetText:Alphabet; + + var dialogueData:DialogueFileDataDef; + var portraitData:PortraitDataDef; + var boxData:BoxDataDef; + + var curPage:Int = 0; + var curCharacter:String; + var curExpression:String; + var curBoxState:String; + + var eventImage:Null; + + public var whenDaFinish:Void->Void; + + var textStarted:Bool = false; + + public static function createDialogue(thisDialogue:String):DialogueBox { // var newDialogue = new DialogueBox(false, thisDialogue); return newDialogue; } - public function new(?talkingRight:Bool = false, ?dialogueList:Array) + function dialoguePath(file:String):String + { + var dialoguePath = Paths.file('assets/images/dialogue/portraits/$curCharacter/$file'); + var truePath = Paths.file(file); + + // load the json file + if (sys.FileSystem.exists(dialoguePath)) + return dialoguePath; + else + return truePath; + } + + public function new(?talkingRight:Bool = false, ?daDialogue:String) { super(); + + trace("start"); + + // get dialog data from dialogue.json + dialogueData = haxe.Json.parse(daDialogue); + + dialogDataCheck(); + + // background fade + bgFade = new FlxSprite(-200, -200).makeGraphic(Std.int(FlxG.width * 1.3), Std.int(FlxG.height * 1.3), FlxColor.BLACK); + bgFade.scrollFactor.set(); + bgFade.alpha = 0; + add(bgFade); + + // add the dialog box + box = new FNFSprite(0, 370); + + // cur portrait + portrait = new FNFSprite(800, 160); + + // thank u sammu for fixing alphabet.hx + // i dont wanna touch it ever + alphabetText = new Alphabet(100, 425, "cool", false, true, 0.7); + + // text + text = new FlxText(100, 480, 1000, "", 35); + text.color = FlxColor.BLACK; + text.visible = false; + + updateDialog(true); + + // add stuff + add(portrait); + add(box); + add(text); + + add(alphabetText); + + // skip text + var skipText = new FlxText(100, 670, 1000, "PRESS SHIFT TO SKIP", 20); + skipText.alignment = FlxTextAlign.CENTER; + + skipText.borderStyle = FlxTextBorderStyle.OUTLINE; + skipText.borderColor = FlxColor.BLACK; + skipText.borderSize = 3; + + skipText.screenCenter(X); + add(skipText); + } + + function updateDialog(force:Bool = false) + { + // set current portrait + updateTextBox(force); + updatePortrait(force); + updateEvents(force); + + var pageData = dialogueData.dialogue[curPage]; + + var startText:Void->Void = function() + { + // Text update + var textToDisplay = "lol u need text for dialog"; + + if (pageData.text != null) + textToDisplay = pageData.text; + + alphabetText.startText(textToDisplay, true); + } + + // change speed + if (pageData.speed != null) + alphabetText.textSpeed = 0.06 / pageData.speed; + else + alphabetText.textSpeed = 0.06; + + // change size + if (pageData.scale != null) + alphabetText.textSize = 0.7 * pageData.scale; + else + alphabetText.textSize = 0.7; + + // If no text has shown up yet, we need to wait a moment + if (textStarted == false) + { + // Set the text to nothing for now + alphabetText.startText('', true); + // To prevent awkward text not against a dialogue background, a quick fix is to delay the initial text + new FlxTimer().start(0.375, function(tmr:FlxTimer) + { + textStarted = true; + startText(); + }); + } + // If the text has started, build the text + else + startText(); + } + + function updateTextBox(force:Bool = false) + { + var curBox = dialogueData.box; + var newState = dialogueData.dialogue[curPage].boxState; + + if (force && newState == null) + newState = dialogueData.boxState; + + if (newState == null) + return; + + if (curBoxState != newState || force) + { + curBoxState = newState; + + // get the path to the json + var boxJson = Paths.file('images/dialogue/boxes/$curBox/$curBox.json'); + + // load the json and sprite + boxData = haxe.Json.parse(sys.io.File.getContent(boxJson)); + box.frames = Paths.getSparrowAtlas('dialogue/boxes/$curBox/$curBox'); + + // get the states sectioon + var curStateData = Reflect.field(boxData.states, curBoxState); + + if (curStateData == null) + return; + + // default and open animations + var defaultAnim:Array = Reflect.field(curStateData, "default"); + var openAnim:Array = Reflect.field(curStateData, "open"); + + // make sure theres atleast a offset if things are null + if (defaultAnim[1] == null) + defaultAnim[1] = [0, 0]; + + if (openAnim[1] == null) + openAnim[1] = [0, 0]; + + // check if single frame + if (boxData.singleFrame == null) + boxData.singleFrame = false; + + // do flip + if (boxData.doFlip == null) + boxData.doFlip = true; + + if (boxData.bgColor != null) + { + var colorArray = boxData.bgColor; + var newColor = FlxColor.fromRGB(colorArray[0], colorArray[1], colorArray[2]); + + bgFade = new FlxSprite(-200, -200).makeGraphic(Std.int(FlxG.width * 1.3), Std.int(FlxG.height * 1.3), newColor); + bgFade.scrollFactor.set(); + bgFade.alpha = 0; + add(bgFade); + } + + // add the animations + box.animation.addByPrefix('normal', defaultAnim[0], 24, true); + box.addOffset('normal', defaultAnim[1][0], defaultAnim[1][1]); + + box.animation.addByPrefix('normalOpen', openAnim[0], 24, false); + box.addOffset('normalOpen', openAnim[1][0], openAnim[1][1]); + + // if the box doesnt have a position set it to 0 0 + if (boxData.position == null) + boxData.position = [0, 0]; + + box.x = boxData.position[0]; + box.y = boxData.position[1]; + + // other stuff + if (boxData.scale == null) + boxData.scale = 1; + + if (boxData.antialiasing == null) + boxData.antialiasing = true; + + box.scale = new FlxPoint(boxData.scale, boxData.scale); + box.antialiasing = boxData.antialiasing; + + if (boxData.textPos != null) + { + text.x = boxData.textPos[0]; + text.y = boxData.textPos[1]; + } + + box.playAnim('normalOpen'); + } + } + + function updatePortrait(force:Bool = false) + { + var newChar = dialogueData.dialogue[curPage].portrait; + + if (curCharacter != newChar || force) + { + if (newChar != null) + { + // made the curCharacter the new character + curCharacter = newChar; + var portraitJson = Paths.file('images/dialogue/portraits/$curCharacter/$curCharacter.json'); + + // load the json file + if (sys.FileSystem.exists(portraitJson)) + { + portraitData = haxe.Json.parse(sys.io.File.getContent(portraitJson)); + portrait.frames = Paths.getSparrowAtlas('dialogue/portraits/$curCharacter/$curCharacter'); + } + + // check if the animation loops for the talking anim lol + var loop = true; + if (portraitData.loop != null) + loop = portraitData.loop; + + // loop through the expressions and add the to the list of expressions + for (n in Reflect.fields(portraitData.expressions)) + { + var curAnim = Reflect.field(portraitData.expressions, n); + var animName = n; + + portrait.animation.addByPrefix(animName, curAnim, 24, loop); + } + + // check for null values + if (portraitData.scale == null) + portraitData.scale = 1; + + if (portraitData.antialiasing == null) + portraitData.antialiasing = true; + + // change some smaller values + portrait.scale.set(portraitData.scale, portraitData.scale); + portrait.antialiasing = portraitData.antialiasing; + + // position and flip stuff + // honestly + var newX = 850; + var newY = 160; + var enterX = -20; + var newFlip = false; + + if (Std.is(portraitData.position, String)) + { + switch (portraitData.position) + { + case "left": + newX = 10; + enterX = -enterX; + newFlip = true; + case "middle": + newX = 400; + } + } + else if (Std.is(portraitData.position, Array)) + { + if (portraitData.flipX) + enterX = -enterX; + + newX = portraitData.position[0]; + newY = portraitData.position[1]; + } + + if (portraitData.offset == null) + portraitData.offset = [0, 0]; + + newX -= portraitData.offset[0]; + newY -= portraitData.offset[1]; + + portrait.x = newX - enterX; + portrait.y = newY; + + // flip + if (portraitData.flipX != null) + newFlip = portraitData.flipX; + + portrait.flipX = newFlip; + + // update bloops + if (portraitData.sounds != null) + { + if (portraitData.soundPath != null) + alphabetText.beginPath = "assets/" + portraitData.soundPath; + else + alphabetText.beginPath = 'assets/images/dialogue/portraits/$curCharacter/'; + + alphabetText.soundChoices = portraitData.sounds; + + if (portraitData.soundChance != null) + alphabetText.soundChance = portraitData.soundChance; + else + alphabetText.soundChance = 40; + } + else + alphabetText.soundChance = 0; + + // flip check + if (boxData.doFlip == true) + box.flipX = newFlip; + + // this causes problems, and i know exactly what the problem is... i just cant fix it + // basically i need to get rid of the last tween before doing a new one, or else the portraits slide around all over the place + // ngl its kinda funny + FlxTween.tween(portrait, {x: newX + enterX}, 0.2, {ease: FlxEase.quadInOut}); + } + } + + // change expressions + var newExpression = dialogueData.dialogue[curPage].expression; + if (newExpression != null) + curExpression = newExpression; + + portrait.animation.play(curExpression); + } + + function runEvent(eventArray:Array) + { + var event = eventArray[0]; + + switch (event) + { + case "image": + var _sprite:Dynamic = eventArray[1]; + var _x = eventArray[2]; + var _y = eventArray[3]; + var _scaleX = eventArray[4]; + var _scaleY = eventArray[5]; + + trace(Paths.file(_sprite)); + + eventImage = new FlxSprite(_x, _y); + + if (Std.is(_sprite, Array)) + { + eventImage.frames = Paths.getSparrowAtlas(_sprite[0]); + + eventImage.animation.addByPrefix("anim", _sprite[1], 24, _sprite[2]); + eventImage.animation.play("anim"); + } + else + { + eventImage.loadGraphic(Paths.file(_sprite + ".png")); + } + + eventImage.scale.set(_scaleX, _scaleY); + add(eventImage); + + case "sound": + var _sound = eventArray[1] + "." + Paths.SOUND_EXT; + + trace(Paths.file(_sound)); + + FlxG.sound.play(Paths.file(_sound)); + } + } + + function updateEvents(force:Bool = false) + { + var curEvents = dialogueData.dialogue[curPage].events; + + if (eventImage != null) + eventImage.destroy(); + + // do da current vent + if (curEvents == null) + return; + + for (event in curEvents) + { + trace(event); + runEvent(event); + } + } + + // mario + function closeDialog() + { + whenDaFinish(); + alphabetText.playSounds = false; + kill(); + } + + function dialogDataCheck() + { + var tisOkay = true; + + if (dialogueData.box == null) + tisOkay = false; + if (dialogueData.dialogue == null) + tisOkay = false; + + if (!tisOkay) + closeDialog(); + } + + override function update(elapsed:Float) + { + if (box.animation.finished) + { + if (boxData.singleFrame != true) + box.playAnim('normal'); + + text.visible = true; + } + + portrait.animation.paused = alphabetText.finishedLine; + if (portrait.animation.paused) + portrait.animation.finish(); + + bgFade.alpha += 0.02; + if (bgFade.alpha > 0.6) + bgFade.alpha = 0.6; + + if (FlxG.keys.justPressed.SHIFT) + closeDialog(); + + if (FlxG.keys.justPressed.ANY && textStarted) + { + FlxG.sound.play(Paths.sound('cancelMenu')); + + curPage += 1; + + if (curPage == dialogueData.dialogue.length) + closeDialog() + else + updateDialog(); + } + + super.update(elapsed); } } diff --git a/source/gameFolder/meta/data/font/Alphabet.hx b/source/gameFolder/meta/data/font/Alphabet.hx index 7de15494a..d81d5964a 100644 --- a/source/gameFolder/meta/data/font/Alphabet.hx +++ b/source/gameFolder/meta/data/font/Alphabet.hx @@ -17,7 +17,11 @@ using StringTools; */ class Alphabet extends FlxSpriteGroup { - public var delay:Float = 0.05; + public var textSpeed:Float = 0.06; + public var randomSpeed:Bool = false; // When enabled, it'll change the speed of the text speed randomly between 80% and 180% + + public var textSize:Float; + public var paused:Bool = false; // for menu shit @@ -39,6 +43,8 @@ class Alphabet extends FlxSpriteGroup public var widthOfWords:Float = FlxG.width; + public var finishedLine:Bool = false; + var yMulti:Float = 1; // custom shit @@ -51,22 +57,32 @@ class Alphabet extends FlxSpriteGroup var isBold:Bool = false; - public function new(x:Float, y:Float, text:String = "", ?bold:Bool = false, typed:Bool = false) + public var soundChoices:Array = ["GF_1", "GF_2", "GF_3", "GF_4",]; + public var beginPath:String = "assets/sounds/"; + public var soundChance:Int = 40; + public var playSounds:Bool = true; + public var lastPlayed:Int = 0; + + public function new(x:Float, y:Float, text:String = "", ?bold:Bool = false, typed:Bool = false, ?textSize:Float = 1) { super(x, y); this.text = text; isBold = bold; + this.textSize = textSize; - restartText(text, typed); + startText(text, typed); } - public function restartText(text, typed) + public function startText(newText, typed) { + yMulti = 1; + finishedLine = false; xPosResetted = true; - _finalText = text; - textInit = text; + _finalText = newText; + textInit = newText; + this.text = newText; if (text != "") { @@ -79,6 +95,22 @@ class Alphabet extends FlxSpriteGroup addText(); } } + else + { + if (swagTypingTimer != null) + { + destroyText(); + swagTypingTimer.cancel(); + swagTypingTimer.destroy(); + } + } + } + + function destroyText():Void + { + for (_sprite in _sprites.copy()) + _sprite.destroy(); + clear(); } public var arrayLetters:Array; @@ -91,10 +123,6 @@ class Alphabet extends FlxSpriteGroup var xPos:Float = 0; for (character in splitWords) { - // if (character.fastCodeAt() == " ") - // { - // } - if (character == " " || character == "-") lastWasSpace = true; @@ -102,7 +130,6 @@ class Alphabet extends FlxSpriteGroup var isSymbol:Bool = AlphaCharacter.symbols.contains(character); if ((AlphaCharacter.alphabet.indexOf(character.toLowerCase()) != -1) || (AlphaCharacter.numbers.contains(character))) - // if (AlphaCharacter.alphabet.contains(character.toLowerCase())) { if (xPosResetted) { @@ -121,8 +148,7 @@ class Alphabet extends FlxSpriteGroup lastWasSpace = false; } - // var letter:AlphaCharacter = new AlphaCharacter(30 * loopNum, 0); - var letter:AlphaCharacter = new AlphaCharacter(xPos, 0); + var letter:AlphaCharacter = new AlphaCharacter(xPos, 0, textSize); if (isBold) letter.createBold(character); @@ -141,35 +167,36 @@ class Alphabet extends FlxSpriteGroup lastSprite = letter; } - - // loopNum += 1; } - - // add(displayedLetters); } function doSplitWords():Void - { splitWords = _finalText.split(""); - } public var personTalking:String = 'gf'; + public var swagTypingTimer:FlxTimer; + public function startTypedText():Void { _finalText = text; doSplitWords(); - // trace(arrayShit); + // Remove all the old garbage + destroyText(); var loopNum:Int = 0; var xPos:Float = 0; var curRow:Int = 0; - new FlxTimer().start(0.05, function(tmr:FlxTimer) + // Forget any potential old timers + if (swagTypingTimer != null) + swagTypingTimer.destroy(); + + // Create a new timer + swagTypingTimer = new FlxTimer().start(textSpeed, function(tmr:FlxTimer) { - // trace(_finalText.fastCodeAt(loopNum) + " " + _finalText.charAt(loopNum)); if (_finalText.fastCodeAt(loopNum) == "\n".code) { yMulti += 1; @@ -191,15 +218,11 @@ class Alphabet extends FlxSpriteGroup #end if (AlphaCharacter.alphabet.indexOf(splitWords[loopNum].toLowerCase()) != -1 || isNumber || isSymbol) - // if (AlphaCharacter.alphabet.contains(splitWords[loopNum].toLowerCase()) || isNumber || isSymbol) - { if (lastSprite != null && !xPosResetted) { lastSprite.updateHitbox(); xPos += lastSprite.width + 3; - // if (isBold) - // xPos -= 80; } else { @@ -212,10 +235,7 @@ class Alphabet extends FlxSpriteGroup xPos += 20; lastWasSpace = false; } - // trace(_finalText.fastCodeAt(loopNum) + " " + _finalText.charAt(loopNum)); - - // var letter:AlphaCharacter = new AlphaCharacter(30 * loopNum, 0); - var letter:AlphaCharacter = new AlphaCharacter(xPos, 55 * yMulti); + var letter:AlphaCharacter = new AlphaCharacter(xPos, 55 * yMulti, textSize); letter.row = curRow; if (isBold) { @@ -233,11 +253,20 @@ class Alphabet extends FlxSpriteGroup letter.x += 90; } - if (FlxG.random.bool(40)) + if (FlxG.random.bool(soundChance) || lastPlayed > 2) { - var daSound:String = "GF_"; - FlxG.sound.play(Paths.soundRandom(daSound, 1, 4)); + if (playSounds) + { + lastPlayed = 0; + + var cur = FlxG.random.int(0, soundChoices.length - 1); + var daSound:String = beginPath + soundChoices[cur] + "." + Paths.SOUND_EXT; + + FlxG.sound.play(daSound); + } } + else + lastPlayed += 1; add(letter); @@ -246,8 +275,18 @@ class Alphabet extends FlxSpriteGroup loopNum += 1; - tmr.time = FlxG.random.float(0.04, 0.09); - }, splitWords.length); + if (randomSpeed) + tmr.time = FlxG.random.float(0.8 * textSpeed, 1.8 * textSpeed); + + // I'm sorry for this implementation being a bit janky but the FlxTimer loops were not reliable for this + // Hope you forgive me <3 <3 xoxo Sammu + // i forgive u sammu :D + if (loopNum >= splitWords.length) + { + finishedLine = true; + tmr.destroy(); + } + }, 0); } override function update(elapsed:Float) @@ -271,7 +310,7 @@ class Alphabet extends FlxSpriteGroup arrayLetters[i].destroy(); // lastSprite = null; - restartText(text, false); + startText(text, false); } super.update(elapsed); @@ -288,9 +327,12 @@ class AlphaCharacter extends FlxSprite public var row:Int = 0; - public function new(x:Float, y:Float) + private var textSize:Float = 1; + + public function new(x:Float, y:Float, ?textSize:Float = 1) { super(x, y); + this.textSize = textSize; var tex = Paths.getSparrowAtlas('UI/default/base/alphabet'); frames = tex; @@ -304,6 +346,7 @@ class AlphaCharacter extends FlxSprite // or just load regular text animation.addByPrefix(letter, letter.toUpperCase() + " bold", 24); animation.play(letter); + scale.set(textSize, textSize); updateHitbox(); } } @@ -318,12 +361,13 @@ class AlphaCharacter extends FlxSprite animation.addByPrefix(letter, letter + " " + letterCase, 24); animation.play(letter); + scale.set(textSize, textSize); updateHitbox(); FlxG.log.add('the row' + row); y = (110 - height); - y += row * 60; + y += row * 50; } public function createNumber(letter:String):Void @@ -341,17 +385,23 @@ class AlphaCharacter extends FlxSprite case '.': animation.addByPrefix(letter, 'period', 24); animation.play(letter); - y += 50; + setGraphicSize(8, 8); + y += 48; case "'": animation.addByPrefix(letter, 'apostraphie', 24); animation.play(letter); - y -= 0; + setGraphicSize(10, 10); + y += 20; case "?": animation.addByPrefix(letter, 'question mark', 24); animation.play(letter); + setGraphicSize(20, 40); + y += 16; case "!": animation.addByPrefix(letter, 'exclamation point', 24); animation.play(letter); + setGraphicSize(10, 40); + y += 16; default: animation.addByPrefix(letter, letter, 24); animation.play(letter); diff --git a/source/gameFolder/meta/data/font/Dialogue.hx b/source/gameFolder/meta/data/font/Dialogue.hx new file mode 100644 index 000000000..061b3d721 --- /dev/null +++ b/source/gameFolder/meta/data/font/Dialogue.hx @@ -0,0 +1,73 @@ +package gameFolder.meta.data.font; + +import flixel.group.FlxSpriteGroup; +import flixel.math.FlxPoint; +import flixel.util.FlxTimer; +import gameFolder.meta.data.font.Alphabet.AlphaCharacter; + +using StringTools; + +class Dialogue extends FlxSpriteGroup +{ + public var textSpeed:Float = 0.05; + public var textSize:Float = 1; + public var textPosition:Int = 0; + + private var _buildText:String; + private var _finalText:String; + + private var splitWords:Array = []; + + private var dialogueMaxSize:FlxPoint; + + public function new(x:Float, y:Float, text:String = "", width:Int, height:Int) + { + super(x, y); + _finalText = text; + dialogueMaxSize = new FlxPoint(width, height); + } + + public function buildText():Void + { + // Reset values on the hypothetical that the text was started over + textPosition = 0; + + var curRow = 0; + var loopNum = 0; + var lastWasSpace = false; + + // Clear out all the old sprites if there are any + if (_sprites.length > 0) + { + for (_sprite in _sprites) + { + _sprite.destroy(); + } + clear(); + } + + // Split all of the text into an array + splitWords = _finalText.split(""); + + new FlxTimer().start(textSpeed, function(timer:FlxTimer) + { + if (_finalText.fastCodeAt(loopNum) == "\n".code) + { + curRow++; + } + + if (splitWords[curRow] == " ") + { + lastWasSpace = true; + } + + #if (haxe >= "4.0.0") + var isNumber:Bool = AlphaCharacter.numbers.contains(splitWords[curRow]); + var isSymbol:Bool = AlphaCharacter.symbols.contains(splitWords[curRow]); + #else + var isNumber:Bool = AlphaCharacter.numbers.indexOf(splitWords[curRow]) != -1; + var isSymbol:Bool = AlphaCharacter.symbols.indexOf(splitWords[curRow]) != -1; + #end + }); + } +} diff --git a/source/gameFolder/meta/state/PlayState.hx b/source/gameFolder/meta/state/PlayState.hx index 886cdd805..45bd00057 100644 --- a/source/gameFolder/meta/state/PlayState.hx +++ b/source/gameFolder/meta/state/PlayState.hx @@ -326,7 +326,8 @@ class PlayState extends MusicBeatState // // call the funny intro cutscene depending on the song - if (isStoryMode) + var freeplayOverride = true; + if (isStoryMode || freeplayOverride) songIntroCutscene(); else startCountdown(); @@ -581,7 +582,8 @@ class PlayState extends MusicBeatState } } - if (generatedMusic) + // if the song is generated + if (generatedMusic && startedCountdown) { for (strumline in strumLines) { @@ -1479,12 +1481,18 @@ class PlayState extends MusicBeatState FlxG.sound.play(Paths.sound('ANGRY')); // schoolIntro(doof); default: - if (sys.FileSystem.exists(Paths.txt(SONG.song.toLowerCase() + '/' + SONG.song.toLowerCase() + 'Dialogue'))) + var dialogPath = Paths.json(SONG.song.toLowerCase() + '/dialogue'); + + if (!Init.trueSettings.get('Skip Cutscenes') && sys.FileSystem.exists(dialogPath)) { + startedCountdown = false; + var dialogueBox:DialogueBox; - dialogueBox = DialogueBox.createDialogue(CoolUtil.coolTextFile(Paths.txt(SONG.song.toLowerCase() + '/' + SONG.song.toLowerCase() - + 'Dialogue'))); + dialogueBox = DialogueBox.createDialogue(sys.io.File.getContent(dialogPath)); dialogueBox.cameras = [camHUD]; + dialogueBox.whenDaFinish = startCountdown; + + add(dialogueBox); } else startCountdown(); @@ -1501,6 +1509,8 @@ class PlayState extends MusicBeatState startTimer = new FlxTimer().start(Conductor.crochet / 1000, function(tmr:FlxTimer) { + startedCountdown = true; + charactersDance(curBeat); var introAssets:Map> = new Map>(); diff --git a/source/gameFolder/meta/state/menus/OptionsMenuState.hx b/source/gameFolder/meta/state/menus/OptionsMenuState.hx index cd8257252..5067913bd 100644 --- a/source/gameFolder/meta/state/menus/OptionsMenuState.hx +++ b/source/gameFolder/meta/state/menus/OptionsMenuState.hx @@ -66,6 +66,7 @@ class OptionsMenuState extends MusicBeatState ['Centered Notefield', getFromOption], ['Ghost Tapping', getFromOption], ['Display Accuracy', getFromOption], + ['Skip Cutscenes', getFromOption], ['Display Miss Count', getFromOption], // ['', null],