gamefic 2.0.2 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
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