gamefic-sdk 1.5.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/html/core/engine.js +16 -61
  3. data/html/sinatra/engine.js +51 -0
  4. data/html/skins/standard/index.html.erb +1 -1
  5. data/html/skins/standard/play.js +20 -4
  6. data/html/skins/standard/style.css +3 -0
  7. data/lib/gamefic-sdk.rb +2 -2
  8. data/lib/gamefic-sdk/debug/plot.rb +1 -1
  9. data/lib/gamefic-sdk/platform.rb +1 -0
  10. data/lib/gamefic-sdk/platform/sinatra.rb +100 -0
  11. data/lib/gamefic-sdk/platform/web.rb +19 -12
  12. data/lib/gamefic-sdk/platform/web/app_config.rb +5 -6
  13. data/lib/gamefic-sdk/platform/web/engine.rb +41 -0
  14. data/lib/gamefic-sdk/platform/web/user.rb +19 -0
  15. data/lib/gamefic-sdk/server.rb +67 -0
  16. data/lib/gamefic-sdk/shell.rb +50 -10
  17. data/lib/gamefic-sdk/shell/init.rb +7 -2
  18. data/lib/gamefic-sdk/version.rb +1 -1
  19. data/scripts/autosuggest.plot.rb +5 -11
  20. data/scripts/clothing/actions/wear.plot.rb +1 -1
  21. data/scripts/clothing/entities/clothing.plot.rb +1 -1
  22. data/scripts/clothing/entities/coat.plot.rb +1 -1
  23. data/scripts/clothing/entities/gloves.plot.rb +1 -1
  24. data/scripts/clothing/entities/hat.plot.rb +1 -1
  25. data/scripts/clothing/entities/pants.plot.rb +1 -1
  26. data/scripts/clothing/entities/shirt.plot.rb +1 -1
  27. data/scripts/clothing/entities/shoes.plot.rb +1 -1
  28. data/scripts/save-restore.plot.rb +1 -0
  29. data/scripts/snapshots.plot.rb +1 -1
  30. data/scripts/standard.plot.rb +1 -1
  31. data/scripts/standard/actions/close.plot.rb +2 -2
  32. data/scripts/standard/actions/drop.plot.rb +6 -2
  33. data/scripts/standard/actions/give.plot.rb +12 -17
  34. data/scripts/standard/actions/go.plot.rb +7 -6
  35. data/scripts/standard/actions/lock.plot.rb +3 -3
  36. data/scripts/standard/actions/look-under.plot.rb +1 -1
  37. data/scripts/standard/actions/look.plot.rb +13 -8
  38. data/scripts/standard/actions/nil.plot.rb +1 -1
  39. data/scripts/standard/actions/place.plot.rb +2 -1
  40. data/scripts/standard/actions/read.plot.rb +1 -1
  41. data/scripts/standard/actions/search.plot.rb +1 -0
  42. data/scripts/standard/actions/take.plot.rb +1 -1
  43. data/scripts/standard/actions/talk.plot.rb +4 -4
  44. data/scripts/standard/actions/unlock.plot.rb +6 -6
  45. data/scripts/standard/actions/use.plot.rb +4 -4
  46. data/scripts/standard/entities/character.plot.rb +0 -2
  47. data/scripts/standard/entities/container.plot.rb +1 -4
  48. data/scripts/standard/entities/door.plot.rb +1 -3
  49. data/scripts/standard/entities/entity.plot.rb +0 -1
  50. data/scripts/standard/entities/exterior.plot.rb +1 -1
  51. data/scripts/standard/entities/fixture.plot.rb +1 -1
  52. data/scripts/standard/entities/item.plot.rb +1 -1
  53. data/scripts/standard/entities/portal.plot.rb +2 -3
  54. data/scripts/standard/entities/receptacle.plot.rb +1 -2
  55. data/scripts/standard/entities/room.plot.rb +4 -6
  56. data/scripts/standard/entities/rubble.plot.rb +1 -1
  57. data/scripts/standard/entities/scenery.plot.rb +1 -1
  58. data/scripts/standard/entities/supporter.plot.rb +2 -2
  59. data/scripts/standard/modules/attachable.plot.rb +1 -1
  60. data/scripts/standard/modules/auto_takes.plot.rb +1 -1
  61. data/scripts/standard/modules/darkenable.plot.rb +1 -1
  62. data/scripts/standard/modules/enterable.plot.rb +1 -1
  63. data/scripts/standard/modules/explicit_exits.plot.rb +1 -1
  64. data/scripts/standard/modules/itemizable.plot.rb +1 -1
  65. data/scripts/standard/modules/lockable.plot.rb +1 -1
  66. data/scripts/standard/modules/openable.plot.rb +4 -1
  67. data/scripts/standard/modules/parent-room.plot.rb +1 -1
  68. data/scripts/standard/modules/portable.plot.rb +1 -1
  69. data/scripts/standard/modules/transparent.plot.rb +1 -1
  70. data/scripts/standard/plural/actions/place.plot.rb +2 -2
  71. data/scripts/standard/queries.plot.rb +21 -4
  72. data/scripts/standard/use.plot.rb +26 -8
  73. data/scripts/suggestible.plot.rb +17 -17
  74. metadata +58 -10
  75. data/lib/gamefic-sdk/platform/web/gamefic_opal.rb +0 -45
  76. data/scripts/standard/queries/from_expression.plot.rb +0 -16
  77. data/scripts/standard/queries/reachable.plot.rb +0 -48
  78. data/scripts/standard/queries/room.plot.rb +0 -17
  79. data/scripts/standard/queries/visible.plot.rb +0 -45
