gamefic 0.2.0 → 0.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/action.rb +87 -56
- data/lib/gamefic/ansi.rb +55 -0
- data/lib/gamefic/character.rb +130 -76
- data/lib/gamefic/command.rb +19 -0
- data/lib/gamefic/core_ext/array.rb +51 -40
- data/lib/gamefic/core_ext/string.rb +4 -0
- data/lib/gamefic/describable.rb +108 -46
- data/lib/gamefic/direction.rb +46 -0
- data/lib/gamefic/director/delegate.rb +91 -0
- data/lib/gamefic/director/order.rb +10 -0
- data/lib/gamefic/director/parser.rb +119 -0
- data/lib/gamefic/director.rb +16 -197
- data/lib/gamefic/engine/cgi.rb +221 -0
- data/lib/gamefic/engine/tty.rb +237 -0
- data/lib/gamefic/engine.rb +88 -67
- data/lib/gamefic/entity.rb +96 -69
- data/lib/gamefic/grammar/conjugator.rb +20 -0
- data/lib/gamefic/grammar/gender.rb +11 -0
- data/lib/gamefic/grammar/person.rb +10 -0
- data/lib/gamefic/grammar/plural.rb +13 -0
- data/lib/gamefic/grammar/pronouns.rb +60 -0
- data/lib/gamefic/grammar/tense.rb +6 -0
- data/lib/gamefic/grammar/verb_set.rb +43 -0
- data/lib/gamefic/grammar/verbs.rb +25 -0
- data/lib/gamefic/grammar/word_adapter.rb +36 -0
- data/lib/gamefic/grammar.rb +13 -0
- data/lib/gamefic/html.rb +53 -0
- data/lib/gamefic/keywords.rb +51 -33
- data/lib/gamefic/node.rb +65 -58
- data/lib/gamefic/plot/article_mount.rb +22 -0
- data/lib/gamefic/plot/command_mount.rb +88 -0
- data/lib/gamefic/plot/entity_mount.rb +45 -0
- data/lib/gamefic/plot/query_mount.rb +9 -0
- data/lib/gamefic/plot/scene_mount.rb +181 -0
- data/lib/gamefic/plot/you_mount.rb +22 -0
- data/lib/gamefic/plot.rb +296 -247
- data/lib/gamefic/query/ambiguous_children.rb +5 -0
- data/lib/gamefic/query/base.rb +265 -0
- data/lib/gamefic/query/children.rb +10 -0
- data/lib/gamefic/query/expression.rb +47 -0
- data/lib/gamefic/query/family.rb +10 -0
- data/lib/gamefic/query/many_children.rb +7 -0
- data/lib/gamefic/query/matches.rb +11 -0
- data/lib/gamefic/query/parent.rb +10 -0
- data/lib/gamefic/query/plural_children.rb +14 -0
- data/lib/gamefic/query/self.rb +10 -0
- data/lib/gamefic/query/siblings.rb +10 -0
- data/lib/gamefic/query/text.rb +43 -0
- data/lib/gamefic/query.rb +19 -203
- data/lib/gamefic/rule.rb +18 -0
- data/lib/gamefic/scene/active.rb +25 -0
- data/lib/gamefic/scene/concluded.rb +22 -0
- data/lib/gamefic/scene/multiplechoice.rb +74 -0
- data/lib/gamefic/scene/paused.rb +26 -0
- data/lib/gamefic/scene/yesorno.rb +43 -0
- data/lib/gamefic/scene.rb +125 -0
- data/lib/gamefic/script/base.rb +33 -0
- data/lib/gamefic/script/file.rb +14 -0
- data/lib/gamefic/script/text.rb +14 -0
- data/lib/gamefic/script.rb +9 -0
- data/lib/gamefic/serialized.rb +24 -0
- data/lib/gamefic/shell.rb +9 -247
- data/lib/gamefic/snapshots.rb +134 -0
- data/lib/gamefic/source/base.rb +12 -0
- data/lib/gamefic/source/file.rb +23 -0
- data/lib/gamefic/source/text.rb +16 -0
- data/lib/gamefic/source.rb +9 -0
- data/lib/gamefic/stage.rb +75 -0
- data/lib/gamefic/syntax.rb +106 -124
- data/lib/gamefic/tester.rb +20 -0
- data/lib/gamefic/version.rb +3 -0
- data/lib/gamefic.rb +18 -12
- metadata +102 -70
- data/lib/gamefic/base.rb +0 -10
- data/lib/gamefic/before.rb +0 -12
- data/lib/gamefic/import/basics/actions/close.rb +0 -16
- data/lib/gamefic/import/basics/actions/commands.rb +0 -3
- data/lib/gamefic/import/basics/actions/drop-in.rb +0 -17
- data/lib/gamefic/import/basics/actions/drop-on.rb +0 -16
- data/lib/gamefic/import/basics/actions/drop.rb +0 -30
- data/lib/gamefic/import/basics/actions/enter.rb +0 -16
- data/lib/gamefic/import/basics/actions/go.rb +0 -35
- data/lib/gamefic/import/basics/actions/inventory.rb +0 -8
- data/lib/gamefic/import/basics/actions/leave.rb +0 -29
- data/lib/gamefic/import/basics/actions/look-in-at.rb +0 -27
- data/lib/gamefic/import/basics/actions/look-under.rb +0 -3
- data/lib/gamefic/import/basics/actions/look.rb +0 -71
- data/lib/gamefic/import/basics/actions/nil.rb +0 -25
- data/lib/gamefic/import/basics/actions/open.rb +0 -23
- data/lib/gamefic/import/basics/actions/quit.rb +0 -3
- data/lib/gamefic/import/basics/actions/take.rb +0 -107
- data/lib/gamefic/import/basics/entities/container.rb +0 -8
- data/lib/gamefic/import/basics/entities/entity.rb +0 -11
- data/lib/gamefic/import/basics/entities/fixture.rb +0 -5
- data/lib/gamefic/import/basics/entities/item.rb +0 -5
- data/lib/gamefic/import/basics/entities/portal.rb +0 -40
- data/lib/gamefic/import/basics/entities/room.rb +0 -30
- data/lib/gamefic/import/basics/entities/scenery.rb +0 -5
- data/lib/gamefic/import/basics/entities/supporter.rb +0 -6
- data/lib/gamefic/import/basics/entities/thing.rb +0 -16
- data/lib/gamefic/import/basics/queries/reachable.rb +0 -38
- data/lib/gamefic/import/basics/queries/room.rb +0 -8
- data/lib/gamefic/import/basics/queries/visible.rb +0 -32
- data/lib/gamefic/import/basics/rules/has-enough-light.rb +0 -14
- data/lib/gamefic/import/basics.old/actions/container.rb +0 -112
- data/lib/gamefic/import/basics.old/actions/inventory.rb +0 -50
- data/lib/gamefic/import/basics.old/actions/look.rb +0 -53
- data/lib/gamefic/import/basics.old/actions/meta.rb +0 -6
- data/lib/gamefic/import/basics.old/actions/traversal.rb +0 -35
- data/lib/gamefic/import/basics.old/actions.rb +0 -1
- data/lib/gamefic/import/basics.old/entities/container.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/fixture.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/item.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/portal.rb +0 -43
- data/lib/gamefic/import/basics.old/entities/room.rb +0 -27
- data/lib/gamefic/import/basics.old/entities/scenery.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/supporter.rb +0 -3
- data/lib/gamefic/import/basics.old/entities.rb +0 -1
- data/lib/gamefic/import/basics.old/room_modes.rb +0 -48
- data/lib/gamefic/import/basics.rb +0 -6
- data/lib/gamefic/import/room_modes.rb +0 -48
- data/lib/gamefic/import/standard.rb +0 -1
- data/lib/gamefic/meta.rb +0 -12
- data/lib/gamefic/optionset.rb +0 -114
- data/lib/gamefic/requirement.rb +0 -14
- data/lib/gamefic/story.rb +0 -14
- data/lib/gamefic/thing.rb +0 -7
data/lib/gamefic/node.rb
CHANGED
|
@@ -1,62 +1,69 @@
|
|
|
1
|
+
# Exception raised when setting a node's parent would cause
|
|
2
|
+
# a circular reference, e.g., A -> A or A -> B -> A
|
|
3
|
+
class CircularNodeReferenceError < Exception
|
|
4
|
+
end
|
|
5
|
+
|
|
1
6
|
module Gamefic
|
|
2
7
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
8
|
+
module Node
|
|
9
|
+
# @return [Array]
|
|
10
|
+
def children
|
|
11
|
+
@children ||= []
|
|
12
|
+
@children.clone
|
|
13
|
+
end
|
|
14
|
+
def flatten
|
|
15
|
+
array = Array.new
|
|
16
|
+
children.each { |child|
|
|
17
|
+
array = array + recurse_flatten(child)
|
|
18
|
+
}
|
|
19
|
+
return array
|
|
20
|
+
end
|
|
21
|
+
def parent
|
|
22
|
+
@parent
|
|
23
|
+
end
|
|
24
|
+
def parent=(node)
|
|
25
|
+
return if node == @parent
|
|
26
|
+
if node == self
|
|
27
|
+
raise CircularNodeReferenceError.new("Node cannot be its own parent")
|
|
28
|
+
end
|
|
29
|
+
# Do not permit circular references
|
|
30
|
+
if node != nil and node.parent == self
|
|
31
|
+
node.parent = nil
|
|
32
|
+
end
|
|
33
|
+
if node != nil and flatten.include?(node)
|
|
34
|
+
raise CircularNodeReferenceError.new("Node cannot be a child of a descendant")
|
|
35
|
+
end
|
|
36
|
+
if @parent != node
|
|
37
|
+
if @parent != nil
|
|
38
|
+
@parent.send(:rem_child, self)
|
|
39
|
+
end
|
|
40
|
+
@parent = node
|
|
41
|
+
if @parent != nil
|
|
42
|
+
@parent.send(:add_child, self)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
protected
|
|
47
|
+
def add_child(node)
|
|
48
|
+
children
|
|
49
|
+
@children.push(node)
|
|
50
|
+
end
|
|
51
|
+
def rem_child(node)
|
|
52
|
+
children
|
|
53
|
+
@children.delete(node)
|
|
54
|
+
end
|
|
55
|
+
def concat_children(children)
|
|
56
|
+
children.concat(children)
|
|
57
|
+
end
|
|
58
|
+
private
|
|
59
|
+
def recurse_flatten(node)
|
|
60
|
+
array = Array.new
|
|
61
|
+
array.push(node)
|
|
62
|
+
node.children.each { |child|
|
|
63
|
+
array = array + recurse_flatten(child)
|
|
64
|
+
}
|
|
65
|
+
return array
|
|
66
|
+
end
|
|
67
|
+
end
|
|
61
68
|
|
|
62
69
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Gamefic
|
|
2
|
+
module ArticleMount
|
|
3
|
+
def a(entity)
|
|
4
|
+
entity.indefinitely
|
|
5
|
+
end
|
|
6
|
+
def an(entity)
|
|
7
|
+
entity.indefinitely
|
|
8
|
+
end
|
|
9
|
+
def the(entity)
|
|
10
|
+
entity.definitely
|
|
11
|
+
end
|
|
12
|
+
def A(entity)
|
|
13
|
+
entity.indefinitely.cap_first
|
|
14
|
+
end
|
|
15
|
+
def An(entity)
|
|
16
|
+
entity.indefinitely.cap_first
|
|
17
|
+
end
|
|
18
|
+
def The(entity)
|
|
19
|
+
entity.definitely.cap_first
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require 'gamefic/action'
|
|
2
|
+
|
|
3
|
+
module Gamefic
|
|
4
|
+
|
|
5
|
+
module Plot::CommandMount
|
|
6
|
+
# Create a Meta Action that responds to a command.
|
|
7
|
+
# Meta Actions are very similar to standard Actions, except the Plot
|
|
8
|
+
# understands them to be commands that operate above and/or outside of the
|
|
9
|
+
# actual game world. Examples of Meta Actions are commands that report the
|
|
10
|
+
# player's current score, save and restore saved games, or list the game's
|
|
11
|
+
# credits.
|
|
12
|
+
#
|
|
13
|
+
# @example A simple Meta Action
|
|
14
|
+
# meta :credits do |actor|
|
|
15
|
+
# actor.tell "This game was written by John Smith."
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# @param command [Symbol] An imperative verb for the command
|
|
19
|
+
# @param *queries [Array<Query::Base>] Queries to filter the command's tokens
|
|
20
|
+
# @yieldparam [Character]
|
|
21
|
+
def meta(command, *queries, &proc)
|
|
22
|
+
act = self.action(command, *queries, &proc)
|
|
23
|
+
act.meta = true
|
|
24
|
+
act
|
|
25
|
+
end
|
|
26
|
+
def action(command, *queries, &proc)
|
|
27
|
+
Action.new(self, command, *queries, &proc)
|
|
28
|
+
end
|
|
29
|
+
# Create an Action that responds to a command.
|
|
30
|
+
# An Action uses the command argument to identify the imperative verb that
|
|
31
|
+
# triggers the action.
|
|
32
|
+
# It can also accept queries to tokenize the remainder of the input and
|
|
33
|
+
# filter for particular entities or properties.
|
|
34
|
+
# The block argument contains the code to be executed when the input
|
|
35
|
+
# matches all of the Action's criteria (i.e., verb and queries).
|
|
36
|
+
#
|
|
37
|
+
# @example A simple Action.
|
|
38
|
+
# respond :salute do |actor|
|
|
39
|
+
# actor.tell "Hello, sir!"
|
|
40
|
+
# end
|
|
41
|
+
# # The command "salute" will respond "Hello, sir!"
|
|
42
|
+
#
|
|
43
|
+
# @example An Action that accepts a Character
|
|
44
|
+
# respond :salute, Use.visible(Character) do |actor, character|
|
|
45
|
+
# actor.tell "#{The character} returns your salute."
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# @param command [Symbol] An imperative verb for the command
|
|
49
|
+
# @param *queries [Array<Query::Base>] Queries to filter the command's tokens
|
|
50
|
+
# @yieldparam [Character]
|
|
51
|
+
def respond(command, *queries, &proc)
|
|
52
|
+
self.action(command, *queries, &proc)
|
|
53
|
+
end
|
|
54
|
+
# Create an alternate Syntax for an Action.
|
|
55
|
+
# The command and its translation can be parameterized.
|
|
56
|
+
#
|
|
57
|
+
# @example Create a synonym for the Inventory Action.
|
|
58
|
+
# interpret "catalogue", "inventory"
|
|
59
|
+
# # The command "catalogue" will be translated to "inventory"
|
|
60
|
+
#
|
|
61
|
+
# @example Create a parameterized synonym for the Look Action.
|
|
62
|
+
# interpret "scrutinize :entity", "look :entity"
|
|
63
|
+
# # The command "scrutinize chair" will be translated to "look chair"
|
|
64
|
+
#
|
|
65
|
+
# @param command [String] The format of the original command
|
|
66
|
+
# @param translation [String] The format of the translated command
|
|
67
|
+
# @return [Syntax] the Syntax object
|
|
68
|
+
def interpret command, translation
|
|
69
|
+
xlate command, translation
|
|
70
|
+
end
|
|
71
|
+
def syntax(*args)
|
|
72
|
+
xlate(*args)
|
|
73
|
+
end
|
|
74
|
+
def xlate(*args)
|
|
75
|
+
syn = Syntax.new(self, *args)
|
|
76
|
+
syn
|
|
77
|
+
end
|
|
78
|
+
def commandwords
|
|
79
|
+
words = Array.new
|
|
80
|
+
syntaxes.each { |s|
|
|
81
|
+
word = s.first_word
|
|
82
|
+
words.push(word) if !word.nil?
|
|
83
|
+
}
|
|
84
|
+
words.uniq
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Gamefic
|
|
2
|
+
|
|
3
|
+
module Plot::EntityMount
|
|
4
|
+
# Make a new Entity with the provided properties.
|
|
5
|
+
#
|
|
6
|
+
# @example Create an Entity
|
|
7
|
+
# chair = make Entity, :name => 'red chair'
|
|
8
|
+
# chair.name #=> 'red chair'
|
|
9
|
+
#
|
|
10
|
+
# @param cls [Class] The Class of the Entity to be created.
|
|
11
|
+
# @param args [Hash] The entity's properties.
|
|
12
|
+
# @return The Entity instance.
|
|
13
|
+
def make(cls, args = {}, &block)
|
|
14
|
+
ent = cls.new(self, args, &block)
|
|
15
|
+
if ent.kind_of?(Entity) == false
|
|
16
|
+
raise "Invalid entity class"
|
|
17
|
+
end
|
|
18
|
+
ent
|
|
19
|
+
end
|
|
20
|
+
# Pick an entity based on its description.
|
|
21
|
+
# The description provided must match exactly one entity; otherwise
|
|
22
|
+
# an error is raised.
|
|
23
|
+
#
|
|
24
|
+
# @example Select the Entity that matches the description
|
|
25
|
+
# red_chair = make Entity, :name => 'red chair'
|
|
26
|
+
# blue_chair make Entity, :name => 'blue chair'
|
|
27
|
+
# pick "red chair" #=> red_chair
|
|
28
|
+
# pick "blue chair" #=> blue_chair
|
|
29
|
+
# pick "chair" #=> IndexError: description is ambiguous
|
|
30
|
+
#
|
|
31
|
+
# @param @description [String] The description of the entity
|
|
32
|
+
# @return [Entity] The entity that matches the description
|
|
33
|
+
def pick(description)
|
|
34
|
+
query = Gamefic::Query::Base.new
|
|
35
|
+
result = query.match(description, entities)
|
|
36
|
+
if result.objects.length == 0
|
|
37
|
+
raise IndexError.new("Unable to find entity from '#{description}'")
|
|
38
|
+
elsif result.objects.length > 1
|
|
39
|
+
raise IndexError.new("Ambiguous entities found from '#{description}'")
|
|
40
|
+
end
|
|
41
|
+
result.objects[0]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
class NotConclusionError < Exception
|
|
2
|
+
end
|
|
3
|
+
|
|
4
|
+
module Gamefic
|
|
5
|
+
|
|
6
|
+
module Plot::SceneMount
|
|
7
|
+
# Get a Hash of SceneManager objects.
|
|
8
|
+
#
|
|
9
|
+
# @return [Hash<Symbol, SceneManager>]
|
|
10
|
+
def scene_managers
|
|
11
|
+
if @scene_managers.nil?
|
|
12
|
+
@scene_managers ||= {}
|
|
13
|
+
@scene_managers[:active] = ActiveSceneManager.new
|
|
14
|
+
@scene_managers[:concluded] = ConcludedSceneManager.new
|
|
15
|
+
end
|
|
16
|
+
@scene_managers
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Create a multiple-choice scene.
|
|
20
|
+
# The user will be required to make a valid choice to continue
|
|
21
|
+
#
|
|
22
|
+
# @yieldparam [Character]
|
|
23
|
+
# @yieldparam [MultipleChoiceSceneData]
|
|
24
|
+
def multiple_choice key, *args, &block
|
|
25
|
+
scene_managers[key] = MultipleChoiceSceneManager.new do |config|
|
|
26
|
+
config.start do |actor, data|
|
|
27
|
+
data.options = args
|
|
28
|
+
end
|
|
29
|
+
config.finish(&block)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Create a yes-or-no scene.
|
|
34
|
+
# The user will be required to answer Yes or No to continue.
|
|
35
|
+
#
|
|
36
|
+
# @yieldparam [Character]
|
|
37
|
+
# @yieldparam [YesOrNoSceneData]
|
|
38
|
+
def yes_or_no key, prompt = nil, &block
|
|
39
|
+
manager = YesOrNoSceneManager.new do |config|
|
|
40
|
+
config.prompt = prompt
|
|
41
|
+
config.finish do |actor, data|
|
|
42
|
+
if data.answer.nil?
|
|
43
|
+
actor.tell "Please answer Yes or No."
|
|
44
|
+
else
|
|
45
|
+
data.next_cue ||= :active
|
|
46
|
+
block.call(actor, data)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
scene_managers[key] = manager
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Create a scene with a prompt.
|
|
54
|
+
# This scene will use the provided block to process arbitrary input
|
|
55
|
+
# from the user.
|
|
56
|
+
#
|
|
57
|
+
# @param key [Symbol] A unique name for the scene.
|
|
58
|
+
# @param prompt [String] The prompt message to display to the user.
|
|
59
|
+
# @yieldparam [Character]
|
|
60
|
+
# @yieldparam [SceneData]
|
|
61
|
+
def prompt key, prompt, &block
|
|
62
|
+
scene_managers[key] = SceneManager.new do |config|
|
|
63
|
+
config.prompt = prompt
|
|
64
|
+
config.finish do |actor, data|
|
|
65
|
+
data.next_cue ||= :active
|
|
66
|
+
block.call actor, data
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
scene_managers[key].state = "Prompted"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Create a scene that pauses the game.
|
|
73
|
+
# This scene will execute the specified block and wait for input
|
|
74
|
+
# from the user (e.g., pressing Enter) to continue.
|
|
75
|
+
#
|
|
76
|
+
# @param key [Symbol] A unique name for the scene.
|
|
77
|
+
# @yieldparam [Character]
|
|
78
|
+
# @yieldparam [SceneData]
|
|
79
|
+
def pause key, &block
|
|
80
|
+
manager = PausedSceneManager.new do |config|
|
|
81
|
+
config.start do |actor, data|
|
|
82
|
+
data.next_cue = :active
|
|
83
|
+
block.call actor, data
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
scene_managers[key] = manager
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Create a conclusion.
|
|
90
|
+
# The game will end after this scene is complete.
|
|
91
|
+
#
|
|
92
|
+
# @param key [Symbol] A unique name for the scene.
|
|
93
|
+
# @yieldparam [Character]
|
|
94
|
+
# @yieldparam [SceneData]
|
|
95
|
+
def conclusion key, &block
|
|
96
|
+
manager = ConcludedSceneManager.new do |config|
|
|
97
|
+
config.start(&block)
|
|
98
|
+
end
|
|
99
|
+
scene_managers[key] = manager
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Create a generic scene.
|
|
103
|
+
# After the scene is complete, it will automatically start the next cue.
|
|
104
|
+
#
|
|
105
|
+
# @param [Symbol] A unique name for the scene.
|
|
106
|
+
# @yieldparam [Character]
|
|
107
|
+
# @yieldparam [SceneData]
|
|
108
|
+
def scene key, &block
|
|
109
|
+
scene = SceneManager.new do |manager|
|
|
110
|
+
manager.start do |actor, data|
|
|
111
|
+
data.next_cue = :active
|
|
112
|
+
block.call(actor, data) if !block.nil?
|
|
113
|
+
cue actor, data.next_cue
|
|
114
|
+
actor.scene.start actor
|
|
115
|
+
end
|
|
116
|
+
# Since generic scenes always cue a new scene, there's no reason to
|
|
117
|
+
# define a finish block.
|
|
118
|
+
end
|
|
119
|
+
scene_managers[key] = scene
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Branch to a new scene based on a list of options.
|
|
123
|
+
# This is a specialized type of multiple-choice scene that determines
|
|
124
|
+
# which scene to cue based on a Hash of choices and scene keys.
|
|
125
|
+
#
|
|
126
|
+
# @example Select a scene
|
|
127
|
+
# branch :select_one_or_two, { "one" => :scene_one, "two" => :scene_two }
|
|
128
|
+
# scene :scene_one do |actor|
|
|
129
|
+
# actor.tell "You went to scene one"
|
|
130
|
+
# end
|
|
131
|
+
# scene :scene_two do |actor|
|
|
132
|
+
# actor.tell "You went to scene two"
|
|
133
|
+
# end
|
|
134
|
+
# introduction do |actor|
|
|
135
|
+
# cue actor, :select_one_or_two # The actor will be prompted to select "one" or "two" and get sent to the corresponding scene
|
|
136
|
+
# end
|
|
137
|
+
#
|
|
138
|
+
# @param key [Symbol] A unique name for the scene.
|
|
139
|
+
# @param options [Hash] A Hash of options and associated scene keys.
|
|
140
|
+
def branch key, options
|
|
141
|
+
multiple_choice key, *options.keys do |actor, data|
|
|
142
|
+
cue actor, options[data.selection]
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Cue a Character's next scene
|
|
147
|
+
#
|
|
148
|
+
# @param actor [Character] The character being cued
|
|
149
|
+
# @param key [Symbol] The name of the scene
|
|
150
|
+
def cue actor, key
|
|
151
|
+
if !actor.scene.nil? and actor.scene.state == "Concluded"
|
|
152
|
+
return
|
|
153
|
+
end
|
|
154
|
+
if key.nil?
|
|
155
|
+
raise "Cueing scene with nil key"
|
|
156
|
+
end
|
|
157
|
+
manager = scene_managers[key]
|
|
158
|
+
if manager.nil?
|
|
159
|
+
raise "No '#{key}' scene found"
|
|
160
|
+
else
|
|
161
|
+
actor.scene = manager.prepare key
|
|
162
|
+
end
|
|
163
|
+
@scene
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# This is functionally identical to #cue, but it also raises an
|
|
167
|
+
# exception if the selected scene is not a Concluded state.
|
|
168
|
+
#
|
|
169
|
+
# @param actor [Character] The character being cued
|
|
170
|
+
# @param key [Symbol] The name of the scene
|
|
171
|
+
def conclude actor, key
|
|
172
|
+
key = :concluded if key.nil?
|
|
173
|
+
manager = scene_managers[key]
|
|
174
|
+
if manager.state != "Concluded"
|
|
175
|
+
raise NotConclusionError("Cued scene '#{key}' is not a conclusion")
|
|
176
|
+
end
|
|
177
|
+
cue actor, key
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'gamefic'
|
|
2
|
+
require 'gamefic/grammar'
|
|
3
|
+
|
|
4
|
+
module Gamefic
|
|
5
|
+
module YouMount
|
|
6
|
+
class YouGrammarSet
|
|
7
|
+
include Grammar::Gender
|
|
8
|
+
include Grammar::Person
|
|
9
|
+
include Grammar::Plural
|
|
10
|
+
include Grammar::WordAdapter
|
|
11
|
+
end
|
|
12
|
+
# @return [YouGrammarSet]
|
|
13
|
+
def you
|
|
14
|
+
if @you.nil?
|
|
15
|
+
@you = YouGrammarSet.new
|
|
16
|
+
@you.person = 2
|
|
17
|
+
end
|
|
18
|
+
@you
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|