gamefic 0.6.1 → 1.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/bin/gamefic +3 -0
  3. data/lib/gamefic/character.rb +42 -6
  4. data/lib/gamefic/director/parser.rb +25 -25
  5. data/lib/gamefic/engine/tty.rb +14 -2
  6. data/lib/gamefic/engine.rb +8 -7
  7. data/lib/gamefic/grammar/gender.rb +1 -1
  8. data/lib/gamefic/grammar/pronouns.rb +53 -8
  9. data/lib/gamefic/grammar/verbs.rb +1 -0
  10. data/lib/gamefic/grammar/word_adapter.rb +31 -18
  11. data/lib/gamefic/html.rb +27 -13
  12. data/lib/gamefic/plot/article_mount.rb +1 -1
  13. data/lib/gamefic/plot/scene_mount.rb +27 -110
  14. data/lib/gamefic/{snapshots.rb → plot/snapshot.rb} +60 -29
  15. data/lib/gamefic/plot/you_mount.rb +1 -1
  16. data/lib/gamefic/plot.rb +56 -29
  17. data/lib/gamefic/query/base.rb +3 -1
  18. data/lib/gamefic/scene/active.rb +11 -18
  19. data/lib/gamefic/scene/base.rb +21 -0
  20. data/lib/gamefic/scene/conclusion.rb +11 -0
  21. data/lib/gamefic/scene/custom.rb +21 -0
  22. data/lib/gamefic/scene/multiple_choice/input.rb +11 -0
  23. data/lib/gamefic/scene/multiple_choice.rb +73 -0
  24. data/lib/gamefic/scene/passive.rb +17 -0
  25. data/lib/gamefic/scene/pause.rb +24 -0
  26. data/lib/gamefic/scene/question.rb +21 -0
  27. data/lib/gamefic/scene/yes_or_no.rb +30 -0
  28. data/lib/gamefic/scene.rb +10 -120
  29. data/lib/gamefic/script/base.rb +7 -2
  30. data/lib/gamefic/script.rb +4 -0
  31. data/lib/gamefic/shell/command/base.rb +38 -0
  32. data/lib/gamefic/shell/command/play.rb +51 -0
  33. data/lib/gamefic/shell/command.rb +4 -0
  34. data/lib/gamefic/shell/registry.rb +13 -0
  35. data/lib/gamefic/shell.rb +14 -71
  36. data/lib/gamefic/source/file.rb +1 -1
  37. data/lib/gamefic/source.rb +5 -0
  38. data/lib/gamefic/tester.rb +0 -1
  39. data/lib/gamefic/version.rb +1 -1
  40. data/lib/gamefic.rb +1 -6
  41. metadata +69 -61
  42. data/lib/gamefic/scene/concluded.rb +0 -22
  43. data/lib/gamefic/scene/multiplechoice.rb +0 -74
  44. data/lib/gamefic/scene/paused.rb +0 -26
  45. data/lib/gamefic/scene/yesorno.rb +0 -43
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e2e173d0a710f12b4d59089817ab6a060b394566
4
- data.tar.gz: ef6fba6bffb3a7cc02d1d8ac281e6238b0d5e7d5
3
+ metadata.gz: 42ffeca62def8cbb07d70245a06be065a602c28d
4
+ data.tar.gz: 49c299be7bafa2aef310aeef08f84b17631b3ce4
5
5
  SHA512:
6
- metadata.gz: ff108a8adea84779d6d60f7d57ce88010b8c983ff8607fe39a7fa87e0679ecb3ce11b8140b367da8f4814c9fcf23e3763ecdac1fe187d728ad1e065e432d7191
7
- data.tar.gz: 18fd4dcd53803fa7167cc6d589fd85987e4c18fbe160e80859486a36f1221be44f75473562652acb7f4ca4bf6ae98876e61ba48d80c5cbc38a48e2fea62dff35
6
+ metadata.gz: a2b1a5142f260633c4b3d59f2c2cd48896926566554e67fe89f09ad0cdd15f98ff2305a09ca8022af3bbc1ae7d50108a9e6a7ead77a1aa48c2445837e918e8b4
7
+ data.tar.gz: 199e10aa6fd28ed8c4d29dbd17eb6420ff5350fa3689e23ba9f4468af75febf9a115af328e6652365a585c3df70bf2a81cf8b803270cf28ff4810a3346fdead0
data/bin/gamefic CHANGED
@@ -4,4 +4,7 @@ require 'gamefic'
4
4
  require 'gamefic/shell'
