gamefic 2.4.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) 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 +9 -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 +52 -183
  11. data/lib/gamefic/active/cue.rb +25 -0
  12. data/lib/gamefic/active/epic.rb +68 -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 +95 -192
  16. data/lib/gamefic/actor.rb +2 -0
  17. data/lib/gamefic/block.rb +28 -0
  18. data/lib/gamefic/command.rb +16 -6
  19. data/lib/gamefic/core_ext/array.rb +4 -4
  20. data/lib/gamefic/core_ext/string.rb +10 -5
  21. data/lib/gamefic/describable.rb +39 -65
  22. data/lib/gamefic/dispatcher.rb +63 -32
  23. data/lib/gamefic/entity.rb +44 -19
  24. data/lib/gamefic/logging.rb +32 -0
  25. data/lib/gamefic/messenger.rb +66 -0
  26. data/lib/gamefic/narrative.rb +104 -0
  27. data/lib/gamefic/node.rb +44 -53
  28. data/lib/gamefic/plot.rb +60 -93
  29. data/lib/gamefic/props/default.rb +41 -0
  30. data/lib/gamefic/props/multiple_choice.rb +65 -0
  31. data/lib/gamefic/props/pause.rb +11 -0
  32. data/lib/gamefic/props/yes_or_no.rb +21 -0
  33. data/lib/gamefic/props.rb +10 -0
  34. data/lib/gamefic/query/base.rb +45 -126
  35. data/lib/gamefic/query/general.rb +46 -0
  36. data/lib/gamefic/query/result.rb +20 -0
  37. data/lib/gamefic/query/scoped.rb +41 -0
  38. data/lib/gamefic/query/text.rb +30 -31
  39. data/lib/gamefic/query.rb +7 -15
  40. data/lib/gamefic/response.rb +118 -0
  41. data/lib/gamefic/rulebook/calls.rb +90 -0
  42. data/lib/gamefic/rulebook/events.rb +79 -0
  43. data/lib/gamefic/rulebook/hooks.rb +57 -0
  44. data/lib/gamefic/rulebook/scenes.rb +68 -0
  45. data/lib/gamefic/rulebook.rb +139 -0
  46. data/lib/gamefic/scanner.rb +103 -0
  47. data/lib/gamefic/scene/activity.rb +9 -17
  48. data/lib/gamefic/scene/conclusion.rb +6 -5
  49. data/lib/gamefic/scene/default.rb +88 -0
  50. data/lib/gamefic/scene/multiple_choice.rb +14 -69
  51. data/lib/gamefic/scene/pause.rb +9 -13
  52. data/lib/gamefic/scene/yes_or_no.rb +6 -46
  53. data/lib/gamefic/scene.rb +11 -7
  54. data/lib/gamefic/scope/base.rb +44 -0
  55. data/lib/gamefic/scope/children.rb +16 -0
  56. data/lib/gamefic/scope/family.rb +20 -0
  57. data/lib/gamefic/scope/myself.rb +13 -0
  58. data/lib/gamefic/scope/parent.rb +13 -0
  59. data/lib/gamefic/scope/siblings.rb +14 -0
  60. data/lib/gamefic/scope.rb +8 -0
  61. data/lib/gamefic/scriptable/actions.rb +156 -0
  62. data/lib/gamefic/scriptable/entities.rb +76 -0
  63. data/lib/gamefic/scriptable/events.rb +65 -0
  64. data/lib/gamefic/scriptable/proxy.rb +55 -0
  65. data/lib/gamefic/scriptable/queries.rb +73 -0
  66. data/lib/gamefic/scriptable/scenes.rb +162 -0
  67. data/lib/gamefic/scriptable.rb +167 -73
  68. data/lib/gamefic/snapshot.rb +36 -0
  69. data/lib/gamefic/stage.rb +51 -0
  70. data/lib/gamefic/subplot.rb +51 -79
  71. data/lib/gamefic/syntax/template.rb +67 -0
  72. data/lib/gamefic/syntax.rb +102 -83
  73. data/lib/gamefic/vault.rb +50 -0
  74. data/lib/gamefic/version.rb +1 -1
  75. data/lib/gamefic.rb +26 -15
  76. data/spec-opal/spec_helper.rb +24 -0
  77. metadata +91 -29
  78. data/lib/gamefic/element.rb +0 -46
  79. data/lib/gamefic/keywords.rb +0 -52
  80. data/lib/gamefic/messaging.rb +0 -43
  81. data/lib/gamefic/plot/darkroom.rb +0 -120
  82. data/lib/gamefic/plot/host.rb +0 -42
  83. data/lib/gamefic/plot/snapshot.rb +0 -27
  84. data/lib/gamefic/query/children.rb +0 -9
  85. data/lib/gamefic/query/descendants.rb +0 -15
  86. data/lib/gamefic/query/external.rb +0 -39
  87. data/lib/gamefic/query/family.rb +0 -18
  88. data/lib/gamefic/query/itself.rb +0 -13
  89. data/lib/gamefic/query/matches.rb +0 -75
  90. data/lib/gamefic/query/parent.rb +0 -9
  91. data/lib/gamefic/query/siblings.rb +0 -13
  92. data/lib/gamefic/query/tree.rb +0 -17
  93. data/lib/gamefic/scene/base.rb +0 -142
  94. data/lib/gamefic/scene/multiple_scene.rb +0 -29
  95. data/lib/gamefic/serialize.rb +0 -196
  96. data/lib/gamefic/world/callbacks.rb +0 -135
  97. data/lib/gamefic/world/commands.rb +0 -181
  98. data/lib/gamefic/world/entities.rb +0 -98
  99. data/lib/gamefic/world/playbook.rb +0 -233
  100. data/lib/gamefic/world/players.rb +0 -37
  101. data/lib/gamefic/world/scenes.rb +0 -228
  102. 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: 6647dc3319080277826fecd19b8466cfbe82a42f4f4c68aeb89fa74bd0de4319
