gamefic 2.4.0 → 3.1.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 (105) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec.yml +41 -40
  3. data/.rspec-opal +2 -0
  4. data/.solargraph.yml +20 -3
  5. data/CHANGELOG.md +18 -0
  6. data/Rakefile +11 -1
  7. data/bin/console +14 -0
  8. data/bin/setup +8 -0
  9. data/gamefic.gemspec +5 -2
  10. data/lib/gamefic/action.rb +58 -181
  11. data/lib/gamefic/active/cue.rb +25 -0
  12. data/lib/gamefic/active/epic.rb +73 -0
  13. data/lib/gamefic/active/messaging.rb +43 -0
  14. data/lib/gamefic/active/take.rb +69 -0
  15. data/lib/gamefic/active.rb +111 -191
  16. data/lib/gamefic/actor.rb +2 -0
  17. data/lib/gamefic/block.rb +28 -0
  18. data/lib/gamefic/command.rb +6 -7
  19. data/lib/gamefic/composer.rb +68 -0
  20. data/lib/gamefic/core_ext/array.rb +4 -4
  21. data/lib/gamefic/core_ext/string.rb +10 -5
  22. data/lib/gamefic/describable.rb +39 -65
  23. data/lib/gamefic/dispatcher.rb +79 -41
  24. data/lib/gamefic/entity.rb +42 -19
  25. data/lib/gamefic/expression.rb +31 -0
  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 +49 -0
  32. data/lib/gamefic/props/multiple_choice.rb +65 -0
  33. data/lib/gamefic/props/output.rb +82 -0
  34. data/lib/gamefic/props/pause.rb +11 -0
  35. data/lib/gamefic/props/yes_or_no.rb +21 -0
  36. data/lib/gamefic/props.rb +11 -0
  37. data/lib/gamefic/query/base.rb +64 -121
  38. data/lib/gamefic/query/general.rb +50 -0
  39. data/lib/gamefic/query/result.rb +20 -0
  40. data/lib/gamefic/query/scoped.rb +46 -0
  41. data/lib/gamefic/query/text.rb +43 -33
  42. data/lib/gamefic/query.rb +7 -15
  43. data/lib/gamefic/response.rb +112 -0
  44. data/lib/gamefic/rulebook/calls.rb +90 -0
  45. data/lib/gamefic/rulebook/events.rb +79 -0
  46. data/lib/gamefic/rulebook/hooks.rb +57 -0
  47. data/lib/gamefic/rulebook/scenes.rb +68 -0
  48. data/lib/gamefic/rulebook.rb +139 -0
  49. data/lib/gamefic/scanner.rb +130 -0
  50. data/lib/gamefic/scene/activity.rb +9 -17
  51. data/lib/gamefic/scene/conclusion.rb +6 -5
  52. data/lib/gamefic/scene/default.rb +88 -0
  53. data/lib/gamefic/scene/multiple_choice.rb +14 -69
  54. data/lib/gamefic/scene/pause.rb +9 -13
  55. data/lib/gamefic/scene/yes_or_no.rb +6 -46
  56. data/lib/gamefic/scene.rb +11 -7
  57. data/lib/gamefic/scope/base.rb +44 -0
  58. data/lib/gamefic/scope/children.rb +16 -0
  59. data/lib/gamefic/scope/family.rb +20 -0
  60. data/lib/gamefic/scope/myself.rb +13 -0
  61. data/lib/gamefic/scope/parent.rb +13 -0
  62. data/lib/gamefic/scope/siblings.rb +14 -0
  63. data/lib/gamefic/scope.rb +8 -0
  64. data/lib/gamefic/scriptable/actions.rb +156 -0
  65. data/lib/gamefic/scriptable/entities.rb +79 -0
  66. data/lib/gamefic/scriptable/events.rb +65 -0
  67. data/lib/gamefic/scriptable/proxy.rb +66 -0
  68. data/lib/gamefic/scriptable/queries.rb +73 -0
  69. data/lib/gamefic/scriptable/scenes.rb +162 -0
  70. data/lib/gamefic/scriptable.rb +167 -73
  71. data/lib/gamefic/snapshot.rb +44 -0
  72. data/lib/gamefic/stage.rb +51 -0
  73. data/lib/gamefic/subplot.rb +51 -79
  74. data/lib/gamefic/syntax/template.rb +67 -0
  75. data/lib/gamefic/syntax.rb +102 -83
  76. data/lib/gamefic/vault.rb +50 -0
  77. data/lib/gamefic/version.rb +1 -1
  78. data/lib/gamefic.rb +28 -15
  79. data/spec-opal/spec_helper.rb +24 -0
  80. metadata +94 -29
  81. data/lib/gamefic/element.rb +0 -46
  82. data/lib/gamefic/keywords.rb +0 -52
  83. data/lib/gamefic/messaging.rb +0 -43
  84. data/lib/gamefic/plot/darkroom.rb +0 -120
  85. data/lib/gamefic/plot/host.rb +0 -42
  86. data/lib/gamefic/plot/snapshot.rb +0 -27
  87. data/lib/gamefic/query/children.rb +0 -9
  88. data/lib/gamefic/query/descendants.rb +0 -15
  89. data/lib/gamefic/query/external.rb +0 -39
  90. data/lib/gamefic/query/family.rb +0 -18
  91. data/lib/gamefic/query/itself.rb +0 -13
  92. data/lib/gamefic/query/matches.rb +0 -75
  93. data/lib/gamefic/query/parent.rb +0 -9
  94. data/lib/gamefic/query/siblings.rb +0 -13
  95. data/lib/gamefic/query/tree.rb +0 -17
  96. data/lib/gamefic/scene/base.rb +0 -142
  97. data/lib/gamefic/scene/multiple_scene.rb +0 -29
  98. data/lib/gamefic/serialize.rb +0 -196
  99. data/lib/gamefic/world/callbacks.rb +0 -135
  100. data/lib/gamefic/world/commands.rb +0 -181
  101. data/lib/gamefic/world/entities.rb +0 -98
  102. data/lib/gamefic/world/playbook.rb +0 -233
  103. data/lib/gamefic/world/players.rb +0 -37
  104. data/lib/gamefic/world/scenes.rb +0 -228
  105. data/lib/gamefic/world.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2288262e86ac4a5cf259b3a37e80ba56a3a4ed334b8842770109e1930799585d
