gamefic 3.6.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +0 -3
- data/CHANGELOG.md +19 -0
- data/Rakefile +1 -0
- data/gamefic.gemspec +1 -1
- data/lib/gamefic/action.rb +68 -54
- data/lib/gamefic/active/cue.rb +84 -6
- data/lib/gamefic/active/messaging.rb +8 -0
- data/lib/gamefic/active/narratives.rb +101 -0
- data/lib/gamefic/active.rb +80 -92
- data/lib/gamefic/binding.rb +44 -0
- data/lib/gamefic/chapter.rb +30 -46
- data/lib/gamefic/command.rb +22 -40
- data/lib/gamefic/core_ext/array.rb +7 -7
- data/lib/gamefic/core_ext/string.rb +2 -2
- data/lib/gamefic/describable.rb +13 -0
- data/lib/gamefic/dispatcher.rb +35 -55
- data/lib/gamefic/entity.rb +6 -5
- data/lib/gamefic/expression.rb +1 -11
- data/lib/gamefic/logging.rb +3 -10
- data/lib/gamefic/match.rb +23 -0
- data/lib/gamefic/messenger.rb +1 -1
- data/lib/gamefic/narrative.rb +38 -74
- data/lib/gamefic/narrator.rb +77 -0
- data/lib/gamefic/node.rb +40 -8
- data/lib/gamefic/order.rb +53 -0
- data/lib/gamefic/plot.rb +41 -59
- data/lib/gamefic/props/default.rb +5 -17
- data/lib/gamefic/props/multiple_choice.rb +5 -2
- data/lib/gamefic/props/multiple_partial.rb +16 -0
- data/lib/gamefic/props/output.rb +7 -5
- data/lib/gamefic/props/yes_or_no.rb +2 -2
- data/lib/gamefic/props.rb +1 -0
- data/lib/gamefic/proxy/attr.rb +11 -0
- data/lib/gamefic/proxy/base.rb +3 -15
- data/lib/gamefic/proxy/config.rb +2 -2
- data/lib/gamefic/proxy/pick.rb +3 -3
- data/lib/gamefic/proxy/pick_ex.rb +11 -0
- data/lib/gamefic/proxy.rb +3 -71
- data/lib/gamefic/query/ascendants.rb +16 -0
- data/lib/gamefic/query/base.rb +47 -73
- data/lib/gamefic/query/children.rb +15 -0
- data/lib/gamefic/query/descendants.rb +17 -0
- data/lib/gamefic/query/extended.rb +20 -0
- data/lib/gamefic/query/family.rb +27 -0
- data/lib/gamefic/query/global.rb +22 -0
- data/lib/gamefic/query/integer.rb +32 -0
- data/lib/gamefic/query/myself.rb +13 -0
- data/lib/gamefic/query/parent.rb +13 -0
- data/lib/gamefic/query/result.rb +1 -1
- data/lib/gamefic/query/siblings.rb +12 -0
- data/lib/gamefic/query/subqueries.rb +17 -0
- data/lib/gamefic/query/text.rb +8 -9
- data/lib/gamefic/query.rb +11 -3
- data/lib/gamefic/request.rb +60 -0
- data/lib/gamefic/response.rb +46 -72
- data/lib/gamefic/scanner/nesting.rb +6 -6
- data/lib/gamefic/scanner/result.rb +3 -0
- data/lib/gamefic/scanner/strict.rb +14 -4
- data/lib/gamefic/scanner.rb +11 -6
- data/lib/gamefic/scene/active_choice.rb +75 -0
- data/lib/gamefic/scene/activity.rb +7 -3
- data/lib/gamefic/scene/base.rb +123 -0
- data/lib/gamefic/scene/conclusion.rb +4 -1
- data/lib/gamefic/scene/multiple_choice.rb +14 -11
- data/lib/gamefic/scene/pause.rb +5 -1
- data/lib/gamefic/scene/yes_or_no.rb +9 -0
- data/lib/gamefic/scene.rb +2 -1
- data/lib/gamefic/scriptable/hooks.rb +161 -0
- data/lib/gamefic/scriptable/queries.rb +38 -29
- data/lib/gamefic/scriptable/responses.rb +70 -0
- data/lib/gamefic/scriptable/scenes.rb +88 -115
- data/lib/gamefic/scriptable/seeds.rb +69 -0
- data/lib/gamefic/scriptable/syntaxes.rb +29 -0
- data/lib/gamefic/scriptable.rb +14 -199
- data/lib/gamefic/{scriptable → scripting}/entities.rb +22 -22
- data/lib/gamefic/scripting/hooks.rb +45 -0
- data/lib/gamefic/{scriptable → scripting}/proxies.rb +5 -3
- data/lib/gamefic/scripting/responses.rb +21 -0
- data/lib/gamefic/scripting/scenes.rb +57 -0
- data/lib/gamefic/scripting/seeds.rb +10 -0
- data/lib/gamefic/scripting/syntaxes.rb +13 -0
- data/lib/gamefic/scripting.rb +43 -0
- data/lib/gamefic/subplot.rb +11 -22
- data/lib/gamefic/syntax.rb +39 -24
- data/lib/gamefic/version.rb +1 -1
- data/lib/gamefic.rb +6 -7
- metadata +38 -41
- data/lib/gamefic/active/epic.rb +0 -74
- data/lib/gamefic/active/take.rb +0 -67
- data/lib/gamefic/block.rb +0 -28
- data/lib/gamefic/callback.rb +0 -16
- data/lib/gamefic/proxy/plot_pick.rb +0 -11
- data/lib/gamefic/query/abstract.rb +0 -12
- data/lib/gamefic/query/general.rb +0 -41
- data/lib/gamefic/query/scoped.rb +0 -27
- data/lib/gamefic/rulebook/calls.rb +0 -86
- data/lib/gamefic/rulebook/events.rb +0 -65
- data/lib/gamefic/rulebook/hooks.rb +0 -57
- data/lib/gamefic/rulebook/scenes.rb +0 -68
- data/lib/gamefic/rulebook.rb +0 -125
- data/lib/gamefic/scene/default.rb +0 -88
- data/lib/gamefic/scope/base.rb +0 -44
- data/lib/gamefic/scope/children.rb +0 -16
- data/lib/gamefic/scope/descendants.rb +0 -16
- data/lib/gamefic/scope/family.rb +0 -43
- data/lib/gamefic/scope/myself.rb +0 -13
- data/lib/gamefic/scope/parent.rb +0 -13
- data/lib/gamefic/scope/siblings.rb +0 -14
- data/lib/gamefic/scope.rb +0 -9
- data/lib/gamefic/scriptable/actions.rb +0 -137
- data/lib/gamefic/scriptable/events.rb +0 -71
- data/lib/gamefic/scriptable/plot_proxies.rb +0 -29
- data/lib/gamefic/snapshot.rb +0 -44
- data/lib/gamefic/stage.rb +0 -51
- data/lib/gamefic/syntax/template.rb +0 -67
- data/lib/gamefic/vault.rb +0 -52
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab6b04620f4d61106e3d7a3321dfc1833f0a7cce7eeb0e509ca77bdc5388d7c0
|
4
|
+
data.tar.gz: d8d37b580f71e20870cbe4124a8af41a953448b4bd5296e8f6f73d5e8a0e5092
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac226d280d8e1a07ad2bc154ae1cbc49a4496a847986f4d8919aef89999386432122673b568c64e2012ef3b910652aa67a31545e59eb65633105854fd0228d4b
|
7
|
+
data.tar.gz: 2b271b4061e74ab43d4cd11e080261b595404f7add90ea017934e49c4d1beda47a44c3ed88b53d4ba57cc6a6602fabf5e330a18544e783b09c56129642f6f82a
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
## 4.0.0
|
2
|
+
- Nuanced scans
|
3
|
+
- Command hooks
|
4
|
+
- Refactored queries
|
5
|
+
- Cue scenes by class or name
|
6
|
+
- Deprecate underscore verbs
|
7
|
+
- Move rules to Scriptable
|
8
|
+
- JIT Scriptable binding
|
9
|
+
- Sunset Rulebook
|
10
|
+
- Separate Scriptable and Scripting modules
|
11
|
+
- ActiveChoice scenes
|
12
|
+
- Narrator class runs narratives
|
13
|
+
- Use construct for static entities
|
14
|
+
- Parent relations
|
15
|
+
- Consolidate Syntax and Template
|
16
|
+
- Remove Snapshot module
|
17
|
+
- Track command activity
|
18
|
+
- MultiplePartial props
|
19
|
+
|
1
20
|
## 3.6.0 - October 6, 2024
|
2
21
|
- Normalized arguments accept strings
|
3
22
|
- Smarter picks and proxies
|
data/Rakefile
CHANGED
data/gamefic.gemspec
CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.add_development_dependency 'opal', '~> 1.7'
|
25
25
|
s.add_development_dependency 'opal-rspec', '~> 1.0'
|
26
26
|
s.add_development_dependency 'opal-sprockets', '~> 1.0'
|
27
|
-
s.add_development_dependency 'rake', '~>
|
27
|
+
s.add_development_dependency 'rake', '~> 13.2'
|
28
28
|
s.add_development_dependency 'rspec', '~> 3.5', '>= 3.5.0'
|
29
29
|
s.add_development_dependency 'simplecov', '~> 0.14'
|
30
30
|
end
|
data/lib/gamefic/action.rb
CHANGED
@@ -1,91 +1,105 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Gamefic
|
4
|
-
# The handler for executing
|
5
|
-
# arguments. It's also responsible for executing before_action and
|
6
|
-
# after_action hooks if necessary.
|
4
|
+
# The handler for executing a command response.
|
7
5
|
#
|
8
6
|
class Action
|
9
|
-
include
|
7
|
+
include Scriptable::Queries
|
10
8
|
|
11
|
-
#
|
12
|
-
# performs an action. A before action hook is capable of cancelling the
|
13
|
-
# action's performance.
|
14
|
-
#
|
15
|
-
class Hook
|
16
|
-
# @param [Array<Symbol>]
|
17
|
-
attr_reader :verbs
|
18
|
-
|
19
|
-
# @param [Callback]
|
20
|
-
attr_reader :callback
|
21
|
-
|
22
|
-
def initialize verbs, callback
|
23
|
-
@verbs = verbs
|
24
|
-
@callback = callback
|
25
|
-
end
|
26
|
-
|
27
|
-
def match?(input)
|
28
|
-
verbs.empty? || verbs.include?(input)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# @return [Active]
|
9
|
+
# @return [Actor]
|
33
10
|
attr_reader :actor
|
34
11
|
|
35
|
-
# @return [Array]
|
36
|
-
attr_reader :arguments
|
37
|
-
|
38
12
|
# @return [Response]
|
39
13
|
attr_reader :response
|
40
14
|
|
41
|
-
# @
|
42
|
-
|
15
|
+
# @return [Array<Match>]
|
16
|
+
attr_reader :matches
|
17
|
+
|
18
|
+
# @return [String, nil]
|
19
|
+
attr_reader :input
|
20
|
+
|
21
|
+
# @param actor [Actor]
|
43
22
|
# @param response [Response]
|
44
|
-
|
23
|
+
# @param matches [Array<Match>]
|
24
|
+
# @param input [String, nil]
|
25
|
+
def initialize(actor, response, matches, input = nil)
|
45
26
|
@actor = actor
|
46
|
-
@arguments = arguments
|
47
27
|
@response = response
|
28
|
+
@matches = matches
|
29
|
+
@input = input
|
48
30
|
end
|
49
31
|
|
50
|
-
|
51
|
-
|
52
|
-
|
32
|
+
def verb
|
33
|
+
response.verb
|
34
|
+
end
|
53
35
|
|
54
|
-
|
55
|
-
@
|
56
|
-
|
36
|
+
def command
|
37
|
+
@command ||= Command.new(response.verb, matches.map(&:argument), response.meta?, input)
|
38
|
+
end
|
39
|
+
|
40
|
+
def queries
|
41
|
+
response.queries
|
42
|
+
end
|
43
|
+
|
44
|
+
def arguments
|
45
|
+
matches.map(&:argument)
|
46
|
+
end
|
47
|
+
|
48
|
+
def execute
|
49
|
+
response.execute(actor, *arguments)
|
57
50
|
self
|
58
51
|
end
|
59
52
|
|
60
|
-
#
|
61
|
-
#
|
62
|
-
# before_action hook.
|
53
|
+
# The total substantiality of the action, based on how many of the
|
54
|
+
# arguments are concrete entities and whether the action has a verb.
|
63
55
|
#
|
64
|
-
def
|
65
|
-
|
56
|
+
def substantiality
|
57
|
+
arguments.that_are(Entity).length + (verb ? 1 : 0)
|
66
58
|
end
|
67
59
|
|
68
|
-
#
|
69
|
-
# prevent subsequent hooks and/or the action itself from being executed.
|
60
|
+
# The total strictness of all the matches.
|
70
61
|
#
|
71
|
-
|
72
|
-
|
62
|
+
# The higher the strictness, the more precisely the tokens from the user
|
63
|
+
# input match the arguments. For example, if the user is interacting with a
|
64
|
+
# pencil, the command TAKE PENCIL is stricter than TAKE PEN.
|
65
|
+
#
|
66
|
+
# @return [Integer]
|
67
|
+
def strictness
|
68
|
+
matches.sum(0, &:strictness)
|
73
69
|
end
|
74
70
|
|
75
|
-
|
76
|
-
|
71
|
+
# The precision of the response.
|
72
|
+
#
|
73
|
+
# @return [Integer]
|
74
|
+
def precision
|
75
|
+
response.precision
|
77
76
|
end
|
78
77
|
|
79
|
-
def
|
80
|
-
response.
|
78
|
+
def valid?
|
79
|
+
response.accept?(actor, command)
|
81
80
|
end
|
82
81
|
|
83
|
-
def
|
84
|
-
|
82
|
+
def invalid?
|
83
|
+
!valid?
|
85
84
|
end
|
86
85
|
|
87
86
|
def meta?
|
88
87
|
response.meta?
|
89
88
|
end
|
89
|
+
|
90
|
+
# Sort an array of actions in the order in which a Dispatcher should
|
91
|
+
# attempt to execute them.
|
92
|
+
#
|
93
|
+
# Order is determined by the actions' substantiality, strictness, and
|
94
|
+
# precision. In the event of a tie, the most recently defined action has
|
95
|
+
# higher priority.
|
96
|
+
#
|
97
|
+
# @param actions [Array<Action>]
|
98
|
+
# @return [Array<Action>]
|
99
|
+
def self.sort(actions)
|
100
|
+
actions.sort_by.with_index do |action, idx|
|
101
|
+
[-action.substantiality, -action.strictness, -action.precision, idx]
|
102
|
+
end
|
103
|
+
end
|
90
104
|
end
|
91
105
|
end
|
data/lib/gamefic/active/cue.rb
CHANGED
@@ -2,24 +2,102 @@
|
|
2
2
|
|
3
3
|
module Gamefic
|
4
4
|
module Active
|
5
|
-
# The
|
5
|
+
# The object that actors use to perform a scene.
|
6
6
|
#
|
7
7
|
class Cue
|
8
|
-
# @return [
|
9
|
-
attr_reader :
|
8
|
+
# @return [Actor]
|
9
|
+
attr_reader :actor
|
10
|
+
|
11
|
+
# @return [Class<Scene::Base>, Symbol]
|
12
|
+
attr_reader :key
|
13
|
+
|
14
|
+
# @return [Narrative]
|
15
|
+
attr_reader :narrative
|
10
16
|
|
11
17
|
# @return [Hash]
|
12
18
|
attr_reader :context
|
13
19
|
|
14
|
-
# @
|
15
|
-
|
16
|
-
|
20
|
+
# @return [Props::Default, nil]
|
21
|
+
attr_reader :props
|
22
|
+
|
23
|
+
# @param actor [Actor]
|
24
|
+
# @param key [Class<Scene::Base>, Symbol]
|
25
|
+
# @param narrative [Narrative]
|
26
|
+
def initialize actor, key, narrative, **context
|
27
|
+
@actor = actor
|
28
|
+
@key = key
|
29
|
+
@narrative = narrative
|
17
30
|
@context = context
|
18
31
|
end
|
19
32
|
|
33
|
+
# @return [void]
|
34
|
+
def start
|
35
|
+
@props = scene.start
|
36
|
+
prepare_output
|
37
|
+
actor.rotate_cue
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [void]
|
41
|
+
def finish
|
42
|
+
props&.enter(actor.queue.shift&.strip)
|
43
|
+
scene.finish
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [Props::Output]
|
47
|
+
def output
|
48
|
+
props&.output.clone.freeze || Props::Output::EMPTY
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Cue]
|
52
|
+
def restart
|
53
|
+
Cue.new(actor, key, narrative, **context)
|
54
|
+
end
|
55
|
+
|
56
|
+
def type
|
57
|
+
scene&.type
|
58
|
+
end
|
59
|
+
|
20
60
|
def to_s
|
21
61
|
scene.to_s
|
22
62
|
end
|
63
|
+
|
64
|
+
# @return [void]
|
65
|
+
def prepare
|
66
|
+
props.output.merge!({
|
67
|
+
scene: scene.to_hash,
|
68
|
+
prompt: props.prompt,
|
69
|
+
messages: actor.messages,
|
70
|
+
queue: actor.queue
|
71
|
+
})
|
72
|
+
actor.narratives.player_output_blocks.each { |block| block.call actor, props.output }
|
73
|
+
end
|
74
|
+
|
75
|
+
# @return [Scene::Base]
|
76
|
+
def scene
|
77
|
+
# @note This method always returns a new instance. Scenes identified
|
78
|
+
# by symbolic keys can be instances of anonymous classes that cannot
|
79
|
+
# be serialized, so memoizing them breaks snapshots.
|
80
|
+
narrative&.prepare(key, actor, props, **context) ||
|
81
|
+
try_unblocked_class ||
|
82
|
+
raise("Failed to cue #{key.inspect} in #{narrative.inspect}")
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
# @return [Scene::Base]
|
88
|
+
def try_unblocked_class
|
89
|
+
return unless key.is_a?(Class) && key <= Scene::Base
|
90
|
+
|
91
|
+
Gamefic.logger.warn "Cueing scene #{key} without narrative" unless narrative
|
92
|
+
key.new(actor, narrative, props, **context)
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return [void]
|
96
|
+
def prepare_output
|
97
|
+
scene
|
98
|
+
props.output.last_input = actor.last_cue&.props&.input
|
99
|
+
props.output.last_prompt = actor.last_cue&.props&.prompt
|
100
|
+
end
|
23
101
|
end
|
24
102
|
end
|
25
103
|
end
|
@@ -30,14 +30,22 @@ module Gamefic
|
|
30
30
|
messenger.stream message
|
31
31
|
end
|
32
32
|
|
33
|
+
# @return [String]
|
33
34
|
def messages
|
34
35
|
messenger.messages
|
35
36
|
end
|
36
37
|
|
38
|
+
# Create a temporary buffer while yielding the given block and return the
|
39
|
+
# buffered text.
|
40
|
+
#
|
41
|
+
# @return [String]
|
37
42
|
def buffer &block
|
38
43
|
messenger.buffer(&block)
|
39
44
|
end
|
40
45
|
|
46
|
+
# Clear the current buffer.
|
47
|
+
#
|
48
|
+
# @return [String] The buffer's messages
|
41
49
|
def flush
|
42
50
|
messenger.flush
|
43
51
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
module Active
|
5
|
+
# A narrative container for active entities.
|
6
|
+
#
|
7
|
+
class Narratives
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
# @param narrative [Narrative]
|
11
|
+
# @return [self]
|
12
|
+
def add(narrative)
|
13
|
+
narrative_set.add(narrative)
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param narrative [Narrative]
|
18
|
+
# @return [self]
|
19
|
+
def delete(narrative)
|
20
|
+
narrative_set.delete(narrative)
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def empty?
|
25
|
+
narrative_set.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Integer]
|
29
|
+
def length
|
30
|
+
narrative_set.length
|
31
|
+
end
|
32
|
+
|
33
|
+
def one?
|
34
|
+
narrative_set.one?
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Array<Response>]
|
38
|
+
def responses
|
39
|
+
narrative_set.flat_map(&:responses)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Array<Response>]
|
43
|
+
def responses_for(*verbs)
|
44
|
+
narrative_set.flat_map { |narr| narr.responses_for(*verbs) }
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Array<Syntax>]
|
48
|
+
def syntaxes
|
49
|
+
narrative_set.flat_map(&:syntaxes)
|
50
|
+
end
|
51
|
+
|
52
|
+
# True if the specified verb is understood by any of the narratives.
|
53
|
+
#
|
54
|
+
# @param verb [String, Symbol]
|
55
|
+
def understand?(verb)
|
56
|
+
verb ? narrative_set.flat_map(&:synonyms).include?(verb.to_sym) : false
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Array<Binding>]
|
60
|
+
def before_commands
|
61
|
+
narrative_set.flat_map(&:before_commands)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Array<Binding>]
|
65
|
+
def after_commands
|
66
|
+
narrative_set.flat_map(&:after_commands)
|
67
|
+
end
|
68
|
+
|
69
|
+
def each(&block)
|
70
|
+
narrative_set.each(&block)
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return [Array<Narrative>]
|
74
|
+
def that_are(*args)
|
75
|
+
narrative_set.to_a.that_are(*args)
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [Array<Narrative>]
|
79
|
+
def that_are_not(*args)
|
80
|
+
narrative_set.to_a.that_are_not(*args)
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [Array<Entity>]
|
84
|
+
def entities
|
85
|
+
narrative_set.flat_map(&:entities)
|
86
|
+
end
|
87
|
+
|
88
|
+
# @return [Array<Binding>]
|
89
|
+
def player_output_blocks
|
90
|
+
narrative_set.flat_map(&:player_output_blocks).uniq(&:code)
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
# @return [Set<Narrative>]
|
96
|
+
def narrative_set
|
97
|
+
@narrative_set ||= Set.new
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|