@@ -0,0 +1,41 @@
1
+ # @hack Explicit requires for Opal
2
+ require 'gamefic/plot/playbook'
3
+ require 'gamefic/grammar'
4
+ require 'gamefic/query'
5
+ require 'gamefic/grammar/verb_set'
6
+ require 'gamefic/grammar/conjugator'
7
+
8
+ module Gamefic
9
+ module Engine
10
+ class Web < Gamefic::Engine::Base
11
+ def post_initialize
12
+ self.user_class = Gamefic::User::Web
13
+ end
14
+
15
+ def run
16
+ connect
17
+ @plot.introduce @character
18
+ @plot.ready
19
+ @user.update @character.state
20
+ end
21
+
22
+ def turn
23
+ @plot.ready
24
+ @user.update @character.state
25
+ update unless @character.queue.empty?
26
+ end
27
+
28
+ def receive input
29
+ @character.queue.push input unless input.nil?
30
+ update
31
+ end
32
+
33
+ private
34
+
35
+ def update
36
+ @plot.update
37
+ turn
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,19 @@
1
+ module Gamefic
2
+ module User
3
+ class Web < Gamefic::User::Base
4
+ def update state
5
+ `Gamefic.update(#{state.to_json});`
6
+ end
7
+
8
+ def save filename, data
9
+ data[:metadata] = GameficOpal.static_plot.metadata
10
+ `Gamefic.save(filename, data);`
11
+ end
12
+
13
+ def restore filename
14
+ data = `Gamefic.restore(filename);`
15
+ return data
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,67 @@
1
+ require 'sinatra/base'
2
+ require 'yaml'
3
+
4
+ module Gamefic
5
+ module Sdk
6
+
7
+ class Server < Sinatra::Base
8
+ set :port, 4342
9
+
10
+ get '/' do
11
+ paths = config_path(settings.source_dir).script_paths + [Gamefic::Sdk::GLOBAL_SCRIPT_PATH]
12
+ config = YAML.load(File.read(File.join(settings.source_dir, 'config.yaml')))
13
+ config['name'] = 'sinatra'
14
+ @@plot = Gamefic::Sdk::Debug::Plot.new Source::File.new(*paths)
15
+ @@plot.script 'main'
16
+ @@plot.script 'debug'
17
+ sinatra = Gamefic::Sdk::Platform::Sinatra.new(settings.source_dir, config)
18
+ sinatra.build
19
+ File.read File.join(sinatra.config['target_dir'], 'index.html')
20
+ end
21
+
22
+ post '/start' do
23
+ content_type :json
24
+ @@character = Character.new(name: 'player', synonyms: 'me myself self you yourself', description: 'As good-looking as ever.')
25
+ @@character.connect User::Base.new
26
+ @@plot.introduce @@character
27
+ @@plot.ready
28
+ @@character.state.to_json
29
+ end
30
+
31
+ post '/update' do
32
+ content_type :json
33
+ @@character.queue.push params['command']
34
+ @@plot.update
35
+ @@plot.ready
36
+ @@character.state.merge(input: params['command']).to_json
37
+ end
38
+
39
+ class << self
40
+
41
+ def run!
42
+ start_browser if settings.browser
43
+ super
44
+ end
45
+
46
+ def start_browser
47
+ Thread.new {
48
+ sleep 1 until Server.running?
49
+ `start http://localhost:#{settings.port}`
50
+ }
51
+ end
52
+
53
+ end
54
+
55
+ private
56
+
57
+ def config_path dir
58
+ if File.directory?(dir)
59
+ PlotConfig.new File.join(dir, 'config.yaml')
60
+ else
61
+ PlotConfig.new
62
+ end
63
+ end
64
+ end
65
+
66
+ end
67
+ end
@@ -21,30 +21,70 @@ module Gamefic
21
21
  option :standard, type: :boolean, default: true, desc: 'Include the standard script'
