gamebox 0.0.1

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 (86) hide show
  1. data/History.txt +18 -0
  2. data/Manifest.txt +85 -0
  3. data/README.txt +33 -0
  4. data/Rakefile +42 -0
  5. data/TODO.txt +29 -0
  6. data/bin/gamebox +49 -0
  7. data/docs/gamebox04_big.png +0 -0
  8. data/docs/getting_started.rdoc +99 -0
  9. data/docs/logo.png +0 -0
  10. data/lib/gamebox.rb +6 -0
  11. data/lib/gamebox/actor.rb +143 -0
  12. data/lib/gamebox/actor_factory.rb +64 -0
  13. data/lib/gamebox/actor_view.rb +35 -0
  14. data/lib/gamebox/ai/line_of_site.rb +61 -0
  15. data/lib/gamebox/ai/polaris.rb +107 -0
  16. data/lib/gamebox/ai/two_d_grid_location.rb +21 -0
  17. data/lib/gamebox/ai/two_d_grid_map.rb +77 -0
  18. data/lib/gamebox/aliasing.rb +16 -0
  19. data/lib/gamebox/animated.rb +84 -0
  20. data/lib/gamebox/behavior.rb +16 -0
  21. data/lib/gamebox/config_manager.rb +22 -0
  22. data/lib/gamebox/console_app.rb +39 -0
  23. data/lib/gamebox/data/fonts/Asimov.ttf +0 -0
  24. data/lib/gamebox/data/fonts/GAMEBOX_FONTS_GO_HERE +0 -0
  25. data/lib/gamebox/data/graphics/GAMEBOX_GRAPHICS_GO_HERE +0 -0
  26. data/lib/gamebox/data/graphics/logo.png +0 -0
  27. data/lib/gamebox/data/music/GAMEBOX_MUSIC_GOES_HERE +0 -0
  28. data/lib/gamebox/data/sounds/GAMEBOX_SOUND_FX_GO_HERE +0 -0
  29. data/lib/gamebox/director.rb +47 -0
  30. data/lib/gamebox/gamebox_application.rb +77 -0
  31. data/lib/gamebox/graphical.rb +24 -0
  32. data/lib/gamebox/graphical_actor_view.rb +31 -0
  33. data/lib/gamebox/inflections.rb +52 -0
  34. data/lib/gamebox/inflector.rb +278 -0
  35. data/lib/gamebox/input_manager.rb +104 -0
  36. data/lib/gamebox/layered.rb +34 -0
  37. data/lib/gamebox/level.rb +64 -0
  38. data/lib/gamebox/linked_list.rb +137 -0
  39. data/lib/gamebox/logo.rb +11 -0
  40. data/lib/gamebox/metaclass.rb +6 -0
  41. data/lib/gamebox/mode.rb +123 -0
  42. data/lib/gamebox/mode_manager.rb +80 -0
  43. data/lib/gamebox/numbers_ext.rb +3 -0
  44. data/lib/gamebox/physical.rb +139 -0
  45. data/lib/gamebox/physical_director.rb +17 -0
  46. data/lib/gamebox/physical_level.rb +89 -0
  47. data/lib/gamebox/physics.rb +27 -0
  48. data/lib/gamebox/publisher_ext.rb +13 -0
  49. data/lib/gamebox/resource_manager.rb +122 -0
  50. data/lib/gamebox/score.rb +35 -0
  51. data/lib/gamebox/sorted_list.rb +59 -0
  52. data/lib/gamebox/sound_manager.rb +84 -0
  53. data/lib/gamebox/surface_ext.rb +37 -0
  54. data/lib/gamebox/svg_actor.rb +55 -0
  55. data/lib/gamebox/svg_document.rb +160 -0
  56. data/lib/gamebox/template_app/README +30 -0
  57. data/lib/gamebox/template_app/Rakefile +20 -0
  58. data/lib/gamebox/template_app/config/boot.rb +5 -0
  59. data/lib/gamebox/template_app/config/environment.rb +29 -0
  60. data/lib/gamebox/template_app/config/game.yml +6 -0
  61. data/lib/gamebox/template_app/config/mode_level_config.yml +3 -0
  62. data/lib/gamebox/template_app/config/objects.yml +29 -0
  63. data/lib/gamebox/template_app/data/fonts/FONTS_GO_HERE +0 -0
  64. data/lib/gamebox/template_app/data/graphics/GRAPHICS_GO_HERE +0 -0
  65. data/lib/gamebox/template_app/data/music/MUSIC_GOES_HERE +0 -0
  66. data/lib/gamebox/template_app/data/sounds/SOUND_FX_GO_HERE +0 -0
  67. data/lib/gamebox/template_app/doc/README_FOR_APP +1 -0
  68. data/lib/gamebox/template_app/lib/code_statistics.rb +107 -0
  69. data/lib/gamebox/template_app/lib/diy.rb +371 -0
  70. data/lib/gamebox/template_app/lib/platform.rb +16 -0
  71. data/lib/gamebox/template_app/src/app.rb +8 -0
  72. data/lib/gamebox/template_app/src/demo_level.rb +20 -0
  73. data/lib/gamebox/template_app/src/game.rb +22 -0
  74. data/lib/gamebox/template_app/src/my_actor.rb +17 -0
  75. data/lib/gamebox/version.rb +10 -0
  76. data/lib/gamebox/viewport.rb +81 -0
  77. data/lib/gamebox/wrapped_screen.rb +15 -0
  78. data/script/perf_polaris.rb +36 -0
  79. data/test/helper.rb +25 -0
  80. data/test/test_actor.rb +38 -0
  81. data/test/test_animated.rb +64 -0
  82. data/test/test_line_of_site.rb +14 -0
  83. data/test/test_physical.rb +26 -0
  84. data/test/test_polaris.rb +193 -0
  85. data/test/test_viewport.rb +116 -0
  86. metadata +188 -0