5
5
 
6
6
  shell = Gamefic::Shell.new
7
+
8
+ shell.register "play", Gamefic::Shell::Command::Play
9
+
7
10
  shell.execute
@@ -7,7 +7,9 @@ module Gamefic
7
7
  attr_reader :last_order
8
8
  # @return [Entity,nil]
9
9
  attr_reader :last_object
10
- attr_accessor :object_of_pronoun, :scene
10
+ attr_accessor :object_of_pronoun
11
+
12
+ serialize :scene
11
13
 
12
14
  def initialize(plot, args = {})
13
15
  @queue = Array.new
@@ -87,7 +89,7 @@ module Gamefic
87
89
  #
88
90
  # @param message [String]
89
91
  def stream(message)
90
- user.stream.send message
92
+ user.stream.send message if !user.nil?
91
93
  end
92
94
 
93
95
  def destroy
@@ -106,20 +108,54 @@ module Gamefic
106
108
  # actor[:has_eaten] = false # Initial value
107
109
  # end
108
110
  # respond :eat do |actor|
111
+ # actor.tell "You eat something."
109
112
  # actor[:has_eaten] = true
110
113
  # end
111
114
  # respond :eat do |actor|
112
115
  # # This version will be executed first because it was implemented last
113
- # actor.tell "You eat something."
114
- # actor[:has_eaten] # Will be false on the first run
115
- # actor.proceed # Execute the previous implementation
116
- # actor[:has_eaten] #=> true
116
+ # if actor[:has_eaten]
117
+ # actor.tell "You already ate."
118
+ # else
119
+ # actor.proceed # Execute the previous implementation
120
+ # end
117
121
  # end
118
122
  #
119
123
  def proceed
120
124
  return if delegate_stack.last.nil?
121
125
  delegate_stack.last.proceed
122
126
  end
127
+
128
+ def cue scene_name
129
+ @scene = scene_name
130
+ @next_scene = nil
131
+ plot.scenes[scene_name].start self
132
+ end
133
+
134
+ def prepare scene_name
135
+ @next_scene = scene_name
136
+ end
137
+
138
+ def conclude scene_name
139
+ scene = plot.scenes[scene_name]
140
+ raise "#{scene_name} is not a conclusion" unless scene.kind_of?(Scene::Conclusion)
141
+ cue scene_name
142
+ end
143
+
144
+ # Get the name of the character's current scene
145
+ #
146
+ # @return [Symbol] The name of the scene
147
+ def scene
148
+ @scene
149
+ end
150
+
151
+ # Alias for Character#cue key
152
+ def scene= key
153
+ cue key.to_sym
154
+ end
155
+
156
+ def next_scene
157
+ @next_scene
158
+ end
123
159
 
124
160
  private
125
161
  def delegate_stack
@@ -5,33 +5,33 @@ module Gamefic
5
5
 
6
6
  module Parser
7
7
  def self.from_tokens(actor, tokens)
8
- options = []
9
- command = tokens.shift
10
- actions = actor.plot.actions_with_verb(command.to_sym)
11
- actions.each { |action|
12
- if action.queries.length == tokens.length
13
- valid = true
14
- index = 0
15
- action.queries.each { |query|
16
- if query.validate(actor, tokens[index]) == false
17
- valid = false
18
- break
19
- end
20
- index += 1
21
- }
22
- if valid
23
- arguments = []
24
- tokens.each { |token|
25
- arguments.push [token]
26
- }
27
- options.push Order.new(actor, action, arguments)
8
+ options = []
9
+ command = tokens.shift
10
+ actions = actor.plot.actions_with_verb(command.to_sym)
11
+ actions.each { |action|
12
+ if action.queries.length == tokens.length
13
+ valid = true
14
+ index = 0
15
+ action.queries.each { |query|
16
+ if query.validate(actor, tokens[index]) == false
17
+ valid = false
18
+ break
28
19
  end
20
+ index += 1
21
+ }
22
+ if valid
23
+ arguments = []
24
+ tokens.each { |token|
25
+ arguments.push [token]
26
+ }
27
+ options.push Order.new(actor, action, arguments)
29
28
  end
30
- }
31
- if options.length == 0
32
- tokens.unshift command
33
29
  end
34
- options
30
+ }
31
+ if options.length == 0
32
+ tokens.unshift command
33
+ end
34
+ options.sort{ |a,b| b.action.specificity <=> a.action.specificity }
35
35
  end
