gamefic 1.3.2 → 1.4.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.
data/lib/gamefic/html.rb CHANGED
@@ -1,20 +1,20 @@
1
1
  require 'rexml/document'
2
2
 
3
- module Gamefic
4
-
3
+ module Gamefic
4
+
5
5
  module Html
6
6
  # Convert ampersands to &
7
7
  #
8
8
  # @param text [String]
9
- # @return [String]
10
- def self.fix_ampersands(text)
11
- codes = []
12
- ENTITIES.keys.each { |e|
13
- codes.push e[1..-1]
14
- }
15
- piped = codes.join('|')
16
- re = Regexp.new("&(?!(#{piped}))")
17
- text.gsub(re, '&\1')
9
+ # @return [String]
10
+ def self.fix_ampersands(text)
11
+ codes = []
12
+ ENTITIES.keys.each { |e|
13
+ codes.push e[1..-1]
14
+ }
15
+ piped = codes.join('|')
16
+ re = Regexp.new("&(?!(#{piped}))")
17
+ text.gsub(re, '&\1')
18
18
  end
19
19
 
20
20
  # Encode a String with HTML entities
@@ -22,10 +22,11 @@ module Gamefic
22
22
  # @param text [String]
23
23
  # @return [String]
24
24
  def self.encode(text)
25
+ encoded = text
25
26
  Gamefic::Html::ENTITIES.each { |k, v|
26
- text = text.gsub(v, k)
27
+ encoded = encoded.gsub(v, k)
27
28
  }
28
- text
29
+ encoded
29
30
  end
30
31
 
31
32
  # Decode a String's HTML entities
@@ -58,10 +59,10 @@ module Gamefic
58
59
  raise e
59
60
  end
60
61
  end