22
22
  option :scripts, type: :array, aliases: [:s, :script], desc: 'Additional scripts'
23
23
  option :webskin, default: 'standard', aliases: [:w], desc: 'Skin to use for the Web platform'
24
+ option :webdir, aliases: [:d], desc: 'HTML directory to copy. This option overrides the webskin.'
24
25
  option :title, type: :string, aliases: [:t], desc: "The game's title"
25
26
  option :author, type: :string, aliases: [:a], desc: "The game's author"
26
27
  def init(directory_name)
27
- Gamefic::Sdk::Shell::Init.new(directory: directory_name,
28
- quiet: options[:quiet], scripts: options[:scripts], webskin: options[:webskin],
29
- title: options[:title], author: options[:author]).run
28
+ Gamefic::Sdk::Shell::Init.new(
29
+ directory: directory_name, quiet: options[:quiet], scripts: options[:scripts],
30
+ webskin: options[:webskin], title: options[:title], author: options[:author],
31
+ webdir: options[:webdir]
32
+ ).run
30
33
  end
31
34
 
32
- desc 'test DIRECTORY_NAME', 'Play the game in DIRECTORY_NAME'
33
- def test(directory_name)
35
+ desc 'test [DIRECTORY_NAME]', 'Play the game in DIRECTORY_NAME'
36
+ def test(directory_name = '.')
34
37
  Gamefic::Sdk::Shell::Test.new(directory: directory_name).run
35
38
  end
36
39
 
37
- desc 'build DIRECTORY_NAME', 'Build the game for specified platforms in DIRECTORY_NAME'
40
+ desc 'server [DIRECTORY_NAME]', 'Run the game in DIRECTORY_NAME in a web server'
41
+ option :browser, type: :boolean, aliases: :b, desc: 'Open a browser when the server starts'
42
+ def server(directory_name = '.')
43
+ Gamefic::Sdk::Server.set :source_dir, directory_name
44
+ Gamefic::Sdk::Server.set :browser, options[:browser]
45
+ pub = File.join(directory_name, 'release', 'sinatra').gsub(/\\/, '/')
46
+ Gamefic::Sdk::Server.set :public_folder, pub
47
+ Gamefic::Sdk::Server.run!
48
+ end
49
+
50
+ desc 'build [DIRECTORY_NAME]', 'Build the game for specified platforms in DIRECTORY_NAME'
38
51
  option :quiet, type: :boolean, aliases: :q, desc: 'Suppress output'
39
- def build(directory_name)
52
+ def build(directory_name = '.')
40
53
  Gamefic::Sdk::Build.release(directory_name, options[:quiet])
41
54
  end