4
- data.tar.gz: 542aaff9c3f4d0aae1a4471fba5d52ff35b569971c861af006ddc78f0d1cc568
3
+ metadata.gz: 3900923aaef12a43321ce6f9cc5957953b12bba7d939e0a1666d8c6c75703855
4
+ data.tar.gz: 6023310c5e26e9632ed4c053a9bea619e1455dbb66966d82fc9e772044d19d3a
5
5
  SHA512:
6
- metadata.gz: 6a89c7fb5978047517827e3cc2de5f172da5e6905a7e7efe0725763b4ce01d56725df46a443b903906effd962cebdbd238d81719b315eb26f3b8bf289d337089
7
- data.tar.gz: 9976d0c58957309ba605e645a8c7e214d798c12e114b71e5d5fa2bb4b6d39878537612e670da6e42168dfd31ee5fce7304eee9983d1177d878262af630e6e582
6
+ metadata.gz: aea9a57211541f056c730707227020f2c35376d59cc4e69c3c7463e8a8bafa7b6fcd4585a99bf2ce235fdc4c583393f53e060554e06f6e5912358959305627eb
7
+ data.tar.gz: 5351ea7cc8b3ff4e01e064eb12a594dc2d928641b19e8593243da00b672780acff0d808d73eca1a80cf2e2139fd657b870fa7a5a5315e0a2a3998bc306db0c05
@@ -1,40 +1,41 @@
1
- # This workflow uses actions that are not certified by GitHub.
2
- # They are provided by a third-party and are governed by
3
- # separate terms of service, privacy policy, and support
4
- # documentation.
5
- # This workflow will download a prebuilt Ruby version, install dependencies and run tests with rspec.
6
- # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
-
8
- name: RSpec
9
-
10
- on:
11
- push:
12
- branches: [ master ]
13
- pull_request:
14
- branches: [ master ]
15
-
16
- permissions:
17
- contents: read
18
-
19
- jobs:
20
- test:
21
-
22
- runs-on: ubuntu-latest
23
- strategy:
24
- matrix:
25
- ruby-version: ['2.5', '2.6', '2.7', '3.0', '3.1']
26
-
27
- steps:
28
- - uses: actions/checkout@v3
29
- - name: Set up Ruby
30
- # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
31
- # change this to (see https://github.com/ruby/setup-ruby#versioning):
32
- uses: ruby/setup-ruby@v1
33
- # uses: ruby/setup-ruby@2b019609e2b0f1ea1a2bc8ca11cb82ab46ada124
34
- with:
35
- ruby-version: ${{ matrix.ruby-version }}
36
- bundler-cache: false
37
- - name: Install dependencies
38
- run: bundle install
39
- - name: Run tests
40
- run: bundle exec rspec
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with rspec.
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: RSpec
9
+
10
+ on:
11
+ push:
12
+ branches: [ master ]
13
+ pull_request:
14
+ branches: [ master ]
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ test:
21
+
22
+ runs-on: ubuntu-latest
23
+ strategy:
24
+ matrix:
25
+ ruby-version: ['2.7', '3.0', '3.1']
26
+
27
+ steps:
28
+ - uses: actions/checkout@v3
29
+ - name: Set up Ruby
30
+ uses: ruby/setup-ruby@v1
31
+ with:
32
+ ruby-version: ${{ matrix.ruby-version }}
33
+ bundler-cache: false
34
+ - name: Set up Node
35
+ uses: actions/setup-node@v3
36
+ - name: Install dependencies
37
+ run: bundle install
38
+ - name: Run Ruby tests
39
+ run: bundle exec rspec
40
+ - name: Run Opal tests
41
+ run: bundle exec rake opal
data/.rspec-opal ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.solargraph.yml CHANGED
@@ -1,5 +1,22 @@
1
+ ---
1
2
  include:
