gamefic 1.6.0 → 1.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ce1dd2ec32bb2872b9c18a40cfdf92ac745506a4
4
- data.tar.gz: 264aa06dc79054f22d07fe4552b6341c05a4c82e
3
+ metadata.gz: f3811a2f448c91712eaa8eec46000577c7d38f7a
4
+ data.tar.gz: 6cb83cb93b14fd5c94401367e51828bad90a8960
5
5
  SHA512:
6
- metadata.gz: ad1365b02676307134c0a141dbe9003989093b2e78f590affd2c5bbaa237758ae1357e3c156551e36f65b22c44bd0f81a06b940e655075bf529dcea59b75fc6a
7
- data.tar.gz: fc741d054f2d4e187d6e51fc96ef4a59354dfc6bea21c39adfcf7a45a5e48195fcae2017c3543bd56d9e4dfbd89bfb584e4a3e8349145bb869878cbd178eb12b
6
+ metadata.gz: 12f2199a4d803e7a179b49e1d50f6e83571f21d601e67828546de918c65b3da0ee97790e66ee5f73f34b1a901d515284e3c8bd030ff0dc5d816678b7c9a0ba48
7
+ data.tar.gz: 31118c91be341821662fe8624b85924b196c52f9b2bcd0c758f5af13fc9d1dceaa76f204d6f6f3d2e274ef049153b416dcd2648a207e4ad07ea02ea4b9b440c4
@@ -3,8 +3,11 @@ require 'gamefic/core_ext/array'
3
3
  require 'gamefic/core_ext/string'
4
4
 
5
5
  require 'gamefic/grammar'
6
+ require 'gamefic/describable'
7
+ require 'gamefic/element'
6
8
  require 'gamefic/entity'
7
- require 'gamefic/character'
9
+ require 'gamefic/active'
10
+ require 'gamefic/actor'
8
11
  require "gamefic/scene"
9
12
  require "gamefic/query"
10
13
  require "gamefic/action"
@@ -90,6 +90,10 @@ module Gamefic
90
90
  "#{verb} #{queries.map{|m| m.signature}.join(',')}"
91
91
  end
92
92
 
93
+ def hidden?
94
+ verb.to_s.start_with?('_')
95
+ end
96
+
93
97
  def executor
94
98
  @executor
95
99
  end
