Flixel+Yarn base code for Ludum Dare
//COPYRIGHT (c) 2015 Robert Pfeiffer | |
//ALL RIGHTS RESERVED | |
package; | |
import flixel.FlxG; | |
import flixel.FlxObject; | |
import flixel.FlxSprite; | |
import flixel.FlxState; | |
import flixel.text.FlxText; | |
import flixel.ui.FlxButton; | |
import flixel.util.FlxMath; | |
import flixel.util.FlxAngle; | |
import flixel.util.FlxSave; | |
import flixel.util.FlxPoint; | |
import flixel.plugin.MouseEventManager; | |
private class CartoonBubble extends FlxSprite{ | |
private var dialog:FlxYarn; | |
private var parent:FlxObject; | |
private var content:String; | |
public function new(content:String, dialog:FlxYarn, | |
?parent:FlxObject, x:Float, y:Float){ | |
var w=100; | |
var h=80; | |
x-=w/2; | |
if(parent != null){ | |
var pt=cast(parent,FlxSprite).getGraphicMidpoint(); | |
if (pt.x<x){ | |
x-=20; | |
} else { | |
x+=20; | |
} | |
} | |
super(x,y); | |
loadGraphic(this.graphicPath(),false,w,h,true); | |
if(parent != null){ | |
var pt=cast(parent,FlxSprite).getGraphicMidpoint(); | |
var tag=new FlxSprite(0, 0); | |
tag.loadGraphic(this.tagPath()); | |
tag.angle=FlxAngle.getAngle(pt,this.getGraphicMidpoint()); | |
if (pt.x<x){ | |
this.stamp(tag, 0, 50); | |
} else { | |
this.stamp(tag, 70, 50); | |
} | |
} | |
var text=new FlxText(0, 0, 80, content); | |
text.setFormat(null,8,0x000000,"center"); | |
this.stamp(text, 10, 15); | |
this.parent=parent; | |
this.dialog=dialog; | |
this.content=content; | |
} | |
private function tagPath():String{ | |
return "assets/images/say.png"; | |
} | |
private function graphicPath():String{ | |
return "assets/images/speech2.png"; | |
} | |
public function onMouseDown(sprite:FlxSprite){ | |
this.onClick(); | |
MouseEventManager.remove(this); | |
destroy(); | |
} | |
public function onClick(){ | |
} | |
} | |
private class SoundBubble extends CartoonBubble{ | |
override private function graphicPath():String{ | |
return "assets/images/sound3.png"; | |
} | |
} | |
private class SpeechBubble extends CartoonBubble{ | |
override private function graphicPath():String{ | |
return "assets/images/speech3.png"; | |
} | |
override private function tagPath():String{ | |
return "assets/images/say.png"; | |
} | |
} | |
private class ThoughtBubble extends CartoonBubble{ | |
override private function tagPath():String{ | |
return "assets/images/think.png"; | |
} | |
override private function graphicPath():String{ | |
return "assets/images/thought3.png"; | |
} | |
override public function onClick(){ | |
this.dialog.action(this.content); | |
} | |
} | |
class FlxYarn | |
{ | |
var characters:Map<String,FlxObject>; | |
var conversation:String; | |
var yarn:Yarn; | |
var protagonist:FlxObject; | |
var bubbles:Array<CartoonBubble>; | |
var state:FlxState; | |
var x:Float; | |
var y: Float; | |
var index:Int; | |
var ticks:Int; | |
public function new(path:String,name:String,chars:Map<String,FlxObject>,state:FlxState,mainChar:FlxObject, x:Float, y:Float){ | |
this.yarn=new Yarn(path,name); | |
this.characters=chars; | |
this.state=state; | |
this.protagonist=mainChar; | |
this.bubbles=new Array(); | |
this.x=x; | |
this.y=y; | |
} | |
public function destroyBubbles(){ | |
for(bubble in this.bubbles){ | |
bubble.destroy(); | |
} | |
this.bubbles=new Array(); | |
} | |
public function createBubbles(){ | |
this.destroyBubbles(); | |
this.index=0; | |
this.ticks=0; | |
this.conversation=this.yarn.message(); | |
var line_rgx = ~/^([a-zA-Z0-9]+): (.*)$/m ; | |
var sound_rgx = ~/^\*(.*)\*$/m ; | |
var name:String; | |
var quip:String; | |
var sb:CartoonBubble; | |
var y1=y; | |
var lines=this.conversation.split("\n"); | |
for (line in lines){ | |
if (line_rgx.match(line)){ | |
name=line_rgx.matched(1); | |
quip=line_rgx.matched(2); | |
sb=new SpeechBubble(quip, this, this.characters[name], x, y1); | |
this.bubbles.push(sb); | |
y1+=80; | |
} | |
if (sound_rgx.match(line)){ | |
quip=sound_rgx.matched(1); | |
sb=new SoundBubble(quip, this, null, x, y1); | |
this.bubbles.push(sb); | |
y1+=80; | |
} | |
} | |
var x1=this.protagonist.x+(this.protagonist.x-x); | |
y1=y; | |
var tb:ThoughtBubble; | |
for(choice in this.yarn.choices()){ | |
sb=new ThoughtBubble(choice, this, this.protagonist, x1,y1); | |
y1+=80; | |
this.bubbles.push(sb); | |
} | |
} | |
public function update(){ | |
if(this.index<this.bubbles.length){ | |
if(this.ticks>50) { | |
this.state.add(this.bubbles[this.index]); | |
MouseEventManager.add(this.bubbles[this.index], | |
this.bubbles[this.index].onMouseDown, null, | |
null, null); | |
this.index+=1; | |
this.ticks=0; | |
} else { | |
this.ticks+=1; | |
} | |
} | |
} | |
public function action(option){ | |
yarn.transition(option); | |
this.createBubbles(); | |
} | |
} |
//COPYRIGHT (c) 2015 Robert Pfeiffer | |
//ALL RIGHTS RESERVED | |
package; | |
import flixel.util.FlxSave; | |
import haxe.Json; | |
import openfl.Assets; | |
import hscript.Interp; | |
import hscript.Parser; | |
private class YarnState{ | |
public var title:String; | |
var body:String; | |
public var transitions:Map<String,String>; | |
public var message:String; | |
public var choices:Array<String>; | |
var controller:Yarn; | |
public function run_parse():Void { | |
this.transitions=new Map(); | |
this.message=""; | |
var code=this.body; | |
var code_rgx = ~/<<([a-z]+) (.*?)>>/g; | |
var var_rgx = ~/^([a-zA-Z0-9_]+) = (.*)$/; | |
var str = code_rgx.map(code, function(r) { | |
var line = r.matched(0); | |
var mod = r.matched(1); | |
var args = r.matched(2); | |
if (mod=="print") { | |
var result = controller.interp.expr( | |
controller.parser.parseString(args)); | |
return Std.string(result); | |
} else if (mod=="run") { | |
controller.interp.expr( | |
controller.parser.parseString(args)); | |
return ""; | |
} else if (mod=="clear") { | |
controller.interp.execute( | |
controller.parser.parseString("1;")); | |
return ""; | |
} else if (mod=="include") { | |
var thestate=controller.states[args]; | |
thestate.run_parse(); | |
return thestate.message; | |
} else if (mod=="var") { | |
if (var_rgx.match(args)){ | |
var var_str = var_rgx.matched(1); | |
var val_str = var_rgx.matched(2); | |
var val = controller.interp.expr( | |
controller.parser.parseString(val_str)); | |
controller.interp.variables.set(var_str,val); | |
} else { | |
return "SYNTAX ERROR"; | |
} | |
return ""; | |
} else { | |
return "UNKNOWN MACRO"; | |
} | |
}); | |
var link_rgx = ~/\[\[(.*?)\|(.*?)\]\]/g; | |
while (link_rgx.match(str)) { | |
this.transitions[link_rgx.matched(1)]=link_rgx.matched(2); | |
this.message+=link_rgx.matchedLeft(); | |
if (controller.echo()) { | |
this.message+=link_rgx.matched(1); | |
} | |
str = link_rgx.matchedRight(); | |
} | |
this.message+=str; | |
this.choices = [for (a in this.transitions.keys()) a]; | |
} | |
public function new(title:String,body:String,controller:Yarn){ | |
this.title=title; | |
this.body=body; | |
this.transitions=new Map(); | |
this.message=""; | |
this.controller=controller; | |
} | |
} | |
class Yarn | |
{ | |
public var states:Map<String,YarnState>; | |
var vars:Map<String,String> ; | |
var state:YarnState; | |
public var parser:hscript.Parser; | |
public var interp:hscript.Interp; | |
//private var _gameSave:FlxSave; | |
var name:String; | |
private var echo_links:Bool=false; | |
public function new(path:String,name:String){ | |
parser = new hscript.Parser(); | |
interp = new hscript.Interp(); | |
interp.execute(parser.parseString("1;")); | |
this.name=name; | |
var json_src = Assets.getText(path); | |
var parsed:Array<Dynamic>= Json.parse(json_src); | |
var body:String; | |
var title:String; | |
states=new Map(); | |
vars=new Map(); | |
for(state in parsed){ | |
body=state.body; | |
title=state.title; | |
states[title]=new YarnState(title,body,this); | |
} | |
state=states["Start"]; | |
state.run_parse(); | |
} | |
public function setState(title:String):Void{ | |
state=states[title]; | |
state.run_parse(); | |
} | |
public function message():String{ | |
return state.message; | |
} | |
public function echo():Bool{ | |
return echo_links; | |
} | |
public function transition(choice:String):Void{ | |
state=states[state.transitions[choice]]; | |
state.run_parse(); | |
} | |
public function choices():Array<String>{ | |
return state.choices; | |
} | |
/* | |
public function saveIn(saveObj:FlxSave){ | |
saveObj.data.yarnStates[name]=state.title; | |
saveObj.data.yarnVars[name]=vars; | |
} | |
public function loadFrom(saveObj:FlxSave){ | |
state=states[saveObj.data.yarnStates[name]]; | |
vars=saveObj.data.yarnVars[name]; | |
} | |
*/ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment