gamefic 2.0.2 → 2.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ac3f26cc5dcb62f6b4af8f0a365a316a4324605efecac8fd852f9980f00e336
4
- data.tar.gz: 11348faf9a1c471c11ca4da93f419167db25a92ec7617fd73fb592fdf0ec7d7f
3
+ metadata.gz: 1ac7da649347939f4c5baadc676151b8a8c73714552c48a290de75bd072bb801
4
+ data.tar.gz: 6d83150ed62df24a4056e3e54ff5b499bc5f7a69ebe8d5cfaddb261f64573af4
5
5
  SHA512:
6
- metadata.gz: 1bd78b541afec9fd2129d785442ddc811dacbc42c41f9c683329776f90ada2e105578324ef73fa7dedd7fc26ed828917f1a796765f0f1b74ade334b54c94d5ab
7
- data.tar.gz: d3a982efcb6bdd0c306ebdab70ee722fcd1040d6cf51501b738b8bca65e955689dcfe2d77aa5e8684e1f0e18d27b71579410f51a6189d23014da47156bbbe3cf
6
+ metadata.gz: e068317caa9fbfb1f8a73bde4725f5df3f3bcad95ba87d44c8f973759cdc991e0fbbcea874fd451eacbd6ffd2ea2ade9144e61f2d358f31fec6a7f1e97ca3a10
7
+ data.tar.gz: 1ab813a5cb14307bc844c42424c9503bae6f73a48d12979a5369812619e5a2e80dfab2ac6181401045511286743fc20f35968bd46e5071cf24f296e3c1c20e36
@@ -10,4 +10,7 @@ Style/StringLiterals:
10
10
 
11
11
  Style/Documentation:
12
12
  Description: 'Document classes and non-namespace modules.'
13
- Enabled: false
13
+ Enabled: false
14
+
15
+ Style/MethodDefParentheses:
16
+ Enabled: false
@@ -1,2 +1,6 @@
1
+ # 2.0.3 - December 14, 2020
2
+ - Remove unused Index class
3
+ - Active#conclude accepts data argument
4
+
1
5
  # 2.0.2 - April 25, 2020
2
6
  - Improved snapshot serialization
@@ -5,7 +5,6 @@ require 'gamefic/core_ext/array'
5
5
  require 'gamefic/core_ext/string'
6
6
 
7
7
  require 'gamefic/describable'
8
- # require 'gamefic/index'
9
8
  require 'gamefic/serialize'
10
9
  require 'gamefic/element'
11
10
  require 'gamefic/entity'
@@ -18,5 +17,4 @@ require "gamefic/syntax"
18
17
  require 'gamefic/world'
19
18
  require 'gamefic/scriptable'
20
19
  require 'gamefic/plot'
21
- require 'gamefic/plot/index'
22
20
  require 'gamefic/subplot'
@@ -1,9 +1,4 @@
1
1
  module Gamefic
2
- # Exception raised when the Action's proc arity is not compatible with the
3
- # number of queries
4
- class ActionArgumentError < ArgumentError
5
- end
6
-
7
2
  class Action
8
3
  # An array of objects on which the action will operate, e.g., an entity
9
4
  # that is a direct object of a command.
@@ -54,25 +49,32 @@ module Gamefic
54
49
  self.class.meta?
55
50
  end
56
51
 
57
- def self.subclass verb, *q, meta: false, &block
52
+ # @param verb [Symbol]
53
+ # @param queries [Array<Gamefic::Query::Base>]
54
+ # @param meta [Boolean]
55
+ # @return [Class<Action>]
56
+ def self.subclass verb, *queries, meta: false, &block
58
57
  act = Class.new(self) do
59
58
  self.verb = verb
60
59
  self.meta = meta
61
- q.each { |q|
60
+ queries.each do |q|
62
61
  add_query q
63
- }
62
+ end
64
63
  on_execute &block
65
64
  end
66
- if !block.nil? and act.queries.length + 1 != block.arity and block.arity > 0
67
- raise ActionArgumentError.new("Number of parameters is not compatible with proc arguments")
65
+ if !block.nil? && act.queries.length + 1 != block.arity && block.arity > 0
66
+ raise ArgumentError.new("Number of parameters is not compatible with proc arguments")
68
67
  end
69
68
  act
70
69
  end
71
70
 
72
71
  class << self
73
- def verb
74
- @verb
75
- end
72
+ attr_reader :verb
73
+
74
+ # The proc to call when the action is executed
75
+ #
76
+ # @return [Proc]
77
+ attr_reader :executor
76
78
 
77
79
  def meta?
78
80
  @meta ||= false
@@ -93,7 +95,7 @@ module Gamefic
93
95
 
94
96
  def signature
95
97
  # @todo This is clearly unfinished
96
- "#{verb} #{queries.map{|m| m.signature}.join(', ')}"
98
+ "#{verb} #{queries.map {|m| m.signature}.join(', ')}"
97
99
  end
98
100
 
99
101
  # True if this action is not intended to be performed directly by a
@@ -107,19 +109,13 @@ module Gamefic
107
109
  verb.to_s.start_with?('_')
108
110
  end
109
111
 
110
- # The proc to call when the action is executed
111
- #
112
- # @return [Proc]
113
- def executor
114
- @executor
115
- end
116
-
112
+ # @return [Integer]
117
113
  def rank
118
114
  if @rank.nil?
119
115
  @rank = 0
120
- queries.each { |q|
116
+ queries.each do |q|
121
117
  @rank += (q.rank + 1)
122
- }
118
+ end
123
119
  @rank -= 1000 if verb.nil?
124
120
  end
125
121
  @rank
@@ -128,22 +124,27 @@ module Gamefic
128
124
  def valid? actor, objects
129
125
  return false if objects.length != queries.length
130
126
  i = 0
131
- queries.each { |p|
127
+ queries.each do |p|
132
128
  return false unless p.include?(actor, objects[i])
133
129
  i += 1
134
- }
130
+ end
135
131
  true
136
132
  end
137
133
 
134
+ # Return an instance of this Action if the actor can execute it with the
135
+ # provided tokens, or nil if the tokens are invalid.
136
+ #
137
+ # @param action [Gamefic::Entity]
138
+ # @param tokens [Array<String>]
139
+ # @return [self, nil]
138
140
  def attempt actor, tokens
139
- i = 0
140
141
  result = []
141
142
  matches = Gamefic::Query::Matches.new([], '', '')
142
- queries.each { |p|
143
- return nil if tokens[i].nil? and matches.remaining == ''
143
+ queries.each_with_index do |p, i|
144
+ return nil if tokens[i].nil? && matches.remaining == ''
144
145
  matches = p.resolve(actor, "#{matches.remaining} #{tokens[i]}".strip, continued: (i < queries.length - 1))
145
146
  return nil if matches.objects.empty?
146
- accepted = matches.objects.select{|o| p.accept?(o)}
147
+ accepted = matches.objects.select { |o| p.accept?(o) }
147
148
  return nil if accepted.empty?
148
149
  if p.ambiguous?
149
150
  result.push accepted
@@ -151,20 +152,15 @@ module Gamefic
151
152
  return nil if accepted.length != 1
152
153
  result.push accepted.first
153
154
  end
154
- i += 1
155
- }
156
- self.new(actor, result)
155
+ end
156
+ new(actor, result)
157
157
  end
158
158
 
159
159
  protected
160
160
 
161
- def verb= sym
162
- @verb = sym
163
- end
161
+ attr_writer :verb
164
162
 
165
- def meta= bool
166
- @meta = bool
167
- end
163
+ attr_writer :meta
168
164
  end
169
165
  end
170
166
  end
@@ -117,9 +117,7 @@ module Gamefic
117
117
  #
118
118
  # @return [String] The output that resulted from performing the command.
119
119
  def quietly(*command)
120
- if buffer_stack == 0
121
- clear_buffer
122
- end
120
+ clear_buffer if buffer_stack == 0
123
121
  set_buffer_stack buffer_stack + 1
124
122
  self.perform *command
125
123
  set_buffer_stack buffer_stack - 1
@@ -152,10 +150,12 @@ module Gamefic
152
150
  # introduction do |actor|
153
151
  # actor[:has_eaten] = false # Initial value
154
152
  # end
153
+ #
155
154
  # respond :eat do |actor|
156
155
  # actor.tell "You eat something."
157
156
  # actor[:has_eaten] = true
158
157
  # end
158
+ #
159
159
  # respond :eat do |actor|
160
160
  # # This version will be executed first because it was implemented last
161
161
  # if actor[:has_eaten]
@@ -187,13 +187,14 @@ module Gamefic
187
187
  # Use #prepare if you want to declare a scene to be started at the
188
188
  # beginning of the next turn.
189
189
  #
190
- # @param new_scene [Class]
191
- def cue new_scene, **options
190
+ # @param new_scene [Class<Scene::Base>]
191
+ # @param data [Hash] Additional scene data
192
+ def cue new_scene, **data
192
193
  @next_scene = nil
193
194
  if new_scene.nil?
194
195
  @scene = nil
195
196
  else
196
- @scene = new_scene.new(self, **options)
197
+ @scene = new_scene.new(self, **data)
197
198
  @scene.start
198
199
  end
199
200
  end
@@ -202,10 +203,11 @@ module Gamefic
202
203
  # next turn. As opposed to #cue, a prepared scene will not start until the
203
204
  # current scene finishes.
204
205
  #
205
- # @param new_scene [Class]
206
- def prepare new_scene, **options
206
+ # @param new_scene [Class<Scene::Base>]
207
+ # @oaram data [Hash] Additional scene data
208
+ def prepare new_scene, **data
207
209
  @next_scene = new_scene
208
- @next_options = options
210
+ @next_options = data
209
211
  end
210
212
 
211
213
  # Return true if the character is expected to be in the specified scene on
@@ -219,9 +221,11 @@ module Gamefic
219
221
  # Cue a conclusion. This method works like #cue, except it will raise a
220
222
  # NotConclusionError if the scene is not a Scene::Conclusion.
221
223
  #
222
- def conclude scene
223
- raise NotConclusionError unless scene <= Scene::Conclusion
224
- cue scene
224
+ # @param new_scene [Class<Scene::Base>]
225
+ # @oaram data [Hash] Additional scene data
226
+ def conclude new_scene, **data
227
+ raise NotConclusionError unless new_scene <= Scene::Conclusion
228
+ cue new_scene, **data
225
229
  end
226
230
 
227
231
  # True if the character is in a conclusion.
@@ -269,6 +273,8 @@ module Gamefic
269
273
  @entered_scenes ||= []
270
274
  end
271
275
 
276
+ # @param actions [Array<Gamefic::Action>]
277
+ # @param quietly [Boolean]
272
278
  def execute_stack actions, quietly: false
273
279
  return nil if actions.empty?
274
280
  a = actions.first
@@ -44,6 +44,9 @@ class Array
44
44
  # animals = ['a dog', 'a cat', 'a mouse']
45
45
  # animals.join_and #=> 'a dog, a cat, and a mouse'
46
46
  #
47
+ # @param sep [String] The separator for all but the last element
48
+ # @param andSep [String] The separator for the last element
49
+ # @param serial [Boolean] Use serial separators (e.g., serial commas)
47
50
  # @return [String]
48
51
  def join_and(sep = ', ', andSep = ' and ', serial = true)
49
52
  if self.length < 3
@@ -12,12 +12,12 @@ module Gamefic
12
12
 
13
13
  # @todo It would be nice if this initialization wasn't necessary.
14
14
  def initialize(args = {})
15
- # super self.class.default_attributes.merge(args)
16
- self.class.default_attributes.merge(args).each_pair do |k, v|
17
- public_send "#{k}=", v
18
- end
19
- post_initialize
20
- yield self if block_given?
15
+ # super self.class.default_attributes.merge(args)
16
+ self.class.default_attributes.merge(args).each_pair do |k, v|
17
+ public_send "#{k}=", v
18
+ end
19
+ post_initialize
20
+ yield self if block_given?
21
21
  end
22
22
 
23
23
  def post_initialize
@@ -27,7 +27,6 @@ module Gamefic
27
27
 
28
28
  exclude_from_serial [:@static]
29
29
 
30
- # @param structure [Gamefic::Structure]
31
30
  # @param metadata [Hash]
32
31
  def initialize metadata: {}
33
32
  @metadata = metadata
@@ -35,11 +34,6 @@ module Gamefic
35
34
  @static = [self] + scene_classes + entities
36
35
  end
37
36
 
38
- def player_class cls = nil
39
- @player_class = cls unless cls.nil?
40
- @player_class ||= Gamefic::Actor
41
- end
42
-
43
37
  # Get an Array of the Plot's current Syntaxes.
44
38
  #
45
39
  # @return [Array<Syntax>]
@@ -93,8 +87,8 @@ module Gamefic
93
87
  end
94
88
  call_player_update
95
89
  call_update
96
- subplots.each { |s| s.update unless s.concluded? }
97
- subplots.delete_if { |s| s.concluded? }
90
+ subplots.delete_if(&:concluded?)
91
+ subplots.each(&:update)
98
92
  end
99
93
 
100
94
  # Send a message to a group of entities.
@@ -5,6 +5,7 @@ module Gamefic
5
5
  autoload :Descendants, 'gamefic/query/descendants'
6
6
  autoload :External, 'gamefic/query/external'
7
7
  autoload :Family, 'gamefic/query/family'
8
+ autoload :Tree, 'gamefic/query/tree'
8
9
  autoload :Itself, 'gamefic/query/itself'
9
10
  autoload :Matches, 'gamefic/query/matches'
10
11
  autoload :Parent, 'gamefic/query/parent'
@@ -55,13 +55,18 @@ module Gamefic
55
55
  result.include?(object)
56
56
  end
57
57
 
58
+ # A ranking of how precise the query's arguments are.
59
+ #
60
+ # Query precision is a factor in calculating Action#rank.
61
+ #
62
+ # @return [Integer]
58
63
  def precision
59
64
  if @precision.nil?
60
65
  @precision = 1
61
66
  arguments.each { |a|
62
- if a.kind_of?(Class)
67
+ if a.is_a?(Class)
63
68
  @precision += 100
64
- elsif a.kind_of?(Gamefic::Entity)
69
+ elsif a.is_a?(Gamefic::Entity)
65
70
  @precision += 1000
66
71
  end
67
72
  }
