Compare commits
22 Commits
cd8be645a3
...
even-moar-
Author | SHA1 | Date | |
---|---|---|---|
2ad54aa613 | |||
ed8bc13402 | |||
d6b042f2a4 | |||
c768073503 | |||
32885707de | |||
bb7690e606 | |||
4b00d713a9 | |||
8be7c18d4f | |||
fd6b9ee8dd | |||
808958f7cc | |||
b16b27e03d | |||
e0d3ca8021 | |||
c1b102860e | |||
bb9db51560 | |||
867b33aef5 | |||
05fc9156b0 | |||
8921f231bd | |||
010aa5323d | |||
dd9fc47787 | |||
021aac9914 | |||
477b57e2a7 | |||
87302a3713 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
||||
/bin/*
|
||||
/res/.tmp/
|
||||
/res/rewild.tiled-*
|
||||
|
25
README.md
25
README.md
@ -2,10 +2,31 @@
|
||||
|
||||
## Development
|
||||
|
||||
### Requirements
|
||||
|
||||
* `haxe`
|
||||
* `haxelib`
|
||||
* Haxelib dependencies; run `haxelib install compile-hl.hxml`
|
||||
* `automake`
|
||||
|
||||
### Compiling
|
||||
|
||||
`haxe compile.hxml`
|
||||
`make debug`
|
||||
|
||||
You can alternatively run `make watch` to automatically rebuild on changes to
|
||||
`.hx` files.
|
||||
|
||||
### Running
|
||||
|
||||
`hl rewild.hl`
|
||||
`make run`
|
||||
|
||||
### Making a release
|
||||
|
||||
**TODO**: Making a redistributable `hl` version
|
||||
|
||||
#### Web
|
||||
|
||||
```
|
||||
make bin/rewild.js
|
||||
abra app cp <domain> bin/rewild.js app:/usr/share/nginx/html/bin/
|
||||
```
|
||||
|
27
TODO.md
Normal file
27
TODO.md
Normal file
@ -0,0 +1,27 @@
|
||||
# TODO
|
||||
|
||||
## Features
|
||||
|
||||
- Saving and loading games
|
||||
- Character select
|
||||
- Stats
|
||||
- Better open / closed state graphics for rooms
|
||||
- Displaying sprites (characters, objects)
|
||||
- Inventory
|
||||
- Room labels
|
||||
- Background music
|
||||
- Sound effects
|
||||
|
||||
## Bugs
|
||||
|
||||
- Fix console rendering issues
|
||||
|
||||
### Dialogue
|
||||
|
||||
- Text formatting
|
||||
- Character profiles / speaker ID
|
||||
|
||||
## Code architecture
|
||||
|
||||
- Move dialogue- and option-drawing out of `Game.hx`
|
||||
- Use domkit for dialogue
|
@ -1,6 +1,7 @@
|
||||
-cp src
|
||||
--macro Init.setup()
|
||||
-lib heaps
|
||||
-lib heaps:git:https://github.com/HeapsIO/heaps.git
|
||||
-lib tiledhx
|
||||
-lib hxyarn
|
||||
-main Main
|
||||
-main Game
|
||||
-lib domkit
|
||||
|
10
makefile
10
makefile
@ -1,4 +1,4 @@
|
||||
.PHONY: watch
|
||||
.PHONY: watch run
|
||||
|
||||
default: bin/rewild.hl
|
||||
|
||||
@ -10,8 +10,14 @@ bin/rewild.hl: $(SOURCES)
|
||||
bin/rewild.js: $(SOURCES)
|
||||
haxe compile-js.hxml
|
||||
|
||||
debug: $(SOURCES)
|
||||
haxe --debug compile-hl.hxml
|
||||
|
||||
clean:
|
||||
rm -f bin/rewild.hl bin/rewild.js
|
||||
|
||||
watch:
|
||||
@inotifywait --recursive --monitor --event modify,move,create,delete --include '.*.hx$$' ./ | while read changed; do echo $$changed; make; done
|
||||
@inotifywait --recursive --monitor --event modify,move,create,delete --include '.*.hx$$' ./ | while read changed; do echo $$changed; make debug; done
|
||||
|
||||
run:
|
||||
hl bin/rewild.hl
|
||||
|
@ -21,6 +21,6 @@
|
||||
<object id="7" name="medical" x="96" y="96" width="224" height="128"/>
|
||||
<object id="8" name="generator" x="96" y="416" width="192" height="128"/>
|
||||
<object id="9" name="quarters" x="320" y="416" width="288" height="128"/>
|
||||
<object id="10" name="hangar" x="640" y="128" width="224" height="416"/>
|
||||
<object id="10" name="hangar" x="640" y="96" width="224" height="448"/>
|
||||
</objectgroup>
|
||||
</map>
|
||||
|
@ -1,13 +1,21 @@
|
||||
text {
|
||||
font : url("res/font/minecraftia_regular_6.fnt")
|
||||
}
|
||||
|
||||
game-container {
|
||||
background: blue;
|
||||
}
|
||||
|
||||
.mybox {
|
||||
background: #080;
|
||||
padding: -10;
|
||||
}
|
||||
|
||||
view.mybox {
|
||||
heading.mybox {
|
||||
padding: 10;
|
||||
}
|
||||
|
||||
title-screen {
|
||||
title-scene {
|
||||
max-width: 300;
|
||||
layout: vertical;
|
||||
}
|
||||
@ -44,3 +52,22 @@ menubutton:hover text {
|
||||
menubutton:active {
|
||||
background: #666;
|
||||
}
|
||||
|
||||
flow.dialogue-container {
|
||||
fill-width: true;
|
||||
height: 20;
|
||||
debug: true;
|
||||
}
|
||||
|
||||
dialogue-box {
|
||||
fill-width: true;
|
||||
height: 20;
|
||||
}
|
||||
|
||||
scale-grid#dialogueBackground {
|
||||
/* fill-width: true; */
|
||||
}
|
||||
|
||||
/* text#dialogueText { */
|
||||
/* font : url("res/font/minecraftia_regular_6.fnt"); */
|
||||
/* } */
|
||||
|
@ -8,4 +8,5 @@ You: What happened to the updates from mission control?
|
||||
-> Is this even still the same ship?
|
||||
You: Best not get too philsophical just yet
|
||||
You: Let's have another look around...
|
||||
<<room quarters>>
|
||||
===
|
||||
|
37
res/text/rooms.yarn
Normal file
37
res/text/rooms.yarn
Normal file
@ -0,0 +1,37 @@
|
||||
title: Room:quarters
|
||||
---
|
||||
<<declare $roll = 0>>
|
||||
<<declare $hangarKeyFound = false>>
|
||||
<<declare $sleepingMatSetUp = false>>
|
||||
You: It's the crew quarters.
|
||||
You: Well, it will be. Set-up hasn't started yet.
|
||||
You: That was meant to be one of my first jobs after defrost...
|
||||
You: ...but it doesn't exactly seem like a priority now.
|
||||
You: Maybe there's something useful here?
|
||||
-> Search the lockers (perception) <<if not $hangarKeyFound>>
|
||||
<<set $roll to dice(6)>>
|
||||
<<if $roll > 2>>
|
||||
You: Aha! A small piece of paper with the **hangar access code**.
|
||||
<<room hangar>>
|
||||
<<set $hangarKeyFound to true>>
|
||||
<<else>>
|
||||
You: Just some dust bunnies. Guess they stowed away from Earth.
|
||||
<<endif>>
|
||||
-> Set up a sleeping mat (ingenuity) <<if not $sleepingMatSetUp>>
|
||||
<<set $roll to dice(6)>>
|
||||
<<if $roll > 2>>
|
||||
You: Nice, at least I have somewhere to crash now.
|
||||
<<set $sleepingMatSetUp to true>>
|
||||
<<else>>
|
||||
You: Hmm, I was never good at this camping stuff. Maybe I can find somewhere else to rest.
|
||||
<<endif>>
|
||||
-> Leave
|
||||
===
|
||||
title: Room:hangar
|
||||
---
|
||||
You: The **hangar** seems eerily deserted.
|
||||
===
|
||||
title: Room not open
|
||||
---
|
||||
You: Hmm, that part of the ship is still inaccessible. I wonder how to get in?
|
||||
===
|
@ -1,105 +0,0 @@
|
||||
class DialogueBox extends h2d.Object {
|
||||
var game:Game;
|
||||
var bg:h2d.ScaleGrid;
|
||||
var tf:h2d.Text;
|
||||
var timer:haxe.Timer;
|
||||
var int:h2d.Interactive;
|
||||
|
||||
public var width(default, set):Int;
|
||||
public var height(default, set):Int;
|
||||
public var text(default, set):String;
|
||||
|
||||
var textPos:Int = 0;
|
||||
var chan:hxd.snd.Channel;
|
||||
|
||||
public function new(width:Int, height:Int, text:String) {
|
||||
super();
|
||||
if (text == null)
|
||||
text = "NULL";
|
||||
game = Game.inst;
|
||||
game.scene.add(this, 1);
|
||||
bg = new h2d.ScaleGrid(Res.ui.toTile(), 5, 5, this);
|
||||
bg.colorKey = 0xFF00FF;
|
||||
tf = new h2d.Text(game.font, this);
|
||||
tf.y = 5;
|
||||
tf.x = 7;
|
||||
tf.dropShadow = {
|
||||
dx: 0,
|
||||
dy: 1,
|
||||
color: 0,
|
||||
alpha: 0.3
|
||||
};
|
||||
int = new h2d.Interactive(0, 0, this);
|
||||
int.onClick = function(_) click();
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
override function onRemove() {
|
||||
super.onRemove();
|
||||
if (chan != null)
|
||||
chan.stop();
|
||||
timer.stop();
|
||||
}
|
||||
|
||||
function updateText() {
|
||||
if (textPos == text.length) {
|
||||
timer.stop();
|
||||
onReady();
|
||||
if (chan != null)
|
||||
chan.stop();
|
||||
return;
|
||||
}
|
||||
if (chan != null) {
|
||||
switch (text.charCodeAt(textPos)) {
|
||||
case " ".code, "\n".code:
|
||||
chan.volume = 0;
|
||||
default:
|
||||
if (chan.volume == 0)
|
||||
chan.volume = 1
|
||||
else
|
||||
chan.volume *= 0.9;
|
||||
}
|
||||
}
|
||||
textPos++;
|
||||
tf.text = text.substr(0, textPos);
|
||||
}
|
||||
|
||||
public function click() {
|
||||
if (textPos == text.length)
|
||||
onClick()
|
||||
else if (textPos < text.length) {
|
||||
textPos = text.length;
|
||||
tf.text = text;
|
||||
updateText();
|
||||
};
|
||||
}
|
||||
|
||||
public dynamic function onClick() {}
|
||||
|
||||
public dynamic function onReady() {}
|
||||
|
||||
function set_text(t) {
|
||||
text = t;
|
||||
timer = new haxe.Timer(30);
|
||||
timer.run = updateText;
|
||||
tf.text = "";
|
||||
textPos = 0;
|
||||
return t;
|
||||
}
|
||||
|
||||
function set_width(w:Int) {
|
||||
bg.width = w;
|
||||
int.width = w;
|
||||
tf.maxWidth = w - 14;
|
||||
tf.text = text;
|
||||
return width = w;
|
||||
}
|
||||
|
||||
function set_height(h:Int) {
|
||||
bg.height = h;
|
||||
int.height = h;
|
||||
return height = h;
|
||||
}
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
import hxyarn.dialogue.Dialogue;
|
||||
import hxyarn.dialogue.VariableStorage.MemoryVariableStore;
|
||||
import hxyarn.dialogue.StringInfo;
|
||||
import hxyarn.dialogue.Line;
|
||||
import hxyarn.dialogue.Command;
|
||||
import hxyarn.dialogue.Option;
|
||||
import hxyarn.dialogue.OptionSet;
|
||||
import hxyarn.compiler.Compiler;
|
||||
import hxyarn.compiler.CompilationJob;
|
||||
|
||||
typedef Option = {
|
||||
text:String,
|
||||
callback:() -> Void
|
||||
};
|
||||
|
||||
class DialogueManager {
|
||||
var storage = new MemoryVariableStore();
|
||||
var dialogue:Dialogue;
|
||||
var stringTable:Map<String, StringInfo>;
|
||||
var dialogueCallback:(DialogueManager, String) -> Void;
|
||||
var optionCallback:(DialogueManager, Array<Option>) -> Void;
|
||||
|
||||
public function new(_dialogueCallback:(DialogueManager, String) -> Void, _optionCallback:(DialogueManager, Array<Option>) -> Void) {
|
||||
dialogue = new Dialogue(new MemoryVariableStore());
|
||||
|
||||
dialogue.logDebugMessage = this.logDebugMessage;
|
||||
dialogue.logErrorMessage = this.logErrorMessage;
|
||||
dialogue.lineHandler = this.lineHandler;
|
||||
dialogue.optionsHandler = this.optionsHandler;
|
||||
dialogue.commandHandler = this.commandHandler;
|
||||
dialogue.nodeCompleteHandler = this.nodeCompleteHandler;
|
||||
dialogue.nodeStartHandler = this.nodeStartHandler;
|
||||
dialogue.dialogueCompleteHandler = this.dialogueCompleteHandler;
|
||||
|
||||
dialogueCallback = _dialogueCallback;
|
||||
optionCallback = _optionCallback;
|
||||
}
|
||||
|
||||
public function load(text:Array<String>, fileNames:Array<String>) {
|
||||
var job = CompilationJob.createFromStrings(text, fileNames, dialogue.library);
|
||||
var compiler = Compiler.compile(job);
|
||||
stringTable = compiler.stringTable;
|
||||
|
||||
dialogue.addProgram(compiler.program);
|
||||
}
|
||||
|
||||
public function runNode(nodeName:String) {
|
||||
dialogue.setNode(nodeName);
|
||||
dialogue.resume();
|
||||
}
|
||||
|
||||
public function unload() {
|
||||
dialogue.unloadAll();
|
||||
}
|
||||
|
||||
public function resume() {
|
||||
dialogue.resume();
|
||||
}
|
||||
|
||||
public function logDebugMessage(message:String):Void {
|
||||
}
|
||||
|
||||
public function logErrorMessage(message:String):Void {
|
||||
}
|
||||
|
||||
public function lineHandler(line:Line):HandlerExecutionType {
|
||||
var text = getComposedTextForLine(line);
|
||||
|
||||
dialogueCallback(this, text);
|
||||
|
||||
return HandlerExecutionType.ContinueExecution;
|
||||
}
|
||||
|
||||
public function optionsHandler(options:OptionSet) {
|
||||
var optionCount = options.options.length;
|
||||
var optionChoices = new Array<Option>();
|
||||
|
||||
for (i => option in options.options) {
|
||||
var text = getComposedTextForLine(option.line);
|
||||
optionChoices.push({
|
||||
text: text,
|
||||
callback: function() {
|
||||
dialogue.setSelectedOption(i);
|
||||
dialogue.resume();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
optionCallback(this, optionChoices);
|
||||
}
|
||||
|
||||
public function getComposedTextForLine(line:Line):String {
|
||||
var substitutedText = Dialogue.expandSubstitutions(stringTable[line.id].text, line.substitutions);
|
||||
|
||||
var markup = dialogue.parseMarkup(substitutedText);
|
||||
|
||||
return markup.text;
|
||||
}
|
||||
|
||||
public function commandHandler(command:Command) {
|
||||
}
|
||||
|
||||
public function nodeCompleteHandler(nodeName:String) {}
|
||||
|
||||
public function nodeStartHandler(nodeName:String) {}
|
||||
|
||||
public function dialogueCompleteHandler() {
|
||||
}
|
||||
|
||||
public function getNodeNames(?includesTag = "") {
|
||||
var nodeNames = [];
|
||||
for (nodeName in dialogue.allNodes) {
|
||||
if (includesTag == "") {
|
||||
nodeNames.push(nodeName);
|
||||
continue;
|
||||
}
|
||||
|
||||
var tags = dialogue.getTagsForNode(nodeName);
|
||||
if (tags.contains(includesTag)) {
|
||||
nodeNames.push(nodeName);
|
||||
}
|
||||
}
|
||||
return nodeNames;
|
||||
}
|
||||
}
|
170
src/Game.hx
170
src/Game.hx
@ -1,85 +1,131 @@
|
||||
import h2d.Layers;
|
||||
import h2d.Console;
|
||||
import h3d.Vector4;
|
||||
import scene.TitleScene;
|
||||
import scene.WorldMapScene;
|
||||
import scene.GameScene;
|
||||
import event.EventBus;
|
||||
import event.ChangeSceneEvent;
|
||||
import Const;
|
||||
import DialogueBox;
|
||||
import DialogueManager;
|
||||
import DialogueManager.Option;
|
||||
import listeners.DialogueBoxController;
|
||||
import dialogue.DialogueManager;
|
||||
import dialogue.command.OpenRoomCommand;
|
||||
|
||||
@:uiComp("game-container")
|
||||
class GameContainer extends h2d.Flow implements h2d.domkit.Object {
|
||||
static var SRC = <game-container>
|
||||
${scene}
|
||||
</game-container>;
|
||||
|
||||
public var scene:GameScene;
|
||||
|
||||
public function new(?parent) {
|
||||
super(parent);
|
||||
initComponent();
|
||||
}
|
||||
}
|
||||
|
||||
@:publicFields
|
||||
class Game extends hxd.App {
|
||||
public var scene:h2d.Scene;
|
||||
public static var loader = new tiled.Tiled();
|
||||
public static var current:Game; // Current Game (singleton)
|
||||
|
||||
public var font:h2d.Font;
|
||||
public var world:World;
|
||||
public var curDialogue:h2d.Object;
|
||||
public var actions:Array<h2d.ScaleGrid> = [];
|
||||
public var options:Array<h2d.ScaleGrid> = [];
|
||||
public var dialogueManager:DialogueManager;
|
||||
public var root:GameContainer;
|
||||
public var console:Console;
|
||||
public var globalEventBus:EventBus;
|
||||
|
||||
var dialogueBoxController:DialogueBoxController;
|
||||
var layer:Layers;
|
||||
var style = null;
|
||||
|
||||
override function init() {
|
||||
scene = s2d;
|
||||
s2d.scaleMode = Stretch(Const.W, Const.H + 12);
|
||||
world = new World(Res.map, Res.tiles);
|
||||
s2d.add(world.root, 0);
|
||||
s2d.scaleMode = Stretch(Const.W, Const.H);
|
||||
layer = new Layers(s2d);
|
||||
|
||||
#if hl
|
||||
hxd.res.Resource.LIVE_UPDATE = true;
|
||||
hxd.Res.initLocal();
|
||||
#else
|
||||
hxd.Res.initEmbed();
|
||||
#end
|
||||
|
||||
root = new GameContainer();
|
||||
root.horizontalAlign = root.verticalAlign = Middle;
|
||||
layer.addChildAt(root, 1);
|
||||
onResize();
|
||||
|
||||
style = new h2d.domkit.Style();
|
||||
style.load(hxd.Res.style);
|
||||
style.allowInspect = true;
|
||||
style.addObject(root);
|
||||
|
||||
font = Res.font.minecraftia_regular_6.toFont();
|
||||
font.resizeTo(24);
|
||||
font.resizeTo(18);
|
||||
|
||||
dialogueManager = new DialogueManager(dialogue, renderActions);
|
||||
#if debug
|
||||
console = new Console(font);
|
||||
layer.addChildAt(console, 4);
|
||||
#end
|
||||
|
||||
var yarnText = [
|
||||
hxd.Res.text.intro.entry.getText(),
|
||||
];
|
||||
var yarnFileNames = [
|
||||
hxd.Res.text.intro.entry.name,
|
||||
];
|
||||
globalEventBus = new EventBus(console);
|
||||
globalEventBus.subscribe(ChangeSceneEvent, onChangeScene);
|
||||
|
||||
dialogueManager = new DialogueManager(globalEventBus);
|
||||
|
||||
var yarnText = [hxd.Res.text.intro.entry.getText(), hxd.Res.text.rooms.entry.getText(),];
|
||||
var yarnFileNames = [hxd.Res.text.intro.entry.name, hxd.Res.text.rooms.entry.name,];
|
||||
dialogueManager.load(yarnText, yarnFileNames);
|
||||
dialogueManager.runNode("Intro");
|
||||
|
||||
dialogueManager.addCommandHandler(new OpenRoomCommand(globalEventBus));
|
||||
|
||||
var dialogueParent = new h2d.Flow();
|
||||
dialogueParent.dom = domkit.Properties.create("flow", dialogueParent);
|
||||
dialogueParent.dom.addClass('dialogue-container');
|
||||
style.addObject(dialogueParent);
|
||||
layer.addChildAt(dialogueParent, 2);
|
||||
dialogueBoxController = new DialogueBoxController(globalEventBus, dialogueParent, style);
|
||||
|
||||
#if debug
|
||||
setGameScene(new WorldMapScene(root));
|
||||
#else
|
||||
setGameScene(new TitleScene(root));
|
||||
#end
|
||||
}
|
||||
|
||||
public static var inst:Game;
|
||||
|
||||
function dialogue(manager:DialogueManager, t:String) {
|
||||
var d = new DialogueBox(Const.W, 100, t);
|
||||
d.y = 0;
|
||||
d.onClick = function() {
|
||||
manager.resume();
|
||||
};
|
||||
|
||||
curDialogue = d;
|
||||
public function new() {
|
||||
super();
|
||||
current = this;
|
||||
}
|
||||
|
||||
function renderActions(manager:DialogueManager, t:Array<Option>) {
|
||||
for (action in t) {
|
||||
addAction(action.text, action.callback);
|
||||
}
|
||||
override function onResize() {
|
||||
root.minWidth = root.maxWidth = s2d.width;
|
||||
root.minHeight = root.maxHeight = s2d.height;
|
||||
}
|
||||
|
||||
function newPanel(w, h) {
|
||||
var g = new h2d.ScaleGrid(Res.ui.toTile(), 5, 5);
|
||||
g.width = w;
|
||||
g.height = h;
|
||||
g.colorKey = 0xFF00FF;
|
||||
scene.add(g, 1);
|
||||
return g;
|
||||
override function update(dt:Float) {
|
||||
style.sync();
|
||||
}
|
||||
|
||||
function addAction(a: String, callback: () -> Void) {
|
||||
var spr = newPanel(Const.W, 60);
|
||||
curDialogue.addChild(spr);
|
||||
var tf = new h2d.Text(font, spr);
|
||||
tf.text = a;
|
||||
tf.x = 10;
|
||||
tf.y = 10;
|
||||
spr.x = 0;
|
||||
spr.y = 100 + actions.length * 60;
|
||||
var int = new h2d.Interactive(spr.width, spr.height, spr);
|
||||
int.onClick = function(_) {
|
||||
for (action in actions) {
|
||||
action.remove();
|
||||
}
|
||||
callback();
|
||||
// removeDialog();
|
||||
}
|
||||
int.cursor = Button;
|
||||
actions.push(spr);
|
||||
return spr;
|
||||
public function onChangeScene(event:ChangeSceneEvent) {
|
||||
setGameScene(event.newScene);
|
||||
}
|
||||
|
||||
public function setGameScene(gs:GameScene) {
|
||||
#if debug
|
||||
console.resetCommands();
|
||||
#end
|
||||
|
||||
if (root.scene != null) {
|
||||
root.scene.getH2dObject().remove();
|
||||
}
|
||||
|
||||
root.scene = gs;
|
||||
style.addObject(gs.getH2dObject());
|
||||
}
|
||||
|
||||
static function main() {
|
||||
new Game();
|
||||
}
|
||||
}
|
||||
|
51
src/Main.hx
51
src/Main.hx
@ -1,51 +0,0 @@
|
||||
import screens.TitleScreen;
|
||||
|
||||
class Main extends hxd.App {
|
||||
var center:h2d.Flow;
|
||||
var style = null;
|
||||
|
||||
override function init() {
|
||||
center = new h2d.Flow(s2d);
|
||||
center.horizontalAlign = center.verticalAlign = Middle;
|
||||
onResize();
|
||||
var root = new TitleScreen(Right, center);
|
||||
|
||||
root.btnNewGame.onClick = function() {
|
||||
Game.inst = new Game();
|
||||
}
|
||||
root.btnContinueGame.onClick = function() {
|
||||
root.btnContinueGame.dom.addClass("highlight");
|
||||
}
|
||||
#if hl
|
||||
root.btnQuit.onClick = function() {
|
||||
Sys.exit(0);
|
||||
}
|
||||
#else
|
||||
root.btnQuit.remove();
|
||||
#end
|
||||
|
||||
style = new h2d.domkit.Style();
|
||||
style.load(hxd.Res.style);
|
||||
style.allowInspect = true;
|
||||
style.addObject(root);
|
||||
}
|
||||
|
||||
override function onResize() {
|
||||
center.minWidth = center.maxWidth = s2d.width;
|
||||
center.minHeight = center.maxHeight = s2d.height;
|
||||
}
|
||||
|
||||
override function update(dt:Float) {
|
||||
style.sync();
|
||||
}
|
||||
|
||||
static function main() {
|
||||
#if hl
|
||||
hxd.res.Resource.LIVE_UPDATE = true;
|
||||
hxd.Res.initLocal();
|
||||
#else
|
||||
hxd.Res.initEmbed();
|
||||
#end
|
||||
new Main();
|
||||
}
|
||||
}
|
229
src/TilemapLayer.hx
Normal file
229
src/TilemapLayer.hx
Normal file
@ -0,0 +1,229 @@
|
||||
import h2d.col.Point;
|
||||
import h2d.Object;
|
||||
import h2d.SpriteBatch;
|
||||
import tiled.types.TmxLayer;
|
||||
import tiled.types.TmxMap;
|
||||
|
||||
using tiled.TiledTools;
|
||||
|
||||
class TilemapLayer extends h2d.TileGroup {
|
||||
public var map:TmxMap;
|
||||
public var layer:TmxLayer;
|
||||
|
||||
public function new(map:TmxMap, layer:TmxLayer, ?parent:Object) {
|
||||
this.map = map;
|
||||
this.layer = layer;
|
||||
super(parent);
|
||||
for (c in layer.tileChunks) {
|
||||
addChunk(c);
|
||||
}
|
||||
// TODO: Animations
|
||||
}
|
||||
|
||||
// public function gidAt(x:Int, y:Int):Int {
|
||||
// if (x < 0 || x >= layer.width || y < 0 || y >= layer.height) return 0;
|
||||
// return layer.data.tiles[y * layer.width + x].gid;
|
||||
// }
|
||||
public function addChunk(chunk:TileChunk) {
|
||||
var tw = map.tileWidth;
|
||||
var th = map.tileHeight;
|
||||
var ix = 0, iy = 0;
|
||||
var tiles = chunk.tiles, i = 0;
|
||||
var tsets = map.tilesets;
|
||||
switch (map.orientation) {
|
||||
case Orthogonal:
|
||||
var cx = chunk.x * tw + layer.offsetX;
|
||||
var cy = chunk.y * th + layer.offsetY;
|
||||
while (i < tiles.length) {
|
||||
var gid = tiles[i++];
|
||||
if (gid != 0) {
|
||||
var t = tsets.getImage(gid);
|
||||
var x = cx + ix * tw;
|
||||
var y = cy + iy * th + th - t.height;
|
||||
if (gid.flippedHorizontally)
|
||||
addTransform(x + t.width, y, -1, 1, 0, t);
|
||||
else if (gid.flippedVertically)
|
||||
addTransform(x, y + t.height, 1, -1, 0, t);
|
||||
else if (gid.flippedDiagonally)
|
||||
addTransform(x + t.width, y + t.height, -1, -1, 0, t);
|
||||
else
|
||||
add(x, y, t);
|
||||
}
|
||||
if (++ix == chunk.width) {
|
||||
iy++;
|
||||
ix = 0;
|
||||
}
|
||||
}
|
||||
case Isometric:
|
||||
var isoW = tw >> 1;
|
||||
var isoH = th >> 1;
|
||||
|
||||
var cx = (chunk.x - chunk.y) * isoW + layer.offsetX;
|
||||
var cy = (chunk.x + chunk.y) * isoH + layer.offsetY;
|
||||
while (i < tiles.length) {
|
||||
var gid = tiles[i++];
|
||||
if (gid != 0) {
|
||||
var t = tsets.getImage(gid);
|
||||
var x = cx + (ix - iy) * isoW;
|
||||
var y = cy + (ix + iy) * isoH + th - t.height;
|
||||
if (gid.flippedHorizontally)
|
||||
addTransform(x + t.width, y, -1, 1, 0, t);
|
||||
else if (gid.flippedVertically)
|
||||
addTransform(x, y + t.height, 1, -1, 0, t);
|
||||
else if (gid.flippedDiagonally)
|
||||
addTransform(x + t.width, y + t.height, -1, -1, 0, t);
|
||||
else
|
||||
add(x, y, t);
|
||||
}
|
||||
if (++ix == chunk.width) {
|
||||
iy++;
|
||||
ix = 0;
|
||||
}
|
||||
}
|
||||
case Staggered(staggerYAxis, staggerIndexOdd):
|
||||
var staggerX = 0,
|
||||
staggerY = 0,
|
||||
stepX = tw >> 1,
|
||||
stepY = th >> 1;
|
||||
var cx = chunk.x * stepX + layer.offsetX;
|
||||
var cy = chunk.y * stepY + layer.offsetY;
|
||||
if (staggerYAxis) {
|
||||
if (staggerIndexOdd) {
|
||||
staggerX = tw >> 1;
|
||||
} else {
|
||||
staggerX = -(tw >> 1);
|
||||
cx -= staggerX;
|
||||
}
|
||||
} else {
|
||||
if (staggerIndexOdd) {
|
||||
staggerY = th >> 1;
|
||||
} else {
|
||||
staggerY = -(tw >> 1);
|
||||
cy -= staggerY;
|
||||
}
|
||||
}
|
||||
cx += (chunk.x % 2) * staggerX;
|
||||
cy += (chunk.y % 2) * staggerY;
|
||||
while (i < tiles.length) {
|
||||
var gid = tiles[i++];
|
||||
if (gid != 0) {
|
||||
var t = tsets.getImage(gid);
|
||||
var x = cx + ix * stepX + staggerX * (ix % 1);
|
||||
var y = cy + iy * stepY + th - t.height;
|
||||
if (gid.flippedHorizontally)
|
||||
addTransform(x + t.width, y, -1, 1, 0, t);
|
||||
else if (gid.flippedVertically)
|
||||
addTransform(x, y + t.height, 1, -1, 0, t);
|
||||
else if (gid.flippedDiagonally)
|
||||
addTransform(x + t.width, y + t.height, -1, -1, 0, t);
|
||||
else
|
||||
add(x, y, t);
|
||||
}
|
||||
if (++ix == chunk.width) {
|
||||
iy++;
|
||||
ix = 0;
|
||||
}
|
||||
}
|
||||
case Hexagonal(sideLength, staggerYAxis, staggerIndexOdd):
|
||||
// TODO: Fix
|
||||
var staggerX = 0, staggerY = 0, stepX, stepY;
|
||||
var cx, cy;
|
||||
if (staggerYAxis) {
|
||||
stepX = tw;
|
||||
stepY = (th + sideLength) >> 1;
|
||||
cx = chunk.x * stepX + layer.offsetX;
|
||||
cy = chunk.y * stepY + layer.offsetY;
|
||||
if (staggerIndexOdd) {
|
||||
staggerX = tw >> 1;
|
||||
} else {
|
||||
staggerX = -(tw >> 1);
|
||||
cx -= staggerX;
|
||||
}
|
||||
} else {
|
||||
stepX = (tw + sideLength) >> 1;
|
||||
stepY = th;
|
||||
cx = chunk.x * stepX + layer.offsetX;
|
||||
cy = chunk.y * stepY + layer.offsetY;
|
||||
if (staggerIndexOdd) {
|
||||
staggerY = th >> 1;
|
||||
} else {
|
||||
staggerY = -(tw >> 1);
|
||||
cy -= staggerY;
|
||||
}
|
||||
}
|
||||
while (i < tiles.length) {
|
||||
var gid = tiles[i++];
|
||||
if (gid != 0) {
|
||||
var t = tsets.getImage(gid);
|
||||
var x = cx + ix * stepX;
|
||||
var y = cy + iy * stepY + th - t.height;
|
||||
if (gid.flippedHorizontally)
|
||||
addTransform(x + t.width, y, -1, 1, 0, t);
|
||||
else if (gid.flippedVertically)
|
||||
addTransform(x, y + t.height, 1, -1, 0, t);
|
||||
else if (gid.flippedDiagonally)
|
||||
addTransform(x + t.width, y + t.height, -1, -1, 0, t);
|
||||
else
|
||||
add(x, y, t);
|
||||
}
|
||||
if (++ix == chunk.width) {
|
||||
iy++;
|
||||
ix = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function addTile(tx:Int, ty:Int, ox:Float, oy:Float, gid:TmxTileIndex) {
|
||||
if (gid == 0)
|
||||
return;
|
||||
var t = map.tilesets.getImage(gid);
|
||||
if (t != null) {
|
||||
var x, y;
|
||||
switch (map.orientation) {
|
||||
case Orthogonal:
|
||||
x = tx * map.tileWidth + ox;
|
||||
y = ty * map.tileHeight + oy;
|
||||
case Isometric:
|
||||
x = (tx - ty) * (map.tileWidth >> 1) + ox;
|
||||
y = (tx + ty) * (map.tileHeight >> 1) + oy;
|
||||
case Staggered(staggerYAxis, staggerIndexOdd):
|
||||
if (staggerYAxis) {
|
||||
y = ty * (map.tileHeight >> 1);
|
||||
if (staggerIndexOdd)
|
||||
x = tx * map.tileWidth + (tx % 2) * (map.tileWidth >> 1);
|
||||
else
|
||||
x = tx * map.tileWidth + ((tx + 1) % 2) * (map.tileWidth >> 1);
|
||||
} else {
|
||||
x = tx * (map.tileWidth >> 1);
|
||||
if (staggerIndexOdd)
|
||||
y = ty * map.tileHeight + (tx % 2) * (map.tileHeight >> 1);
|
||||
else
|
||||
y = ty * map.tileHeight + ((tx + 1) % 2) * (map.tileHeight >> 1);
|
||||
}
|
||||
case Hexagonal(sideLength, staggerYAxis, staggerIndexOdd):
|
||||
if (staggerYAxis) {
|
||||
y = ty * ((map.tileHeight + sideLength) >> 1);
|
||||
if (staggerIndexOdd)
|
||||
x = tx * map.tileWidth + (tx % 2) * (map.tileWidth >> 1);
|
||||
else
|
||||
x = tx * map.tileWidth + ((tx + 1) % 2) * (map.tileWidth >> 1);
|
||||
} else {
|
||||
x = tx * ((map.tileWidth + sideLength) >> 1);
|
||||
if (staggerIndexOdd)
|
||||
y = ty * map.tileHeight + (tx % 2) * (map.tileHeight >> 1);
|
||||
else
|
||||
y = ty * map.tileHeight + ((tx + 1) % 2) * (map.tileHeight >> 1);
|
||||
}
|
||||
}
|
||||
if (gid.flippedHorizontally)
|
||||
addTransform(x + t.width, y, -1, 1, 0, t);
|
||||
else if (gid.flippedVertically)
|
||||
addTransform(x, y + t.height, 1, -1, 0, t);
|
||||
else if (gid.flippedDiagonally)
|
||||
addTransform(x + t.width, y + t.height, -1, -1, 0, t);
|
||||
else
|
||||
add(x, y, t);
|
||||
}
|
||||
}
|
||||
}
|
65
src/World.hx
65
src/World.hx
@ -1,65 +0,0 @@
|
||||
class World {
|
||||
var game:Game;
|
||||
var map:hxd.res.TiledMap.TiledMapData;
|
||||
|
||||
public var root:h2d.Object;
|
||||
|
||||
var layers:Map<String, {
|
||||
name:String,
|
||||
data:Array<Int>,
|
||||
g:h2d.TileGroup,
|
||||
alpha:Float
|
||||
}>;
|
||||
var tiles:Array<h2d.Tile>;
|
||||
|
||||
public var width:Int;
|
||||
public var height:Int;
|
||||
|
||||
public function new(r:hxd.res.TiledMap, tiles:hxd.res.Image) {
|
||||
game = Game.inst;
|
||||
root = new h2d.Object();
|
||||
map = r.toMap();
|
||||
root = new h2d.Object();
|
||||
width = map.width;
|
||||
height = map.height;
|
||||
var t = tiles.toTile();
|
||||
layers = new Map();
|
||||
var font:h2d.Font = hxd.res.DefaultFont.get();
|
||||
this.tiles = [
|
||||
for (y in 0...Std.int(t.height) >> 5) for (x in 0...Std.int(t.width) >> 5) t.sub(x * 32, y * 32, 32, 32)
|
||||
];
|
||||
for (ld in map.layers) {
|
||||
if (ld.name == "hotspots")
|
||||
continue;
|
||||
var l = {
|
||||
name: ld.name,
|
||||
data: ld.data,
|
||||
g: new h2d.TileGroup(t, root),
|
||||
alpha: ld.opacity,
|
||||
}
|
||||
// l.g.colorKey = 0x1D8700;
|
||||
l.g.alpha = ld.opacity;
|
||||
layers.set(ld.name, l);
|
||||
rebuildLayer(ld.name);
|
||||
}
|
||||
}
|
||||
|
||||
function rebuildLayer(name:String) {
|
||||
var l = layers.get(name);
|
||||
if (l == null)
|
||||
return;
|
||||
var pos = 0;
|
||||
var g = l.g;
|
||||
g.clear();
|
||||
while (g.numChildren > 0)
|
||||
g.getChildAt(0).remove();
|
||||
|
||||
for (y in 0...height)
|
||||
for (x in 0...width) {
|
||||
var t = l.data[pos++] - 1;
|
||||
if (t < 0)
|
||||
continue;
|
||||
g.add(x * 32, y * 32, tiles[t]);
|
||||
}
|
||||
}
|
||||
}
|
135
src/components/DialogueBoxComponent.hx
Normal file
135
src/components/DialogueBoxComponent.hx
Normal file
@ -0,0 +1,135 @@
|
||||
package components;
|
||||
|
||||
import dialogue.event.NextLine;
|
||||
|
||||
@:uiComp("dialogue-option")
|
||||
class DialogueOptionComponent extends h2d.Flow implements h2d.domkit.Object {
|
||||
static var SRC = <dialogue-option>
|
||||
<text text={label} public font={Game.current.font} id="labelTxt" />
|
||||
</dialogue-option>;
|
||||
|
||||
public var label(get, set):String;
|
||||
|
||||
function get_label()
|
||||
return labelTxt.text;
|
||||
|
||||
function set_label(s) {
|
||||
labelTxt.text = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
public function new(label:String, ?parent) {
|
||||
super(parent);
|
||||
initComponent();
|
||||
enableInteractive = true;
|
||||
interactive.onClick = function(_) onClick();
|
||||
interactive.onOver = function(_) {
|
||||
dom.hover = true;
|
||||
};
|
||||
interactive.onPush = function(_) {
|
||||
dom.active = true;
|
||||
};
|
||||
interactive.onRelease = function(_) {
|
||||
dom.active = false;
|
||||
};
|
||||
interactive.onOut = function(_) {
|
||||
dom.hover = false;
|
||||
};
|
||||
}
|
||||
|
||||
public dynamic function onClick() {}
|
||||
}
|
||||
|
||||
@:uiComp("dialogue-options")
|
||||
class DialogueOptionsComponent extends h2d.Flow implements h2d.domkit.Object {
|
||||
static var SRC = <dialogue-options>
|
||||
${options}
|
||||
</dialogue-options>;
|
||||
|
||||
public var options:Array<DialogueOptionComponent>;
|
||||
}
|
||||
|
||||
@:uiComp("dialogue-box")
|
||||
class DialogueBoxComponent extends h2d.Flow implements h2d.domkit.Object {
|
||||
var timer:haxe.Timer;
|
||||
var chan:hxd.snd.Channel;
|
||||
var textPos:Int = 0;
|
||||
var game:Game;
|
||||
|
||||
static var SRC = <dialogue-box>
|
||||
<scale-grid id="dialogueBackground">
|
||||
<text text={displayedText} public font={Game.current.font} id="dialogueText" />
|
||||
</scale-grid>
|
||||
</dialogue-box>
|
||||
|
||||
public var displayedText(get, set):String;
|
||||
|
||||
function get_displayedText()
|
||||
return dialogueText.text;
|
||||
|
||||
function set_displayedText(s) {
|
||||
dialogueText.text = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
public var internalText(default, set):String;
|
||||
|
||||
function set_internalText(t) {
|
||||
internalText = t;
|
||||
timer = new haxe.Timer(30);
|
||||
timer.run = updateText;
|
||||
displayedText = "";
|
||||
textPos = 0;
|
||||
return t;
|
||||
}
|
||||
|
||||
public function new(text:String, ?parent) {
|
||||
this.game = Game.current;
|
||||
super(parent);
|
||||
initComponent();
|
||||
internalText = text;
|
||||
this.dialogueBackground.tile = Res.ui.toTile();
|
||||
this.dialogueBackground.borderWidth = 5;
|
||||
this.dialogueBackground.borderHeight = 5;
|
||||
this.dialogueBackground.colorKey = 0xFF00FF;
|
||||
this.dialogueBackground.width = this.innerWidth;
|
||||
this.dialogueBackground.height = this.innerHeight;
|
||||
// trace(this.dialogueBackground.width);
|
||||
|
||||
enableInteractive = true;
|
||||
interactive.onClick = function(_) onClick();
|
||||
}
|
||||
|
||||
function updateText() {
|
||||
if (textPos == internalText.length) {
|
||||
timer.stop();
|
||||
// onReady();
|
||||
// if (chan != null)
|
||||
// chan.stop();
|
||||
return;
|
||||
}
|
||||
// if (chan != null) {
|
||||
// switch (text.charCodeAt(textPos)) {
|
||||
// case " ".code, "\n".code:
|
||||
// chan.volume = 0;
|
||||
// default:
|
||||
// if (chan.volume == 0)
|
||||
// chan.volume = 1
|
||||
// else
|
||||
// chan.volume *= 0.9;
|
||||
// }
|
||||
// }
|
||||
textPos++;
|
||||
displayedText = internalText.substr(0, textPos);
|
||||
}
|
||||
|
||||
public dynamic function onClick() {
|
||||
if (textPos == internalText.length)
|
||||
Game.current.globalEventBus.publishEvent(new NextLine());
|
||||
else if (textPos < internalText.length) {
|
||||
textPos = internalText.length;
|
||||
displayedText = internalText;
|
||||
updateText();
|
||||
};
|
||||
}
|
||||
}
|
164
src/dialogue/DialogueManager.hx
Normal file
164
src/dialogue/DialogueManager.hx
Normal file
@ -0,0 +1,164 @@
|
||||
package dialogue;
|
||||
|
||||
import hxyarn.dialogue.Dialogue;
|
||||
import hxyarn.dialogue.VariableStorage.MemoryVariableStore;
|
||||
import hxyarn.dialogue.StringInfo;
|
||||
import hxyarn.dialogue.Line;
|
||||
import hxyarn.dialogue.Command;
|
||||
import hxyarn.dialogue.Option;
|
||||
import hxyarn.dialogue.OptionSet;
|
||||
import hxyarn.dialogue.markup.MarkupParseResult;
|
||||
import hxyarn.compiler.Compiler;
|
||||
import hxyarn.compiler.CompilationJob;
|
||||
import event.EventBus;
|
||||
import dialogue.command.ICommandHandler;
|
||||
import dialogue.event.LineShown;
|
||||
import dialogue.event.OptionsShown;
|
||||
import dialogue.event.OptionsShown.OptionChoice;
|
||||
import dialogue.event.OptionSelected;
|
||||
import dialogue.event.NextLine;
|
||||
import dialogue.event.StartDialogueNode;
|
||||
import dialogue.event.DialogueComplete;
|
||||
|
||||
class DialogueManager {
|
||||
public var eventBus:EventBus;
|
||||
|
||||
var storage = new MemoryVariableStore();
|
||||
var dialogue:Dialogue;
|
||||
var stringTable:Map<String, StringInfo>;
|
||||
var commandHandlers = new Map<String, ICommandHandler>();
|
||||
|
||||
public var waitingForOption:Bool = false;
|
||||
|
||||
public function new(eventBus:EventBus) {
|
||||
this.eventBus = eventBus;
|
||||
dialogue = new Dialogue(new MemoryVariableStore());
|
||||
|
||||
dialogue.logDebugMessage = this.logDebugMessage;
|
||||
dialogue.logErrorMessage = this.logErrorMessage;
|
||||
dialogue.lineHandler = this.lineHandler;
|
||||
dialogue.optionsHandler = this.optionsHandler;
|
||||
dialogue.commandHandler = this.commandHandler;
|
||||
dialogue.nodeCompleteHandler = this.nodeCompleteHandler;
|
||||
dialogue.nodeStartHandler = this.nodeStartHandler;
|
||||
dialogue.dialogueCompleteHandler = this.dialogueCompleteHandler;
|
||||
|
||||
eventBus.subscribe(NextLine, this.nextLine);
|
||||
eventBus.subscribe(OptionSelected, this.optionSelected);
|
||||
eventBus.subscribe(StartDialogueNode, function(event) {
|
||||
this.runNode(event.node);
|
||||
});
|
||||
}
|
||||
|
||||
public function load(text:Array<String>, fileNames:Array<String>) {
|
||||
var job = CompilationJob.createFromStrings(text, fileNames, dialogue.library);
|
||||
var compiler = Compiler.compile(job);
|
||||
stringTable = compiler.stringTable;
|
||||
|
||||
dialogue.addProgram(compiler.program);
|
||||
}
|
||||
|
||||
public function runNode(nodeName:String) {
|
||||
dialogue.setNode(nodeName);
|
||||
dialogue.resume();
|
||||
}
|
||||
|
||||
public function unload() {
|
||||
dialogue.unloadAll();
|
||||
}
|
||||
|
||||
public function resume() {
|
||||
dialogue.resume();
|
||||
}
|
||||
|
||||
public function logDebugMessage(message:String):Void {}
|
||||
|
||||
public function logErrorMessage(message:String):Void {}
|
||||
|
||||
public function nextLine(event:NextLine) {
|
||||
if ((dialogue.isActive()) && (!waitingForOption))
|
||||
dialogue.resume();
|
||||
}
|
||||
|
||||
public function lineHandler(line:Line):Void {
|
||||
var text = getComposedTextForLine(line);
|
||||
eventBus.publishEvent(new LineShown(text));
|
||||
}
|
||||
|
||||
public function optionSelected(event:OptionSelected) {
|
||||
dialogue.setSelectedOption(event.index);
|
||||
dialogue.resume();
|
||||
waitingForOption = false;
|
||||
}
|
||||
|
||||
public function optionsHandler(options:OptionSet) {
|
||||
var optionCount = options.options.length;
|
||||
var optionChoices = new Array<OptionChoice>();
|
||||
|
||||
for (i => option in options.options) {
|
||||
var markup = getComposedTextForLine(option.line);
|
||||
optionChoices.push({
|
||||
text: markup.text,
|
||||
index: i,
|
||||
enabled: option.enabled,
|
||||
markup: markup
|
||||
});
|
||||
}
|
||||
|
||||
eventBus.publishEvent(new OptionsShown(optionChoices));
|
||||
|
||||
waitingForOption = true;
|
||||
}
|
||||
|
||||
public function getComposedTextForLine(line:Line):MarkupParseResult {
|
||||
var substitutedText = Dialogue.expandSubstitutions(stringTable[line.id].text, line.substitutions);
|
||||
|
||||
return dialogue.parseMarkup(substitutedText);
|
||||
}
|
||||
|
||||
public function commandHandler(command:Command) {
|
||||
var parts = command.text.split(' ');
|
||||
var key = parts.shift();
|
||||
if (commandHandlers.exists(key)) {
|
||||
if (commandHandlers.get(key).handleCommand(parts)) {
|
||||
resume();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
resume();
|
||||
}
|
||||
|
||||
public function addCommandHandler(handler:ICommandHandler) {
|
||||
if (handler.commandName == null || handler.commandName == "") {
|
||||
trace("Command handler has no command name");
|
||||
return;
|
||||
}
|
||||
|
||||
commandHandlers.set(handler.commandName, handler);
|
||||
}
|
||||
|
||||
public function nodeCompleteHandler(nodeName:String) {}
|
||||
|
||||
public function nodeStartHandler(nodeName:String) {}
|
||||
|
||||
public function dialogueCompleteHandler() {
|
||||
eventBus.publishEvent(new DialogueComplete(""));
|
||||
}
|
||||
|
||||
public function getNodeNames(?includesTag = "") {
|
||||
var nodeNames = [];
|
||||
for (nodeName in dialogue.allNodes) {
|
||||
if (includesTag == "") {
|
||||
nodeNames.push(nodeName);
|
||||
continue;
|
||||
}
|
||||
|
||||
var tags = dialogue.getTagsForNode(nodeName);
|
||||
if (tags.contains(includesTag)) {
|
||||
nodeNames.push(nodeName);
|
||||
}
|
||||
}
|
||||
return nodeNames;
|
||||
}
|
||||
}
|
6
src/dialogue/command/ICommandHandler.hx
Normal file
6
src/dialogue/command/ICommandHandler.hx
Normal file
@ -0,0 +1,6 @@
|
||||
package dialogue.command;
|
||||
|
||||
interface ICommandHandler {
|
||||
var commandName:String;
|
||||
function handleCommand(args:Array<String>):Bool;
|
||||
}
|
19
src/dialogue/command/OpenRoomCommand.hx
Normal file
19
src/dialogue/command/OpenRoomCommand.hx
Normal file
@ -0,0 +1,19 @@
|
||||
package dialogue.command;
|
||||
|
||||
import event.EventBus;
|
||||
import event.OpenRoomEvent;
|
||||
|
||||
class OpenRoomCommand implements ICommandHandler {
|
||||
public var commandName:String = "room";
|
||||
|
||||
var eventBus:EventBus;
|
||||
|
||||
public function new(eventBus:EventBus) {
|
||||
this.eventBus = eventBus;
|
||||
}
|
||||
|
||||
public function handleCommand(args:Array<String>):Bool {
|
||||
eventBus.publishEvent(new OpenRoomEvent(args[0]));
|
||||
return true;
|
||||
}
|
||||
}
|
11
src/dialogue/event/DialogueComplete.hx
Normal file
11
src/dialogue/event/DialogueComplete.hx
Normal file
@ -0,0 +1,11 @@
|
||||
package dialogue.event;
|
||||
|
||||
import event.IEvent;
|
||||
|
||||
class DialogueComplete implements IEvent {
|
||||
public var nodeName:String;
|
||||
|
||||
public function new(nodeName:String) {
|
||||
this.nodeName = nodeName;
|
||||
}
|
||||
}
|
36
src/dialogue/event/LineShown.hx
Normal file
36
src/dialogue/event/LineShown.hx
Normal file
@ -0,0 +1,36 @@
|
||||
package dialogue.event;
|
||||
|
||||
import hxyarn.dialogue.markup.MarkupAttribute;
|
||||
import hxyarn.dialogue.markup.MarkupParseResult;
|
||||
import event.IEvent;
|
||||
|
||||
class LineShown implements IEvent {
|
||||
public var markUpResults:MarkupParseResult;
|
||||
|
||||
public function new(markupParseResults:MarkupParseResult) {
|
||||
this.markUpResults = markupParseResults;
|
||||
}
|
||||
|
||||
public function line():String {
|
||||
var text = markUpResults.text;
|
||||
var characterAttribute = characterNameAttribute();
|
||||
|
||||
if (characterAttribute != null)
|
||||
text = text.substr(characterAttribute.position + characterAttribute.length);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
public function characterName():String {
|
||||
var attribute = characterNameAttribute();
|
||||
|
||||
if (attribute != null)
|
||||
return attribute.properties[0].value.stringValue;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function characterNameAttribute():MarkupAttribute {
|
||||
return markUpResults.tryGetAttributeWithName("character");
|
||||
}
|
||||
}
|
7
src/dialogue/event/NextLine.hx
Normal file
7
src/dialogue/event/NextLine.hx
Normal file
@ -0,0 +1,7 @@
|
||||
package dialogue.event;
|
||||
|
||||
import event.IEvent;
|
||||
|
||||
class NextLine implements IEvent {
|
||||
public function new() {}
|
||||
}
|
11
src/dialogue/event/OptionSelected.hx
Normal file
11
src/dialogue/event/OptionSelected.hx
Normal file
@ -0,0 +1,11 @@
|
||||
package dialogue.event;
|
||||
|
||||
import event.IEvent;
|
||||
|
||||
class OptionSelected implements IEvent {
|
||||
public var index:Int;
|
||||
|
||||
public function new(index:Int) {
|
||||
this.index = index;
|
||||
}
|
||||
}
|
19
src/dialogue/event/OptionsShown.hx
Normal file
19
src/dialogue/event/OptionsShown.hx
Normal file
@ -0,0 +1,19 @@
|
||||
package dialogue.event;
|
||||
|
||||
import hxyarn.dialogue.markup.MarkupParseResult;
|
||||
import event.IEvent;
|
||||
|
||||
typedef OptionChoice = {
|
||||
text:String,
|
||||
index:Int,
|
||||
enabled:Bool,
|
||||
markup:MarkupParseResult
|
||||
}
|
||||
|
||||
class OptionsShown implements IEvent {
|
||||
public var options:Array<OptionChoice>;
|
||||
|
||||
public function new(options:Array<OptionChoice>) {
|
||||
this.options = options;
|
||||
}
|
||||
}
|
11
src/dialogue/event/StartDialogueNode.hx
Normal file
11
src/dialogue/event/StartDialogueNode.hx
Normal file
@ -0,0 +1,11 @@
|
||||
package dialogue.event;
|
||||
|
||||
import event.IEvent;
|
||||
|
||||
class StartDialogueNode implements IEvent {
|
||||
public var node:String;
|
||||
|
||||
public function new(node:String) {
|
||||
this.node = node;
|
||||
}
|
||||
}
|
11
src/event/ChangeSceneEvent.hx
Normal file
11
src/event/ChangeSceneEvent.hx
Normal file
@ -0,0 +1,11 @@
|
||||
package event;
|
||||
|
||||
import scene.GameScene;
|
||||
|
||||
class ChangeSceneEvent implements IEvent {
|
||||
public var newScene:GameScene;
|
||||
|
||||
public function new(gs:GameScene) {
|
||||
this.newScene = gs;
|
||||
}
|
||||
}
|
48
src/event/EventBus.hx
Normal file
48
src/event/EventBus.hx
Normal file
@ -0,0 +1,48 @@
|
||||
package event;
|
||||
|
||||
import h2d.Console;
|
||||
|
||||
class EventBus {
|
||||
var listeners = new Map<String, Array<Dynamic>>();
|
||||
var console:Console;
|
||||
|
||||
public function new(console:Console) {
|
||||
this.console = console;
|
||||
}
|
||||
|
||||
public function subscribe<T:IEvent>(event:Class<T>, callback:(T) -> Void) {
|
||||
var type = Type.getClassName(event);
|
||||
if (!listeners.exists(type))
|
||||
listeners.set(type, new Array<T>());
|
||||
|
||||
listeners.get(type).push(callback);
|
||||
}
|
||||
|
||||
public function unsubscribe<T:IEvent>(event:Class<T>, callback:(T) -> Void) {
|
||||
var type = Type.getClassName(event);
|
||||
if (!listeners.exists(type))
|
||||
return;
|
||||
|
||||
var callbacks = listeners.get(type);
|
||||
for (c in callbacks) {
|
||||
if (Reflect.compareMethods(c, callback)) {
|
||||
callbacks.remove(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function publishEvent<T:IEvent>(event:T) {
|
||||
var type = Type.getClassName(Type.getClass(event));
|
||||
if (!listeners.exists(type)) {
|
||||
#if debug
|
||||
console.log('Publishing event with no listeners: $type');
|
||||
#end
|
||||
return;
|
||||
}
|
||||
trace('Publishing event $type');
|
||||
|
||||
for (func in listeners.get(type))
|
||||
func(event);
|
||||
}
|
||||
}
|
3
src/event/IEvent.hx
Normal file
3
src/event/IEvent.hx
Normal file
@ -0,0 +1,3 @@
|
||||
package event;
|
||||
|
||||
interface IEvent {}
|
9
src/event/OpenRoomEvent.hx
Normal file
9
src/event/OpenRoomEvent.hx
Normal file
@ -0,0 +1,9 @@
|
||||
package event;
|
||||
|
||||
class OpenRoomEvent implements IEvent {
|
||||
public var roomName:String;
|
||||
|
||||
public function new(rn:String) {
|
||||
this.roomName = rn;
|
||||
}
|
||||
}
|
125
src/listeners/DialogueBoxController.hx
Normal file
125
src/listeners/DialogueBoxController.hx
Normal file
@ -0,0 +1,125 @@
|
||||
package listeners;
|
||||
|
||||
import Game;
|
||||
|
||||
import event.EventBus;
|
||||
|
||||
import dialogue.DialogueManager;
|
||||
|
||||
import dialogue.event.LineShown;
|
||||
import dialogue.event.OptionsShown;
|
||||
import dialogue.event.OptionSelected;
|
||||
import dialogue.event.DialogueComplete;
|
||||
import dialogue.event.NextLine;
|
||||
|
||||
import components.DialogueBoxComponent;
|
||||
|
||||
class DialogueBoxController {
|
||||
public var curDialogue:DialogueBoxComponent;
|
||||
|
||||
var game:Game;
|
||||
var bg:h2d.ScaleGrid;
|
||||
var tf:h2d.Text;
|
||||
var int:h2d.Interactive;
|
||||
var eventBus:EventBus;
|
||||
var parent:h2d.Object;
|
||||
var style:h2d.domkit.Style;
|
||||
|
||||
public function new(eventBus:EventBus, parent:h2d.Object, style:h2d.domkit.Style) {
|
||||
this.parent = parent;
|
||||
this.style = style;
|
||||
// game = Game.current;
|
||||
// parent.getScene().add(this, 1);
|
||||
// bg = new h2d.ScaleGrid(Res.ui.toTile(), 5, 5, this);
|
||||
// bg.colorKey = 0xFF00FF;
|
||||
// tf = new h2d.Text(game.font, this);
|
||||
// tf.y = 5;
|
||||
// tf.x = 7;
|
||||
// tf.dropShadow = {
|
||||
// dx: 0,
|
||||
// dy: 1,
|
||||
// color: 0,
|
||||
// alpha: 0.3
|
||||
// };
|
||||
// int = new h2d.Interactive(0, 0, this);
|
||||
// int.onClick = function(_) click();
|
||||
|
||||
eventBus.subscribe(LineShown, dialogue);
|
||||
// eventBus.subscribe(OptionsShown, renderOptions);
|
||||
// eventBus.subscribe(OptionSelected, onOptionSelected);
|
||||
eventBus.subscribe(DialogueComplete, onDialogueComplete);
|
||||
}
|
||||
|
||||
// override function onRemove() {
|
||||
// super.onRemove();
|
||||
// // if (chan != null)
|
||||
// // chan.stop();
|
||||
// // timer.stop();
|
||||
// }
|
||||
|
||||
public dynamic function onClick() {}
|
||||
|
||||
public dynamic function onReady() {}
|
||||
|
||||
function dialogue(event:LineShown) {
|
||||
if (curDialogue != null) {
|
||||
curDialogue.remove();
|
||||
}
|
||||
var d = new DialogueBoxComponent(event.line(), this.parent);
|
||||
style.addObject(d);
|
||||
|
||||
curDialogue = d;
|
||||
}
|
||||
|
||||
// function renderOptions(event:OptionsShown) {
|
||||
// for (option in event.options) {
|
||||
// addOption(option.text, option.enabled, function() {
|
||||
// if (option.enabled) {
|
||||
// globalEventBus.publishEvent(new OptionSelected(option.index));
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// function newPanel(w, h) {
|
||||
// var g = new h2d.ScaleGrid(Res.ui.toTile(), 5, 5);
|
||||
// g.width = w;
|
||||
// g.height = h;
|
||||
// g.colorKey = 0xFF00FF;
|
||||
// root.scene.getH2dObject().getScene().add(g, 1);
|
||||
// return g;
|
||||
// }
|
||||
//
|
||||
// function addOption(o:String, enabled:Bool, callback:() -> Void) {
|
||||
// var spr = newPanel(Const.W, 60);
|
||||
// curDialogue.addChild(spr);
|
||||
// var tf = new h2d.Text(font, spr);
|
||||
// if (!enabled) {
|
||||
// tf.color = new Vector4(0.5, 0.5, 0.5, 1);
|
||||
// }
|
||||
// tf.text = o;
|
||||
// tf.x = 10;
|
||||
// tf.y = 10;
|
||||
// spr.x = 0;
|
||||
// spr.y = 100 + options.length * 60;
|
||||
// var int = new h2d.Interactive(spr.width, spr.height, spr);
|
||||
// int.onClick = function(_) {
|
||||
// callback();
|
||||
// }
|
||||
// int.cursor = Button;
|
||||
// options.push(spr);
|
||||
// return spr;
|
||||
// }
|
||||
//
|
||||
// function onOptionSelected(event:OptionSelected) {
|
||||
// for (o in options) {
|
||||
// o.remove();
|
||||
// }
|
||||
// options.splice(0, options.length);
|
||||
// }
|
||||
|
||||
function onDialogueComplete(event:DialogueComplete) {
|
||||
curDialogue.remove();
|
||||
}
|
||||
|
||||
}
|
5
src/scene/GameScene.hx
Normal file
5
src/scene/GameScene.hx
Normal file
@ -0,0 +1,5 @@
|
||||
package scene;
|
||||
|
||||
interface GameScene {
|
||||
function getH2dObject():h2d.Object;
|
||||
}
|
52
src/scene/TitleScene.hx
Normal file
52
src/scene/TitleScene.hx
Normal file
@ -0,0 +1,52 @@
|
||||
package scene;
|
||||
|
||||
import scene.WorldMapScene;
|
||||
import event.ChangeSceneEvent;
|
||||
import components.MenubuttonComponent;
|
||||
|
||||
@:uiComp("heading")
|
||||
class HeadingComp extends h2d.Flow implements h2d.domkit.Object {
|
||||
static var SRC = <heading class="mybox" min-width="200">
|
||||
<text text={"Glass House"}/>
|
||||
for( i in icons )
|
||||
<bitmap src={i} id="icons[]"/>
|
||||
</heading>;
|
||||
|
||||
public function new(icons:Array<h2d.Tile>, ?parent) {
|
||||
super(parent);
|
||||
initComponent();
|
||||
}
|
||||
}
|
||||
|
||||
@:uiComp("title-scene")
|
||||
class TitleScene extends h2d.Flow implements GameScene implements h2d.domkit.Object {
|
||||
static var SRC = <title-scene>
|
||||
<heading([]) id="view"/>
|
||||
<menubutton("New game") public id="btnNewGame"/>
|
||||
<menubutton("Continue game") public id="btnContinueGame"/>
|
||||
<menubutton("Credits") public id="btnCredits"/>
|
||||
<menubutton("Quit") public id="btnQuit"/>
|
||||
</title-scene>;
|
||||
|
||||
public function new(?parent) {
|
||||
super(parent);
|
||||
initComponent();
|
||||
btnNewGame.onClick = function() {
|
||||
Game.current.globalEventBus.publishEvent(new ChangeSceneEvent(new WorldMapScene(Game.current.root)));
|
||||
}
|
||||
btnContinueGame.onClick = function() {
|
||||
btnContinueGame.dom.addClass("highlight");
|
||||
}
|
||||
#if hl
|
||||
btnQuit.onClick = function() {
|
||||
Sys.exit(0);
|
||||
}
|
||||
#else
|
||||
btnQuit.remove();
|
||||
#end
|
||||
}
|
||||
|
||||
public function getH2dObject() {
|
||||
return this;
|
||||
}
|
||||
}
|
76
src/scene/WorldMapScene.hx
Normal file
76
src/scene/WorldMapScene.hx
Normal file
@ -0,0 +1,76 @@
|
||||
package scene;
|
||||
|
||||
import dialogue.event.StartDialogueNode;
|
||||
import event.OpenRoomEvent;
|
||||
import TilemapLayer;
|
||||
|
||||
typedef Room = {
|
||||
rect:h2d.Graphics,
|
||||
area:h2d.Interactive,
|
||||
isOpen:Bool
|
||||
}
|
||||
|
||||
@:uiComp("world-map-scene")
|
||||
class WorldMapScene extends h2d.Flow implements GameScene implements h2d.domkit.Object {
|
||||
public var width:Int;
|
||||
public var height:Int;
|
||||
|
||||
var rooms:Map<String, Room> = new Map<String, Room>();
|
||||
|
||||
public function new(?parent) {
|
||||
super(parent);
|
||||
initComponent();
|
||||
|
||||
var root = new h2d.Object(this);
|
||||
|
||||
var map = Game.loader.loadTMX("map.tmx");
|
||||
|
||||
Game.current.globalEventBus.subscribe(OpenRoomEvent, openRoom);
|
||||
|
||||
for (layer in (map.layers)) {
|
||||
switch (layer.kind) {
|
||||
case TTileLayer:
|
||||
var l = new TilemapLayer(map, layer, root);
|
||||
case TObjectGroup:
|
||||
for (obj in layer.objects) {
|
||||
var rect = new h2d.Graphics(root);
|
||||
rect.beginFill(0x333333);
|
||||
rect.alpha = 0.8;
|
||||
rect.drawRect(obj.x, obj.y, obj.width, obj.height);
|
||||
rect.endFill();
|
||||
var area = new h2d.Interactive(obj.width, obj.height, root);
|
||||
area.x = obj.x;
|
||||
area.y = obj.y;
|
||||
area.onClick = function(_) {
|
||||
var room = rooms.get(obj.name);
|
||||
if (room.isOpen) {
|
||||
Game.current.globalEventBus.publishEvent(new StartDialogueNode("Room:" + obj.name));
|
||||
} else {
|
||||
Game.current.globalEventBus.publishEvent(new StartDialogueNode("Room not open"));
|
||||
}
|
||||
};
|
||||
rooms.set(obj.name, {rect: rect, area: area, isOpen: false});
|
||||
}
|
||||
case TImageLayer:
|
||||
case TGroup:
|
||||
}
|
||||
}
|
||||
|
||||
#if debug
|
||||
trace('skipping intro dialogue');
|
||||
Game.current.globalEventBus.publishEvent(new OpenRoomEvent("quarters"));
|
||||
#else
|
||||
Game.current.globalEventBus.publishEvent(new StartDialogueNode("Intro"));
|
||||
#end
|
||||
}
|
||||
|
||||
public function openRoom(event:OpenRoomEvent) {
|
||||
var room = rooms.get(event.roomName);
|
||||
room.isOpen = true;
|
||||
room.rect.alpha = 0;
|
||||
}
|
||||
|
||||
public function getH2dObject() {
|
||||
return this;
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package screens;
|
||||
|
||||
import components.MenubuttonComponent;
|
||||
|
||||
@:uiComp("heading")
|
||||
class HeadingComp extends h2d.Flow implements h2d.domkit.Object {
|
||||
static var SRC = <heading class="mybox" min-width="200" content-halign={align}>
|
||||
<text text={"Rewild"}/>
|
||||
for( i in icons )
|
||||
<bitmap src={i} id="icons[]"/>
|
||||
</heading>;
|
||||
|
||||
public function new(align:h2d.Flow.FlowAlign, icons:Array<h2d.Tile>, ?parent) {
|
||||
super(parent);
|
||||
initComponent();
|
||||
}
|
||||
}
|
||||
|
||||
@:uiComp("title-screen")
|
||||
class TitleScreen extends h2d.Flow implements h2d.domkit.Object {
|
||||
static var SRC = <title-screen>
|
||||
<heading(align,[]) id="view"/>
|
||||
<menubutton("New game") public id="btnNewGame"/>
|
||||
<menubutton("Continue game") public id="btnContinueGame"/>
|
||||
<menubutton("Credits") public id="btnCredits"/>
|
||||
<menubutton("Quit") public id="btnQuit"/>
|
||||
</title-screen>;
|
||||
|
||||
public function new(align:h2d.Flow.FlowAlign, ?parent) {
|
||||
super(parent);
|
||||
initComponent();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user