gamefic 1.7.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +13 -0
  5. data/.solargraph.yml +5 -0
  6. data/Gemfile +7 -0
  7. data/LICENSE +20 -0
  8. data/README.md +28 -0
  9. data/Rakefile +10 -0
  10. data/gamefic.gemspec +27 -0
  11. data/lib/gamefic.rb +7 -6
  12. data/lib/gamefic/action.rb +38 -28
  13. data/lib/gamefic/active.rb +325 -280
  14. data/lib/gamefic/actor.rb +8 -5
  15. data/lib/gamefic/command.rb +9 -7
  16. data/lib/gamefic/core_ext/array.rb +24 -49
  17. data/lib/gamefic/core_ext/string.rb +25 -16
  18. data/lib/gamefic/describable.rb +21 -23
  19. data/lib/gamefic/element.rb +43 -31
  20. data/lib/gamefic/entity.rb +6 -12
  21. data/lib/gamefic/index.rb +121 -0
  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 -89
  26. data/lib/gamefic/plot/darkroom.rb +92 -264
  27. data/lib/gamefic/plot/host.rb +42 -48
  28. data/lib/gamefic/plot/snapshot.rb +5 -18
  29. data/lib/gamefic/query.rb +14 -18
  30. data/lib/gamefic/query/base.rb +30 -18
  31. data/lib/gamefic/query/children.rb +0 -0
  32. data/lib/gamefic/query/external.rb +18 -14
  33. data/lib/gamefic/query/family.rb +1 -7
  34. data/lib/gamefic/query/matches.rb +75 -67
  35. data/lib/gamefic/query/parent.rb +0 -0
  36. data/lib/gamefic/query/siblings.rb +0 -0
  37. data/lib/gamefic/query/text.rb +2 -1
  38. data/lib/gamefic/scene.rb +0 -2
  39. data/lib/gamefic/scene/activity.rb +24 -26
  40. data/lib/gamefic/scene/base.rb +64 -8
  41. data/lib/gamefic/scene/conclusion.rb +0 -2
  42. data/lib/gamefic/scene/custom.rb +0 -2
  43. data/lib/gamefic/scene/multiple_choice.rb +18 -3
  44. data/lib/gamefic/scene/multiple_scene.rb +29 -20
  45. data/lib/gamefic/scene/pause.rb +7 -2
  46. data/lib/gamefic/scene/yes_or_no.rb +21 -9
  47. data/lib/gamefic/scriptable.rb +87 -0
  48. data/lib/gamefic/serialize.rb +68 -0
  49. data/lib/gamefic/subplot.rb +29 -35
  50. data/lib/gamefic/syntax.rb +14 -13
  51. data/lib/gamefic/version.rb +3 -3
  52. data/lib/gamefic/world.rb +16 -0
  53. data/lib/gamefic/world/callbacks.rb +135 -0
  54. data/lib/gamefic/world/commands.rb +184 -0
  55. data/lib/gamefic/{plot → world}/entities.rb +30 -29
  56. data/lib/gamefic/{plot → world}/playbook.rb +255 -240
  57. data/lib/gamefic/world/players.rb +21 -0
  58. data/lib/gamefic/world/scenes.rb +226 -0
  59. metadata +41 -92
  60. data/bin/gamefic +0 -9
  61. data/lib/gamefic/engine.rb +0 -7
  62. data/lib/gamefic/engine/base.rb +0 -59
  63. data/lib/gamefic/engine/tty.rb +0 -24
  64. data/lib/gamefic/grammar.rb +0 -13
  65. data/lib/gamefic/grammar/conjugator.rb +0 -20
  66. data/lib/gamefic/grammar/gender.rb +0 -11
  67. data/lib/gamefic/grammar/person.rb +0 -10
  68. data/lib/gamefic/grammar/plural.rb +0 -13
  69. data/lib/gamefic/grammar/pronouns.rb +0 -106
  70. data/lib/gamefic/grammar/tense.rb +0 -6
  71. data/lib/gamefic/grammar/verb_set.rb +0 -43
  72. data/lib/gamefic/grammar/verbs.rb +0 -26
  73. data/lib/gamefic/grammar/word_adapter.rb +0 -49
  74. data/lib/gamefic/plot/articles.rb +0 -22
  75. data/lib/gamefic/plot/callbacks.rb +0 -126
  76. data/lib/gamefic/plot/commands.rb +0 -120
  77. data/lib/gamefic/plot/players.rb +0 -15
  78. data/lib/gamefic/plot/scenes.rb +0 -187
  79. data/lib/gamefic/plot/theater.rb +0 -73
  80. data/lib/gamefic/plot/you_mount.rb +0 -22
  81. data/lib/gamefic/script.rb +0 -13
  82. data/lib/gamefic/script/base.rb +0 -42
  83. data/lib/gamefic/script/file.rb +0 -14
  84. data/lib/gamefic/script/text.rb +0 -14
  85. data/lib/gamefic/shell.rb +0 -76
  86. data/lib/gamefic/source.rb +0 -14
  87. data/lib/gamefic/source/base.rb +0 -12
  88. data/lib/gamefic/source/file.rb +0 -23
  89. data/lib/gamefic/source/text.rb +0 -16
  90. data/lib/gamefic/tester.rb +0 -19
  91. data/lib/gamefic/text.rb +0 -8
  92. data/lib/gamefic/text/ansi.rb +0 -53
  93. data/lib/gamefic/text/html.rb +0 -68
  94. data/lib/gamefic/text/html/conversions.rb +0 -250
  95. data/lib/gamefic/text/html/entities.rb +0 -9
  96. data/lib/gamefic/tty.rb +0 -10
  97. data/lib/gamefic/user.rb +0 -7
  98. data/lib/gamefic/user/base.rb +0 -29
  99. data/lib/gamefic/user/tty.rb +0 -38
