Skip to content

Instantly share code, notes, and snippets.

@robertpfeiffer
Created December 11, 2015 17:39
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save robertpfeiffer/e00a88fa1d514cc4302b to your computer and use it in GitHub Desktop.
Save robertpfeiffer/e00a88fa1d514cc4302b to your computer and use it in GitHub Desktop.
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