gamefic 3.6.0 → 4.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/.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
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Gamefic
|
|
4
|
-
module Scene
|
|
5
|
-
# The base class for scenes. Authors can instantiate this class directly
|
|
6
|
-
# and customize it with on_start and on_finish blocks.
|
|
7
|
-
#
|
|
8
|
-
class Default
|
|
9
|
-
# @return [Symbol]
|
|
10
|
-
attr_reader :name
|
|
11
|
-
|
|
12
|
-
# @param name [Symbol]
|
|
13
|
-
# @param narrative [Narrative]
|
|
14
|
-
# @param on_start [Proc, nil]
|
|
15
|
-
# @param on_finish [Proc, nil]
|
|
16
|
-
# @yieldparam [self]
|
|
17
|
-
def initialize name, narrative, on_start: nil, on_finish: nil
|
|
18
|
-
@name = name
|
|
19
|
-
@narrative = narrative
|
|
20
|
-
@start_blocks = []
|
|
21
|
-
@finish_blocks = []
|
|
22
|
-
@start_blocks.push on_start if on_start
|
|
23
|
-
@finish_blocks.push on_finish if on_finish
|
|
24
|
-
yield(self) if block_given?
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# @return [String]
|
|
28
|
-
def type
|
|
29
|
-
@type ||= self.class.to_s.sub(/^Gamefic::Scene::/, '')
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def new_props(**context)
|
|
33
|
-
self.class.props_class.new(self, **context)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def on_start &block
|
|
37
|
-
@start_blocks.push block
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def on_finish &block
|
|
41
|
-
@finish_blocks.push block
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# @param actor [Gamefic::Actor]
|
|
45
|
-
# @param props [Props::Default]
|
|
46
|
-
# @return [void]
|
|
47
|
-
def start actor, props
|
|
48
|
-
props.output[:scene] = to_hash
|
|
49
|
-
props.output[:prompt] = props.prompt
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# @param actor [Gamefic::Actor]
|
|
53
|
-
# @param props [Props::Default]
|
|
54
|
-
# @return [void]
|
|
55
|
-
def finish actor, props
|
|
56
|
-
props.input = actor.queue.shift&.strip
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def run_start_blocks actor, props
|
|
60
|
-
@start_blocks.each { |blk| Stage.run(@narrative, actor, props, &blk) }
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def run_finish_blocks actor, props
|
|
64
|
-
@finish_blocks.each { |blk| Stage.run(@narrative, actor, props, &blk) }
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def self.props_class
|
|
68
|
-
@props_class ||= Props::Default
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def conclusion?
|
|
72
|
-
is_a?(Conclusion)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def to_hash
|
|
76
|
-
{ name: name, type: type }
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
class << self
|
|
80
|
-
protected
|
|
81
|
-
|
|
82
|
-
def use_props_class klass
|
|
83
|
-
@props_class = klass
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
end
|
data/lib/gamefic/scope/base.rb
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Gamefic
|
|
4
|
-
module Scope
|
|
5
|
-
# The base class for a Scoped query's scope.
|
|
6
|
-
#
|
|
7
|
-
class Base
|
|
8
|
-
attr_reader :context
|
|
9
|
-
|
|
10
|
-
# @param [Gamefic::Entity]
|
|
11
|
-
def initialize context
|
|
12
|
-
@context = context
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# @param [Array<Gamefic::Entity>]
|
|
16
|
-
def matches
|
|
17
|
-
[]
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# @param [Gamefic::Entity]
|
|
21
|
-
def self.matches context
|
|
22
|
-
new(context).matches
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def self.precision
|
|
26
|
-
0
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
private
|
|
30
|
-
|
|
31
|
-
# Return an array of the entity's accessible descendants.
|
|
32
|
-
#
|
|
33
|
-
# @param [Entity]
|
|
34
|
-
# @return [Array<Entity>]
|
|
35
|
-
def subquery_accessible entity
|
|
36
|
-
return [] unless entity&.accessible?
|
|
37
|
-
|
|
38
|
-
entity.children.flat_map do |c|
|
|
39
|
-
[c] + subquery_accessible(c)
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Gamefic
|
|
4
|
-
module Scope
|
|
5
|
-
# The Children scope returns an entity's children and all accessible
|
|
6
|
-
# descendants.
|
|
7
|
-
#
|
|
8
|
-
class Children < Base
|
|
9
|
-
def matches
|
|
10
|
-
context.children.flat_map do |c|
|
|
11
|
-
[c] + subquery_accessible(c)
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Gamefic
|
|
4
|
-
module Scope
|
|
5
|
-
# The Descendants scope returns an entity's children and accessible
|
|
6
|
-
# descendants.
|
|
7
|
-
#
|
|
8
|
-
class Descendants < Base
|
|
9
|
-
def matches
|
|
10
|
-
context.children.flat_map do |child|
|
|
11
|
-
[child] + subquery_accessible(child)
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
data/lib/gamefic/scope/family.rb
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Gamefic
|
|
4
|
-
module Scope
|
|
5
|
-
# The Family scope returns an entity's ascendants, descendants, siblings,
|
|
6
|
-
# and siblings' descendants.
|
|
7
|
-
#
|
|
8
|
-
class Family < Base
|
|
9
|
-
def matches
|
|
10
|
-
match_ascendants + match_descendants + match_siblings
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
private
|
|
14
|
-
|
|
15
|
-
def match_ascendants
|
|
16
|
-
[].tap do |result|
|
|
17
|
-
here = context.parent
|
|
18
|
-
while here
|
|
19
|
-
result.push here
|
|
20
|
-
here = here.parent
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def match_descendants
|
|
26
|
-
context.children.flat_map do |child|
|
|
27
|
-
[child] + subquery_accessible(child)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def match_siblings
|
|
32
|
-
return [] unless context.parent
|
|
33
|
-
|
|
34
|
-
context.parent
|
|
35
|
-
.children
|
|
36
|
-
.that_are_not(context)
|
|
37
|
-
.flat_map do |child|
|
|
38
|
-
[child] + subquery_accessible(child)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
data/lib/gamefic/scope/myself.rb
DELETED
data/lib/gamefic/scope/parent.rb
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Gamefic
|
|
4
|
-
module Scope
|
|
5
|
-
# A query scope that matches the entity's siblings, i.e., the other
|
|
6
|
-
# entities that share its parent.
|
|
7
|
-
#
|
|
8
|
-
class Siblings < Base
|
|
9
|
-
def matches
|
|
10
|
-
context.parent.children - [context]
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
data/lib/gamefic/scope.rb
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'gamefic/scope/base'
|
|
4
|
-
require 'gamefic/scope/children'
|
|
5
|
-
require 'gamefic/scope/descendants'
|
|
6
|
-
require 'gamefic/scope/family'
|
|
7
|
-
require 'gamefic/scope/parent'
|
|
8
|
-
require 'gamefic/scope/siblings'
|
|
9
|
-
require 'gamefic/scope/myself'
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Gamefic
|
|
4
|
-
module Scriptable
|
|
5
|
-
# Scriptable methods related to creating actions.
|
|
6
|
-
#
|
|
7
|
-
module Actions
|
|
8
|
-
include Queries
|
|
9
|
-
|
|
10
|
-
# Create a response to a command.
|
|
11
|
-
# A Response uses the `verb` argument to identify the imperative verb
|
|
12
|
-
# that triggers the action. It can also accept queries to tokenize the
|
|
13
|
-
# remainder of the input and filter for particular entities or
|
|
14
|
-
# properties. The `block`` argument is the proc to execute when the input
|
|
15
|
-
# matches all of the Response's criteria (i.e., verb and queries).
|
|
16
|
-
#
|
|
17
|
-
# @example A simple Response.
|
|
18
|
-
# respond :wave do |actor|
|
|
19
|
-
# actor.tell "Hello!"
|
|
20
|
-
# end
|
|
21
|
-
# # The command "wave" will respond "Hello!"
|
|
22
|
-
#
|
|
23
|
-
# @example A Response that accepts a Character
|
|
24
|
-
# respond :salute, available(Character) do |actor, character|
|
|
25
|
-
# actor.tell "#{The character} returns your salute."
|
|
26
|
-
# end
|
|
27
|
-
#
|
|
28
|
-
# @param verb [Symbol] An imperative verb for the command
|
|
29
|
-
# @param args [Array<Object>] Filters for the command's tokens
|
|
30
|
-
# @yieldparam [Gamefic::Actor]
|
|
31
|
-
# @return [Symbol]
|
|
32
|
-
def respond(verb, *args, &proc)
|
|
33
|
-
rulebook.calls.add_response Response.new(verb, self, *args, &proc)
|
|
34
|
-
verb
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Create a meta response for a command.
|
|
38
|
-
# Meta responses are very similar to standard responses, except they're
|
|
39
|
-
# flagged as meta (`Response#meta?`) to indicate that they provide a
|
|
40
|
-
# feature that is not considered an in-game action, such as displaying
|
|
41
|
-
# help documentation or a scoreboard.
|
|
42
|
-
#
|
|
43
|
-
# @example A simple meta Response
|
|
44
|
-
# meta :credits do |actor|
|
|
45
|
-
# actor.tell "This game was written by John Smith."
|
|
46
|
-
# end
|
|
47
|
-
#
|
|
48
|
-
# @param verb [Symbol] An imperative verb for the command
|
|
49
|
-
# @param args [Array<Object>] Filters for the command's tokens
|
|
50
|
-
# @yieldparam [Gamefic::Actor]
|
|
51
|
-
# @return [Symbol]
|
|
52
|
-
def meta(verb, *args, &proc)
|
|
53
|
-
rulebook.calls.add_response Response.new(verb, self, *args, meta: true, &proc)
|
|
54
|
-
verb
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Add a proc to be evaluated before a character executes an action.
|
|
58
|
-
# When verbs are specified, the proc will only be evaluated if the
|
|
59
|
-
# action's verb matches them.
|
|
60
|
-
#
|
|
61
|
-
# @param verbs [Array<Symbol>]
|
|
62
|
-
# @yieldparam [Gamefic::Action]
|
|
63
|
-
# @return [Action::Hook]
|
|
64
|
-
def before_action *verbs, &block
|
|
65
|
-
rulebook.hooks.before_action self, *verbs, &block
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Add a proc to be evaluated after a character executes an action.
|
|
69
|
-
# When a verbs are specified, the proc will only be evaluated if the
|
|
70
|
-
# action's verb matches them.
|
|
71
|
-
#
|
|
72
|
-
# @param verbs [Array<Symbol>]
|
|
73
|
-
# @yieldparam [Gamefic::Action]
|
|
74
|
-
# @return [Action::Hook]
|
|
75
|
-
def after_action *verbs, &block
|
|
76
|
-
rulebook.hooks.after_action self, *verbs, &block
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# Create an alternate Syntax for a response.
|
|
80
|
-
# The command and its translation can be parameterized.
|
|
81
|
-
#
|
|
82
|
-
# @example Create a synonym for an `inventory` response.
|
|
83
|
-
# interpret "catalogue", "inventory"
|
|
84
|
-
# # The command "catalogue" will be translated to "inventory"
|
|
85
|
-
#
|
|
86
|
-
# @example Create a parameterized synonym for a `look` response.
|
|
87
|
-
# interpret "scrutinize :entity", "look :entity"
|
|
88
|
-
# # The command "scrutinize chair" will be translated to "look chair"
|
|
89
|
-
#
|
|
90
|
-
# @param command [String] The format of the original command
|
|
91
|
-
# @param translation [String] The format of the translated command
|
|
92
|
-
# @return [Syntax] the Syntax object
|
|
93
|
-
def interpret command, translation
|
|
94
|
-
rulebook.calls.add_syntax Syntax.new(command, translation)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Verbs are the symbols that have responses defined in the rulebook.
|
|
98
|
-
#
|
|
99
|
-
# @example
|
|
100
|
-
# class MyPlot < Gamefic::Plot
|
|
101
|
-
# script do
|
|
102
|
-
# respond :think { |actor| actor.tell 'You think.' }
|
|
103
|
-
#
|
|
104
|
-
# verbs #=> [:think]
|
|
105
|
-
# end
|
|
106
|
-
# end
|
|
107
|
-
#
|
|
108
|
-
# @return [Array<Symbol>]
|
|
109
|
-
def verbs
|
|
110
|
-
rulebook.verbs
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
# Synonyms are a combination of the rulebook's concrete verbs plus the
|
|
114
|
-
# alternative variants defined in syntaxes.
|
|
115
|
-
#
|
|
116
|
-
# @example
|
|
117
|
-
# class MyPlot < Gamefic::Plot
|
|
118
|
-
# respond :think { |actor| actor.tell 'You think.' }
|
|
119
|
-
# interpret 'ponder', 'think'
|
|
120
|
-
#
|
|
121
|
-
# verbs #=> [:think]
|
|
122
|
-
# synonyms #=> [:think, :ponder]
|
|
123
|
-
# end
|
|
124
|
-
# end
|
|
125
|
-
#
|
|
126
|
-
# @return [Array<Symbol>]
|
|
127
|
-
def synonyms
|
|
128
|
-
rulebook.synonyms
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
# @return [Array<Syntax>]
|
|
132
|
-
def syntaxes
|
|
133
|
-
rulebook.syntaxes
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
end
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Gamefic
|
|
4
|
-
module Scriptable
|
|
5
|
-
# Scriptable methods related to creating event callbacks.
|
|
6
|
-
#
|
|
7
|
-
module Events
|
|
8
|
-
# Add a block to be executed on preparation of every turn.
|
|
9
|
-
#
|
|
10
|
-
# @example Increment a turn counter
|
|
11
|
-
# turn = 0
|
|
12
|
-
# on_ready do
|
|
13
|
-
# turn += 1
|
|
14
|
-
# end
|
|
15
|
-
#
|
|
16
|
-
def on_ready &block
|
|
17
|
-
rulebook.events.on_ready(Callback.new(self, block))
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Add a block to be executed for each player at the beginning of a turn.
|
|
21
|
-
#
|
|
22
|
-
# @example Tell the player how many turns they've played.
|
|
23
|
-
# on_player_ready do |player|
|
|
24
|
-
# player[:turns] ||= 1
|
|
25
|
-
# player.tell "Turn #{player[:turns]}"
|
|
26
|
-
# player[:turns] += 1
|
|
27
|
-
# end
|
|
28
|
-
#
|
|
29
|
-
# @yieldparam [Gamefic::Actor]
|
|
30
|
-
def on_player_ready &block
|
|
31
|
-
wrapper = proc do
|
|
32
|
-
players.each { |player| Stage.run(self, player, &block) }
|
|
33
|
-
end
|
|
34
|
-
on_ready &wrapper
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Add a block to be executed after the Plot is finished updating a turn.
|
|
38
|
-
#
|
|
39
|
-
def on_update &block
|
|
40
|
-
rulebook.events.on_update(Callback.new(self, block))
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Add a block to be executed for each player at the end of a turn.
|
|
44
|
-
#
|
|
45
|
-
# @yieldparam [Gamefic::Actor]
|
|
46
|
-
def on_player_update &block
|
|
47
|
-
wrapper = proc do
|
|
48
|
-
players.each { |player| Stage.run(self, player, &block) }
|
|
49
|
-
end
|
|
50
|
-
on_update &wrapper
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def on_conclude &block
|
|
54
|
-
rulebook.events.on_conclude(Callback.new(self, block))
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# @yieldparam [Actor]
|
|
58
|
-
# @return [Proc]
|
|
59
|
-
def on_player_conclude &block
|
|
60
|
-
rulebook.events.on_player_conclude(Callback.new(self, block))
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# @yieldparam [Actor]
|
|
64
|
-
# @yieldparam [Hash]
|
|
65
|
-
# @return [Proc]
|
|
66
|
-
def on_player_output &block
|
|
67
|
-
rulebook.events.on_player_output Callback.new(self, block)
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Gamefic
|
|
4
|
-
module Scriptable
|
|
5
|
-
module PlotProxies
|
|
6
|
-
def lazy_plot key
|
|
7
|
-
Logging.logger.warn "#{caller.first ? "#{caller.first}: " : ''}`lazy_plot` is deprecated. Use `plot_pick`, `plot_pick!, or pass the entity from the plot in a `config` option instead."
|
|
8
|
-
Proxy.new(:attr, [:plot, key])
|
|
9
|
-
end
|
|
10
|
-
alias _plot lazy_plot
|
|
11
|
-
|
|
12
|
-
def attr_plot attr
|
|
13
|
-
define_method(attr) { plot.send(attr) }
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def plot_pick *args
|
|
17
|
-
Proxy::PlotPick.new(*args)
|
|
18
|
-
end
|
|
19
|
-
alias lazy_plot_pick plot_pick
|
|
20
|
-
alias _plot_pick plot_pick
|
|
21
|
-
|
|
22
|
-
def plot_pick! *args
|
|
23
|
-
Proxy::PlotPick.new(*args)
|
|
24
|
-
end
|
|
25
|
-
alias lazy_plot_pick! plot_pick!
|
|
26
|
-
alias _plot_pick! plot_pick!
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
data/lib/gamefic/snapshot.rb
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'corelib/marshal' if RUBY_ENGINE == 'opal' # Required in browser
|
|
4
|
-
require 'base64'
|
|
5
|
-
|
|
6
|
-
module Gamefic
|
|
7
|
-
# Save and restore plots.
|
|
8
|
-
#
|
|
9
|
-
module Snapshot
|
|
10
|
-
# Save a base64-encoded snapshot of a plot.
|
|
11
|
-
#
|
|
12
|
-
# @param plot [Plot]
|
|
13
|
-
# @return [String]
|
|
14
|
-
def self.save plot
|
|
15
|
-
cache = plot.detach
|
|
16
|
-
binary = Marshal.dump(plot)
|
|
17
|
-
plot.attach cache
|
|
18
|
-
Base64.encode64(binary)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Restore a plot from a base64-encoded string.
|
|
22
|
-
#
|
|
23
|
-
# @param snapshot [String]
|
|
24
|
-
# @return [Plot]
|
|
25
|
-
def self.restore snapshot
|
|
26
|
-
binary = Base64.decode64(snapshot)
|
|
27
|
-
Marshal.load(binary).tap do |plot|
|
|
28
|
-
plot.hydrate
|
|
29
|
-
# @todo Opal marshal dumps are not idempotent
|
|
30
|
-
next if RUBY_ENGINE == 'opal' || match?(plot, snapshot)
|
|
31
|
-
|
|
32
|
-
Logging.logger.warn "Scripts modified #{plot.class} data. Snapshot may not have restored properly"
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# True if the plot's state matches the snapshot.
|
|
37
|
-
#
|
|
38
|
-
# @param plot [Plot]
|
|
39
|
-
# @param snapshot [String]
|
|
40
|
-
def self.match?(plot, snapshot)
|
|
41
|
-
save(plot) == snapshot
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
data/lib/gamefic/stage.rb
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Gamefic
|
|
4
|
-
# A safe execution environment for narrative code.
|
|
5
|
-
#
|
|
6
|
-
module Stage
|
|
7
|
-
module_function
|
|
8
|
-
|
|
9
|
-
# @param narrative [Narrative]
|
|
10
|
-
def run(narrative, *args, &code)
|
|
11
|
-
container = narrative.clone
|
|
12
|
-
narrative.instance_exec(*args, &code).tap { validate_changes narrative, container, code }
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
OVERWRITEABLE_CLASSES = [String, Numeric, Symbol].freeze
|
|
16
|
-
|
|
17
|
-
SWAPPABLE_VALUES = [true, false].freeze
|
|
18
|
-
|
|
19
|
-
class << self
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
def validate_changes narrative, container, code
|
|
23
|
-
container.instance_variables.each do |var|
|
|
24
|
-
next unless narrative.instance_variables.include?(var)
|
|
25
|
-
|
|
26
|
-
cval = container.instance_variable_get(var)
|
|
27
|
-
|
|
28
|
-
nval = narrative.instance_variable_get(var)
|
|
29
|
-
next if cval == nval
|
|
30
|
-
|
|
31
|
-
validate_overwriteable(cval, nval, "Unsafe reassignment of #{var} in #{code}")
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def validate_overwriteable cval, nval, error
|
|
36
|
-
raise error unless overwriteable?(cval, nval)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def overwriteable? cval, nval
|
|
40
|
-
return true if cval.nil? || swappable?(cval, nval)
|
|
41
|
-
|
|
42
|
-
allowed = OVERWRITEABLE_CLASSES.find { |klass| cval.is_a?(klass) }
|
|
43
|
-
allowed && cval.is_a?(allowed)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def swappable? *values
|
|
47
|
-
values.all? { |val| SWAPPABLE_VALUES.include?(val) }
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Gamefic
|
|
4
|
-
class Syntax
|
|
5
|
-
# Template data for syntaxes.
|
|
6
|
-
#
|
|
7
|
-
class Template
|
|
8
|
-
PARAM_REGEXP = /^:[a-z0-9_]+$/i.freeze
|
|
9
|
-
|
|
10
|
-
# @return [String]
|
|
11
|
-
attr_reader :text
|
|
12
|
-
|
|
13
|
-
# @return [Array<String>]
|
|
14
|
-
attr_reader :params
|
|
15
|
-
|
|
16
|
-
def initialize text
|
|
17
|
-
@text = text.normalize
|
|
18
|
-
@params = @text.keywords.select { |word| word.start_with?(':') }
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def keywords
|
|
22
|
-
text.keywords
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def to_s
|
|
26
|
-
text
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def regexp
|
|
30
|
-
@regexp ||= Regexp.new("^#{make_tokens.join(' ')}$", Regexp::IGNORECASE)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def verb
|
|
34
|
-
@verb ||= Syntax.literal_or_nil(keywords.first)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def compare other
|
|
38
|
-
if keywords.length == other.keywords.length
|
|
39
|
-
other.verb <=> verb
|
|
40
|
-
else
|
|
41
|
-
other.keywords.length <=> keywords.length
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# @param tmpl_or_str [Template, String]
|
|
46
|
-
# @return [Template]
|
|
47
|
-
def self.to_template tmpl_or_str
|
|
48
|
-
return tmpl_or_str if tmpl_or_str.is_a?(Template)
|
|
49
|
-
|
|
50
|
-
Template.new(tmpl_or_str)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
private
|
|
54
|
-
|
|
55
|
-
# @return [Array<String>]
|
|
56
|
-
def make_tokens
|
|
57
|
-
keywords.map.with_index do |word, idx|
|
|
58
|
-
next word unless word.match?(PARAM_REGEXP)
|
|
59
|
-
|
|
60
|
-
next nil if idx.positive? && keywords[idx - 1].match?(PARAM_REGEXP)
|
|
61
|
-
|
|
62
|
-
'([\w\W\s\S]*?)'
|
|
63
|
-
end.compact
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|