gamefic 1.5.1 → 1.6.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/lib/gamefic.rb +1 -3
- data/lib/gamefic/action.rb +140 -79
- data/lib/gamefic/character.rb +120 -53
- data/lib/gamefic/character/state.rb +12 -0
- data/lib/gamefic/core_ext/array.rb +53 -11
- data/lib/gamefic/core_ext/string.rb +1 -0
- data/lib/gamefic/describable.rb +37 -11
- data/lib/gamefic/engine/base.rb +17 -4
- data/lib/gamefic/engine/tty.rb +4 -0
- data/lib/gamefic/entity.rb +4 -15
- data/lib/gamefic/matchable.rb +50 -0
- data/lib/gamefic/messaging.rb +45 -0
- data/lib/gamefic/node.rb +16 -0
- data/lib/gamefic/plot.rb +27 -33
- data/lib/gamefic/plot/{article_mount.rb → articles.rb} +22 -22
- data/lib/gamefic/plot/callbacks.rb +30 -4
- data/lib/gamefic/plot/{command_mount.rb → commands.rb} +121 -121
- data/lib/gamefic/plot/entities.rb +3 -3
- data/lib/gamefic/plot/host.rb +3 -3
- data/lib/gamefic/plot/playbook.rb +74 -30
- data/lib/gamefic/plot/scenes.rb +149 -0
- data/lib/gamefic/plot/snapshot.rb +14 -39
- data/lib/gamefic/plot/theater.rb +73 -0
- data/lib/gamefic/query.rb +6 -19
- data/lib/gamefic/query/base.rb +127 -246
- data/lib/gamefic/query/children.rb +6 -7
- data/lib/gamefic/query/descendants.rb +15 -0
- data/lib/gamefic/query/family.rb +19 -7
- data/lib/gamefic/query/itself.rb +13 -0
- data/lib/gamefic/query/matches.rb +67 -11
- data/lib/gamefic/query/parent.rb +6 -7
- data/lib/gamefic/query/siblings.rb +10 -7
- data/lib/gamefic/query/text.rb +39 -35
- data/lib/gamefic/scene.rb +1 -1
- data/lib/gamefic/scene/active.rb +12 -6
- data/lib/gamefic/scene/base.rb +56 -5
- data/lib/gamefic/scene/conclusion.rb +3 -0
- data/lib/gamefic/scene/custom.rb +0 -83
- data/lib/gamefic/scene/multiple_choice.rb +54 -32
- data/lib/gamefic/scene/multiple_scene.rb +11 -6
- data/lib/gamefic/scene/pause.rb +3 -4
- data/lib/gamefic/scene/yes_or_no.rb +23 -9
- data/lib/gamefic/script/base.rb +4 -0
- data/lib/gamefic/subplot.rb +22 -19
- data/lib/gamefic/syntax.rb +7 -15
- data/lib/gamefic/user/base.rb +7 -13
- data/lib/gamefic/user/buffer.rb +7 -0
- data/lib/gamefic/user/tty.rb +13 -12
- data/lib/gamefic/version.rb +1 -1
- metadata +11 -37
- data/lib/gamefic/director.rb +0 -23
- data/lib/gamefic/director/delegate.rb +0 -126
- data/lib/gamefic/director/order.rb +0 -17
- data/lib/gamefic/director/parser.rb +0 -137
- data/lib/gamefic/keywords.rb +0 -67
- data/lib/gamefic/plot/query_mount.rb +0 -9
- data/lib/gamefic/plot/scene_mount.rb +0 -182
- data/lib/gamefic/query/ambiguous_children.rb +0 -5
- data/lib/gamefic/query/expression.rb +0 -47
- data/lib/gamefic/query/many_children.rb +0 -7
- data/lib/gamefic/query/plural_children.rb +0 -14
- data/lib/gamefic/query/self.rb +0 -10
- data/lib/gamefic/scene_data.rb +0 -10
- data/lib/gamefic/scene_data/base.rb +0 -12
- data/lib/gamefic/scene_data/multiple_choice.rb +0 -19
- data/lib/gamefic/scene_data/multiple_scene.rb +0 -21
- data/lib/gamefic/scene_data/yes_or_no.rb +0 -18
- data/lib/gamefic/serialized.rb +0 -24
- data/lib/gamefic/stage.rb +0 -106
@@ -1,182 +0,0 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
module Plot::SceneMount
|
4
|
-
def default_scene
|
5
|
-
@default_scene ||= Scene::Active.new(self)
|
6
|
-
end
|
7
|
-
|
8
|
-
def default_conclusion
|
9
|
-
@default_conclusion ||= Scene::Conclusion.new
|
10
|
-
end
|
11
|
-
|
12
|
-
# Add a block to be executed when a player is added to the game.
|
13
|
-
# Each Plot can only have one introduction. Subsequent calls will
|
14
|
-
# overwrite the existing one.
|
15
|
-
#
|
16
|
-
# @example Welcome the player to the game
|
17
|
-
# introduction do |actor|
|
18
|
-
# actor.tell "Welcome to the game!"
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
# @yieldparam [Character]
|
22
|
-
def introduction (&proc)
|
23
|
-
@introduction = proc
|
24
|
-
end
|
25
|
-
|
26
|
-
# Introduce a player to the game.
|
27
|
-
# This method is typically called by the Engine that manages game execution.
|
28
|
-
def introduce(player)
|
29
|
-
player.playbook = playbook
|
30
|
-
player.cue default_scene
|
31
|
-
p_players.push player
|
32
|
-
@introduction.call(player) unless @introduction.nil?
|
33
|
-
end
|
34
|
-
|
35
|
-
# Create a multiple-choice scene.
|
36
|
-
# The user will be required to make a valid choice to continue.
|
37
|
-
#
|
38
|
-
# @yieldparam [Character]
|
39
|
-
# @yieldparam [Scene::Data::MultipleChoice]
|
40
|
-
def multiple_choice *choices, &block
|
41
|
-
s = Scene::MultipleChoice.new
|
42
|
-
s.on_start do |actor, data|
|
43
|
-
data.options.clear
|
44
|
-
data.options.push *choices
|
45
|
-
end
|
46
|
-
s.on_finish &block
|
47
|
-
s
|
48
|
-
end
|
49
|
-
|
50
|
-
# Create a yes-or-no scene.
|
51
|
-
# The user will be required to answer Yes or No to continue.
|
52
|
-
#
|
53
|
-
# @yieldparam [Character]
|
54
|
-
# @yieldparam [String] "yes" or "no"
|
55
|
-
def yes_or_no prompt = nil, &block
|
56
|
-
s = Scene::YesOrNo.new
|
57
|
-
unless prompt.nil?
|
58
|
-
s.on_start do |actor, data|
|
59
|
-
data.prompt = prompt
|
60
|
-
end
|
61
|
-
end
|
62
|
-
s.on_finish do |actor, data|
|
63
|
-
block.call actor, data unless block.nil?
|
64
|
-
actor.cue default_scene if actor.scene == s and actor.next_scene.nil?
|
65
|
-
end
|
66
|
-
s
|
67
|
-
end
|
68
|
-
|
69
|
-
def question prompt = 'What is your answer?', &block
|
70
|
-
s = Scene::Custom.new
|
71
|
-
s.on_start do |actor, data|
|
72
|
-
data.prompt = prompt
|
73
|
-
end
|
74
|
-
s.on_finish &block
|
75
|
-
s
|
76
|
-
end
|
77
|
-
|
78
|
-
# Create a scene that pauses the game.
|
79
|
-
# This scene will execute the specified block and wait for input from the
|
80
|
-
# the user (e.g., pressing Enter) to continue.
|
81
|
-
#
|
82
|
-
# @param key [Symbol] A unique name for the scene.
|
83
|
-
# @param prompt [String] The text to display when prompting the user to continue.
|
84
|
-
# @yieldparam [Character]
|
85
|
-
# @yieldparam [Scene::Data::Base]
|
86
|
-
def pause prompt = nil, &block
|
87
|
-
s = Scene::Pause.new
|
88
|
-
s.on_start do |actor, data|
|
89
|
-
data.prompt = prompt unless prompt.nil?
|
90
|
-
block.call actor, data unless block.nil?
|
91
|
-
end
|
92
|
-
s.on_finish do |actor, data|
|
93
|
-
#actor.cue :active if actor.scene == key and actor.next_scene.nil?
|
94
|
-
actor.cue default_scene if actor.scene == s and actor.next_scene.nil?
|
95
|
-
end
|
96
|
-
s
|
97
|
-
end
|
98
|
-
|
99
|
-
# Create a conclusion.
|
100
|
-
# The game (or the character's participation in it) will end after this
|
101
|
-
# scene is complete.
|
102
|
-
#
|
103
|
-
# @param key [Symbol] A unique name for the scene.
|
104
|
-
# @yieldparam [Character]
|
105
|
-
# @yieldparam [Scene::Data::Base]
|
106
|
-
def conclusion &block
|
107
|
-
s = Scene::Conclusion.new
|
108
|
-
s.on_start &block
|
109
|
-
s
|
110
|
-
end
|
111
|
-
|
112
|
-
# Create a custom scene.
|
113
|
-
#
|
114
|
-
# Custom scenes should always specify the next scene to be cued or
|
115
|
-
# prepared. If not, the scene will get repeated on the next turn.
|
116
|
-
#
|
117
|
-
# This method creates a Scene::Custom by default. You can customize other
|
118
|
-
# scene types by specifying the class to create.
|
119
|
-
#
|
120
|
-
# @example Ask the user for a name
|
121
|
-
# scene :ask_for_name do |scene|
|
122
|
-
# scene.on_start do |actor, data|
|
123
|
-
# data.prompt = "What's your name?"
|
124
|
-
# end
|
125
|
-
# scene.on_finish do |actor, data|
|
126
|
-
# actor.name = data.input
|
127
|
-
# actor.tell "Hello, #{actor.name}!"
|
128
|
-
# actor.cue :active
|
129
|
-
# end
|
130
|
-
# end
|
131
|
-
#
|
132
|
-
# @example Customize the prompt for a MultipleChoice scene
|
133
|
-
# scene :ask_for_choice, Scene::MultipleChoice do |scene|
|
134
|
-
# scene.on_start do |actor, data|
|
135
|
-
# data.options.push 'red', 'green', 'blue'
|
136
|
-
# data.prompt = "Which color?"
|
137
|
-
# end
|
138
|
-
# scene.on_finish do |actor, data|
|
139
|
-
# actor.tell "You chose #{data.selection}"
|
140
|
-
# actor.cue :active
|
141
|
-
# end
|
142
|
-
# end
|
143
|
-
#
|
144
|
-
# @param key [Symbol] A unique name for the scene.
|
145
|
-
# @param key [cls] The class of scene to be instantiated.
|
146
|
-
# @yieldparam [Scene::Custom] The instantiated scene.
|
147
|
-
def scene cls = Scene::Custom, &block
|
148
|
-
s = cls.new
|
149
|
-
yield s if block_given?
|
150
|
-
s
|
151
|
-
end
|
152
|
-
|
153
|
-
# Choose a new scene based on a list of options.
|
154
|
-
# This is a specialized type of multiple-choice scene that determines
|
155
|
-
# which scene to cue based on a Hash of choices and scene keys.
|
156
|
-
#
|
157
|
-
# @example Select a scene
|
158
|
-
# multiple_scene :select_one_or_two, { "one" => :scene_one, "two" => :scene_two }
|
159
|
-
# scene :scene_one do |actor|
|
160
|
-
# actor.tell "You went to scene one"
|
161
|
-
# end
|
162
|
-
# scene :scene_two do |actor|
|
163
|
-
# actor.tell "You went to scene two"
|
164
|
-
# end
|
165
|
-
# introduction do |actor|
|
166
|
-
# actor.cue :select_one_or_two # The actor will be prompted to select "one" or "two" and get sent to the corresponding scene
|
167
|
-
# end
|
168
|
-
#
|
169
|
-
# @param key [Symbol] A unique name for the scene.
|
170
|
-
# @param map [Hash] A Hash of options and associated scene keys.
|
171
|
-
def multiple_scene map
|
172
|
-
s = Scene::MultipleScene.new
|
173
|
-
s.on_start do |actor, data|
|
174
|
-
map.each { |k, v|
|
175
|
-
data.map k, v
|
176
|
-
}
|
177
|
-
end
|
178
|
-
s
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
module Gamefic::Query
|
2
|
-
class Expression < Base
|
3
|
-
def base_specificity
|
4
|
-
10
|
5
|
-
end
|
6
|
-
def validate(subject, description)
|
7
|
-
return false unless description.kind_of?(String)
|
8
|
-
valid = false
|
9
|
-
words = description.split_words
|
10
|
-
words.each { |word|
|
11
|
-
if description.include?(word)
|
12
|
-
valid = true
|
13
|
-
break
|
14
|
-
end
|
15
|
-
}
|
16
|
-
valid
|
17
|
-
end
|
18
|
-
def execute(subject, description)
|
19
|
-
if @arguments.length == 0
|
20
|
-
return Matches.new([description], description, '')
|
21
|
-
end
|
22
|
-
keywords = Keywords.new(description)
|
23
|
-
possible = []
|
24
|
-
@arguments.each { |regexp|
|
25
|
-
remainder = keywords.clone
|
26
|
-
used = []
|
27
|
-
while remainder.length > 0
|
28
|
-
used.push remainder.shift
|
29
|
-
if used.join(' ').match(regexp)
|
30
|
-
possible.push Matches.new([used.join(' ')], used.join(' '), remainder.join(' '))
|
31
|
-
end
|
32
|
-
end
|
33
|
-
}
|
34
|
-
if possible.length > 0
|
35
|
-
possible.sort! { |a, b|
|
36
|
-
b.matching_text.length <=> a.matching_text.length
|
37
|
-
}
|
38
|
-
return possible[0]
|
39
|
-
else
|
40
|
-
return Matches.new([], '', description)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
def test_arguments arguments
|
44
|
-
# No test for text
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
class Gamefic::Query::PluralChildren < Gamefic::Query::AmbiguousChildren
|
2
|
-
def execute(subject, description)
|
3
|
-
if (!description.end_with?("s") and !description.end_with?("i") and !description.end_with?("ae")) or (description.end_with?("ous") or description.end_with?("ess"))
|
4
|
-
return Gamefic::Query::Matches.new([], '', description)
|
5
|
-
end
|
6
|
-
super
|
7
|
-
end
|
8
|
-
def validate(subject, object)
|
9
|
-
# Plural queries always return false on validation. Their only purpose is
|
10
|
-
# to provide syntactic sugar for plural nouns, so it should never get triggered
|
11
|
-
# by a token call.
|
12
|
-
false
|
13
|
-
end
|
14
|
-
end
|
data/lib/gamefic/query/self.rb
DELETED
data/lib/gamefic/scene_data.rb
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
module SceneData
|
4
|
-
autoload :Base, 'gamefic/scene_data/base'
|
5
|
-
autoload :MultipleChoice, 'gamefic/scene_data/multiple_choice'
|
6
|
-
autoload :MultipleScene, 'gamefic/scene_data/multiple_scene'
|
7
|
-
autoload :YesOrNo, 'gamefic/scene_data/yes_or_no'
|
8
|
-
end
|
9
|
-
|
10
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
class SceneData::MultipleChoice < SceneData::Base
|
4
|
-
attr_accessor :selection
|
5
|
-
attr_accessor :number
|
6
|
-
attr_accessor :index
|
7
|
-
attr_writer :invalid_message
|
8
|
-
def options
|
9
|
-
@options ||= []
|
10
|
-
end
|
11
|
-
def prompt
|
12
|
-
@prompt ||= 'Enter a choice:'
|
13
|
-
end
|
14
|
-
def invalid_message
|
15
|
-
@invalid_message ||= 'That is not a valid choice.'
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
class SceneData::MultipleScene < SceneData::MultipleChoice
|
4
|
-
def options
|
5
|
-
scene_map.keys
|
6
|
-
end
|
7
|
-
|
8
|
-
def map choice, scene
|
9
|
-
scene_map[choice] = scene
|
10
|
-
end
|
11
|
-
|
12
|
-
def scene_for choice
|
13
|
-
scene_map[choice]
|
14
|
-
end
|
15
|
-
|
16
|
-
def scene_map
|
17
|
-
@scene_map ||= {}
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
class SceneData::YesOrNo < SceneData::Base
|
4
|
-
def yes?
|
5
|
-
input.to_s[0,1].downcase == 'y'
|
6
|
-
end
|
7
|
-
def no?
|
8
|
-
input.to_s[0,1].downcase == 'n'
|
9
|
-
end
|
10
|
-
def prompt
|
11
|
-
@prompt ||= 'Yes or No?'
|
12
|
-
end
|
13
|
-
def invalid_message
|
14
|
-
@invalid_message ||= 'Please enter Yes or No.'
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
data/lib/gamefic/serialized.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
module Gamefic
|
2
|
-
module Serialized
|
3
|
-
def serialized_attributes
|
4
|
-
self.class.serializer.keys
|
5
|
-
end
|
6
|
-
module ClassMethods
|
7
|
-
def serialize *args
|
8
|
-
args.each { |a|
|
9
|
-
serializer[a] = nil
|
10
|
-
}
|
11
|
-
end
|
12
|
-
def serializer
|
13
|
-
@@serialized_attributes ||= from_superclass(:serializer, {}).dup
|
14
|
-
end
|
15
|
-
private
|
16
|
-
def from_superclass(m, default = nil)
|
17
|
-
superclass.respond_to?(m) ? superclass.send(m) : default
|
18
|
-
end
|
19
|
-
end
|
20
|
-
def self.included(base)
|
21
|
-
base.extend(Gamefic::Serialized::ClassMethods)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
data/lib/gamefic/stage.rb
DELETED
@@ -1,106 +0,0 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
module Stage
|
4
|
-
# Execute a block of code in a subset of the object's scope.
|
5
|
-
#
|
6
|
-
# An object's stage is an isolated namespace that has its own instance
|
7
|
-
# variables and limited access to its parent's instance methods.
|
8
|
-
def stage *args, &block
|
9
|
-
s = generate_stage
|
10
|
-
if block.nil?
|
11
|
-
s.module_eval(*args)
|
12
|
-
else
|
13
|
-
s.module_exec(*args, &block)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def generate_stage
|
20
|
-
return @stage unless @stage.nil?
|
21
|
-
|
22
|
-
exposed = self.class.exposed_methods.keys
|
23
|
-
mounted = self.class.mounted_modules.keys
|
24
|
-
instance = self
|
25
|
-
|
26
|
-
@stage = Module.new do
|
27
|
-
define_singleton_method(:__instance__) do
|
28
|
-
unless caller.length == 0 or caller[0].include?(__FILE__)
|
29
|
-
raise NoMethodError.new("Method __instance__ is not available from the stage.")
|
30
|
-
end
|
31
|
-
instance
|
32
|
-
end
|
33
|
-
exposed.each do |exposed_method|
|
34
|
-
define_singleton_method(exposed_method) do |*args, &block|
|
35
|
-
__instance__.public_send(exposed_method, *args, &block)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
mounted.each { |dsl|
|
39
|
-
dsl.public_instance_methods.each { |method|
|
40
|
-
define_singleton_method(method) do |*args, &block|
|
41
|
-
#puts "Calling a mounted method"
|
42
|
-
result = __instance__.public_send(method, *args, &block)
|
43
|
-
#puts "Done"
|
44
|
-
result
|
45
|
-
end
|
46
|
-
}
|
47
|
-
}
|
48
|
-
end
|
49
|
-
|
50
|
-
return @stage
|
51
|
-
end
|
52
|
-
|
53
|
-
module ClassMethods
|
54
|
-
# Mount a module in this class.
|
55
|
-
#
|
56
|
-
# Mounting a module will include it like a typical mixin and expose its
|
57
|
-
# public methods to the stage.
|
58
|
-
#
|
59
|
-
# Assuming you have a module Foo with one public method bar,
|
60
|
-
# <code>mount Foo</code> is functionally equivalent to
|
61
|
-
# <code>include Foo; expose bar</code>.
|
62
|
-
def mount *args
|
63
|
-
args.each { |a|
|
64
|
-
include a
|
65
|
-
mounted_modules[a] = nil
|
66
|
-
}
|
67
|
-
end
|
68
|
-
|
69
|
-
# Give this object's stage access to an instance method.
|
70
|
-
#
|
71
|
-
# @example
|
72
|
-
# class Container
|
73
|
-
# def foobar; end
|
74
|
-
# expose :foobar
|
75
|
-
# end
|
76
|
-
# x = Container.new
|
77
|
-
# x.stage do
|
78
|
-
# foobar
|
79
|
-
# end
|
80
|
-
def expose *args
|
81
|
-
args.each { |a|
|
82
|
-
exposed_methods[a] = nil
|
83
|
-
}
|
84
|
-
end
|
85
|
-
|
86
|
-
def exposed_methods
|
87
|
-
@@exposed_methods ||= from_superclass(:exposed_methods, {}).dup
|
88
|
-
end
|
89
|
-
|
90
|
-
def mounted_modules
|
91
|
-
@@mounted_modules ||= from_superclass(:mounted_modules, {}).dup
|
92
|
-
end
|
93
|
-
|
94
|
-
private
|
95
|
-
|
96
|
-
def from_superclass(m, default = nil)
|
97
|
-
superclass.respond_to?(m) ? superclass.send(m) : default
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def self.included(base)
|
102
|
-
base.extend(ClassMethods)
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
end
|