gamefic 2.3.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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec.yml +41 -0
  3. data/.rspec-opal +2 -0
  4. data/.rubocop.yml +4 -1
  5. data/.solargraph.yml +20 -3
  6. data/CHANGELOG.md +15 -0
  7. data/Gemfile +0 -4
  8. data/Rakefile +11 -1
  9. data/bin/console +14 -0
  10. data/bin/setup +8 -0
  11. data/gamefic.gemspec +5 -2
  12. data/lib/gamefic/action.rb +53 -173
  13. data/lib/gamefic/active/cue.rb +25 -0
  14. data/lib/gamefic/active/epic.rb +68 -0
  15. data/lib/gamefic/active/messaging.rb +43 -0
  16. data/lib/gamefic/active/take.rb +69 -0
  17. data/lib/gamefic/active.rb +97 -192
  18. data/lib/gamefic/actor.rb +2 -0
  19. data/lib/gamefic/block.rb +28 -0
  20. data/lib/gamefic/command.rb +16 -6
  21. data/lib/gamefic/core_ext/array.rb +4 -4
  22. data/lib/gamefic/core_ext/string.rb +10 -5
  23. data/lib/gamefic/describable.rb +39 -65
  24. data/lib/gamefic/dispatcher.rb +67 -29
  25. data/lib/gamefic/entity.rb +44 -19
  26. data/lib/gamefic/logging.rb +32 -0
  27. data/lib/gamefic/messenger.rb +66 -0
  28. data/lib/gamefic/narrative.rb +104 -0
  29. data/lib/gamefic/node.rb +44 -53
  30. data/lib/gamefic/plot.rb +60 -93
  31. data/lib/gamefic/props/default.rb +41 -0
  32. data/lib/gamefic/props/multiple_choice.rb +65 -0
  33. data/lib/gamefic/props/pause.rb +11 -0
  34. data/lib/gamefic/props/yes_or_no.rb +21 -0
  35. data/lib/gamefic/props.rb +10 -0
  36. data/lib/gamefic/query/base.rb +45 -126
  37. data/lib/gamefic/query/general.rb +46 -0
  38. data/lib/gamefic/query/result.rb +20 -0
  39. data/lib/gamefic/query/scoped.rb +41 -0
  40. data/lib/gamefic/query/text.rb +30 -31
  41. data/lib/gamefic/query.rb +7 -15
  42. data/lib/gamefic/response.rb +118 -0
  43. data/lib/gamefic/rulebook/calls.rb +90 -0
  44. data/lib/gamefic/rulebook/events.rb +79 -0
  45. data/lib/gamefic/rulebook/hooks.rb +57 -0
  46. data/lib/gamefic/rulebook/scenes.rb +68 -0
  47. data/lib/gamefic/rulebook.rb +139 -0
  48. data/lib/gamefic/scanner.rb +103 -0
  49. data/lib/gamefic/scene/activity.rb +9 -17
  50. data/lib/gamefic/scene/conclusion.rb +6 -5
  51. data/lib/gamefic/scene/default.rb +88 -0
  52. data/lib/gamefic/scene/multiple_choice.rb +14 -69
  53. data/lib/gamefic/scene/pause.rb +9 -13
  54. data/lib/gamefic/scene/yes_or_no.rb +6 -46
  55. data/lib/gamefic/scene.rb +11 -7
  56. data/lib/gamefic/scope/base.rb +44 -0
  57. data/lib/gamefic/scope/children.rb +16 -0
  58. data/lib/gamefic/scope/family.rb +20 -0
  59. data/lib/gamefic/scope/myself.rb +13 -0
  60. data/lib/gamefic/scope/parent.rb +13 -0
  61. data/lib/gamefic/scope/siblings.rb +14 -0
  62. data/lib/gamefic/scope.rb +8 -0
  63. data/lib/gamefic/scriptable/actions.rb +156 -0
  64. data/lib/gamefic/scriptable/entities.rb +76 -0
  65. data/lib/gamefic/scriptable/events.rb +65 -0
  66. data/lib/gamefic/scriptable/proxy.rb +55 -0
  67. data/lib/gamefic/scriptable/queries.rb +73 -0
  68. data/lib/gamefic/scriptable/scenes.rb +162 -0
  69. data/lib/gamefic/scriptable.rb +167 -68
  70. data/lib/gamefic/snapshot.rb +36 -0
  71. data/lib/gamefic/stage.rb +51 -0
  72. data/lib/gamefic/subplot.rb +51 -79
  73. data/lib/gamefic/syntax/template.rb +67 -0
  74. data/lib/gamefic/syntax.rb +102 -83
  75. data/lib/gamefic/vault.rb +50 -0
  76. data/lib/gamefic/version.rb +3 -1
  77. data/lib/gamefic.rb +26 -15
  78. data/spec-opal/spec_helper.rb +24 -0
  79. metadata +92 -29
  80. data/lib/gamefic/element.rb +0 -46
  81. data/lib/gamefic/keywords.rb +0 -52
  82. data/lib/gamefic/messaging.rb +0 -43
  83. data/lib/gamefic/plot/darkroom.rb +0 -120
  84. data/lib/gamefic/plot/host.rb +0 -42
  85. data/lib/gamefic/plot/snapshot.rb +0 -27
  86. data/lib/gamefic/query/children.rb +0 -9
  87. data/lib/gamefic/query/descendants.rb +0 -15
  88. data/lib/gamefic/query/external.rb +0 -39
  89. data/lib/gamefic/query/family.rb +0 -18
  90. data/lib/gamefic/query/itself.rb +0 -13
  91. data/lib/gamefic/query/matches.rb +0 -75
  92. data/lib/gamefic/query/parent.rb +0 -9
  93. data/lib/gamefic/query/siblings.rb +0 -13
  94. data/lib/gamefic/query/tree.rb +0 -17
  95. data/lib/gamefic/scene/base.rb +0 -142
  96. data/lib/gamefic/scene/multiple_scene.rb +0 -29
  97. data/lib/gamefic/serialize.rb +0 -196
  98. data/lib/gamefic/world/callbacks.rb +0 -135
  99. data/lib/gamefic/world/commands.rb +0 -173
  100. data/lib/gamefic/world/entities.rb +0 -98
  101. data/lib/gamefic/world/playbook.rb +0 -225
  102. data/lib/gamefic/world/players.rb +0 -37
  103. data/lib/gamefic/world/scenes.rb +0 -226
  104. data/lib/gamefic/world.rb +0 -18