61
- end
62
-
63
- module Html
64
- ENTITIES = { "&quot;" => "\"", "&amp;" => "&", "&lt;" => "<", "&gt;" => ">", "&nbsp;" => " ", "&iexcl;" => "¡", "&cent;" => "¢", "&pound;" => "£", "&curren;" => "¤", "&yen;" => "¥", "&brvbar;" => "¦", "&sect;" => "§", "&uml;" => "¨", "&copy;" => "©", "&ordf;" => "ª", "&laquo;" => "«", "&not;" => "¬", "&shy;" => "­", "&reg;" => "®", "&macr;" => "¯", "&deg;" => "°", "&plusmn;" => "±", "&sup2;" => "²", "&sup3;" => "³", "&acute;" => "´", "&micro;" => "µ", "&para;" => "¶", "&middot;" => "·", "&cedil;" => "¸", "&sup1;" => "¹", "&ordm;" => "º", "&raquo;" => "»", "&frac14;" => "¼", "&frac12;" => "½", "&frac34;" => "¾", "&iquest;" => "¿", "&Agrave;" => "À", "&Aacute;" => "Á", "&Acirc;" => "Â", "&Atilde;" => "Ã", "&Auml;" => "Ä", "&Aring;" => "Å", "&AElig;" => "Æ", "&Ccedil;" => "Ç", "&Egrave;" => "È", "&Eacute;" => "É", "&Ecirc;" => "Ê", "&Euml;" => "Ë", "&Igrave;" => "Ì", "&Iacute;" => "Í", "&Icirc;" => "Î", "&Iuml;" => "Ï", "&ETH;" => "Ð", "&Ntilde;" => "Ñ", "&Ograve;" => "Ò", "&Oacute;" => "Ó", "&Ocirc;" => "Ô", "&Otilde;" => "Õ", "&Ouml;" => "Ö", "&times;" => "×", "&Oslash;" => "Ø", "&Ugrave;" => "Ù", "&Uacute;" => "Ú", "&Ucirc;" => "Û", "&Uuml;" => "Ü", "&Yacute;" => "Ý", "&THORN;" => "Þ", "&szlig;" => "ß", "&agrave;" => "à", "&aacute;" => "á", "&acirc;" => "â", "&atilde;" => "ã", "&auml;" => "ä", "&aring;" => "å", "&aelig;" => "æ", "&ccedil;" => "ç", "&egrave;" => "è", "&eacute;" => "é", "&ecirc;" => "ê", "&euml;" => "ë", "&igrave;" => "ì", "&iacute;" => "í", "&icirc;" => "î", "&iuml;" => "ï", "&eth;" => "ð", "&ntilde;" => "ñ", "&ograve;" => "ò", "&oacute;" => "ó", "&ocirc;" => "ô", "&otilde;" => "õ", "&ouml;" => "ö", "&divide;" => "÷", "&oslash;" => "ø", "&ugrave;" => "ù", "&uacute;" => "ú", "&ucirc;" => "û", "&uuml;" => "ü", "&yacute;" => "ý", "&thorn;" => "þ", "&yuml;" => "ÿ", "&OElig;" => "Œ", "&oelig;" => "œ", "&Scaron;" => "Š", "&scaron;" => "š", "&Yuml;" => "Ÿ", "&fnof;" => "ƒ", "&circ;" => "ˆ", "&tilde;" => "˜", "&Alpha;" => "Α", "&Beta;" => "Β", "&Gamma;" => "Γ", "&Delta;" => "Δ", "&Epsilon;" => "Ε", "&Zeta;" => "Ζ", "&Eta;" => "Η", "&Theta;" => "Θ", "&Iota;" => "Ι", "&Kappa;" => "Κ", "&Lambda;" => "Λ", "&Mu;" => "Μ", "&Nu;" => "Ν", "&Xi;" => "Ξ", "&Omicron;" => "Ο", "&Pi;" => "Π", "&Rho;" => "Ρ", "&Sigma;" => "Σ", "&Tau;" => "Τ", "&Upsilon;" => "Υ", "&Phi;" => "Φ", "&Chi;" => "Χ", "&Psi;" => "Ψ", "&Omega;" => "Ω", "&alpha;" => "α", "&beta;" => "β", "&gamma;" => "γ", "&delta;" => "δ", "&epsilon;" => "ε", "&zeta;" => "ζ", "&eta;" => "η", "&theta;" => "θ", "&iota;" => "ι", "&kappa;" => "κ", "&lambda;" => "λ", "&mu;" => "μ", "&nu;" => "ν", "&xi;" => "ξ", "&omicron;" => "ο", "&pi;" => "π", "&rho;" => "ρ", "&sigmaf;" => "ς", "&sigma;" => "σ", "&tau;" => "τ", "&upsilon;" => "υ", "&phi;" => "φ", "&chi;" => "χ", "&psi;" => "ψ", "&omega;" => "ω", "&thetasym;" => "ϑ", "&upsih;" => "ϒ", "&piv;" => "ϖ", "&ensp;" => " ", "&emsp;" => " ", "&thinsp;" => " ", "&zwnj;" => "‌", "&zwj;" => "‍", "&lrm;" => "‎", "&rlm;" => "‏", "&ndash;" => "–", "&mdash;" => "—", "&lsquo;" => "‘", "&rsquo;" => "’", "&sbquo;" => "‚", "&ldquo;" => "“", "&rdquo;" => "”", "&bdquo;" => "„", "&dagger;" => "†", "&Dagger;" => "‡", "&bull;" => "•", "&hellip;" => "…", "&permil;" => "‰", "&prime;" => "′", "&Prime;" => "″", "&lsaquo;" => "‹", "&rsaquo;" => "›", "&oline;" => "‾", "&frasl;" => "⁄", "&euro;" => "€", "&image;" => "ℑ", "&weierp;" => "℘", "&real;" => "ℜ", "&trade;" => "™", "&alefsym;" => "ℵ", "&larr;" => "←", "&uarr;" => "↑", "&rarr;" => "→", "&darr;" => "↓", "&harr;" => "↔", "&crarr;" => "↵", "&lArr;" => "⇐", "&uArr;" => "⇑", "&rArr;" => "⇒", "&dArr;" => "⇓", "&hArr;" => "⇔", "&forall;" => "∀", "&part;" => "∂", "&exist;" => "∃", "&empty;" => "∅", "&nabla;" => "∇", "&isin;" => "∈", "&notin;" => "∉", "&ni;" => "∋", "&prod;" => "∏", "&sum;" => "∑", "&minus;" => "−", "&lowast;" => "∗", "&radic;" => "√", "&prop;" => "∝", "&infin;" => "∞", "&ang;" => "∠", "&and;" => "∧", "&or;" => "∨", "&cap;" => "∩", "&cup;" => "∪", "&int;" => "∫", "&there4;" => "∴", "&sim;" => "∼", "&cong;" => "≅", "&asymp;" => "≈", "&ne;" => "≠", "&equiv;" => "≡", "&le;" => "≤", "&ge;" => "≥", "&sub;" => "⊂", "&sup;" => "⊃", "&nsub;" => "⊄", "&sube;" => "⊆", "&supe;" => "⊇", "&oplus;" => "⊕", "&otimes;" => "⊗", "&perp;" => "⊥", "&sdot;" => "⋅", "&lceil;" => "⌈", "&rceil;" => "⌉", "&lfloor;" => "⌊", "&rfloor;" => "⌋", "&lang;" => "〈", "&rang;" => "〉", "&loz;" => "◊", "&spades;" => "♠", "&clubs;" => "♣", "&hearts;" => "♥", "&diams;" => "♦" }
65
62
  end