2
- - lib/**/*.rb
3
+ - "**/*.rb"
3
4
  exclude:
4
- - spec/**/*
5
- - test/**/*
5
+ - spec/**/*
6
+ - test/**/*
7
+ - vendor/**/*
8
+ - ".bundle/**/*"
9
+ require: []
10
+ domains: []
11
+ reporters:
12
+ - rubocop
13
+ - require_not_found
14
+ formatter:
15
+ rubocop:
16
+ cops: safe
17
+ except: []
18
+ only: []
19
+ extra_args: []
20
+ require_paths: []
21
+ plugins: []
22
+ max_files: 5000
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 3.1.0 - April 8, 2024
2
+ - Dispatcher prioritizes strict token matches
3
+ - Scanner builds commands
4
+ - Tokenize expressions and execute commands
5
+ - Delete concluded subplots last in Plot#ready
6
+ - Fix plot conclusion check after subplots conclude
7
+ - Correct contexts for conclude and output blocks
8
+ - Reinstate Active#last_input
9
+
10
+ ## 3.0.0 - January 27, 2024
11
+ - Instantiate subplots from snapshots
12
+ - Split Action into Response and Action
13
+ - Logging
14
+ - Remove deprecated Active#perform behavior
15
+ - Snapshots use single static index
16
+ - Hydration improvements
17
+ - Snapshot metadata validation
18
+
1
19
  ## 2.4.0 - February 11, 2023
2
20
  - Fix arity of delegated methods in scripts
3
21
  - Action hooks accept verb filters
data/Rakefile CHANGED
@@ -1,5 +1,9 @@
1
- require 'rake'
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
2
5
  require 'rspec/core/rake_task'
6
+ require 'opal/rspec/rake_task'
3
7
 
4
8
  RSpec::Core::RakeTask.new(:spec) do |t|
5
9
  t.pattern = Dir.glob('spec/**/*_spec.rb')
@@ -8,3 +12,9 @@ RSpec::Core::RakeTask.new(:spec) do |t|
8
12
  #t.rcov = true
9
13
  end
10
14
  task :default => :spec
15
+
16
+ Opal::RSpec::RakeTask.new(:opal) do |_, config|
17
+ Opal.append_path File.expand_path('../lib', __FILE__)
18
+ config.pattern = 'spec/**/*_spec.rb'
19
+ config.requires = ['spec_helper']
20
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "gamefic-sdk"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/gamefic.gemspec CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
11
11
  s.description = "An adventure game and interactive fiction framework"
