diff --git a/assets/data/dialogue/boxes/default.xml b/assets/data/dialogue/boxes/default.xml index 2f95670f..99cf3859 100644 --- a/assets/data/dialogue/boxes/default.xml +++ b/assets/data/dialogue/boxes/default.xml @@ -1,5 +1,5 @@ - + diff --git a/assets/data/dialogue/boxes/senpai.xml b/assets/data/dialogue/boxes/senpai.xml new file mode 100644 index 00000000..cb163d61 --- /dev/null +++ b/assets/data/dialogue/boxes/senpai.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/dialogue/boxes/pixel/dialogueBox.png b/assets/images/dialogue/boxes/pixel/dialogueBox.png new file mode 100644 index 00000000..c7d88ff5 Binary files /dev/null and b/assets/images/dialogue/boxes/pixel/dialogueBox.png differ diff --git a/assets/images/dialogue/boxes/pixel/dialogueBox.xml b/assets/images/dialogue/boxes/pixel/dialogueBox.xml new file mode 100644 index 00000000..4414787b --- /dev/null +++ b/assets/images/dialogue/boxes/pixel/dialogueBox.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/songs/roses/dialogue.xml b/assets/songs/roses/dialogue.xml new file mode 100644 index 00000000..e058c1d5 --- /dev/null +++ b/assets/songs/roses/dialogue.xml @@ -0,0 +1,17 @@ + + + + + + + + + Not bad for an ugly worm. + + + But this time I'll rip your nuts off right after your girlfriend finishes gargling mine. + + + Bop beep be be skdoo bep + + \ No newline at end of file diff --git a/assets/songs/roses/end-cutscene.hx b/assets/songs/roses/end-cutscene.hx index 885a2b55..e265ba2c 100644 --- a/assets/songs/roses/end-cutscene.hx +++ b/assets/songs/roses/end-cutscene.hx @@ -1,4 +1,3 @@ -static var roses_shouldPlayOnThorns = false; var self = this; __script__.setParent(PlayState.instance); @@ -36,10 +35,7 @@ function create() { FlxG.camera.followLerp = 0; FlxTween.tween(camGame.scroll, {x: -366, y: 192}, 3, { ease: FlxEase.cubeInOut, - onComplete: (_) -> { - roses_shouldPlayOnThorns = true; - self.close(); - } + onComplete: (_) -> {self.close();} }); } }); diff --git a/assets/songs/senpai/dialogue.xml b/assets/songs/senpai/dialogue.xml new file mode 100644 index 00000000..2b2a081a --- /dev/null +++ b/assets/songs/senpai/dialogue.xml @@ -0,0 +1,17 @@ + + + + + + + + + Ah, a new fair maiden has come in search of true love! + + + A serenade between gentlemen shall decide where her beautiful heart shall reside. + + + Beep bo bop + + \ No newline at end of file diff --git a/assets/songs/thorns/creepyDialogue.xml b/assets/songs/thorns/creepyDialogue.xml new file mode 100644 index 00000000..bfa5c80e --- /dev/null +++ b/assets/songs/thorns/creepyDialogue.xml @@ -0,0 +1,22 @@ + + + + + + + + Direct contact with real humans, after being trapped in here for so long... + + + and HER of all people. + + + I'll make her father pay for what he's done to me and all the others,,,, + + + I'll beat you and make you take my place. + + + You don't mind your bodies being borrowed right? It's only fair... + + \ No newline at end of file diff --git a/assets/songs/thorns/cutscene.hx b/assets/songs/thorns/cutscene.hx index 88369443..9d0be5b8 100644 --- a/assets/songs/thorns/cutscene.hx +++ b/assets/songs/thorns/cutscene.hx @@ -1,6 +1,5 @@ import flixel.addons.util.FlxSimplex; -static var roses_shouldPlayOnThorns = false; var self = this; __script__.setParent(PlayState.instance); @@ -9,13 +8,6 @@ var senpaiEvil:FlxSprite; var dfx = 0; var dfy = 0; function create() { - // Cutscene stuff - if(!roses_shouldPlayOnThorns) { - disableScript(); - self.close(); - return; - } - roses_shouldPlayOnThorns = false; camHUD.visible = false; var red:FlxSprite = new FlxSprite().makeSolid(FlxG.width + 100, FlxG.height + 100, 0xFFff1b31); @@ -64,7 +56,7 @@ function create() { white.destroy(); camHUD.visible = true; - self.close(); + self.startDialogue("songs/" + PlayState.instance.SONG.meta.name.toLowerCase() + "/creepyDialogue.xml", self.close); } }); diff --git a/assets/sounds/Senpai_Dies.mp3 b/assets/sounds/Senpai_Dies.mp3 deleted file mode 100644 index e5749a14..00000000 Binary files a/assets/sounds/Senpai_Dies.mp3 and /dev/null differ diff --git a/assets/sounds/Senpai_Dies.ogg b/assets/sounds/Senpai_Dies.ogg deleted file mode 100644 index 68647de8..00000000 Binary files a/assets/sounds/Senpai_Dies.ogg and /dev/null differ diff --git a/assets/sounds/pixel/clickText.mp3 b/assets/sounds/pixel/clickText.mp3 new file mode 100644 index 00000000..b34a82c0 Binary files /dev/null and b/assets/sounds/pixel/clickText.mp3 differ diff --git a/source/funkin/backend/utils/XMLUtil.hx b/source/funkin/backend/utils/XMLUtil.hx index b7120680..1d9aa526 100644 --- a/source/funkin/backend/utils/XMLUtil.hx +++ b/source/funkin/backend/utils/XMLUtil.hx @@ -61,16 +61,14 @@ class XMLUtil { } /** - * Creates a new sprite based on a XML node. + * Overrides a sprite based on a XML node. */ - public static function createSpriteFromXML(node:Access, parentFolder:String = "", defaultAnimType:XMLAnimType = BEAT, ?cl:Class):FunkinSprite { + public static function loadSpriteFromXML(spr:FunkinSprite, node:Access, parentFolder:String = "", defaultAnimType:XMLAnimType = BEAT):FunkinSprite { if (parentFolder == null) parentFolder = ""; - var spr:FunkinSprite = cl != null ? Type.createInstance(cl, []) : new FunkinSprite(); spr.name = node.getAtt("name"); spr.antialiasing = true; - - spr.loadSprite(Paths.image('$parentFolder${node.getAtt("sprite")}', null, true)); + spr.loadSprite(Paths.image('$parentFolder${node.getAtt("sprite").getDefault(spr.name)}', null, true)); spr.spriteAnimType = defaultAnimType; if (node.has.type) { @@ -88,15 +86,14 @@ class XMLUtil { if (node.has.scroll) { var scroll:Null = Std.parseFloat(node.att.scroll); if (scroll.isNotNull()) spr.scrollFactor.set(scroll, scroll); - } else { - if (node.has.scrollx) { - var scroll:Null = Std.parseFloat(node.att.scrollx); - if (scroll.isNotNull()) spr.scrollFactor.x = scroll; - } - if (node.has.scrolly) { - var scroll:Null = Std.parseFloat(node.att.scrolly); - if (scroll.isNotNull()) spr.scrollFactor.y = scroll; - } + } + if (node.has.scrollx) { + var scroll:Null = Std.parseFloat(node.att.scrollx); + if (scroll.isNotNull()) spr.scrollFactor.x = scroll; + } + if (node.has.scrolly) { + var scroll:Null = Std.parseFloat(node.att.scrolly); + if (scroll.isNotNull()) spr.scrollFactor.y = scroll; } if (node.has.skewx) { var skew:Null = Std.parseFloat(node.att.skewx); @@ -107,6 +104,14 @@ class XMLUtil { if (skew.isNotNull()) spr.skew.y = skew; } if (node.has.antialiasing) spr.antialiasing = node.att.antialiasing == "true"; + if (node.has.width) { + var width:Null = Std.parseFloat(node.att.width); + if (width.isNotNull()) spr.width = width; + } + if (node.has.height) { + var height:Null = Std.parseFloat(node.att.height); + if (height.isNotNull()) spr.height = height; + } if (node.has.scale) { var scale:Null = Std.parseFloat(node.att.scale); if (scale.isNotNull()) spr.scale.set(scale, scale); @@ -119,6 +124,18 @@ class XMLUtil { var scale:Null = Std.parseFloat(node.att.scaley); if (scale.isNotNull()) spr.scale.y = scale; } + if (node.has.graphicSize) { + var graphicSize:Null = Std.parseInt(node.att.graphicSize); + if (graphicSize.isNotNull()) spr.setGraphicSize(graphicSize, graphicSize); + } + if (node.has.graphicSizex) { + var graphicSizex:Null = Std.parseInt(node.att.graphicSizex); + if (graphicSizex.isNotNull()) spr.setGraphicSize(graphicSizex); + } + if (node.has.graphicSizey) { + var graphicSizey:Null = Std.parseInt(node.att.graphicSizey); + if (graphicSizey.isNotNull()) spr.setGraphicSize(0, graphicSizey); + } if (node.has.updateHitbox && node.att.updateHitbox == "true") spr.updateHitbox(); if(node.has.zoomfactor) @@ -156,6 +173,15 @@ class XMLUtil { return spr; } + /** + * Creates a new sprite based on a XML node. + */ + public static inline function createSpriteFromXML(node:Access, parentFolder:String = "", defaultAnimType:XMLAnimType = BEAT, ?cl:Class, ?args:Array):FunkinSprite { + if(cl == null) cl = FunkinSprite; + if(args == null) args = []; + return loadSpriteFromXML(Type.createInstance(cl, args), node, parentFolder, defaultAnimType); + } + public static function extractAnimFromXML(anim:Access, animType:XMLAnimType = NONE, loop:Bool = false):AnimData { var animData:AnimData = { name: null, diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 99d4608c..ee9b9bc5 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -801,7 +801,7 @@ class PlayState extends MusicBeatState */ public function startCutscene(prefix:String = "", ?cutsceneScriptPath:String, ?callback:Void->Void, checkSeen:Bool = false) { if (callback == null) callback = startCountdown; - if (checkSeen && seenCutscene) { + if ((checkSeen && seenCutscene) || !playCutscenes) { callback(); return; } @@ -809,34 +809,31 @@ class PlayState extends MusicBeatState if (cutsceneScriptPath == null) cutsceneScriptPath = Paths.script('songs/${SONG.meta.name.toLowerCase()}/${prefix}cutscene'); - if (playCutscenes) { - inCutscene = true; - var videoCutscene = Paths.video('${PlayState.SONG.meta.name.toLowerCase()}-${prefix}cutscene'); - var videoCutsceneAlt = Paths.file('songs/${PlayState.SONG.meta.name.toLowerCase()}/${prefix}cutscene.mp4'); - var dialogue = Paths.file('songs/${PlayState.SONG.meta.name.toLowerCase()}/${prefix}dialogue.xml'); - persistentUpdate = true; - var toCall:Void->Void = function() { - if(checkSeen) seenCutscene = true; - callback(); - } + inCutscene = true; + var videoCutscene = Paths.video('${PlayState.SONG.meta.name.toLowerCase()}-${prefix}cutscene'); + var videoCutsceneAlt = Paths.file('songs/${PlayState.SONG.meta.name.toLowerCase()}/${prefix}cutscene.mp4'); + var dialogue = Paths.file('songs/${PlayState.SONG.meta.name.toLowerCase()}/${prefix}dialogue.xml'); + persistentUpdate = true; + var toCall:Void->Void = function() { + if(checkSeen) seenCutscene = true; + callback(); + } - if (cutsceneScriptPath != null && Assets.exists(cutsceneScriptPath)) { - openSubState(new ScriptedCutscene(cutsceneScriptPath, toCall)); - } else if (Assets.exists(dialogue)) { - MusicBeatState.skipTransIn = true; - openSubState(new DialogueCutscene(dialogue, toCall)); - } else if (Assets.exists(videoCutsceneAlt)) { - MusicBeatState.skipTransIn = true; - persistentUpdate = false; - openSubState(new VideoCutscene(videoCutsceneAlt, toCall)); - persistentDraw = false; - } else if (Assets.exists(videoCutscene)) { - MusicBeatState.skipTransIn = true; - persistentUpdate = false; - openSubState(new VideoCutscene(videoCutscene, toCall)); - persistentDraw = false; - } else - callback(); + if (cutsceneScriptPath != null && Assets.exists(cutsceneScriptPath)) { + openSubState(new ScriptedCutscene(cutsceneScriptPath, toCall)); + } else if (Assets.exists(dialogue)) { + MusicBeatState.skipTransIn = true; + openSubState(new DialogueCutscene(dialogue, toCall)); + } else if (Assets.exists(videoCutsceneAlt)) { + MusicBeatState.skipTransIn = true; + persistentUpdate = false; + openSubState(new VideoCutscene(videoCutsceneAlt, toCall)); + persistentDraw = false; + } else if (Assets.exists(videoCutscene)) { + MusicBeatState.skipTransIn = true; + persistentUpdate = false; + openSubState(new VideoCutscene(videoCutscene, toCall)); + persistentDraw = false; } else callback(); } diff --git a/source/funkin/game/cutscenes/dialogue/DialogueBox.hx b/source/funkin/game/cutscenes/dialogue/DialogueBox.hx index 20262a04..296be07c 100644 --- a/source/funkin/game/cutscenes/dialogue/DialogueBox.hx +++ b/source/funkin/game/cutscenes/dialogue/DialogueBox.hx @@ -18,13 +18,16 @@ class DialogueBox extends FunkinSprite { super(); try { dialogueBoxData = new Access(Xml.parse(Assets.getText(Paths.xml('dialogue/boxes/$name'))).firstElement()); + if(!dialogueBoxData.has.sprite) dialogueBoxData.x.set("sprite", name); + XMLUtil.loadSpriteFromXML(this, dialogueBoxData, "dialogue/boxes/", NONE); + visible = false; - loadSprite(Paths.image('dialogue/boxes/${dialogueBoxData.getAtt('sprite').getDefault(name)}')); + var preX:Float = x; + screenCenter(X); x += preX; + y += FlxG.height - height; - for(anim in dialogueBoxData.nodes.anim) { - if (!anim.has.name) continue; - XMLUtil.addXMLAnimation(this, anim); - } + if(dialogueBoxData.has.textSound) textTypeSFX = Paths.sound(dialogueBoxData.att.textSound); + if(dialogueBoxData.has.nextSound) nextSFX = Paths.sound(dialogueBoxData.att.nextSound); for(pos in dialogueBoxData.nodes.charpos) { if (!pos.has.name) continue; @@ -35,16 +38,6 @@ class DialogueBox extends FunkinSprite { }; } - if (dialogueBoxData.has.textSound) textTypeSFX = Paths.sound(dialogueBoxData.att.textSound); - if (dialogueBoxData.has.nextSound) nextSFX = Paths.sound(dialogueBoxData.att.nextSound); - - antialiasing = dialogueBoxData.getAtt("antialiasing").getDefault("true") == "true"; - screenCenter(X); - y = FlxG.height - height; - if (dialogueBoxData.has.x) x += Std.parseFloat(dialogueBoxData.att.x).getDefault(0); - if (dialogueBoxData.has.y) y += Std.parseFloat(dialogueBoxData.att.y).getDefault(0); - visible = false; - var textNode = dialogueBoxData.node.text; if (textNode == null) throw "The dialog box XML requires one text element."; @@ -57,6 +50,16 @@ class DialogueBox extends FunkinSprite { text.font = Paths.font('${textNode.getAtt("font").getDefault("vcr.ttf")}'); text.antialiasing = textNode.getAtt("antialiasing").getDefault("false") == "true"; text.sounds = [FlxG.sound.load(textTypeSFX)]; + if(textNode.has.borderStyle) { + text.borderStyle = switch(textNode.att.borderStyle.trim().toLowerCase()) { + case "none": NONE; + case "shadow": SHADOW; + case "outline_fast": OUTLINE_FAST; + default: OUTLINE; + } + text.borderSize = Std.parseFloat(textNode.getAtt("borderSize")).getDefault(1); + text.borderColor = textNode.getAtt("borderColor").getColorFromDynamic().getDefault(0xFFFFFFFF); + } } catch(e) { active = false; Logs.trace('Couldn\'t load dialogue box "$name": ${e.toString()}', ERROR);