66
-
67
- end
63
+
64
+ module Html
65
+ ENTITIES = { "&quot;" => "\"", "&amp;" => "&", "&lt;" => "<", "&gt;" => ">", "&nbsp;" => " ", "&iexcl;" => "¡", "&cent;" => "¢", "&pound;" => "£", "&curren;" => "¤", "&yen;" => "¥", "&brvbar;" => "¦", "&sect;" => "§", "&uml;" => "¨", "&copy;" => "©", "&ordf;" => "ª", "&laquo;" => "«", "&not;" => "¬", "&shy;" => "­", "&reg;" => "®", "&macr;" => "¯", "&deg;" => "°", "&plusmn;" => "±", "&sup2;" => "²", "&sup3;" => "³", "&acute;" => "´", "&micro;" => "µ", "&para;" => "¶", "&middot;" => "·", "&cedil;" => "¸", "&sup1;" => "¹", "&ordm;" => "º", "&raquo;" => "»", "&frac14;" => "¼", "&frac12;" => "½", "&frac34;" => "¾", "&iquest;" => "¿", "&Agrave;" => "À", "&Aacute;" => "Á", "&Acirc;" => "Â", "&Atilde;" => "Ã", "&Auml;" => "Ä", "&Aring;" => "Å", "&AElig;" => "Æ", "&Ccedil;" => "Ç", "&Egrave;" => "È", "&Eacute;" => "É", "&Ecirc;" => "Ê", "&Euml;" => "Ë", "&Igrave;" => "Ì", "&Iacute;" => "Í", "&Icirc;" => "Î", "&Iuml;" => "Ï", "&ETH;" => "Ð", "&Ntilde;" => "Ñ", "&Ograve;" => "Ò", "&Oacute;" => "Ó", "&Ocirc;" => "Ô", "&Otilde;" => "Õ", "&Ouml;" => "Ö", "&times;" => "×", "&Oslash;" => "Ø", "&Ugrave;" => "Ù", "&Uacute;" => "Ú", "&Ucirc;" => "Û", "&Uuml;" => "Ü", "&Yacute;" => "Ý", "&THORN;" => "Þ", "&szlig;" => "ß", "&agrave;" => "à", "&aacute;" => "á", "&acirc;" => "â", "&atilde;" => "ã", "&auml;" => "ä", "&aring;" => "å", "&aelig;" => "æ", "&ccedil;" => "ç", "&egrave;" => "è", "&eacute;" => "é", "&ecirc;" => "ê", "&euml;" => "ë", "&igrave;" => "ì", "&iacute;" => "í", "&icirc;" => "î", "&iuml;" => "ï", "&eth;" => "ð", "&ntilde;" => "ñ", "&ograve;" => "ò", "&oacute;" => "ó", "&ocirc;" => "ô", "&otilde;" => "õ", "&ouml;" => "ö", "&divide;" => "÷", "&oslash;" => "ø", "&ugrave;" => "ù", "&uacute;" => "ú", "&ucirc;" => "û", "&uuml;" => "ü", "&yacute;" => "ý", "&thorn;" => "þ", "&yuml;" => "ÿ", "&OElig;" => "Œ", "&oelig;" => "œ", "&Scaron;" => "Š", "&scaron;" => "š", "&Yuml;" => "Ÿ", "&fnof;" => "ƒ", "&circ;" => "ˆ", "&tilde;" => "˜", "&Alpha;" => "Α", "&Beta;" => "Β", "&Gamma;" => "Γ", "&Delta;" => "Δ", "&Epsilon;" => "Ε", "&Zeta;" => "Ζ", "&Eta;" => "Η", "&Theta;" => "Θ", "&Iota;" => "Ι", "&Kappa;" => "Κ", "&Lambda;" => "Λ", "&Mu;" => "Μ", "&Nu;" => "Ν", "&Xi;" => "Ξ", "&Omicron;" => "Ο", "&Pi;" => "Π", "&Rho;" => "Ρ", "&Sigma;" => "Σ", "&Tau;" => "Τ", "&Upsilon;" => "Υ", "&Phi;" => "Φ", "&Chi;" => "Χ", "&Psi;" => "Ψ", "&Omega;" => "Ω", "&alpha;" => "α", "&beta;" => "β", "&gamma;" => "γ", "&delta;" => "δ", "&epsilon;" => "ε", "&zeta;" => "ζ", "&eta;" => "η", "&theta;" => "θ", "&iota;" => "ι", "&kappa;" => "κ", "&lambda;" => "λ", "&mu;" => "μ", "&nu;" => "ν", "&xi;" => "ξ", "&omicron;" => "ο", "&pi;" => "π", "&rho;" => "ρ", "&sigmaf;" => "ς", "&sigma;" => "σ", "&tau;" => "τ", "&upsilon;" => "υ", "&phi;" => "φ", "&chi;" => "χ", "&psi;" => "ψ", "&omega;" => "ω", "&thetasym;" => "ϑ", "&upsih;" => "ϒ", "&piv;" => "ϖ", "&ensp;" => " ", "&emsp;" => " ", "&thinsp;" => " ", "&zwnj;" => "‌", "&zwj;" => "‍", "&lrm;" => "‎", "&rlm;" => "‏", "&ndash;" => "–", "&mdash;" => "—", "&lsquo;" => "‘", "&rsquo;" => "’", "&sbquo;" => "‚", "&ldquo;" => "“", "&rdquo;" => "”", "&bdquo;" => "„", "&dagger;" => "†", "&Dagger;" => "‡", "&bull;" => "•", "&hellip;" => "…", "&permil;" => "‰", "&prime;" => "′", "&Prime;" => "″", "&lsaquo;" => "‹", "&rsaquo;" => "›", "&oline;" => "‾", "&frasl;" => "⁄", "&euro;" => "€", "&image;" => "ℑ", "&weierp;" => "℘", "&real;" => "ℜ", "&trade;" => "™", "&alefsym;" => "ℵ", "&larr;" => "←", "&uarr;" => "↑", "&rarr;" => "→", "&darr;" => "↓", "&harr;" => "↔", "&crarr;" => "↵", "&lArr;" => "⇐", "&uArr;" => "⇑", "&rArr;" => "⇒", "&dArr;" => "⇓", "&hArr;" => "⇔", "&forall;" => "∀", "&part;" => "∂", "&exist;" => "∃", "&empty;" => "∅", "&nabla;" => "∇", "&isin;" => "∈", "&notin;" => "∉", "&ni;" => "∋", "&prod;" => "∏", "&sum;" => "∑", "&minus;" => "−", "&lowast;" => "∗", "&radic;" => "√", "&prop;" => "∝", "&infin;" => "∞", "&ang;" => "∠", "&and;" => "∧", "&or;" => "∨", "&cap;" => "∩", "&cup;" => "∪", "&int;" => "∫", "&there4;" => "∴", "&sim;" => "∼", "&cong;" => "≅", "&asymp;" => "≈", "&ne;" => "≠", "&equiv;" => "≡", "&le;" => "≤", "&ge;" => "≥", "&sub;" => "⊂", "&sup;" => "⊃", "&nsub;" => "⊄", "&sube;" => "⊆", "&supe;" => "⊇", "&oplus;" => "⊕", "&otimes;" => "⊗", "&perp;" => "⊥", "&sdot;" => "⋅", "&lceil;" => "⌈", "&rceil;" => "⌉", "&lfloor;" => "⌊", "&rfloor;" => "⌋", "&lang;" => "〈", "&rang;" => "〉", "&loz;" => "◊", "&spades;" => "♠", "&clubs;" => "♣", "&hearts;" => "♥", "&diams;" => "♦" }
66
+ end
67
+
68
+ end
@@ -0,0 +1,185 @@
1
+ require 'gamefic/ansi'
2
+ require 'gamefic/html'
3
+ require 'io/console'
4
+
5
+ module Gamefic
6
+
7
+ module HtmlToAnsi
8
+ include Ansi
9
+ include Ansi::Code
10
+
11
+ def html_to_ansi text, wrap: true, width: nil
12
+ return '' if text.strip == ''
13
+ output = ''
14
+ begin
15
+ doc = Html.parse("<body>#{text.strip}</body>")
16
+ format_recursively doc
17
+ texts = REXML::XPath.match(doc, './/text()')
18
+ output = texts.join('').gsub(/&apos;/, "'").gsub(/&quot;/, '"').gsub(/&lt;/, '<').gsub(/&gt;/, '>')
19
+ output += Ansi.graphics_mode(Attribute::NORMAL)
20
+ output = Html.decode(output)
21
+ rescue REXML::ParseException => e
22
+ output = Html.encode(text) + "\n\n"
23
+ #output = data
24
+ end
25
+ output.gsub!(/(\n\n)+/, "\n\n")
26
+ calc_width = size[0] || width
27
+ if calc_width.nil? or !wrap
28
+ output
29
+ else
30
+ terminalize(output, calc_width - 1)
31
+ end
32
+ output
33
+ end
34
+
35
+ private
36
+
37
+ def format_recursively(top, stack = nil)
38
+ ol_index = 1
39
+ stack ||= [Attribute::NORMAL]
40
+ top.elements.each { |element|
41
+ formats = [Attribute::NORMAL]
42
+ classes = element.attribute('class').to_s.split(' ')
43
+ if classes.include?('hint')
44
+ formats.push Foreground::YELLOW
45
+ end
46
+ case element.name
47
+ when 'strong', 'b'
48
+ formats.push Attribute::BOLD
49
+ when 'em', 'i', 'u'
50
+ formats.push Attribute::UNDERSCORE
51
+ when 'a'
52
+ if element.attributes['rel'].to_s == 'gamefic'
53
+ element.attributes['href'] = element.attributes['href'][5..-1]
54
+ formats.push [Attribute::UNDERSCORE, Extra::COMMAND]
55
+ else
56
+ formats.push [Attribute::UNDERSCORE, Extra::HREF]
57
+ end
58
+ when 'li'
59
+ if top.name == 'ol'
60
+ element.text = "#{ol_index}. #{element.text}"
61
+ ol_index += 1
62
+ else
63
+ element.text = "* #{element.text}"
64
+ end
65
+ formats.push [Extra::LINE]
66
+ when 'img'
67
+ formats.push [Extra::IGNORED]
68
+ when 'body', 'p', 'ol', 'ul'
69
+ formats.push Extra::BLOCK
70
+ when 'pre'
71
+ formats.push [Extra::BLOCK, Extra::PRE]
72
+ when 'nav'
73
+ formats.push Extra::BLOCK
74
+ when 'h1', 'h2', 'h3', 'h4', 'h5'
75
+ formats.push [Attribute::BOLD, Extra::BLOCK, Extra::UPPERCASE]
76
+ when 'kbd'
77
+ formats.push [Foreground::GREEN]
78
+ end
79
+ if has_code?(stack, Extra::IGNORED)
80
+ element.parent.delete_element(element)
81
+ end
82
+ stack.push formats
83
+ if has_code?(stack, Extra::UPPERCASE)
84
+ element.texts.each { |text|
85
+ text.value.upcase!
86
+ }
87
+ end
88
+ element.texts.each { |text|
89
+ text.value = "#{Ansi.graphics_mode(*stack)}#{text.value}"
90
+ }
91
+ if has_code?(stack.last, Extra::IMAGE)
92
+ element.text = "#{element.attribute('alt') ? element.attribute('alt') : '[Image]'}"
93
+ end
94
+ format_recursively element, stack
95
+ if has_code?(stack.last, Extra::COMMAND)
96
+ if element.attribute('data-command').to_s != ''
97
+ tmp = stack.pop
98
+ element.add_text "#{Ansi.graphics_mode(*stack)}"
99
+ element.add_text " [#{element.attribute('data-command')}]"
100
+ stack.push tmp
101
+ element.add_text "#{Ansi.graphics_mode(*stack)}"
102
+ end
103
+ end
104
+ if has_code?(stack.last, Extra::BLOCK) and !has_code?(stack.last, Extra::PRE)
105
+ element.texts.first.value.lstrip! unless element.texts.first.nil?
106
+ element.texts.last.value.rstrip! unless element.texts.last.nil?
107
+ element.texts.each { |t|
108
+ t.value = t.value.gsub(/ +/, ' ').strip
109
+ }
110
+ end
111
+ if has_code?(stack.last, Extra::BLOCK)
112
+ element.add_text("\n\n")
113
+ elsif has_code?(stack.last, Extra::LINE)
114
+ if !has_code?(stack[-2], Extra::BLOCK) || element != top.elements.to_a.last
115
+ element.add_text("\n")
116
+ end
117
+ end
118
+ if has_code?(stack.last, Extra::HREF)
119
+ if element.attribute('href').to_s != "#"
120
+ tmp = stack.pop
121
+ element.add_text "#{Ansi.graphics_mode(*stack)}"
122
+ element.add_text(" [#{element.attribute('href')}]")
123
+ stack.push tmp
124
+ element.add_text "#{Ansi.graphics_mode(*stack)}"
125
+ end
126
+ end
127
+ if has_code?(stack.last, Extra::IMAGE)
128
+ element.add_text(" [#{element.attribute('src')}]")
129
+ if !has_code?(stack, Extra::BLOCK)
130
+ element.add_text("\n\n")
131
+ end
132
+ end
133
+ stack.pop
134
+ }
135
+ end
136
+
137
+ def terminalize string, max_length
138
+ i = 0
139
+ output = ''
140
+ line_length = 0
141
+ while i < string.length
142
+ line_length += 1
143
+ char = string[i,1]
144
+ if char == "\e"
145
+ # Right now, graphics modes are the only supported ANSI sequences.
146
+ end_of_seq = string.index("m", i)
147
+ output += string[i..end_of_seq]
148
+ i = end_of_seq + 1
149
+ elsif char == " "
150
+ next_space = string.index(/[\s]/, i + 1)
151
+ if !next_space.nil? and line_length + (next_space - i) > max_length
152
+ output += "\n"
153
+ line_length = 0
154
+ else
155
+ output += char
156
+ end
157
+ i += 1
158
+ else
159
+ if char == "\n"
160
+ line_length = 0
161
+ end
162
+ output += char
163
+ i += 1
164
+ end
165
+ end
166
+ output #output.strip
167
+ end
168
+
169
+ def has_code?(fmt, code)
170
+ if fmt.kind_of?(Array)
171
+ return fmt.flatten.include?(code)
172
+ end
173
+ return fmt == code
174
+ end
175
+
176
+ def size
177
+ begin
178
+ return STDOUT.winsize.reverse
179
+ rescue
180
+ return [nil,nil]
181
+ end
182
+ end
183
+ end
184
+
185
+ end
data/lib/gamefic/plot.rb CHANGED
@@ -67,7 +67,7 @@ module Gamefic
67
67
 
