gamefic 1.7.0 → 2.1.0

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