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.
- 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
|