12
12
  s.authors = ["Fred Snyder"]
13
13
  s.email = 'fsnyder@gamefic.com'
14
- s.homepage = 'http://gamefic.com'
14
+ s.homepage = 'https://gamefic.com'
15
15
  s.license = 'MIT'
16
16
 
17
17
  s.files = Dir.chdir(File.expand_path('..', __FILE__)) do
@@ -19,8 +19,11 @@ Gem::Specification.new do |s|
19
19
  end
20
20
  s.require_paths = ['lib']
21
21
 
22
- s.required_ruby_version = '>= 2.3.0'
22
+ s.required_ruby_version = '>= 2.7.0'
23
23
 
24
+ s.add_development_dependency 'opal', '~> 1.7'
25
+ s.add_development_dependency 'opal-rspec', '~> 1.0'
26
+ s.add_development_dependency 'opal-sprockets', '~> 1.0'
24
27
  s.add_development_dependency 'rake', '~> 12.3', '>= 12.3'
25
28
  s.add_development_dependency 'rspec', '~> 3.5', '>= 3.5.0'
26
29
  s.add_development_dependency 'simplecov', '~> 0.14'
@@ -1,213 +1,90 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gamefic
4
+ # The handler for executing responses for a provided actor and array of
5
+ # arguments. It's also responsible for executing before_action and
6
+ # after_action hooks if necessary.
7
+ #
2
8
  class Action
3
- # @return [Gamefic::Actor]
4
- attr_reader :actor
9
+ include Logging
5
10
 
6
- # An array of objects on which the action will operate, e.g., an entity
7
- # that is a direct object of a command.
11
+ # Hooks are blocks of code that get executed before or after an actor
12
+ # performs an action. A before action hook is capable of cancelling the
13
+ # action's performance.
8
14
  #
9
- # @return [Array<Object>]
15
+ class Hook
16
+ # @param [Array<Symbol>]
17
+ attr_reader :verbs
18
+
19
+ # @param [Proc]
20
+ attr_reader :block
21
+
22
+ def initialize *verbs, &block
23
+ @verbs = verbs
24
+ @block = block
25
+ end
26
+
27
+ def match?(input)
28
+ verbs.empty? || verbs.include?(input)
29
+ end
30
+ end
31
+
32
+ # @return [Active]
33
+ attr_reader :actor
34
+
35
+ # @return [Array]
10
36
  attr_reader :arguments
11
- alias parameters arguments
12
37
 
13
- # @param actor [Gamefic::Actor]
14
- # @param arguments [Array<Object>]
15
- def initialize actor, arguments, with_callbacks = false
38
+ # @return [Response]
39
+ attr_reader :response
40
+
41
+ # @param actor [Active]
42
+ # @param arguments [Array]
43
+ # @param response [Response]
44
+ def initialize actor, arguments, response
16
45
  @actor = actor
17
46
  @arguments = arguments
18
- @executed = false
19
- @with_callbacks = with_callbacks
47
+ @response = response
20
48
  end
21
49
 
22
- # Perform the action.
23
- #
50
+ # @return [self]
24
51
  def execute
25
- return if @cancelled
26
- run_before_actions
27
- return if @cancelled
28
- self.class.executor.call(@actor, *arguments) unless self.class.executor.nil?
52
+ return self if cancelled? || executed?
53
+
29
54
  @executed = true
30
- run_after_actions
55
+ response.execute actor, *arguments
56
+ self
31
57
  end
32
58
 
33
- # Cancel an action. This method can be called in a before_action hook to
34
- # prevent subsequent hooks and the action itself from being executed.
35
- # Cancelling an action in an after_action hook has no effect.
59
+ # True if the response has been executed. False typically means that the
60
+ # #execute method has not been called or the action was cancelled in a
61
+ # before_action hook.
36
62
  #
37
- def cancel
38
- # @todo Emit a warning for attempts to cancel an action after it's been
39
- # executed
40
- @cancelled = true
63
+ def executed?
64
+ @executed ||= false
41
65
  end
42
66
 
43
- # True if the #execute method has been called for this action.
67
+ # Cancel an action. This method can be called in an action hook to
68
+ # prevent subsequent hooks and/or the action itself from being executed.
44
69
  #