@@ -1,18 +1,14 @@
1
- #require 'gamefic/keywords'
2
-
3
- module Gamefic
4
-
5
- module Query
6
- autoload :Base, 'gamefic/query/base'
7
- autoload :Children, 'gamefic/query/children'
8
- autoload :Descendants, 'gamefic/query/descendants'
9
- autoload :External, 'gamefic/query/external'
10
- autoload :Family, 'gamefic/query/family'
11
- autoload :Itself, 'gamefic/query/itself'
12
- autoload :Matches, 'gamefic/query/matches'
13
- autoload :Parent, 'gamefic/query/parent'
14
- autoload :Siblings, 'gamefic/query/siblings'
15
- autoload :Text, 'gamefic/query/text'
16
- end
17
-
18
- end
1
+ module Gamefic
2
+ module Query
3
+ autoload :Base, 'gamefic/query/base'
4
+ autoload :Children, 'gamefic/query/children'
5
+ autoload :Descendants, 'gamefic/query/descendants'
6
+ autoload :External, 'gamefic/query/external'
7
+ autoload :Family, 'gamefic/query/family'
8
+ autoload :Itself, 'gamefic/query/itself'
9
+ autoload :Matches, 'gamefic/query/matches'
10
+ autoload :Parent, 'gamefic/query/parent'
11
+ autoload :Siblings, 'gamefic/query/siblings'
12
+ autoload :Text, 'gamefic/query/text'
13
+ end
14
+ end
@@ -9,6 +9,13 @@ module Gamefic
9
9
  @arguments = args
10
10
  end
11
11
 
12
+ # Determine whether the query allows ambiguous entity references.
13
+ # If false, actions that use this query will only be valid if the token
14
+ # passed into it resolves to a single entity. If true, actions will
15
+ # accept an array of matching entities instead.
16
+ # Queries are not ambiguous by default (ambiguous? == false).
17
+ #
18
+ # @return [Boolean]
12
19
  def ambiguous?
