Compare commits

..

2 commits

Author SHA1 Message Date
Bill Niblock
8233af348a Rakefile: Dummy task to make builds pass 2017-10-18 21:48:17 -04:00
Bill Niblock
26a6691ce4 Add minitest 2017-10-18 21:48:03 -04:00
8 changed files with 114 additions and 153 deletions

View file

@ -9,4 +9,5 @@ gem "yard"
gem "yard-ghpages"
# Testing
gem "minitest"
gem "rubocop"

View file

@ -3,6 +3,7 @@ GEM
specs:
ast (2.3.0)
git (1.3.0)
minitest (5.10.1)
parser (2.4.0.0)
ast (~> 2.2)
powerpack (0.1.1)
@ -30,6 +31,7 @@ PLATFORMS
ruby
DEPENDENCIES
minitest
rdoc
redcarpet
rubocop

View file

@ -1,3 +1,9 @@
require 'yard-ghpages'
task default: %w[truth]
task :truth do
puts 'True!'
end
Yard::GHPages::Tasks.install_tasks

View file

@ -8,20 +8,19 @@ Dir[File.join(__dir__, 'sapling', '*.rb')].each { |file| require file }
# The main Sapling interface.
class Sapling < Thor
desc 'read TREE', 'Load and traverse the TREE'
def read(file)
def read(tree)
exit unless verify_tree(tree)
puts 'Welcome to Sapling, a Dialogue Tree Utility.'
exit unless verify_tree(file)
tree = Gardner::Plot.new(YAML.load_file(file))
speaker = Dialogue::Speaker.new(tree, false)
speaker = Dialogue::Speaker.new(YAML.load_file(tree), false)
speaker.conversation
end
desc 'edit TREE', 'Edit a new or existing TREE'
def edit(file = '')
def edit(tree = '')
puts 'Welcome to Sapling, a Dialogue Tree Utility.'
if !tree.empty?
puts "Loading tree: #{file}"
exit unless verify_tree(file)
puts "Loading tree: #{tree}"
exit unless verify_tree(tree)
gardner = Planter::Spade.new(YAML.load_file(tree, false))
else
puts 'Creating a new tree!'

View file