45
- # @return [Boolean]
46
- def executed?
47
- @executed
70
+ def cancel
71
+ @cancelled = true
48
72
  end
49
73
 
50
74
  def cancelled?
51
- !@executed && @cancelled
75
+ @cancelled ||= false
52
76
  end
53
77
 
54
- # The verb associated with this action.
55
- #
56
- # @return [Symbol] The symbol representing the verb
57
78
  def verb
58
- self.class.verb
79
+ response.verb
59
80
  end
60
81
 
61
- def signature
62
- self.class.signature
82
+ def narrative
83
+ response.narrative
63
84
  end
64
85
 
65
- def rank
66
- self.class.rank
67
- end
68
-
69
- # True if the action is flagged as meta.
70
- #
71
- # @return [Boolean]
72
86
  def meta?
73
- self.class.meta?
74
- end
75
-
76
- # @param verb [Symbol]
77
- # @param queries [Array<Gamefic::Query::Base>]
78
- # @param meta [Boolean]
79
- # @return [Class<Action>]
80
- def self.subclass verb, *queries, meta: false, &block
81
- act = Class.new(self) do
82
- self.verb = verb
83
- self.meta = meta
84
- queries.each do |q|
85
- add_query q
86
- end
87
- on_execute &block
88
- end
89
- if !block.nil? && act.queries.length + 1 != block.arity && block.arity > 0
90
- raise ArgumentError.new("Number of parameters is not compatible with proc arguments")
91
- end
92
- act
93
- end
94
-
95
- private
96
-
97
- def run_before_actions
98
- return unless @with_callbacks
99
- @actor.playbooks
100
- .flat_map(&:before_actions)
101
- .each do |hook|
102
- next unless hook.verb.nil? || hook.verb == verb
103
- hook.block.call(self)
104
- break if @cancelled
105
- end
106
- end
107
-
108
- def run_after_actions
109
- return unless @with_callbacks
110
- @actor.playbooks
111
- .flat_map(&:after_actions)
112
- .each do |hook|
113
- next unless hook.verb.nil? || hook.verb == verb
114
- hook.block.call(self)
115
- end
116
- end
117
-
118
- class << self
119
- attr_reader :verb
120
-
121
- # The proc to call when the action is executed
122
- #
123
- # @return [Proc]
124
- attr_reader :executor
125
-
126
- def meta?
127
- @meta ||= false
128
- end
129
-
130
- def add_query q
131
- @specificity = nil
132
- queries.push q
133
- end
134
-
135
- def queries
136
- @queries ||= []
137
- end
138
-
139
- def on_execute &block
140
- @executor = block
141
- end
142
-
143
- def signature
144
- # @todo This is clearly unfinished
145
- "#{verb} #{queries.map {|m| m.signature}.join(', ')}"
146
- end
147
-
148
- # True if this action is not intended to be performed directly by a
149
- # character.
150
- # If the action is hidden, users should not be able to perform it with a
151
- # direct command. By default, any action whose verb starts with an
152
- # underscore is hidden.
153
- #
154
- # @return [Boolean]
155
- def hidden?
156
- verb.to_s.start_with?('_')
157
- end
158
-
159
- # @return [Integer]
160
- def rank
161
- if @rank.nil?
162
- @rank = 0
163
- queries.each do |q|
164
- @rank += (q.rank + 1)
165
- end
166
- @rank -= 1000 if verb.nil?
167
- end
168
- @rank
169
- end
170
-
171
- def valid? actor, objects
172
- return false if objects.length != queries.length
173
- queries.each_with_index do |p, i|
174
- return false unless p.include?(actor, objects[i])
175
- end
176
- true
177
- end
178
-
179
- # Return an instance of this Action if the actor can execute it with the
180
- # provided tokens, or nil if the tokens are invalid.
181
- #
182
- # @param action [Gamefic::Entity]
183
- # @param command [Command]
184
- # @return [self, nil]
185
- def attempt actor, command, with_callbacks = false
186
- return nil if command.verb != verb
187
- tokens = command.arguments
188
- result = []
189
- matches = Gamefic::Query::Matches.new([], '', '')
190
- queries.each_with_index do |p, i|
191
- return nil if tokens[i].nil? && matches.remaining == ''
192
- matches = p.resolve(actor, "#{matches.remaining} #{tokens[i]}".strip, continued: (i < queries.length - 1))
193
- return nil if matches.objects.empty?
194
- accepted = matches.objects.select { |o| p.accept?(o) }
195
- return nil if accepted.empty?
196
- if p.ambiguous?
197
- result.push accepted
198
- else
199
- return nil if accepted.length != 1
200
- result.push accepted.first
201
- end
202
- end
203
- new(actor, result, with_callbacks)
204
- end
205
-
206
- protected
207
-
208
- attr_writer :verb
209
-
210
- attr_writer :meta
87
+ response.meta?
211
88
  end
