From 3c03ffce2c7c3a770d2b83429033e57c25ea80c5 Mon Sep 17 00:00:00 2001 From: NeeEoo Date: Mon, 26 Feb 2024 10:53:29 +0100 Subject: [PATCH 1/3] Optimization to updateNote --- source/funkin/game/StrumLine.hx | 34 +++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index 1ab58628..68320514 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -163,31 +163,33 @@ class StrumLine extends FlxTypedGroup { } public inline function updateNotes() { + __updateNote_songPos = Conductor.songPosition; + if(__updateNote_event == null) __updateNote_event = PlayState.instance.__updateNote_event; notes.forEach(updateNote); } var __updateNote_strum:Strum; + var __updateNote_songPos:Float; + var __updateNote_event:NoteUpdateEvent; public function updateNote(daNote:Note) { __updateNote_strum = members[daNote.noteData]; if (__updateNote_strum == null) return; - PlayState.instance.__updateNote_event.recycle(daNote, FlxG.elapsed, __updateNote_strum); - onNoteUpdate.dispatch(PlayState.instance.__updateNote_event); - if (PlayState.instance.__updateNote_event.cancelled) return; + __updateNote_event.recycle(daNote, FlxG.elapsed, __updateNote_strum); + onNoteUpdate.dispatch(__updateNote_event); + if (__updateNote_event.cancelled) return; - var songPosition = Conductor.songPosition; + if (__updateNote_event.__updateHitWindow) { + daNote.canBeHit = (daNote.strumTime > __updateNote_songPos - (PlayState.instance.hitWindow * daNote.latePressWindow) + && daNote.strumTime < __updateNote_songPos + (PlayState.instance.hitWindow * daNote.earlyPressWindow)); - if (PlayState.instance.__updateNote_event.__updateHitWindow) { - daNote.canBeHit = (daNote.strumTime > songPosition - (PlayState.instance.hitWindow * daNote.latePressWindow) - && daNote.strumTime < songPosition + (PlayState.instance.hitWindow * daNote.earlyPressWindow)); - - if (daNote.strumTime < songPosition - PlayState.instance.hitWindow && !daNote.wasGoodHit) + if (daNote.strumTime < __updateNote_songPos - PlayState.instance.hitWindow && !daNote.wasGoodHit) daNote.tooLate = true; } - if (cpu && PlayState.instance.__updateNote_event.__autoCPUHit && !daNote.avoid&& !daNote.wasGoodHit && daNote.strumTime < songPosition) PlayState.instance.goodNoteHit(this, daNote); + if (cpu && __updateNote_event.__autoCPUHit && !daNote.avoid && !daNote.wasGoodHit && daNote.strumTime < __updateNote_songPos) PlayState.instance.goodNoteHit(this, daNote); - if (daNote.wasGoodHit && daNote.isSustainNote && daNote.strumTime + (daNote.sustainLength) < songPosition) { + if (daNote.wasGoodHit && daNote.isSustainNote && daNote.strumTime + (daNote.sustainLength) < __updateNote_songPos) { deleteNote(daNote); return; } @@ -199,11 +201,11 @@ class StrumLine extends FlxTypedGroup { } - if (PlayState.instance.__updateNote_event.strum == null) return; + if (__updateNote_event.strum == null) return; - if (PlayState.instance.__updateNote_event.__reposNote) PlayState.instance.__updateNote_event.strum.updateNotePosition(daNote); + if (__updateNote_event.__reposNote) __updateNote_event.strum.updateNotePosition(daNote); if (daNote.isSustainNote) - daNote.updateSustain(PlayState.instance.__updateNote_event.strum); + daNote.updateSustain(__updateNote_event.strum); } var __funcsToExec:ArrayVoid> = []; @@ -302,10 +304,10 @@ class StrumLine extends FlxTypedGroup { animPrefix = strumAnimPrefix[i % strumAnimPrefix.length]; var babyArrow:Strum = new Strum(startingPos.x + ((Note.swagWidth * strumScale) * i), startingPos.y); babyArrow.ID = i; - + if(data.scrollSpeed != null) babyArrow.scrollSpeed = data.scrollSpeed; - + var event = EventManager.get(StrumCreationEvent).recycle(babyArrow, PlayState.instance.strumLines.members.indexOf(this), i, animPrefix); event.__doAnimation = !MusicBeatState.skipTransIn; event = PlayState.instance.scripts.event("onStrumCreation", event); From 0ef969db1f4b41d3817825f443279bd45bc8f9f7 Mon Sep 17 00:00:00 2001 From: NeeEoo Date: Mon, 26 Feb 2024 19:43:30 +0100 Subject: [PATCH 2/3] a --- .github/workflows/windows.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 1c93ad8c..6925d8ef 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -2,6 +2,7 @@ name: Windows Build on: push: + workflow_dispatch: jobs: build: name: Windows Build From 9ca7f018669ef4bd729c3402ee6af337d241ca2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=8D=9A=7ENex?= <87421482+NexIsDumb@users.noreply.github.com> Date: Tue, 27 Feb 2024 01:50:21 +0100 Subject: [PATCH 3/3] finalizing the dialogue system 11 more stuff and text formats! Co-Authored-By: Ne_Eo --- assets/data/dialogue/boxes/default.xml | 4 +- .../data/dialogue/boxes/hating-simulator.xml | 2 +- assets/data/dialogue/characters/senpai.xml | 2 +- assets/songs/test/dialogue.xml | 8 +- assets/songs/thorns/cutscene.hx | 2 +- .../events/DialogueBoxSetTextEvent.hx | 12 -- .../backend/scripting/events/DynamicEvent.hx | 12 ++ .../DialogueBoxCharPopupEvent.hx | 4 +- .../DialogueBoxPlayBubbleEvent.hx | 5 +- .../dialogue/DialogueBoxSetTextEvent.hx | 15 +++ .../DialogueBoxStructureEvent.hx | 2 +- .../{ => dialogue}/DialogueCharHideEvent.hx | 2 +- .../{ => dialogue}/DialogueCharShowEvent.hx | 2 +- .../DialogueCharStructureEvent.hx | 2 +- .../{ => dialogue}/DialogueStructureEvent.hx | 2 +- source/funkin/backend/utils/CoolUtil.hx | 44 +++++++ source/funkin/backend/utils/XMLUtil.hx | 49 ++++++++ source/funkin/game/SplashGroup.hx | 118 +++++++++--------- .../funkin/game/cutscenes/DialogueCutscene.hx | 37 +++--- .../game/cutscenes/dialogue/DialogueBox.hx | 44 +++++-- .../cutscenes/dialogue/DialogueCharacter.hx | 4 +- 21 files changed, 261 insertions(+), 111 deletions(-) delete mode 100644 source/funkin/backend/scripting/events/DialogueBoxSetTextEvent.hx create mode 100644 source/funkin/backend/scripting/events/DynamicEvent.hx rename source/funkin/backend/scripting/events/{ => dialogue}/DialogueBoxCharPopupEvent.hx (68%) rename source/funkin/backend/scripting/events/{ => dialogue}/DialogueBoxPlayBubbleEvent.hx (71%) create mode 100644 source/funkin/backend/scripting/events/dialogue/DialogueBoxSetTextEvent.hx rename source/funkin/backend/scripting/events/{ => dialogue}/DialogueBoxStructureEvent.hx (83%) rename source/funkin/backend/scripting/events/{ => dialogue}/DialogueCharHideEvent.hx (82%) rename source/funkin/backend/scripting/events/{ => dialogue}/DialogueCharShowEvent.hx (84%) rename source/funkin/backend/scripting/events/{ => dialogue}/DialogueCharStructureEvent.hx (83%) rename source/funkin/backend/scripting/events/{ => dialogue}/DialogueStructureEvent.hx (79%) diff --git a/assets/data/dialogue/boxes/default.xml b/assets/data/dialogue/boxes/default.xml index f40a3196..b67c4ab0 100644 --- a/assets/data/dialogue/boxes/default.xml +++ b/assets/data/dialogue/boxes/default.xml @@ -8,9 +8,9 @@ - + - + - + - yo its me your enemy! how are you + yo its me your enemy! how are you shut up u stupid bitch @@ -59,4 +59,10 @@ ok now stfu and lets rap its getting annoying + + Wanna have sex later cutie + + + bro what + \ No newline at end of file diff --git a/assets/songs/thorns/cutscene.hx b/assets/songs/thorns/cutscene.hx index ee84365e..79a46451 100644 --- a/assets/songs/thorns/cutscene.hx +++ b/assets/songs/thorns/cutscene.hx @@ -47,7 +47,7 @@ function create() { if(FlxG.camera._fxFadeAlpha > 0.3) swagTimer.reset(); else { camHUD.visible = true; - self.startDialogue("songs/" + PlayState.instance.SONG.meta.name.toLowerCase() + "/creepyDialogue.xml", self.close); + self.startDialogue("assets/songs/" + PlayState.instance.SONG.meta.name.toLowerCase() + "/creepyDialogue.xml", self.close); } }); }); diff --git a/source/funkin/backend/scripting/events/DialogueBoxSetTextEvent.hx b/source/funkin/backend/scripting/events/DialogueBoxSetTextEvent.hx deleted file mode 100644 index b8b7072a..00000000 --- a/source/funkin/backend/scripting/events/DialogueBoxSetTextEvent.hx +++ /dev/null @@ -1,12 +0,0 @@ -package funkin.backend.scripting.events; - -import flixel.sound.FlxSound; - -final class DialogueBoxSetTextEvent extends CancellableEvent -{ - public var text:String; - - public var speed:Float; - - public var customTypeSFX:Array; -} \ No newline at end of file diff --git a/source/funkin/backend/scripting/events/DynamicEvent.hx b/source/funkin/backend/scripting/events/DynamicEvent.hx new file mode 100644 index 00000000..077fec27 --- /dev/null +++ b/source/funkin/backend/scripting/events/DynamicEvent.hx @@ -0,0 +1,12 @@ +package funkin.backend.scripting.events; + +/** + * Event with random dynamic values useful for making your own custom events + */ +final class DynamicEvent extends CancellableEvent { + public var value1:Dynamic = null; + public var value2:Dynamic = null; + public var value3:Dynamic = null; + public var value4:Dynamic = null; + public var value5:Dynamic = null; +} \ No newline at end of file diff --git a/source/funkin/backend/scripting/events/DialogueBoxCharPopupEvent.hx b/source/funkin/backend/scripting/events/dialogue/DialogueBoxCharPopupEvent.hx similarity index 68% rename from source/funkin/backend/scripting/events/DialogueBoxCharPopupEvent.hx rename to source/funkin/backend/scripting/events/dialogue/DialogueBoxCharPopupEvent.hx index 3b36c160..ad098040 100644 --- a/source/funkin/backend/scripting/events/DialogueBoxCharPopupEvent.hx +++ b/source/funkin/backend/scripting/events/dialogue/DialogueBoxCharPopupEvent.hx @@ -1,8 +1,10 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.dialogue; import funkin.game.cutscenes.dialogue.DialogueCharacter; final class DialogueBoxCharPopupEvent extends CancellableEvent { public var char:DialogueCharacter; + + public var force:Bool; } \ No newline at end of file diff --git a/source/funkin/backend/scripting/events/DialogueBoxPlayBubbleEvent.hx b/source/funkin/backend/scripting/events/dialogue/DialogueBoxPlayBubbleEvent.hx similarity index 71% rename from source/funkin/backend/scripting/events/DialogueBoxPlayBubbleEvent.hx rename to source/funkin/backend/scripting/events/dialogue/DialogueBoxPlayBubbleEvent.hx index 0baf7a65..66363a4d 100644 --- a/source/funkin/backend/scripting/events/DialogueBoxPlayBubbleEvent.hx +++ b/source/funkin/backend/scripting/events/dialogue/DialogueBoxPlayBubbleEvent.hx @@ -1,5 +1,6 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.dialogue; +import funkin.backend.utils.XMLUtil.TextFormat; import flixel.sound.FlxSound; final class DialogueBoxPlayBubbleEvent extends CancellableEvent @@ -10,6 +11,8 @@ final class DialogueBoxPlayBubbleEvent extends CancellableEvent public var text:String; + public var format:Array; + public var speed:Float; public var customSFX:FlxSound; diff --git a/source/funkin/backend/scripting/events/dialogue/DialogueBoxSetTextEvent.hx b/source/funkin/backend/scripting/events/dialogue/DialogueBoxSetTextEvent.hx new file mode 100644 index 00000000..91c3d113 --- /dev/null +++ b/source/funkin/backend/scripting/events/dialogue/DialogueBoxSetTextEvent.hx @@ -0,0 +1,15 @@ +package funkin.backend.scripting.events.dialogue; + +import funkin.backend.utils.XMLUtil.TextFormat; +import flixel.sound.FlxSound; + +final class DialogueBoxSetTextEvent extends CancellableEvent +{ + public var text:String; + + public var format:Array; + + public var speed:Null; + + public var customTypeSFX:Array; +} \ No newline at end of file diff --git a/source/funkin/backend/scripting/events/DialogueBoxStructureEvent.hx b/source/funkin/backend/scripting/events/dialogue/DialogueBoxStructureEvent.hx similarity index 83% rename from source/funkin/backend/scripting/events/DialogueBoxStructureEvent.hx rename to source/funkin/backend/scripting/events/dialogue/DialogueBoxStructureEvent.hx index 83923d09..cabda907 100644 --- a/source/funkin/backend/scripting/events/DialogueBoxStructureEvent.hx +++ b/source/funkin/backend/scripting/events/dialogue/DialogueBoxStructureEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.dialogue; import haxe.xml.Access; diff --git a/source/funkin/backend/scripting/events/DialogueCharHideEvent.hx b/source/funkin/backend/scripting/events/dialogue/DialogueCharHideEvent.hx similarity index 82% rename from source/funkin/backend/scripting/events/DialogueCharHideEvent.hx rename to source/funkin/backend/scripting/events/dialogue/DialogueCharHideEvent.hx index 8b372643..c06cce27 100644 --- a/source/funkin/backend/scripting/events/DialogueCharHideEvent.hx +++ b/source/funkin/backend/scripting/events/dialogue/DialogueCharHideEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.dialogue; import funkin.game.cutscenes.dialogue.DialogueCharacter.DialogueCharAnimContext; diff --git a/source/funkin/backend/scripting/events/DialogueCharShowEvent.hx b/source/funkin/backend/scripting/events/dialogue/DialogueCharShowEvent.hx similarity index 84% rename from source/funkin/backend/scripting/events/DialogueCharShowEvent.hx rename to source/funkin/backend/scripting/events/dialogue/DialogueCharShowEvent.hx index acdb26dc..5f9456d8 100644 --- a/source/funkin/backend/scripting/events/DialogueCharShowEvent.hx +++ b/source/funkin/backend/scripting/events/dialogue/DialogueCharShowEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.dialogue; import funkin.game.cutscenes.dialogue.DialogueCharacter.DialogueCharAnimContext; diff --git a/source/funkin/backend/scripting/events/DialogueCharStructureEvent.hx b/source/funkin/backend/scripting/events/dialogue/DialogueCharStructureEvent.hx similarity index 83% rename from source/funkin/backend/scripting/events/DialogueCharStructureEvent.hx rename to source/funkin/backend/scripting/events/dialogue/DialogueCharStructureEvent.hx index 11503096..c518179b 100644 --- a/source/funkin/backend/scripting/events/DialogueCharStructureEvent.hx +++ b/source/funkin/backend/scripting/events/dialogue/DialogueCharStructureEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.dialogue; import haxe.xml.Access; diff --git a/source/funkin/backend/scripting/events/DialogueStructureEvent.hx b/source/funkin/backend/scripting/events/dialogue/DialogueStructureEvent.hx similarity index 79% rename from source/funkin/backend/scripting/events/DialogueStructureEvent.hx rename to source/funkin/backend/scripting/events/dialogue/DialogueStructureEvent.hx index da4d5da4..97ac8a03 100644 --- a/source/funkin/backend/scripting/events/DialogueStructureEvent.hx +++ b/source/funkin/backend/scripting/events/dialogue/DialogueStructureEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.dialogue; import haxe.xml.Access; diff --git a/source/funkin/backend/utils/CoolUtil.hx b/source/funkin/backend/utils/CoolUtil.hx index d73304c1..67d0b4c0 100644 --- a/source/funkin/backend/utils/CoolUtil.hx +++ b/source/funkin/backend/utils/CoolUtil.hx @@ -1,5 +1,8 @@ package funkin.backend.utils; +import flixel.text.FlxText; +import openfl.text.TextFormatAlign; +import funkin.backend.utils.XMLUtil.TextFormat; import flixel.tweens.FlxTween; import flixel.system.frontEnds.SoundFrontEnd; import flixel.sound.FlxSound; @@ -457,6 +460,47 @@ class CoolUtil return xml.att.resolve(name); } + /** + * Sets automatically all the compatible formats to a text. + * + * WARNING: These are dependant from the font, so if the font doesn't support for example the `bold` format it won't work! + * @param text Text to set the format for + * @param formats Array of the formats (to get the formats from a node, you can use `XMLUtil.getTextFormats(node)`) + */ + public static function autoSetFormat(text:FlxText, formats:Array) { + var i = 0; + @:privateAccess + for(format in formats) { + var fmtt = format.format; + var start = i; + var end = i + format.text.length; + i = end; + if(Reflect.fields(fmtt).length == 0) continue; + var fmt = new FlxTextFormat(); + + fmt.format.color = Reflect.hasField(fmtt, "color") ? FlxColor.fromString(fmtt.color) : text.color; + fmt.format.font = Reflect.hasField(fmtt, "font") ? Paths.font(fmtt.font) : text.font; + fmt.format.size = Reflect.hasField(fmtt, "size") ? Std.parseInt(fmtt.size) : text.size; + fmt.format.italic = Reflect.hasField(fmtt, "italic") ? fmtt.italic == "true" : text.italic; + fmt.format.bold = Reflect.hasField(fmtt, "bold") ? fmtt.bold == "true" : text.bold; + fmt.borderColor = Reflect.hasField(fmtt, "borderColor") ? FlxColor.fromString(fmtt.borderColor) : text.borderColor; + fmt.format.align = Reflect.hasField(fmtt, "align") ? TextFormatAlign.fromString(fmtt.align) : FlxTextAlign.toOpenFL(text.alignment); + + if(Reflect.hasField(fmtt, "leading")) fmt.format.leading = Std.parseInt(fmtt.leading); + if(Reflect.hasField(fmtt, "kerning")) fmt.format.kerning = fmtt.kerning == "true"; + if(Reflect.hasField(fmtt, "blockIndent")) fmt.format.blockIndent = Std.parseInt(fmtt.blockIndent); + if(Reflect.hasField(fmtt, "bullet")) fmt.format.bullet = fmtt.bullet == "true"; + if(Reflect.hasField(fmtt, "indent")) fmt.format.indent = Std.parseInt(fmtt.indent); + if(Reflect.hasField(fmtt, "leftMargin")) fmt.format.leftMargin = Std.parseInt(fmtt.leftMargin); + if(Reflect.hasField(fmtt, "letterSpacing")) fmt.format.letterSpacing = Std.parseFloat(fmtt.letterSpacing); + if(Reflect.hasField(fmtt, "rightMargin")) fmt.format.rightMargin = Std.parseInt(fmtt.rightMargin); + if(Reflect.hasField(fmtt, "tabStops")) fmt.format.tabStops = [for(x in cast(fmtt.tabStops, String).split(",")) Std.parseInt(x)]; + if(Reflect.hasField(fmtt, "underline")) fmt.format.underline = fmtt.underline == "true"; + text.addFormat(fmt, start, end); + } + return text; + } + /** * Loads an animated graphic, and automatically animates it. * @param spr Sprite to load the graphic for diff --git a/source/funkin/backend/utils/XMLUtil.hx b/source/funkin/backend/utils/XMLUtil.hx index 7058ae28..0ac1bef7 100644 --- a/source/funkin/backend/utils/XMLUtil.hx +++ b/source/funkin/backend/utils/XMLUtil.hx @@ -5,9 +5,13 @@ import funkin.backend.system.ErrorCode; import funkin.backend.FunkinSprite.XMLAnimType; import flixel.util.FlxColor; import haxe.xml.Access; +import flixel.util.typeLimit.OneOfTwo; import funkin.backend.system.interfaces.IOffsetCompatible; using StringTools; + +typedef TextFormat = { text:String, format:Dynamic } + /** * Class made to make XML parsing easier. */ @@ -279,6 +283,51 @@ class XMLUtil { var v:String; return [for(l in text.split("\n")) if ((v = l.trim()) != "") v].join("\n"); } + + /** + * WARNING: will edit directly the node! + */ + public static function fixSpacingInNode(node:Access):Access { + var arr = [for(x in node.x) x]; + for(i => n in arr) { + if(n.nodeType == PCData) { + if(i == 0) n.nodeValue = n.nodeValue.ltrim(); + if(i == arr.length - 1) n.nodeValue = n.nodeValue.rtrim(); + if(n.nodeValue.contains("\n")) { + var a = n.nodeValue.split("\n"); + n.nodeValue = [for(i => x in a) i == 0 ? x.rtrim() : i == arr.length - 1 ? x.ltrim() : x.trim()].join("\n"); + } + } + } + return node; + } + + public static function getTextFormats(_node:OneOfTwo, currentFormat:Dynamic = null, parsedSegments:Array = null):Array { + var node:Xml = cast _node; + if (currentFormat == null) + currentFormat = {}; + if (parsedSegments == null) + parsedSegments = []; + + for (child in node) { + switch (child.nodeType) { + case Element: + if (child.nodeName == "format") { + var format:Dynamic = Reflect.copy(currentFormat); + @:privateAccess for (key => name in child.attributeMap) { + Reflect.setField(format, key, name); + } + getTextFormats(child, format, parsedSegments); + } + case PCData: + parsedSegments.push({ text: child.nodeValue, format: Reflect.copy(currentFormat) }); + default: + // Ignore other node types + } + } + + return parsedSegments; + } } typedef AnimData = { diff --git a/source/funkin/game/SplashGroup.hx b/source/funkin/game/SplashGroup.hx index 1fad99d4..75008a29 100644 --- a/source/funkin/game/SplashGroup.hx +++ b/source/funkin/game/SplashGroup.hx @@ -23,82 +23,84 @@ class SplashGroup extends FlxTypedGroup { * Creates a new Splash group * @param path Path to the splash data (xml) */ - public function new(path) { + public function new(path:String) { super(); try { xml = new Access(Xml.parse(Assets.getText(path)).firstElement()); if (!xml.has.sprite) throw "The element requires a sprite attribute."; - - var splashSprite = new FunkinSprite(); - splashSprite.antialiasing = true; - splashSprite.active = splashSprite.visible = false; - splashSprite.loadSprite(Paths.image(xml.att.sprite)); - - /** - * ANIMATIONS - */ - for(strum in xml.nodes.strum) { - var id:Null = Std.parseInt(strum.att.id); - if (id != null) { - animationNames[id] = []; - for(anim in strum.nodes.anim) { - if (!anim.has.name) continue; - XMLUtil.addXMLAnimation(splashSprite, anim, false); - animationNames[id].push(anim.att.name); - } - } - } - if (animationNames.length <= 0) - animationNames.push([]); - for(anim in xml.nodes.anim) { - if (!anim.has.name) continue; - XMLUtil.addXMLAnimation(splashSprite, anim, false); - for(a in animationNames) { - if (a == null) continue; - a.push(anim.att.name); - } - } - /** - * END OF ANIMATIONS - */ - splashSprite.animation.finishCallback = function(name) { - splashSprite.active = splashSprite.visible = false; - }; - - if (xml.has.scale) splashSprite.scale.scale(Std.parseFloat(xml.att.scale).getDefault(1)); - if (xml.has.alpha) splashSprite.alpha = Std.parseFloat(xml.att.alpha).getDefault(1); - if (xml.has.antialiasing) splashSprite.antialiasing = xml.att.antialiasing == "true"; - - add(splashSprite); - - for (i in 0...7) { - // make 7 additional splashes - var spr = FunkinSprite.copyFrom(splashSprite); - spr.animation.finishCallback = function(name) { - spr.active = spr.visible = false; - }; - add(spr); - } + var splash = createSplash(xml.att.sprite); + setupAnims(xml, splash); + pregenerateSplashes(splash); + add(splash); // immediatly draw once and put image in GPU to prevent freezes // TODO: change to graphics cache - splashSprite.drawComplex(FlxG.camera); - } catch(e) { + splash.drawComplex(FlxG.camera); + } catch(e:Dynamic) { Logs.trace('Couldn\'t parse splash data for "${path}": ${e.toString()}', ERROR); valid = false; } - maxSize = 8; } + function createSplash(imagePath:String) { + var splash = new FunkinSprite(); + splash.antialiasing = true; + splash.active = splash.visible = false; + splash.loadSprite(Paths.image(imagePath)); + if (xml.has.scale) splash.scale.scale(Std.parseFloat(xml.att.scale).getDefault(1)); + if (xml.has.alpha) splash.alpha = Std.parseFloat(xml.att.alpha).getDefault(1); + if (xml.has.antialiasing) splash.antialiasing = xml.att.antialiasing == "true"; + return splash; + } + + function setupAnims(xml:Access, splash:FunkinSprite) { + for(strum in xml.nodes.strum) { + var id:Null = Std.parseInt(strum.att.id); + if (id != null) { + animationNames[id] = []; + for(anim in strum.nodes.anim) { + if (!anim.has.name) continue; + XMLUtil.addXMLAnimation(splash, anim, false); + animationNames[id].push(anim.att.name); + } + } + } + + // if (animationNames.length <= 0) + // animationNames.push([]); + + for(anim in xml.nodes.anim) { + if (!anim.has.name) continue; + XMLUtil.addXMLAnimation(splash, anim, false); + for(a in animationNames) { + if (a == null) continue; + a.push(anim.att.name); + } + } + splash.animation.finishCallback = function(name:String) { + splash.active = splash.visible = false; + }; + } + + function pregenerateSplashes(splash:FunkinSprite) { + // make 7 additional splashes + for(i in 0...7) { + var spr = FunkinSprite.copyFrom(splash); + spr.animation.finishCallback = function(name:String) { + spr.active = spr.visible = false; + }; + add(spr); + } + } + public function getSplashAnim(id:Int):String { if (animationNames.length <= 0) return null; id %= animationNames.length; - if (animationNames[id] == null) return null; - if (animationNames[id].length <= 0) return null; - return animationNames[id][FlxG.random.int(0, animationNames[id].length-1)]; + if (animationNames[id] == null || animationNames[id].length <= 0) return null; + return animationNames[id][FlxG.random.int(0, animationNames[id].length - 1)]; } var __splash:FunkinSprite; diff --git a/source/funkin/game/cutscenes/DialogueCutscene.hx b/source/funkin/game/cutscenes/DialogueCutscene.hx index d4c706e3..656df6f2 100644 --- a/source/funkin/game/cutscenes/DialogueCutscene.hx +++ b/source/funkin/game/cutscenes/DialogueCutscene.hx @@ -1,7 +1,9 @@ package funkin.game.cutscenes; import funkin.backend.utils.FunkinParentDisabler; -import funkin.backend.scripting.events.*; +import funkin.backend.scripting.events.dialogue.*; +import funkin.backend.scripting.events.CancellableEvent; +import funkin.backend.scripting.events.DynamicEvent; import funkin.backend.scripting.Script; import flixel.sound.FlxSound; import funkin.game.cutscenes.dialogue.*; @@ -49,7 +51,7 @@ class DialogueCutscene extends Cutscene { public override function create() { super.create(); - add(parentDisabler = new FunkinParentDisabler()); + add(parentDisabler = new FunkinParentDisabler()); // MESS WITH THIS IN SCRIPTS TO RESUME PLAYSTATE'S TWEENS!! - Nex try { var event = EventManager.get(DialogueStructureEvent).recycle(dialogueData); event.dialogueData = new Access(Xml.parse(Assets.getText(dialoguePath)).firstElement()); @@ -64,7 +66,7 @@ class DialogueCutscene extends Cutscene { Logs.trace('2 dialogue characters share the same name (${char.att.name}, ${char.att.name}). The old character has been replaced.'); var leChar:DialogueCharacter = new DialogueCharacter(char.att.name, char.getAtt('position').getDefault('default')); - if(char.has.defaultAnim) leChar.defaultAnim = char.getAtt('defaultAnim'); + if(char.has.defaultAnim) leChar.defaultAnim = char.att.defaultAnim; add(charMap[char.att.name] = leChar); } @@ -74,26 +76,28 @@ class DialogueCutscene extends Cutscene { // Add lines for(node in dialogueData.nodes.line) { + var formats = XMLUtil.getTextFormats(XMLUtil.fixSpacingInNode(node)); var volume:Null = 0.8; var line:DialogueLine = { - text: XMLUtil.fixXMLText(node.innerHTML), + text: [for(x in formats) x.text].join(""), + format: formats, char: node.getAtt('char').getDefault('boyfriend'), bubble: node.getAtt('bubble').getDefault('normal'), callback: node.getAtt('callback'), changeDefAnim: node.getAtt('changeDefAnim'), - speed: node.has.speed ? Std.parseFloat(node.att.speed).getDefault(0.05) : 0.05, + speed: Std.parseFloat(node.getAtt("speed")).getDefault(0.05), musicVolume: node.has.musicVolume ? (volume = Std.parseFloat(node.att.speed).getDefault(0.8)) : null, - changeMusic: node.has.changeMusic ? FlxG.sound.load(Paths.music(node.getAtt('changeMusic')), volume, true) : null, - playSound: node.has.playSound ? FlxG.sound.load(Paths.sound(node.getAtt('playSound'))) : null, - nextSound: node.has.nextSound ? FlxG.sound.load(Paths.music(node.getAtt('nextSound'))) : null, + changeMusic: node.has.changeMusic ? FlxG.sound.load(Paths.music(node.att.changeMusic), volume, true) : null, + playSound: node.has.playSound ? FlxG.sound.load(Paths.sound(node.att.playSound)) : null, + nextSound: node.has.nextSound ? FlxG.sound.load(Paths.music(node.att.nextSound)) : null, textSound: null }; - if(node.has.textSound) line.textSound = FlxG.sound.load(Paths.sound(node.getAtt('textSound'))); + if(node.has.textSound) line.textSound = FlxG.sound.load(Paths.sound(node.att.textSound)); else if(!useDef) { var char:DialogueCharacter = charMap[line.char]; if(char != null && char.charData != null && char.charData.has.textSound) - line.textSound = FlxG.sound.load(Paths.sound(char.charData.getAtt("textSound"))); + line.textSound = FlxG.sound.load(Paths.sound(char.charData.att.textSound)); } dialogueLines.push(line); @@ -107,6 +111,7 @@ class DialogueCutscene extends Cutscene { next(true); } catch(e) { Logs.trace('Error while loading dialogue at ${dialoguePath}: ${e.toString()}', ERROR); + trace(CoolUtil.getLastExceptionStack()); close(); } dialogueScript.call("postCreate"); @@ -122,7 +127,6 @@ class DialogueCutscene extends Cutscene { dialogueScript.call("update", [elapsed]); if(controls.ACCEPT) { - @:privateAccess if(dialogueBox.dialogueEnded) next(); else dialogueBox.text.skip(); } @@ -134,8 +138,9 @@ class DialogueCutscene extends Cutscene { public var canProceed:Bool = true; public function next(playFirst:Bool = false) { + var event = EventManager.get(DynamicEvent).recycle(playFirst); dialogueScript.call("next", [playFirst]); - if(!canProceed) return; + if(event.cancelled || !canProceed) return; if ((curLine = dialogueLines.shift()) == null) { if(lastLine != null && dialogueBox.hasAnimation(lastLine.bubble + "-close")) { @@ -148,7 +153,10 @@ class DialogueCutscene extends Cutscene { return; } - for(k=>c in charMap) + if (curLine.callback != null) + dialogueScript.call(curLine.callback, [playFirst]); + + for (k=>c in charMap) if (k != curLine.char) c.hide(); @@ -160,7 +168,7 @@ class DialogueCutscene extends Cutscene { } var finalSuffix:String = playFirst && dialogueBox.hasAnimation(curLine.bubble + "-firstOpen") ? "-firstOpen" : dialogueBox.hasAnimation(curLine.bubble + "-open") ? "-open" : ""; - dialogueBox.playBubbleAnim(curLine.bubble, finalSuffix, curLine.text, curLine.speed, curLine.nextSound, curLine.textSound != null ? [curLine.textSound] : null, finalSuffix == "-firstOpen" || finalSuffix == "-open", !playFirst); + dialogueBox.playBubbleAnim(curLine.bubble, finalSuffix, curLine.text, curLine.format, curLine.speed, curLine.nextSound, curLine.textSound != null ? [curLine.textSound] : null, finalSuffix == "-firstOpen" || finalSuffix == "-open", !playFirst); if(curLine.playSound != null) curLine.playSound.play(); if(curLine.changeMusic != null) { @@ -196,6 +204,7 @@ class DialogueCutscene extends Cutscene { typedef DialogueLine = { var text:String; + var format:Array; var char:String; var bubble:String; var callback:String; diff --git a/source/funkin/game/cutscenes/dialogue/DialogueBox.hx b/source/funkin/game/cutscenes/dialogue/DialogueBox.hx index 68cc5157..caf45949 100644 --- a/source/funkin/game/cutscenes/dialogue/DialogueBox.hx +++ b/source/funkin/game/cutscenes/dialogue/DialogueBox.hx @@ -1,13 +1,14 @@ package funkin.game.cutscenes.dialogue; -import funkin.backend.scripting.events.*; +import funkin.backend.scripting.events.dialogue.*; +import funkin.backend.scripting.events.PlayAnimEvent; import funkin.backend.scripting.events.PlayAnimEvent.PlayAnimContext; import funkin.backend.scripting.events.CancellableEvent; import funkin.backend.scripting.Script; import flixel.sound.FlxSound; import flixel.addons.text.FlxTypeText; -import flixel.text.FlxText; import flixel.math.FlxPoint; +import flixel.util.FlxColor; import haxe.xml.Access; class DialogueBox extends FunkinSprite { @@ -72,7 +73,7 @@ class DialogueBox extends FunkinSprite { textNode.has.width ? Std.parseInt(textNode.att.width).getDefault(FlxG.width) : FlxG.width, ""); text.color = textNode.getAtt("color").getColorFromDynamic().getDefault(0xFF000000); text.size = Std.parseInt(textNode.att.size).getDefault(20); - text.font = Paths.font('${textNode.getAtt("font").getDefault("vcr.ttf")}'); + text.font = Paths.font(textNode.getAtt("font").getDefault("vcr.ttf")); text.antialiasing = textNode.getAtt("antialiasing").getDefault("false") == "true"; if(textNode.has.borderStyle) { text.borderStyle = switch(textNode.att.borderStyle.trim().toLowerCase()) { @@ -81,6 +82,9 @@ class DialogueBox extends FunkinSprite { case "outline_fast": OUTLINE_FAST; default: OUTLINE; } + text.borderQuality = Std.parseFloat(textNode.getAtt("borderQuality")).getDefault(1); + text.shadowOffset.x = Std.parseFloat(textNode.getAtt("shadowOffsetX")).getDefault(1); + text.shadowOffset.y = Std.parseFloat(textNode.getAtt("shadowOffsetY")).getDefault(1); text.borderSize = Std.parseFloat(textNode.getAtt("borderSize")).getDefault(1); text.borderColor = textNode.getAtt("borderColor").getColorFromDynamic().getDefault(0xFFFFFFFF); } @@ -112,29 +116,31 @@ class DialogueBox extends FunkinSprite { } public function popupChar(char:DialogueCharacter, force:Bool = false) { - var event = EventManager.get(DialogueBoxCharPopupEvent).recycle(char); + var event = EventManager.get(DialogueBoxCharPopupEvent).recycle(char, force); dialogueBoxScript.call("popupChar", [event]); if (event.cancelled || event.char == null || !active) return; var pos = positions[event.char.positionName]; if (pos == null) return; - event.char.show((FlxG.width / 2) + pos.x, FlxG.height - pos.y, null, force); + event.char.show((FlxG.width / 2) + pos.x, FlxG.height - pos.y, null, event.force); + flipX = pos.flipBubble; + dialogueBoxScript.call("postPopupChar", [event]); } - public function playBubbleAnim(bubble:String, suffix:String = '', text:String = '', speed:Float = 0.05, ?customSFX:FlxSound, ?customTypeSFX:Array, setTextAfter:Bool = false, allowDefault:Bool = true) { - var event = EventManager.get(DialogueBoxPlayBubbleEvent).recycle(bubble, suffix, text, speed, customSFX, customTypeSFX, setTextAfter, allowDefault); + public function playBubbleAnim(bubble:String, suffix:String = '', text:String = '', ?format:Array, speed:Float = 0.05, ?customSFX:FlxSound, ?customTypeSFX:Array, setTextAfter:Bool = false, allowDefault:Bool = true) { + var event = EventManager.get(DialogueBoxPlayBubbleEvent).recycle(bubble, suffix, text, format, speed, customSFX, customTypeSFX, setTextAfter, allowDefault); dialogueBoxScript.call("playBubbleAnim", [event]); if(event.cancelled) return; if(event.customSFX != null) event.customSFX.play(); else if(event.allowDefault) FlxG.sound.play(nextSFX); var idk:Void->Void = () -> { - if(text != null && text.trim().length > 0) setText(event.text, event.speed, event.customTypeSFX); + if(text != null && text.trim().length > 0) startText(event.text, event.format, event.speed, event.customTypeSFX); else dialogueEnded = true; } dialogueEnded = false; - this.text.resetText(''); + resetText(); var anim:String = event.bubble + event.suffix; if(hasAnimation(anim)) playAnim(anim, true); if(!event.setTextAfter) idk(); @@ -148,16 +154,30 @@ class DialogueBox extends FunkinSprite { dialogueBoxScript.call("postPlayBubbleAnim", [event]); } - public function setText(text:String, speed:Float = 0.05, ?customTypeSFX:Array) { - var event = EventManager.get(DialogueBoxSetTextEvent).recycle(text, speed, customTypeSFX); - dialogueBoxScript.call("setText", [event]); + public function resetText(text:String = '', ?formats:Array, ?event:DialogueBoxSetTextEvent) { + if(event == null) event = EventManager.get(DialogueBoxSetTextEvent).recycle(text, formats, null, null); + dialogueBoxScript.call("resetText", [event]); if(event.cancelled) return; + this.text.clearFormats(); + if(formats != null) + this.text.autoSetFormat(formats); + this.text.resetText(event.text); + dialogueBoxScript.call("postResetText"); + } + + public function startText(text:String = '', ?format:Array, speed:Float = 0.05, ?customTypeSFX:Array) { + var event = EventManager.get(DialogueBoxSetTextEvent).recycle(text, format, speed, customTypeSFX); + dialogueBoxScript.call("startText", [event]); + if(event.cancelled) return; + + resetText(event.text, event.format, event); this.text.sounds = event.customTypeSFX != null ? event.customTypeSFX : defaultTextTypeSFX; this.text.delay = event.speed; this.text.start(event.speed, true); this.text.completeCallback = () -> dialogueEnded = true; + dialogueBoxScript.call("postStartText"); } public override function destroy() { diff --git a/source/funkin/game/cutscenes/dialogue/DialogueCharacter.hx b/source/funkin/game/cutscenes/dialogue/DialogueCharacter.hx index 73088192..97701e1f 100644 --- a/source/funkin/game/cutscenes/dialogue/DialogueCharacter.hx +++ b/source/funkin/game/cutscenes/dialogue/DialogueCharacter.hx @@ -1,7 +1,7 @@ package funkin.game.cutscenes.dialogue; -import funkin.backend.scripting.events.*; -import funkin.backend.scripting.events.PlayAnimEvent.PlayAnimContext; +import funkin.backend.scripting.events.PlayAnimEvent; +import funkin.backend.scripting.events.dialogue.*; import funkin.backend.scripting.events.CancellableEvent; import funkin.backend.scripting.Script; import flixel.tweens.FlxTween;