42
55
 
43
- desc 'clean DIRECTORY_NAME', 'Perform cleanup of DIRECTORY_NAME'
44
- def clean(directory_name)
56
+ desc 'clean [DIRECTORY_NAME]', 'Perform cleanup of DIRECTORY_NAME'
57
+ def clean(directory_name = '.')
45
58
  Gamefic::Sdk::Build.clean(directory_name)
46
59
  end
47
-
60
+
61
+ desc 'import-scripts [DIRECTORY_NAME]', 'Copy external scripts to the local scripts directory'
62
+ def import_scripts(directory_name = '.')
63
+ config_yaml = File.join(directory_name, 'config.yaml')
64
+ if File.exist?(config_yaml)
65
+ config_path = PlotConfig.new config_yaml
66
+ else
67
+ config_path = PlotConfig.new
68
+ end
69
+ FileUtils.mkdir_p(File.join(directory_name, 'scripts'))
70
+ paths = config_path.script_paths + [Gamefic::Sdk::GLOBAL_SCRIPT_PATH]
71
+ plot = Gamefic::Sdk::Debug::Plot.new Source::File.new(*paths)
72
+ plot.script 'main'
73
+ plot.imported_scripts.each { |s|
74
+ src = File.absolute_path(s.absolute_path)
75
+ dst = File.absolute_path(File.join(directory_name, 'scripts', "#{s.path}.plot.rb"))
76
+ next if src == dst
77
+ puts "Importing #{s.path}"
78
+ FileUtils.mkdir_p(File.dirname(dst))
79
+ FileUtils.cp_r(src, dst)
80
+ }
81
+ end
82
+
83
+ desc 'reset-package [DIRECTORY_NAME]', 'Reset package.yaml to the default values'
84
+ def reset_config(directory_name = '.')
85
+
86
+ end
87
+
48
88
  desc 'webskins', 'List the available skins for the Web platform'
49
89
  def webskins
