gamefic 1.7.0 → 2.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 +5 -5
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +13 -0
- data/.solargraph.yml +5 -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 -6
- data/lib/gamefic/action.rb +38 -28
- data/lib/gamefic/active.rb +325 -280
- data/lib/gamefic/actor.rb +8 -5
- data/lib/gamefic/command.rb +9 -7
- data/lib/gamefic/core_ext/array.rb +24 -49
- data/lib/gamefic/core_ext/string.rb +25 -16
- data/lib/gamefic/describable.rb +21 -23
- data/lib/gamefic/element.rb +43 -31
- data/lib/gamefic/entity.rb +6 -12
- data/lib/gamefic/index.rb +121 -0
- 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 -89
- data/lib/gamefic/plot/darkroom.rb +92 -264
- data/lib/gamefic/plot/host.rb +42 -48
- data/lib/gamefic/plot/snapshot.rb +5 -18
- data/lib/gamefic/query.rb +14 -18
- data/lib/gamefic/query/base.rb +30 -18
- data/lib/gamefic/query/children.rb +0 -0
- data/lib/gamefic/query/external.rb +18 -14
- data/lib/gamefic/query/family.rb +1 -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 +2 -1
- data/lib/gamefic/scene.rb +0 -2
- data/lib/gamefic/scene/activity.rb +24 -26
- data/lib/gamefic/scene/base.rb +64 -8
- data/lib/gamefic/scene/conclusion.rb +0 -2
- data/lib/gamefic/scene/custom.rb +0 -2
- data/lib/gamefic/scene/multiple_choice.rb +18 -3
- data/lib/gamefic/scene/multiple_scene.rb +29 -20
- data/lib/gamefic/scene/pause.rb +7 -2
- data/lib/gamefic/scene/yes_or_no.rb +21 -9
- data/lib/gamefic/scriptable.rb +87 -0
- data/lib/gamefic/serialize.rb +68 -0
- data/lib/gamefic/subplot.rb +29 -35
- data/lib/gamefic/syntax.rb +14 -13
- data/lib/gamefic/version.rb +3 -3
- data/lib/gamefic/world.rb +16 -0
- data/lib/gamefic/world/callbacks.rb +135 -0
- data/lib/gamefic/world/commands.rb +184 -0
- data/lib/gamefic/{plot → world}/entities.rb +30 -29
- data/lib/gamefic/{plot → world}/playbook.rb +255 -240
- data/lib/gamefic/world/players.rb +21 -0
- data/lib/gamefic/world/scenes.rb +226 -0
- metadata +41 -92
- 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/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
data/lib/gamefic/actor.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
module Gamefic
|
2
|
+
# An entity that is capable of performing actions and participating in
|
3
|
+
# scenes.
|
4
|
+
#
|
5
|
+
class Actor < Gamefic::Entity
|
6
|
+
include Gamefic::Active
|
7
|
+
end
|
8
|
+
end
|
data/lib/gamefic/command.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
module Gamefic
|
2
2
|
# A Command is a collection of tokens parsed from a Syntax.
|
3
|
-
#
|
3
|
+
# Playbooks use Commands to find and execute corresponding Actions.
|
4
4
|
#
|
5
5
|
class Command
|
6
|
-
#
|
7
|
-
#
|
6
|
+
# A Symbol representing the command's verb or verbal phrase.
|
7
|
+
#
|
8
|
+
# @return [Symbol]
|
8
9
|
attr_reader :verb
|
9
|
-
|
10
|
-
#
|
11
|
-
#
|
10
|
+
|
11
|
+
# An Array of arguments to be mapped to an Action's Queries.
|
12
|
+
#
|
13
|
+
# @return [Array<String>]
|
12
14
|
attr_reader :arguments
|
13
|
-
|
15
|
+
|
14
16
|
def initialize verb, arguments
|
15
17
|
@verb = verb
|
16
18
|
@arguments = arguments
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class Array
|
2
|
-
# Get a subset of the array that matches the
|
2
|
+
# Get a subset of the array that matches the arguments.
|
3
3
|
# If the argument is a Class or Module, the elements must be of the type.
|
4
4
|
# If the argument is a Symbol, it should be a method for which the elements must return true.
|
5
5
|
# If the argument is an Object, the elements must equal the object.
|
@@ -11,45 +11,24 @@ class Array
|
|
11
11
|
# animals.that_are(:nil?) #=> [nil]
|
12
12
|
#
|
13
13
|
# @return [Array]
|
14
|
-
def that_are(cls)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
return self.clone.delete_if { |i| i.send(cls) == false }
|
19
|
-
else
|
20
|
-
if self.include?(cls)
|
21
|
-
return [cls]
|
22
|
-
end
|
23
|
-
return Array.new
|
14
|
+
def that_are(*cls)
|
15
|
+
result = self.clone
|
16
|
+
cls.each do |c|
|
17
|
+
_keep result, c, true
|
24
18
|
end
|
19
|
+
result
|
25
20
|
end
|
26
21
|
|
27
|
-
# Get a subset of the array that does not match the
|
22
|
+
# Get a subset of the array that does not match the arguments.
|
28
23
|
# See Array#that_are for information about how arguments are evaluated.
|
29
24
|
#
|
30
25
|
# @return [Array]
|
31
|
-
def that_are_not(cls)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
return self.clone.delete_if { |i| i.send(cls) == true }
|
36
|
-
else
|
37
|
-
return self.clone - [cls]
|
26
|
+
def that_are_not(*cls)
|
27
|
+
result = self.clone
|
28
|
+
cls.each do |c|
|
29
|
+
_keep result, c, false
|
38
30
|
end
|
39
|
-
|
40
|
-
|
41
|
-
# Get a random element from the array.
|
42
|
-
# @deprecated Use Array#sample instead.
|
43
|
-
#
|
44
|
-
def random
|
45
|
-
return self[rand(self.length)]
|
46
|
-
end
|
47
|
-
|
48
|
-
# Pop a random element from the array.
|
49
|
-
# @deprecated Use Array#pop_sample instead.
|
50
|
-
#
|
51
|
-
def pop_random
|
52
|
-
pop_sample
|
31
|
+
result
|
53
32
|
end
|
54
33
|
|
55
34
|
# Pop a random element from the array.
|
@@ -58,22 +37,6 @@ class Array
|
|
58
37
|
delete_at(rand(self.length))
|
59
38
|
end
|
60
39
|
|
61
|
-
# @todo Candidate for deprecation
|
62
|
-
# @return [Array]
|
63
|
-
#def shuffle
|
64
|
-
# self.sort { |a, b|
|
65
|
-
# rand(3) <=> rand(3)
|
66
|
-
# }
|
67
|
-
#end
|
68
|
-
|
69
|
-
# @todo Candidate for deprecation
|
70
|
-
# @return [Array]
|
71
|
-
#def shuffle!
|
72
|
-
# self.sort! { |a, b|
|
73
|
-
# rand(3) <=> rand(3)
|
74
|
-
# }
|
75
|
-
#end
|
76
|
-
|
77
40
|
# Get a string representation of the array that separates elements with
|
78
41
|
# commas and adds a conjunction before the last element.
|
79
42
|
#
|
@@ -97,4 +60,16 @@ class Array
|
|
97
60
|
def join_or(sep = ', ', orSep = ' or ', serial = true)
|
98
61
|
join_and(sep, orSep, serial)
|
99
62
|
end
|
63
|
+
|
64
|
+
# private
|
65
|
+
|
66
|
+
def _keep(arr, cls, bool)
|
67
|
+
if (cls.kind_of?(Class) or cls.kind_of?(Module))
|
68
|
+
arr.keep_if { |i| i.is_a?(cls) == bool }
|
69
|
+
elsif cls.kind_of?(Symbol)
|
70
|
+
arr.keep_if { |i| i.send(cls) == bool }
|
71
|
+
else
|
72
|
+
arr.keep_if {|i| (i == cls) == bool}
|
73
|
+
end
|
74
|
+
end
|
100
75
|
end
|
@@ -1,16 +1,25 @@
|
|
1
|
-
class String
|
2
|
-
include Gamefic::
|
3
|
-
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
1
|
+
class String
|
2
|
+
include Gamefic::Keywords
|
3
|
+
|
4
|
+
# Capitalize the first letter without changing the rest of the string.
|
5
|
+
# (String#capitalize makes the rest of the string lower-case.)
|
6
|
+
#
|
7
|
+
# @return [String] The capitalized text
|
8
|
+
def capitalize_first
|
9
|
+
"#{self[0,1].upcase}#{self[1,self.length]}"
|
10
|
+
end
|
11
|
+
|
12
|
+
# An alias for #capitalize_first.
|
13
|
+
#
|
14
|
+
# @return [String]
|
15
|
+
def cap_first
|
16
|
+
self.capitalize_first
|
17
|
+
end
|
18
|
+
|
19
|
+
# Get an array of words split by any whitespace.
|
20
|
+
#
|
21
|
+
# @return [Array]
|
22
|
+
def split_words
|
23
|
+
self.gsub(/[\s]+/, ' ').strip.split
|
24
|
+
end
|
25
|
+
end
|
data/lib/gamefic/describable.rb
CHANGED
@@ -1,13 +1,8 @@
|
|
1
|
-
#require 'gamefic/keywords'
|
2
|
-
require 'gamefic/grammar'
|
3
|
-
|
4
1
|
module Gamefic
|
5
|
-
|
6
2
|
# Add a variety of text properties for naming, describing, and referencing
|
7
3
|
# objects.
|
8
4
|
module Describable
|
9
|
-
include
|
10
|
-
include Matchable
|
5
|
+
include Keywords
|
11
6
|
|
12
7
|
# Get the name of the object.
|
13
8
|
# The name is usually presented without articles (e.g., "object" instead
|
@@ -17,25 +12,28 @@ module Gamefic
|
|
17
12
|
# @return [String]
|
18
13
|
attr_reader :name
|
19
14
|
|
15
|
+
# Alternate words that can be used to describe the object. Synonyms are
|
16
|
+
# used in conjunction with the object's name when generating keywords.
|
17
|
+
#
|
20
18
|
# @return [String]
|
21
19
|
attr_reader :synonyms
|
22
20
|
|
23
|
-
#
|
21
|
+
# The object's indefinite article (usually "a" or "an").
|
24
22
|
#
|
25
23
|
# @return [String]
|
26
24
|
attr_reader :indefinite_article
|
27
25
|
|
28
|
-
#
|
26
|
+
# The object's definite article (usually "the").
|
29
27
|
#
|
30
28
|
# @return [String]
|
31
29
|
attr_reader :definite_article
|
32
30
|
|
33
|
-
# Get a set of
|
31
|
+
# Get a set of keywords associated with the object.
|
34
32
|
# Keywords are typically the words in the object's name plus its synonyms.
|
35
33
|
#
|
36
|
-
# @return [
|
34
|
+
# @return [Array<String>]
|
37
35
|
def keywords
|
38
|
-
@keywords ||= "#{definite_article} #{indefinite_article} #{name} #{synonyms}".downcase.split(
|
36
|
+
@keywords ||= "#{definite_article} #{indefinite_article} #{name} #{synonyms}".downcase.split(Keywords::SPLIT_REGEXP).uniq
|
39
37
|
end
|
40
38
|
|
41
39
|
# Get the name of the object with an indefinite article.
|
@@ -46,7 +44,7 @@ module Gamefic
|
|
46
44
|
def indefinitely
|
47
45
|
((proper_named? or indefinite_article == '') ? '' : "#{indefinite_article} ") + name.to_s
|
48
46
|
end
|
49
|
-
|
47
|
+
|
50
48
|
# Get the name of the object with a definite article.
|
51
49
|
# Note: proper-named objects never append an article, though an article
|
52
50
|
# may be included in its proper name.
|
@@ -55,14 +53,14 @@ module Gamefic
|
|
55
53
|
def definitely
|
56
54
|
((proper_named? or definite_article == '') ? '' : "#{definite_article} ") + name.to_s
|
57
55
|
end
|
58
|
-
|
56
|
+
|
59
57
|
# Get the definite article for this object (usually "the").
|
60
58
|
#
|
61
59
|
# @return [String]
|
62
60
|
def definite_article
|
63
61
|
@definite_article || "the"
|
64
62
|
end
|
65
|
-
|
63
|
+
|
66
64
|
# Set the definite article.
|
67
65
|
#
|
68
66
|
# @param [String] article
|
@@ -78,7 +76,7 @@ module Gamefic
|
|
78
76
|
@keywords = nil
|
79
77
|
@indefinite_article = article
|
80
78
|
end
|
81
|
-
|
79
|
+
|
82
80
|
# Is the object proper-named?
|
83
81
|
# Proper-named objects typically do not add articles to their names when
|
84
82
|
# referenced #definitely or #indefinitely, e.g., "Jane Doe" instead of
|
@@ -88,7 +86,7 @@ module Gamefic
|
|
88
86
|
def proper_named?
|
89
87
|
(@proper_named == true)
|
90
88
|
end
|
91
|
-
|
89
|
+
|
92
90
|
# Set whether the object has a proper name.
|
93
91
|
#
|
94
92
|
# @param bool [Boolean]
|
@@ -101,7 +99,7 @@ module Gamefic
|
|
101
99
|
end
|
102
100
|
@proper_named = bool
|
103
101
|
end
|
104
|
-
|
102
|
+
|
105
103
|
# Set the name of the object.
|
106
104
|
# Setting the name performs some magic to determine how to handle
|
107
105
|
# articles ("an object" and "the object").
|
@@ -132,21 +130,21 @@ module Gamefic
|
|
132
130
|
end
|
133
131
|
@name = value
|
134
132
|
end
|
135
|
-
|
133
|
+
|
136
134
|
# Does the object have a description?
|
137
135
|
#
|
138
136
|
# @return [Boolean]
|
139
137
|
def has_description?
|
140
138
|
(@description.to_s != '')
|
141
139
|
end
|
142
|
-
|
140
|
+
|
143
141
|
# Get the object's description.
|
144
142
|
#
|
145
143
|
# @return [String]
|
146
144
|
def description
|
147
145
|
@description || (Describable.default_description % { :name => self.definitely, :Name => self.definitely.capitalize_first })
|
148
146
|
end
|
149
|
-
|
147
|
+
|
150
148
|
# Set the object's description.
|
151
149
|
#
|
152
150
|
# @param text [String]
|
@@ -157,7 +155,7 @@ module Gamefic
|
|
157
155
|
@description = nil
|
158
156
|
end
|
159
157
|
end
|
160
|
-
|
158
|
+
|
161
159
|
def synonyms= text
|
162
160
|
@keywords = nil
|
163
161
|
@synonyms = text
|
@@ -172,14 +170,14 @@ module Gamefic
|
|
172
170
|
def self.default_description=(text)
|
173
171
|
@default_description = text
|
174
172
|
end
|
175
|
-
|
173
|
+
|
176
174
|
# Get the object's default description.
|
177
175
|
#
|
178
176
|
# @return [String]
|
179
177
|
def self.default_description
|
180
178
|
@default_description || "There's nothing special about %{name}."
|
181
179
|
end
|
182
|
-
|
180
|
+
|
183
181
|
# Get a String representation of the object. By default, this is the
|
184
182
|
# object's name with an indefinite article, e.g., "a person" or "a red
|
185
183
|
# dog."
|
data/lib/gamefic/element.rb
CHANGED
@@ -1,31 +1,43 @@
|
|
1
|
-
module Gamefic
|
2
|
-
class
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
1
|
+
module Gamefic
|
2
|
+
# The simplest class that can compose an object for use in a plot.
|
3
|
+
# Most game objects, especially tangible items in the game, should derive
|
4
|
+
# from the Entity class. Elements, on the other hand, can be used for
|
5
|
+
# abstractions and ideas that don't have a physical presence but still might
|
6
|
+
# need to be referenced in a command.
|
7
|
+
#
|
8
|
+
class Element
|
9
|
+
include Gamefic::Describable
|
10
|
+
include Gamefic::Index
|
11
|
+
|
12
|
+
# @todo It would be nice if this initialization wasn't necessary.
|
13
|
+
def initialize(args = {})
|
14
|
+
super self.class.default_attributes.merge(args)
|
15
|
+
post_initialize
|
16
|
+
yield self if block_given?
|
17
|
+
end
|
18
|
+
|
19
|
+
def post_initialize
|
20
|
+
# raise NotImplementedError, "#{self.class} must implement post_initialize"
|
21
|
+
end
|
22
|
+
|
23
|
+
class << self
|
24
|
+
# Set or update the default values for new instances.
|
25
|
+
#
|
26
|
+
# @param attrs [Hash] The attributes to be merged into the defaults.
|
27
|
+
def set_default attrs = {}
|
28
|
+
default_attributes.merge! attrs
|
29
|
+
end
|
30
|
+
|
31
|
+
# A hash of default values for attributes when creating an instance.
|
32
|
+
#
|
33
|
+
# @return [Hash]
|
34
|
+
def default_attributes
|
35
|
+
@default_attributes ||= {}
|
36
|
+
end
|
37
|
+
|
38
|
+
def inherited subclass
|
39
|
+
subclass.set_default default_attributes
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/gamefic/entity.rb
CHANGED
@@ -3,22 +3,17 @@ require "gamefic/describable"
|
|
3
3
|
require 'gamefic/messaging'
|
4
4
|
|
5
5
|
module Gamefic
|
6
|
-
|
6
|
+
# A physical object that can exist in a plot. Most objects with which
|
7
|
+
# players interact are entities. Player characters themselves typically
|
8
|
+
# derive from entities, e.g., the Gamefic::Actor class.
|
9
|
+
#
|
7
10
|
class Entity < Element
|
8
11
|
include Node
|
9
12
|
include Messaging
|
10
|
-
include Grammar::WordAdapter
|
11
13
|
|
12
|
-
# Execute the entity's on_update blocks.
|
13
|
-
# This method is typically called by the Engine that manages game execution.
|
14
|
-
# The base method does nothing. Subclasses can override it.
|
15
|
-
#
|
16
|
-
def update
|
17
|
-
end
|
18
|
-
|
19
14
|
# Set the Entity's parent.
|
20
15
|
#
|
21
|
-
# @param node [Entity] The new parent.
|
16
|
+
# @param node [Gamefic::Entity] The new parent.
|
22
17
|
def parent=(node)
|
23
18
|
if node != nil and node.kind_of?(Entity) == false
|
24
19
|
raise "Entity's parent must be an Entity"
|
@@ -43,7 +38,7 @@ module Gamefic
|
|
43
38
|
def [](key)
|
44
39
|
session[key]
|
45
40
|
end
|
46
|
-
|
41
|
+
|
47
42
|
# Set a custom property.
|
48
43
|
#
|
49
44
|
# @param key [Symbol] The property's name
|
@@ -52,5 +47,4 @@ module Gamefic
|
|
52
47
|
session[key] = value
|
53
48
|
end
|
54
49
|
end
|
55
|
-
|
56
50
|
end
|