gamefic 1.7.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +16 -0
  5. data/.solargraph.yml +5 -0
  6. data/CHANGELOG.md +10 -0
  7. data/Gemfile +7 -0
  8. data/LICENSE +20 -0
  9. data/README.md +28 -0
  10. data/Rakefile +10 -0
  11. data/gamefic.gemspec +27 -0
  12. data/lib/gamefic.rb +7 -7
  13. data/lib/gamefic/action.rb +66 -60
  14. data/lib/gamefic/active.rb +331 -280
  15. data/lib/gamefic/actor.rb +8 -5
  16. data/lib/gamefic/command.rb +9 -7
  17. data/lib/gamefic/core_ext/array.rb +27 -49
  18. data/lib/gamefic/core_ext/string.rb +25 -16
  19. data/lib/gamefic/describable.rb +21 -23
  20. data/lib/gamefic/element.rb +47 -31
  21. data/lib/gamefic/entity.rb +6 -12
  22. data/lib/gamefic/{matchable.rb → keywords.rb} +52 -50
  23. data/lib/gamefic/messaging.rb +43 -44
  24. data/lib/gamefic/node.rb +14 -5
  25. data/lib/gamefic/plot.rb +69 -91
  26. data/lib/gamefic/plot/darkroom.rb +80 -264
  27. data/lib/gamefic/plot/host.rb +42 -48
  28. data/lib/gamefic/plot/snapshot.rb +14 -19
  29. data/lib/gamefic/query.rb +15 -18
  30. data/lib/gamefic/query/base.rb +50 -37
  31. data/lib/gamefic/query/children.rb +0 -0
  32. data/lib/gamefic/query/descendants.rb +2 -2
  33. data/lib/gamefic/query/external.rb +18 -14
  34. data/lib/gamefic/query/family.rb +3 -7
  35. data/lib/gamefic/query/matches.rb +75 -67
  36. data/lib/gamefic/query/parent.rb +0 -0
  37. data/lib/gamefic/query/siblings.rb +0 -0
  38. data/lib/gamefic/query/text.rb +12 -12
  39. data/lib/gamefic/query/tree.rb +17 -0
  40. data/lib/gamefic/scene.rb +0 -2
  41. data/lib/gamefic/scene/activity.rb +24 -26
  42. data/lib/gamefic/scene/base.rb +71 -10
  43. data/lib/gamefic/scene/conclusion.rb +1 -3
  44. data/lib/gamefic/scene/multiple_choice.rb +19 -14
  45. data/lib/gamefic/scene/multiple_scene.rb +29 -20
  46. data/lib/gamefic/scene/pause.rb +8 -3
  47. data/lib/gamefic/scene/yes_or_no.rb +22 -10
  48. data/lib/gamefic/scriptable.rb +88 -0
  49. data/lib/gamefic/serialize.rb +223 -0
  50. data/lib/gamefic/subplot.rb +38 -35
  51. data/lib/gamefic/syntax.rb +15 -13
  52. data/lib/gamefic/version.rb +3 -3
  53. data/lib/gamefic/world.rb +18 -0
  54. data/lib/gamefic/world/callbacks.rb +135 -0
  55. data/lib/gamefic/world/commands.rb +184 -0
  56. data/lib/gamefic/{plot → world}/entities.rb +33 -35
  57. data/lib/gamefic/{plot → world}/playbook.rb +245 -240
  58. data/lib/gamefic/world/players.rb +37 -0
  59. data/lib/gamefic/world/scenes.rb +226 -0
  60. metadata +37 -88
  61. data/bin/gamefic +0 -9
  62. data/lib/gamefic/engine.rb +0 -7
  63. data/lib/gamefic/engine/base.rb +0 -59
  64. data/lib/gamefic/engine/tty.rb +0 -24
  65. data/lib/gamefic/grammar.rb +0 -13
  66. data/lib/gamefic/grammar/conjugator.rb +0 -20
  67. data/lib/gamefic/grammar/gender.rb +0 -11
  68. data/lib/gamefic/grammar/person.rb +0 -10
  69. data/lib/gamefic/grammar/plural.rb +0 -13
  70. data/lib/gamefic/grammar/pronouns.rb +0 -106
  71. data/lib/gamefic/grammar/tense.rb +0 -6
  72. data/lib/gamefic/grammar/verb_set.rb +0 -43
  73. data/lib/gamefic/grammar/verbs.rb +0 -26
  74. data/lib/gamefic/grammar/word_adapter.rb +0 -49
  75. data/lib/gamefic/plot/articles.rb +0 -22
  76. data/lib/gamefic/plot/callbacks.rb +0 -126
  77. data/lib/gamefic/plot/commands.rb +0 -120
  78. data/lib/gamefic/plot/players.rb +0 -15
  79. data/lib/gamefic/plot/scenes.rb +0 -187
  80. data/lib/gamefic/plot/theater.rb +0 -73
  81. data/lib/gamefic/plot/you_mount.rb +0 -22
  82. data/lib/gamefic/scene/custom.rb +0 -9
  83. data/lib/gamefic/script.rb +0 -13
  84. data/lib/gamefic/script/base.rb +0 -42
  85. data/lib/gamefic/script/file.rb +0 -14
  86. data/lib/gamefic/script/text.rb +0 -14
  87. data/lib/gamefic/shell.rb +0 -76
  88. data/lib/gamefic/source.rb +0 -14
  89. data/lib/gamefic/source/base.rb +0 -12
  90. data/lib/gamefic/source/file.rb +0 -23
  91. data/lib/gamefic/source/text.rb +0 -16
  92. data/lib/gamefic/tester.rb +0 -19
  93. data/lib/gamefic/text.rb +0 -8
  94. data/lib/gamefic/text/ansi.rb +0 -53
  95. data/lib/gamefic/text/html.rb +0 -68
  96. data/lib/gamefic/text/html/conversions.rb +0 -250
  97. data/lib/gamefic/text/html/entities.rb +0 -9
  98. data/lib/gamefic/tty.rb +0 -10
  99. data/lib/gamefic/user.rb +0 -7
  100. data/lib/gamefic/user/base.rb +0 -29
  101. data/lib/gamefic/user/tty.rb +0 -38