68
68
  # Get an Array of all Actions defined in the Plot.
69
69
  #
70
- # @return Array[Action]
70
+ # @return [Array<Action>]
71
71
  def actions
72
72
  @commands.values.flatten
73
73
  end
@@ -75,7 +75,7 @@ module Gamefic
75
75
  # Get an Array of all Actions associated with the specified verb.
76
76
  #
77
77
  # @param verb [Symbol] The Symbol for the verb (e.g., :go or :look)
78
- # @return Array<Action> The verb's associated Actions
78
+ # @return [Array<Action>] The verb's associated Actions
79
79
  def actions_with_verb(verb)
80
80
  @commands[verb].clone || []
81
81
  end
@@ -202,30 +202,20 @@ module Gamefic
202
202
  # Update the Plot's current turn of gameplay.
203
203
  # This method is typically called by the Engine that manages game execution.
204
204
  def update
205
- # Update the plot.
206
205
  @players.each { |player|
207
- # TODO: This really doesn't belong here. We need a before_update in the plot.
208
- @before_player_update_procs.each { |p|
209
- p.call player
210
- }
211
- this_scene = player.next_scene || player.scene
212
- player.prepare nil
213
- if this_scene != player.scene
214
- player.cue this_scene
215
- player.queue.shift
216
- else
206
+ @before_player_update_procs.each { |p| p.call player }
207
+ this_scene = player.next_scene || player.scene
208
+ player.prepare nil
209
+ if this_scene != player.scene
210
+ player.cue this_scene
211
+ player.queue.shift
212
+ else
217
213
  process_input player
218
214
  end
219
215
  }
