gamefic 1.6.0 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +16 -0
  5. data/.solargraph.yml +5 -0
  6. data/CHANGELOG.md +6 -0
  7. data/Gemfile +7 -0
  8. data/LICENSE +20 -0
  9. data/README.md +28 -0
  10. data/Rakefile +10 -0
  11. data/gamefic.gemspec +27 -0
  12. data/lib/gamefic.rb +11 -8
  13. data/lib/gamefic/action.rb +68 -58
  14. data/lib/gamefic/active.rb +331 -0
  15. data/lib/gamefic/actor.rb +8 -0
  16. data/lib/gamefic/command.rb +9 -7
  17. data/lib/gamefic/core_ext/array.rb +27 -49
  18. data/lib/gamefic/core_ext/string.rb +25 -16
  19. data/lib/gamefic/describable.rb +37 -22
  20. data/lib/gamefic/element.rb +47 -0
  21. data/lib/gamefic/entity.rb +24 -48
  22. data/lib/gamefic/{matchable.rb → keywords.rb} +52 -50
  23. data/lib/gamefic/messaging.rb +43 -45
  24. data/lib/gamefic/node.rb +14 -5
  25. data/lib/gamefic/plot.rb +73 -85
  26. data/lib/gamefic/plot/darkroom.rb +80 -0
  27. data/lib/gamefic/plot/host.rb +42 -46
  28. data/lib/gamefic/plot/snapshot.rb +14 -214
  29. data/lib/gamefic/query.rb +15 -17
  30. data/lib/gamefic/query/base.rb +51 -42
  31. data/lib/gamefic/query/children.rb +0 -0
  32. data/lib/gamefic/query/descendants.rb +2 -2
  33. data/lib/gamefic/query/external.rb +18 -0
  34. data/lib/gamefic/query/family.rb +3 -7
  35. data/lib/gamefic/query/matches.rb +75 -67
  36. data/lib/gamefic/query/parent.rb +0 -0
  37. data/lib/gamefic/query/siblings.rb +0 -0
  38. data/lib/gamefic/query/text.rb +12 -12
  39. data/lib/gamefic/query/tree.rb +17 -0
  40. data/lib/gamefic/scene.rb +1 -5
  41. data/lib/gamefic/scene/{active.rb → activity.rb} +4 -6
  42. data/lib/gamefic/scene/base.rb +77 -13
  43. data/lib/gamefic/scene/conclusion.rb +0 -2
  44. data/lib/gamefic/scene/custom.rb +0 -2
  45. data/lib/gamefic/scene/multiple_choice.rb +18 -16
  46. data/lib/gamefic/scene/multiple_scene.rb +29 -20
  47. data/lib/gamefic/scene/pause.rb +7 -2
  48. data/lib/gamefic/scene/yes_or_no.rb +21 -9
  49. data/lib/gamefic/scriptable.rb +88 -0
  50. data/lib/gamefic/serialize.rb +223 -0
  51. data/lib/gamefic/subplot.rb +47 -51
  52. data/lib/gamefic/syntax.rb +15 -13
  53. data/lib/gamefic/version.rb +3 -3
  54. data/lib/gamefic/world.rb +18 -0
  55. data/lib/gamefic/world/callbacks.rb +135 -0
  56. data/lib/gamefic/world/commands.rb +184 -0
  57. data/lib/gamefic/world/entities.rb +98 -0
  58. data/lib/gamefic/{plot → world}/playbook.rb +245 -236
  59. data/lib/gamefic/world/players.rb +37 -0
  60. data/lib/gamefic/world/scenes.rb +226 -0
  61. metadata +40 -108
  62. data/bin/gamefic +0 -9
  63. data/lib/gamefic/character.rb +0 -232
  64. data/lib/gamefic/character/state.rb +0 -12
  65. data/lib/gamefic/engine.rb +0 -7
  66. data/lib/gamefic/engine/base.rb +0 -66
  67. data/lib/gamefic/engine/tty.rb +0 -24
  68. data/lib/gamefic/grammar.rb +0 -13
  69. data/lib/gamefic/grammar/conjugator.rb +0 -20
  70. data/lib/gamefic/grammar/gender.rb +0 -11
  71. data/lib/gamefic/grammar/person.rb +0 -10
  72. data/lib/gamefic/grammar/plural.rb +0 -13
  73. data/lib/gamefic/grammar/pronouns.rb +0 -105
  74. data/lib/gamefic/grammar/tense.rb +0 -6
  75. data/lib/gamefic/grammar/verb_set.rb +0 -43
  76. data/lib/gamefic/grammar/verbs.rb +0 -26
  77. data/lib/gamefic/grammar/word_adapter.rb +0 -49
  78. data/lib/gamefic/plot/articles.rb +0 -22
  79. data/lib/gamefic/plot/callbacks.rb +0 -127
  80. data/lib/gamefic/plot/commands.rb +0 -121
  81. data/lib/gamefic/plot/entities.rb +0 -88
  82. data/lib/gamefic/plot/players.rb +0 -15
  83. data/lib/gamefic/plot/scenes.rb +0 -149
  84. data/lib/gamefic/plot/theater.rb +0 -73
  85. data/lib/gamefic/plot/you_mount.rb +0 -22
  86. data/lib/gamefic/script.rb +0 -13
  87. data/lib/gamefic/script/base.rb +0 -42
  88. data/lib/gamefic/script/file.rb +0 -14
  89. data/lib/gamefic/script/text.rb +0 -14
  90. data/lib/gamefic/shell.rb +0 -76
  91. data/lib/gamefic/source.rb +0 -14
  92. data/lib/gamefic/source/base.rb +0 -12
  93. data/lib/gamefic/source/file.rb +0 -23
  94. data/lib/gamefic/source/text.rb +0 -16
  95. data/lib/gamefic/tester.rb +0 -19
  96. data/lib/gamefic/text.rb +0 -8
  97. data/lib/gamefic/text/ansi.rb +0 -53
  98. data/lib/gamefic/text/html.rb +0 -68
  99. data/lib/gamefic/text/html/conversions.rb +0 -250
  100. data/lib/gamefic/text/html/entities.rb +0 -9
  101. data/lib/gamefic/tty.rb +0 -10
  102. data/lib/gamefic/user.rb +0 -8
  103. data/lib/gamefic/user/base.rb +0 -15
  104. data/lib/gamefic/user/buffer.rb +0 -32
  105. data/lib/gamefic/user/tty.rb +0 -54