@@ -69,10 +74,7 @@ module Gamefic
69
74
  end
70
75
  @precision
71
76
  end
72
-
73
- def rank
74
- precision
75
- end
77
+ alias rank precision
76
78
 
77
79
  def signature
78
80
  "#{self.class.to_s.split('::').last.downcase}(#{simplify_arguments.join(', ')})"
@@ -83,18 +85,18 @@ module Gamefic
83
85
  # @return [Boolean]
84
86
  def accept?(entity)
85
87
  result = true
86
- arguments.each { |a|
87
- if a.kind_of?(Symbol)
88
- result = (entity.send(a) != false)
89
- elsif a.kind_of?(Regexp)
90
- result = (!entity.to_s.match(a).nil?)
91
- elsif a.is_a?(Module) or a.is_a?(Class)
92
- result = (entity.is_a?(a))
88
+ arguments.each do |a|
89
+ result = if a.is_a?(Symbol)
90
+ (entity.send(a) != false)
91
+ elsif a.is_a?(Regexp)
92
+ !entity.to_s.match(a).nil?
93
+ elsif a.is_a?(Module) || a.is_a?(Class)
94
+ entity.is_a?(a)
93
95
  else
94
- result = (entity == a)
96
+ (entity == a)
95
97
  end
96
98
  break if result == false
97
- }
99
+ end
98
100
  result
99
101
  end
100
102
 
@@ -109,10 +111,10 @@ module Gamefic
109
111
  return [] if entity.nil?
110
112
  result = []
111
113
  if entity.accessible?
112
- entity.children.each { |c|
114
+ entity.children.each do |c|
113
115
  result.push c
114
116
  result.concat subquery_accessible(c)
115
- }
117
+ end
116
118
  end
117
119
  result
118
120
  end
@@ -121,7 +123,7 @@ module Gamefic
121
123
 
122
124
  def simplify_arguments
123
125
  arguments.map do |a|
124
- if a.kind_of?(Class) or a.kind_of?(Object)
126
+ if a.is_a?(Class) || a.is_a?(Object)
125
127
  a.to_s.split('::').last.downcase
126
128
  else
127
129
  a.to_s.downcase
@@ -136,13 +138,12 @@ module Gamefic
136
138
  def denest(objects, token)
137
139
  parts = token.split(NEST_REGEXP)
138
140
  current = parts.pop
139
- last_result = objects.select{ |e| e.specified?(current) }
140
- last_result = objects.select{ |e| e.specified?(current, fuzzy: true) } if last_result.empty?
141
- result = last_result
142
- while parts.length > 0
141
+ last_result = objects.select { |e| e.specified?(current) }
142
+ last_result = objects.select { |e| e.specified?(current, fuzzy: true) } if last_result.empty?
143
+ until parts.empty?
143
144
  current = "#{parts.last} #{current}"
144
- result = last_result.select{ |e| e.specified?(current) }
145
- result = last_result.select{ |e| e.specified?(current, fuzzy: true) } if result.empty?
145
+ result = last_result.select { |e| e.specified?(current) }
146
+ result = last_result.select { |e| e.specified?(current, fuzzy: true) } if result.empty?
146
147
  break if result.empty?
147
148
  parts.pop
148
149
  last_result = result
@@ -5,9 +5,9 @@ module Gamefic
5
5
  result = []
6
6
  children = super
7
7
  result.concat children
8
- children.each { |c|
8
+ children.each do |c|
9
9
  result.concat subquery_accessible(c)
10
- }
10
+ end
11
11
  result
12
12
  end
13
13
  end
@@ -1,5 +1,7 @@
1
1
  module Gamefic
2
2
  module Query
3
+ # Query to retrieve the subject's siblings and all accessible descendants.
4
+ #
3
5
  class Family < Base
4
6
  def context_from(subject)
5
7
  result = []
@@ -2,14 +2,15 @@ module Gamefic
2
2
  module Query
3
3
  class Text < Base
4
4
  def initialize *arguments
5
- arguments.each { |a|
6
- if (a.kind_of?(Symbol) or a.kind_of?(String)) and !a.to_s.end_with?('?')
5
+ arguments.each do |a|
6
+ if (a.kind_of?(Symbol) || a.kind_of?(String)) && !a.to_s.end_with?('?')
7
7
  raise ArgumentError.new("Text query arguments can only be boolean method names (:method?) or regular expressions")
8
8
  end
9
- }
9
+ end
10
10
  super
11
11
  end
12
- def resolve(subject, token, continued: false)
12
+
13
+ def resolve _subject, token, continued: false
13
14
  return Matches.new([], '', token) unless accept?(token)
14
15
  parts = token.split(Keywords::SPLIT_REGEXP)