220
- @entities.each { |e|
221
- e.update
222
- }
223
- @players.each { |player|
224
- update_player player
225
- }
226
- @update_procs.each { |p|
227
- p.call
228
- }
216
+ @entities.each { |e| e.update }
217
+ @players.each { |player| update_player player }
218
+ @update_procs.each { |p| p.call }
229
219
  end
230
220
 
231
221
  def tell entities, message, refresh = false
@@ -294,13 +284,14 @@ module Gamefic
294
284
  end
295
285
  def rem_entity(entity)
296
286
  @entities.delete(entity)
287
+ @players.delete(entity)
297
288
  end
298
289
  def recursive_update(entity)
299
290
  entity.update
300
291
  entity.children.each { |e|
301
292
  recursive_update e
302
293
  }
303
- end
294
+ end
304
295
  def add_syntax syntax
305
296
  if @commands[syntax.verb] == nil
306
297
  raise "Action \"#{syntax.verb}\" does not exist."
@@ -317,12 +308,10 @@ module Gamefic
317
308
  else
318
309
  b.token_count <=> a.token_count
319
310
  end
320
- }
311
+ }
321
312
  end
322
313
  def add_action(action)
323
- if (@commands[action.verb] == nil)
324
- @commands[action.verb] = Array.new
325
- end
314
+ @commands[action.verb] ||= []
326
315
  @commands[action.verb].unshift action