@@ -1,225 +0,0 @@
1
- require 'set'
2
-
3
- module Gamefic
4
- module World
5
- # A collection of rules for performing commands.
6
- #
7
- class Playbook
8
- # An array of available syntaxes.
9
- #
10
- # @return [Array<Gamefic::Syntax>]
11
- attr_reader :syntaxes
12
-
13
- # An array of blocks to execute before actions.
14
- #
15
- # @return [Array<Proc>]
16
- attr_reader :before_actions
17
-
18
- # An array of blocks to execute after actions.
19
- #
20
- # @return [Array<Proc>]
21
- attr_reader :after_actions
22
-
23
- # @param commands [Hash]
24
- # @param syntaxes [Array<Syntax>, Set<Syntax>]
25
- # @param before_actions [Array]
26
- # @param after_actions [Array]
27
- def initialize commands: {}, syntaxes: [], before_actions: [], after_actions: []
28
- @commands = commands
29
- @syntax_set = syntaxes.to_set
30
- sort_syntaxes
31
- @before_actions = before_actions
32
- @after_actions = after_actions
33
- end
34
-
35
- # An array of available actions.
36
- #
37
- # @return [Array<Gamefic::Action>]
38
- def actions
39
- @commands.values.flatten
40
- end
41
-
42
- # An array of recognized verbs.
43
- #
44
- # @return [Array<Symbol>]
45
- def verbs
46
- @commands.keys
47
- end
48
-
49
- # Add a proc to be evaluated before a character executes an action.
50
- #
51
- # @yieldparam [Gamefic::Action]
52
- def before_action &block
53
- @before_actions.push block
54
- end
55
- alias validate before_action
56
-
57
- # Add a proc to be evaluated after a character executes an action.
58
- #
59
- # @yieldparam [Gamefic::Action]
60
- def after_action &block
61
- @after_actions.push block
62
- end
63
-
64
- # Get an Array of all Actions associated with the specified verb.
65
- #
66
- # @param verb [Symbol] The Symbol for the verb (e.g., :go or :look)
67
- # @return [Array<Class<Action>>] The verb's associated Actions
68
- def actions_for verb
69
- @commands[verb] || []
70
- end
71
-
72
- # Create an Action that responds to a command.
73
- # An Action uses the command argument to identify the imperative verb that
74
- # triggers the action.
75
- # It can also accept queries to tokenize the remainder of the input and
76
- # filter for particular entities or properties.
77
- # The block argument contains the code to be executed when the input
78
- # matches all of the Action's criteria (i.e., verb and queries).
79
- #
80
- # @example A simple Action.
81
- # respond :salute do |actor|
82
- # actor.tell "Hello, sir!"
83
- # end
84
- # # The command "salute" will respond "Hello, sir!"
85
- #
86
- # @example An Action that accepts a Character
87
- # respond :salute, Use.visible(Character) do |actor, character|
88
- # actor.tell "#{The character} returns your salute."
89
- # end
90
- #
91
- # @param verb [Symbol] An imperative verb for the command
92
- # @param queries [Array<Query::Base>] Filters for the command's tokens
93
- # @yieldparam [Gamefic::Actor]
94
- # @return [Class<Gamefic::Action>]
95
- def respond(verb, *queries, &proc)
96
- act = Action.subclass verb, *queries, &proc
97
- add_action act
98
- act
99
- end
100
-
101
- # Create a Meta Action that responds to a command.
102
- # Meta Actions are very similar to standard Actions, except the Plot
103
- # understands them to be commands that operate above and/or outside of the
104
- # actual game world. Examples of Meta Actions are commands that report the
105
- # player's current score, save and restore saved games, or list the game's
106
- # credits.
107
- #
108
- # @example A simple Meta Action
109
- # meta :credits do |actor|
110
- # actor.tell "This game was written by John Smith."
111
- # end
112
- #
113
- # @param verb [Symbol] An imperative verb for the command
114
- # @param queries [Array<Query::Base>] Filters for the command's tokens
115
- # @yieldparam [Gamefic::Actor]
116
- # @return [Class<Gamefic::Action>]
117
- def meta(verb, *queries, &proc)
118
- act = Action.subclass verb, *queries, meta: true, &proc
119
- add_action act
120
- act
121
- end
122
-
123
- # Create an alternate Syntax for an Action.
124
- # The command and its translation can be parameterized.
125
- #
126
- # @example Create a synonym for the Inventory Action.
127
- # interpret "catalogue", "inventory"
128
- # # The command "catalogue" will be translated to "inventory"
129
- #
130
- # @example Create a parameterized synonym for the Look Action.
131
- # interpret "scrutinize :entity", "look :entity"
132
- # # The command "scrutinize chair" will be translated to "look chair"
133
- #
134
- # @param input [String] The format of the original command
135
- # @param translation [String] The format of the translated command
136
- # @return [Syntax] the Syntax object
137
- def interpret(input, translation)
138
- syn = Syntax.new(input, translation)
139
- add_syntax syn
140
- syn
141
- end
142
-
143
- # Get a Dispatcher to select actions that can potentially be executed
144
- # from the specified command string.
145
- #
146
- # @param actor [Actor]
147
- # @param text [String]
148
- # @return [Dispatcher]
149
- def dispatch(actor, text)
150
- commands = Syntax.tokenize(text, actor.syntaxes)
151
- actions = commands.flat_map { |cmd| actions_for(cmd.verb).reject(&:hidden?) }
152
- Dispatcher.new(actor, commands, sort_and_reduce_actions(actions))
153
- end
154
-
155
- # Get an array of actions, derived from the specified verb and params,
156
- # that the actor can potentially execute.
157
- #
158
- # @return [Array<Gamefic::Action>]
159
- def dispatch_from_params actor, verb, params
160
- available = actions_for(verb)
161
- Dispatcher.new(actor, [Command.new(verb, params)], sort_and_reduce_actions(available))
162
- end
163
-
164
- # Duplicate the playbook.
165
- # This method will duplicate the commands hash and the syntax array so
166
- # the new playbook can be modified without affecting the original.
167
- #
168
- # @return [Playbook]
169
- def dup
170
- Playbook.new commands: @commands.dup, syntaxes: @syntaxes.dup
171
- end
172
-
173
- def freeze
174
- @commands.freeze
175
- @syntaxes.freeze
176
- end
177
-
178
- private
179
-
180
- def add_action(action)
181
- @commands[action.verb] ||= []
182
- @commands[action.verb].push action
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 do |_c|
191
- num = 1
192
- new_name = ":var"
193
- while used_names.include? new_name
194
- 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
- end
201
- add_syntax Syntax.new(user_friendly.strip, "#{action.verb} #{args.join(' ')}") unless action.verb.to_s.start_with?('_')
202
- end
203
-
204
- def add_syntax syntax
205
- raise "No actions exist for \"#{syntax.verb}\"" if @commands[syntax.verb].nil?
206
- sort_syntaxes if @syntax_set.add?(syntax)
207
- end
208
-
209
- def sort_and_reduce_actions arr
210
- arr.sort_by.with_index { |a, i| [a.rank, i] }.reverse.uniq
211
- end
212
-
213
- def sort_syntaxes
214
- @syntaxes = @syntax_set.sort do |a, b|
215
- if a.token_count == b.token_count
216
- # For syntaxes of the same length, sort first word
217
- b.first_word <=> a.first_word
218
- else
219
- b.token_count <=> a.token_count
220
- end
221
- end
222
- end
223
- end
224
- end
225
- end
@@ -1,37 +0,0 @@
1
- module Gamefic
2
- module World
3
- module Players
4
- include Gamefic::World::Entities
5
- include Gamefic::World::Commands
6
-
7
- # An array of entities that are currently connected to users.
8
- #
9
- # @return [Array<Gamefic::Actor>]
10
- def players
11
- @players ||= []
12
- end
13
-
14
- def player_class cls = nil
15
- STDERR.puts "Modifying player_class this way is deprecated. Use set_player_class instead" unless cls.nil?
16
- @player_class = cls unless cls.nil?
17
- @player_class ||= Gamefic::Actor
18
- end
19
-
20
- # @param cls [Class]
21
- def set_player_class cls
22
- unless cls < Gamefic::Active && cls <= Gamefic::Entity
23
- raise ArgumentError, "Player class must be an active entity"
24
- end
25
- @player_class = cls
26
- end
27
-
28
- # Make a character that a player will control on introduction.
29
- #
30
- # @return [Gamefic::Actor]
31
- def make_player_character
32
- cast player_class, name: 'yourself', synonyms: 'self myself you me', proper_named: true
33
- end
34
- alias get_player_character make_player_character
35
- end
36
- end
37
- end
@@ -1,226 +0,0 @@
1
- module Gamefic
2
- module World
3
- module Scenes
4
- # @return [Class<Gamefic::Scene::Activity>]
5
- def default_scene
6
- @default_scene ||= Scene::Activity
7
- end
8
-
9
- # @return [Class<Gamefic::Scene::Conclusion>]
10
- def default_conclusion
11
- @default_conclusion ||= Scene::Conclusion
12
- end
13
-
14
- # Add a block to be executed when a player is added to the game.
15
- # Each Plot can only have one introduction. Subsequent calls will
16
- # overwrite the existing one.
17
- #
18
- # @example Welcome the player to the game
19
- # introduction do |actor|
20
- # actor.tell "Welcome to the game!"
21
- # end
22
- #
23
- # @yieldparam [Gamefic::Actor]
24
- def introduction(&proc)
25
- @introduction = proc
26
- end
27
-
28
- # Introduce a player to the game.
29
- # This method is typically called by the Engine that manages game execution.
30
- #
31
- # @param [Gamefic::Actor]
32
- def introduce(player)
33
- player.playbooks.push playbook unless player.playbooks.include?(playbook)
34
- player.cue default_scene
35
- players.push player
36
- @introduction.call(player) unless @introduction.nil?
37
- # @todo Find a better way to persist player characters
38
- # Gamefic::Index.stick
39
- end
40
-
41
- # Create a multiple-choice scene.
42
- # The user will be required to make a valid choice to continue.
43
- #
44
- # @example
45
- # @scene = multiple_choice 'Go to work', 'Go to school' do |actor, scene|
46
- # # Assuming user selected the first choice:
47
- # scene.selection #=> 'Go to work'
48
- # scene.index #=> 0
49
- # scene.number #=> 1
50
- # end
51
- #
52
- # @yieldparam [Gamefic::Actor]
53
- # @yieldparam [Gamefic::Scene::MultipleChoice]
54
- # @return [Class<Gamefic::Scene::MultipleChoice>]
55
- def multiple_choice *choices, &block
56
- s = Scene::MultipleChoice.subclass do |actor, scene|
57
- scene.options.concat choices
58
- scene.on_finish &block
59
- end
60
- scene_classes.push s
61
- s
62
- end
63
-
64
- # Create a yes-or-no scene.
65
- # The user will be required to answer Yes or No to continue.
66
- #
67
- # @example
68
- # @scene = yes_or_no 'What is your answer?' do |actor, scene|
69
- # if scene.yes?
70
- # actor.tell "You said yes."
71
- # else
72
- # actor.tell "You said no."
73
- # end
74
- # end
75
- #
76
- # @param prompt [String]
77
- # @yieldparam [Gamefic::Actor]
78
- # @yieldparam [Gamefic::Scene::YesOrNo]
79
- # @return [Class<Gamefic::Scene::YesOrNo>]
80
- def yes_or_no prompt = nil, &block
81
- s = Scene::YesOrNo.subclass do |actor, scene|
82
- scene.prompt = prompt
83
- scene.on_finish &block
84
- end
85
- scene_classes.push s
86
- s
87
- end
88
-
89
- # Create a scene with custom processing on user input.
90
- #
91
- # @example Echo the user's response
92
- # @scene = question 'What do you say?' do |actor, scene|
93
- # actor.tell "You said #{scene.input}"
94
- # end
95
- #
96
- # @param prompt [String]
97
- # @yieldparam [Gamefic::Actor]
98
- # @yieldparam [Gamefic::Scene::Base]
99
- # @return [Class<Gamefic::Scene::Base>]
100
- def question prompt = 'What is your answer?', &block
101
- s = Scene::Base.subclass do |actor, scene|
102
- scene.prompt = prompt
103
- scene.on_finish &block
104
- end
105
- scene_classes.push s
106
- s
107
- end
108
-
109
- # Create a scene that pauses the game.
110
- # This scene will execute the specified block and wait for input from the
111
- # the user (e.g., pressing Enter) to continue.
112
- #
113
- # @example
114
- # @scene = pause 'Continue' do |actor|
115
- # actor.tell "After you continue, you will be prompted for a command."
116
- # actor.prepare default_scene
117
- # end
118
- #
119
- # @param prompt [String] The text to display when prompting the user to continue.
120
- # @yieldparam [Gamefic::Actor]
121
- # @return [Class<Gamefic::Scene::Pause>]
122
- def pause prompt = nil, &block
123
- s = Scene::Pause.subclass do |actor, scene|
124
- scene.prompt = prompt unless prompt.nil?
125
- block.call(actor, scene) unless block.nil?
126
- end
127
- scene_classes.push s
128
- s
129
- end
130
-
131
- # Create a conclusion.
132
- # The game (or the character's participation in it) will end after this
133
- # scene is complete.
134
- #
135
- # @example
136
- # @scene = conclusion do |actor|
137
- # actor.tell 'Game over'
138
- # end
139
- #
140
- # @yieldparam [Gamefic::Actor]
141
- # @return [Class<Gamefic::Scene::Conclusion>]
142
- def conclusion &block
143
- s = Scene::Conclusion.subclass &block
144
- scene_classes.push s
145
- s
146
- end
147
-
148
- # Create a custom scene.
149
- #
150
- # Custom scenes should always specify the next scene to be cued or
151
- # prepared. If not, the scene will get repeated on the next turn.
152
- #
153
- # This method creates a Scene::Base by default. You can customize other
154
- # scene types by specifying the class to create.
155
- #
156
- # @example Ask the user for a name
157
- # @scene = custom do |actor, scene|
158
- # scene.prompt = "What's your name?"
159
- # scene.on_finish do
160
- # actor.name = scene.input
161
- # actor.tell "Hello, #{actor.name}!"
162
- # actor.cue default_scene
163
- # end
164
- # end
165
- #
166
- # @param cls [Class] The class of scene to be instantiated.
167
- # @yieldparam [Gamefic::Actor]
168
- # @return [Class<Gamefic::Scene::Base>]
169
- def custom cls = Scene::Base, &block
170
- s = cls.subclass &block
171
- scene_classes.push s
172
- s
173
- end
174
-
175
- # Choose a new scene based on a list of options.
176
- # This is a specialized type of multiple-choice scene that determines
177
- # which scene to cue based on a Hash of choices and scene keys.
178
- #
179
- # @example Select a scene
180
- # scene_one = pause do |actor|
181
- # actor.tell "You went to scene one"
182
- # end
183
- #
184
- # scene_two = pause do |actor|
185
- # actor.tell "You went to scene two"
186
- # end
187
- #
188
- # select_one_or_two = multiple_scene "One" => scene_one, "Two" => scene_two
189
- #
190
- # introduction do |actor|
191
- # actor.cue select_one_or_two # The actor will be prompted to select "one" or "two" and get sent to the corresponding scene
192
- # end
193
- #
194
- # @example Customize options
195
- # scene_one = pause # do...
196
- # scene_two = pause # do...
197
- #
198
- # # Some event in the game sets actor[:can_go_to_scene_two] to true
199
- #
200
- # select_one_or_two = multiple_scene do |actor, scene|
201
- # scene.map "Go to scene one", scene_one
202
- # scene.map "Go to scene two", scene_two if actor[:can_go_to_scene_two]
203
- # end
204
- #
205
- # @param map [Hash] A Hash of options and associated scenes.
206
- # @yieldparam [Gamefic::Actor]
207
- # @yieldparam [Gamefic::Scene::MultipleScene]
208
- # @return [Class<Gamefic::Scene::MultipleScene>]
209
- def multiple_scene map = {}, &block
210
- s = Scene::MultipleScene.subclass do |actor, scene|
211
- map.each_pair { |k, v|
212
- scene.map k, v
213
- }
214
- block.call actor, scene unless block.nil?
215
- end
216
- scene_classes.push s
217
- s
218
- end
219
-
220
- # @return [Array<Class<Gamefic::Scene::Base>>]
221
- def scene_classes
222
- @scene_classes ||= []
223
- end
224
- end
225
- end
226
- end
data/lib/gamefic/world.rb DELETED
@@ -1,18 +0,0 @@
1
- module Gamefic
2
- # A collection of classes and modules related to generating a world model.
3
- #
4
- module World
5
- autoload :Playbook, 'gamefic/world/playbook'
6
- autoload :Entities, 'gamefic/world/entities'
7
- autoload :Commands, 'gamefic/world/commands'
8
- autoload :Callbacks, 'gamefic/world/callbacks'
9
- autoload :Scenes, 'gamefic/world/scenes'
10
- autoload :Players, 'gamefic/world/players'
11
-
12
- include Entities
13
- include Commands
14
- include Callbacks
15
- include Scenes
16
- include Players
17
- end
18
- end