gamefic 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/gamefic.rb +1 -0
- data/lib/gamefic/character.rb +19 -13
- data/lib/gamefic/engine.rb +2 -103
- data/lib/gamefic/engine/base.rb +55 -0
- data/lib/gamefic/engine/tty.rb +20 -254
- data/lib/gamefic/html.rb +20 -19
- data/lib/gamefic/html_to_ansi.rb +185 -0
- data/lib/gamefic/plot.rb +21 -29
- data/lib/gamefic/plot/scene_mount.rb +103 -36
- data/lib/gamefic/scene.rb +3 -2
- data/lib/gamefic/scene/active.rb +1 -6
- data/lib/gamefic/scene/base.rb +34 -8
- data/lib/gamefic/scene/conclusion.rb +0 -3
- data/lib/gamefic/scene/custom.rb +28 -7
- data/lib/gamefic/scene/multiple_choice.rb +31 -38
- data/lib/gamefic/scene/multiple_scene.rb +15 -0
- data/lib/gamefic/scene/pause.rb +4 -15
- data/lib/gamefic/scene/yes_or_no.rb +8 -13
- data/lib/gamefic/scene_data.rb +9 -0
- data/lib/gamefic/scene_data/base.rb +12 -0
- data/lib/gamefic/scene_data/multiple_choice.rb +19 -0
- data/lib/gamefic/scene_data/multiple_scene.rb +25 -0
- data/lib/gamefic/scene_data/yes_or_no.rb +18 -0
- data/lib/gamefic/shell.rb +77 -78
- data/lib/gamefic/tester.rb +1 -1
- data/lib/gamefic/tty.rb +8 -0
- data/lib/gamefic/user.rb +8 -0
- data/lib/gamefic/user/base.rb +21 -0
- data/lib/gamefic/user/buffer.rb +25 -0
- data/lib/gamefic/user/tty.rb +55 -0
- data/lib/gamefic/version.rb +1 -1
- metadata +15 -6
- data/lib/gamefic/engine/cgi.rb +0 -221
- data/lib/gamefic/scene/multiple_choice/input.rb +0 -11
- data/lib/gamefic/scene/passive.rb +0 -17
- data/lib/gamefic/scene/question.rb +0 -21
data/lib/gamefic/scene.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
|
+
require 'gamefic/scene_data'
|
2
|
+
|
1
3
|
module Gamefic
|
2
4
|
|
3
5
|
module Scene
|
4
6
|
autoload :Base, 'gamefic/scene/base'
|
5
7
|
autoload :Custom, 'gamefic/scene/custom'
|
6
8
|
autoload :Active, 'gamefic/scene/active'
|
7
|
-
autoload :Passive, 'gamefic/scene/passive'
|
8
9
|
autoload :Pause, 'gamefic/scene/pause'
|
9
10
|
autoload :Conclusion, 'gamefic/scene/conclusion'
|
10
11
|
autoload :MultipleChoice, 'gamefic/scene/multiple_choice'
|
12
|
+
autoload :MultipleScene, 'gamefic/scene/multiple_scene'
|
11
13
|
autoload :YesOrNo, 'gamefic/scene/yes_or_no'
|
12
|
-
autoload :Question, 'gamefic/scene/question'
|
13
14
|
end
|
14
15
|
|
15
16
|
end
|
data/lib/gamefic/scene/active.rb
CHANGED
@@ -5,13 +5,8 @@ module Gamefic
|
|
5
5
|
# a Plot.
|
6
6
|
#
|
7
7
|
class Scene::Active < Scene::Base
|
8
|
-
def start actor
|
9
|
-
# TODO Anything necessary here?
|
10
|
-
end
|
11
8
|
def finish actor, input
|
12
|
-
|
13
|
-
# HACK Set the last_order here so inline performs don't set it
|
14
|
-
actor.send(:last_order=, last_order)
|
9
|
+
actor.perform input, from_user: true
|
15
10
|
end
|
16
11
|
end
|
17
12
|
|
data/lib/gamefic/scene/base.rb
CHANGED
@@ -1,21 +1,47 @@
|
|
1
|
-
module Gamefic
|
1
|
+
module Gamefic
|
2
2
|
|
3
3
|
# The Base Scene is not intended for instantiation. Other Scene classes
|
4
4
|
# should inherit from it.
|
5
5
|
#
|
6
|
-
class Base
|
7
|
-
def prompt
|
8
|
-
@prompt ||= '>'
|
9
|
-
end
|
6
|
+
class Scene::Base
|
10
7
|
def start actor
|
11
|
-
|
8
|
+
start_data_for actor
|
12
9
|
end
|
10
|
+
|
13
11
|
def finish actor, input
|
14
|
-
|
12
|
+
finish_data_for actor, input
|
15
13
|
end
|
16
|
-
|
14
|
+
|
15
|
+
def type
|
17
16
|
self.class.to_s.split('::').last
|
18
17
|
end
|
18
|
+
|
19
|
+
def data_class
|
20
|
+
SceneData::Base
|
21
|
+
end
|
22
|
+
|
23
|
+
# Get the prompt to be displayed to the user when accepting input.
|
24
|
+
#
|
25
|
+
# @return [String] The text to be displayed.
|
26
|
+
def prompt_for actor
|
27
|
+
character_data[actor].prompt || '>'
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def character_data
|
33
|
+
@character_data ||= {}
|
34
|
+
end
|
35
|
+
|
36
|
+
def start_data_for actor
|
37
|
+
character_data[actor] = data_class.new
|
38
|
+
end
|
39
|
+
|
40
|
+
def finish_data_for actor, input
|
41
|
+
data = character_data[actor]
|
42
|
+
data.input = input.strip
|
43
|
+
data
|
44
|
+
end
|
19
45
|
end
|
20
46
|
|
21
47
|
end
|
data/lib/gamefic/scene/custom.rb
CHANGED
@@ -1,21 +1,42 @@
|
|
1
1
|
module Gamefic
|
2
2
|
|
3
3
|
# A Custom Scene is a generic scene that allows for complete configuration
|
4
|
-
# of its behavior upon instantiation. It is suitable for direct
|
4
|
+
# of its behavior upon instantiation. It is suitable for direct instantiation
|
5
5
|
# or extension by other Scene classes.
|
6
6
|
#
|
7
7
|
class Scene::Custom < Scene::Base
|
8
|
-
def initialize
|
9
|
-
|
10
|
-
@finish = config[:finish]
|
11
|
-
@prompt = config[:prompt]
|
8
|
+
def initialize
|
9
|
+
yield self if block_given?
|
12
10
|
end
|
11
|
+
def on_start &block
|
12
|
+
@start = block
|
13
|
+
end
|
14
|
+
|
15
|
+
def on_finish &block
|
16
|
+
@finish = block
|
17
|
+
end
|
18
|
+
|
13
19
|
def start actor
|
14
|
-
|
20
|
+
data = start_data_for(actor)
|
21
|
+
do_start_block actor, data
|
22
|
+
data
|
15
23
|
end
|
24
|
+
|
16
25
|
def finish actor, input
|
17
|
-
|
26
|
+
data = finish_data_for(actor, input)
|
27
|
+
do_finish_block actor, data
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def do_start_block actor, data
|
33
|
+
@start.call actor, data unless @start.nil?
|
34
|
+
end
|
35
|
+
|
36
|
+
def do_finish_block actor, data
|
37
|
+
@finish.call actor, data unless @finish.nil?
|
18
38
|
end
|
39
|
+
|
19
40
|
end
|
20
41
|
|
21
42
|
end
|
@@ -8,59 +8,52 @@ module Gamefic
|
|
8
8
|
# instead of a String.
|
9
9
|
#
|
10
10
|
class Scene::MultipleChoice < Scene::Custom
|
11
|
-
|
12
|
-
|
13
|
-
def initialize config = {}
|
14
|
-
super
|
15
|
-
@options = (config[:options] || [])
|
16
|
-
@invalid_choice_message = config[:invalid_choice_message]
|
11
|
+
def data_class
|
12
|
+
SceneData::MultipleChoice
|
17
13
|
end
|
18
|
-
|
14
|
+
|
19
15
|
def start actor
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
data = start_data_for(actor)
|
17
|
+
do_start_block actor, data
|
18
|
+
tell_options actor, data
|
23
19
|
end
|
24
20
|
|
25
21
|
def finish actor, input
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
22
|
+
data = finish_data_for(actor, input)
|
23
|
+
get_choice data
|
24
|
+
if data.selection.nil?
|
25
|
+
actor.tell data.invalid_message
|
26
|
+
tell_options actor, data
|
27
|
+
else
|
28
|
+
do_finish_block actor, data
|
29
|
+
end
|
30
|
+
data
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def get_choice data
|
36
|
+
if data.input.strip =~ /[0-9]+/ and data.input.to_i > 0
|
37
|
+
data.number = data.input.to_i
|
38
|
+
data.index = data.number - 1
|
39
|
+
data.selection = data.options[data.number - 1]
|
32
40
|
else
|
33
41
|
index = 0
|
34
|
-
|
35
|
-
if o.casecmp(input).zero?
|
36
|
-
|
42
|
+
data.options.each { |o|
|
43
|
+
if o.casecmp(data.input).zero?
|
44
|
+
data.selection = o
|
45
|
+
data.index = index
|
46
|
+
data.number = index + 1
|
37
47
|
break
|
38
48
|
end
|
39
49
|
index += 1
|
40
50
|
}
|
41
51
|
end
|
42
|
-
if choice.nil?
|
43
|
-
actor.tell invalid_choice_message
|
44
|
-
tell_choices actor
|
45
|
-
else
|
46
|
-
input_object = Input.new(input, index, choice)
|
47
|
-
super actor, input_object
|
48
|
-
end
|
49
52
|
end
|
50
53
|
|
51
|
-
def
|
52
|
-
@prompt ||= "Enter a choice:"
|
53
|
-
end
|
54
|
-
|
55
|
-
def invalid_choice_message
|
56
|
-
@invalid_choice_message ||= "That's not a valid selection."
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def tell_choices actor
|
54
|
+
def tell_options actor, data
|
62
55
|
list = '<ol class="multiple_choice">'
|
63
|
-
|
56
|
+
data.options.each { |o|
|
64
57
|
list += "<li>#{o}</li>"
|
65
58
|
}
|
66
59
|
list += "</ol>"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Gamefic
|
2
|
+
|
3
|
+
class Scene::MultipleScene < Scene::Custom
|
4
|
+
def data_class
|
5
|
+
SceneData::MultipleScene
|
6
|
+
end
|
7
|
+
|
8
|
+
def finish actor, input
|
9
|
+
data = super
|
10
|
+
unless data.selection.nil?
|
11
|
+
actor.cue data.scene_for(selection)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/gamefic/scene/pause.rb
CHANGED
@@ -1,23 +1,12 @@
|
|
1
1
|
module Gamefic
|
2
2
|
|
3
|
-
#
|
4
|
-
# Enter), the :active scene will be cued if no other scene has been prepared
|
5
|
-
# or cued.
|
3
|
+
# Pause for user input.
|
6
4
|
#
|
7
5
|
class Scene::Pause < Scene::Custom
|
8
|
-
def initialize prompt = nil, &block
|
9
|
-
@prompt = prompt
|
10
|
-
@start = block
|
11
|
-
end
|
12
6
|
def start actor
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
def finish actor, input
|
17
|
-
actor.cue :active if (actor.scene == @start_scene and actor.next_scene.nil?)
|
18
|
-
end
|
19
|
-
def prompt
|
20
|
-
@prompt ||= "Press Enter to continue..."
|
7
|
+
data = start_data_for(actor)
|
8
|
+
data.prompt = 'Press enter to continue...'
|
9
|
+
do_start_block actor, data
|
21
10
|
end
|
22
11
|
end
|
23
12
|
|
@@ -6,23 +6,18 @@ module Gamefic
|
|
6
6
|
# other scene has been prepared or cued.
|
7
7
|
#
|
8
8
|
class Scene::YesOrNo < Scene::Custom
|
9
|
-
def
|
10
|
-
|
11
|
-
@finish = block
|
9
|
+
def data_class
|
10
|
+
SceneData::YesOrNo
|
12
11
|
end
|
12
|
+
|
13
13
|
def finish actor, input
|
14
|
-
|
15
|
-
if
|
16
|
-
answer = "yes"
|
17
|
-
elsif input.downcase[0, 1] == "n"
|
18
|
-
answer = "no"
|
19
|
-
end
|
20
|
-
if answer.nil?
|
21
|
-
actor.tell "Please enter Yes or No."
|
22
|
-
else
|
14
|
+
data = finish_data_for(actor, input)
|
15
|
+
if data.yes? or data.no?
|
23
16
|
this_scene = actor.scene
|
24
|
-
|
17
|
+
do_finish_block actor, data
|
25
18
|
actor.cue :active if (actor.scene == this_scene and actor.next_scene.nil?)
|
19
|
+
else
|
20
|
+
actor.tell data.invalid_message
|
26
21
|
end
|
27
22
|
end
|
28
23
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Gamefic
|
2
|
+
|
3
|
+
class SceneData::MultipleChoice < SceneData::Base
|
4
|
+
attr_accessor :selection
|
5
|
+
attr_accessor :number
|
6
|
+
attr_accessor :index
|
7
|
+
attr_writer :invalid_message
|
8
|
+
def options
|
9
|
+
@options ||= []
|
10
|
+
end
|
11
|
+
def prompt
|
12
|
+
@prompt ||= 'Enter a choice:'
|
13
|
+
end
|
14
|
+
def invalid_message
|
15
|
+
@invalid_message ||= 'That is not a valid choice.'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Gamefic
|
2
|
+
|
3
|
+
class SceneData::MultipleScene < SceneData::MultipleChoice
|
4
|
+
attr_accessor :selection
|
5
|
+
attr_accessor :number
|
6
|
+
def options
|
7
|
+
scene_map.keys
|
8
|
+
end
|
9
|
+
|
10
|
+
def map choice, scene
|
11
|
+
scene_map[choice] = scene
|
12
|
+
end
|
13
|
+
|
14
|
+
def scene_for choice
|
15
|
+
scene_map[choice]
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def scene_map
|
21
|
+
@scene_map ||= {}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Gamefic
|
2
|
+
|
3
|
+
class SceneData::YesOrNo < SceneData::Base
|
4
|
+
def yes?
|
5
|
+
input.to_s[0,1].downcase == 'y'
|
6
|
+
end
|
7
|
+
def no?
|
8
|
+
input.to_s[0,1].downcase == 'n'
|
9
|
+
end
|
10
|
+
def prompt
|
11
|
+
@prompt ||= 'Yes or No?'
|
12
|
+
end
|
13
|
+
def invalid_message
|
14
|
+
@invalid_message ||= 'Please enter Yes or No.'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
data/lib/gamefic/shell.rb
CHANGED
@@ -1,78 +1,77 @@
|
|
1
|
-
require 'thor'
|
2
|
-
require 'gamefic/engine/tty'
|
3
|
-
require 'zip'
|
4
|
-
require 'tmpdir'
|
5
|
-
require 'yaml'
|
6
|
-
|
7
|
-
module Gamefic
|
8
|
-
class Shell < Thor
|
9
|
-
map %w[--version -v] => :version
|
10
|
-
|
11
|
-
desc "--version, -v", "Print the version"
|
12
|
-
def version
|
13
|
-
puts "gamefic #{Gamefic::VERSION}"
|
14
|
-
end
|
15
|
-
|
16
|
-
desc 'play FILE_NAME', 'Execute a compiled (.gfic) game'
|
17
|
-
option :verbose, type: :boolean, aliases: :v, desc: "Don't suppress Ruby exceptions"
|
18
|
-
def play(file)
|
19
|
-
Dir.mktmpdir 'gamefic_' do |dir|
|
20
|
-
puts 'Loading...'
|
21
|
-
decompress file, dir
|
22
|
-
run_game(dir)
|
23
|
-
end
|
24
|
-
rescue Zip::Error => e
|
25
|
-
puts "'#{file}' does not appear to be a valid Gamefic file."
|
26
|
-
show_exception(e) if options[:verbose]
|
27
|
-
rescue StandardError => e
|
28
|
-
puts "An error occurred: #{e.message}"
|
29
|
-
show_exception(e) if options[:verbose]
|
30
|
-
end
|
31
|
-
|
32
|
-
desc 'info FILE_NAME', 'Print information about a (.gfic) game'
|
33
|
-
option :verbose, type: :boolean, aliases: :v, desc: "Don't suppress Ruby exceptions"
|
34
|
-
def info(file)
|
35
|
-
Dir.mktmpdir 'gamefic_' do |dir|
|
36
|
-
decompress file, dir
|
37
|
-
metadata = YAML.load_file File.join(dir, 'metadata.yaml')
|
38
|
-
metadata.each { |k, v|
|
39
|
-
puts "#{k}: #{v}"
|
40
|
-
}
|
41
|
-
end
|
42
|
-
rescue StandardError, Zip::Error => e
|
43
|
-
puts "'#{file}' does not appear to be a valid Gamefic file."
|
44
|
-
show_exception(e) if options[:verbose]
|
45
|
-
end
|
46
|
-
|
47
|
-
# Custom error message for invalid command or filename
|
48
|
-
def method_missing(symbol, *args)
|
49
|
-
raise UndefinedCommandError, "Could not find command or file named \"#{symbol}\"."
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
def show_exception(exception)
|
55
|
-
puts exception.inspect
|
56
|
-
puts exception.backtrace.join("\n")
|
57
|
-
end
|
58
|
-
|
59
|
-
def decompress(zipfile, destination)
|
60
|
-
Zip::File.open(zipfile) do |z|
|
61
|
-
z.each do |entry|
|
62
|
-
FileUtils.mkdir_p File.join(destination, File.dirname(entry.name))
|
63
|
-
full_path = File.join(destination, entry.name)
|
64
|
-
entry.extract full_path unless File.exist?(full_path)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def run_game(directory)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
1
|
+
require 'thor'
|
2
|
+
require 'gamefic/engine/tty'
|
3
|
+
require 'zip'
|
4
|
+
require 'tmpdir'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
module Gamefic
|
8
|
+
class Shell < Thor
|
9
|
+
map %w[--version -v] => :version
|
10
|
+
|
11
|
+
desc "--version, -v", "Print the version"
|
12
|
+
def version
|
13
|
+
puts "gamefic #{Gamefic::VERSION}"
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'play FILE_NAME', 'Execute a compiled (.gfic) game'
|
17
|
+
option :verbose, type: :boolean, aliases: :v, desc: "Don't suppress Ruby exceptions"
|
18
|
+
def play(file)
|
19
|
+
Dir.mktmpdir 'gamefic_' do |dir|
|
20
|
+
puts 'Loading...'
|
21
|
+
decompress file, dir
|
22
|
+
run_game(dir)
|
23
|
+
end
|
24
|
+
rescue Zip::Error => e
|
25
|
+
puts "'#{file}' does not appear to be a valid Gamefic file."
|
26
|
+
show_exception(e) if options[:verbose]
|
27
|
+
rescue StandardError => e
|
28
|
+
puts "An error occurred: #{e.message}"
|
29
|
+
show_exception(e) if options[:verbose]
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'info FILE_NAME', 'Print information about a (.gfic) game'
|
33
|
+
option :verbose, type: :boolean, aliases: :v, desc: "Don't suppress Ruby exceptions"
|
34
|
+
def info(file)
|
35
|
+
Dir.mktmpdir 'gamefic_' do |dir|
|
36
|
+
decompress file, dir
|
37
|
+
metadata = YAML.load_file File.join(dir, 'metadata.yaml')
|
38
|
+
metadata.each { |k, v|
|
39
|
+
puts "#{k}: #{v}"
|
40
|
+
}
|
41
|
+
end
|
42
|
+
rescue StandardError, Zip::Error => e
|
43
|
+
puts "'#{file}' does not appear to be a valid Gamefic file."
|
44
|
+
show_exception(e) if options[:verbose]
|
45
|
+
end
|
46
|
+
|
47
|
+
# Custom error message for invalid command or filename
|
48
|
+
def method_missing(symbol, *args)
|
49
|
+
raise UndefinedCommandError, "Could not find command or file named \"#{symbol}\"."
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def show_exception(exception)
|
55
|
+
puts exception.inspect
|
56
|
+
puts exception.backtrace.join("\n")
|
57
|
+
end
|
58
|
+
|
59
|
+
def decompress(zipfile, destination)
|
60
|
+
Zip::File.open(zipfile) do |z|
|
61
|
+
z.each do |entry|
|
62
|
+
FileUtils.mkdir_p File.join(destination, File.dirname(entry.name))
|
63
|
+
full_path = File.join(destination, entry.name)
|
64
|
+
entry.extract full_path unless File.exist?(full_path)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def run_game(directory)
|
70
|
+
plot = Plot.new(Source::File.new(File.join(directory, 'scripts')))
|
71
|
+
plot.script 'main'
|
72
|
+
plot.metadata = YAML.load_file File.join(directory, 'metadata.yaml')
|
73
|
+
puts ""
|
74
|
+
Engine::Tty.start(plot)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|