@ -2,15 +2,16 @@ require_relative './gardner'
# Dialogue is the module for traversing an existing tree.
module Dialogue
# Format and display the trunk
#
# @param trunk [Hash] The trunk hash
# @param debug [Boolean] The status of showing debug information
def self.display_trunk(trunk, debug = false)
40.times { print '-' }
def self.display_trunk(trunk, debug=false)
40.times { print "-" }
puts "\n[ Trunk ]\n" if debug
puts "\n#{trunk['trunk']}"
40.times { print '-' }
puts "\n#{trunk["trunk"]}"
40.times { print "-" }
puts "\n"
end
@ -19,11 +20,11 @@ module Dialogue
# @param branch [Hash] A branch data set
# @param branch_no [Integer] The branch number
# @param debug [Boolean] Status of showing debug information
def self.display_branch(branch, branch_no, debug = false)
def self.display_branch(branch, branch_no, debug=false)
puts "\n[ Branch: #{branch_no} ]" if debug
puts "\n#{branch['desc']}\n\n"
puts "\n#{branch["desc"]}\n\n"
branch['options'].each_pair do |k, v|
branch["options"].each_pair do |k,v|
puts "\t#{k}: #{v.keys[0]}"
puts "\t\t[ Goes to branch #{v.values[0]} ]\n" if debug
end
@ -31,27 +32,30 @@ module Dialogue
# Speaker holds the functionality for going through a dialogue tree.
class Speaker
# The tree, an instance of Gardner::Plot
attr_reader :tree
# The file, which should be a dialogue tree YAML file.
attr_accessor :file
# Status of verbose/debug mode. True = on; false = off.
attr_reader :debug
attr_accessor :debug
def initialize(tree, debug = false)
@tree = tree
def initialize(file="", debug=false)
@file = file
@debug = debug
end
# Conversation handles navigating the tree, until the option to end is
# reached.
def conversation
Dialogue.display_trunk(@tree.trunk, @debug)
def conversation()
tree = Gardner.prune_trunk(@file)
Dialogue.display_trunk(tree[0], false)
branches = Gardner.prune_branches(tree[1])
next_branch = 1
until next_branch.zero?
next_branch = talk(@tree.branches[next_branch], next_branch)
until next_branch == 0 do
next_branch = talk(branches[next_branch], next_branch)
end
puts "\n#{@tree.branches[0]['desc']}"
puts "\n#{branches[0]["desc"]}"
exit
end
@ -61,18 +65,25 @@ module Dialogue
# @param branch_no [Integer] The branch number
# @return [Integer] The number of the next branch
def talk(branch, branch_no)
return 0 if terminal?(branch)
# If there are no options on this branch, we assume it's a terminal
# branch. Return 0, and end the program.
if branch["options"].empty?
puts "\n#{branch["desc"]}\n\n"
return 0
end
Dialogue.display_branch(branch, branch_no, @debug)
response = get_response(branch)
unless response.zero?
puts "(Your choice: #{branch['options'][response].keys[0]})"
response = branch['options'][response].values[0].to_i
unless response == 0
puts "\n"
10.times { print "*" }
puts "\n(Your choice: #{branch["options"][response].keys[0]})"
response = branch["options"][response].values[0].to_i
end
response
return response
end
# Get a response for the displayed branch
@ -80,32 +91,20 @@ module Dialogue
# @param branch [Hash] A branch data set
# @return [Integer] the next branch
def get_response(branch)
valid_options = branch['options'].keys.join(', ')
valid_options = branch["options"].keys.join(", ")
print "\n[#{valid_options}]> "
STDOUT.flush
response = STDIN.gets.chomp
response = STDIN.gets.chomp.to_i
until valid_options.include?(response) || response.to_i.zero?
print "[## Invalid options. Valid options are #{valid_options}," \
"or 0 to exit.\n[#{valid_options}]> "
response = STDIN.gets.chomp
until branch["options"].keys.include?(response) or response == 0
print "[## Invalid options. "
print "Valid options are #{valid_options}, or 0 to exit."
print "\n[#{valid_options}]> "
response = STDIN.gets.chomp.to_i
end
response.to_i
end
# Check if a branch is terminal
#
# @param branch [Hash] A branch data set
# @return [Boolean] true if the branch is terminal, false otherwise
def terminal?(branch)
if branch['options'].empty?
puts "\n#{branch['desc']}\n\n"
return true
end
false
return response
end
end
end

View file

@ -2,95 +2,47 @@ require 'yaml'
# Gardner is the module for working with a dialogue tree file
module Gardner
# The Plot class handles a specific tree file. It provides functionality for
# parsing trunks and branches, and provides these as class attributes.
class Plot
# The trunk and branches instance variables
attr_reader :branches, :tree, :trunk
# Initialize a new Plot from a tree file
#
# @param tree [File] The dialogue tree file
def initialize(file)
@tree = file
prune_trunk
prune_branches
# Parse the tree array into an array of numbered branches, and ordered leaves.
#
# @param tree [Array] The dialogue tree
# @return [Array] An array of numbered branches, with numbered leaves
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
# Parse the tree array into an array of numbered branches, and ordered
# leaves.
#
# @param tree [File] The dialogue tree
# @return [Array] An array of numbered branches, with numbered leaves
def prune_branches
@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
end
# Parse the leaves of a branch into a numbered hash of options.
#
# @param leaves [Array] The option of leaf hashes
# @return [Hash] A numbered hash of options
def prune_leaves(leaves)
x = 1
options = {}
return options if leaves.nil?
leaves.each do |l|
options[x] = { l['text'] => l['branch'] }
x += 1
end
options
end
# Parse the trunk of the tree.
#
# @return [Array] The trunk, and the remainder of the tree
def prune_trunk
@trunk = @tree.shift
end
return branches
end
# Digiplot represents a Plot used for editing. The Digiplot functions exactly
# like a Plot, except with additional functionality for over-writing existing
# branches, leaves, and the trunk.
class Digiplot < Plot
# Duplicate the "old" trunk and branches, for restoration purposes
attr_reader :old_branches, :old_trunk
# Parse the leaves of a branch into a numbered hash of options.
#
# @param leaves [Array] The option of leaf hashes
# @return [Hash] A numbered hash of options
def self.prune_leaves(leaves)
x = 1
options = {}
# Enable editing for the trunk
attr_writer :trunk
return options if leaves.nil?
# Initialize a Digiplot just like a Plot, but also copy the trunk and
# branches to "old" instance variables.
def initialize
super
@old_trunk = @trunk
@old_branches = @branches
leaves.each do |l|
options[x] = { l["text"] => l["branch"] }
x += 1
end
# Change a branch
#
# @param branch [Integer] the number of the branch to be edited
def branch=(branch, text)
@branches[branch]['desc'] = text
end
return options
end
# Change a leaf on a branch, grasshopper
#
# @param branch [Integer] the number of the branch to be edited
# @param leaf [Integer] the number of the leaf to be edited
# @param text [String] the new text for the leaf
# @param target [Integer] the branch number target for the leaf option
def leaf=(branch, leaf, text, target)
@branches[branch]['options'][leaf] = { text => target }
end
# Parse the trunk of the tree.
#
# @param tree [Hash] The entire tree
# @return [Array] The trunk, and the remainder of the tree
def self.prune_trunk(tree)
trunk = tree.shift
return [trunk,tree]
end
end

