gamefic 2.4.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.
- checksums.yaml +4 -4
- data/.github/workflows/rspec.yml +41 -40
- data/.rspec-opal +2 -0
- data/.solargraph.yml +20 -3
- data/CHANGELOG.md +9 -0
- data/Rakefile +11 -1
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/gamefic.gemspec +5 -2
- data/lib/gamefic/action.rb +52 -183
- data/lib/gamefic/active/cue.rb +25 -0
- data/lib/gamefic/active/epic.rb +68 -0
- data/lib/gamefic/active/messaging.rb +43 -0
- data/lib/gamefic/active/take.rb +69 -0
- data/lib/gamefic/active.rb +95 -192
- data/lib/gamefic/actor.rb +2 -0
- data/lib/gamefic/block.rb +28 -0
- data/lib/gamefic/command.rb +16 -6
- data/lib/gamefic/core_ext/array.rb +4 -4
- data/lib/gamefic/core_ext/string.rb +10 -5
- data/lib/gamefic/describable.rb +39 -65
- data/lib/gamefic/dispatcher.rb +63 -32
- data/lib/gamefic/entity.rb +44 -19
- data/lib/gamefic/logging.rb +32 -0
- data/lib/gamefic/messenger.rb +66 -0
- data/lib/gamefic/narrative.rb +104 -0
- data/lib/gamefic/node.rb +44 -53
- data/lib/gamefic/plot.rb +60 -93
- data/lib/gamefic/props/default.rb +41 -0
- data/lib/gamefic/props/multiple_choice.rb +65 -0
- data/lib/gamefic/props/pause.rb +11 -0
- data/lib/gamefic/props/yes_or_no.rb +21 -0
- data/lib/gamefic/props.rb +10 -0
- data/lib/gamefic/query/base.rb +45 -126
- data/lib/gamefic/query/general.rb +46 -0
- data/lib/gamefic/query/result.rb +20 -0
- data/lib/gamefic/query/scoped.rb +41 -0
- data/lib/gamefic/query/text.rb +30 -31
- data/lib/gamefic/query.rb +7 -15
- data/lib/gamefic/response.rb +118 -0
- data/lib/gamefic/rulebook/calls.rb +90 -0
- data/lib/gamefic/rulebook/events.rb +79 -0
- data/lib/gamefic/rulebook/hooks.rb +57 -0
- data/lib/gamefic/rulebook/scenes.rb +68 -0
- data/lib/gamefic/rulebook.rb +139 -0
- data/lib/gamefic/scanner.rb +103 -0
- data/lib/gamefic/scene/activity.rb +9 -17
- data/lib/gamefic/scene/conclusion.rb +6 -5
- data/lib/gamefic/scene/default.rb +88 -0
- data/lib/gamefic/scene/multiple_choice.rb +14 -69
- data/lib/gamefic/scene/pause.rb +9 -13
- data/lib/gamefic/scene/yes_or_no.rb +6 -46
- data/lib/gamefic/scene.rb +11 -7
- data/lib/gamefic/scope/base.rb +44 -0
- data/lib/gamefic/scope/children.rb +16 -0
- data/lib/gamefic/scope/family.rb +20 -0
- data/lib/gamefic/scope/myself.rb +13 -0
- data/lib/gamefic/scope/parent.rb +13 -0
- data/lib/gamefic/scope/siblings.rb +14 -0
- data/lib/gamefic/scope.rb +8 -0
- data/lib/gamefic/scriptable/actions.rb +156 -0
- data/lib/gamefic/scriptable/entities.rb +76 -0
- data/lib/gamefic/scriptable/events.rb +65 -0
- data/lib/gamefic/scriptable/proxy.rb +55 -0
- data/lib/gamefic/scriptable/queries.rb +73 -0
- data/lib/gamefic/scriptable/scenes.rb +162 -0
- data/lib/gamefic/scriptable.rb +167 -73
- data/lib/gamefic/snapshot.rb +36 -0
- data/lib/gamefic/stage.rb +51 -0
- data/lib/gamefic/subplot.rb +51 -79
- data/lib/gamefic/syntax/template.rb +67 -0
- data/lib/gamefic/syntax.rb +102 -83
- data/lib/gamefic/vault.rb +50 -0
- data/lib/gamefic/version.rb +1 -1
- data/lib/gamefic.rb +26 -15
- data/spec-opal/spec_helper.rb +24 -0
- metadata +91 -29
- data/lib/gamefic/element.rb +0 -46
- data/lib/gamefic/keywords.rb +0 -52
- data/lib/gamefic/messaging.rb +0 -43
- data/lib/gamefic/plot/darkroom.rb +0 -120
- data/lib/gamefic/plot/host.rb +0 -42
- data/lib/gamefic/plot/snapshot.rb +0 -27
- data/lib/gamefic/query/children.rb +0 -9
- data/lib/gamefic/query/descendants.rb +0 -15
- data/lib/gamefic/query/external.rb +0 -39
- data/lib/gamefic/query/family.rb +0 -18
- data/lib/gamefic/query/itself.rb +0 -13
- data/lib/gamefic/query/matches.rb +0 -75
- data/lib/gamefic/query/parent.rb +0 -9
- data/lib/gamefic/query/siblings.rb +0 -13
- data/lib/gamefic/query/tree.rb +0 -17
- data/lib/gamefic/scene/base.rb +0 -142
- data/lib/gamefic/scene/multiple_scene.rb +0 -29
- data/lib/gamefic/serialize.rb +0 -196
- data/lib/gamefic/world/callbacks.rb +0 -135
- data/lib/gamefic/world/commands.rb +0 -181
- data/lib/gamefic/world/entities.rb +0 -98
- data/lib/gamefic/world/playbook.rb +0 -233
- data/lib/gamefic/world/players.rb +0 -37
- data/lib/gamefic/world/scenes.rb +0 -228
- data/lib/gamefic/world.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6647dc3319080277826fecd19b8466cfbe82a42f4f4c68aeb89fa74bd0de4319
|
4
|
+
data.tar.gz: 0dc0680cb42b981d6e93c12d03e95b4ffcce0a98acb0678595723ab2d5ae7db9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ec19185367b63b0ebe352e620af403e0aef00bdd7afac7135801551568ef5c35c923b45092cb4d781cd8458bba71776b042efe9837a0e9d2f144141b6b8d606
|
7
|
+
data.tar.gz: 300d2c30b05993ee4689f14ce8daeeec3585bfdf1cd7636ec6a931bfc568e8227a97771fdf6d20ec859080f3792b914a29ba1fc6a7246c9f9ef9eb563d81920c
|
data/.github/workflows/rspec.yml
CHANGED
@@ -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.
|
26
|
-
|
27
|
-
steps:
|
28
|
-
- uses: actions/checkout@v3
|
29
|
-
- name: Set up Ruby
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
data/.solargraph.yml
CHANGED
@@ -1,5 +1,22 @@
|
|
1
|
+
---
|
1
2
|
include:
|
2
|
-
|
3
|
+
- "**/*.rb"
|
3
4
|
exclude:
|
4
|
-
|
5
|
-
|
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
|
-
|
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
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 = '
|
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.
|
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'
|
data/lib/gamefic/action.rb
CHANGED
@@ -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
|
-
|
4
|
-
attr_reader :actor
|
9
|
+
include Logging
|
5
10
|
|
6
|
-
#
|
7
|
-
#
|
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
|
-
|
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
|
-
# @
|
14
|
-
|
15
|
-
|
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
|
-
@
|
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
|
26
|
-
|
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
|
-
|
52
|
+
response.execute actor, *arguments
|
31
53
|
end
|
32
54
|
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
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
|
38
|
-
|
39
|
-
# executed
|
40
|
-
@cancelled = true
|
59
|
+
def executed?
|
60
|
+
@executed ||= false
|
41
61
|
end
|
42
62
|
|
43
|
-
#
|
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
|
-
|
46
|
-
|
47
|
-
@executed
|
66
|
+
def cancel
|
67
|
+
@cancelled = true
|
48
68
|
end
|
49
69
|
|
50
70
|
def cancelled?
|
51
|
-
|
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
|
-
|
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
|
-
|
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
|