gamefic 2.4.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec.yml +41 -40
  3. data/.rspec-opal +2 -0
  4. data/.solargraph.yml +20 -3
  5. data/CHANGELOG.md +9 -0
  6. data/Rakefile +11 -1
  7. data/bin/console +14 -0
  8. data/bin/setup +8 -0
  9. data/gamefic.gemspec +5 -2
  10. data/lib/gamefic/action.rb +52 -183
  11. data/lib/gamefic/active/cue.rb +25 -0
  12. data/lib/gamefic/active/epic.rb +68 -0
  13. data/lib/gamefic/active/messaging.rb +43 -0
  14. data/lib/gamefic/active/take.rb +69 -0
  15. data/lib/gamefic/active.rb +95 -192
  16. data/lib/gamefic/actor.rb +2 -0
  17. data/lib/gamefic/block.rb +28 -0
  18. data/lib/gamefic/command.rb +16 -6
  19. data/lib/gamefic/core_ext/array.rb +4 -4
  20. data/lib/gamefic/core_ext/string.rb +10 -5
  21. data/lib/gamefic/describable.rb +39 -65
  22. data/lib/gamefic/dispatcher.rb +63 -32
  23. data/lib/gamefic/entity.rb +44 -19
  24. data/lib/gamefic/logging.rb +32 -0
  25. data/lib/gamefic/messenger.rb +66 -0
  26. data/lib/gamefic/narrative.rb +104 -0
  27. data/lib/gamefic/node.rb +44 -53
  28. data/lib/gamefic/plot.rb +60 -93
  29. data/lib/gamefic/props/default.rb +41 -0
  30. data/lib/gamefic/props/multiple_choice.rb +65 -0
  31. data/lib/gamefic/props/pause.rb +11 -0
  32. data/lib/gamefic/props/yes_or_no.rb +21 -0
  33. data/lib/gamefic/props.rb +10 -0
  34. data/lib/gamefic/query/base.rb +45 -126
  35. data/lib/gamefic/query/general.rb +46 -0
  36. data/lib/gamefic/query/result.rb +20 -0
  37. data/lib/gamefic/query/scoped.rb +41 -0
  38. data/lib/gamefic/query/text.rb +30 -31
  39. data/lib/gamefic/query.rb +7 -15
  40. data/lib/gamefic/response.rb +118 -0
  41. data/lib/gamefic/rulebook/calls.rb +90 -0
  42. data/lib/gamefic/rulebook/events.rb +79 -0
  43. data/lib/gamefic/rulebook/hooks.rb +57 -0
  44. data/lib/gamefic/rulebook/scenes.rb +68 -0
  45. data/lib/gamefic/rulebook.rb +139 -0
  46. data/lib/gamefic/scanner.rb +103 -0
  47. data/lib/gamefic/scene/activity.rb +9 -17
  48. data/lib/gamefic/scene/conclusion.rb +6 -5
  49. data/lib/gamefic/scene/default.rb +88 -0
  50. data/lib/gamefic/scene/multiple_choice.rb +14 -69
  51. data/lib/gamefic/scene/pause.rb +9 -13
  52. data/lib/gamefic/scene/yes_or_no.rb +6 -46
  53. data/lib/gamefic/scene.rb +11 -7
  54. data/lib/gamefic/scope/base.rb +44 -0
  55. data/lib/gamefic/scope/children.rb +16 -0
  56. data/lib/gamefic/scope/family.rb +20 -0
  57. data/lib/gamefic/scope/myself.rb +13 -0
  58. data/lib/gamefic/scope/parent.rb +13 -0
  59. data/lib/gamefic/scope/siblings.rb +14 -0
  60. data/lib/gamefic/scope.rb +8 -0
  61. data/lib/gamefic/scriptable/actions.rb +156 -0
  62. data/lib/gamefic/scriptable/entities.rb +76 -0
  63. data/lib/gamefic/scriptable/events.rb +65 -0
  64. data/lib/gamefic/scriptable/proxy.rb +55 -0
  65. data/lib/gamefic/scriptable/queries.rb +73 -0
  66. data/lib/gamefic/scriptable/scenes.rb +162 -0
  67. data/lib/gamefic/scriptable.rb +167 -73
  68. data/lib/gamefic/snapshot.rb +36 -0
  69. data/lib/gamefic/stage.rb +51 -0
  70. data/lib/gamefic/subplot.rb +51 -79
  71. data/lib/gamefic/syntax/template.rb +67 -0
  72. data/lib/gamefic/syntax.rb +102 -83
  73. data/lib/gamefic/vault.rb +50 -0
  74. data/lib/gamefic/version.rb +1 -1
  75. data/lib/gamefic.rb +26 -15
  76. data/spec-opal/spec_helper.rb +24 -0
  77. metadata +91 -29
  78. data/lib/gamefic/element.rb +0 -46
  79. data/lib/gamefic/keywords.rb +0 -52
  80. data/lib/gamefic/messaging.rb +0 -43
  81. data/lib/gamefic/plot/darkroom.rb +0 -120
  82. data/lib/gamefic/plot/host.rb +0 -42
  83. data/lib/gamefic/plot/snapshot.rb +0 -27
  84. data/lib/gamefic/query/children.rb +0 -9
  85. data/lib/gamefic/query/descendants.rb +0 -15
  86. data/lib/gamefic/query/external.rb +0 -39
  87. data/lib/gamefic/query/family.rb +0 -18
  88. data/lib/gamefic/query/itself.rb +0 -13
  89. data/lib/gamefic/query/matches.rb +0 -75
  90. data/lib/gamefic/query/parent.rb +0 -9
  91. data/lib/gamefic/query/siblings.rb +0 -13
  92. data/lib/gamefic/query/tree.rb +0 -17
  93. data/lib/gamefic/scene/base.rb +0 -142
  94. data/lib/gamefic/scene/multiple_scene.rb +0 -29
  95. data/lib/gamefic/serialize.rb +0 -196
  96. data/lib/gamefic/world/callbacks.rb +0 -135
  97. data/lib/gamefic/world/commands.rb +0 -181
  98. data/lib/gamefic/world/entities.rb +0 -98
  99. data/lib/gamefic/world/playbook.rb +0 -233
  100. data/lib/gamefic/world/players.rb +0 -37
  101. data/lib/gamefic/world/scenes.rb +0 -228
  102. data/lib/gamefic/world.rb +0 -18