36
36
  def self.from_string(actor, command)
37
37
  options = []
@@ -45,7 +45,7 @@ module Gamefic
45
45
  options.concat bind_contexts_in_result(actor, match.arguments, action)
46
46
  }
47
47
  }
48
- options
48
+ options.sort{ |a,b| b.action.specificity <=> a.action.specificity }
49
49
  end
50
50
  class << self
51
51
  private
@@ -2,6 +2,7 @@ require 'gamefic/engine'
2
2
  require 'rexml/document'
3
3
  require 'gamefic/ansi'
4
4
  require 'gamefic/html'
5
+ require 'json'
5
6
 
6
7
  begin
7
8
  require 'io/console'
@@ -31,7 +32,12 @@ module Gamefic
31
32
  def ansi
32
33
  @stream.ansi
33
34
  end
34
- def save filename, json
35
+ def save filename, snapshot
36
+ data = {
37
+ :metadata => @character.plot.metadata,
38
+ :entities => snapshot
39
+ }
40
+ json = JSON.generate data
35
41
  if json.nil?
36
42
  @character.tell "Nothing to save."
37
43
  end
@@ -53,11 +59,17 @@ module Gamefic
53
59
  end
54
60
  if filename != ''
55
61
  if File.exists?(filename)
56
- return File.read(filename)
62
+ data = JSON.parse File.read(filename), symbolize_names: true
63
+ if (data[:metadata] != @character.plot.metadata)
64
+ @character.tell "The save file is not compatible with this version of the game."
65
+ else
66
+ return data[:entities]
67
+ end
57
68
  else
58
69
  @character.tell "File \"#{filename}\" not found."
59
70
  end
60
71
  end
72
+ nil
61
73
  end
62
74
  end
63
75
  class UserStream < Gamefic::UserStream
@@ -11,7 +11,7 @@ module Gamefic
11
11
  def run
12
12
  @plot.introduce @user.character
13
13
  print @user.state.output
14
- while @user.character.scene.state != "Concluded"
14
+ while !@plot.concluded?(@user.character)
15
15
  turn
16
16
  end
17
17
  print @user.state.output
@@ -19,12 +19,13 @@ module Gamefic
19
19
  def turn
20
20
  @plot.ready
21
21
  print @user.state.output
22
- # HACK Exception for test scenes
23
- if @user.character.scene.key != :test
24
- if @user.character.scene.state != "Concluded"
25
- @user.stream.select @user.character.scene.data.prompt
26
- @user.state.input
27
- end
22
+ if !@user.character[:test_queue].nil? and @user.character[:test_queue].length > 0
23
+ test_command = @user.character[:test_queue].shift
24
+ @user.character.tell "[TESTING] #{@plot.scenes[@user.character.scene].prompt} #{test_command}"
25
+ @user.character.queue.push test_command
26
+ else
27
+ @user.stream.select @plot.scenes[@user.character.scene].prompt
28
+ @user.state.input
28
29
  end
29
30
  @plot.update
30
31
  print @user.state.output
@@ -5,7 +5,7 @@ module Gamefic::Grammar
5
5
  attr_writer :gender
6
6
  def gender
7
7
  # Supported values are "male", "female", "other", and "neutral"
8
- @gender ||= "neutral"
8
+ @gender ||= (self.kind_of?(Character) ? "other" : "neutral")
9
9
  end
10
10
  end
11
11
  end
@@ -6,24 +6,68 @@ module Gamefic::Grammar
6
6
  def initialize object
7
7
  @object = object
8
8
  end
9
+
10
+ # Get the subjective pronoun (I, you, we, etc.)
11
+ #
12
+ # @return [String]
9
13
  def subj
10
14
  Pronouns.get_pronoun_set(@object)[0]
11
15
  end
16
+
17
+ # Get the objective pronoun (me, them, us, etc.)
18
+ #
19
+ # @return [String]
12
20
  def obj
