gamebox 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +18 -0
- data/Manifest.txt +85 -0
- data/README.txt +33 -0
- data/Rakefile +42 -0
- data/TODO.txt +29 -0
- data/bin/gamebox +49 -0
- data/docs/gamebox04_big.png +0 -0
- data/docs/getting_started.rdoc +99 -0
- data/docs/logo.png +0 -0
- data/lib/gamebox.rb +6 -0
- data/lib/gamebox/actor.rb +143 -0
- data/lib/gamebox/actor_factory.rb +64 -0
- data/lib/gamebox/actor_view.rb +35 -0
- data/lib/gamebox/ai/line_of_site.rb +61 -0
- data/lib/gamebox/ai/polaris.rb +107 -0
- data/lib/gamebox/ai/two_d_grid_location.rb +21 -0
- data/lib/gamebox/ai/two_d_grid_map.rb +77 -0
- data/lib/gamebox/aliasing.rb +16 -0
- data/lib/gamebox/animated.rb +84 -0
- data/lib/gamebox/behavior.rb +16 -0
- data/lib/gamebox/config_manager.rb +22 -0
- data/lib/gamebox/console_app.rb +39 -0
- data/lib/gamebox/data/fonts/Asimov.ttf +0 -0
- data/lib/gamebox/data/fonts/GAMEBOX_FONTS_GO_HERE +0 -0
- data/lib/gamebox/data/graphics/GAMEBOX_GRAPHICS_GO_HERE +0 -0
- data/lib/gamebox/data/graphics/logo.png +0 -0
- data/lib/gamebox/data/music/GAMEBOX_MUSIC_GOES_HERE +0 -0
- data/lib/gamebox/data/sounds/GAMEBOX_SOUND_FX_GO_HERE +0 -0
- data/lib/gamebox/director.rb +47 -0
- data/lib/gamebox/gamebox_application.rb +77 -0
- data/lib/gamebox/graphical.rb +24 -0
- data/lib/gamebox/graphical_actor_view.rb +31 -0
- data/lib/gamebox/inflections.rb +52 -0
- data/lib/gamebox/inflector.rb +278 -0
- data/lib/gamebox/input_manager.rb +104 -0
- data/lib/gamebox/layered.rb +34 -0
- data/lib/gamebox/level.rb +64 -0
- data/lib/gamebox/linked_list.rb +137 -0
- data/lib/gamebox/logo.rb +11 -0
- data/lib/gamebox/metaclass.rb +6 -0
- data/lib/gamebox/mode.rb +123 -0
- data/lib/gamebox/mode_manager.rb +80 -0
- data/lib/gamebox/numbers_ext.rb +3 -0
- data/lib/gamebox/physical.rb +139 -0
- data/lib/gamebox/physical_director.rb +17 -0
- data/lib/gamebox/physical_level.rb +89 -0
- data/lib/gamebox/physics.rb +27 -0
- data/lib/gamebox/publisher_ext.rb +13 -0
- data/lib/gamebox/resource_manager.rb +122 -0
- data/lib/gamebox/score.rb +35 -0
- data/lib/gamebox/sorted_list.rb +59 -0
- data/lib/gamebox/sound_manager.rb +84 -0
- data/lib/gamebox/surface_ext.rb +37 -0
- data/lib/gamebox/svg_actor.rb +55 -0
- data/lib/gamebox/svg_document.rb +160 -0
- data/lib/gamebox/template_app/README +30 -0
- data/lib/gamebox/template_app/Rakefile +20 -0
- data/lib/gamebox/template_app/config/boot.rb +5 -0
- data/lib/gamebox/template_app/config/environment.rb +29 -0
- data/lib/gamebox/template_app/config/game.yml +6 -0
- data/lib/gamebox/template_app/config/mode_level_config.yml +3 -0
- data/lib/gamebox/template_app/config/objects.yml +29 -0
- data/lib/gamebox/template_app/data/fonts/FONTS_GO_HERE +0 -0
- data/lib/gamebox/template_app/data/graphics/GRAPHICS_GO_HERE +0 -0
- data/lib/gamebox/template_app/data/music/MUSIC_GOES_HERE +0 -0
- data/lib/gamebox/template_app/data/sounds/SOUND_FX_GO_HERE +0 -0
- data/lib/gamebox/template_app/doc/README_FOR_APP +1 -0
- data/lib/gamebox/template_app/lib/code_statistics.rb +107 -0
- data/lib/gamebox/template_app/lib/diy.rb +371 -0
- data/lib/gamebox/template_app/lib/platform.rb +16 -0
- data/lib/gamebox/template_app/src/app.rb +8 -0
- data/lib/gamebox/template_app/src/demo_level.rb +20 -0
- data/lib/gamebox/template_app/src/game.rb +22 -0
- data/lib/gamebox/template_app/src/my_actor.rb +17 -0
- data/lib/gamebox/version.rb +10 -0
- data/lib/gamebox/viewport.rb +81 -0
- data/lib/gamebox/wrapped_screen.rb +15 -0
- data/script/perf_polaris.rb +36 -0
- data/test/helper.rb +25 -0
- data/test/test_actor.rb +38 -0
- data/test/test_animated.rb +64 -0
- data/test/test_line_of_site.rb +14 -0
- data/test/test_physical.rb +26 -0
- data/test/test_polaris.rb +193 -0
- data/test/test_viewport.rb +116 -0
- metadata +188 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'actor'
|
2
|
+
require 'graphical_actor_view'
|
3
|
+
class ActorFactory
|
4
|
+
constructor :input_manager, :sound_manager
|
5
|
+
|
6
|
+
attr_accessor :mode_manager, :director
|
7
|
+
|
8
|
+
# returns a hash of actor params
|
9
|
+
def cached_actor_def(actor)
|
10
|
+
@actor_cache ||= {}
|
11
|
+
cached_actor = @actor_cache[actor]
|
12
|
+
return cached_actor if cached_actor
|
13
|
+
|
14
|
+
begin
|
15
|
+
require actor.to_s
|
16
|
+
require actor.to_s+"_view"
|
17
|
+
rescue LoadError => ex
|
18
|
+
# maybe its included somewhere else
|
19
|
+
end
|
20
|
+
model_klass_name = Inflector.camelize actor
|
21
|
+
model_klass = Object.const_get model_klass_name
|
22
|
+
|
23
|
+
begin
|
24
|
+
view_klass = Object.const_get model_klass_name+"View"
|
25
|
+
rescue Exception => ex
|
26
|
+
# hrm...
|
27
|
+
end
|
28
|
+
|
29
|
+
actor_def = {
|
30
|
+
:model_klass => model_klass,
|
31
|
+
:view_klass => view_klass
|
32
|
+
}
|
33
|
+
@actor_cache[actor] = actor_def
|
34
|
+
actor_def
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def build(actor, level, opts={})
|
39
|
+
actor_def = cached_actor_def actor
|
40
|
+
|
41
|
+
basic_opts = {
|
42
|
+
:level => level,
|
43
|
+
:input => @input_manager,
|
44
|
+
:sound => @sound_manager,
|
45
|
+
:resources => level.resource_manager
|
46
|
+
}
|
47
|
+
merged_opts = basic_opts.merge(opts)
|
48
|
+
|
49
|
+
model = actor_def[:model_klass].new merged_opts
|
50
|
+
|
51
|
+
view_klass = opts[:view]
|
52
|
+
view_klass ||= actor_def[:view_klass]
|
53
|
+
|
54
|
+
if model.is? :animated or model.is? :graphical or model.is? :physical
|
55
|
+
view_klass ||= GraphicalActorView
|
56
|
+
end
|
57
|
+
view_klass.new @mode_manager.current_mode, model if view_klass
|
58
|
+
|
59
|
+
# Register our new actor with the system
|
60
|
+
@director.add_actor model
|
61
|
+
|
62
|
+
return model
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class ActorView
|
2
|
+
attr_accessor :actor, :mode, :layer, :parallax
|
3
|
+
def initialize(mode,actor)
|
4
|
+
@mode = mode
|
5
|
+
@actor = actor
|
6
|
+
|
7
|
+
@layer = 0
|
8
|
+
@parallax = 1
|
9
|
+
if @actor.is? :layered
|
10
|
+
@layer = @actor.layer
|
11
|
+
@parallax = @actor.parallax
|
12
|
+
end
|
13
|
+
|
14
|
+
actor.when :remove_me do
|
15
|
+
@mode.unregister_drawable self
|
16
|
+
end
|
17
|
+
|
18
|
+
actor.when :hide_me do
|
19
|
+
@mode.unregister_drawable self
|
20
|
+
end
|
21
|
+
|
22
|
+
actor.when :show_me do
|
23
|
+
@mode.register_drawable self
|
24
|
+
end
|
25
|
+
|
26
|
+
actor.show
|
27
|
+
|
28
|
+
setup
|
29
|
+
end
|
30
|
+
|
31
|
+
def setup
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# LineOfSite is a class for finding neighbors in a map that are visible
|
2
|
+
class LineOfSite
|
3
|
+
def initialize(map)
|
4
|
+
@map = map
|
5
|
+
end
|
6
|
+
|
7
|
+
def losline(x, y, x2, y2)
|
8
|
+
brensenham_line(x, y, x2, y2).each do |i|
|
9
|
+
iterx, itery = *i
|
10
|
+
occ = @map.occupant(loc2(iterx,itery))
|
11
|
+
return unless occ
|
12
|
+
occ.lit = true
|
13
|
+
occ.seen = true
|
14
|
+
|
15
|
+
return if occ.solid?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Brensenham line algorithm
|
20
|
+
def brensenham_line(x,y,x2,y2)
|
21
|
+
steep = false
|
22
|
+
coords = []
|
23
|
+
dx = (x2 - x).abs
|
24
|
+
if (x2 - x) > 0
|
25
|
+
sx = 1
|
26
|
+
else
|
27
|
+
sx = -1
|
28
|
+
end
|
29
|
+
dy = (y2 - y).abs
|
30
|
+
if (y2 - y) > 0
|
31
|
+
sy = 1
|
32
|
+
else
|
33
|
+
sy = -1
|
34
|
+
end
|
35
|
+
if dy > dx
|
36
|
+
steep = true
|
37
|
+
x,y = y,x
|
38
|
+
dx,dy = dy,dx
|
39
|
+
sx,sy = sy,sx
|
40
|
+
end
|
41
|
+
d = (2 * dy) - dx
|
42
|
+
|
43
|
+
dx.times do
|
44
|
+
if steep
|
45
|
+
coords << [y,x]
|
46
|
+
else
|
47
|
+
coords << [x,y]
|
48
|
+
end
|
49
|
+
while d >= 0
|
50
|
+
y = y + sy
|
51
|
+
d = d - (2 * dx)
|
52
|
+
end
|
53
|
+
x = x + sx
|
54
|
+
d = d + (2 * dy)
|
55
|
+
end
|
56
|
+
coords << [x2,y2]
|
57
|
+
|
58
|
+
coords
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'algorithms'
|
2
|
+
include Containers
|
3
|
+
|
4
|
+
# Polaris is a star that guides, aka "The North Star". It implements the A* algorithm.
|
5
|
+
class Polaris
|
6
|
+
attr_reader :nodes_considered
|
7
|
+
|
8
|
+
def initialize(map)
|
9
|
+
@map = map
|
10
|
+
@nodes_considered = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def guide(from, to, unit_type=nil, max_depth=400)
|
14
|
+
return nil if @map.blocked?(from, unit_type) || @map.blocked?(to, unit_type)
|
15
|
+
from_element = PathElement.new(from)
|
16
|
+
from_element.dist_from = @map.distance(from,to)
|
17
|
+
open = PriorityQueue.new { |x, y| (x <=> y) == -1 }
|
18
|
+
open.push from_element, from_element.rating
|
19
|
+
closed = SplayTreeMap.new
|
20
|
+
step = 0
|
21
|
+
|
22
|
+
until open.empty? || step > max_depth
|
23
|
+
step += 1
|
24
|
+
|
25
|
+
current_element = open.pop
|
26
|
+
@nodes_considered += 1
|
27
|
+
|
28
|
+
loc = current_element.location
|
29
|
+
if @map.cost(loc,to) == 0
|
30
|
+
path = []
|
31
|
+
until current_element.parent.nil?
|
32
|
+
path.unshift current_element
|
33
|
+
current_element = current_element.parent
|
34
|
+
end
|
35
|
+
|
36
|
+
return path
|
37
|
+
else
|
38
|
+
closed.push current_element.location, current_element
|
39
|
+
@map.neighbors(loc).each do |next_door|
|
40
|
+
el = PathElement.new(next_door,current_element)
|
41
|
+
next if closed.has_key? next_door
|
42
|
+
|
43
|
+
if @map.blocked? next_door, unit_type
|
44
|
+
#closed.push el.location, el
|
45
|
+
else
|
46
|
+
current_rating = current_element.cost_to + @map.cost(loc, next_door)
|
47
|
+
|
48
|
+
# add to open
|
49
|
+
el.cost_to = current_rating
|
50
|
+
el.dist_from = @map.distance(next_door,to)
|
51
|
+
|
52
|
+
open.push el, el.rating
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class PathElement
|
62
|
+
include Comparable
|
63
|
+
|
64
|
+
attr_accessor :location, :parent
|
65
|
+
attr_reader :cost_to, :dist_from, :rating
|
66
|
+
def initialize(location=nil,parent=nil)
|
67
|
+
@location = location
|
68
|
+
@parent = parent
|
69
|
+
@cost_to = 0
|
70
|
+
@dist_from = 0
|
71
|
+
@rating = 99_999
|
72
|
+
end
|
73
|
+
|
74
|
+
def cost_to=(new_cost)
|
75
|
+
@cost_to = new_cost
|
76
|
+
reset_rating
|
77
|
+
end
|
78
|
+
|
79
|
+
def dist_from=(new_dist_from)
|
80
|
+
@dist_from = new_dist_from
|
81
|
+
reset_rating
|
82
|
+
end
|
83
|
+
|
84
|
+
def reset_rating
|
85
|
+
@rating = @cost_to + @dist_from
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_s
|
89
|
+
"#{@location} at cost of #{@cost_to} and rating of #{@rating}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def <=>(b)
|
93
|
+
a = self
|
94
|
+
if a.rating < b.rating
|
95
|
+
return -1
|
96
|
+
elsif a.rating > b.rating
|
97
|
+
return 1
|
98
|
+
else
|
99
|
+
0
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def ==(other)
|
104
|
+
return false if other.nil?
|
105
|
+
@location == other.location
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# TwoDGridLocation exibits an x,y,cost location
|
2
|
+
class TwoDGridLocation
|
3
|
+
attr_accessor :x,:y
|
4
|
+
def initialize(x,y);@x=x;@y=y;end
|
5
|
+
def ==(other)
|
6
|
+
@x == other.x and @y == other.y
|
7
|
+
end
|
8
|
+
|
9
|
+
def <=>(b)
|
10
|
+
ret = 1
|
11
|
+
if @x == b.x && @y == b.y
|
12
|
+
ret = 0
|
13
|
+
end
|
14
|
+
ret = -1 if @x <= b.x && @y < b.y
|
15
|
+
return ret
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
"#{@x},#{@y}"
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'ai/two_d_grid_location'
|
2
|
+
|
3
|
+
# TwoDGridMap exibits the contract that the map requires.
|
4
|
+
# Works on an X,Y grid that uses Ftors for 2D vectors
|
5
|
+
class TwoDGridMap
|
6
|
+
attr_accessor :w, :h
|
7
|
+
TRAVEL_COST_DIAG = 14
|
8
|
+
TRAVEL_COST_STRAIGHT = 10
|
9
|
+
|
10
|
+
def initialize(w,h)
|
11
|
+
@w = w
|
12
|
+
@h = h
|
13
|
+
@grid = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
# place thing at location. If thing is nil, location will be placed in the map
|
17
|
+
def place(location, thing=nil)
|
18
|
+
thing ||= location
|
19
|
+
@grid[location.x] ||= {}
|
20
|
+
@grid[location.x][location.y] = thing
|
21
|
+
end
|
22
|
+
|
23
|
+
def occupant(location)
|
24
|
+
@grid[location.x][location.y] if @grid[location.x]
|
25
|
+
end
|
26
|
+
|
27
|
+
def clear(location)
|
28
|
+
@grid[location.x] ||= {}
|
29
|
+
@grid[location.x][location.y] = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# is the location available for the specified type
|
33
|
+
def blocked?(location, type=nil)
|
34
|
+
return true if type == :blocked
|
35
|
+
return true if location.x >= @w || location.y >= @h || location.x < 0 || location.y < 0
|
36
|
+
if @grid[location.x] and @grid[location.x][location.y]
|
37
|
+
return true
|
38
|
+
else
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# returns a list of neighbors and their distance
|
44
|
+
def neighbors(location)
|
45
|
+
x = location.x
|
46
|
+
y = location.y
|
47
|
+
[
|
48
|
+
TwoDGridLocation.new(x-1, y-1),
|
49
|
+
TwoDGridLocation.new(x-1, y+1),
|
50
|
+
TwoDGridLocation.new(x+1, y-1),
|
51
|
+
TwoDGridLocation.new(x+1, y+1),
|
52
|
+
TwoDGridLocation.new(x-1, y),
|
53
|
+
TwoDGridLocation.new(x+1, y),
|
54
|
+
TwoDGridLocation.new(x, y-1),
|
55
|
+
TwoDGridLocation.new(x, y+1)
|
56
|
+
]
|
57
|
+
end
|
58
|
+
|
59
|
+
def distance(from,to)
|
60
|
+
h_diagonal = [(from.x-to.x).abs, (from.y-to.y).abs].min
|
61
|
+
h_straight = ((from.x-to.x).abs + (from.y-to.y).abs)
|
62
|
+
return TRAVEL_COST_DIAG * h_diagonal + TRAVEL_COST_STRAIGHT * (h_straight - 2*h_diagonal)
|
63
|
+
end
|
64
|
+
|
65
|
+
# return the cost to go from => to (assuming from and to are neighbors)
|
66
|
+
def cost(from, to)
|
67
|
+
if from.x == to.x or from.y == to.y
|
68
|
+
if from.x == to.x and from.y == to.y
|
69
|
+
0
|
70
|
+
else
|
71
|
+
TRAVEL_COST_STRAIGHT
|
72
|
+
end
|
73
|
+
else
|
74
|
+
TRAVEL_COST_DIAG
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Module
|
2
|
+
# Encapsulates the common pattern of:
|
3
|
+
#
|
4
|
+
# alias_method :foo_without_feature, :foo
|
5
|
+
# alias_method :foo, :foo_with_feature
|
6
|
+
#
|
7
|
+
# With this, you simply do:
|
8
|
+
#
|
9
|
+
# alias_method_chain :foo, :feature
|
10
|
+
#
|
11
|
+
# And both aliases are set up for you.
|
12
|
+
def alias_method_chain(target, feature)
|
13
|
+
alias_method "#{target}_without_#{feature}", target
|
14
|
+
alias_method target, "#{target}_with_#{feature}"
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'behavior'
|
2
|
+
# keeps track of an image for you based on the actor's class, and
|
3
|
+
# current action, and frame num
|
4
|
+
# by default it expects images to be:
|
5
|
+
# data/graphics/classname/action/01..n.png
|
6
|
+
class Animated < Behavior
|
7
|
+
FRAME_UPDATE_TIME = 60
|
8
|
+
|
9
|
+
attr_accessor :frame_time, :frame_num, :animating
|
10
|
+
def setup
|
11
|
+
@images = {}
|
12
|
+
@frame_time = 0
|
13
|
+
|
14
|
+
# all animated actors have to have an idle animation
|
15
|
+
# data/graphics/ship/idle/1.png
|
16
|
+
@frame_num = 0
|
17
|
+
self.action = :idle
|
18
|
+
|
19
|
+
animated_obj = self
|
20
|
+
|
21
|
+
@actor.instance_eval do
|
22
|
+
(class << self; self; end).class_eval do
|
23
|
+
define_method :image do
|
24
|
+
animated_obj.image
|
25
|
+
end
|
26
|
+
define_method :start_animating do
|
27
|
+
animated_obj.start_animating
|
28
|
+
end
|
29
|
+
define_method :stop_animating do
|
30
|
+
animated_obj.stop_animating
|
31
|
+
end
|
32
|
+
define_method :action= do |action_sym|
|
33
|
+
animated_obj.action = action_sym
|
34
|
+
end
|
35
|
+
define_method :animated do
|
36
|
+
animated_obj
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
def update(time)
|
44
|
+
return unless @animating
|
45
|
+
@frame_time += time
|
46
|
+
if @frame_time > FRAME_UPDATE_TIME
|
47
|
+
next_frame
|
48
|
+
@frame_time = @frame_time-FRAME_UPDATE_TIME
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def next_frame()
|
53
|
+
@frame_num = (@frame_num + 1) % @images[@action].size
|
54
|
+
end
|
55
|
+
|
56
|
+
# load all the images for this action
|
57
|
+
def load_action(action)
|
58
|
+
@actor.resource_manager.load_animation_set @actor, action
|
59
|
+
end
|
60
|
+
|
61
|
+
def action=(new_action)
|
62
|
+
@images[new_action] ||= load_action(new_action)
|
63
|
+
if @images[new_action].size > 1
|
64
|
+
start_animating
|
65
|
+
else
|
66
|
+
stop_animating
|
67
|
+
end
|
68
|
+
@frame_num = 0
|
69
|
+
@action = new_action
|
70
|
+
end
|
71
|
+
|
72
|
+
# returns the current image, or nil if no action is defined
|
73
|
+
def image
|
74
|
+
@images[@action][@frame_num]
|
75
|
+
end
|
76
|
+
|
77
|
+
def start_animating
|
78
|
+
@animating = true
|
79
|
+
end
|
80
|
+
|
81
|
+
def stop_animating
|
82
|
+
@animating = false
|
83
|
+
end
|
84
|
+
end
|