@@ -0,0 +1,8 @@
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
@@ -1,16 +1,18 @@
1
1
  module Gamefic
2
2
  # A Command is a collection of tokens parsed from a Syntax.
3
- # The Director uses Commands to find and execute corresponding Actions.
3
+ # Playbooks use Commands to find and execute corresponding Actions.
4
4
  #
5
5
  class Command
6
- # @!attribute [r] verb
7
- # @return [Symbol] A Symbol representing the command's verb or verbal phrase.
6
+ # A Symbol representing the command's verb or verbal phrase.
7
+ #
8
+ # @return [Symbol]
8
9
  attr_reader :verb
9
-
10
- # @!attribute [r] arguments
11
- # @return [Array<String>] An Array of arguments to be mapped to an Action's Queries.
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 argument.
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
- if (cls.kind_of?(Class) or cls.kind_of?(Module))
16
- return self.clone.delete_if { |i| i.kind_of?(cls) == false }
17
- elsif cls.kind_of?(Symbol)
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 argument.
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
- if (cls.kind_of?(Class) or cls.kind_of?(Module))
33
- return self.clone.delete_if { |i| i.kind_of?(cls) == true }
34
- elsif cls.kind_of?(Symbol)
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
- end
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
  #
@@ -81,6 +44,9 @@ class Array
81
44
  # animals = ['a dog', 'a cat', 'a mouse']
82
45
  # animals.join_and #=> 'a dog, a cat, and a mouse'
83
46
  #
47
+ # @param sep [String] The separator for all but the last element
48
+ # @param andSep [String] The separator for the last element
49
+ # @param serial [Boolean] Use serial separators (e.g., serial commas)
84
50
  # @return [String]
85
51
  def join_and(sep = ', ', andSep = ' and ', serial = true)
86
52
  if self.length < 3
@@ -97,4 +63,16 @@ class Array
97
63
  def join_or(sep = ', ', orSep = ' or ', serial = true)
98
64
  join_and(sep, orSep, serial)
99
65
  end
66
+
67
+ # private
68
+
69
+ def _keep(arr, cls, bool)
70
+ if (cls.kind_of?(Class) or cls.kind_of?(Module))
71
+ arr.keep_if { |i| i.is_a?(cls) == bool }
72
+ elsif cls.kind_of?(Symbol)
73
+ arr.keep_if { |i| i.send(cls) == bool }
74
+ else
75
+ arr.keep_if {|i| (i == cls) == bool}
76
+ end
77
+ end
100
78
  end
