gamefic 1.7.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +13 -0
  5. data/.solargraph.yml +5 -0
  6. data/Gemfile +7 -0
  7. data/LICENSE +20 -0
  8. data/README.md +28 -0
  9. data/Rakefile +10 -0
  10. data/gamefic.gemspec +27 -0
  11. data/lib/gamefic.rb +7 -6
  12. data/lib/gamefic/action.rb +38 -28
  13. data/lib/gamefic/active.rb +325 -280
  14. data/lib/gamefic/actor.rb +8 -5
  15. data/lib/gamefic/command.rb +9 -7
  16. data/lib/gamefic/core_ext/array.rb +24 -49
  17. data/lib/gamefic/core_ext/string.rb +25 -16
  18. data/lib/gamefic/describable.rb +21 -23
  19. data/lib/gamefic/element.rb +43 -31
  20. data/lib/gamefic/entity.rb +6 -12
  21. data/lib/gamefic/index.rb +121 -0
  22. data/lib/gamefic/{matchable.rb → keywords.rb} +52 -50
  23. data/lib/gamefic/messaging.rb +43 -44
  24. data/lib/gamefic/node.rb +14 -5
  25. data/lib/gamefic/plot.rb +69 -89
  26. data/lib/gamefic/plot/darkroom.rb +92 -264
  27. data/lib/gamefic/plot/host.rb +42 -48
  28. data/lib/gamefic/plot/snapshot.rb +5 -18
  29. data/lib/gamefic/query.rb +14 -18
  30. data/lib/gamefic/query/base.rb +30 -18
  31. data/lib/gamefic/query/children.rb +0 -0
  32. data/lib/gamefic/query/external.rb +18 -14
  33. data/lib/gamefic/query/family.rb +1 -7
  34. data/lib/gamefic/query/matches.rb +75 -67
  35. data/lib/gamefic/query/parent.rb +0 -0
  36. data/lib/gamefic/query/siblings.rb +0 -0
  37. data/lib/gamefic/query/text.rb +2 -1
  38. data/lib/gamefic/scene.rb +0 -2
  39. data/lib/gamefic/scene/activity.rb +24 -26
  40. data/lib/gamefic/scene/base.rb +64 -8
  41. data/lib/gamefic/scene/conclusion.rb +0 -2
  42. data/lib/gamefic/scene/custom.rb +0 -2
  43. data/lib/gamefic/scene/multiple_choice.rb +18 -3
  44. data/lib/gamefic/scene/multiple_scene.rb +29 -20
  45. data/lib/gamefic/scene/pause.rb +7 -2
  46. data/lib/gamefic/scene/yes_or_no.rb +21 -9
  47. data/lib/gamefic/scriptable.rb +87 -0
  48. data/lib/gamefic/serialize.rb +68 -0
  49. data/lib/gamefic/subplot.rb +29 -35
  50. data/lib/gamefic/syntax.rb +14 -13
  51. data/lib/gamefic/version.rb +3 -3
  52. data/lib/gamefic/world.rb +16 -0
  53. data/lib/gamefic/world/callbacks.rb +135 -0
  54. data/lib/gamefic/world/commands.rb +184 -0
  55. data/lib/gamefic/{plot → world}/entities.rb +30 -29
  56. data/lib/gamefic/{plot → world}/playbook.rb +255 -240
  57. data/lib/gamefic/world/players.rb +21 -0
  58. data/lib/gamefic/world/scenes.rb +226 -0
  59. metadata +41 -92
  60. data/bin/gamefic +0 -9
  61. data/lib/gamefic/engine.rb +0 -7
  62. data/lib/gamefic/engine/base.rb +0 -59
  63. data/lib/gamefic/engine/tty.rb +0 -24
  64. data/lib/gamefic/grammar.rb +0 -13
  65. data/lib/gamefic/grammar/conjugator.rb +0 -20
  66. data/lib/gamefic/grammar/gender.rb +0 -11
  67. data/lib/gamefic/grammar/person.rb +0 -10
  68. data/lib/gamefic/grammar/plural.rb +0 -13
  69. data/lib/gamefic/grammar/pronouns.rb +0 -106
  70. data/lib/gamefic/grammar/tense.rb +0 -6
  71. data/lib/gamefic/grammar/verb_set.rb +0 -43
  72. data/lib/gamefic/grammar/verbs.rb +0 -26
  73. data/lib/gamefic/grammar/word_adapter.rb +0 -49
  74. data/lib/gamefic/plot/articles.rb +0 -22
  75. data/lib/gamefic/plot/callbacks.rb +0 -126
  76. data/lib/gamefic/plot/commands.rb +0 -120
  77. data/lib/gamefic/plot/players.rb +0 -15
  78. data/lib/gamefic/plot/scenes.rb +0 -187
  79. data/lib/gamefic/plot/theater.rb +0 -73
  80. data/lib/gamefic/plot/you_mount.rb +0 -22
  81. data/lib/gamefic/script.rb +0 -13
  82. data/lib/gamefic/script/base.rb +0 -42
  83. data/lib/gamefic/script/file.rb +0 -14
  84. data/lib/gamefic/script/text.rb +0 -14
  85. data/lib/gamefic/shell.rb +0 -76
  86. data/lib/gamefic/source.rb +0 -14
  87. data/lib/gamefic/source/base.rb +0 -12
  88. data/lib/gamefic/source/file.rb +0 -23
  89. data/lib/gamefic/source/text.rb +0 -16
  90. data/lib/gamefic/tester.rb +0 -19
  91. data/lib/gamefic/text.rb +0 -8
  92. data/lib/gamefic/text/ansi.rb +0 -53
  93. data/lib/gamefic/text/html.rb +0 -68
  94. data/lib/gamefic/text/html/conversions.rb +0 -250
  95. data/lib/gamefic/text/html/entities.rb +0 -9
  96. data/lib/gamefic/tty.rb +0 -10
  97. data/lib/gamefic/user.rb +0 -7
  98. data/lib/gamefic/user/base.rb +0 -29
  99. data/lib/gamefic/user/tty.rb +0 -38
