gamefic 1.7.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +16 -0
- data/.solargraph.yml +5 -0
- data/CHANGELOG.md +10 -0
- data/Gemfile +7 -0
- data/LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +10 -0
- data/gamefic.gemspec +27 -0
- data/lib/gamefic.rb +7 -7
- data/lib/gamefic/action.rb +66 -60
- data/lib/gamefic/active.rb +331 -280
- data/lib/gamefic/actor.rb +8 -5
- data/lib/gamefic/command.rb +9 -7
- data/lib/gamefic/core_ext/array.rb +27 -49
- data/lib/gamefic/core_ext/string.rb +25 -16
- data/lib/gamefic/describable.rb +21 -23
- data/lib/gamefic/element.rb +47 -31
- data/lib/gamefic/entity.rb +6 -12
- data/lib/gamefic/{matchable.rb → keywords.rb} +52 -50
- data/lib/gamefic/messaging.rb +43 -44
- data/lib/gamefic/node.rb +14 -5
- data/lib/gamefic/plot.rb +69 -91
- data/lib/gamefic/plot/darkroom.rb +80 -264
- data/lib/gamefic/plot/host.rb +42 -48
- data/lib/gamefic/plot/snapshot.rb +14 -19
- data/lib/gamefic/query.rb +15 -18
- data/lib/gamefic/query/base.rb +50 -37
- data/lib/gamefic/query/children.rb +0 -0
- data/lib/gamefic/query/descendants.rb +2 -2
- data/lib/gamefic/query/external.rb +18 -14
- data/lib/gamefic/query/family.rb +3 -7
- data/lib/gamefic/query/matches.rb +75 -67
- data/lib/gamefic/query/parent.rb +0 -0
- data/lib/gamefic/query/siblings.rb +0 -0
- data/lib/gamefic/query/text.rb +12 -12
- data/lib/gamefic/query/tree.rb +17 -0
- data/lib/gamefic/scene.rb +0 -2
- data/lib/gamefic/scene/activity.rb +24 -26
- data/lib/gamefic/scene/base.rb +71 -10
- data/lib/gamefic/scene/conclusion.rb +1 -3
- data/lib/gamefic/scene/multiple_choice.rb +19 -14
- data/lib/gamefic/scene/multiple_scene.rb +29 -20
- data/lib/gamefic/scene/pause.rb +8 -3
- data/lib/gamefic/scene/yes_or_no.rb +22 -10
- data/lib/gamefic/scriptable.rb +88 -0
- data/lib/gamefic/serialize.rb +223 -0
- data/lib/gamefic/subplot.rb +38 -35
- data/lib/gamefic/syntax.rb +15 -13
- data/lib/gamefic/version.rb +3 -3
- data/lib/gamefic/world.rb +18 -0
- data/lib/gamefic/world/callbacks.rb +135 -0
- data/lib/gamefic/world/commands.rb +184 -0
- data/lib/gamefic/{plot → world}/entities.rb +33 -35
- data/lib/gamefic/{plot → world}/playbook.rb +245 -240
- data/lib/gamefic/world/players.rb +37 -0
- data/lib/gamefic/world/scenes.rb +226 -0
- metadata +37 -88
- data/bin/gamefic +0 -9
- data/lib/gamefic/engine.rb +0 -7
- data/lib/gamefic/engine/base.rb +0 -59
- data/lib/gamefic/engine/tty.rb +0 -24
- data/lib/gamefic/grammar.rb +0 -13
- data/lib/gamefic/grammar/conjugator.rb +0 -20
- data/lib/gamefic/grammar/gender.rb +0 -11
- data/lib/gamefic/grammar/person.rb +0 -10
- data/lib/gamefic/grammar/plural.rb +0 -13
- data/lib/gamefic/grammar/pronouns.rb +0 -106
- data/lib/gamefic/grammar/tense.rb +0 -6
- data/lib/gamefic/grammar/verb_set.rb +0 -43
- data/lib/gamefic/grammar/verbs.rb +0 -26
- data/lib/gamefic/grammar/word_adapter.rb +0 -49
- data/lib/gamefic/plot/articles.rb +0 -22
- data/lib/gamefic/plot/callbacks.rb +0 -126
- data/lib/gamefic/plot/commands.rb +0 -120
- data/lib/gamefic/plot/players.rb +0 -15
- data/lib/gamefic/plot/scenes.rb +0 -187
- data/lib/gamefic/plot/theater.rb +0 -73
- data/lib/gamefic/plot/you_mount.rb +0 -22
- data/lib/gamefic/scene/custom.rb +0 -9
- data/lib/gamefic/script.rb +0 -13
- data/lib/gamefic/script/base.rb +0 -42
- data/lib/gamefic/script/file.rb +0 -14
- data/lib/gamefic/script/text.rb +0 -14
- data/lib/gamefic/shell.rb +0 -76
- data/lib/gamefic/source.rb +0 -14
- data/lib/gamefic/source/base.rb +0 -12
- data/lib/gamefic/source/file.rb +0 -23
- data/lib/gamefic/source/text.rb +0 -16
- data/lib/gamefic/tester.rb +0 -19
- data/lib/gamefic/text.rb +0 -8
- data/lib/gamefic/text/ansi.rb +0 -53
- data/lib/gamefic/text/html.rb +0 -68
- data/lib/gamefic/text/html/conversions.rb +0 -250
- data/lib/gamefic/text/html/entities.rb +0 -9
- data/lib/gamefic/tty.rb +0 -10
- data/lib/gamefic/user.rb +0 -7
- data/lib/gamefic/user/base.rb +0 -29
- data/lib/gamefic/user/tty.rb +0 -38
@@ -0,0 +1,17 @@
|
|
1
|
+
module Gamefic
|
2
|
+
module Query
|
3
|
+
# Query to retrieve all of the subject's ancestors, siblings, and descendants.
|
4
|
+
#
|
5
|
+
class Tree < Family
|
6
|
+
def context_from(subject)
|
7
|
+
result = super
|
8
|
+
parent = subject.parent
|
9
|
+
until parent.nil?
|
10
|
+
result.unshift parent
|
11
|
+
parent = parent.parent
|
12
|
+
end
|
13
|
+
result
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/gamefic/scene.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Gamefic
|
2
|
-
|
3
2
|
module Scene
|
4
3
|
autoload :Base, 'gamefic/scene/base'
|
5
4
|
autoload :Custom, 'gamefic/scene/custom'
|
@@ -10,5 +9,4 @@ module Gamefic
|
|
10
9
|
autoload :MultipleScene, 'gamefic/scene/multiple_scene'
|
11
10
|
autoload :YesOrNo, 'gamefic/scene/yes_or_no'
|
12
11
|
end
|
13
|
-
|
14
12
|
end
|
@@ -1,26 +1,24 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
o =
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
1
|
+
module Gamefic
|
2
|
+
# Active Scenes handle the default command prompt, where input is parsed
|
3
|
+
# into an Action performed by the Character. This is the default scene in
|
4
|
+
# a Plot.
|
5
|
+
#
|
6
|
+
class Scene::Activity < Scene::Base
|
7
|
+
def post_initialize
|
8
|
+
self.type = 'Activity'
|
9
|
+
end
|
10
|
+
|
11
|
+
def finish
|
12
|
+
super
|
13
|
+
o = nil
|
14
|
+
o = actor.perform input.strip unless input.to_s.strip.empty?
|
15
|
+
actor.performed o
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def type
|
20
|
+
'Activity'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/gamefic/scene/base.rb
CHANGED
@@ -1,61 +1,105 @@
|
|
1
1
|
module Gamefic
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
2
|
+
# An abstract class for building different types of scenes. It can be
|
3
|
+
# extended either through concrete subclasses or by creating anonymous
|
4
|
+
# subclasses through a scene helper method like
|
5
|
+
# `Gamefic::World::Scenes#custom`.
|
5
6
|
#
|
6
7
|
class Scene::Base
|
8
|
+
include Gamefic::Serialize
|
9
|
+
extend Gamefic::Serialize
|
10
|
+
|
11
|
+
# The scene's primary actor.
|
12
|
+
#
|
13
|
+
# @return [Gamefic::Actor]
|
7
14
|
attr_reader :actor
|
15
|
+
|
16
|
+
# A human-readable string identifying the type of scene.
|
17
|
+
#
|
18
|
+
# @return [String]
|
8
19
|
attr_writer :type
|
20
|
+
|
21
|
+
# The text to display when requesting input.
|
22
|
+
#
|
23
|
+
# @return [String]
|
9
24
|
attr_writer :prompt
|
25
|
+
|
26
|
+
# The input received from the actor.
|
27
|
+
#
|
28
|
+
# @return [String]
|
10
29
|
attr_reader :input
|
11
30
|
|
12
|
-
|
31
|
+
# @return [Hash{Symbol => Object}]
|
32
|
+
attr_reader :data
|
33
|
+
|
34
|
+
def initialize actor, **data
|
13
35
|
@actor = actor
|
36
|
+
@data = data
|
14
37
|
post_initialize
|
15
38
|
end
|
16
39
|
|
40
|
+
# A shortcut for the #data hash.
|
41
|
+
#
|
42
|
+
# @param key [Symbol]
|
43
|
+
# @return [Object]
|
44
|
+
def [] key
|
45
|
+
data[key]
|
46
|
+
end
|
47
|
+
|
17
48
|
def post_initialize
|
18
49
|
end
|
19
50
|
|
51
|
+
# Set a proc to be executed at the end of the scene.
|
52
|
+
#
|
20
53
|
def on_finish &block
|
21
54
|
@finish_block = block
|
22
55
|
end
|
23
56
|
|
57
|
+
# Update the scene.
|
58
|
+
#
|
24
59
|
def update
|
25
60
|
@input = actor.queue.shift
|
26
61
|
finish
|
27
62
|
end
|
28
63
|
|
64
|
+
# Start the scene.
|
65
|
+
#
|
29
66
|
def start
|
30
67
|
self.class.start_block.call @actor, self unless self.class.start_block.nil?
|
68
|
+
@actor.entered self if tracked?
|
31
69
|
end
|
32
70
|
|
71
|
+
# Finish the scene.
|
72
|
+
#
|
33
73
|
def finish
|
34
74
|
@finish_block.call @actor, self unless @finish_block.nil?
|
35
75
|
@finished = true
|
36
76
|
end
|
37
77
|
|
78
|
+
# Determine whether the scene's execution is finished.
|
79
|
+
#
|
80
|
+
# @return [Boolean]
|
38
81
|
def finished?
|
39
82
|
@finished ||= false
|
40
83
|
end
|
41
84
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
85
|
+
# Get a hash that describes the current state of the scene.
|
86
|
+
#
|
87
|
+
# @return [Hash]
|
46
88
|
def state
|
47
89
|
{
|
48
90
|
scene: type, prompt: prompt
|
49
91
|
}
|
50
92
|
end
|
51
93
|
|
94
|
+
# @yieldparam [Class<Gamefic::Actor>]
|
95
|
+
# @return [Class<Gamefic::Scene::Base>]
|
52
96
|
def self.subclass &block
|
53
97
|
c = Class.new(self) do
|
54
98
|
on_start &block
|
55
99
|
end
|
56
100
|
c
|
57
101
|
end
|
58
|
-
|
102
|
+
|
59
103
|
# Get the prompt to be displayed to the user when accepting input.
|
60
104
|
#
|
61
105
|
# @return [String] The text to be displayed.
|
@@ -63,19 +107,36 @@ module Gamefic
|
|
63
107
|
@prompt ||= '>'
|
64
108
|
end
|
65
109
|
|
110
|
+
# Get a String that describes the type of scene.
|
111
|
+
#
|
112
|
+
# @return [String]
|
66
113
|
def type
|
67
114
|
@type ||= 'Scene'
|
68
115
|
end
|
69
116
|
|
117
|
+
# @yieldparam [Class<Gamefic::Scene::Base>]
|
70
118
|
def self.on_start &block
|
71
119
|
@start_block = block
|
72
120
|
end
|
73
121
|
|
122
|
+
def tracked?
|
123
|
+
self.class.tracked?
|
124
|
+
end
|
125
|
+
|
126
|
+
def tracked= bool
|
127
|
+
self.class.tracked = bool
|
128
|
+
end
|
129
|
+
|
74
130
|
class << self
|
131
|
+
attr_writer :tracked
|
132
|
+
|
75
133
|
def start_block
|
76
134
|
@start_block
|
77
135
|
end
|
136
|
+
|
137
|
+
def tracked?
|
138
|
+
@tracked ||= false
|
139
|
+
end
|
78
140
|
end
|
79
141
|
end
|
80
|
-
|
81
142
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
module Gamefic
|
2
|
-
|
3
2
|
# Provide a list of options and process the selection in the scene's finish
|
4
3
|
# block. After the scene is finished, the :active scene will be cued unless
|
5
4
|
# some other scene has already been prepared or cued.
|
@@ -7,10 +6,22 @@ module Gamefic
|
|
7
6
|
# The finish block's input parameter receives a MultipleChoice::Input object
|
8
7
|
# instead of a String.
|
9
8
|
#
|
10
|
-
class Scene::MultipleChoice < Scene::
|
9
|
+
class Scene::MultipleChoice < Scene::Base
|
10
|
+
# The zero-based index of the selected option.
|
11
|
+
#
|
12
|
+
# @return [Integer]
|
11
13
|
attr_reader :index
|
14
|
+
|
15
|
+
# The one-based index of the selected option.
|
16
|
+
#
|
17
|
+
# @return [Integer]
|
12
18
|
attr_reader :number
|
19
|
+
|
20
|
+
# The full text of the selected option.
|
21
|
+
#
|
22
|
+
# @return [String]
|
13
23
|
attr_reader :selection
|
24
|
+
|
14
25
|
attr_writer :invalid_message
|
15
26
|
|
16
27
|
def post_initialize
|
@@ -22,16 +33,21 @@ module Gamefic
|
|
22
33
|
get_choice
|
23
34
|
if selection.nil?
|
24
35
|
actor.tell invalid_message
|
25
|
-
tell_options
|
26
36
|
else
|
27
37
|
super
|
28
38
|
end
|
29
39
|
end
|
30
40
|
|
41
|
+
# The array of available options.
|
42
|
+
#
|
43
|
+
# @return [Array<String>]
|
31
44
|
def options
|
32
45
|
@options ||= []
|
33
46
|
end
|
34
47
|
|
48
|
+
# The text to display when an invalid selection is received.
|
49
|
+
#
|
50
|
+
# @return [String]
|
35
51
|
def invalid_message
|
36
52
|
@invalid_message ||= 'That is not a valid choice.'
|
37
53
|
end
|
@@ -60,16 +76,5 @@ module Gamefic
|
|
60
76
|
}
|
61
77
|
end
|
62
78
|
end
|
63
|
-
|
64
|
-
def tell_options
|
65
|
-
list = '<ol class="multiple_choice">'
|
66
|
-
options.each { |o|
|
67
|
-
list += "<li><a href=\"#\" rel=\"gamefic\" data-command=\"#{o}\">#{o}</a></li>"
|
68
|
-
}
|
69
|
-
list += "</ol>"
|
70
|
-
actor.tell list
|
71
|
-
end
|
72
|
-
|
73
79
|
end
|
74
|
-
|
75
80
|
end
|
@@ -1,20 +1,29 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
1
|
+
module Gamefic
|
2
|
+
class Scene::MultipleScene < Scene::MultipleChoice
|
3
|
+
def option_map
|
4
|
+
@option_map ||= {}
|
5
|
+
end
|
6
|
+
|
7
|
+
# @param option [String]
|
8
|
+
# @param scene [Class<Gamefic::Scene::Base>]
|
9
|
+
def map option, scene
|
10
|
+
options.push option
|
11
|
+
option_map[option] = scene
|
12
|
+
end
|
13
|
+
|
14
|
+
def finish
|
15
|
+
get_choice
|
16
|
+
unless selection.nil?
|
17
|
+
actor.prepare option_map[selection]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def state
|
22
|
+
entered = {}
|
23
|
+
option_map.each_pair do |k, v|
|
24
|
+
entered[k] = actor.entered?(v)
|
25
|
+
end
|
26
|
+
super.merge entered: entered
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/gamefic/scene/pause.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
module Gamefic
|
2
|
-
|
3
2
|
# Pause for user input.
|
4
3
|
#
|
5
|
-
class Scene::Pause < Scene::
|
4
|
+
class Scene::Pause < Scene::Base
|
6
5
|
def post_initialize
|
7
6
|
self.type = 'Pause'
|
8
7
|
self.prompt = 'Press enter to continue...'
|
9
8
|
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def tracked?
|
12
|
+
@tracked = true if @tracked.nil?
|
13
|
+
@tracked
|
14
|
+
end
|
15
|
+
end
|
10
16
|
end
|
11
|
-
|
12
17
|
end
|
@@ -1,32 +1,41 @@
|
|
1
1
|
module Gamefic
|
2
|
-
|
3
2
|
# Prompt the user to answer "yes" or "no". The scene will accept variations
|
4
3
|
# like "YES" or "n" and normalize the answer to "yes" or "no" in the finish
|
5
4
|
# block. After the scene is finished, the :active scene will be cued if no
|
6
5
|
# other scene has been prepared or cued.
|
7
6
|
#
|
8
|
-
class Scene::YesOrNo < Scene::
|
7
|
+
class Scene::YesOrNo < Scene::Base
|
8
|
+
attr_writer :invalid_message
|
9
|
+
|
9
10
|
def post_initialize
|
10
11
|
self.type = 'YesOrNo'
|
11
|
-
self.prompt = 'Yes or No
|
12
|
+
self.prompt = 'Yes or No:'
|
12
13
|
end
|
13
14
|
|
15
|
+
# True if the actor's answer is Yes.
|
16
|
+
# Any answer beginning with letter Y is considered Yes.
|
17
|
+
#
|
18
|
+
# @return [Boolean]
|
14
19
|
def yes?
|
15
|
-
input.to_s[0,1].downcase == 'y'
|
20
|
+
input.to_s[0,1].downcase == 'y' or input.to_i == 1
|
16
21
|
end
|
17
22
|
|
23
|
+
# True if the actor's answer is No.
|
24
|
+
# Any answer beginning with letter N is considered No.
|
25
|
+
#
|
26
|
+
# @return [Boolean]
|
18
27
|
def no?
|
19
|
-
input.to_s[0,1].downcase == 'n'
|
28
|
+
input.to_s[0,1].downcase == 'n' or input.to_i == 2
|
20
29
|
end
|
21
30
|
|
31
|
+
# The message sent to the user for an invalid answer, i.e., the input
|
32
|
+
# could not be resolved to either Yes or No.
|
33
|
+
#
|
34
|
+
# @return [String]
|
22
35
|
def invalid_message
|
23
36
|
@invalid_message ||= 'Please enter Yes or No.'
|
24
37
|
end
|
25
38
|
|
26
|
-
def prompt
|
27
|
-
@prompt ||= 'Yes or No?'
|
28
|
-
end
|
29
|
-
|
30
39
|
def finish
|
31
40
|
if yes? or no?
|
32
41
|
super
|
@@ -34,6 +43,9 @@ module Gamefic
|
|
34
43
|
actor.tell invalid_message
|
35
44
|
end
|
36
45
|
end
|
37
|
-
end
|
38
46
|
|
47
|
+
def state
|
48
|
+
super.merge options: ['Yes', 'No']
|
49
|
+
end
|
50
|
+
end
|
39
51
|
end
|