13
21
  Pronouns.get_pronoun_set(@object)[1]
14
22
  end
23
+
24
+ # Get the possessive pronoun (my, your, our, etc.)
25
+ #
26
+ # @return [String]
15
27
  def poss
16
28
  Pronouns.get_pronoun_set(@object)[2]
17
29
  end
30
+
31
+ # Get the reflexive pronoun (myself, yourself, etc.)
32
+ #
33
+ # @return [String]
34
+ def reflex
35
+ Pronouns.get_pronoun_set(@object)[3]
36
+ end
37
+
38
+ # Get the capitalized subjective pronoun
39
+ #
40
+ # @return [String]
18
41
  def Subj
19
42
  subj.cap_first
20
43
  end
44
+
45
+ # Get the capitalized objective pronoun
46
+ #
47
+ # @return [String]
21
48
  def Obj
22
49
  obj.cap_first
23
50
  end
51
+
52
+ # Get the capitalized possessive pronoun
53
+ #
54
+ # @return [String]
24
55
  def Poss
25
56
  obj.cap_first
26
57
  end
58
+
59
+ # Get the capitalized reflexive pronoun
60
+ #
61
+ # @return [String]
62
+ def Reflex
63
+ reflex.cap_first
64
+ end
65
+
66
+ # Get a an array of pronouns based on the person, gender, and plurality of
67
+ # the provided object.
68
+ #
69
+ # @param obj An object that responds to #person, #gender, and #plural
70
+ # @return [Array<String>]
27
71
  def self.get_pronoun_set(obj)
28
72
  set = Pronouns.sets["#{obj.person}"]
29
73
  if set.nil?
@@ -37,22 +81,23 @@ module Gamefic::Grammar
37
81
  end
38
82
  set
39
83
  end
84
+
40
85
  # TODO Consider implementing method_missing to determine correct pronoun
41
86
  # from example, e.g., "he" would change to "she" for female entities
42
87
  def self.sets
43
88
  if @sets.nil?
44
89
  @sets = {}
45
- @sets["1:singular"] = ["I", "me", "my"]
46
- @sets["2"] = ["you", "you", "your"]
47
- @sets["3:singular:male"] = ["he", "him", "his"]
48
- @sets["3:singular:female"] = ["she", "her", "her"]
90
+ @sets["1:singular"] = ["I", "me", "my", "myself"]
91
+ @sets["2"] = ["you", "you", "your", "yourself"]
92
+ @sets["3:singular:male"] = ["he", "him", "his", "himself"]
93
+ @sets["3:singular:female"] = ["she", "her", "her", "herself"]
49
94
  # "other" refers to a person or living being that is neither
50
95
  # male or female or for whom gender is unspecified. It's
51
96
  # typically used to avoid referring to a person as "it."
52
- @sets["3:singular:other"] = ["they", "them", "their"]
53
- @sets["3:singular:neutral"] = ["it", "it", "its"]
54
- @sets["1:plural"] = ["we", "us", "our"]
55
- @sets["3:plural"] = ["they", "them", "their"]
97
+ @sets["3:singular:other"] = ["they", "them", "their", "themselves"]
98
+ @sets["3:singular:neutral"] = ["it", "it", "its", "itself"]
99
+ @sets["1:plural"] = ["we", "us", "our", "ourselves"]
100
+ @sets["3:plural"] = ["they", "them", "their", "themselves"]
56
101
  end
57
102
  @sets
58
103
  end
@@ -21,5 +21,6 @@ module Gamefic::Grammar
21
21
  end
22
22
  conjugate :be, :present, :am, :are, :is, :are
23
23
  conjugate :have, :present, :have, :have, :has, :have
24
+ conjugate :can, :present, :can, :can, :can
24
25
  end
25
26
  end
@@ -8,29 +8,42 @@ module Gamefic::Grammar
8
8
  include Plural
9
9
  # @return [Gamefic::Grammar::Pronouns]
10
10
  def pronoun
11
- @pronoun ||= Grammar::Pronouns.new(self)
11
+ @pronoun ||= Gamefic::Grammar::Pronouns.new(self)
12
12
  end
13
13
  # @return [Gamefic::Grammar::Verbs]
14
14
  def verb