@@ -1,240 +1,255 @@
1
- module Gamefic
2
-
3
- class Plot
4
- class Playbook
5
- def initialize commands: {}, syntaxes: [], validators: [], disambiguator: nil
6
- @commands = commands
7
- @syntaxes = syntaxes
8
- @validators = validators
9
- @disambiguator = disambiguator
10
- end
11
-
12
- def syntaxes
13
- @syntaxes
14
- end
15
-
16
- def actions
17
- @commands.values.flatten
18
- end
19
-
20
- def verbs
21
- @commands.keys
22
- end
23
-
24
- def validators
25
- @validators
26
- end
27
-
28
- def disambiguator
29
- @disambiguator ||= Action.new(nil, Query::Base.new) do |actor, entities|
30
- definites = []
31
- entities.each { |entity|
32
- definites.push entity.definitely
33
- }
34
- actor.tell "I don't know which you mean: #{definites.join_or}."
35
- end
36
- end
37
-
38
- #def disambiguate &block
39
- # @disambiguator = Action.new(nil, Query::Base.new, &block)
40
- # @disambiguator.meta = true
41
- # @disambiguator
42
- #end
43
-
44
- def validate &block
45
- @validators.push block
46
- end
47
-
48
- # Get an Array of all Actions associated with the specified verb.
49
- #
50
- # @param verb [Symbol] The Symbol for the verb (e.g., :go or :look)
51
- # @return [Array<Action>] The verb's associated Actions
52
- def actions_for verb
53
- @commands[verb] || []
54
- end
55
-
56
- # Create an Action that responds to a command.
57
- # An Action uses the command argument to identify the imperative verb that
58
- # triggers the action.
59
- # It can also accept queries to tokenize the remainder of the input and
60
- # filter for particular entities or properties.
61
- # The block argument contains the code to be executed when the input
62
- # matches all of the Action's criteria (i.e., verb and queries).
63
- #
64
- # @example A simple Action.
65
- # respond :salute do |actor|
66
- # actor.tell "Hello, sir!"
67
- # end
68
- # # The command "salute" will respond "Hello, sir!"
69
- #
70
- # @example An Action that accepts a Character
71
- # respond :salute, Use.visible(Character) do |actor, character|
72
- # actor.tell "#{The character} returns your salute."
73
- # end
74
- #
75
- # @param verb [Symbol] An imperative verb for the command
76
- # @param queries [Array<Query::Base>] Filters for the command's tokens
77
- # @yieldparam [Gamefic::Character]
78
- # @return [Gamefic::Action]
79
- def respond(verb, *queries, &proc)
80
- act = Action.subclass verb, *queries, order_key: raise_order_key, &proc
81
- add_action act
82
- act
83
- end
84
-
85
- # Create a Meta Action that responds to a command.
86
- # Meta Actions are very similar to standard Actions, except the Plot
87
- # understands them to be commands that operate above and/or outside of the
88
- # actual game world. Examples of Meta Actions are commands that report the
89
- # player's current score, save and restore saved games, or list the game's
90
- # credits.
91
- #
92
- # @example A simple Meta Action
93
- # meta :credits do |actor|
94
- # actor.tell "This game was written by John Smith."
95
- # end
96
- #
97
- # @param verb [Symbol] An imperative verb for the command
98
- # @param queries [Array<Query::Base>] Filters for the command's tokens
99
- # @yieldparam [Gamefic::Character]
100
- def meta(verb, *queries, &proc)
101
- act = Action.subclass verb, *queries, meta: true, &proc
102
- add_action act
103
- act
104
- end
105
-
106
- # Create an alternate Syntax for an Action.
107
- # The command and its translation can be parameterized.
108
- #
109
- # @example Create a synonym for the Inventory Action.
110
- # interpret "catalogue", "inventory"
111
- # # The command "catalogue" will be translated to "inventory"
112
- #
113
- # @example Create a parameterized synonym for the Look Action.
114
- # interpret "scrutinize :entity", "look :entity"
115
- # # The command "scrutinize chair" will be translated to "look chair"
116
- #
117
- # @param input [String] The format of the original command
118
- # @param translation [String] The format of the translated command
119
- # @return [Syntax] the Syntax object
120
- def interpret(input, translation)
121
- syn = Syntax.new(input, translation)
122
- add_syntax syn
123
- syn
124
- end
125
-
126
- def dispatch(actor, *command)
127
- result = []
128
- if command.length > 1
129
- result.concat dispatch_from_params(actor, command[0], command[1..-1])
130
- end
131
- if result.empty?
132
- result.concat dispatch_from_string(actor, command.join(' '))
133
- end
134
- result
135
- end
136
-
137
- def dispatch_from_string actor, text
138
- result = []
139
- commands = Syntax.tokenize(text, syntaxes)
140
- commands.each { |c|
141
- available = actions_for(c.verb)
142
- available.each { |a|
143
- next if a.hidden?
144
- o = a.attempt(actor, c.arguments)
145
- result.unshift o unless o.nil?
146
- }
147
- }
148
- sort_and_reduce_actions result
149
- end
150
-
151
- def dispatch_from_params actor, verb, params
152
- result = []
153
- available = actions_for(verb)
154
- available.each { |a|
155
- result.unshift a.new(actor, params) if a.valid?(actor, params)
156
- }
157
- sort_and_reduce_actions result
158
- end
159
-
160
- # Duplicate the playbook.
161
- # This method will duplicate the commands hash and the syntax array so
162
- # the new playbook can be modified without affecting the original.
163
- #
164
- # @return [Playbook]
165
- def dup
166
- Playbook.new commands: @commands.dup, syntaxes: @syntaxes.dup
167
- end
168
-
169
- def freeze
170
- @commands.freeze
171
- @syntaxes.freeze
172
- end
173
-
174
- private
175
-
176
- def add_action(action)
177
- @commands[action.verb] ||= []
178
- @commands[action.verb].push action
179
- #@commands[action.verb].uniq!
180
- #@commands[action.verb].sort! { |a, b|
181
- # b.rank <=> a.rank
182
- #}
183
- generate_default_syntax action
184
- end
185
-
186
- def generate_default_syntax action
187
- user_friendly = action.verb.to_s.gsub(/_/, ' ')
188
- args = []
189
- used_names = []
190
- action.queries.each { |c|
191
- num = 1
192
- new_name = ":var"
193
- while used_names.include? new_name
194
- num = num + 1
195
- new_name = ":var#{num}"
196
- end
197
- used_names.push new_name
198
- user_friendly += " #{new_name}"
199
- args.push new_name
200
- }
201
- add_syntax Syntax.new(user_friendly.strip, "#{action.verb} #{args.join(' ')}")
202
- end
203
-
204
- def add_syntax syntax
205
- if @commands[syntax.verb] == nil
206
- raise "No actions exist for \"#{syntax.verb}\""
207
- end
208
- @syntaxes.unshift syntax
209
- @syntaxes.uniq!
210
- @syntaxes.sort! { |a, b|
211
- if a.token_count == b.token_count
212
- # For syntaxes of the same length, length of action takes precedence
213
- b.first_word <=> a.first_word
214
- else
215
- b.token_count <=> a.token_count
216
- end
217
- }
218
- end
219
-
220
- def sort_and_reduce_actions arr
221
- arr.sort { |a,b|
222
- if a.rank == b.rank
223
- b.order_key <=> a.order_key
224
- else
225
- b.rank <=> a.rank
226
- end
227
- }.uniq{|a| a.class}
228
- end
229
-
230
- def raise_order_key
231
- @@order_key ||= 0
232
- tmp = @@order_key
233
- @@order_key += 1
234
- tmp
235
- end
236
-
237
- end
238
- end
239
-
240
- end
1
+ module Gamefic
2
+ module World
3
+ # A collection of rules for performing commands.
4
+ #
5
+ class Playbook
6
+ def initialize commands: {}, syntaxes: [], validators: [], disambiguator: nil
7
+ @commands = commands
8
+ @syntaxes = syntaxes
9
+ @validators = validators
10
+ @disambiguator = disambiguator
11
+ end
12
+
13
+ # An array of available syntaxes.
14
+ #
15
+ # @return [Array<Gamefic::Syntax>]
16
+ def syntaxes
17
+ @syntaxes
18
+ end
19
+
20
+ # An array of available actions.
21
+ #
22
+ # @return [Array<Gamefic::Action>]
23
+ def actions
24
+ @commands.values.flatten
25
+ end
26
+
27
+ # An array of recognized verbs.
28
+ #
29
+ # @return [Array<Symbol>]
30
+ def verbs
31
+ @commands.keys
32
+ end
33
+
34
+ # An array of defined validators.
35
+ #
36
+ # @return [Array<Proc>]
37
+ def validators
38
+ @validators
39
+ end
40
+
41
+ # Get the action for handling ambiguous entity references.
42
+ #
43
+ def disambiguator
44
+ @disambiguator ||= Action.subclass(nil, Query::Base.new) do |actor, entities|
45
+ definites = []
46
+ entities.each { |entity|
47
+ definites.push entity.definitely
48
+ }
49
+ actor.tell "I don't know which you mean: #{definites.join_or}."
50
+ end
51
+ end
52
+
53
+ # Set the action for handling ambiguous entity references.
54
+ #
55
+ def disambiguate &block
56
+ @disambiguator = Action.subclass(nil, Query::Base.new, meta: true, &block)
57
+ @disambiguator
58
+ end
59
+
60
+ # Add a block that determines whether an action can be executed.
61
+ #
62
+ def validate &block
63
+ @validators.push block
64
+ end
65
+
66
+ # Get an Array of all Actions associated with the specified verb.
67
+ #
68
+ # @param verb [Symbol] The Symbol for the verb (e.g., :go or :look)
69
+ # @return [Array<Action>] The verb's associated Actions
70
+ def actions_for verb
71
+ @commands[verb] || []
72
+ end
73
+
74
+ # Create an Action that responds to a command.
75
+ # An Action uses the command argument to identify the imperative verb that
76
+ # triggers the action.
77
+ # It can also accept queries to tokenize the remainder of the input and
78
+ # filter for particular entities or properties.
79
+ # The block argument contains the code to be executed when the input
80
+ # matches all of the Action's criteria (i.e., verb and queries).
81
+ #
82
+ # @example A simple Action.
83
+ # respond :salute do |actor|
84
+ # actor.tell "Hello, sir!"
85
+ # end
86
+ # # The command "salute" will respond "Hello, sir!"
87
+ #
88
+ # @example An Action that accepts a Character
89
+ # respond :salute, Use.visible(Character) do |actor, character|
90
+ # actor.tell "#{The character} returns your salute."
91
+ # end
92
+ #
93
+ # @param verb [Symbol] An imperative verb for the command
94
+ # @param queries [Array<Query::Base>] Filters for the command's tokens
95
+ # @yieldparam [Gamefic::Actor]
96
+ # @return [Class<Gamefic::Action>]
97
+ def respond(verb, *queries, &proc)
98
+ act = Action.subclass verb, *queries, &proc
99
+ add_action act
100
+ act
101
+ end
102
+
103
+ # Create a Meta Action that responds to a command.
104
+ # Meta Actions are very similar to standard Actions, except the Plot
105
+ # understands them to be commands that operate above and/or outside of the
106
+ # actual game world. Examples of Meta Actions are commands that report the
107
+ # player's current score, save and restore saved games, or list the game's
108
+ # credits.
109
+ #
110
+ # @example A simple Meta Action
111
+ # meta :credits do |actor|
112
+ # actor.tell "This game was written by John Smith."
113
+ # end
114
+ #
115
+ # @param verb [Symbol] An imperative verb for the command
116
+ # @param queries [Array<Query::Base>] Filters for the command's tokens
117
+ # @yieldparam [Gamefic::Actor]
118
+ # @return [Class<Gamefic::Action>]
119
+ def meta(verb, *queries, &proc)
120
+ act = Action.subclass verb, *queries, meta: true, &proc
121
+ add_action act
122
+ act
123
+ end
124
+
125
+ # Create an alternate Syntax for an Action.
126
+ # The command and its translation can be parameterized.
127
+ #
128
+ # @example Create a synonym for the Inventory Action.
129
+ # interpret "catalogue", "inventory"
130
+ # # The command "catalogue" will be translated to "inventory"
131
+ #
132
+ # @example Create a parameterized synonym for the Look Action.
133
+ # interpret "scrutinize :entity", "look :entity"
134
+ # # The command "scrutinize chair" will be translated to "look chair"
135
+ #
136
+ # @param input [String] The format of the original command
137
+ # @param translation [String] The format of the translated command
138
+ # @return [Syntax] the Syntax object
139
+ def interpret(input, translation)
140
+ syn = Syntax.new(input, translation)
141
+ add_syntax syn
142
+ syn
143
+ end
144
+
145
+ # Get an array of actions, derived from the specified command, that the
146
+ # actor can potentially execute.
147
+ # The command can either be a single string (e.g., "examine book") or a
148
+ # list of tokens (e.g., :examine, @book).
149
+ #
150
+ # @return [Array<Gamefic::Action>]
151
+ def dispatch(actor, *command)
152
+ 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
159
+ result
160
+ end
161
+
162
+ # Get an array of actions, derived from the specified command, that the
163
+ # actor can potentially execute.
164
+ # The command should be a plain-text string, e.g., "examine the book."
165
+ #
166
+ # @return [Array<Gamefic::Action>]
167
+ def dispatch_from_string actor, text
168
+ result = []
169
+ commands = Syntax.tokenize(text, actor.syntaxes)
170
+ commands.each { |c|
171
+ available = actions_for(c.verb)
172
+ available.each { |a|
173
+ next if a.hidden?
174
+ o = a.attempt(actor, c.arguments)
175
+ result.unshift o unless o.nil?
176
+ }
177
+ }
178
+ sort_and_reduce_actions result
179
+ end
180
+
181
+ # Get an array of actions, derived from the specified verb and params,
182
+ # that the actor can potentially execute.
183
+ #
184
+ # @return [Array<Gamefic::Action>]
185
+ def dispatch_from_params actor, verb, params
186
+ result = []
187
+ available = actions_for(verb)
188
+ available.each { |a|
189
+ result.unshift a.new(actor, params) if a.valid?(actor, params)
190
+ }
191
+ sort_and_reduce_actions result
192
+ end
193
+
194
+ # Duplicate the playbook.
195
+ # This method will duplicate the commands hash and the syntax array so
196
+ # the new playbook can be modified without affecting the original.
197
+ #
198
+ # @return [Playbook]
199
+ def dup
200
+ Playbook.new commands: @commands.dup, syntaxes: @syntaxes.dup
201
+ end
202
+
203
+ def freeze
204
+ @commands.freeze
205
+ @syntaxes.freeze
206
+ end
207
+
208
+ private
209
+
210
+ def add_action(action)
211
+ @commands[action.verb] ||= []
212
+ @commands[action.verb].push action
213
+ generate_default_syntax action
214
+ end
215
+
216
+ def generate_default_syntax action
217
+ user_friendly = action.verb.to_s.gsub(/_/, ' ')
218
+ args = []
219
+ used_names = []
220
+ action.queries.each { |c|
221
+ num = 1
222
+ new_name = ":var"
223
+ while used_names.include? new_name
224
+ num = num + 1
225
+ new_name = ":var#{num}"
226
+ end
227
+ used_names.push new_name
228
+ user_friendly += " #{new_name}"
229
+ args.push new_name
230
+ }
231
+ add_syntax Syntax.new(user_friendly.strip, "#{action.verb} #{args.join(' ')}") unless action.verb.to_s.start_with?('_')
232
+ end
233
+
234
+ def add_syntax syntax
235
+ if @commands[syntax.verb] == nil
236
+ raise "No actions exist for \"#{syntax.verb}\""
237
+ end
238
+ @syntaxes.unshift syntax
239
+ @syntaxes.uniq! &:signature
240
+ @syntaxes.sort! { |a, b|
241
+ if a.token_count == b.token_count
242
+ # For syntaxes of the same length, length of action takes precedence
243
+ b.first_word <=> a.first_word
244
+ else
245
+ b.token_count <=> a.token_count
246
+ end
247
+ }
248
+ end
249
+
250
+ def sort_and_reduce_actions arr
251
+ arr.sort_by.with_index{|a, i| [a.rank, -i]}.reverse.uniq(&:class)
252
+ end
253
+ end
254
+ end
255
+ end