View file

@ -7,32 +7,34 @@
# documentation.
# The default trunk text of a new tree
SKELE_TRUNK_TEXT = 'Welcome to the Sapling Editor. For details, please see the
documentation!'.freeze
SKELE_TRUNK_TEXT = "Welcome to the Sapling Editor. For details, please see the
documentation!"
# The default first-branch text of a new tree
SKELE_BRANCH_TEXT = 'The first branch is always shown by default. It should act
as the introduction to the story. From here, the user enters your world!'.freeze
SKELE_BRANCH_TEXT = "The first branch is always shown by default. It should act
as the introduction to the story. From here, the user enters your world!"
# The default first-leaf text of the first branch of a new tree. The leaf points
# to it's own branch. The only way out of the program is to either force-quit or
# reply with option 0.
SKELE_LEAF_TEXT = 'Each branch can have any number of leaves, which represent
SKELE_LEAF_TEXT = "Each branch can have any number of leaves, which represent
the options a user has on that branch. Each leaf points to another branch, or
can point to branch 0 to immediately exit.'.freeze
can point to branch 0 to immediately exit."
# The final tree
SKELETON_TREE = [
{ 'trunk' => SKELE_TRUNK_TEXT.to_s },
{ 'branch' => {
'number' => 1,
'text' => SKELE_BRANCH_TEXT.to_s,
'leaf' => [{
'text' => SKELE_LEAF_TEXT.to_s,
'branch' => 1
}]
} }
].freeze
{"trunk" => "#{SKELE_TRUNK_TEXT}"},
{"branch" => {
"number" => 1,
"text" => "#{SKELE_BRANCH_TEXT}",
"leaf" => [{
"text" => "#{SKELE_LEAF_TEXT}",
"branch" => 1
}]
}
}
]
# Verify that a file is a dialogue tree file.
#
@ -42,13 +44,13 @@ def 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')
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.'
puts "Verify your YAML file is formatted properly."
results << false
end

View file

@ -1,10 +1,10 @@
Gem::Specification.new do |s|
s.name = 'sapling-dialogue'
s.version = '0.1.2'
s.version = '0.1.0'
s.executables << 'sapling'
s.date = '2017-10-19'
s.date = '2017-10-14'
s.summary = 'A Dialogue Tree Utility'
s.description = 'Create, edit, and traverse dialogue trees'
s.description = 'Create, edit, and traverse Dialogue trees'
s.authors = ['Bill Niblock']
s.email = 'azulien@gmail.com'
s.files = Dir['lib/**/*.rb'] + Dir['bin/*']