15
16
  cursor = []
@@ -22,20 +23,18 @@ module Gamefic
22
23
  }
23
24
  if continued
24
25
  Matches.new([matches.join(' ')], matches.join(' '), parts[i..-1].join(' '))
26
+ elsif matches.length == parts.length
27
+ Matches.new([matches.join(' ')], matches.join(' '), '')
25
28
  else
26
- if matches.length == parts.length
27
- Matches.new([matches.join(' ')], matches.join(' '), '')
28
- else
29
- Matches.new([], '', parts.join(' '))
30
- end
29
+ Matches.new([], '', parts.join(' '))
31
30
  end
32
31
  end
33
32
 
34
- def include?(subject, token)
33
+ def include? _subject, token
35
34
  accept?(token)
36
35
  end
37
36
 
38
- def accept?(entity)
37
+ def accept? entity
39
38
  return false unless entity.kind_of?(String) and !entity.empty?
40
39
  super
41
40
  end
@@ -0,0 +1,17 @@
1
+ module Gamefic
2
+ module Query
3
+ # Query to retrieve all of the subject's ancestors, siblings, and descendants.
4
+ #
5
+ class Tree < Family
6
+ def context_from(subject)
7
+ result = super
8
+ parent = subject.parent
9
+ until parent.nil?
10
+ result.unshift parent
11
+ parent = parent.parent
12
+ end
13
+ result
14
+ end
15
+ end
16
+ end
17
+ end
@@ -14,7 +14,7 @@ module Gamefic
14
14
 
15
15
  # The one-based index of the selected option.
16
16
  #
17
- # @return [Number]
17
+ # @return [Integer]
18
18
  attr_reader :number
19
19
 
20
20
  # The full text of the selected option.
@@ -1,3 +1,5 @@
1
+ require 'set'
2
+
1
3
  module Gamefic
2
4
  module Serialize
3
5
  def to_serial(index = [])
@@ -83,6 +85,8 @@ class Object
83
85
  return object
84
86
  elsif self['class'] == 'Class'
85
87
  return Gamefic::Serialize.string_to_constant(self['name'])
88
+ elsif self['class'] == 'Set'
89
+ return Set.new(self['data'].map { |el| el.from_serial(index) })
86
90
  else
