niko bellic

This commit is contained in:
Yoshman29
2023-01-20 22:22:35 +01:00
parent 899944fcd3
commit b7d01c2a08
26 changed files with 328 additions and 2 deletions
@@ -1,10 +1,101 @@
package funkin.cutscenes;
import flixel.system.FlxSound;
import flixel.FlxG;
import flixel.FlxCamera;
import openfl.utils.Assets;
import funkin.cutscenes.dialogue.*;
import haxe.xml.Access;
class DialogueCutscene extends Cutscene {
public var dialoguePath:String;
public var dialogueData:Access;
public var charMap:Map<String, DialogueCharacter> = [];
public var dialogueLines:Array<DialogueLine> = [];
public var curLine:DialogueLine = null;
public var dialogueBox:DialogueBox;
public var nextSFX:FlxSound;
public function new(dialoguePath:String, callback:Void->Void) {
super(callback);
this.dialoguePath = dialoguePath;
camera = new FlxCamera();
camera.bgColor = 0;
FlxG.cameras.add(camera, false);
}
public override function create() {
super.create();
try {
dialogueData = new Access(Xml.parse(Assets.getText(dialoguePath)).firstElement());
// Add characters
for(char in dialogueData.nodes.char) {
if (!char.has.name) continue;
if (charMap.exists(char.att.name))
Logs.trace('2 dialogue characters share the same name (${char.att.name}, ${char.att.name}). The old character has been replaced.');
add(charMap[char.att.name] = new DialogueCharacter(char.att.name, char.getAtt('position').getDefault('default')));
}
// Add lines
for(node in dialogueData.nodes.line) {
dialogueLines.push({
text: XMLUtil.fixXMLText(node.innerHTML),
char: node.getAtt('char').getDefault('boyfriend'),
bubble: node.getAtt('bubble').getDefault('normal'),
callback: node.getAtt('callback'),
speed: node.has.speed ? Std.parseFloat(node.att.speed).getDefault(0.02) : 0.02
});
}
// Add dialogue box
dialogueBox = new DialogueBox(dialogueData.getAtt("box").getDefault("default"));
add(dialogueBox);
next();
} catch(e) {
Logs.trace('Error while loading dialogue at ${dialoguePath}', ERROR);
close();
}
}
public override function update(elapsed:Float) {
super.update(elapsed);
if (controls.ACCEPT)
next();
}
public function next() {
if ((curLine = dialogueLines.shift()) == null) {
close();
return;
}
for(c in charMap) {
if (c.visible) {
// TODO: remove character smoothly
}
}
if (charMap[curLine.char] != null)
dialogueBox.popupChar(charMap[curLine.char]);
dialogueBox.playBubbleAnim(curLine.bubble);
}
public override function destroy() {
super.destroy();
FlxG.cameras.remove(camera);
}
}
typedef DialogueLine = {
var text:String;
var char:String;
var bubble:String;
var callback:String;
var speed:Float;
}
@@ -0,0 +1,76 @@
package funkin.cutscenes.dialogue;
import openfl.utils.Assets;
import flixel.FlxG;
import flixel.math.FlxPoint;
import haxe.xml.Access;
class DialogueBox extends FunkinSprite {
public var dialogueBoxData:Access;
public var positions:Map<String, CharPosDef> = [];
public function new(name:String) {
super();
try {
dialogueBoxData = new Access(Xml.parse(Assets.getText(Paths.xml('dialogue/boxes/$name'))).firstElement());
loadSprite(Paths.image('dialogue/boxes/${dialogueBoxData.getAtt('sprite').getDefault(name)}'));
for(anim in dialogueBoxData.nodes.anim) {
if (!anim.has.name) continue;
XMLUtil.addXMLAnimation(this, anim);
}
for(pos in dialogueBoxData.nodes.charpos) {
if (!pos.has.name) continue;
positions[pos.att.name] = {
x: pos.has.x ? Std.parseFloat(pos.att.x).getDefault(0) : 0,
y: pos.has.y ? Std.parseFloat(pos.att.y).getDefault(0) : 0,
flipBubble: pos.getAtt('flipBubble') == "true"
};
}
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;
} catch(e) {
active = false;
Logs.trace('Couldn\'t load dialogue box "$name": ${e.toString()}', ERROR);
}
}
public function popupChar(char:DialogueCharacter) {
if (!active) return;
var pos = positions[char.positionName];
if (pos == null) return;
if (!char.visible) {
char.alpha = 0;
char.visible = true;
char.setPosition(pos.x, pos.y + 100);
char.show(pos.y);
}
}
public function playBubbleAnim(bubble:String) {
if (hasAnimation('$bubble-open'))
playAnim('$bubble-open', true);
else
playAnim(bubble);
visible = true;
}
public override function destroy() {
super.destroy();
positions = null;
}
}
typedef CharPosDef = {
var x:Float;
var y:Float;
var flipBubble:Bool;
}
@@ -0,0 +1,38 @@
package funkin.cutscenes.dialogue;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import openfl.utils.Assets;
import haxe.xml.Access;
class DialogueCharacter extends FunkinSprite {
public var charData:Access;
public var curTween:FlxTween;
public var positionName:String;
public function new(name:String, position:String) {
super();
this.positionName = position;
try {
charData = new Access(Xml.parse(Assets.getText(Paths.xml('dialogue/characters/$name'))).firstElement());
loadSprite(Paths.image('dialogue/characters/${charData.getAtt('sprite').getDefault(name)}'));
antialiasing = charData.getAtt('antialiasing').getDefault('true') == 'true';
offset.set(
charData.has.x ? Std.parseFloat(charData.att.x).getDefault(0) : 0,
charData.has.y ? Std.parseFloat(charData.att.y).getDefault(0) : 0);
scale.scale(charData.has.scale ? Std.parseFloat(charData.att.scale).getDefault(1) : 1);
for(anim in charData.nodes.anim)
XMLUtil.addXMLAnimation(this, anim, true);
} catch(e) {
Logs.trace('Failed to load dialogue character $name: ${e.toString()}', ERROR);
}
visible = false;
}
public function show(y:Float) {
if (curTween != null)
curTween.cancel();
curTween = FlxTween.tween(this, {alpha: 1, y: y}, 0.5, {ease: FlxEase.cubeOut});
}
}
+7 -1
View File
@@ -696,18 +696,24 @@ class PlayState extends MusicBeatState
if (playCutscenes) {
var videoCutscene = Paths.video('${PlayState.SONG.song.toLowerCase()}-cutscene');
var videoCutsceneAlt = Paths.file('songs/${PlayState.SONG.song.toLowerCase()}/cutscene.mp4');
var dialogue = Paths.file('songs/${PlayState.SONG.song.toLowerCase()}/dialogue.xml');
persistentUpdate = false;
if (cutscene != null) {
openSubState(new ScriptedCutscene(cutscene, function() {
startCountdown();
}));
} else if (Assets.exists(dialogue)) {
FlxTransitionableState.skipNextTransIn = true;
openSubState(new DialogueCutscene(dialogue, function() {
startCountdown();
}));
} else if (Assets.exists(videoCutsceneAlt)) {
FlxTransitionableState.skipNextTransIn = true;
openSubState(new VideoCutscene(videoCutsceneAlt, function() {
startCountdown();
}));
persistentDraw = false;
}else if (Assets.exists(videoCutscene)) {
} else if (Assets.exists(videoCutscene)) {
FlxTransitionableState.skipNextTransIn = true;
openSubState(new VideoCutscene(videoCutscene, function() {
startCountdown();
+5
View File
@@ -170,6 +170,11 @@ class XMLUtil {
}
return MISSING_PROPERTY;
}
public static inline function fixXMLText(text:String) {
var v:String;
return [for(l in text.split("\n")) if ((v = l.trim()) != "") v].join("\n");
}
}
typedef AnimData = {