@@ -1,16 +1,25 @@
1
- class String
2
- include Gamefic::Matchable
3
- # Capitalize the first letter without changing the rest of the string.
4
- # (String#capitalize makes the rest of the string lower-case.)
5
- def capitalize_first
6
- "#{self[0,1].upcase}#{self[1,self.length]}"
7
- end
8
- # @return [String]
9
- def cap_first
10
- self.capitalize_first
11
- end
12
- # @return [Array]
13
- def split_words
14
- self.gsub(/ +/, ' ').strip.split
15
- end
16
- end
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
@@ -1,32 +1,39 @@
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 Grammar::Person, Grammar::Plural
10
- include Matchable
5
+ include Keywords
11
6
 
7
+ # Get the name of the object.
8
+ # The name is usually presented without articles (e.g., "object" instead
9
+ # of "an object" or "the object" unless the article is part of a proper
10
+ # name (e.g., "The Ohio State University").
11
+ #
12
12
  # @return [String]
13
13
  attr_reader :name
14
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
+ #
15
18
  # @return [String]
16
19
  attr_reader :synonyms
17
20
 
21
+ # The object's indefinite article (usually "a" or "an").
22
+ #
18
23
  # @return [String]
19
24
  attr_reader :indefinite_article
20
25
 
26
+ # The object's definite article (usually "the").
27
+ #
21
28
  # @return [String]
22
29
  attr_reader :definite_article
23
30
 
24
- # Get a set of Keywords associated with the object.
31
+ # Get a set of keywords associated with the object.
25
32
  # Keywords are typically the words in the object's name plus its synonyms.
26
33
  #
27
- # @return [Keywords]
34
+ # @return [Array<String>]
28
35
  def keywords
29
- @keywords ||= "#{definite_article} #{indefinite_article} #{name} #{synonyms}".downcase.split(Matchable::SPLIT_REGEXP).uniq
36
+ @keywords ||= "#{definite_article} #{indefinite_article} #{name} #{synonyms}".downcase.split(Keywords::SPLIT_REGEXP).uniq
30
37
  end
31
38
 
32
39
  # Get the name of the object with an indefinite article.
@@ -37,31 +44,39 @@ module Gamefic
37
44
  def indefinitely
38
45
  ((proper_named? or indefinite_article == '') ? '' : "#{indefinite_article} ") + name.to_s
39
46
  end
40
-
47
+
41
48
  # Get the name of the object with a definite article.
42
49
  # Note: proper-named objects never append an article, though an article
43
50
  # may be included in its proper name.
51
+ #
52
+ # @return [String]
44
53
  def definitely
45
54
  ((proper_named? or definite_article == '') ? '' : "#{definite_article} ") + name.to_s
46
55
  end
47
-
48
- # Get the definite article for this object.
56
+
57
+ # Get the definite article for this object (usually "the").
49
58
  #
50
59
  # @return [String]
51
60
  def definite_article
52
61
  @definite_article || "the"
53
62
  end
54
-
63
+
64
+ # Set the definite article.
65
+ #
66
+ # @param [String] article
55
67
  def definite_article= article
56
68
  @keywords = nil
57
69
  @definite_article = article
58
70
  end
59
71
 
72
+ # Set the indefinite article.
73
+ #
74
+ # @param [String] article
60
75
  def indefinite_article= article
61
76
  @keywords = nil
62
77
  @indefinite_article = article
63
78
  end
64
-
79
+
65
80
  # Is the object proper-named?
66
81
  # Proper-named objects typically do not add articles to their names when
67
82
  # referenced #definitely or #indefinitely, e.g., "Jane Doe" instead of
@@ -71,7 +86,7 @@ module Gamefic
71
86
  def proper_named?
72
87
  (@proper_named == true)
73
88
  end
74
-
89
+
75
90
  # Set whether the object has a proper name.
76
91
  #
77
92
  # @param bool [Boolean]
@@ -84,7 +99,7 @@ module Gamefic
84
99
  end
85
100
  @proper_named = bool
86
101
  end
87
-
102
+
88
103
  # Set the name of the object.
89
104
  # Setting the name performs some magic to determine how to handle
90
105
  # articles ("an object" and "the object").
@@ -115,21 +130,21 @@ module Gamefic
115
130
  end
116
131
  @name = value
117
132
  end
118
-
133
+
119
134
  # Does the object have a description?
120
135
  #
121
136
  # @return [Boolean]
122
137
  def has_description?
123
138
  (@description.to_s != '')
124
139
  end
125
-
140
+
126
141
  # Get the object's description.
127
142
  #
128
143
  # @return [String]
129
144
  def description
130
145
  @description || (Describable.default_description % { :name => self.definitely, :Name => self.definitely.capitalize_first })
131
146
  end
132
-
147
+
133
148
  # Set the object's description.
134
149
  #
135
150
  # @param text [String]
