gamefic 1.6.0 → 1.7.0

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
  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