87
91
  elematch = self['class'].match(/^#<ELE_([\d]+)>$/)
88
92
  if elematch
@@ -208,3 +212,12 @@ class Hash
208
212
  result
209
213
  end
210
214
  end
215
+
216
+ class Set
217
+ def to_serial(index = [])
218
+ {
219
+ 'class' => 'Set',
220
+ 'data' => to_a.map { |el| el.to_serial(index) }
221
+ }
222
+ end
223
+ end
@@ -11,8 +11,9 @@ module Gamefic
11
11
  attr_reader :plot
12
12
 
13
13
  # @param plot [Gamefic::Plot]
14
- # @param introduce [Gamefic::Actor]
15
- # @param next_cue [Class<Gamefic::Scene::Base>]
14
+ # @param introduce [Gamefic::Actor, nil]
15
+ # @param next_cue [Class<Gamefic::Scene::Base>, nil]
16
+ # @param more [Hash]
16
17
  def initialize plot, introduce: nil, next_cue: nil, **more
17
18
  @plot = plot
18
19
  @next_cue = next_cue
@@ -98,15 +99,5 @@ module Gamefic
98
99
  #
99
100
  def configure more
100
101
  end
101
-
102
- # def to_serial(index)
103
- # puts "Serializing #{self}"
104
- # super
105
- # end
106
-
107
- # def from_serial index = []
108
- # # @todo Customize subplot unserialization
109
- # super
110
- # end
111
102
  end
112
103
  end
@@ -86,6 +86,7 @@ module Gamefic
86
86
  end
87
87
 
88
88
  def ==(other)
89
+ return false unless other.is_a?(Syntax)
89
90
  signature == other.signature
90
91
  end
91
92
 
@@ -1,3 +1,3 @@
1
1
  module Gamefic
2
- VERSION = '2.0.2'
2
+ VERSION = '2.0.3'
3
3
  end
@@ -1,4 +1,6 @@
1
1
  module Gamefic
2
+ # A collection of classes and modules related to generating a world model.
3
+ #
2
4
  module World
3
5
  autoload :Playbook, 'gamefic/world/playbook'
4
6
  autoload :Entities, 'gamefic/world/entities'
@@ -96,7 +96,7 @@ module Gamefic
96
96
  end
97
97
 
98
98
  # Declare a dismabiguation response for actions.
99
- # The disambigurator is executed when an action expects an argument to
99
+ # The disambiguator is executed when an action expects an argument to
100
100
  # reference a specific entity but its query matched more than one. For
101
101
  # example, "red" might refer to either a red key or a red book.
102
102
  #
@@ -144,7 +144,7 @@ module Gamefic
144
144
  #
145
145
  # @return [Array<String>]
146
146
  def verbs
147
- playbook.verbs.map { |v| v.to_s }.reject{ |v| v.start_with?('_') }
147
+ playbook.verbs.map(&:to_s).reject { |v| v.start_with?('_') }
148
148
  end
149
149
 
150
150
  # Get an Array of all Actions defined in the Plot.
@@ -164,20 +164,20 @@ module Gamefic
164
164
 
165
165
  private
166
166
 
167
+ # @param queries [Array]
168
+ # @return [Array<Query::Base>]
167
169
  def map_response_args queries
168
- result = []
169
- queries.each do |q|
170
+ queries.map do |q|
170
171
  if q.is_a?(Regexp)
171
- result.push Gamefic::Query::Text.new(q)
172
+ Gamefic::Query::Text.new(q)
172
173
  elsif q.is_a?(Gamefic::Query::Base)
173
- result.push q
174
+ q
174
175
  elsif q.is_a?(Gamefic::Element) || (q.is_a?(Class) && q <= Gamefic::Element)
175
- result.push get_default_query.new(q)
176
+ get_default_query.new(q)
176
177
  else
177
178
  raise ArgumentError.new("Invalid argument for response: #{q}")
178
179
  end
179
180
  end
180
- result
181
181
  end
182
182
  end
183
183
  end
@@ -93,16 +93,6 @@ module Gamefic
93
93
  def players
94
94
  @players ||= []
95
95
  end
96
-
97
- # private
98
-
99
- # def mark_static_entities
100
- # @static_entity_length ||= entities.length
101
- # end
102
-
103
- # def static_entity_length
104
- # @static_entity_length || 0
105
- # end
106
96
  end
107
97
  end
108
98
  end
@@ -3,6 +3,16 @@ module Gamefic
3
3
  # A collection of rules for performing commands.
4
4
  #
5
5
  class Playbook
6
+ # An array of available syntaxes.
7
+ #
8
+ # @return [Array<Gamefic::Syntax>]
9
+ attr_reader :syntaxes
10
+
11
+ # An array of defined validators.
12
+ #
13
+ # @return [Array<Proc>]
14
+ attr_reader :validators
15
+
6
16
  def initialize commands: {}, syntaxes: [], validators: [], disambiguator: nil
7
17
  @commands = commands
8
18
  @syntaxes = syntaxes
@@ -10,13 +20,6 @@ module Gamefic
10
20
  @disambiguator = disambiguator
11
21
  end
12
22
 
13
- # An array of available syntaxes.
14
- #
15
- # @return [Array<Gamefic::Syntax>]
16
- def syntaxes
17
- @syntaxes
18
- end
19
-
20
23
  # An array of available actions.
21
24
  #
22
25
  # @return [Array<Gamefic::Action>]
@@ -31,21 +34,14 @@ module Gamefic
31
34
  @commands.keys
32
35
  end
33
36
 
34
- # An array of defined validators.
35
- #
36
- # @return [Array<Proc>]
37
- def validators
38
- @validators
39
- end
40
-
41
37
  # Get the action for handling ambiguous entity references.
42
38
  #
43
39
  def disambiguator
44
40
  @disambiguator ||= Action.subclass(nil, Query::Base.new) do |actor, entities|
45
41
  definites = []
46
- entities.each { |entity|
42
+ entities.each do |entity|
47
43
  definites.push entity.definitely
48
- }
44
+ end
49
45
  actor.tell "I don't know which you mean: #{definites.join_or}."
50
46
  end
51
47
  end
@@ -66,7 +62,7 @@ module Gamefic
66
62
  # Get an Array of all Actions associated with the specified verb.
67
63
  #
68
64
  # @param verb [Symbol] The Symbol for the verb (e.g., :go or :look)
69
- # @return [Array<Action>] The verb's associated Actions
65
+ # @return [Array<Class<Action>>] The verb's associated Actions
70
66
  def actions_for verb
71
67
  @commands[verb] || []
72
68
  end
@@ -150,12 +146,8 @@ module Gamefic
150
146
  # @return [Array<Gamefic::Action>]
151
147
  def dispatch(actor, *command)
152
148
  result = []
153
- if command.length > 1
154
- result.concat dispatch_from_params(actor, command[0], command[1..-1])
155
- end
156
- if result.empty?
157
- result.concat dispatch_from_string(actor, command.join(' '))
158
- end
149
+ result.concat dispatch_from_params(actor, command[0], command[1..-1]) if command.length > 1
150
+ result.concat dispatch_from_string(actor, command.join(' ')) if result.empty?
159
151
  result
160
152
  end
161
153
 
@@ -167,14 +159,13 @@ module Gamefic
167
159
  def dispatch_from_string actor, text
168
160
  result = []
169
161
  commands = Syntax.tokenize(text, actor.syntaxes)
170
- commands.each { |c|
171
- available = actions_for(c.verb)
172
- available.each { |a|
162
+ commands.each do |c|
163
+ actions_for(c.verb).each do |a|
173
164
  next if a.hidden?
174
165
  o = a.attempt(actor, c.arguments)
175
166
  result.unshift o unless o.nil?
176
- }
177
- }
167
+ end
168
+ end
178
169
  sort_and_reduce_actions result
179
170
  end
180
171
 
@@ -185,9 +176,9 @@ module Gamefic
185
176
  def dispatch_from_params actor, verb, params
186
177
  result = []
187
178
  available = actions_for(verb)
188
- available.each { |a|
179
+ available.each do |a|
189
180
  result.unshift a.new(actor, params) if a.valid?(actor, params)
190
- }
181
+ end
191
182
  sort_and_reduce_actions result
192
183
  end
193
184
 
@@ -217,38 +208,37 @@ module Gamefic
217
208
  user_friendly = action.verb.to_s.gsub(/_/, ' ')
218
209
  args = []
219
210
  used_names = []
220
- action.queries.each { |c|
211
+ action.queries.each do |_c|
221
212
  num = 1
222
213
  new_name = ":var"
223
214
  while used_names.include? new_name
224
- num = num + 1
215
+ num += 1
225
216
  new_name = ":var#{num}"
226
217
  end
227
218
  used_names.push new_name
228
219
  user_friendly += " #{new_name}"
229
220
  args.push new_name
230
- }
221
+ end
231
222
  add_syntax Syntax.new(user_friendly.strip, "#{action.verb} #{args.join(' ')}") unless action.verb.to_s.start_with?('_')
232
223
  end
233
224
 
234
225
  def add_syntax syntax
235
- if @commands[syntax.verb] == nil
236
- raise "No actions exist for \"#{syntax.verb}\""
237
- end
226
+ raise "No actions exist for \"#{syntax.verb}\"" if @commands[syntax.verb].nil?
227
+
238
228
  @syntaxes.unshift syntax
239
- @syntaxes.uniq! &:signature
240
- @syntaxes.sort! { |a, b|
229
+ @syntaxes.uniq!(&:signature)
230
+ @syntaxes.sort! do |a, b|
241
231
  if a.token_count == b.token_count
242
232
  # For syntaxes of the same length, length of action takes precedence
243
233
  b.first_word <=> a.first_word
244
234
  else
245
235
  b.token_count <=> a.token_count
246
236
  end
247
- }
237
+ end
248
238
  end
249
239
 
250
240
  def sort_and_reduce_actions arr
251
- arr.sort_by.with_index{|a, i| [a.rank, -i]}.reverse.uniq(&:class)
241
+ arr.sort_by.with_index { |a, i| [a.rank, -i]}.reverse.uniq(&:class)
252
242
  end
253
243
  end
254
244
  end
@@ -2,6 +2,7 @@ module Gamefic
2
2
  module World
3
3
  module Players
4
4
  include Gamefic::World::Entities
5
+ include Gamefic::World::Commands
5
6
 
6
7
  # An array of entities that are currently connected to users.
7
8
  #
@@ -10,12 +11,27 @@ module Gamefic
10
11
  @players ||= []
11
12
  end
12
13
 
13
- # Get the character that the player will control on introduction.
14
+ def player_class cls = nil
15
+ STDERR.puts "Modifying player_class this way is deprecated. Use set_player_class instead" unless cls.nil?
16
+ @player_class = cls unless cls.nil?
17
+ @player_class ||= Gamefic::Actor
18
+ end
19
+
20
+ # @param cls [Class]
21
+ def set_player_class cls
22
+ unless cls < Gamefic::Active && cls <= Gamefic::Entity
23
+ raise ArgumentError, "Player class must be an active entity"
24
+ end
25
+ @player_class = cls
26
+ end
27
+
28
+ # Make a character that a player will control on introduction.
14
29
  #
15
30
  # @return [Gamefic::Actor]
16
- def get_player_character
31
+ def make_player_character
17
32
  cast player_class, name: 'yourself', synonyms: 'self myself you me', proper_named: true
18
33
  end
34
+ alias get_player_character make_player_character
19
35
  end
20
36
  end
21
37
  end
metadata CHANGED
@@ -1,55 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gamefic
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-25 00:00:00.000000000 Z
11
+ date: 2020-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '12.3'
20
- - - "~>"
20
+ - - ">="
21
21
  - !ruby/object:Gem::Version
22
22
  version: '12.3'
23
23
  type: :development
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - ">="
27
+ - - "~>"
28
28
  - !ruby/object:Gem::Version
29
29
  version: '12.3'
30
- - - "~>"
30
+ - - ">="
31
31
  - !ruby/object:Gem::Version
32
32
  version: '12.3'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: rspec
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - ">="
38
- - !ruby/object:Gem::Version
39
- version: 3.5.0
40
37
  - - "~>"
41
38
  - !ruby/object:Gem::Version
42
39
  version: '3.5'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 3.5.0
43
43
  type: :development
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
46
46
  requirements:
47
- - - ">="
48
- - !ruby/object:Gem::Version
49
- version: 3.5.0
50
47
  - - "~>"
51
48
  - !ruby/object:Gem::Version
52
49
  version: '3.5'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 3.5.0
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: simplecov
55
55
  requirement: !ruby/object:Gem::Requirement
@@ -96,7 +96,6 @@ files:
96
96
  - lib/gamefic/plot.rb
97
97
  - lib/gamefic/plot/darkroom.rb
98
98
  - lib/gamefic/plot/host.rb
99
- - lib/gamefic/plot/index.rb
100
99
  - lib/gamefic/plot/snapshot.rb
101
100
  - lib/gamefic/query.rb
102
101
  - lib/gamefic/query/base.rb
@@ -109,6 +108,7 @@ files:
109
108
  - lib/gamefic/query/parent.rb
110
109
  - lib/gamefic/query/siblings.rb
111
110
  - lib/gamefic/query/text.rb
111
+ - lib/gamefic/query/tree.rb
112
112
  - lib/gamefic/scene.rb
113
113
  - lib/gamefic/scene/activity.rb
114
114
  - lib/gamefic/scene/base.rb
@@ -149,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
149
  - !ruby/object:Gem::Version
150
150
  version: '0'
151
151
  requirements: []
152
- rubygems_version: 3.0.3
152
+ rubygems_version: 3.1.2
153
153
  signing_key:
154
154
  specification_version: 4
155
155
  summary: Gamefic
@@ -1,92 +0,0 @@
1
- module Gamefic
2
- class Plot
3
- # A fixed index of plot elements. Plots and subplots use an index to track
4
- # objects created in scripts.
5
- #
6
- class Index
7
- # @param elements [Array]
8
- def initialize elements
9
- @elements = elements.uniq.freeze
10
- end
11
-
12
- # @return [Boolean]
13
- def include? element
14
- @elements.include?(element)
15
- end
16
-
17
- def all
18
- @elements
19
- end
20
-
21
- def concat elements
22
- @elements = (@elements + elements).uniq.freeze
23
- end
24
-
25
- def remove elements
26
- @elements = (@elements - elements).uniq.freeze
27
- end
28
-
29
- # @return [Object]
30
- def element index
31
- @elements[index]
32
- end
33
-
34
- def id_for(element)
35
- include?(element) ? "#<ELE_#{@elements.index(element)}>" : nil
36
- end
37
-
38
- # def self.from_serial serial, static
39
- # if serial.is_a?(Hash) && (serial['class'] || serial['element'])
40
- # if serial['class']
41
- # elematch = serial['class'].match(/^#<ELE_([\d]+)>$/)
42
- # if elematch
43
- # klass = static.element(elematch[1].to_i)
44
- # else
45
- # klass = eval(serial['class'])
46
- # end
47
- # object = klass.allocate
48
- # elsif serial['element']
49
- # object = static.element(serial['element'])
50
- # end
51
- # serial.each_pair do |k, v|
52
- # next unless k.to_s.start_with?('@')
53
- # object.instance_variable_set(k, from_serial(v, static))
54
- # end
55
- # object
56
- # elsif serial.is_a?(Numeric)
57
- # serial
58
- # elsif serial.is_a?(String)
59
- # match = serial.match(/#<ELE_([0-9]+)>/)
60
- # return static.element(match[1].to_i) if match
61
- # match = serial.match(/#<SYM:([a-z0-9_\?\!]+)>/i)
62
- # return match[1].to_sym if match
63
- # serial
64
- # elsif serial.is_a?(Array)
65
- # result = serial.map { |e| from_serial(e, static) }
66
- # result = "#<UNKNOWN>" if result.any? { |e| e == "#<UNKNOWN>" }
67
- # result
68
- # elsif serial.is_a?(Hash)
69
- # result = {}
70
- # unknown = false
71
- # serial.each_pair do |k, v|
72
- # k2 = from_serial(k, static)
73
- # v2 = from_serial(v, static)
74
- # if k2 == "#<UNKNOWN>" || v2 == "#<UNKNOWN>"
75
- # unknown = true
76
- # break
77
- # end
78
- # result[k2] = v2
79
- # end
80
- # result = "#<UNKNOWN>" if unknown
81
- # result
82
- # elsif serial && serial != true
83
- # STDERR.puts "Unable to unserialize #{serial.class}"
84
- # nil
85
- # else
86
- # # true, false, or nil
87
- # serial
88
- # end
89
- # end
90
- end
91
- end
92
- end