4
+ data.tar.gz: 0dc0680cb42b981d6e93c12d03e95b4ffcce0a98acb0678595723ab2d5ae7db9
5
5
  SHA512:
6
- metadata.gz: 6a89c7fb5978047517827e3cc2de5f172da5e6905a7e7efe0725763b4ce01d56725df46a443b903906effd962cebdbd238d81719b315eb26f3b8bf289d337089
7
- data.tar.gz: 9976d0c58957309ba605e645a8c7e214d798c12e114b71e5d5fa2bb4b6d39878537612e670da6e42168dfd31ee5fce7304eee9983d1177d878262af630e6e582
6
+ metadata.gz: 1ec19185367b63b0ebe352e620af403e0aef00bdd7afac7135801551568ef5c35c923b45092cb4d781cd8458bba71776b042efe9837a0e9d2f144141b6b8d606
7
+ data.tar.gz: 300d2c30b05993ee4689f14ce8daeeec3585bfdf1cd7636ec6a931bfc568e8227a97771fdf6d20ec859080f3792b914a29ba1fc6a7246c9f9ef9eb563d81920c
@@ -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,12 @@
1
+ ## 3.0.0
2
+ - Instantiate subplots from snapshots
3
+ - Split Action into Response and Action
4
+ - Logging
5
+ - Remove deprecated Active#perform behavior
6
+ - Snapshots use single static index
7
+ - Hydration improvements
8
+ - Snapshot metadata validation
9
+
1
10
  ## 2.4.0 - February 11, 2023
2
11
  - Fix arity of delegated methods in scripts
3
12
  - 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,82 @@
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
+ attr_reader :verbs
17
+
18
+ attr_reader :block
19
+
20
+ def initialize *verbs, &block
21
+ @verbs = verbs
22
+ @block = block
23
+ end
24
+
25
+ def match?(input)
26
+ verbs.empty? || verbs.include?(input)
27
+ end
28
+ end
29
+
30
+ # @return [Active]
31
+ attr_reader :actor
32
+
33
+ # @return [Array]
10
34
  attr_reader :arguments
11
- alias parameters arguments
12
35
 
13
- # @param actor [Gamefic::Actor]
14
- # @param arguments [Array<Object>]
15
- def initialize actor, arguments, with_callbacks = false
36
+ # @return [Response]
37
+ attr_reader :response
38
+
39
+ # @param actor [Active]
40
+ # @param arguments [Array]
41
+ # @param response [Response]
42
+ def initialize actor, arguments, response
16
43
  @actor = actor
17
44
  @arguments = arguments
18
- @executed = false
19
- @with_callbacks = with_callbacks
45
+ @response = response
20
46
  end
21
47
 
22
- # Perform the action.
23
- #
24
48
  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?
