245 lines
6.2 KiB
Ruby
245 lines
6.2 KiB
Ruby
#!/usr/bin/env ruby
|
|
|
|
require 'optparse'
|
|
require 'yaml'
|
|
|
|
# Gardner is the module for working with a dialogue tree file
|
|
module Gardner
|
|
|
|
# Parse the branch
|
|
#
|
|
# @param tree [Array] The dialogue tree
|
|
# @return branches [Array] The array of options on the branch.
|
|
def self.prune_branches(tree)
|
|
branches = { 0 => { "desc" => "Thanks for using Sapling!" } }
|
|
tree.each do |b|
|
|
branches[b["branch"]["number"]] = {
|
|
"desc" => b["branch"]["text"],
|
|
"options" => prune_leaves(b["branch"]["leaf"]) }
|
|
end
|
|
|
|
return branches
|
|
|
|
end
|
|
|
|
# Parse the options
|
|
#
|
|
# @param leaves [Array] The option of leaf hashes
|
|
# @return options [Hash] A has of options
|
|
def self.prune_leaves(leaves)
|
|
x = 1
|
|
options = {}
|
|
leaves.each do |l|
|
|
options[x] = { l["text"] => l["branch"] }
|
|
x += 1
|
|
end
|
|
|
|
return options
|
|
|
|
end
|
|
|
|
# Parse the trunk
|
|
# The trunk is like the introduction to the tree.
|
|
#
|
|
# @param tree [Hash] The entire tree
|
|
# @return tree [Hash] The tree without the trunk
|
|
def self.prune_trunk(tree)
|
|
trunk = tree.shift
|
|
puts "Welcome to Sapling, a Dialogue Tree Utility.\n"
|
|
40.times { print "-" }
|
|
puts "\n#{trunk["trunk"]}"
|
|
40.times { print "-" }
|
|
puts "\n"
|
|
|
|
return tree
|
|
|
|
end
|
|
|
|
# The main method for Sapling. From here, the tree is grown.
|
|
#
|
|
# @param file [File] The dialogue tree file
|
|
# @return branches [Hash] The final, constructed data set
|
|
def self.grow(file)
|
|
tree = YAML.load_file(file[0])
|
|
tree = Gardner.prune_trunk(tree)
|
|
branches = Gardner.prune_branches(tree)
|
|
|
|
return branches
|
|
end
|
|
|
|
# Verify that a file is a dialogue tree file.
|
|
#
|
|
# @param file [File] The provided file
|
|
# @return status [Boolean] True if the file is a tree; false otherwise
|
|
def self.verify_tree(file)
|
|
results = []
|
|
begin
|
|
tree = YAML.load_file(file)
|
|
results << tree[0].keys.include?("trunk")
|
|
results << tree[1]["branch"].keys.include?("number")
|
|
results << tree[1]["branch"].keys.include?("text")
|
|
results << tree[1]["branch"].keys.include?("leaf")
|
|
rescue
|
|
puts "Sorry chummer, I don't think this is a tree."
|
|
puts "Verify your YAML file is formatted properly."
|
|
results << false
|
|
end
|
|
|
|
results.include?(false) ? false : true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
# Dialogue is the module for traversing an existing tree.
|
|
module Dialogue
|
|
|
|
class Speaker
|
|
attr_accessor :file
|
|
|
|
def initialize
|
|
@file = ""
|
|
end
|
|
|
|
# Scribe generates the new data set, which provides properly organized
|
|
# branches and choices
|
|
def scribe
|
|
tree = Gardner.grow(@file)
|
|
conversation(tree)
|
|
end
|
|
|
|
# Conversation handles navigating the tree, until the option to end is
|
|
# reached.
|
|
#
|
|
# @param tree [Hash] The data set of branches and associated choices
|
|
def conversation(tree)
|
|
10.times { print "*" }
|
|
next_branch = talk(tree[1])
|
|
until next_branch == 0 do
|
|
next_branch = talk(tree[next_branch])
|
|
end
|
|
|
|
puts tree[0]["desc"]
|
|
exit
|
|
end
|
|
|
|
# Talk displays a branch, the options, and prompts for a response
|
|
#
|
|
# @param branch [Hash] A branch data set
|
|
# @return response [Integer] The number of the next branch
|
|
def talk(branch)
|
|
puts "\n#{branch["desc"]}\n\n"
|
|
branch["options"].each_pair do |k,v|
|
|
puts "\t#{k}: #{v.keys[0]}"
|
|
end
|
|
|
|
print "> "
|
|
STDOUT.flush
|
|
response = STDIN.gets.chomp.to_i
|
|
|
|
puts "\n"
|
|
10.times { print "*" }
|
|
puts "\n(Your choice: #{branch["options"][response].keys[0]})"
|
|
return branch["options"][response].values[0].to_i
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
# Planter is the module for creating or editing a tree.
|
|
module Planter
|
|
|
|
end
|
|
|
|
# Parsing is the class for option parsing, and the gateway to the program
|
|
class Parsing
|
|
|
|
# Option parsing, and gateway to either reading and traversing a tree, or
|
|
# editing/creating a tree.
|
|
#
|
|
# @params file [String] The location of the file to read, or write.
|
|
def talk(options)
|
|
opt_parser = OptionParser.new do |opt|
|
|
opt.banner = "Usage: sapling [-t][-e] FILE" \
|
|
|
|
opt.on_tail("-h", "--help", "Show this menu") do
|
|
puts opt
|
|
exit
|
|
end
|
|
|
|
opt.on("-t", "--talk",
|
|
"Begin traversing the provided dialogue tree") do
|
|
|
|
if ARGV.empty?
|
|
puts opt_parser
|
|
exit
|
|
end
|
|
|
|
unless Gardner.verify_tree(ARGV[0])
|
|
puts "\n#{opt}\n"
|
|
exit
|
|
end
|
|
|
|
speaker = Dialogue::Speaker.new
|
|
speaker.file = ARGV
|
|
speaker.scribe
|
|
end
|
|
|
|
opt.on("-e", "--edit",
|
|
"Create or edit a dialogue tree") do
|
|
puts "We gonna make a tree!"
|
|
end
|
|
|
|
end
|
|
opt_parser.parse!(options)
|
|
|
|
if ARGV.empty?
|
|
puts opt_parser
|
|
exit
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
Parsing.new.talk(ARGV)
|
|
|
|
=begin
|
|
|
|
For a given tree:
|
|
|
|
tree[0] is the trunk. We want to display this, then pull it out of the array.
|
|
With the trunk gone, the remaining tree top-level elements are branches.
|
|
Now, we can parse each branch by doing tree.each { |b| parse_branch(b) }
|
|
parse_branch puts each branch into an array, at the location of it's number
|
|
At branch[0], it puts logic to end the program.
|
|
Now, we have a convenient way of moving around: when a leaf points to a branch,
|
|
we just reference branch[n] to get to that branch.
|
|
|
|
Traversing the tree thus goes like such:
|
|
- Start the loop by pulling in the appropriate branch. If the branch called is [0],
|
|
then we're done. End the program.
|
|
- Display all relevant information, formatted to display the text, followed by a
|
|
blank line, followed by the options, 1 per line, and numbered accordingly. At the
|
|
bottom is a prompt.
|
|
- The prompt expects a number. Anything not a number just displays another prompt
|
|
on the next line.
|
|
- Upon choosing that option, restart the loop with the new branch.
|
|
|
|
Parsing a branch:
|
|
- Format is tree[x] = branch element
|
|
tree[x]["branch"]["number"] is the branch number, aka the @branches array place
|
|
tree[x]["branch"]["text"] is the branch text
|
|
branches[tree[x]["branch"]["number"] = {
|
|
"desc" => tree[x]["branch"]["text"],
|
|
"options" => options_hash }
|
|
options_hash = {}
|
|
|
|
{ branch_number =>
|
|
{ "desc" => branch_text,
|
|
"options" => {
|
|
{ 1 => { leaf_text => next_branch },
|
|
2 => { leaf_text => next_branch }}
|
|
|
|
=end
|