Compare commits

...

8 commits

Author SHA1 Message Date
Bill Niblock
a856736739 Update Gem -> Version 0.1.2 2017-10-19 00:48:15 -04:00
Bill Niblock
ce2eb89b44 dialogue.rb: Rubocop ABC fixes 2017-10-19 00:44:41 -04:00
Bill Niblock
c6c9522a18 Update gem 2017-10-19 00:26:35 -04:00
Bill Niblock
54f23625a8 sapling.rb: Fix argument passing
Accidentially moved the debug argument to the Gardner invokation,
instead of leaving with the Dialogue invokation.
2017-10-19 00:21:03 -04:00
Bill Niblock
bd45e60bb7 utility.rb: Rubocop fixes 2017-10-19 00:20:21 -04:00
Bill Niblock
e85a882851 dialogue.rb: Rubocop fixes and refactor changes
- Fix several Rubocop problems
- Update module for gardner refactor
    - Update all tree references to now reference Gardner::Plot object
2017-10-19 00:19:14 -04:00
Bill Niblock
6e16b250a5 gardner.rb: Rubocop fixes and refactor changes
- Fix several rubocop problems
- Update module to more fully encapsulate all dialogue tree handling
    - Add Plot class for storing all tree data
    - Add Digiplot class for (eventual) tree editing
2017-10-19 00:18:07 -04:00
Bill Niblock
8700f307a7 sapling.rb: Rubocop and refactor updates
- Fix several rubocop violations
- Update functionality to account for gardner refactor
2017-10-18 23:46:00 -04:00
5 changed files with 155 additions and 107 deletions

View file

@ -8,19 +8,20 @@ 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(tree)
exit unless verify_tree(tree)
def read(file)
puts 'Welcome to Sapling, a Dialogue Tree Utility.'
speaker = Dialogue::Speaker.new(YAML.load_file(tree), false)
exit unless verify_tree(file)
tree = Gardner::Plot.new(YAML.load_file(file))
speaker = Dialogue::Speaker.new(tree, false)
speaker.conversation
end
desc 'edit TREE', 'Edit a new or existing TREE'
def edit(tree = '')
def edit(file = '')
puts 'Welcome to Sapling, a Dialogue Tree Utility.'
if !tree.empty?
puts "Loading tree: #{tree}"
exit unless verify_tree(tree)
puts "Loading tree: #{file}"
exit unless verify_tree(file)
gardner = Planter::Spade.new(YAML.load_file(tree, false))
else
puts 'Creating a new tree!'

View file

@ -2,16 +2,15 @@ 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
@ -20,11 +19,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
@ -32,30 +31,27 @@ module Dialogue
# Speaker holds the functionality for going through a dialogue tree.
class Speaker
# The file, which should be a dialogue tree YAML file.
attr_accessor :file
# The tree, an instance of Gardner::Plot
attr_reader :tree
# Status of verbose/debug mode. True = on; false = off.
attr_accessor :debug
attr_reader :debug
def initialize(file="", debug=false)
@file = file
def initialize(tree, debug = false)
@tree = tree
@debug = debug
end
# Conversation handles navigating the tree, until the option to end is
# reached.
def conversation()
tree = Gardner.prune_trunk(@file)
Dialogue.display_trunk(tree[0], false)
branches = Gardner.prune_branches(tree[1])
def conversation
Dialogue.display_trunk(@tree.trunk, @debug)
next_branch = 1
until next_branch == 0 do
next_branch = talk(branches[next_branch], next_branch)
until next_branch.zero?
next_branch = talk(@tree.branches[next_branch], next_branch)
end
puts "\n#{branches[0]["desc"]}"
puts "\n#{@tree.branches[0]['desc']}"
exit
end
@ -65,25 +61,18 @@ module Dialogue
# @param branch_no [Integer] The branch number
# @return [Integer] The number of the next branch
def talk(branch, branch_no)
# 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
return 0 if terminal?(branch)
Dialogue.display_branch(branch, branch_no, @debug)
response = get_response(branch)
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
unless response.zero?
puts "(Your choice: #{branch['options'][response].keys[0]})"
response = branch['options'][response].values[0].to_i
end
return response
response
end
# Get a response for the displayed branch
@ -91,20 +80,32 @@ 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.to_i
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
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
end
return response
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
end
end
end

View file

@ -2,47 +2,95 @@ 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
# 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"]) }
# Initialize a new Plot from a tree file
#
# @param tree [File] The dialogue tree file
def initialize(file)
@tree = file
prune_trunk
prune_branches
end
return branches
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 self.prune_leaves(leaves)
x = 1
options = {}
return options if leaves.nil?
leaves.each do |l|
options[x] = { l["text"] => l["branch"] }
x += 1
# 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
return options
# 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
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
# 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
return [trunk,tree]
# Enable editing for the trunk
attr_writer :trunk
# 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
end
# Change a branch
#
# @param branch [Integer] the number of the branch to be edited
def branch=(branch, text)
@branches[branch]['desc'] = text
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
end
end

View file

@ -7,34 +7,32 @@
# documentation.
# The default trunk text of a new tree
SKELE_TRUNK_TEXT = "Welcome to the Sapling Editor. For details, please see the
documentation!"
SKELE_TRUNK_TEXT = 'Welcome to the Sapling Editor. For details, please see the
documentation!'.freeze
# 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!"
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
# 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."
can point to branch 0 to immediately exit.'.freeze
# The final tree
SKELETON_TREE = [
{"trunk" => "#{SKELE_TRUNK_TEXT}"},
{"branch" => {
"number" => 1,
"text" => "#{SKELE_BRANCH_TEXT}",
"leaf" => [{
"text" => "#{SKELE_LEAF_TEXT}",
"branch" => 1
}]
}
}
]
{ '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
# Verify that a file is a dialogue tree file.
#
@ -44,13 +42,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.0'
s.version = '0.1.2'
s.executables << 'sapling'
s.date = '2017-10-14'
s.date = '2017-10-19'
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/*']