@@ -1,232 +1,280 @@
1
- #require 'gamefic/director'
2
-
3
-
4
- module Gamefic
5
- class NotConclusionError < Exception
6
- end
7
-
8
- class Character < Entity
9
- autoload :State, 'gamefic/character/state'
10
-
11
- attr_reader :queue, :user
12
- # @return [Gamefic::Action]
13
- attr_reader :last_action
14
- # @return [Entity,nil]
15
- attr_reader :last_object
16
- attr_accessor :object_of_pronoun
17
- attr_reader :scene
18
- attr_reader :next_scene
19
- attr_accessor :playbook
20
-
21
- include Character::State
22
-
23
- def initialize(args = {})
24
- super
25
- @queue = Array.new
26
- @messages = ''
27
- @buffer_stack = 0
28
- @buffer = ""
29
- end
30
-
31
- # Connect a User.
32
- #
33
- # @param user [User]
34
- def connect(user)
35
- @user = user
36
- end
37
-
38
- # Disconnect the current User.
39
- #
40
- def disconnect
41
- @user = nil
42
- end
43
-
44
- # Send a message to the entity.
45
- # This method will automatically wrap the message in HTML paragraphs.
46
- # To send a message without paragraph formatting, use #stream instead.
47
- #
48
- # @param message [String]
49
- def tell(message)
50
- if @buffer_stack > 0
51
- @buffer += message
52
- else
53
- super
54
- end
55
- end
56
-
57
- # Send a message to the Character as raw text.
58
- # Unlike #tell, this method will not wrap the message in HTML paragraphs.
59
- #
60
- # @param message [String]
61
- def stream(message)
62
- if @buffer_stack > 0
63
- @buffer += message
64
- else
65
- super
66
- end
67
- end
68
-
69
- # Perform a command.
70
- # The command can be specified as a String or a set of tokens. Either form
71
- # should yield the same result, but using tokens can yield better
72
- # performance since it bypasses the parser.
73
- #
74
- # The command will be executed immediately regardless of game state.
75
- #
76
- # @example Send a command as a string
77
- # character.perform "take the key"
78
- #
79
- # @example Send a command as a set of tokens
80
- # character.perform :take, @key
81
- #
82
- def perform(*command)
83
- #Director.dispatch(self, *command)
84
- actions = playbook.dispatch(self, *command)
85
- a = actions.first
86
- okay = true
87
- unless a.meta?
88
- playbook.validators.each { |v|
89
- result = v.call(self, a.verb, a.parameters)
90
- okay = (result != false)
91
- break if not okay
92
- }
93
- end
94
- if okay
95
- performance_stack.push actions
96
- proceed
97
- performance_stack.pop
98
- end
99
- a
100
- end
101
-
102
- # Quietly perform a command.
103
- # This method executes the command exactly as #perform does, except it
104
- # buffers the resulting output instead of sending it to the user.
105
- #
106
- # @return [String] The output that resulted from performing the command.
107
- def quietly(*command)
108
- if @buffer_stack == 0
109
- @buffer = ""
110
- end
111
- @buffer_stack += 1
112
- self.perform *command
113
- @buffer_stack -= 1
114
- @buffer
115
- end
116
-
117
- # Proceed to the next Action in the current stack.
118
- # This method is typically used in Action blocks to cascade through
119
- # multiple implementations of the same verb.
120
- #
121
- # @example Proceed through two implementations of a verb
122
- # introduction do |actor|
123
- # actor[:has_eaten] = false # Initial value
124
- # end
125
- # respond :eat do |actor|
126
- # actor.tell "You eat something."
127
- # actor[:has_eaten] = true
128
- # end
129
- # respond :eat do |actor|
130
- # # This version will be executed first because it was implemented last
131
- # if actor[:has_eaten]
132
- # actor.tell "You already ate."
133
- # else
134
- # actor.proceed # Execute the previous implementation
135
- # end
136
- # end
137
- #
138
- def proceed quietly: false
139
- #Director::Delegate.proceed_for self
140
- return if performance_stack.empty?
141
- a = performance_stack.last.shift
142
- unless a.nil?
143
- if quietly
144
- if @buffer_stack == 0
145
- @buffer = ""
146
- end
147
- @buffer_stack += 1
148
- end
149
- a.execute
150
- if quietly
151
- @buffer_stack -= 1
152
- @buffer
153
- end
154
- end
155
- end
156
-
157
- # Immediately start a new scene for the character.
158
- # Use #prepare if you want to declare a scene to be started at the
159
- # beginning of the next turn.
160
- #
161
- def cue new_scene
162
- @next_scene = nil
163
- if new_scene.nil?
164
- @scene = nil
165
- else
166
- @scene = new_scene.new(self)
167
- @scene.start
168
- end
169
- end
170
-
171
- # Prepare a scene to be started for this character at the beginning of the
172
- # next turn.
173
- #
174
- def prepare s
175
- @next_scene = s
176
- end
177
-
178
- # Return true if the character is expected to be in the specified scene on
179
- # the next turn.
180
- #
181
- # @return [Boolean]
182
- def will_cue? scene
183
- (@scene.class == scene and @next_scene.nil?) or @next_scene == scene
184
- end
185
-
186
- # Cue a conclusion. This method works like #cue, except it will raise a
187
- # NotConclusionError if the scene is not a Scene::Conclusion.
188
- #
189
- def conclude scene
190
- raise NotConclusionError unless scene <= Scene::Conclusion
191
- cue scene
192
- end
193
-
194
- # True if the character is in a conclusion.
195
- #
196
- # @return [Boolean]
197
- def concluded?
198
- !scene.nil? and scene.kind_of?(Scene::Conclusion)
199
- end
200
-
201
- def performed order
202
- order.freeze
203
- @last_action = order
204
- end
205
-
206
- # Get the prompt that the user should see for the current scene.
207
- #
208
- # @return [String]
209
- #def prompt
210
- # scene.nil? ? '>' : scene.prompt
211
- #end
212
-
213
- def accessible?
214
- false
215
- end
216
-
217
- def inspect
218
- to_s
219
- end
220
-
221
- private
222
-
223
- def delegate_stack
224
- @delegate_stack ||= []
225
- end
226
-
227
- def performance_stack
228
- @performance_stack ||= []
229
- end
230
- end
231
-
232
- end
1
+ module Gamefic
2
+ class NotConclusionError < Exception
3
+ end
4
+
5
+ # The Active module gives entities the ability to perform actions and
6
+ # participate in scenes.
7
+ #
8
+ module Active
9
+ # @return [Gamefic::Action]
10
+ attr_reader :last_action
11
+
12
+ # @return [Gamefic::User::Base]
13
+ attr_reader :user
14
+
15
+ # @return [Gamefic::Scene::Base]
16
+ attr_reader :scene
17
+
18
+ attr_reader :next_scene
19
+
20
+ # @return [Gamefic::Plot::Playbook]
21
+ #attr_accessor :playbook
22
+
23
+ # @return [Array<Gamefic::Plot::Playbook>]
24
+ def playbooks
25
+ @playbooks ||= []
26
+ end
27
+
28
+ def connect user
29
+ @user = user
30
+ end
31
+
32
+ # An array of actions waiting to be performed.
33
+ #
34
+ # @return [Array<String>]
35
+ def queue
36
+ @queue ||= []
37
+ end
38
+
39
+ # A hash of values representing the state of a performing entity.
40
+ #
41
+ # @return [Hash]
42
+ def state
43
+ @state = {}
44
+ @state.merge! scene.state unless scene.nil?
45
+ @state[:output] = messages
46
+ @state
47
+ end
48
+
49
+ # Send a message to the entity.
50
+ # This method will automatically wrap the message in HTML paragraphs.
51
+ # To send a message without paragraph formatting, use #stream instead.
52
+ #
53
+ # @param message [String]
54
+ def tell(message)
55
+ if buffer_stack > 0
56
+ append_buffer message
57
+ else
58
+ super
59
+ end
60
+ end
61
+
62
+ # Send a message to the Character as raw text.
63
+ # Unlike #tell, this method will not wrap the message in HTML paragraphs.
64
+ #
65
+ # @param message [String]
66
+ def stream(message)
67
+ if buffer_stack > 0
68
+ append_buffer message
69
+ else
70
+ super
71
+ end
72
+ end
73
+
74
+ # Perform a command.
75
+ # The command can be specified as a String or a verb with a list of
76
+ # parameters. Either form should yield the same result, but the
77
+ # verb/parameter form can yield better performance since it bypasses the
78
+ # parser.
79
+ #
80
+ # The command will be executed immediately regardless of the entity's
81
+ # state.
82
+ #
83
+ # @example Send a command as a string
84
+ # character.perform "take the key"
85
+ #
86
+ # @example Send a command as a verb with parameters
87
+ # character.perform :take, @key
88
+ #
89
+ # @return [Gamefic::Action]
90
+ def perform(*command)
91
+ actions = []
92
+ playbooks.reverse.each { |p| actions.concat p.dispatch(self, *command) }
93
+ execute_stack actions
94
+ end
95
+
96
+ # Quietly perform a command.
97
+ # This method executes the command exactly as #perform does, except it
98
+ # buffers the resulting output instead of sending it to the user.
99
+ #
100
+ # @return [String] The output that resulted from performing the command.
101
+ def quietly(*command)
102
+ if buffer_stack == 0
103
+ clear_buffer
104
+ end
105
+ set_buffer_stack buffer_stack + 1
106
+ self.perform *command
107
+ set_buffer_stack buffer_stack - 1
108
+ buffer
109
+ end
110
+
111
+ # Perform an action.
112
+ # This is functionally identical to the `perform` method, except the
113
+ # action must be declared as a verb with a list of parameters. Use
114
+ # `perform` if you need to parse a string as a command.
115
+ #
116
+ # The command will be executed immediately regardless of the entity's
117
+ # state.
118
+ #
119
+ # @example
120
+ # character.execute :take, @key
121
+ #
122
+ # @return [Gamefic::Action]
123
+ def execute(verb, *params, quietly: false)
124
+ actions = []
125
+ playbooks.reverse.each { |p| actions.concat p.dispatch_from_params(self, verb, params) }
126
+ execute_stack actions, quietly: quietly
127
+ end
128
+
129
+ # Proceed to the next Action in the current stack.
130
+ # This method is typically used in Action blocks to cascade through
131
+ # multiple implementations of the same verb.
132
+ #
133
+ # @example Proceed through two implementations of a verb
134
+ # introduction do |actor|
135
+ # actor[:has_eaten] = false # Initial value
136
+ # end
137
+ # respond :eat do |actor|
138
+ # actor.tell "You eat something."
139
+ # actor[:has_eaten] = true
140
+ # end
141
+ # respond :eat do |actor|
142
+ # # This version will be executed first because it was implemented last
143
+ # if actor[:has_eaten]
144
+ # actor.tell "You already ate."
145
+ # else
146
+ # actor.proceed # Execute the previous implementation
147
+ # end
148
+ # end
149
+ #
150
+ def proceed quietly: false
151
+ #Director::Delegate.proceed_for self
152
+ return if performance_stack.empty?
153
+ a = performance_stack.last.shift
154
+ unless a.nil?
155
+ if quietly
156
+ if @buffer_stack == 0
157
+ @buffer = ""
158
+ end
159
+ @buffer_stack += 1
160
+ end
161
+ a.execute
162
+ if quietly
163
+ @buffer_stack -= 1
164
+ @buffer
165
+ end
166
+ end
167
+ end
168
+
169
+ # Immediately start a new scene for the character.
170
+ # Use #prepare if you want to declare a scene to be started at the
171
+ # beginning of the next turn.
172
+ #
173
+ def cue new_scene
174
+ @next_scene = nil
175
+ if new_scene.nil?
176
+ @scene = nil
177
+ else
178
+ @scene = new_scene.new(self)
179
+ @scene.start
180
+ end
181
+ end
182
+
183
+ # Prepare a scene to be started for this character at the beginning of the
184
+ # next turn.
185
+ #
186
+ def prepare s
187
+ @next_scene = s
188
+ end
189
+
190
+ # Return true if the character is expected to be in the specified scene on
191
+ # the next turn.
192
+ #
193
+ # @return [Boolean]
194
+ def will_cue? scene
195
+ (@scene.class == scene and @next_scene.nil?) or @next_scene == scene
196
+ end
197
+
198
+ # Cue a conclusion. This method works like #cue, except it will raise a
199
+ # NotConclusionError if the scene is not a Scene::Conclusion.
200
+ #
201
+ def conclude scene
202
+ raise NotConclusionError unless scene <= Scene::Conclusion
203
+ cue scene
204
+ end
205
+
206
+ # True if the character is in a conclusion.
207
+ #
208
+ # @return [Boolean]
209
+ def concluded?
210
+ !scene.nil? and scene.kind_of?(Scene::Conclusion)
211
+ end
212
+
213
+ def performed order
214
+ order.freeze
215
+ @last_action = order
216
+ end
217
+
218
+ def accessible?
219
+ false
220
+ end
221
+
222
+ def inspect
223
+ to_s
224
+ end
225
+
226
+ private
227
+
228
+ def execute_stack actions, quietly: false
229
+ return nil if actions.empty?
230
+ a = actions.first
231
+ okay = true
232
+ unless a.meta?
233
+ playbooks.reverse.each do |playbook|
234
+ okay = validate_playbook playbook, a
235
+ break unless okay
236
+ end
237
+ end
238
+ if okay
239
+ performance_stack.push actions
240
+ proceed quietly: quietly
241
+ performance_stack.pop
242
+ end
243
+ a
244
+ end
245
+
246
+ def validate_playbook playbook, action
247
+ okay = true
248
+ playbook.validators.each { |v|
249
+ result = v.call(self, action.verb, action.parameters)
250
+ okay = (result != false)
251
+ break unless okay
252
+ }
253
+ okay
254
+ end
255
+
256
+ def buffer_stack
257
+ @buffer_stack ||= 0
258
+ end
259
+
260
+ def set_buffer_stack num
261
+ @buffer_stack = num
262
+ end
263
+
264
+ def buffer
265
+ @buffer ||= ''
266
+ end
267
+
268
+ def append_buffer str
269
+ @buffer += str
270
+ end
271
+
272
+ def clear_buffer
273
+ @buffer = '' unless @buffer.empty?
274
+ end
275
+
276
+ def performance_stack
277
+ @performance_stack ||= []
278
+ end
279
+ end
280
+ end