gamefic 0.6.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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