49
+ return if cancelled?
50
+
29
51
  @executed = true
30
- run_after_actions
52
+ response.execute actor, *arguments
31
53
  end
32
54
 
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.
55
+ # True if the response has been executed. False typically means that the
56
+ # #execute method has not been called or the action was cancelled in a
57
+ # before_action hook.
36
58
  #
37
- def cancel
38
- # @todo Emit a warning for attempts to cancel an action after it's been
39
- # executed
40
- @cancelled = true
59
+ def executed?
60
+ @executed ||= false
41
61
  end
42
62
 
43
- # True if the #execute method has been called for this action.
63
+ # Cancel an action. This method can be called in an action hook to
64
+ # prevent subsequent hooks and/or the action itself from being executed.
44
65
  #
45
- # @return [Boolean]
46
- def executed?
47
- @executed
66
+ def cancel
67
+ @cancelled = true
48
68
  end
49
69
 
50
70
  def cancelled?
51
- !@executed && @cancelled
71
+ @cancelled ||= false
52
72
  end
53
73
 
54
- # The verb associated with this action.
55
- #
56
- # @return [Symbol] The symbol representing the verb
57
74
  def verb
58
- self.class.verb
59
- end
60
-
61
- def signature
62
- self.class.signature
63
- end
64
-
65
- def rank
66
- self.class.rank
75
+ response.verb
67
76
  end
68
77
 
69
- # True if the action is flagged as meta.
70
- #
71
- # @return [Boolean]
72
78
  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
79
+ response.meta?
211
80
  end
212
81
  end
213
82
  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,68 @@
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
+ # @param name [Symbol]
51
+ def conclusion? name
52
+ select_scene(name).conclusion?
53
+ end
54
+
55
+ # @param name [Symbol]
56
+ # @return [Scene]
57
+ def select_scene name
58
+ scenes = rulebooks.map { |rlbk| rlbk.scenes[name] }
59
+ .compact
60
+ raise ArgumentError, "Scene named `#{name}` does not exist" if scenes.empty?
61
+
62
+ logger.warn "Found #{scenes.length} scenes named `#{name}`" unless scenes.one?
63
+
64
+ scenes.last
65
+ end
66
+ end
67
+ end
68
+ 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
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gamefic
4
+ module Active
5
+ # The combination of an actor and a scene to be performed in a plot turn.
6
+ #
7
+ class Take
8
+ # @return [Active]
9
+ attr_reader :actor
10
+
11
+ # @return [Active::Cue]
12
+ attr_reader :cue
13
+
14
+ # @return [Scene::Default]
15
+ attr_reader :scene
16
+
17
+ # @param actor [Active]
18
+ # @param cue [Active::Cue]
19
+ # @param props [Props::Default]
20
+ def initialize actor, cue, props = nil
21
+ @actor = actor
22
+ @cue = cue
23
+ @scene = actor.epic.select_scene(cue.scene)
24
+ @props = props
25
+ end
26
+
27
+ # @return [Props::Default]
28
+ def props
29
+ @props ||= @scene.new_props(**cue.context)
30
+ end
31
+
32
+ # @return [Props::Default]
33
+ def start
34
+ actor.output[:scene] = scene.to_hash
35
+ scene.run_start_blocks actor, props
36
+ scene.start actor, props
37
+ # @todo See if this can be handled better
38
+ actor.epic.rulebooks.each { |rlbk| rlbk.run_player_output_blocks actor, actor.output }
39
+ actor.output.merge!({
40
+ messages: actor.messages,
41
+ queue: actor.queue
42
+ })
43
+ props
44
+ end
45
+
46
+ # @return [void]
47
+ def finish
48
+ actor.flush
49
+ scene.finish(actor, props)
50
+ actor.output.replace(last_prompt: props.prompt, last_input: props.input)
51
+ scene.run_finish_blocks actor, props
52
+ end
53
+
54
+ # @param actor [Active]
55
+ # @param cue [Active::Cue]
56
+ # @return [Props::Default]
57
+ def self.start actor, cue
58
+ Take.new(actor, cue).start
59
+ end
60
+
61
+ # @param actor [Active]
62
+ # @param cue [Active::Cue]
63
+ # @return [void]
64
+ def self.finish actor, cue, props
65
+ Take.new(actor, cue, props).finish
66
+ end
67
+ end
68
+ end
69
+ end