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,47 +1,52 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
3
5
|
module Gamefic
|
|
4
6
|
module Scriptable
|
|
5
|
-
# Scriptable methods related to creating scenes.
|
|
6
|
-
#
|
|
7
7
|
module Scenes
|
|
8
|
-
#
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
#
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
# # The scene's finish is where you can process the user's input
|
|
18
|
-
# scene.on_finish do |actor, props|
|
|
19
|
-
# if props.input.empty?
|
|
20
|
-
# # You can use recue to start the scene again
|
|
21
|
-
# actor.recue
|
|
22
|
-
# else
|
|
23
|
-
# actor.tell "Hello, #{props.input}!"
|
|
24
|
-
# end
|
|
25
|
-
# end
|
|
26
|
-
# end
|
|
27
|
-
#
|
|
28
|
-
# @param name [Symbol]
|
|
29
|
-
# @param klass [Class<Scene::Default>]
|
|
30
|
-
# @param on_start [Proc, nil]
|
|
31
|
-
# @param on_finish [Proc, nil]
|
|
32
|
-
# @param block [Proc]
|
|
33
|
-
# @yieldparam [Scene]
|
|
34
|
-
# @return [Symbol]
|
|
35
|
-
def block name, klass = Scene::Default, on_start: nil, on_finish: nil, &blk
|
|
36
|
-
rulebook.scenes.add klass.new(name, self, on_start: on_start, on_finish: on_finish, &blk)
|
|
37
|
-
name
|
|
8
|
+
# @return [Scene::Base]
|
|
9
|
+
attr_reader :default_scene
|
|
10
|
+
|
|
11
|
+
# @return [Scene::Conclusion]
|
|
12
|
+
attr_reader :default_conclusion
|
|
13
|
+
|
|
14
|
+
def select_default_scene(klass)
|
|
15
|
+
scene_classes.add klass
|
|
16
|
+
@default_scene = klass
|
|
38
17
|
end
|
|
39
18
|
|
|
40
|
-
def
|
|
41
|
-
|
|
42
|
-
|
|
19
|
+
def select_default_conclusion(klass)
|
|
20
|
+
scene_classes.add klass
|
|
21
|
+
@default_conclusion = klass
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def named_scenes
|
|
25
|
+
@named_scenes ||= {}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def scene_classes
|
|
29
|
+
@scene_classes ||= Set.new
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def scene_classes_map
|
|
33
|
+
scene_classes.each_with_object(named_scenes.clone) { |klass, hash| hash[klass] = klass }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def scenes
|
|
37
|
+
scene_classes_map.values.uniq
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def block(scene, name = nil)
|
|
41
|
+
named_scenes[name] = scene if name
|
|
42
|
+
scene_classes.add scene
|
|
43
|
+
scene
|
|
44
|
+
end
|
|
45
|
+
alias scene block
|
|
46
|
+
|
|
47
|
+
def introductions
|
|
48
|
+
@introductions ||= []
|
|
43
49
|
end
|
|
44
|
-
alias precursor preface
|
|
45
50
|
|
|
46
51
|
# Add a block to be executed when a player is added to the game.
|
|
47
52
|
# Each Plot should only have one introduction.
|
|
@@ -57,10 +62,7 @@ module Gamefic
|
|
|
57
62
|
# @yieldparam [Props::Default]
|
|
58
63
|
# @return [Symbol]
|
|
59
64
|
def introduction(&start)
|
|
60
|
-
|
|
61
|
-
.introduction Scene::Default.new nil,
|
|
62
|
-
self,
|
|
63
|
-
on_start: proc { |actor, _props| Stage.run(self, actor, &start) }
|
|
65
|
+
introductions.push start
|
|
64
66
|
end
|
|
65
67
|
|
|
66
68
|
# Create a multiple-choice scene.
|
|
@@ -68,28 +70,23 @@ module Gamefic
|
|
|
68
70
|
# will restart if the user input is not a valid choice.
|
|
69
71
|
#
|
|
70
72
|
# @example
|
|
71
|
-
# multiple_choice :go_somewhere,
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
73
|
+
# multiple_choice :go_somewhere, do
|
|
74
|
+
# on_start do |actor, props|
|
|
75
|
+
# props.options.push 'Go to work', 'Go to school'
|
|
76
|
+
# end
|
|
77
|
+
#
|
|
78
|
+
# on_finish do |actor, props|
|
|
79
|
+
# # Assuming the user selected the first choice:
|
|
80
|
+
# props.selection #=> 'Go to work'
|
|
81
|
+
# props.index #=> 0
|
|
82
|
+
# props.number #=> 1
|
|
83
|
+
# end
|
|
76
84
|
# end
|
|
77
85
|
#
|
|
78
|
-
# @param name [Symbol]
|
|
79
|
-
# @
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
# @yieldparam [Actor]
|
|
83
|
-
# @yieldparam [Props::MultipleChoice]
|
|
84
|
-
# @return [Symbol]
|
|
85
|
-
def multiple_choice name, choices = [], prompt = 'What is your choice?', &blk
|
|
86
|
-
block name,
|
|
87
|
-
Scene::MultipleChoice,
|
|
88
|
-
on_start: proc { |_actor, props|
|
|
89
|
-
props.prompt = prompt
|
|
90
|
-
props.options.concat choices
|
|
91
|
-
},
|
|
92
|
-
on_finish: blk
|
|
86
|
+
# @param name [Symbol, nil]
|
|
87
|
+
# @return [Class<Scene::MultipleChoice>]
|
|
88
|
+
def multiple_choice(name = nil, &block)
|
|
89
|
+
block Class.new(Scene::MultipleChoice, &block), name
|
|
93
90
|
end
|
|
94
91
|
|
|
95
92
|
# Create a yes-or-no scene.
|
|
@@ -97,48 +94,36 @@ module Gamefic
|
|
|
97
94
|
# will restart if the user input is not a valid choice.
|
|
98
95
|
#
|
|
99
96
|
# @example
|
|
100
|
-
# yes_or_no :answer_scene
|
|
101
|
-
#
|
|
102
|
-
# actor.tell
|
|
103
|
-
#
|
|
104
|
-
#
|
|
97
|
+
# yes_or_no :answer_scene do
|
|
98
|
+
# on_start do |actor, props|
|
|
99
|
+
# actor.tell 'Yes or no?'
|
|
100
|
+
# end
|
|
101
|
+
#
|
|
102
|
+
# on_finish do |actor, props|
|
|
103
|
+
# if props.yes?
|
|
104
|
+
# actor.tell 'You said yes.'
|
|
105
|
+
# else
|
|
106
|
+
# actor.tell 'You said no.'
|
|
107
|
+
# end
|
|
105
108
|
# end
|
|
106
109
|
# end
|
|
107
110
|
#
|
|
108
|
-
# @param name [Symbol]
|
|
109
|
-
# @
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
# @return [Symbol]
|
|
113
|
-
def yes_or_no name, prompt = 'Answer:', &blk
|
|
114
|
-
block name,
|
|
115
|
-
Scene::YesOrNo,
|
|
116
|
-
on_start: proc { |_actor, props|
|
|
117
|
-
props.prompt = prompt
|
|
118
|
-
},
|
|
119
|
-
on_finish: blk
|
|
111
|
+
# @param name [Symbol, nil]
|
|
112
|
+
# @return [Class<Scene::YesOrNo>]
|
|
113
|
+
def yes_or_no(name = nil, &block)
|
|
114
|
+
block Class.new(Scene::YesOrNo, &block), name
|
|
120
115
|
end
|
|
121
116
|
|
|
122
|
-
#
|
|
123
|
-
#
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
# @param prompt [String, nil] The text to display when prompting the user to continue
|
|
133
|
-
# @yieldparam [Actor]
|
|
134
|
-
# @return [Symbol]
|
|
135
|
-
def pause name, prompt: 'Press enter to continue...', &start
|
|
136
|
-
block name,
|
|
137
|
-
Scene::Pause,
|
|
138
|
-
on_start: proc { |actor, props|
|
|
139
|
-
props.prompt = prompt if prompt
|
|
140
|
-
instance_exec(actor, props, &start)
|
|
141
|
-
}
|
|
117
|
+
# @param name [Symbol, nil]
|
|
118
|
+
# @return [Class<Scene::ActiveChoice>]
|
|
119
|
+
def active_choice(name = nil, &block)
|
|
120
|
+
block Class.new(Scene::ActiveChoice, &block), name
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# @param name [Symbol, nil]
|
|
124
|
+
# @return [Class<Scene::Pause>]
|
|
125
|
+
def pause(name = nil, &block)
|
|
126
|
+
block Class.new(Scene::Pause, &block), name
|
|
142
127
|
end
|
|
143
128
|
|
|
144
129
|
# Create a conclusion.
|
|
@@ -150,24 +135,12 @@ module Gamefic
|
|
|
150
135
|
# actor.tell 'GAME OVER'
|
|
151
136
|
# end
|
|
152
137
|
#
|
|
153
|
-
# @param name [Symbol]
|
|
154
|
-
# @
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
on_start: start
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
# @return [Array<Symbol>]
|
|
163
|
-
def scenes
|
|
164
|
-
rulebook.scenes.names
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
# @param name [Symbol]
|
|
168
|
-
# @return [Scene::Default, nil]
|
|
169
|
-
def scene(name)
|
|
170
|
-
rulebook.scenes[name]
|
|
138
|
+
# @param name [Symbol, nil]
|
|
139
|
+
# @return [Class<Scene::Conclusion>]
|
|
140
|
+
def conclusion(name = nil, &block)
|
|
141
|
+
block(Class.new(Scene::Conclusion) do
|
|
142
|
+
on_start(&block)
|
|
143
|
+
end, name)
|
|
171
144
|
end
|
|
172
145
|
end
|
|
173
146
|
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gamefic
|
|
4
|
+
module Scriptable
|
|
5
|
+
module Seeds
|
|
6
|
+
def seeds
|
|
7
|
+
@seeds ||= []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def seed *methods, &block
|
|
11
|
+
seeds.push(proc { methods.flatten.each { |method| send(method) } }) unless methods.empty?
|
|
12
|
+
seeds.push block if block
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Construct an entity.
|
|
16
|
+
#
|
|
17
|
+
# This method adds an instance method for the entity and a class method to
|
|
18
|
+
# reference it with a proxy.
|
|
19
|
+
#
|
|
20
|
+
# @param name [Symbol, String] The method name for the entity
|
|
21
|
+
# @param klass [Class<Gamefic::Entity>]
|
|
22
|
+
# @return [void]
|
|
23
|
+
def construct name, klass, **opts
|
|
24
|
+
ivname = "@#{name}"
|
|
25
|
+
define_method(name) do
|
|
26
|
+
return instance_variable_get(ivname) if instance_variable_defined?(ivname)
|
|
27
|
+
|
|
28
|
+
instance_variable_set(ivname, make(klass, **unproxy(opts)))
|
|
29
|
+
end
|
|
30
|
+
seed { send(name) }
|
|
31
|
+
define_singleton_method(name) { Proxy::Attr.new(name) }
|
|
32
|
+
end
|
|
33
|
+
alias attr_make construct
|
|
34
|
+
alias attr_seed construct
|
|
35
|
+
|
|
36
|
+
# Add an entity to be seeded when the narrative gets instantiated.
|
|
37
|
+
#
|
|
38
|
+
# @param klass [Class<Gamefic::Entity>]
|
|
39
|
+
# @return [void]
|
|
40
|
+
def make klass, **opts
|
|
41
|
+
seed { make(klass, **unproxy(opts)) }
|
|
42
|
+
end
|
|
43
|
+
alias make_seed make
|
|
44
|
+
alias seed_make make
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Lazy pick an entity.
|
|
48
|
+
#
|
|
49
|
+
# @example
|
|
50
|
+
# pick('the red box')
|
|
51
|
+
#
|
|
52
|
+
# @param args [Array]
|
|
53
|
+
# @return [Proxy]
|
|
54
|
+
def pick *args
|
|
55
|
+
Proxy::Pick.new(*args)
|
|
56
|
+
end
|
|
57
|
+
alias lazy_pick pick
|
|
58
|
+
|
|
59
|
+
# Lazy pick an entity or raise an error.
|
|
60
|
+
#
|
|
61
|
+
# @note The class method version of `pick!` returns a proxy, so the error
|
|
62
|
+
# won't get raised until it gets unproxied in an instance.
|
|
63
|
+
#
|
|
64
|
+
def pick! *args
|
|
65
|
+
Proxy::PickEx.new(*args)
|
|
66
|
+
end
|
|
67
|
+
alias lazy_pick! pick!
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gamefic
|
|
4
|
+
module Scriptable
|
|
5
|
+
module Syntaxes
|
|
6
|
+
# Create an alternate Syntax for a response.
|
|
7
|
+
# The command and its translation can be parameterized.
|
|
8
|
+
#
|
|
9
|
+
# @example Create a synonym for an `inventory` response.
|
|
10
|
+
# interpret "catalogue", "inventory"
|
|
11
|
+
# # The command "catalogue" will be translated to "inventory"
|
|
12
|
+
#
|
|
13
|
+
# @example Create a parameterized synonym for a `look` response.
|
|
14
|
+
# interpret "scrutinize :entity", "look :entity"
|
|
15
|
+
# # The command "scrutinize chair" will be translated to "look chair"
|
|
16
|
+
#
|
|
17
|
+
# @param command [String] The format of the original command
|
|
18
|
+
# @param translation [String] The format of the translated command
|
|
19
|
+
# @return [Syntax] the Syntax object
|
|
20
|
+
def interpret command, translation
|
|
21
|
+
syntaxes.push Syntax.new(command, translation)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def syntaxes
|
|
25
|
+
@syntaxes ||= []
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/gamefic/scriptable.rb
CHANGED
|
@@ -23,207 +23,22 @@ module Gamefic
|
|
|
23
23
|
# end
|
|
24
24
|
#
|
|
25
25
|
module Scriptable
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
require 'gamefic/scriptable/hooks'
|
|
27
|
+
require 'gamefic/scriptable/queries'
|
|
28
|
+
require 'gamefic/scriptable/syntaxes'
|
|
29
|
+
require 'gamefic/scriptable/responses'
|
|
30
|
+
require 'gamefic/scriptable/scenes'
|
|
31
|
+
require 'gamefic/scriptable/seeds'
|
|
32
|
+
|
|
33
|
+
include Hooks
|
|
34
34
|
include Queries
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
# @return [Array<Block>]
|
|
41
|
-
def blocks
|
|
42
|
-
@blocks ||= []
|
|
43
|
-
end
|
|
44
|
-
alias scripts blocks
|
|
45
|
-
|
|
46
|
-
# Add a block of code to be executed during initialization.
|
|
47
|
-
#
|
|
48
|
-
# These blocks are primarily used to define actions, scenes, and hooks in
|
|
49
|
-
# the narrative's rulebook. Entities and game data should be initialized
|
|
50
|
-
# with `seed`.
|
|
51
|
-
#
|
|
52
|
-
# @example
|
|
53
|
-
# class MyPlot < Gamefic::Plot
|
|
54
|
-
# script do
|
|
55
|
-
# introduction do |actor|
|
|
56
|
-
# actor.tell 'Hello, world!'
|
|
57
|
-
# end
|
|
58
|
-
#
|
|
59
|
-
# respond :wait do |actor|
|
|
60
|
-
# actor.tell 'Time passes.'
|
|
61
|
-
# end
|
|
62
|
-
# end
|
|
63
|
-
# end
|
|
64
|
-
#
|
|
65
|
-
def script &block
|
|
66
|
-
blocks.push Block.new(:script, block)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# Add a block of code to generate content after initialization.
|
|
70
|
-
#
|
|
71
|
-
# Seeds run after the initial scripts have been executed. Their primary
|
|
72
|
-
# use is to add entities and other data components, especially randomized
|
|
73
|
-
# or procedurally generated content that can vary between instances.
|
|
74
|
-
#
|
|
75
|
-
# @note Seeds do not get executed when a narrative is restored from a
|
|
76
|
-
# snapshot.
|
|
77
|
-
#
|
|
78
|
-
# @example
|
|
79
|
-
# class MyPlot < Gamefic::Plot
|
|
80
|
-
# seed do
|
|
81
|
-
# @thing = make Gamefic::Entity, name: 'a thing'
|
|
82
|
-
# end
|
|
83
|
-
# end
|
|
84
|
-
#
|
|
85
|
-
def seed &block
|
|
86
|
-
blocks.push Block.new(:seed, block)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# @return [Array<Block>]
|
|
90
|
-
def included_blocks
|
|
91
|
-
included_modules.that_are(Scriptable)
|
|
92
|
-
.uniq
|
|
93
|
-
.reverse
|
|
94
|
-
.flat_map(&:blocks)
|
|
95
|
-
.concat(blocks)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
# Seed an entity.
|
|
99
|
-
#
|
|
100
|
-
# @example
|
|
101
|
-
# make_seed Gamefic::Entity, name: 'thing'
|
|
102
|
-
#
|
|
103
|
-
# @param klass [Class<Gamefic::Entity>]
|
|
104
|
-
# @return [Proxy]
|
|
105
|
-
def make_seed klass, **opts
|
|
106
|
-
seed { make(klass, **opts) }
|
|
107
|
-
Proxy::Pick.new(klass, opts[:name], raise: true)
|
|
108
|
-
end
|
|
109
|
-
alias make make_seed
|
|
110
|
-
|
|
111
|
-
# Seed an entity with an attribute method.
|
|
112
|
-
#
|
|
113
|
-
# @example
|
|
114
|
-
# class Plot < Gamefic::Plot
|
|
115
|
-
# attr_seed :thing, Gamefic::Entity, name: 'thing'
|
|
116
|
-
# end
|
|
117
|
-
#
|
|
118
|
-
# plot = Plot.new
|
|
119
|
-
# plot.thing #=> #<Gamefic::Entity a thing>
|
|
120
|
-
#
|
|
121
|
-
# @param name [Symbol] The attribute name
|
|
122
|
-
# @param klass [Class<Gamefic::Entity>]
|
|
123
|
-
# @return [Proxy]
|
|
124
|
-
def attr_seed name, klass, **opts
|
|
125
|
-
ivname = "@#{name}"
|
|
126
|
-
define_method(name) do
|
|
127
|
-
return instance_variable_get(ivname) if instance_variable_defined?(ivname)
|
|
128
|
-
|
|
129
|
-
instance_variable_set(ivname, make(klass, **opts))
|
|
130
|
-
end
|
|
131
|
-
seed { send name }
|
|
132
|
-
Proxy.new(:attr, name)
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# @param symbol [Symbol]
|
|
136
|
-
# @return [Proxy]
|
|
137
|
-
def proxy symbol
|
|
138
|
-
Logging.logger.warn "#{caller.first ? "#{caller.first}: " : ''}`proxy` is deprecated. Use `pick` or `pick!` instead."
|
|
139
|
-
if symbol.to_s.start_with?('@')
|
|
140
|
-
Proxy.new(:ivar, symbol)
|
|
141
|
-
else
|
|
142
|
-
Proxy.new(:attr, symbol)
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
# Lazy reference an entity by its instance variable.
|
|
147
|
-
#
|
|
148
|
-
# @example
|
|
149
|
-
# lazy_ivar(:@variable)
|
|
150
|
-
#
|
|
151
|
-
# @param key [Symbol]
|
|
152
|
-
# @return [Proxy]
|
|
153
|
-
def lazy_ivar key
|
|
154
|
-
Gamefic.logger.warn "#{caller.first ? "#{caller.first}: " : ''}`lazy_ivar` is deprecated. Use `pick` or `pick!` instead."
|
|
155
|
-
Proxy.new(:ivar, key)
|
|
156
|
-
end
|
|
157
|
-
alias _ivar lazy_ivar
|
|
158
|
-
|
|
159
|
-
# Lazy reference an entity by its attribute or method.
|
|
160
|
-
#
|
|
161
|
-
# @example
|
|
162
|
-
# lazy_attr(:method)
|
|
163
|
-
#
|
|
164
|
-
# @param key [Symbol]
|
|
165
|
-
# @return [Proxy]
|
|
166
|
-
def lazy_attr key
|
|
167
|
-
Gamefic.logger.warn "#{caller.first ? "#{caller.first}: " : ''}`lazy_attr` is deprecated. Use `pick` or `pick!` instead."
|
|
168
|
-
Proxy.new(:attr, key)
|
|
169
|
-
end
|
|
170
|
-
alias _attr lazy_attr
|
|
171
|
-
|
|
172
|
-
# Lazy pick an entity.
|
|
173
|
-
#
|
|
174
|
-
# @example
|
|
175
|
-
# pick('the red box')
|
|
176
|
-
#
|
|
177
|
-
# @param args [Array]
|
|
178
|
-
# @return [Proxy]
|
|
179
|
-
def pick *args
|
|
180
|
-
Proxy::Pick.new(*args)
|
|
181
|
-
end
|
|
182
|
-
alias lazy_pick pick
|
|
183
|
-
alias _pick pick
|
|
184
|
-
|
|
185
|
-
# Lazy pick an entity or raise
|
|
186
|
-
#
|
|
187
|
-
def pick! *args
|
|
188
|
-
Proxy::Pick.new(*args)
|
|
189
|
-
end
|
|
190
|
-
alias lazy_pick! pick
|
|
191
|
-
alias _pick! pick
|
|
192
|
-
|
|
193
|
-
if RUBY_ENGINE == 'opal'
|
|
194
|
-
# :nocov:
|
|
195
|
-
def method_missing method, *args, &block
|
|
196
|
-
return super unless respond_to_missing?(method)
|
|
197
|
-
|
|
198
|
-
script { send(method, *args, &block) }
|
|
199
|
-
end
|
|
200
|
-
# :nocov:
|
|
201
|
-
else
|
|
202
|
-
def method_missing method, *args, **kwargs, &block
|
|
203
|
-
return super unless respond_to_missing?(method)
|
|
204
|
-
|
|
205
|
-
script { send(method, *args, **kwargs, &block) }
|
|
206
|
-
end
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
def respond_to_missing?(method, _with_private = false)
|
|
210
|
-
[Scriptable::Actions, Scriptable::Events, Scriptable::Scenes].flat_map(&:public_instance_methods)
|
|
211
|
-
.include?(method)
|
|
212
|
-
end
|
|
35
|
+
include Responses
|
|
36
|
+
include Scenes
|
|
37
|
+
include Seeds
|
|
38
|
+
include Syntaxes
|
|
213
39
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
#
|
|
217
|
-
# This can be useful when you need access to the Scriptable's constants and
|
|
218
|
-
# instance methods, but you don't want to duplicate its rules.
|
|
219
|
-
#
|
|
220
|
-
# @deprecated Removing script blocks is no longer necessary. This method
|
|
221
|
-
# will simply return self until it's removed.
|
|
222
|
-
#
|
|
223
|
-
# @return [Module<self>]
|
|
224
|
-
def no_scripts
|
|
225
|
-
Logging.logger.warn "#{caller.first ? "#{caller.first}: " : ''}Calling `no_scripts` on Scriptable modules is no longer necessary."
|
|
226
|
-
self
|
|
40
|
+
def included_scripts
|
|
41
|
+
ancestors.that_are(Scriptable).uniq
|
|
227
42
|
end
|
|
228
43
|
end
|
|
229
44
|
end
|
|
@@ -1,32 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
3
5
|
module Gamefic
|
|
4
|
-
module
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# @note The public versions of the entity and player arrays are frozen.
|
|
8
|
-
# Authors need access to them but shouldn't modify them directly. Use
|
|
9
|
-
# #make and #destroy instead.
|
|
6
|
+
module Scripting
|
|
7
|
+
# Methods related to managing entities.
|
|
10
8
|
#
|
|
11
9
|
module Entities
|
|
10
|
+
# extend Scriptable
|
|
12
11
|
include Proxies
|
|
13
12
|
|
|
14
|
-
def entity_vault
|
|
15
|
-
@entity_vault ||= Vault.new
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def player_vault
|
|
19
|
-
@player_vault ||= Vault.new
|
|
20
|
-
end
|
|
21
|
-
|
|
22
13
|
# @return [Array<Gamefic::Entity>]
|
|
23
14
|
def entities
|
|
24
|
-
|
|
15
|
+
entity_set.to_a
|
|
25
16
|
end
|
|
26
17
|
|
|
27
18
|
# @return [Array<Gamefic::Actor, Gamefic::Active>]
|
|
28
19
|
def players
|
|
29
|
-
|
|
20
|
+
player_set.to_a
|
|
30
21
|
end
|
|
31
22
|
|
|
32
23
|
# Create an entity.
|
|
@@ -36,17 +27,17 @@ module Gamefic
|
|
|
36
27
|
# seed { make Gamefic::Entity, name: 'thing' }
|
|
37
28
|
# end
|
|
38
29
|
#
|
|
39
|
-
# @param [Class<Gamefic::Entity>]
|
|
40
|
-
# @param args [Hash]
|
|
30
|
+
# @param klass [Class<Gamefic::Entity>]
|
|
41
31
|
# @return [Gamefic::Entity]
|
|
42
32
|
def make klass, **opts
|
|
43
|
-
|
|
33
|
+
klass.new(**unproxy(opts)).tap { |entity| entity_set.add entity }
|
|
44
34
|
end
|
|
45
35
|
|
|
46
|
-
def destroy
|
|
36
|
+
def destroy(entity)
|
|
47
37
|
entity.children.each { |child| destroy child }
|
|
48
38
|
entity.parent = nil
|
|
49
|
-
|
|
39
|
+
entity_set.delete entity
|
|
40
|
+
entity
|
|
50
41
|
end
|
|
51
42
|
|
|
52
43
|
def find *args
|
|
@@ -66,7 +57,6 @@ module Gamefic
|
|
|
66
57
|
# if an entity could not be found or there is more than one possible
|
|
67
58
|
# match.
|
|
68
59
|
#
|
|
69
|
-
# @param description [Array]
|
|
70
60
|
# @return [Gamefic::Entity, nil]
|
|
71
61
|
def pick *args
|
|
72
62
|
matches = find(*args)
|
|
@@ -89,6 +79,16 @@ module Gamefic
|
|
|
89
79
|
|
|
90
80
|
matches.first
|
|
91
81
|
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def entity_set
|
|
86
|
+
@entity_set ||= Set.new
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def player_set
|
|
90
|
+
@player_set ||= Set.new
|
|
91
|
+
end
|
|
92
92
|
end
|
|
93
93
|
end
|
|
94
94
|
end
|