13
20
  false
14
21
  end
@@ -36,8 +43,8 @@ module Gamefic
36
43
  return Matches.new(drill, token, '') unless drill.length != 1
37
44
  return Matches.new([], '', token)
38
45
  end
39
- result = available.select{ |e| e.match?(token) }
40
- result = available.select{ |e| e.match?(token, fuzzy: true) } if result.empty?
46
+ result = available.select{ |e| e.specified?(token) }
47
+ result = available.select{ |e| e.specified?(token, fuzzy: true) } if result.empty?
41
48
  result.keep_if{ |e| accept? e }
42
49
  Matches.new(result, (result.empty? ? '' : token), (result.empty? ? token : ''))
43
50
  end
@@ -52,15 +59,6 @@ module Gamefic
52
59
  if @precision.nil?
53
60
  @precision = 1
54
61
  arguments.each { |a|
55
- #if a.kind_of?(Symbol) or a.kind_of?(Regexp)
56
- # @precision += 1
57
- #elsif a.kind_of?(Class)
58
- # @precision += (count_superclasses(a) * 100)
59
- #elsif a.kind_of?(Module)
60
- # @precision += 10
61
- #elsif a.kind_of?(Object)
62
- # @precision += 1000
63
- #end
64
62
  if a.kind_of?(Class)
65
63
  @precision += 100
66
64
  elsif a.kind_of?(Gamefic::Entity)
@@ -77,9 +75,12 @@ module Gamefic
77
75
  end
78
76
 
79
77
  def signature
80
- "#{self.class.to_s.downcase}(#{@arguments.join(',')})"
78
+ "#{self.class.to_s.split('::').last.downcase}(#{simplify_arguments.join(', ')})"
81
79
  end
82
80
 
81
+ # Determine whether the specified entity passes the query's arguments.
82
+ #
83
+ # @return [Boolean]
83
84
  def accept?(entity)
84
85
  result = true
85
86
  arguments.each { |a|
@@ -98,13 +99,14 @@ module Gamefic
98
99
  end
99
100
 
100
101
  protected
101
-
102
- # Return an array of the entity's children. If the child is neighborly,
102
+
103
+ # Return an array of the entity's children. If the child is accessible,
103
104
  # recursively append its children.
104
105
  # The result will NOT include the original entity itself.
105
106
  #
106
107
  # @return [Array<Object>]
107
108
  def subquery_accessible entity
109
+ return [] if entity.nil?
108
110
  result = []
109
111
  if entity.accessible?
110
112
  entity.children.each { |c|
@@ -117,6 +119,16 @@ module Gamefic
117
119
 
118
120
  private
119
121
 
122
+ def simplify_arguments
123
+ arguments.map do |a|
124
+ if a.kind_of?(Class) or a.kind_of?(Object)
125
+ a.to_s.split('::').last.downcase
126
+ else
127
+ a.to_s.downcase
128
+ end
129
+ end
130
+ end
131
+
120
132
  def nested?(token)
121
133
  !token.match(NEST_REGEXP).nil?
122
134
  end
@@ -124,13 +136,13 @@ module Gamefic
124
136
  def denest(objects, token)
125
137
  parts = token.split(NEST_REGEXP)
126
138
  current = parts.pop
127
- last_result = objects.select{ |e| e.match?(current) }
128
- last_result = objects.select{ |e| e.match?(current, fuzzy: true) } if last_result.empty?
139
+ last_result = objects.select{ |e| e.specified?(current) }
140
+ last_result = objects.select{ |e| e.specified?(current, fuzzy: true) } if last_result.empty?
129
141
  result = last_result
130
142
  while parts.length > 0
131
143
  current = "#{parts.last} #{current}"
132
- result = last_result.select{ |e| e.match?(current) }
133
- result = last_result.select{ |e| e.match?(current, fuzzy: true) } if result.empty?
144
+ result = last_result.select{ |e| e.specified?(current) }
145
+ result = last_result.select{ |e| e.specified?(current, fuzzy: true) } if result.empty?
134
146
  break if result.empty?
135
147
  parts.pop
136
148
  last_result = result
File without changes
@@ -1,14 +1,18 @@
1
- module Gamefic
2
- module Query
3
- class External < Base
4
- def initialize objects, *args
5
- super(*args)
6
- @objects = objects
7
- end
8
-
9
- def context_from subject
10
- @objects
11
- end
12
- end
13
- end
14
- end
1
+ module Gamefic
2
+ module Query
3
+ class External < Base
4
+ def initialize objects, *args
5
+ super(*args)
6
+ @objects = objects
7
+ end
8
+
9
+ def context_from subject
10
+ @objects
11
+ end
12
+
13
+ def accept?(entity)
14
+ @objects.include?(entity) && super(entity)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -3,13 +3,7 @@ module Gamefic
3
3
  class Family < Base
4
4
  def context_from(subject)
5
5
  result = []
6
- top = subject.parent
7
- unless top.nil?
8
- #until top.parent.nil?
9
- # top = top.parent
10
- #end
11
- result.concat subquery_accessible(top)
12
- end
6
+ result.concat subquery_accessible(subject.parent)
13
7
  result.delete subject
14
8
  subject.children.each { |c|
15
9
  result.push c
@@ -1,67 +1,75 @@
1
- module Gamefic
2
- module Query
3
- class Matches
4
- attr_accessor :objects, :matching, :remaining
5
-
6
- def initialize objects, matching, remaining
7
- @objects = objects
8
- @matching = matching
9
- @remaining = remaining
10
- end
11
-
12
- def self.execute objects, description, continued: false
13
- if continued
14
- match_with_remainder objects, description
15
- else
16
- match_without_remainder objects, description
17
- end
18
- end
19
-
20
- class << self
21
- private
22
-
23
- def match_without_remainder objects, description
24
- matches = objects.select{ |e| e.match?(description) }
25
- if matches.empty?
26
- matching = ''
27
- remaining = description
28
- else
29
- matching = description
30
- remaining = ''
31
- end
32
- Matches.new(matches, matching, remaining)
33
- end
34
-
35
- def match_with_remainder objects, description
36
- matching_objects = objects
37
- matching_text = []
38
- words = description.split(Matchable::SPLIT_REGEXP)
39
- i = 0
40
- #cursor = []
41
- words.each { |w|
42
- cursor = inner_match matching_objects, words, matching_text, i, w
43
- break if cursor.empty? or (cursor & matching_objects).empty?
44
- matching_objects = (cursor & matching_objects)
45
- i += 1
46
- }
47
- objects = matching_objects
48
- matching = matching_text.uniq.join(' ')
49
- remaining = words[i..-1].join(' ')
50
- m = Matches.new(objects, matching, remaining)
51
- m
52
- end
53
-
54
- def inner_match matching_objects, words, matching_text, i, w
55
- cursor = []
56
- matching_objects.each { |o|
57
- if o.match?(words[0..i].join(' '), fuzzy: true)
58
- cursor.push o
59
- matching_text.push w
60
- end
61
- }
62
- cursor
63
- end
64
- end
65
- end
66
- end
67
- end
1
+ module Gamefic
2
+ module Query
3
+ class Matches
4
+ # The resolved tokens
5
+ # @return [Array<Object>]
6
+ attr_reader :objects
7
+
8
+ # The matching string
9
+ # @return [String]
10
+ attr_reader :matching
11
+
12
+ # The remaining (unmatched) string
13
+ # @return [String]
14
+ attr_reader :remaining
15
+
16
+ def initialize objects, matching, remaining
17
+ @objects = objects
18
+ @matching = matching
19
+ @remaining = remaining
20
+ end
21
+
22
+ def self.execute objects, description, continued: false
23
+ if continued
24
+ match_with_remainder objects, description
25
+ else
26
+ match_without_remainder objects, description
27
+ end
28
+ end
29
+
30
+ class << self
31
+ private
32
+
33
+ def match_without_remainder objects, description
34
+ matches = objects.select{ |e| e.specified?(description) }
35
+ if matches.empty?
36
+ matching = ''
37
+ remaining = description
38
+ else
39
+ matching = description
40
+ remaining = ''
41
+ end
42
+ Matches.new(matches, matching, remaining)
43
+ end
44
+
45
+ def match_with_remainder objects, description
46
+ matching_objects = objects
47
+ matching_text = []
48
+ words = description.split(Keywords::SPLIT_REGEXP)
49
+ i = 0
50
+ words.each { |w|
51
+ cursor = inner_match matching_objects, words, matching_text, i, w
52
+ break if cursor.empty? or (cursor & matching_objects).empty?
53
+ matching_objects = (cursor & matching_objects)
54
+ i += 1
55
+ }
56
+ objects = matching_objects
57
+ matching = matching_text.uniq.join(' ')
58
+ remaining = words[i..-1].join(' ')
59
+ Matches.new(objects, matching, remaining)
60
+ end
61
+
62
+ def inner_match matching_objects, words, matching_text, i, w
63
+ cursor = []
64
+ matching_objects.each { |o|
65
+ if o.specified?(words[0..i].join(' '), fuzzy: true)
66
+ cursor.push o
67
+ matching_text.push w
68
+ end
69
+ }
70
+ cursor
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
File without changes
File without changes
@@ -10,7 +10,8 @@ module Gamefic
10
10
  super
11
11
  end
12
12
  def resolve(subject, token, continued: false)
13
- parts = token.split(Matchable::SPLIT_REGEXP)
13
+ return Matches.new([], '', token) unless accept?(token)
14
+ parts = token.split(Keywords::SPLIT_REGEXP)
14
15
  cursor = []
15
16
  matches = []
16
17
  i = 0
@@ -1,5 +1,4 @@
1
1
  module Gamefic
2
-
3
2
  module Scene
4
3
  autoload :Base, 'gamefic/scene/base'
5
4
  autoload :Custom, 'gamefic/scene/custom'
@@ -10,5 +9,4 @@ module Gamefic
10
9
  autoload :MultipleScene, 'gamefic/scene/multiple_scene'
11
10
  autoload :YesOrNo, 'gamefic/scene/yes_or_no'
12
11
  end
13
-
14
12
  end
@@ -1,26 +1,24 @@
1
- module Gamefic
2
-
3
- # Active Scenes handle the default command prompt, where input is parsed
4
- # into an Action performed by the Character. This is the default scene in
5
- # a Plot.
6
- #
7
- class Scene::Activity < Scene::Base
8
- def post_initialize
9
- self.type = 'Activity'
10
- end
11
-
12
- def finish
13
- super
14
- o = nil
15
- o = actor.perform input.strip unless input.to_s.strip.empty?
16
- actor.performed o
17
- end
18
-
19
- class << self
20
- def type
21
- 'Activity'
22
- end
23
- end
24
- end
25
-
26
- end
1
+ module Gamefic
2
+ # Active Scenes handle the default command prompt, where input is parsed
3
+ # into an Action performed by the Character. This is the default scene in
4
+ # a Plot.
5
+ #
6
+ class Scene::Activity < Scene::Base
7
+ def post_initialize
8
+ self.type = 'Activity'
9
+ end
10
+
11
+ def finish
12
+ super
13
+ o = nil
14
+ o = actor.perform input.strip unless input.to_s.strip.empty?
15
+ actor.performed o
16
+ end
17
+
18
+ class << self
19
+ def type
20
+ 'Activity'
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,61 +1,100 @@
1
1
  module Gamefic
2
-
3
2
  # The Base Scene is not intended for instantiation. Other Scene classes
4
3
  # should inherit from it.
5
4
  #
6
5
  class Scene::Base
6
+ # The scene's primary actor.
7
+ #
8
+ # @return [Gamefic::Actor]
7
9
  attr_reader :actor
10
+
11
+ # A human-readable string identifying the type of scene.
12
+ #
13
+ # @return [String]
8
14
  attr_writer :type
15
+
16
+ # The text to display when requesting input.
17
+ #
18
+ # @return [String]
9
19
  attr_writer :prompt
20
+
21
+ # The input received from the actor.
22
+ #
23
+ # @return [String]
10
24
  attr_reader :input
11
25
 
12
- def initialize actor
26
+ # @return [Hash{Symbol => Object}]
27
+ attr_reader :data
28
+
29
+ def initialize actor, **data
13
30
  @actor = actor
31
+ @data = data
14
32
  post_initialize
15
33
  end
16
34
 
35
+ # A shortcut for the #data hash.
36
+ #
37
+ # @param key [Symbol]
38
+ # @return [Object]
39
+ def [] key
40
+ data[key]
41
+ end
42
+
17
43
  def post_initialize
18
44
  end
19
45
 
46
+ # Set a proc to be executed at the end of the scene.
47
+ #
20
48
  def on_finish &block
21
49
  @finish_block = block
22
50
  end
23
51
 
52
+ # Update the scene.
53
+ #
24
54
  def update
25
55
  @input = actor.queue.shift
26
56
  finish
27
57
  end
28
58
 
59
+ # Start the scene.
60
+ #
29
61
  def start
30
62
  self.class.start_block.call @actor, self unless self.class.start_block.nil?
63
+ @actor.entered self if tracked?
31
64
  end
32
65
 
66
+ # Finish the scene.
67
+ #
33
68
  def finish
34
69
  @finish_block.call @actor, self unless @finish_block.nil?
35
70
  @finished = true
36
71
  end
37
72
 
73
+ # Determine whether the scene's execution is finished.
74
+ #
75
+ # @return [Boolean]
38
76
  def finished?
39
77
  @finished ||= false
40
78
  end
41
79
 
42
- def flush
43
- @state.clear
44
- end
45
-
80
+ # Get a hash that describes the current state of the scene.
81
+ #
82
+ # @return [Hash]
46
83
  def state
47
84
  {
48
85
  scene: type, prompt: prompt
49
86
  }
50
87
  end
51
88
 
89
+ # @yieldparam [Class<Gamefic::Actor>]
90
+ # @return [Class<Gamefic::Scene::Base>]
52
91
  def self.subclass &block
53
92
  c = Class.new(self) do
54
93
  on_start &block
55
94
  end
56
95
  c
57
96
  end
58
-
97
+
59
98
  # Get the prompt to be displayed to the user when accepting input.
60
99
  #
61
100
  # @return [String] The text to be displayed.
@@ -63,19 +102,36 @@ module Gamefic
63
102
  @prompt ||= '>'
64
103
  end
65
104
 
105
+ # Get a String that describes the type of scene.
106
+ #
107
+ # @return [String]
66
108
  def type
67
109
  @type ||= 'Scene'
68
110
  end
69
111
 
112
+ # @yieldparam [Class<Gamefic::Scene::Base>]
70
113
  def self.on_start &block
71
114
  @start_block = block
72
115
  end
73
116
 
117
+ def tracked?
118
+ self.class.tracked?
119
+ end
120
+
121
+ def tracked= bool
122
+ self.class.tracked = bool
123
+ end
124
+
74
125
  class << self
126
+ attr_writer :tracked
127
+
75
128
  def start_block
76
129
  @start_block
77
130
  end
131
+
132
+ def tracked?
133
+ @tracked ||= false
134
+ end
78
135
  end
79
136
  end
80
-
81
137
  end