212
89
  end
213
90
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gamefic
4
+ module Active
5
+ # The data that actors use to configure a Take.
6
+ #
7
+ class Cue
8
+ # @return [Symbol]
9
+ attr_reader :scene
10
+
11
+ # @return [Hash]
12
+ attr_reader :context
13
+
14
+ # @param scene [Symbol]
15
+ def initialize scene, **context
16
+ @scene = scene
17
+ @context = context
18
+ end
19
+
20
+ def to_s
21
+ scene.to_s
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gamefic
4
+ module Active
5
+ # A collection of narratives.
6
+ #
7
+ class Epic
8
+ include Logging
9
+
10
+ # @return [Set<Narrative>]
11
+ attr_reader :narratives
12
+
13
+ def initialize
14
+ @narratives = Set.new
15
+ end
16
+
17
+ # @param narrative [Narrative]
18
+ def add narrative
19
+ narratives.add narrative
20
+ end
21
+
22
+ # @param narrative [Narrative]
23
+ def delete narrative
24
+ narratives.delete narrative
25
+ end
26
+
27
+ # @return [Array<Rulebook>]
28
+ def rulebooks
29
+ narratives.map(&:rulebook)
30
+ end
31
+
32
+ # @return [Array<Symbol>]
33
+ def verbs
34
+ rulebooks.flat_map(&:verbs).uniq
35
+ end
36
+
37
+ # @return [Array<Symbol>]
38
+ def synonyms
39
+ rulebooks.flat_map(&:synonyms).uniq
40
+ end
41
+
42
+ def empty?
43
+ narratives.empty?
44
+ end
45
+
46
+ def one?
47
+ narratives.one?
48
+ end
49
+
50
+ def syntaxes
51
+ rulebooks.flat_map(&:syntaxes)
52
+ end
53
+
54
+ def responses_for(*verbs)
55
+ rulebooks.to_a
56
+ .reverse
57
+ .flat_map { |rb| rb.responses_for(*verbs) }
58
+ end
59
+
60
+ # @param name [Symbol]
61
+ # @return [Scene]
62
+ def select_scene name
63
+ scenes = rulebooks.map { |rlbk| rlbk.scenes[name] }
64
+ .compact
65
+ raise ArgumentError, "Scene named `#{name}` does not exist" if scenes.empty?
66
+
67
+ logger.warn "Found #{scenes.length} scenes named `#{name}`" unless scenes.one?
68
+
69
+ scenes.last
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,43 @@
1
+ module Gamefic
2
+ module Active
3
+ # A module for active entities that provides a default Messenger with
4
+ # a few shortcuts.
5
+ #
6
+ module Messaging
7
+ def messenger
8
+ @messenger ||= Messenger.new
9
+ end
10
+
11
+ # Send a message to the entity.
12
+ #
13
+ # This method will automatically wrap the message in HTML paragraphs.
14
+ # To send a message without paragraph formatting, use #stream instead.
15
+ #
16
+ # @param message [String]
17
+ def tell(message)
18
+ messenger.tell message
19
+ end
20
+
21
+ # Send a message to the entity as raw text.
22
+ #
23
+ # Unlike #tell, this method will not wrap the message in HTML paragraphs.
24
+ #
25
+ # @param message [String]
26
+ def stream(message)
27
+ messenger.stream message
28
+ end
29
+
30
+ def messages
31
+ messenger.messages
32
+ end
33
+
34
+ def buffer &block
35
+ messenger.buffer(&block)
36
+ end
37
+
38
+ def flush
39
+ messenger.flush
40
+ end
41
+ end
42
+ end
43
+ end