@@ -140,7 +155,7 @@ module Gamefic
140
155
  @description = nil
141
156
  end
142
157
  end
143
-
158
+
144
159
  def synonyms= text
145
160
  @keywords = nil
146
161
  @synonyms = text
@@ -155,14 +170,14 @@ module Gamefic
155
170
  def self.default_description=(text)
156
171
  @default_description = text
157
172
  end
158
-
173
+
159
174
  # Get the object's default description.
160
175
  #
161
176
  # @return [String]
162
177
  def self.default_description
163
178
  @default_description || "There's nothing special about %{name}."
164
179
  end
165
-
180
+
166
181
  # Get a String representation of the object. By default, this is the
167
182
  # object's name with an indefinite article, e.g., "a person" or "a red
168
183
  # dog."
@@ -0,0 +1,47 @@
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
+ include Gamefic::Serialize
12
+
13
+ # @todo It would be nice if this initialization wasn't necessary.
14
+ def initialize(args = {})
15
+ # super self.class.default_attributes.merge(args)
16
+ self.class.default_attributes.merge(args).each_pair do |k, v|
17
+ public_send "#{k}=", v
18
+ end
19
+ post_initialize
20
+ yield self if block_given?
21
+ end
22
+
23
+ def post_initialize
24
+ # raise NotImplementedError, "#{self.class} must implement post_initialize"
25
+ end
26
+
27
+ class << self
28
+ # Set or update the default values for new instances.
29
+ #
30
+ # @param attrs [Hash] The attributes to be merged into the defaults.
31
+ def set_default attrs = {}
32
+ default_attributes.merge! attrs
33
+ end
34
+
35
+ # A hash of default values for attributes when creating an instance.
36
+ #
37
+ # @return [Hash]
38
+ def default_attributes
39
+ @default_attributes ||= {}
40
+ end
41
+
42
+ def inherited subclass
43
+ subclass.set_default default_attributes
44
+ end
45
+ end
46
+ end
47
+ end
@@ -3,72 +3,48 @@ require "gamefic/describable"
3
3
  require 'gamefic/messaging'
4
4
 
5
5
  module Gamefic
6
-
7
- class Entity
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
+ #
10
+ class Entity < Element
8
11
  include Node
9
- include Describable
10
12
  include Messaging
11
- include Grammar::WordAdapter
12
-
13
- attr_reader :session
14
-
15
- def initialize(args = {})
16
- pre_initialize
17
- args.each { |key, value|
18
- send "#{key}=", value
19
- }
20
- @session = Hash.new
21
- yield self if block_given?
22
- post_initialize
23
- end
24
-
25
- def uid
26
- if @uid == nil
27
- @uid = self.object_id.to_s
28
- end
29
- @uid
30
- end
31
13
 
32
- def pre_initialize
33
- # raise NotImplementedError, "#{self.class} must implement post_initialize"
34
- end
35
-
36
- def post_initialize
37
- # raise NotImplementedError, "#{self.class} must implement post_initialize"
38
- end
39
-
40
- # Execute the entity's on_update blocks.
41
- # This method is typically called by the Engine that manages game execution.
42
- # The base method does nothing. Subclasses can override it.
43
- #
44
- def update
45
- end
46
-
47
14
  # Set the Entity's parent.
48
15
  #
49
- # @param node [Entity] The new parent.
16
+ # @param node [Gamefic::Entity] The new parent.
50
17
  def parent=(node)
51
18
  if node != nil and node.kind_of?(Entity) == false
52
19
  raise "Entity's parent must be an Entity"
53
20
  end
54
21
  super
55
22
  end
56
-
57
- # Get an extended property.
23
+
24
+ # A freeform property dictionary.
25
+ # Authors can use the session hash to assign custom properties to the
26
+ # entity. It can also be referenced directly using [] without the method
27
+ # name, e.g., entity.session[:my_value] or entity[:my_value].
58
28
  #
59
- # @param key [Symbol] The property's name.
29
+ # @return [Hash]
30
+ def session
31
+ @session ||= {}
32
+ end
33
+
34
+ # Get a custom property.
35
+ #
36
+ # @param key [Symbol] The property's name
37
+ # @return The value of the property
60
38
  def [](key)
61
39
  session[key]
62
40
  end
63
-
64
- # Set an extended property.
41
+
42
+ # Set a custom property.
65
43
  #
66
- # @param key [Symbol] The property's name.
67
- # @param value The value to set.
44
+ # @param key [Symbol] The property's name
45
+ # @param value The value to set
68
46
  def []=(key, value)
69
47
  session[key] = value
70
48
  end
71
-
72
49
  end
73
-
74
50
  end