@@ -1,196 +0,0 @@
1
- require 'set'
2
-
3
- module Gamefic
4
- module Serialize
5
- def to_serial(index = [])
6
- if index.include?(self)
7
- {
8
- 'instance' => "#<ELE_#{index.index(self)}>",
9
- 'ivars' => {}
10
- }
11
- else
12
- if self.class == Class && self.name
13
- {
14
- 'class' => 'Class',
15
- 'name' => name
16
- }
17
- else
18
- {
19
- 'class' => serialized_class(index),
20
- 'ivars' => serialize_instance_variables(index)
21
- }
22
- end
23
- end
24
- end
25
-
26
- def serialized_class index
27
- if index.include?(self.class)
28
- "#<ELE_#{index.index(self.class)}>"
29
- else
30
- self.class.to_s
31
- end
32
- end
33
-
34
- # @param string [String]
35
- # @return [Object]
36
- def self.string_to_constant string
37
- space = Object
38
- string.split('::').each do |part|
39
- space = space.const_get(part)
40
- end
41
- space
42
- end
43
- end
44
- end
45
-
46
- class Object
47
- class << self
48
- def exclude_from_serial ary
49
- @excluded_from_serial = ary
50
- end
51
-
52
- def excluded_from_serial
53
- @excluded_from_serial ||= []
54
- end
55
- end
56
-
57
- def to_serial(_index)
58
- return self if [true, false, nil].include?(self)
59
- # @todo This warning is a little too spammy. Set up a logger so it can be
60
- # limited to an info or debug level.
61
- # STDERR.puts "Unable to convert #{self} to element"
62
- "#<UNKNOWN>"
63
- end
64
-
65
- def from_serial(index = [])
66
- if self.is_a?(Hash)
67
- if self['instance']
68
- elematch = self['instance'].match(/^#<ELE_([\d]+)>$/)
69
- object = index[elematch[1].to_i]
70
- raise "Unable to load indexed element ##{elematch[1]} #{self}" if object.nil?
71
- elsif self['class']
72
- if self['class'] == 'Hash'
73
- object = {}
74
- self['data'].each do |arr|
75
- object[arr[0].from_serial(index)] = arr[1].from_serial(index)
76
- end
77
- return object
78
- elsif self['class'] == 'Class'
79
- return Gamefic::Serialize.string_to_constant(self['name'])
80
- elsif self['class'] == 'Set'
81
- return Set.new(self['data'].map { |el| el.from_serial(index) })
82
- else
83
- elematch = self['class'].match(/^#<ELE_([\d]+)>$/)
84
- if elematch
85
- klass = index[elematch[1].to_i]
86
- else
87
- klass = Gamefic::Serialize.string_to_constant(self['class'])
88
- end
89
- raise "Unable to find class #{self['class']} #{self}" if klass.nil?
90
- object = klass.allocate
91
- end
92
- end
93
- self['ivars'].each_pair do |k, v|
94
- object.instance_variable_set(k, v.from_serial(index))
95
- end
96
- object
97
- elsif self.is_a?(Numeric)
98
- self
99
- elsif self.is_a?(String)
100
- match = self.match(/#<ELE_([0-9]+)>/)
101
- return index.index(match[1].to_i) if match
102
- match = self.match(/#<SYM:([a-z0-9_\?\!]+)>/i)
103
- return match[1].to_sym if match
104
- self
105
- else
106
- # true, false, or nil
107
- self
108
- end
109
- end
110
-
111
- def serialize_instance_variables(index)
112
- result = {}
113
- instance_variables.each do |k|
114
- next if self.class.excluded_from_serial.include?(k)
115
- val = instance_variable_get(k)
116
- if index.include?(val)
117
- result[k.to_s] = {
118
- 'instance' => "#<ELE_#{index.index(val)}>",
119
- 'ivars' => {}
120
- }
121
- else
122
- result[k.to_s] = val.to_serial(index)
123
- end
124
- end
125
- result
126
- end
127
- end
128
-
129
- class Class
130
- def to_serial(index = [])
131
- if name.nil?
132
- super
133
- else
134
- {
135
- 'class' => 'Class',
136
- 'name' => name
137
- }
138
- end
139
- end
140
- end
141
-
142
- class Symbol
143
- def to_serial(_index = [])
144
- "#<SYM:#{self}>"
145
- end
146
- end
147
-
148
- class String
149
- def to_serial(_index = [])
150
- self
151
- end
152
- end
153
-
154
- class Numeric
155
- def to_serial(_index = [])
156
- self
157
- end
158
- end
159
-
160
- class Array
161
- def to_serial(index = [])
162
- map do |e|
163
- s = e.to_serial(index)
164
- return "#<UNKNOWN>" if s == "#<UNKNOWN>"
165
- s
166
- end
167
- end
168
-
169
- def from_serial(index = [])
170
- result = map { |e| e.from_serial(index) }
171
- result = "#<UNKNOWN>" if result.any? { |e| e == "#<UNKNOWN>" }
172
- result
173
- end
174
- end
175
-
176
- class Hash
177
- def to_serial(index = [])
178
- result = {'class' => 'Hash', 'data' => []}
179
- each_pair do |key, value|
180
- k2 = key.to_serial(index)
181
- v2 = value.to_serial(index)
182
- return "#<UNKNOWN>" if k2 == "#<UNKNOWN>" || v2 == "#<UNKNOWN>"
183
- result['data'].push [k2, v2]
184
- end
185
- result
186
- end
187
- end
188
-
189
- class Set
190
- def to_serial(index = [])
191
- {
192
- 'class' => 'Set',
193
- 'data' => to_a.map { |el| el.to_serial(index) }
194
- }
195
- end
196
- end
@@ -1,135 +0,0 @@
1
- module Gamefic
2
- module World
3
- module Callbacks
4
- # Add a block to be executed on preparation of every turn.
5
- #
6
- # @example Increment a turn counter
7
- # turn = 0
8
- # on_ready do
9
- # turn += 1
10
- # end
11
- #
12
- def on_ready &block
13
- ready_procs.push block
14
- end
15
-
16
- # Add a block to be executed after the Plot is finished updating a turn.
17
- #
18
- def on_update &block
19
- update_procs.push block
20
- end
21
-
22
- # Add a block to be executed for each player at the beginning of a turn.
23
- #
24
- # @example Tell the player how many turns they've played.
25
- # on_player_ready do |player|
26
- # player[:turns] ||= 0
27
- # if player[:turns] > 0
28
- # player.tell "Turn #{player[:turns]}"
29
- # end
30
- # player[:turns] += 1
31
- # end
32
- #
33
- # @yieldparam [Gamefic::Actor]
34
- def on_player_ready &block
35
- player_ready_procs.push block
36
- end
37
-
38
- # Add a block to be executed for each player before an update.
39
- #
40
- # @yieldparam[Gamefic::Actor]
41
- def before_player_update &block
42
- before_player_update_procs.push block
43
- end
44
-
45
- # Add a block to be executed for each player at the end of a turn.
46
- #
47
- # @yieldparam [Gamefic::Actor]
48
- def on_player_update &block
49
- player_update_procs.push block
50
- end
51
-
52
- # Add a block to be executed at the conclusion of the plot.
53
- #
54
- # @yieldparam [Gamefic::Actor]
55
- def on_player_conclude &block
56
- player_conclude_procs.push block
57
- end
58
-
59
- def player_conclude_procs
60
- @player_conclude_procs ||= []
61
- end
62
-
63
- def ready_procs
64
- @ready_procs ||= []
65
- end
66
-
67
- def update_procs
68
- @update_procs ||= []
69
- end
70
-
71
- def player_ready_procs
72
- @player_ready_procs ||= []
73
- end
74
-
75
- def before_player_update_procs
76
- @before_player_update_procs ||= []
77
- end
78
-
79
- def player_update_procs
80
- @player_update_procs ||= []
81
- end
82
-
83
- private
84
-
85
- # Execute the on_ready blocks. This method is typically called by the
86
- # Plot while beginning a turn.
87
- #
88
- def call_ready
89
- ready_procs.each { |p| p.call }
90
- end
91
-
92
- # Execute the on_update blocks. This method is typically called by the
93
- # Plot while ending a turn.
94
- #
95
- def call_update
96
- update_procs.each { |p| p.call }
97
- end
98
-
99
- # Execute the before_player_update blocks for each player. This method is
100
- # typically called by the Plot while updating a turn, immediately before
101
- # processing player input.
102
- #
103
- def call_before_player_update
104
- players.each { |player|
105
- player.flush
106
- before_player_update_procs.each { |block| block.call player }
107
- }
108
- end
109
-
110
- # Execute the on_player_ready blocks for each player. This method is
111
- # typically called by the Plot while beginning a turn, immediately after
112
- # the on_ready blocks.
113
- #
114
- def call_player_ready
115
- players.each { |player|
116
- unless player.next_scene.nil? || !player.scene.finished?
117
- player.cue player.next_scene, **player.next_options
118
- end
119
- player.cue default_scene if player.scene.nil?
120
- player_ready_procs.each { |block| block.call player }
121
- }
122
- end
123
-
124
- # Execute the on_player_update blocks for each player. This method is
125
- # typically called by the Plot while ending a turn, immediately before the
126
- # on_ready blocks.
127
- #
128
- def call_player_update
129
- players.each { |player|
130
- player_update_procs.each { |block| block.call player }
131
- }
132
- end
133
- end
134
- end
135
- end
@@ -1,181 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'gamefic/action'
4
-
5
- module Gamefic
6
- module World
7
- module Commands
8
- include Gamefic::World::Entities
9
-
10
- # @return [Gamefic::World::Playbook]
11
- def playbook
12
- @playbook ||= Gamefic::World::Playbook.new
13
- end
14
-
15
- # Create an Action that responds to a command.
16
- # An Action uses the command argument to identify the imperative verb that
17
- # triggers the action.
18
- # It can also accept queries to tokenize the remainder of the input and
19
- # filter for particular entities or properties.
20
- # The block argument contains the code to be executed when the input
21
- # matches all of the Action's criteria (i.e., verb and queries).
22
- #
23
- # @example A simple Action.
24
- # respond :salute do |actor|
25
- # actor.tell "Hello, sir!"
26
- # end
27
- # # The command "salute" will respond "Hello, sir!"
28
- #
29
- # @example An Action that accepts a Character
30
- # respond :salute, Use.visible(Character) do |actor, character|
31
- # actor.tell "#{The character} returns your salute."
32
- # end
33
- #
34
- # @param command [Symbol] An imperative verb for the command
35
- # @param queries [Array<Query::Base>] Filters for the command's tokens
36
- # @yieldparam [Gamefic::Actor]
37
- # @return [Class] The resulting Action subclass
38
- def respond(command, *queries, &proc)
39
- playbook.respond(command, *map_response_args(queries), &proc)
40
- end
41
- alias action respond
42
-
43
- # Parse a verb and a list of arguments into an action.
44
- # This method serves as a shortcut to creating an action with one or more
45
- # arguments that identify specific entities.
46
- #
47
- # @example
48
- # @thing = make Entity, name: 'a thing'
49
- # parse "use", "the thing" do |actor, thing|
50
- # actor.tell "You use it."
51
- # end
52
- #
53
- # @raise [ArgumentError] if tokens are unrecognized or ambiguous
54
- #
55
- # @param verb [String, Symbol] The command's verb
56
- # @param tokens [Array<String>] The arguments passed to the action
57
- # @return [Class] The resulting Action subclass
58
- def parse verb, *tokens, &proc
59
- query = Query::External.new(entities)
60
- params = []
61
- tokens.each do |arg|
62
- matches = query.resolve(nil, arg)
63
- raise ArgumentError, "Unable to resolve token '#{arg}'" if matches.objects.empty?
64
- raise ArgumentError, "Ambiguous results for '#{arg}'" if matches.objects.length > 1
65
- params.push Query::Family.new(matches.objects[0])
66
- end
67
- respond(verb.to_sym, *params, &proc)
68
- end
69
-
70
- # Tokenize and parse a command to create a new Action subclass.
71
- #
72
- # @param command [String] The command
73
- # @yieldparam [Gamefic::Actor]
74
- # @return [Class] the resulting Action subclass
75
- def override(command, &proc)
76
- cmd = Syntax.tokenize(command, playbook.syntaxes).first
77
- raise "Unable to tokenize command '#{command}'" if cmd.nil?
78
- parse cmd.verb, *cmd.arguments, &proc
79
- end
80
-
81
- # Create a Meta Action that responds to a command.
82
- # Meta Actions are very similar to standard Actions, except the Plot
83
- # understands them to be commands that operate above and/or outside of the
84
- # actual game world. Examples of Meta Actions are commands that report the
85
- # player's current score, save and restore saved games, or list the game's
86
- # credits.
87
- #
88
- # @example A simple Meta Action
89
- # meta :credits do |actor|
90
- # actor.tell "This game was written by John Smith."
91
- # end
92
- #
93
- # @param command [Symbol] An imperative verb for the command
94
- # @param queries [Array<Query::Base>] Filters for the command's tokens
95
- # @yieldparam [Gamefic::Actor]
96
- def meta(command, *queries, &block)
97
- playbook.meta command, *queries, &block
98
- end
99
-
100
- # Add a proc to be evaluated before a character executes an action.
101
- # When a verb is specified, the proc will only be evaluated if the
102
- # action's verb matches it.
103
- #
104
- # @param verb [Symbol, nil]
105
- # @yieldparam [Gamefic::Action]
106
- def before_action verb = nil, &block
107
- playbook.before_action verb, &block
108
- end
109
- alias validate before_action
110
-
111
- # Add a proc to be evaluated after a character executes an action.
112
- # When a verb is specified, the proc will only be evaluated if the
113
- # action's verb matches it.
114
- #
115
- # @param [Symbol, nil]
116
- # @yieldparam [Gamefic::Action]
117
- def after_action verb = nil, &block
118
- playbook.after_action verb, &block
119
- end
120
-
121
- # Create an alternate Syntax for an Action.
122
- # The command and its translation can be parameterized.
123
- #
124
- # @example Create a synonym for the Inventory Action.
125
- # interpret "catalogue", "inventory"
126
- # # The command "catalogue" will be translated to "inventory"
127
- #
128
- # @example Create a parameterized synonym for the Look Action.
129
- # interpret "scrutinize :entity", "look :entity"
130
- # # The command "scrutinize chair" will be translated to "look chair"
131
- #
132
- # @param command [String] The format of the original command
133
- # @param translation [String] The format of the translated command
134
- # @return [Syntax] the Syntax object
135
- def interpret command, translation
136
- playbook.interpret command, translation
137
- end
138
- alias xlate interpret
139
-
140
- # Get an Array of available verbs.
141
- #
142
- # @return [Array<String>]
143
- def verbs
144
- playbook.verbs.map(&:to_s).reject { |v| v.start_with?('_') }
145
- end
146
-
147
- # Get an Array of all Actions defined in the Plot.
148
- #
149
- # @return [Array<Action>]
150
- def actions
151
- playbook.actions
152
- end
153
-
154
- def get_default_query
155
- @default_query_class ||= Gamefic::Query::Family
156
- end
157
-
158
- def set_default_query cls
159
- @default_query_class = cls
160
- end
161
-
162
- private
163
-
164
- # @param queries [Array]
165
- # @return [Array<Query::Base>]
166
- def map_response_args queries
167
- queries.map do |q|
168
- if q.is_a?(Regexp)
169
- Gamefic::Query::Text.new(q)
170
- elsif q.is_a?(Gamefic::Query::Base)
171
- q
172
- elsif q.is_a?(Gamefic::Element) || (q.is_a?(Class) && q <= Gamefic::Element)
173
- get_default_query.new(q)
174
- else
175
- raise ArgumentError, "Invalid argument for response: #{q.inspect}"
176
- end
177
- end
178
- end
179
- end
180
- end
181
- end
@@ -1,98 +0,0 @@
1
- module Gamefic
2
- module World
3
- module Entities
4
- # Make a new Entity with the provided properties.
5
- #
6
- # @example Create an Entity
7
- # chair = make Entity, name: 'red chair'
8
- # chair.name #=> 'red chair'
9
- #
10
- # @raise [ArgumentError] if class is not an Entity
11
- #
12
- # @param cls [Class] The Class of the Entity to be created.
13
- # @param args [Hash] The entity's properties.
14
- # @!macro [attach] make_entity
15
- # @return [$1]
16
- def make cls, args = {}, &block
17
- raise ArgumentError, "Invalid Entity class" unless cls.is_a?(Class) && cls <= Entity
18
- ent = cls.new args, &block
19
- entities.push ent
20
- ent
21
- end
22
-
23
- # Cast an active entity.
24
- # This method is similar to make, but it also provides the plot's
25
- # playbook to the entity so it can perform actions. The entity should
26
- # either be a kind of Gamefic::Actor or include the Gamefic::Active
27
- # module.
28
- #
29
- # @return [Gamefic::Actor, Gamefic::Active]
30
- def cast cls, args = {}, &block
31
- ent = make cls, args, &block
32
- ent.playbooks.push playbook
33
- ent
34
- end
35
-
36
- # Safely remove an entity from a plot.
37
- #
38
- # If the entity is dynamic (e.g., created after a plot is already
39
- # running), it is safe to delete it completely. Otherwise the entity
40
- # will still be referenced in the entities array, but its parent will be
41
- # set to nil.
42
- #
43
- # @param [Gamefic::Entity] The entity to remove
44
- def destroy entity
45
- entity.parent = nil
46
- # index = entities.index(entity)
47
- # return if index.nil? || index < static_entity_length - 1
48
- # entities.delete_at index
49
- # players.delete entity
50
- # entity.destroy
51
-
52
- # @todo It might make sense to destroy the entity completely now. It
53
- # will still have a reference in the index, but that shouldn't impact
54
- # the current state of the plot.
55
- return if static.include?(entity)
56
- entities.delete entity
57
- players.delete entity
58
- end
59
-
60
- # Pick an entity based on its description.
61
- # The description provided must match exactly one entity; otherwise
62
- # an error is raised.
63
- #
64
- # @example Select the Entity that matches the description
65
- # red_chair = make Entity, :name => 'red chair'
66
- # blue_chair = make Entity, :name => 'blue chair'
67
- # pick "red chair" #=> red_chair
68
- # pick "blue chair" #=> blue_chair
69
- # pick "chair" #=> IndexError: description is ambiguous
70
- #
71
- # @param description [String] The description of the entity
72
- # @return [Gamefic::Entity] The entity that matches the description
73
- def pick(description)
74
- result = Query::Matches.execute(entities, description)
75
- if result.objects.length == 0
76
- raise IndexError.new("Unable to find entity from '#{description}'")
77
- elsif result.objects.length > 1
78
- raise IndexError.new("Ambiguous entities found from '#{description}'")
79
- end
80
- result.objects[0]
81
- end
82
-
83
- # Get an array of entities associated with this plot.
84
- #
85
- # @return [Array<Gamefic::Entity>]
86
- def entities
87
- @entities ||= []
88
- end
89
-
90
- # Get an array of players associated with this plot.
91
- #
92
- # @return [Array<Gamefic::Actor>]
93
- def players
94
- @players ||= []
95
- end
96
- end
97
- end
98
- end