50
90
  Dir[File.join(Gamefic::Sdk::HTML_TEMPLATE_PATH, 'skins', '*')].sort.each { |d|
@@ -5,10 +5,11 @@ module Gamefic
5
5
  module Sdk
6
6
  class Shell
7
7
  class Init
8
- def initialize(directory:, standard: true, quiet: false, scripts: [], webskin: 'standard', title: nil, author: nil)
8
+ def initialize(directory:, standard: true, quiet: false, scripts: [], webskin: 'standard', webdir: nil, title: nil, author: nil)
9
9
  @quiet = quiet
10
10
  @directory = directory
11
11
  @html = webskin
12
+ @webdir = webdir
12
13
  @scripts = []
13
14
  @scripts.push('standard') if standard
14
15
  @scripts += scripts if scripts
@@ -86,7 +87,11 @@ module Gamefic
86
87
 
87
88
  def copy_html_skin
88
89
  Dir.mkdir("#{@directory}/html")
89
- FileUtils.cp_r(Dir[Gamefic::Sdk::HTML_TEMPLATE_PATH + '/skins/' + @html + '/*'], "#{@directory}/html")
90
+ if @webdir.nil?
91
+ FileUtils.cp_r(Dir[Gamefic::Sdk::HTML_TEMPLATE_PATH + '/skins/' + @html + '/*'], "#{@directory}/html")
92
+ else
93
+ FileUtils.cp_r(Dir[File.join(File.realpath(@webdir), '*')], "#{@directory}/html")
94
+ end
90
95
  end
91
96
  end
92
97
  end
@@ -1,5 +1,5 @@
1
1
  module Gamefic
2
2
  module Sdk
3
- VERSION = '1.5.0'
3
+ VERSION = '1.6.0'
4
4
  end
5
5
  end
@@ -22,8 +22,11 @@ class Gamefic::Character
22
22
  include Autosuggest
23
23
  end
24
24
 
25
- on_player_update do |actor|
26
- if actor.scene == :active
25
+ # Suggestions get updated (future suggestions become current) in
26
+ # on_player_ready. Autosuggest gathers suggestions in on_ready so it gets
27
+ # done first.
28
+ on_player_ready do |actor|
29
+ if (actor.scene == default_scene and actor.next_scene.nil?) or actor.next_scene == default_scene
27
30
  actor.suggest "look around"
28
31
  actor.suggest "inventory"
29
32
  actor.room.children.that_are(Portal).each { |entity|
@@ -55,15 +58,6 @@ on_player_update do |actor|
55
58
  if (actor.parent != actor.room)
56
59
  actor.suggest "#{actor.parent.leave_verb} #{the actor.parent}"
57
60
  end
58
- actor.children.that_are_not(:attached?).each { |entity|
59
- actor.suggest "drop #{the entity}"
60
- Use.siblings.context_from(actor).that_are(Supporter).each { |supporter|
61
- actor.suggest "put #{the entity} on #{the supporter}"
62
- }
63
- Use.siblings.context_from(actor).that_are(Receptacle).each { |receptacle|
64
- actor.suggest "put #{the entity} in #{the receptacle}"
65
- }
66
- }
67
61
  vicinity = actor.parent.children.that_are_not(Portal)
68
62
  if actor.parent != actor.room
69
63
  vicinity.concat actor.room.children.that_are_not(Portal)
@@ -1,4 +1,4 @@
1
- respond :wear, Query::Reachable.new(Clothing) do |actor, clothing|
1
+ respond :wear, Use.family(Clothing) do |actor, clothing|
2
2
  if actor.auto_takes?(clothing)
3
3
  if clothing.attached?
4
4
  actor.tell "You're already wearing #{the clothing}."
@@ -1,6 +1,6 @@
1
1
  script 'standard/entities/item'
2
2
 
3
- class Gamefic::Clothing < Gamefic::Item
3
+ class Clothing < Item
4
4
  def worn?
5
5
  self.parent.kind_of?(Character) and self.attached?
6
6
  end
@@ -1,5 +1,5 @@
1
1
  script 'clothing/entities/clothing'
2
2
 
3
- class Gamefic::Coat < Gamefic::Clothing
3
+ class Coat < Clothing
4
4
 
5
5
  end
@@ -1,5 +1,5 @@
1
1
  script 'clothing/entities/clothing'
2
2
 
3
- class Gamefic::Gloves < Gamefic::Clothing
3
+ class Gloves < Clothing
4
4
 
5
5
  end
@@ -1,5 +1,5 @@
1
1
  script 'clothing/entities/clothing'
2
2
 
3
- class Gamefic::Hat < Gamefic::Clothing
3
+ class Hat < Clothing
4
4
 
5
5
  end
@@ -1,5 +1,5 @@
1
1
  script 'clothing/entities/clothing'
2
2
 
3
- class Gamefic::Pants < Gamefic::Clothing
3
+ class Pants < Clothing
4
4
 
5
5
  end
@@ -1,5 +1,5 @@
1
1
  script 'clothing/entities/clothing'
2
2
 
3
- class Gamefic::Shirt < Gamefic::Clothing
3
+ class Shirt < Clothing
4
4
 
5
5
  end
@@ -1,5 +1,5 @@
1
1
  script 'clothing/entities/clothing'
2
2
 
3
- class Gamefic::Shoes < Gamefic::Clothing
3
+ class Shoes < Clothing
4
4
 
5
5
  end
@@ -6,6 +6,7 @@ end
6
6
 
7
7
  meta :save, Query::Text.new() do |actor, filename|
8
8
  actor.user.save filename, save
9
+ actor.tell "Game saved."
9
10
  end
10
11
 
11
12
  meta :restore do |actor|
@@ -7,7 +7,7 @@ end
7
7
  last_snapshot = nil
8
8
 
9
9
  on_player_ready do |actor|
10
- if (actor.last_order.nil? or !actor.last_order.action.meta?) and !last_snapshot.nil?
10
+ if (actor.last_action.nil? or !actor.last_action.meta?) and !last_snapshot.nil?
11
11
  Snapshots.history.push last_snapshot
12
12
  end
13
13
  last_snapshot = save
@@ -3,10 +3,10 @@
3
3
  # interactive fiction. Designed to be suitable for parser-based text
4
4
  # adventures, but easily extensible for other formats.
5
5
 
6
+ script 'standard/queries'
6
7
  script 'standard/use'
7
8
  script 'standard/modules'
8
9
  script 'standard/direction'
9
10
  script 'standard/entities'
10
- script 'standard/queries'
11
11
  script 'standard/actions'
12
12
  script 'standard/pathfinder'
@@ -2,11 +2,11 @@ respond :close, Query::Text.new() do |actor, string|
2
2
  actor.tell "#{you.pronoun.Subj} #{you.contract you.verb.do + ' not'} see any \"#{string}\" here."
3
3
  end
4
4
 
5
- respond :close, Query::Reachable.new() do |actor, thing|
5
+ respond :close, Use.reachable() do |actor, thing|
6
6
  actor.tell "#{you.pronoun.Subj} #{you.contract you.verb.can + ' not'} close #{the thing}."
7
7
  end
8
8
 
9
- respond :close, Query::Reachable.new(Gamefic::Openable) do |actor, container|
9
+ respond :close, Use.reachable(Openable) do |actor, container|
10
10
  if !container.open?
11
11
  actor.tell "It's already closed."
12
12
  else
@@ -1,5 +1,9 @@
1
- respond :drop, Query::Visible.new() do |actor, thing|
2
- actor.tell "#{you.contract you.pronoun.Subj + ' ' + you.verb.be} not carrying #{the thing}."
1
+ respond :drop, Use.family() do |actor, thing|
2
+ if thing.parent != actor
3
+ actor.tell "#{you.contract you.pronoun.Subj + ' ' + you.verb.be} not carrying #{the thing}."
4
+ else
5
+ actor.proceed
6
+ end
3
7
  end
4
8
 
5
9
  respond :drop, Query::Children.new() do |actor, thing|
@@ -1,21 +1,8 @@
1
- respond :give, Query::Reachable.new(Character), Query::Reachable.new do |actor, character, gift|
2
- if gift.parent != actor
3
- actor.perform :take, gift
4
- end
5
- if gift.parent == actor
6
- actor.perform :give, character, gift
7
- end
8
- end
9
-
10
- respond :give, Query::Reachable.new, Query::Children.new do |actor, character, gift|
1
+ respond :give, Use.available, Query::Children.new do |actor, character, gift|
11
2
  actor.tell "Nothing happens."
12
3
  end
13
4
 
14
- respond :give, Query::Reachable.new, Query::Reachable.new do |actor, character, gift|
15
- actor.tell "#{you.pronoun.Subj} #{you.contract(you.verb.do + ' not')} have #{the gift}."
16
- end
17
-
18
- respond :give, Query::Reachable.new(Character), Query::Children.new do |actor, character, gift|
5
+ respond :give, Use.available(Character), Use.available do |actor, character, gift|
19
6
  if gift.sticky?
20
7
  actor.tell gift.sticky_message || "#{you.pronoun.Subj} #{you.verb.need} to keep #{the gift} for now."
21
8
  else
@@ -23,8 +10,16 @@ respond :give, Query::Reachable.new(Character), Query::Children.new do |actor, c
23
10
  end
24
11
  end
25
12
 
26
- respond :give, Query::Text.new, Query::Children.new do |actor, character, gift|
13
+ respond :give, Use.available(Character), Use.available do |actor, character, gift|
14
+ if gift.parent == actor
15
+ actor.proceed
16
+ else
17
+ actor.tell "#{you.pronoun.Subj} #{you.contract(you.verb.do + ' not')} have #{the gift}."
18
+ end
19
+ end
20
+
21
+ respond :give, Use.text, Use.available do |actor, character, gift|
27
22
  actor.tell "#{you.pronoun.Subj} #{you.contract(you.verb.do + ' not')} see any \"#{character}\" here."
28
23
  end
29
24
 
30
- xlate "give :gift to :character", "give :character :gift"
25
+ interpret "give :gift to :character", "give :character :gift"