15
- @verb ||= Grammar::Verbs.new(self)
15
+ @verb ||= Gamefic::Grammar::Verbs.new(self)
16
16
  end
17
17
  def contract words
18
- @contractions ||= {
19
- "I am" => "I'm",
20
- "you are" => "you're",
21
- "he is" => "he's",
22
- "she is" => "she's",
23
- "it is" => "it's",
24
- "we are" => "we're",
25
- "they are" => "they're",
26
- "am not" => "am not",
27
- "are not" => "aren't",
28
- "is not" => "isn't",
29
- "do not" => "don't",
30
- "does not" => "doesn't"
31
- }
32
- src = pronoun.subj + " " + verb.send(words)
33
- @contractions[src] || src
18
+ contractions[words] || words
19
+ end
20
+ private
21
+ def contractions
22
+ if @contractions.nil?
23
+ @contractions ||= {
24
+ "I am" => "I'm",
25
+ "you are" => "you're",
26
+ "he is" => "he's",
27
+ "she is" => "she's",
28
+ "it is" => "it's",
29
+ "we are" => "we're",
30
+ "they are" => "they're",
31
+ "am not" => "am not",
32
+ "are not" => "aren't",
33
+ "is not" => "isn't",
34
+ "do not" => "don't",
35
+ "does not" => "doesn't",
36
+ "can not" => "can't"
37
+ }
38
+ capitalized = {}
39
+ @contractions.each_pair { |k,v |
40
+ if k[0] != k[0].capitalize
41
+ capitalized[k.cap_first] = v.cap_first
42
+ end
43
+ }
44
+ @contractions.merge! capitalized
45
+ end
46
+ @contractions
34
47
  end
35
48
  end
36
49
  end
data/lib/gamefic/html.rb CHANGED
@@ -2,7 +2,11 @@
2
2
 
3
3
  module Gamefic
4
4
 
5
- module Html
5
+ module Html
6
+ # Convert ampersands to &amp;
7
+ #
8
+ # @param text [String]
9
+ # @return [String]
6
10
  def self.fix_ampersands(text)
7
11
  codes = []
8
12
  ENTITIES.keys.each { |e|
@@ -12,34 +16,44 @@ module Gamefic
12
16
  re = Regexp.new("&(?!(#{piped}))")
13
17
  text.gsub(re, '&amp;\1')
14
18
  end
19
+
20
+ # Encode a String with HTML entities
21
+ #
22
+ # @param text [String]
23
+ # @return [String]
15
24
  def self.encode(text)
16
25
  Gamefic::Html::ENTITIES.each { |k, v|
17
- while text.include?(k)
18
- text[v] = k
19
- end
26
+ text = text.gsub(v, k)
20
27
  }
21
28
  text
22
29
  end
30
+
31
+ # Decode a String's HTML entities
32
+ #
33
+ # @param text [String]
34
+ # @return [String]
23
35
  def self.decode(text)
24
36
  Gamefic::Html::ENTITIES.each { |k, v|
25
- while text.include?(k)
26
- text[k] = v
27
- end
37
+ text = text.gsub(k, v)
28
38
  }
29
39
  text
30
40
  end
41
+
42
+ # Parse a String into an XML document
43
+ #
44
+ # @param code [String]
45
+ # @return [REXML::Document]
31
46
  def self.parse(code)
32
47
  code = fix_ampersands(code).strip
33
48
  last = nil
34
49
  begin
35
50
  doc = REXML::Document.new code
36
51
  rescue REXML::ParseException => e
37
- if e.source.buffer != last
38
- if e.source.buffer[0,1] == '<'
39
- code = code[0,(code.length - e.source.buffer.length)] + '&lt;' + e.source.buffer[1..-1]
40
- last = e.source.buffer
41
- retry
42
- end
52
+ # Convert invalid < characters to &lt;
53
+ if e.source.buffer != last and e.source.buffer[0,1] == '<'
54
+ code = code[0,(code.length - e.source.buffer.length)] + '&lt;' + e.source.buffer[1..-1]
55
+ last = e.source.buffer
56
+ retry
43
57
  end
44
58
  raise e
45
59
  end
@@ -1,5 +1,5 @@
1
1
  module Gamefic
2
- module ArticleMount
2
+ module Plot::ArticleMount
3
3
  def a(entity)
4
4
  entity.indefinitely
5
5
  end