@@ -0,0 +1,80 @@
1
+ require 'inflector'
2
+ require 'mode'
3
+ class ModeManager
4
+
5
+ constructor :resource_manager, :actor_factory, :input_manager,
6
+ :sound_manager, :config_manager
7
+
8
+ def setup
9
+ @modes = {}
10
+ @actor_factory.mode_manager = self
11
+ modes = @resource_manager.load_config('mode_level_config')[:modes]
12
+
13
+ @mode_names = []
14
+ for mode_hash in modes
15
+ for mode, levels in mode_hash
16
+ @mode_names << mode
17
+ mode_klass_name = "Mode"
18
+ unless mode == :default
19
+ mode_klass_name = Inflector.camelize mode.to_s+"Mode"
20
+ end
21
+ begin
22
+ require mode.to_s+"_mode"
23
+ rescue LoadError
24
+ # hope it's defined somewhere else
25
+ end
26
+ mode_klass = ObjectSpace.const_get mode_klass_name
27
+ mode_instance = mode_klass.new(@input_manager, @actor_factory, @resource_manager, @sound_manager, @config_manager, levels)
28
+ mode_instance.when :next_mode do
29
+ next_mode
30
+ end
31
+ mode_instance.when :prev_mode do
32
+ prev_mode
33
+ end
34
+ add_mode mode, mode_instance
35
+ end
36
+ end
37
+ end
38
+
39
+ def next_mode
40
+ index = @mode_names.index @mode
41
+ if index == @mode_names.size-1
42
+ puts "last mode, exiting"
43
+ exit
44
+ end
45
+ change_mode_to @mode_names[index+1]
46
+ end
47
+
48
+ def prev_mode
49
+ index = @mode_names.index @mode
50
+ if index == 0
51
+ puts "first mode, exiting"
52
+ exit
53
+ end
54
+ change_mode_to @mode_names[index-1]
55
+ end
56
+
57
+ def add_mode(mode_sym, mode_instance)
58
+ @modes[mode_sym] = mode_instance
59
+ @mode = mode_sym unless @mode
60
+ mode_instance
61
+ end
62
+
63
+ def change_mode_to(mode, *args)
64
+ @modes[@mode].stop unless @modes[@mode].nil?
65
+ @mode = mode
66
+ @modes[@mode].start *args
67
+ end
68
+
69
+ def current_mode
70
+ @modes[@mode]
71
+ end
72
+
73
+ def update(time)
74
+ @modes[@mode].update time unless @modes[@mode].nil?
75
+ end
76
+
77
+ def draw(target)
78
+ @modes[@mode].draw target unless @modes[@mode].nil?
79
+ end
80
+ end
@@ -0,0 +1,3 @@
1
+ class Float
2
+ Infinity = 1.0/0.0
3
+ end
@@ -0,0 +1,139 @@
1
+ require 'physics'
2
+ require 'behavior'
3
+ require 'inflector'
4
+ require 'publisher'
5
+ class Physical < Behavior
6
+ attr_accessor :shapes, :body, :opts, :parts
7
+
8
+ def shape
9
+ @shapes.first if @shapes
10
+ end
11
+
12
+ def setup
13
+ # TODO add defaults?
14
+ @mass = @opts[:mass]
15
+ @mass ||= Float::Infinity
16
+ @parts = {}
17
+ @shapes = []
18
+
19
+ moment_of_inertia = @opts[:moment]
20
+
21
+ case @opts[:shape]
22
+ when :circle
23
+ @radius = @opts[:radius]
24
+
25
+ moment_of_inertia ||= @opts[:fixed] ? Float::Infinity : moment_for_circle(@mass, @radius, 0, ZeroVec2)
26
+ @body = Body.new(@mass, moment_of_inertia)
27
+ @shape = Shape::Circle.new(@body, @radius, ZeroVec2)
28
+
29
+ when :poly
30
+ shape_array = @opts[:verts].collect{|v| vec2(v[0],v[1])}
31
+
32
+ moment_of_inertia ||= @opts[:fixed] ? Float::Infinity : moment_for_poly(@mass, shape_array, ZeroVec2)
33
+ @body = Body.new(@mass, moment_of_inertia)
34
+ @shape = Shape::Poly.new(@body, shape_array, ZeroVec2)
35
+ end
36
+
37
+ collision_type = @opts[:collision_group]
38
+ collision_type ||=
39
+ Inflector.underscore(@actor.class).to_sym
40
+
41
+ @body.a = @opts[:angle] if @opts[:angle]
42
+
43
+ @shape.collision_type = collision_type
44
+ start_x = @opts[:x]
45
+ start_y = @opts[:y]
46
+ start_x ||= @actor.x
47
+ start_y ||= @actor.y
48
+ @shape.body.p = vec2(start_x,start_y)
49
+ @shape.e = 0
50
+ friction = @opts[:friction]
51
+ friction ||= 0.4
52
+ @shape.u = friction
53
+
54
+ @shapes << @shape
55
+
56
+ if @opts[:parts]
57
+ for obj in @opts[:parts]
58
+ for part_name, part_def in obj
59
+ # add another shape here
60
+ part_shape_array = part_def[:verts].collect{|v| vec2(v[0],v[1])}
61
+ part_shape = Shape::Poly.new(@body, part_shape_array, part_def[:offset])
62
+ part_shape.collision_type = part_name.to_sym
63
+ # TODO pass all physics params to parts (ie u and e)
64
+ part_shape.u = friction
65
+ @shapes << part_shape
66
+ end
67
+ end
68
+ end
69
+
70
+
71
+ physical_obj = self
72
+
73
+ if @actor.level.respond_to? :register_physical_object
74
+ if @opts[:fixed]
75
+ @actor.level.register_physical_object physical_obj, true
76
+ else
77
+ @actor.level.register_physical_object physical_obj
78
+ end
79
+ else
80
+ raise "physical actor in a non-physical level!"
81
+ end
82
+
83
+ # write code here to keep physics and x,y of actor in sync
84
+ @actor.instance_eval do
85
+ (class << self; self; end).class_eval do
86
+ define_method :x do
87
+ physical_obj.body.p.x
88
+ end
89
+ define_method :y do
90
+ physical_obj.body.p.y
91
+ end
92
+ define_method :x= do |new_x|
93
+ raise "I am physical, you should apply forces"
94
+ end
95
+ define_method :y= do |new_y|
96
+ raise "I am physical, you should apply forces"
97
+ end
98
+ define_method :shape do
99
+ physical_obj.shape
100
+ end
101
+ define_method :body do
102
+ physical_obj.body
103
+ end
104
+ define_method :parts do
105
+ physical_obj.parts
106
+ end
107
+ define_method :deg do
108
+ # TODO hack!! why do poly's not work the same?
109
+ if physical_obj.opts[:shape] == :poly
110
+ -((physical_obj.body.a-1.57) * 180.0 / Math::PI + 90)
111
+ else
112
+ -((physical_obj.body.a) * 180.0 / Math::PI + 90)
113
+ end
114
+ end
115
+ define_method :warp do |new_p|
116
+ physical_obj.body.p = new_p
117
+ @level.space.rehash_static if physical_obj.opts[:fixed]
118
+ end
119
+ define_method :physical do
120
+ physical_obj
121
+ end
122
+ define_method :image do
123
+ old_image = nil
124
+ rot_deg = deg.round % 360
125
+
126
+ if is? :animated
127
+ old_image = animated.image
128
+ elsif is? :graphical
129
+ old_image = graphical.image
130
+ end
131
+
132
+ if old_image
133
+ old_image.rotozoom(rot_deg,1,true)
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,17 @@
1
+ require 'director'
2
+
3
+ class PhysicalDirector < Director
4
+ def find_physical_obj(shape)
5
+ @actors.select{|a|a.respond_to?(:shape) && a.shape==shape}.first
6
+ end
7
+
8
+ def remove_physical_obj(shape)
9
+ act = find_physical_obj shape
10
+ act.remove_self
11
+ act
12
+ end
13
+
14
+ def actor_removed(act)
15
+ act.level.unregister_physical_object act if act.is? :physical
16
+ end
17
+ end
@@ -0,0 +1,89 @@
1
+ # Levels represent on level of game play. Some games will likely have only one
2
+ # level. Level is responsible for loading its background, props, and directors.
3
+ # PhysicalLevel adds a physics space to the Level
4
+ require 'level'
5
+ require 'physics'
6
+ require 'physical_director'
7
+ class PhysicalLevel < Level
8
+
9
+ attr_accessor :space
10
+
11
+ def initialize(actor_factory, resource_manager, sound_manager, viewport, opts={})
12
+ @actor_factory = actor_factory
13
+ @director = PhysicalDirector.new
14
+ @actor_factory.director = @director
15
+
16
+ @resource_manager = resource_manager
17
+ @sound_manager = sound_manager
18
+ @viewport = viewport
19
+ @opts = opts
20
+
21
+ @space = Space.new
22
+ @space.iterations = 20
23
+ @space.elastic_iterations = 0
24
+
25
+ setup
26
+ end
27
+
28
+ PHYSICS_STEP = 25.0
29
+ def update_physics(time)
30
+ unless @physics_paused
31
+ steps = (time/PHYSICS_STEP).ceil
32
+ # from chipmunk demo
33
+ dt = 1.0/60/steps
34
+ steps.times do
35
+ @space.step dt
36
+ end
37
+ end
38
+ end
39
+
40
+ def pause_physics
41
+ @physics_paused = true
42
+ end
43
+
44
+ def restart_physics
45
+ @physics_paused = false
46
+ end
47
+
48
+ def update(time)
49
+ update_physics time
50
+ super
51
+ end
52
+
53
+ def register_physical_object(obj,static=false)
54
+ if static
55
+ obj.shapes.each do |shape|
56
+ @space.add_static_shape shape
57
+ end
58
+ else
59
+ @space.add_body(obj.body)
60
+
61
+ obj.shapes.each do |shape|
62
+ @space.add_shape shape
63
+ end
64
+ end
65
+ end
66
+
67
+ def register_physical_constraint(constraint)
68
+ @space.add_constraint constraint
69
+ end
70
+
71
+ def unregister_physical_constraint(constraint)
72
+ @space.remove_constraint constraint
73
+ end
74
+
75
+ def unregister_physical_object(obj,static=false)
76
+ if static
77
+ obj.physical.shapes.each do |shape|
78
+ @space.remove_static_shape shape
79
+ end
80
+ else
81
+ @space.remove_body(obj.body)
82
+
83
+ obj.physical.shapes.each do |shape|
84
+ @space.remove_shape shape
85
+ end
86
+ end
87
+ end
88
+
89
+ end
@@ -0,0 +1,27 @@
1
+ require 'chipmunk'
2
+
3
+ require 'numbers_ext'
4
+ include CP
5
+ ZeroVec2 = vec2(0,0)
6
+
7
+ class Space
8
+ alias :add_collision_func_old :add_collision_func
9
+
10
+ # allows for passing arrays of collision types not just single ones
11
+ # add_collision_func([:foo,:bar], [:baz,:yar]) becomes:
12
+ # add_collision_func(:foo, :baz)
13
+ # add_collision_func(:foo, :yar)
14
+ # add_collision_func(:bar, :baz)
15
+ # add_collision_func(:bar, :yar)
16
+ def add_collision_func(first_objs, second_objs, &block)
17
+ firsts = [first_objs].flatten
18
+ seconds = [second_objs].flatten
19
+
20
+ firsts.each do |f|
21
+ seconds.each do |s|
22
+ add_collision_func_old(f,s,&block)
23
+ end
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,13 @@
1
+ module Publisher
2
+ module InstanceMethods
3
+ def unsubscribe_all(listener)
4
+ if @subscriptions
5
+ for event in @subscriptions.keys
6
+ @subscriptions[event].delete_if do |block|
7
+ eval('self',block.binding).equal?(listener)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env ruby
2
+ $: << "#{File.dirname(__FILE__)}/../config"
3
+ require "fileutils"
4
+ require 'inflector'
5
+ require 'svg_document'
6
+
7
+ class ResourceManager
8
+ def initialize
9
+ @loaded_images = {}
10
+ @loaded_fonts = {}
11
+ @loaded_svgs = {}
12
+ end
13
+
14
+ def load_actor_image(actor)
15
+ # use pngs only for now
16
+ actor_name = Inflector.underscore(actor.class)
17
+ return load_image("#{actor_name}.png")
18
+ end
19
+
20
+ def load_animation_set(actor, action)
21
+ # use pngs only for now
22
+ actor_dir = Inflector.underscore(actor.class)
23
+ frames = Dir.glob("#{GFX_PATH}#{actor_dir}/#{action}/*.png")
24
+ action_imgs = []
25
+
26
+ frames = frames.sort_by {|f| File.basename(f).to_i }
27
+
28
+ for frame in frames
29
+ rel_path = frame.slice(GFX_PATH.size,frame.size)
30
+ action_imgs << load_image(rel_path)
31
+ end
32
+ action_imgs
33
+ end
34
+
35
+ def load_image(file_name)
36
+ cached_img = @loaded_images[file_name]
37
+ if cached_img.nil?
38
+ begin
39
+ cached_img = Rubygame::Surface.load(File.expand_path(GFX_PATH + file_name))
40
+ rescue Exception => ex
41
+ #check global gamebox location
42
+ cached_img = Rubygame::Surface.load(File.expand_path(GAMEBOX_GFX_PATH + file_name))
43
+ end
44
+ @loaded_images[file_name] = cached_img
45
+ end
46
+ cached_img
47
+ end
48
+
49
+ def load_music(full_name)
50
+ begin
51
+ sound = Rubygame::Music.load(full_name)
52
+ return sound
53
+ rescue Rubygame::SDLError => ex
54
+ puts "Cannot load music " + full_name + " : " + ex
55
+ end
56
+ end
57
+
58
+ def load_sound(full_name)
59
+ begin
60
+ sound = Rubygame::Sound.load(full_name)
61
+ return sound
62
+ rescue Rubygame::SDLError => ex
63
+ puts "Cannot load sound " + full_name + " : " + ex
64
+ end
65
+ end
66
+
67
+ # loads TTF fonts from the fonts dir and caches them for later
68
+ def load_font(name, size)
69
+ @loaded_fonts[name] ||= {}
70
+ return @loaded_fonts[name][size] if @loaded_fonts[name][size]
71
+ begin
72
+ unless @ttf_loaded
73
+ TTF.setup
74
+ @ttf_loaded = true
75
+ end
76
+ full_name = File.expand_path(FONTS_PATH + name)
77
+ begin
78
+ font = TTF.new(full_name, size)
79
+ @loaded_fonts[name][size] = font
80
+ rescue Exception => ex
81
+ full_name = File.expand_path(GAMEBOX_FONTS_PATH + name)
82
+ font = TTF.new(full_name, size)
83
+ @loaded_fonts[name][size] = font
84
+ end
85
+ return font
86
+ rescue Exception => ex
87
+ puts "Cannot load font #{full_name}:#{ex}"
88
+ end
89
+ return nil
90
+ end
91
+
92
+ # TODO make this path include that app name?
93
+ def load_config(name)
94
+ conf = YAML::load_file(CONFIG_PATH + name + ".yml")
95
+ user_file = "#{ENV['HOME']}/.gamebox/#{name}.yml"
96
+ if File.exist? user_file
97
+ user_conf = YAML::load_file user_file
98
+ conf = conf.merge user_conf
99
+ end
100
+ conf
101
+ end
102
+
103
+ def save_settings(name, settings)
104
+ user_gamebox_dir = "#{ENV['HOME']}/.gamebox"
105
+ FileUtils.mkdir_p user_gamebox_dir
106
+ user_file = "#{ENV['HOME']}/.gamebox/#{name}.yml"
107
+ File.open user_file, "w" do |f|
108
+ f.write settings.to_yaml
109
+ end
110
+ end
111
+
112
+ def load_svg(file_name)
113
+ # TODO create LEVEL_PATH in environment
114
+ cached_svg = @loaded_svgs[file_name]
115
+ if cached_svg.nil?
116
+ cached_svg = SvgDocument.new(File.open(File.expand_path(DATA_PATH + "levels/" + file_name + ".svg")))
117
+ @loaded_svgs[file_name] = cached_svg
118
+ end
119
+ cached_svg
120
+ end
121
+
122
+ end