gamefic 1.5.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|