@@ -1,73 +0,0 @@
1
- module Gamefic
2
-
3
- module Plot::Theater
4
- # Execute a block of code in a subset of the object's scope. An object's
5
- # stage is an isolated namespace that has its own instance variables and
6
- # access to its owner's public methods.
7
- #
8
- # There are two ways to execute code on the stage. It will accept either a
9
- # string of code with an optional file name and line number, or a proc
10
- # with optional arguments. See module_eval and module_exec for more
11
- # information.
12
- #
13
- # @example Evaluate a string of code
14
- # stage "puts 'Hello'"
15
- #
16
- # @example Evaluate a string of code with a file name and line number
17
- # stage "puts 'Hello'", "file.rb", 1
18
- #
19
- # @example Execute a block of code
20
- # stage {
21
- # puts 'Hello'
22
- # }
23
- #
24
- # @example Execute a block of code with arguments
25
- # stage 'hello' { |message|
26
- # puts message # <- prints 'hello'
27
- # }
28
- #
29
- # @example Use an instance variable
30
- # stage "@message = 'hello'"
31
- # stage "puts @message" # <- prints 'hello'
32
- #
33
- # @return [Object] The value returned by the executed code
34
- def stage *args, &block
35
- if block.nil?
36
- theater.module_eval *args
37
- else
38
- theater.module_exec *args, &block
39
- end
40
- end
41
-
42
- # The module that acts as an isolated namespace for staged code.
43
- #
44
- # @return [Module]
45
- def theater
46
- return @theater unless @theater.nil?
47
- instance = self
48
-
49
- @theater = Module.new do
50
- define_singleton_method :method_missing do |symbol, *args, &block|
51
- instance.public_send :public_send, symbol, *args, &block
52
- end
53
-
54
- define_singleton_method :stage do |*args|
55
- raise NoMethodError.new("The stage method is not available from inside staged scripts")
56
- end
57
-
58
- define_singleton_method :to_s do
59
- "[Theater]"
60
- end
61
- end
62
-
63
- # HACK: Include the theater module in Object so that classes and modules
64
- # defined in scripts are accessible from procs passed to the stage.
65
- Object.class_exec(@theater) do |t|
66
- include t
67
- end
68
-
69
- @theater
70
- end
71
- end
72
-
73
- end
@@ -1,22 +0,0 @@
1
- require 'gamefic'
2
- require 'gamefic/grammar'
3
-
4
- module Gamefic
5
- module Plot::YouMount
6
- class YouGrammarSet
7
- include Grammar::Gender
8
- include Grammar::Person
9
- include Grammar::Plural
10
- include Grammar::WordAdapter
11
- end
12
- # @return [YouGrammarSet]
13
- def you
14
- if @you.nil?
15
- @you = YouGrammarSet.new
16
- @you.person = 2
17
- end
18
- @you
19
- end
20
- end
21
-
22
- end
@@ -1,9 +0,0 @@
1
- module Gamefic
2
-
3
- # A Custom Scene allows for complete configuration of its behavior upon
4
- # instantiation. It is suitable for direct instantiation or subclassing.
5
- #
6
- class Scene::Custom < Scene::Base
7
- end
8
-
9
- end
@@ -1,13 +0,0 @@
1
- module Gamefic
2
-
3
- # Script classes provide code to be executed in Plots. They are accessed
4
- # through Source classes, e.g., a Source::Text object is used to find
5
- # Source::Files.
6
- #
7
- module Script
8
- autoload :Base, 'gamefic/script/base'
9
- autoload :File, 'gamefic/script/file'
10
- autoload :Text, 'gamefic/script/text'
11
- end
12
-
13
- end
@@ -1,42 +0,0 @@
1
- module Gamefic
2
-
3
- class Script::Base
4
- def initialize
5
- raise "#initialize must be defined in subclasses"
6
- end
7
-
8
- # Get the script's text.
9
- # The text must be source code suitable for evaluation via Plot#stage.
10
- #
11
- # @return [String]
12
- def read
13
- raise "#read must be defined in subclasses"
14
- end
15
-
16
- # Get the script's path.
17
- #
18
- # @return [String]
19
- def path
20
- raise "#path must be defined in subclasses"
21
- end
22
-
23
- # Get the absolute path of the script's original file, or its URL for
24
- # sources that are not file-based.
25
- #
26
- # @return [String]
27
- def absolute_path
28
- raise "#absolute_path must be defined in subclasses"
29
- end
30
-
31
- # Script objects are equal if their relative paths are the same. Note that
32
- # they are still considered equal if their absolute paths are different,
33
- # or even if they come from different types of sources.
34
- #
35
- # @param other[Script::Base]
36
- # @return [Boolean]
37
- def==(other)
38
- path == other.path
39
- end
40
- end
41
-
42
- end
@@ -1,14 +0,0 @@
1
- module Gamefic
2
-
3
- class Script::File < Script::Base
4
- attr_reader :path, :absolute_path
5
- def initialize filename, path
6
- @absolute_path = filename.gsub(/\/+/, '/')
7
- @path = path
8
- end
9
- def read
10
- File.read(@absolute_path)
11
- end
12
- end
13
-
14
- end
@@ -1,14 +0,0 @@
1
- module Gamefic
2
-
3
- class Script::Text < Script::Base
4
- attr_reader :path, :absolute_path
5
- def initialize path, code, absolute_path = nil
6
- @path = path
7
- @code = code
8
- @absolute_path = absolute_path || path
9
- end
10
- def read
11
- @code
12
- end
13
- end
14
- end
data/lib/gamefic/shell.rb DELETED
@@ -1,76 +0,0 @@
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
- Engine::Tty.start(plot)
74
- end
75
- end
76
- end
@@ -1,14 +0,0 @@
1
- module Gamefic
2
-
3
- # Plots use Source classes to fetch scripts to be executed. The most common
4
- # type of Source class is Source::File, which searches for scripts in
5
- # a predefined list of directories on the filesystem, similar to the way
6
- # that Kernel#require works.
7
- #
8
- module Source
9
- autoload :Base, 'gamefic/source/base'
10
- autoload :File, 'gamefic/source/file'
11
- autoload :Text, 'gamefic/source/text'
12
- end
13
-
14
- end
@@ -1,12 +0,0 @@
1
- module Gamefic
2
-
3
- class Source::Base
4
- def initialize
5
- raise "#initialize must be defined in subclasses"
6
- end
7
- def export path
8
- raise "#export must be defined in subclasses"
9
- end
10
- end
11
-
12
- end
@@ -1,23 +0,0 @@
1
- module Gamefic
2
-
3
- class Source::File < Source::Base
4
- @@extensions = ['.plot.rb', '.plot', '.rb']
5
- attr_reader :directories
6
- attr_accessor :main_dir
7
- def initialize(*directories)
8
- @directories = directories || []
9
- end
10
- def export path
11
- @directories.each { |directory|
12
- @@extensions.each { |ext|
13
- abs_file = File.join(directory, path + ext)
14
- if File.file?(abs_file)
15
- return Script::File.new(abs_file, path)
16
- end
17
- }
18
- }
19
- raise LoadError.new("cannot load script -- #{path}")
20
- end
21
- end
22
-
23
- end
@@ -1,16 +0,0 @@
1
- module Gamefic
2
-
3
- class Source::Text < Source::Base
4
- def initialize scripts = {}
5
- @scripts = scripts
6
- end
7
- def export path
8
- if @scripts.has_key?(path)
9
- Script::Text.new(path, @scripts[path])
10
- else
11
- raise "Script #{path} not found"
12
- end
13
- end
14
- end
15
-
16
- end
@@ -1,19 +0,0 @@
1
- module Gamefic
2
-
3
- module Tester
4
- def test_procs
5
- @test_procs ||= Hash.new
6
- end
7
- def on_test name = :me, &block
8
- test_procs[name] = block
9
- end
10
- def run_test name, actor
11
- queue = []
12
- stage actor, queue, &test_procs[name]
13
- actor.queue.push *queue
14
- actor[:test_queue_length] = queue.length
15
- actor[:test_queue_scene] = actor.scene
16
- end
17
- end
18
-
19
- end
data/lib/gamefic/text.rb DELETED
@@ -1,8 +0,0 @@
1
- module Gamefic
2
-
3
- module Text
4
- autoload :Ansi, 'gamefic/text/ansi'
5
- autoload :Html, 'gamefic/text/html'
6
- end
7
-
8
- end
@@ -1,53 +0,0 @@
1
- module Gamefic
2
-
3
- # Constants for ANSI codes, plus ExtraCodes for custom formatting.
4
- module Text::Ansi
5
- module Code
6
- module Attribute
7
- NORMAL = 0
8
- BOLD = 1
9
- UNDERSCORE = 4
10
- BLINK = 5
11
- REVERSE = 7
12
- CONCEALED = 8
13
- end
14
- module Foreground
15
- BLACK = 30
16
- RED = 31
17
- GREEN = 32
18
- YELLOW = 33
19
- BLUE = 34
20
- MAGENTA = 35
21
- CYAN = 36
22
- WHITE = 37
23
- end
24
- module Background
25
- BLACK = 40
26
- RED = 41
27
- GREEN = 42
28
- YELLOW = 43
29
- BLUE = 44
30
- MAGENTA = 45
31
- CYAN = 46
32
- WHITE = 47
33
- end
34
- module Extra
35
- BLOCK = :block
36
- PRE = :pre
37
- HREF = :href
38
- IMAGE = :image
39
- SRC = :src
40
- UPPERCASE = :uppercase
41
- COMMAND = :command
42
- IGNORED = :ignored
43
- LINE = :line
44
- end
45
- end
46
- def self.graphics_mode(*settings)
47
- ansi = settings.flatten.that_are(Fixnum)
48
- return '' if ansi.length == 0
49
- "\e[#{ansi.join(';')}m"
50
- end
51
- end
52
-
53
- end
@@ -1,68 +0,0 @@
1
- require 'rexml/document'
2
- require 'gamefic/text/html/entities'
3
-
4
- module Gamefic
5
- module Text
6
- module Html
7
- autoload :Conversions, 'gamefic/text/html/conversions'
8
-
9
- # Convert ampersands to &amp;
10
- #
11
- # @param text [String]
12
- # @return [String]
13
- def self.fix_ampersands(text)
14
- codes = []
15
- ENTITIES.keys.each { |e|
16
- codes.push e[1..-1]
17
- }
18
- piped = codes.join('|')
19
- re = Regexp.new("&(?!(#{piped}))")
20
- text.gsub(re, '&amp;\1')
21
- end
22
-
23
- # Encode a String with HTML entities
24
- #
25
- # @param text [String]
26
- # @return [String]
27
- def self.encode(text)
28
- encoded = text
29
- ENTITIES.each { |k, v|
30
- encoded = encoded.gsub(v, k)
31
- }
32
- encoded
33
- end
34
-
35
- # Decode a String's HTML entities
36
- #
37
- # @param text [String]
38
- # @return [String]
39
- def self.decode(text)
40
- ENTITIES.each { |k, v|
41
- text = text.gsub(k, v)
42
- }
43
- text
44
- end
45
-
46
- # Parse a String into an XML document
47
- #
48
- # @param code [String]
49
- # @return [REXML::Document]
50
- def self.parse(code)
51
- code = fix_ampersands(code).strip
52
- last = nil
53
- begin
54
- doc = REXML::Document.new code
55
- rescue REXML::ParseException => e
56
- # Convert invalid < characters to &lt;
57
- if e.source.buffer != last and e.source.buffer[0,1] == '<'
58
- code = code[0,(code.length - e.source.buffer.length)] + '&lt;' + e.source.buffer[1..-1]
59
- last = e.source.buffer
60
- retry
61
- end
62
- raise e
63
- end
64
- end
65
- end
66
- end
67
-
68
- end