gamefic 3.4.0 → 3.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +1 -1
- data/lib/gamefic/action.rb +1 -0
- data/lib/gamefic/active/epic.rb +1 -0
- data/lib/gamefic/active.rb +4 -0
- data/lib/gamefic/chapter.rb +25 -42
- data/lib/gamefic/command.rb +49 -1
- data/lib/gamefic/dispatcher.rb +8 -31
- data/lib/gamefic/entity.rb +26 -0
- data/lib/gamefic/expression.rb +3 -0
- data/lib/gamefic/narrative.rb +9 -5
- data/lib/gamefic/node.rb +3 -5
- data/lib/gamefic/plot.rb +5 -0
- data/lib/gamefic/proxy/base.rb +27 -0
- data/lib/gamefic/proxy/config.rb +16 -0
- data/lib/gamefic/proxy/pick.rb +11 -0
- data/lib/gamefic/proxy/plot_pick.rb +11 -0
- data/lib/gamefic/proxy.rb +79 -0
- data/lib/gamefic/query/abstract.rb +12 -0
- data/lib/gamefic/query/base.rb +62 -16
- data/lib/gamefic/query/general.rb +6 -15
- data/lib/gamefic/query/result.rb +4 -1
- data/lib/gamefic/query/scoped.rb +3 -21
- data/lib/gamefic/query/text.rb +17 -15
- data/lib/gamefic/query.rb +1 -0
- data/lib/gamefic/response.rb +75 -34
- data/lib/gamefic/scanner/base.rb +44 -0
- data/lib/gamefic/scanner/fuzzy.rb +17 -0
- data/lib/gamefic/scanner/fuzzy_nesting.rb +14 -0
- data/lib/gamefic/scanner/nesting.rb +39 -0
- data/lib/gamefic/scanner/result.rb +60 -0
- data/lib/gamefic/scanner/strict.rb +31 -0
- data/lib/gamefic/scanner.rb +33 -111
- data/lib/gamefic/scope/descendants.rb +16 -0
- data/lib/gamefic/scope/family.rb +31 -8
- data/lib/gamefic/scope.rb +1 -0
- data/lib/gamefic/scriptable/actions.rb +4 -23
- data/lib/gamefic/scriptable/entities.rb +32 -17
- data/lib/gamefic/scriptable/plot_proxies.rb +29 -0
- data/lib/gamefic/scriptable/proxies.rb +31 -0
- data/lib/gamefic/scriptable/queries.rb +27 -8
- data/lib/gamefic/scriptable/scenes.rb +7 -1
- data/lib/gamefic/scriptable.rb +78 -16
- data/lib/gamefic/stage.rb +2 -2
- data/lib/gamefic/subplot.rb +33 -1
- data/lib/gamefic/version.rb +1 -1
- data/lib/gamefic.rb +1 -1
- metadata +17 -4
- data/lib/gamefic/composer.rb +0 -70
- data/lib/gamefic/scriptable/proxy.rb +0 -69
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5c61efc096ecbacdd43431dd3f0b755120650258a4a3e5d724692957ee45f53
|
4
|
+
data.tar.gz: 499d740594a35e767c86fae7610a51dafa9ba647658af7b3ab46c61de4335501
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd430f5dfa970cacfe12915ff09284ef286b1116aa701ce5484472fdf880b68a5de91953182189c647e90c527653bde92db26016045ac5ea5304e660082979fe
|
7
|
+
data.tar.gz: 1726fc542b40c55443bc9b452df85d94fe0831fa19bc73e40076cd6768e92bc71e504fd2da7998ef65b913c21f8b3ccb7bd4f04329186b88e71eb9dc675b0b69
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
## 3.6.0 - October 6, 2024
|
2
|
+
- Normalized arguments accept strings
|
3
|
+
- Smarter picks and proxies
|
4
|
+
- Commands prefer strictness over precision
|
5
|
+
- Queries scan for ambiguity before filtering through arguments
|
6
|
+
- Abstract queries
|
7
|
+
- Command logging
|
8
|
+
|
9
|
+
## 3.5.0 - October 5, 2024
|
10
|
+
- Configurable scanners
|
11
|
+
- Refactored scanners and queries
|
12
|
+
- Allow assignment to nil instance variables in stage
|
13
|
+
- Lazy proxies
|
14
|
+
- Remove buggy index proxies
|
15
|
+
- Chapter inherits Narrative
|
16
|
+
- Plot attribute proxy for chapters and subplots
|
17
|
+
- Entity#leave
|
18
|
+
- Descendants query
|
19
|
+
- Persistent subplots
|
20
|
+
- Entity#broadcast
|
21
|
+
- Ascendants in family query
|
22
|
+
|
1
23
|
## 3.4.0 - September 10, 2024
|
2
24
|
- Chapters
|
3
25
|
- Subplots and chapters do not repeat plot scripts
|
data/README.md
CHANGED
@@ -16,7 +16,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
16
16
|
|
17
17
|
## Contributing
|
18
18
|
|
19
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/castwide/gamefic
|
19
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/castwide/gamefic.
|
20
20
|
|
21
21
|
## License
|
22
22
|
|
data/lib/gamefic/action.rb
CHANGED
data/lib/gamefic/active/epic.rb
CHANGED
data/lib/gamefic/active.rb
CHANGED
data/lib/gamefic/chapter.rb
CHANGED
@@ -6,17 +6,15 @@ module Gamefic
|
|
6
6
|
# adding the required instance variables, methods, and attributes to the
|
7
7
|
# plot.
|
8
8
|
#
|
9
|
-
# Chapters are similar to subplots with
|
10
|
-
# * Chapters persist for the duration of
|
9
|
+
# Chapters are similar to subplots with three important exceptions:
|
10
|
+
# * Chapters normally persist for the duration of a plot.
|
11
11
|
# * Players do not need to be introduced to a chapter.
|
12
|
-
# *
|
13
|
-
# * Using `make` to create an entity in a chapter adds it to the parent
|
14
|
-
# plot's entity list.
|
12
|
+
# * Chapters share their plot's entities, players, and rulebook.
|
15
13
|
#
|
16
14
|
# @example
|
17
15
|
# class MyChapter < Gamefic::Chapter
|
18
|
-
#
|
19
|
-
# @thing
|
16
|
+
# def thing
|
17
|
+
# @thing ||= make Gamefic::Entity, name: 'chapter thing'
|
20
18
|
# end
|
21
19
|
# end
|
22
20
|
#
|
@@ -25,64 +23,49 @@ module Gamefic
|
|
25
23
|
# end
|
26
24
|
#
|
27
25
|
# plot = MyPlot.new
|
28
|
-
# plot.entities
|
29
|
-
# plot.
|
26
|
+
# plot.entities #=> [<#Gamefic::Entity a chapter thing>]
|
27
|
+
# plot.thing # raises NoMethodError
|
28
|
+
# plot.chapters.first.thing #=> <#Gamefic::Entity a chapter thing>
|
30
29
|
#
|
31
|
-
class Chapter
|
32
|
-
extend Scriptable
|
33
|
-
|
34
|
-
include Scriptable::Actions
|
35
|
-
include Scriptable::Events
|
36
|
-
include Scriptable::Proxy
|
37
|
-
include Scriptable::Queries
|
38
|
-
include Scriptable::Scenes
|
30
|
+
class Chapter < Narrative
|
31
|
+
extend Scriptable::PlotProxies
|
39
32
|
|
40
33
|
# @return [Plot]
|
41
34
|
attr_reader :plot
|
42
35
|
|
43
36
|
# @param plot [Plot]
|
44
|
-
def initialize
|
37
|
+
def initialize(plot)
|
45
38
|
@plot = plot
|
46
|
-
|
47
|
-
|
48
|
-
def included_blocks
|
49
|
-
self.class.included_blocks - plot.included_blocks
|
50
|
-
end
|
51
|
-
|
52
|
-
def seed
|
53
|
-
included_blocks.select(&:seed?).each { |blk| Stage.run self, &blk.code }
|
39
|
+
# The plot is responsible for hydrating chapters
|
40
|
+
super(hydrate: false)
|
54
41
|
end
|
55
42
|
|
56
43
|
def script
|
57
44
|
included_blocks.select(&:script?).each { |blk| Stage.run self, &blk.code }
|
58
45
|
end
|
59
46
|
|
60
|
-
def
|
61
|
-
plot.
|
62
|
-
end
|
63
|
-
|
64
|
-
def make klass, **opts
|
65
|
-
plot.make klass, **opts
|
47
|
+
def included_blocks
|
48
|
+
self.class.included_blocks - plot.included_blocks
|
66
49
|
end
|
67
50
|
|
68
|
-
def
|
69
|
-
plot.
|
51
|
+
def rulebook
|
52
|
+
plot.rulebook
|
70
53
|
end
|
71
54
|
|
72
|
-
def
|
73
|
-
plot.
|
55
|
+
def entity_vault
|
56
|
+
plot.entity_vault
|
74
57
|
end
|
75
58
|
|
76
|
-
def
|
77
|
-
plot.
|
59
|
+
def player_vault
|
60
|
+
plot.player_vault
|
78
61
|
end
|
79
62
|
|
80
|
-
def
|
81
|
-
plot.
|
63
|
+
def subplots
|
64
|
+
plot.subplots
|
82
65
|
end
|
83
66
|
|
84
|
-
def
|
85
|
-
plot.
|
67
|
+
def branch(...)
|
68
|
+
plot.branch(...)
|
86
69
|
end
|
87
70
|
end
|
88
71
|
end
|
data/lib/gamefic/command.rb
CHANGED
@@ -10,11 +10,59 @@ module Gamefic
|
|
10
10
|
# @return [Array<Array<Entity>, Entity, String>]
|
11
11
|
attr_reader :arguments
|
12
12
|
|
13
|
+
# @return [Integer]
|
14
|
+
attr_reader :strictness
|
15
|
+
|
16
|
+
# @return [Integer]
|
17
|
+
attr_reader :precision
|
18
|
+
|
13
19
|
# @param verb [Symbol]
|
14
20
|
# @param arguments [Array<Array<Entity>, Entity, String>]
|
15
|
-
|
21
|
+
# @param strictness [Integer]
|
22
|
+
# @param precision [Integer]
|
23
|
+
#
|
24
|
+
# @todo Consider making strictness and precision required or providing
|
25
|
+
# another generator
|
26
|
+
def initialize verb, arguments, strictness = 0, precision = 0
|
16
27
|
@verb = verb
|
17
28
|
@arguments = arguments
|
29
|
+
@strictness = strictness
|
30
|
+
@precision = precision
|
31
|
+
end
|
32
|
+
|
33
|
+
def substantiality
|
34
|
+
@substantiality ||= arguments.that_are(Entity).length + (verb ? 1 : 0)
|
35
|
+
end
|
36
|
+
|
37
|
+
def inspect
|
38
|
+
"#<#{self.class} #{([verb] + arguments).map(&:inspect).join(', ')}>"
|
39
|
+
end
|
40
|
+
|
41
|
+
class << self
|
42
|
+
# Compose a command from input.
|
43
|
+
#
|
44
|
+
# @param actor [Actor]
|
45
|
+
# @param input [String]
|
46
|
+
# @return [Command]
|
47
|
+
def compose actor, input
|
48
|
+
expressions = Syntax.tokenize(input, actor.epic.syntaxes)
|
49
|
+
expressions.flat_map { |expression| expression_to_commands(actor, expression) }
|
50
|
+
.first || Command.new(nil, [])
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# @param actor [Actor]
|
56
|
+
# @param expression [Expression]
|
57
|
+
# @return [Array<Command>]
|
58
|
+
def expression_to_commands actor, expression
|
59
|
+
Gamefic.logger.info "Evaluating #{expression.inspect}"
|
60
|
+
actor.epic
|
61
|
+
.responses_for(expression.verb)
|
62
|
+
.map { |response| response.to_command(actor, expression) }
|
63
|
+
.compact
|
64
|
+
.sort_by.with_index { |result, idx| [-result.substantiality, -result.strictness, -result.precision, idx] }
|
65
|
+
end
|
18
66
|
end
|
19
67
|
end
|
20
68
|
end
|
data/lib/gamefic/dispatcher.rb
CHANGED
@@ -4,13 +4,15 @@ module Gamefic
|
|
4
4
|
# The action executor for character commands.
|
5
5
|
#
|
6
6
|
class Dispatcher
|
7
|
+
include Logging
|
8
|
+
|
7
9
|
# @param actor [Actor]
|
8
10
|
# @param command [Command]
|
9
11
|
def initialize actor, command
|
10
12
|
@actor = actor
|
11
13
|
@command = command
|
12
14
|
@executed = false
|
13
|
-
|
15
|
+
Gamefic.logger.info "Dispatching #{command.inspect}"
|
14
16
|
end
|
15
17
|
|
16
18
|
# Run the dispatcher.
|
@@ -23,11 +25,11 @@ module Gamefic
|
|
23
25
|
action = next_action
|
24
26
|
return unless action
|
25
27
|
|
26
|
-
|
28
|
+
actor.epic.rulebooks.flat_map { |rlbk| rlbk.run_before_actions action }
|
27
29
|
return if action.cancelled?
|
28
30
|
|
29
31
|
action.execute
|
30
|
-
|
32
|
+
actor.epic.rulebooks.flat_map { |rlbk| rlbk.run_after_actions action }
|
31
33
|
action
|
32
34
|
end
|
33
35
|
|
@@ -46,9 +48,9 @@ module Gamefic
|
|
46
48
|
# @param input [String]
|
47
49
|
# @return [Dispatcher]
|
48
50
|
def self.dispatch actor, input
|
49
|
-
expressions = Syntax.tokenize(input, actor.epic.syntaxes)
|
50
|
-
|
51
|
-
new(actor,
|
51
|
+
# expressions = Syntax.tokenize(input, actor.epic.syntaxes)
|
52
|
+
# new(actor, Command.compose(actor, expressions))
|
53
|
+
new(actor, Command.compose(actor, input))
|
52
54
|
end
|
53
55
|
|
54
56
|
# @param actor [Active]
|
@@ -82,31 +84,6 @@ module Gamefic
|
|
82
84
|
|
83
85
|
return Action.new(actor, @command.arguments, response) if response.accept?(actor, @command)
|
84
86
|
end
|
85
|
-
finalize
|
86
|
-
end
|
87
|
-
|
88
|
-
# @return [void]
|
89
|
-
def run_before_action_hooks action
|
90
|
-
actor.epic.rulebooks.flat_map { |rlbk| rlbk.run_before_actions action }
|
91
|
-
end
|
92
|
-
|
93
|
-
# @return [void]
|
94
|
-
def run_after_action_hooks action
|
95
|
-
actor.epic.rulebooks.flat_map { |rlbk| rlbk.run_after_actions action }
|
96
|
-
end
|
97
|
-
|
98
|
-
# If the dispatcher proceeds through all possible responses, it can fall
|
99
|
-
# back to a nil response as a catchall for commands that could not be
|
100
|
-
# completed.
|
101
|
-
#
|
102
|
-
# @return [Action, nil]
|
103
|
-
def finalize
|
104
|
-
return nil if @finalized
|
105
|
-
|
106
|
-
@finalized = true
|
107
|
-
@command = Command.new(nil, ["#{command.verb} #{command.arguments.join(' ').strip}"])
|
108
|
-
@responses = actor.epic.responses_for(nil)
|
109
|
-
next_action
|
110
87
|
end
|
111
88
|
end
|
112
89
|
end
|
data/lib/gamefic/entity.rb
CHANGED
@@ -55,6 +55,32 @@ module Gamefic
|
|
55
55
|
"#<#{self.class} #{name}>"
|
56
56
|
end
|
57
57
|
|
58
|
+
# Move this entity to its parent entity.
|
59
|
+
#
|
60
|
+
# @example
|
61
|
+
# room = Gamefic::Entity.new(name: 'room')
|
62
|
+
# person = Gamefic::Entity.new(name: 'person', parent: room)
|
63
|
+
# thing = Gamefic::Entity.new(name: 'thing', parent: person)
|
64
|
+
#
|
65
|
+
# thing.parent #=> person
|
66
|
+
# thing.leave
|
67
|
+
# thing.parent #=> room
|
68
|
+
#
|
69
|
+
# @return [void]
|
70
|
+
def leave
|
71
|
+
self.parent = parent&.parent
|
72
|
+
end
|
73
|
+
|
74
|
+
# Tell a message to all of this entity's accessible descendants.
|
75
|
+
#
|
76
|
+
# @param message [String]
|
77
|
+
# @return [void]
|
78
|
+
def broadcast message
|
79
|
+
Query::Scoped.new(Scope::Descendants).select(self)
|
80
|
+
.that_are(Active, proc(&:acting?))
|
81
|
+
.each { |actor| actor.tell message }
|
82
|
+
end
|
83
|
+
|
58
84
|
class << self
|
59
85
|
# Set or update the default attributes for new instances.
|
60
86
|
#
|
data/lib/gamefic/expression.rb
CHANGED
data/lib/gamefic/narrative.rb
CHANGED
@@ -14,16 +14,18 @@ module Gamefic
|
|
14
14
|
include Scriptable::Actions
|
15
15
|
include Scriptable::Entities
|
16
16
|
include Scriptable::Events
|
17
|
-
include Scriptable::
|
17
|
+
include Scriptable::Proxies
|
18
18
|
include Scriptable::Queries
|
19
19
|
include Scriptable::Scenes
|
20
20
|
|
21
21
|
attr_reader :rulebook
|
22
22
|
|
23
|
-
def initialize
|
23
|
+
def initialize(hydrate: true)
|
24
|
+
return unless hydrate
|
25
|
+
|
24
26
|
seed
|
25
27
|
script
|
26
|
-
|
28
|
+
post_script
|
27
29
|
end
|
28
30
|
|
29
31
|
def seed
|
@@ -35,15 +37,17 @@ module Gamefic
|
|
35
37
|
included_blocks.select(&:script?).each { |blk| Stage.run self, &blk.code }
|
36
38
|
end
|
37
39
|
|
40
|
+
# @return [Array<Module>]
|
38
41
|
def included_blocks
|
39
42
|
self.class.included_blocks
|
40
43
|
end
|
41
44
|
|
42
|
-
def
|
45
|
+
def post_script
|
43
46
|
entity_vault.lock
|
44
47
|
rulebook.freeze
|
45
48
|
end
|
46
49
|
|
50
|
+
# @return [Array<Symbol>]
|
47
51
|
def scenes
|
48
52
|
rulebook.scenes.names
|
49
53
|
end
|
@@ -111,7 +115,7 @@ module Gamefic
|
|
111
115
|
|
112
116
|
def hydrate
|
113
117
|
script
|
114
|
-
|
118
|
+
post_script
|
115
119
|
end
|
116
120
|
|
117
121
|
def self.inherited klass
|
data/lib/gamefic/node.rb
CHANGED
@@ -88,11 +88,9 @@ module Gamefic
|
|
88
88
|
end
|
89
89
|
|
90
90
|
def validate_parent(node)
|
91
|
-
raise NodeError,
|
92
|
-
|
93
|
-
raise NodeError, "
|
94
|
-
|
95
|
-
raise NodeError, 'Node cannot be a child of a descendant' if flatten.include?(node)
|
91
|
+
raise NodeError, "Parent of #{inspect} must be a Node, received #{node.inspect}" unless node.is_a?(Node) || node.nil?
|
92
|
+
raise NodeError, "#{inspect} cannot be its own parent" if node == self
|
93
|
+
raise NodeError, "#{inspect} cannot be a child of descendant #{node.inspect}" if flatten.include?(node)
|
96
94
|
end
|
97
95
|
end
|
98
96
|
end
|
data/lib/gamefic/plot.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
class Proxy
|
5
|
+
class Base
|
6
|
+
attr_reader :args
|
7
|
+
|
8
|
+
def initialize *args, raise: false
|
9
|
+
@args = args
|
10
|
+
@raise = raise
|
11
|
+
end
|
12
|
+
|
13
|
+
def raise?
|
14
|
+
@raise
|
15
|
+
end
|
16
|
+
|
17
|
+
def fetch narrative
|
18
|
+
result = select(narrative)
|
19
|
+
return result if result
|
20
|
+
raise "#{self.class} failed for #{args.inspect}" if raise?
|
21
|
+
end
|
22
|
+
|
23
|
+
def select narrative
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gamefic
|
4
|
+
# @todo Turn this into a module after the old proxies are completely deprecated
|
5
|
+
#
|
6
|
+
class Proxy
|
7
|
+
require 'gamefic/proxy/base'
|
8
|
+
require 'gamefic/proxy/config'
|
9
|
+
require 'gamefic/proxy/pick'
|
10
|
+
require 'gamefic/proxy/plot_pick'
|
11
|
+
|
12
|
+
TYPES = %i[attr ivar pick pick! plot_pick plot_pick! config].freeze
|
13
|
+
|
14
|
+
# @return [Symbol]
|
15
|
+
attr_reader :type
|
16
|
+
|
17
|
+
# @return [Symbol, Array<Symbol>, String, Integer]
|
18
|
+
attr_reader :key
|
19
|
+
|
20
|
+
# @param type [Symbol]
|
21
|
+
# @param key [Symbol, String, Array]
|
22
|
+
def initialize type, key
|
23
|
+
Gamefic.logger.debug "Using deprecated #{type} proxy"
|
24
|
+
@type = type
|
25
|
+
validate_type
|
26
|
+
@key = type == :config ? [key].compact : key
|
27
|
+
end
|
28
|
+
|
29
|
+
def fetch narrative
|
30
|
+
send(type, narrative) ||
|
31
|
+
raise(ArgumentError, "Unable to fetch entity from proxy agent symbol `#{key}`")
|
32
|
+
end
|
33
|
+
|
34
|
+
def [](key)
|
35
|
+
raise ArgumentError, 'Invalid []' unless type == :config
|
36
|
+
|
37
|
+
@key.push key
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def attr narrative
|
44
|
+
Stage.run(narrative, [key].flatten) { |keys| keys.inject(self) { |obj, key| obj.send key } }
|
45
|
+
rescue NoMethodError
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def ivar narrative
|
50
|
+
narrative.instance_variable_get key
|
51
|
+
end
|
52
|
+
|
53
|
+
def pick narrative
|
54
|
+
narrative.pick *key
|
55
|
+
end
|
56
|
+
|
57
|
+
def pick! narrative
|
58
|
+
narrative.pick! *key
|
59
|
+
end
|
60
|
+
|
61
|
+
def plot_pick narrative
|
62
|
+
narrative.plot.pick *key
|
63
|
+
end
|
64
|
+
|
65
|
+
def plot_pick! narrative
|
66
|
+
narrative.plot.pick! *key
|
67
|
+
end
|
68
|
+
|
69
|
+
def config narrative
|
70
|
+
key.inject(narrative.config) { |hash, key| hash[key] }
|
71
|
+
end
|
72
|
+
|
73
|
+
def validate_type
|
74
|
+
return if TYPES.include?(type)
|
75
|
+
|
76
|
+
raise ArgumentError, "Invalid proxy type `#{type}` (must be #{TYPES.join_or})"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|