327
316
  @commands[action.verb].sort! { |a, b|
328
317
  if a.specificity == b.specificity
@@ -333,9 +322,12 @@ module Gamefic
333
322
  b.specificity <=> a.specificity
334
323
  end
335
324
  }
325
+ generate_default_syntax action
326
+ end
327
+ def generate_default_syntax action
336
328
  user_friendly = action.verb.to_s.gsub(/_/, ' ')
337
- args = Array.new
338
- used_names = Array.new
329
+ args = []
330
+ used_names = []
339
331
  action.queries.each { |c|
340
332
  num = 1
341
333
  new_name = ":var"
@@ -5,15 +5,19 @@ module Gamefic
5
5
 
6
6
  module Plot::SceneMount
7
7
  # Create a multiple-choice scene.
8
- # The user will be required to make a valid choice to continue
8
+ # The user will be required to make a valid choice to continue.
9
9
  #
10
10
  # @yieldparam [Character]
11
- # @yieldparam [String]
12
- def multiple_choice key, options, &block
13
- scenes[key] = Scene::MultipleChoice.new(
14
- options: options,
15
- finish: block
16
- )
11
+ # @yieldparam [Scene::Data::MultipleChoice]
12
+ def multiple_choice key, *choices, &block
13
+ scenes[key] = Scene::MultipleChoice.new
14
+ scenes[key].on_start do |actor, data|
15
+ data.options.push *choices
16
+ end
17
+ scenes[key].on_finish do |actor, data|
18
+ block.call actor, data unless block.nil?
19
+ actor.cue :active if actor.scene == key and actor.next_scene.nil?
20
+ end
17
21
  end
18
22
 
19
23
  # Create a yes-or-no scene.
@@ -22,50 +26,113 @@ module Gamefic
22
26
  # @yieldparam [Character]
23
27
  # @yieldparam [String] "yes" or "no"
24
28
  def yes_or_no key, prompt = nil, &block
25
- scenes[key] = Scene::YesOrNo.new(prompt, &block)
29
+ scenes[key] = Scene::YesOrNo.new
30
+ unless prompt.nil?
31
+ scenes[key].on_start do |actor, data|
32
+ data.prompt = prompt
33
+ end
34
+ end
35
+ scenes[key].on_finish &block
26
36
  end
27
37
 
28
- # Create a scene with a prompt.
29
- # This scene will use the provided block to process arbitrary input
30
- # from the user.
31
- #
32
- # @param key [Symbol] A unique name for the scene.
33
- # @param prompt [String] The input prompt to display to the user.
34
- # @yieldparam [Character]
35
- # @yieldparam [String]
36
- def question key, prompt, &block
37
- scenes[key] = Scene::Question.new prompt, &block
38
+ def question key, prompt = 'What is your answer?', &block
39
+ scenes[key] = Scene::Custom.new
40
+ scenes[key].on_start do |actor, data|
41
+ data.prompt = prompt
42
+ end
43
+ scenes[key].on_finish do |actor, data|
44
+ block.call actor, data unless block.nil?
45
+ actor.cue :active if actor.scene == key and actor.next_scene.nil?
46
+ end
38
47
  end
39
-
48
+
40
49
  # Create a scene that pauses the game.
41
- # This scene will execute the specified block and wait for input
42
- # from the user (e.g., pressing Enter) to continue.
50
+ # This scene will execute the specified block and wait for input from the
51
+ # the user (e.g., pressing Enter) to continue.
43
52
  #
44
53
  # @param key [Symbol] A unique name for the scene.
54
+ # @param prompt [String] The text to display when prompting the user to continue.
45
55
  # @yieldparam [Character]
46
- def pause key, &block
47
- scenes[key] = Scene::Pause.new &block
56
+ # @yieldparam [Scene::Data::Base]
57
+ def pause key, prompt = nil, &block
58
+ scenes[key] = Scene::Pause.new
59
+ scenes[key].on_start do |actor, data|
60
+ data.prompt = prompt unless prompt.nil?
61
+ block.call actor, data unless block.nil?
62
+ end
63
+ scenes[key].on_finish do |actor, data|
64
+ actor.cue :active if actor.scene == key and actor.next_scene.nil?
65
+ end
48
66
  end
49
67
 
50
68
  # Create a conclusion.
51
- # The game will end after this scene is complete.
69
+ # The game (or the character's participation in it) will end after this
70
+ # scene is complete.
52
71
  #
53
72
  # @param key [Symbol] A unique name for the scene.
54
73
  # @yieldparam [Character]
74
+ # @yieldparam [Scene::Data::Base]
55
75
  def conclusion key, &block
56
- scenes[key] = Scene::Conclusion.new &block
76
+ scenes[key] = Scene::Conclusion.new
77
+ scenes[key].on_start &block
57
78
  end
58
79
 
59
- # Create a generic scene.
60
- # After the scene is complete, it will automatically start the next
61
- # prepared scene, or the :active scene if none is prepared.
80
+ # Create a passive scene.
81
+ # Passive scenes will cue the active scene if another scene
82
+ # has not been prepared or cued.
62
83
  #
63
84
  # @param [Symbol] A unique name for the scene.
64
85
  # @yieldparam [Character]
65
- def scene key, &block
66
- scenes[key] = Scene::Passive.new &block
86
+ # @yieldparam [Scene::Data::Base]
87
+ def passive key, &block
88
+ scenes[key] = Scene::Custom.new
89
+ scenes[key].on_start do |actor, data|
90
+ block.call actor, data
91
+ actor.cue :active if actor.scene == key and actor.next_scene.nil?
92
+ end
67
93
  end
68
-
94
+
95
+ # Create a custom scene.
96
+ #
97
+ # Custom scenes should always specify the next scene to be cued or
98
+ # prepared. If not, the scene will get repeated on the next turn.
99
+ #
100
+ # This method creates a Scene::Custom by default. You can customize other
101
+ # scene types by specifying the class to create.
102
+ #
103
+ # @example Ask the user for a name
104
+ # scene :ask_for_name do |scene|
105
+ # scene.on_start do |actor, data|
106
+ # data.prompt = "What's your name?"
107
+ # end
108
+ # scene.on_finish do |actor, data|
109
+ # actor.name = data.input
110
+ # actor.tell "Hello, #{actor.name}!"
111
+ # actor.cue :active
112
+ # end
113
+ # end
114
+ #
115
+ # @example Customize the prompt for a MultipleChoice scene
116
+ # scene :ask_for_choice, Scene::MultipleChoice do |scene|
117
+ # scene.on_start do |actor, data|
118
+ # data.options.push 'red', 'green', 'blue'
119
+ # data.prompt = "Which color?"
120
+ # end
121
+ # scene.on_finish do |actor, data|
122
+ # actor.tell "You chose #{data.selection}"
123
+ # actor.cue :active
124
+ # end
125
+ # end
126
+ #
127
+ # @param key [Symbol] A unique name for the scene.
128
+ # @param key [cls] The class of scene to be instantiated.
129
+ # @yieldparam [Scene::Custom] The instantiated scene.
130
+ def scene key, cls = Scene::Custom, &block
131
+ scenes[key] = cls.new
132
+ #block.call scenes[key]
133
+ yield scenes[key] if block_given?
134
+ end
135
+
69
136
  # Choose a new scene based on a list of options.
70
137
  # This is a specialized type of multiple-choice scene that determines
71
138
  # which scene to cue based on a Hash of choices and scene keys.
@@ -85,12 +152,12 @@ module Gamefic
85
152
  # @param key [Symbol] A unique name for the scene.
86
153
  # @param map [Hash] A Hash of options and associated scene keys.
87
154
  def multiple_scene key, map
88
- scenes[key] = Scene::MultipleChoice.new(
89
- options: map.keys,
90
- finish: proc { |actor, input|
91
- actor.cue map[input.choice]
155
+ scenes[key] = Scene::MultipleChoice.new
156
+ scenes[key].on_start do |actor, data|
157
+ map.each { |k, v|
158
+ data.map k, v
92
159
  }
93
- )
160
+ end
94
161
  end
95
162
 
96
163
  end