delve 0.0.7

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 (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +24 -0
  5. data/LICENSE +25 -0
  6. data/README.md +6 -0
  7. data/ROADMAP.md +92 -0
  8. data/Rakefile +25 -0
  9. data/bin/delve +15 -0
  10. data/delve.gemspec +17 -0
  11. data/doc/examples/ascii.rb +34 -0
  12. data/doc/examples/astar.rb +39 -0
  13. data/doc/examples/border.rb +13 -0
  14. data/doc/examples/cellular.rb +15 -0
  15. data/doc/examples/display.rb +27 -0
  16. data/doc/examples/engine.rb +29 -0
  17. data/doc/examples/menu.rb +22 -0
  18. data/doc/examples/noise.rb +37 -0
  19. data/doc/examples/progress.rb +25 -0
  20. data/doc/examples/rogue.rb +15 -0
  21. data/lib/delve.rb +109 -0
  22. data/lib/delve/component/collision.rb +17 -0
  23. data/lib/delve/component/movement.rb +78 -0
  24. data/lib/delve/component/position.rb +30 -0
  25. data/lib/delve/component/symbol.rb +23 -0
  26. data/lib/delve/display/curses_renderer.rb +63 -0
  27. data/lib/delve/display/display.rb +51 -0
  28. data/lib/delve/engine.rb +30 -0
  29. data/lib/delve/entity.rb +23 -0
  30. data/lib/delve/event_queue.rb +48 -0
  31. data/lib/delve/fov/discrete_shadowcasting.rb +78 -0
  32. data/lib/delve/fov/fov.rb +83 -0
  33. data/lib/delve/game.rb +22 -0
  34. data/lib/delve/generator/cellular.rb +111 -0
  35. data/lib/delve/generator/dungeon.rb +19 -0
  36. data/lib/delve/generator/map.rb +30 -0
  37. data/lib/delve/generator/noise.rb +33 -0
  38. data/lib/delve/generator/rogue.rb +403 -0
  39. data/lib/delve/input/curses_input.rb +28 -0
  40. data/lib/delve/input/input.rb +12 -0
  41. data/lib/delve/path/astar.rb +83 -0
  42. data/lib/delve/path/path.rb +70 -0
  43. data/lib/delve/scheduler/action_scheduler.rb +38 -0
  44. data/lib/delve/scheduler/scheduler.rb +37 -0
  45. data/lib/delve/scheduler/simple_scheduler.rb +21 -0
  46. data/lib/delve/screen_manager.rb +41 -0
  47. data/lib/delve/widgets/border.rb +44 -0
  48. data/lib/delve/widgets/key_value.rb +36 -0
  49. data/lib/delve/widgets/menu.rb +84 -0
  50. data/lib/delve/widgets/multi_line.rb +51 -0
  51. data/lib/delve/widgets/progress.rb +37 -0
  52. data/lib/delve/widgets/text.rb +37 -0
  53. data/lib/delve/widgets/viewport.rb +67 -0
  54. data/rot.js.LICENSE +25 -0
  55. data/templates/Gemfile.erb +4 -0
  56. data/templates/README.md.erb +22 -0
  57. data/templates/binfile.erb +21 -0
  58. data/templates/game_screen.rb.erb +51 -0
  59. data/templates/gemspec.erb +27 -0
  60. data/templates/loading_screen.rb.erb +46 -0
  61. data/templates/player_factory.rb.erb +19 -0
  62. data/templates/title_screen.rb.erb +42 -0
  63. data/templates/world.rb.erb +63 -0
  64. data/test/component/collision_test.rb +39 -0
  65. data/test/component/movement_test.rb +155 -0
  66. data/test/component/position_test.rb +43 -0
  67. data/test/component/symbol_test.rb +30 -0
  68. data/test/delve_test.rb +61 -0
  69. data/test/display/curses_renderer_test.rb +60 -0
  70. data/test/display/display_test.rb +86 -0
  71. data/test/engine_test.rb +36 -0
  72. data/test/entity_test.rb +48 -0
  73. data/test/event_queue_test.rb +86 -0
  74. data/test/game_test.rb +47 -0
  75. data/test/generator/dungeon_test.rb +21 -0
  76. data/test/generator/map_test.rb +31 -0
  77. data/test/generator/noise_test.rb +49 -0
  78. data/test/input/curses_input_test.rb +24 -0
  79. data/test/input/input_test.rb +21 -0
  80. data/test/path/astar_test.rb +22 -0
  81. data/test/path/path_test.rb +41 -0
  82. data/test/scheduler/action_scheduler_test.rb +54 -0
  83. data/test/scheduler/scheduler_test.rb +50 -0
  84. data/test/scheduler/simple_scheduler_test.rb +33 -0
  85. data/test/screen_manager_test.rb +92 -0
  86. data/test/widgets/border_test.rb +60 -0
  87. data/test/widgets/key_value_test.rb +68 -0
  88. data/test/widgets/menu_test.rb +103 -0
  89. data/test/widgets/multi_line_test.rb +73 -0
  90. data/test/widgets/progress_test.rb +96 -0
  91. data/test/widgets/text_test.rb +66 -0
  92. data/test/widgets/viewport_test.rb +154 -0
  93. metadata +193 -0
@@ -0,0 +1,23 @@
1
+ class Entity
2
+ def initialize
3
+ @components = Hash.new
4
+ end
5
+
6
+ def has?(component_id)
7
+ @components.keys.include? component_id
8
+ end
9
+
10
+ def add(component)
11
+ raise 'Cannot add the same component more than once' if has?(component.id)
12
+ @components[component.id] = component
13
+ end
14
+
15
+ def get(component_id)
16
+ return nil unless has?(component_id)
17
+ @components[component_id]
18
+ end
19
+
20
+ def act
21
+ get(:actor).act if has?(:actor)
22
+ end
23
+ end
@@ -0,0 +1,48 @@
1
+ class EventQueue
2
+ def initialize
3
+ @time = 0
4
+ @events = Array.new
5
+ end
6
+
7
+ def time
8
+ @time
9
+ end
10
+
11
+ def add(event, time)
12
+ raise 'Unable to add a nil event' unless event
13
+ raise 'Unable to schedule event with no time' unless time
14
+
15
+ i = @events.length
16
+ (0..@events.length - 1).each do |e|
17
+ if @events[e][:time] > time
18
+ i = e
19
+ break
20
+ end
21
+ end
22
+
23
+ @events.insert(i, { :event => event, :time => time })
24
+ end
25
+
26
+ def get
27
+ return nil unless @events.length > 0
28
+
29
+ e = @events.shift
30
+
31
+ if e[:time] > 0
32
+ @time += e[:time]
33
+ @events.each { |x| x[:time] -= e[:time] }
34
+ end
35
+ e[:event]
36
+ end
37
+
38
+ def remove(event)
39
+ index = @events.index { |e| e[:event] == event }
40
+ return false unless index
41
+ @events.delete_at index
42
+ true
43
+ end
44
+
45
+ def clear
46
+ @events = Array.new
47
+ end
48
+ end
@@ -0,0 +1,78 @@
1
+ require 'delve/fov/fov'
2
+
3
+ class DiscreteShadowCasting < FieldOfView
4
+
5
+ def initialize(options=Hash.new, block&)
6
+ super options, block&
7
+ end
8
+
9
+ def compute(x, y, radius)
10
+ raise 'Cannot call compute without a block' unless block_given?
11
+ center = @coords
12
+ map = @map
13
+
14
+ yield x, y, 0, nil
15
+
16
+ return nil unless light_passes? x, y
17
+ #TODO: need to create a light_passes? method on the base class, that calls into the bllock thats passed in. The block should be a class that exposes a light_passes? method
18
+
19
+ data = Arrray.new
20
+
21
+ a, b, cx, cy, blocks = nil
22
+
23
+ (1..radius-1).each do |r|
24
+ neighbours = get_circle x, y, r
25
+ angle = 360 / neighbours.length
26
+
27
+ (0..neighbours.length-1).each do |i|
28
+ cx = neighbours[i][0]
29
+ cy = neighbours[i][1]
30
+ a = angle * (i - 0.5)
31
+ b = a + angle
32
+
33
+ blocks = !light_passes? cx, cy
34
+ yield cx, cy, r, 1 if visible_coords a.floor, b.ceil, blocks, data
35
+
36
+ return nil if data.length == 2 and data[0] == 0 && data[1] == 360
37
+ end
38
+ end
39
+ end
40
+
41
+ private
42
+ def visible_coords(a, b, blocks, data)
43
+ #TODO: How to deal with the arguments.calee section?
44
+
45
+ index = 0
46
+ index += 1 while index < data.length and data[index] < a
47
+
48
+ if index == data.length
49
+ if blocks
50
+ data << a
51
+ data << b
52
+ end
53
+ return true
54
+ end
55
+
56
+ count = 0
57
+
58
+ if index % 2 == 0
59
+ while index < data.length and data[index] < b
60
+ index += 1
61
+ count += 1
62
+ end
63
+
64
+ return false if count == 0
65
+
66
+ if blocks
67
+ if count % 2 == 0
68
+ #TODO Splice needs to be rubified
69
+ data.splice index-count, count, b
70
+ else
71
+ data.splice index-count, count
72
+ end
73
+ end
74
+ end
75
+ return true
76
+ end
77
+
78
+ end
@@ -0,0 +1,83 @@
1
+ class FieldOfView
2
+
3
+ def initialize(options=Hash.new, block&)
4
+ @light_passes = block&
5
+ @options = { :topology => :eight }
6
+ options.each { |k, v| @options[k] = v }
7
+ end
8
+
9
+ def compute(x, y, r, block&)
10
+
11
+ end
12
+
13
+ private
14
+ def get_circle(cx, cy, r)
15
+ result = Array.new
16
+ dirs, count_factor, start_offset = nil
17
+
18
+ if @options[:topology] == :four
19
+ count_factor = 1
20
+ start_offset = [0, 1]
21
+ dirs = [
22
+ directions[:eight][7],
23
+ directions[:eight][1],
24
+ directions[:eight][3],
25
+ directions[:eight][5]
26
+ ]
27
+ elsif @options[:topology] == :six
28
+ dirs = directions[:six]
29
+ count_factor = 1
30
+ start_offset = [-1, 1]
31
+ elsif @options[:topology] == :eight
32
+ dirs = directions[:four]
33
+ count_factor = 2
34
+ start_offset = [-1, 1]
35
+ end
36
+
37
+ x = cx + (start_offset[0] * r)
38
+ y = cy + (start_offset[1] * r)
39
+
40
+ (0..dirs.length-1).each do |i|
41
+ (0..((r*count_factor) - 1)).each do |j|
42
+ result << [x, y]
43
+ x += dirs[i][0]
44
+ y += dirs[i][1]
45
+ end
46
+ end
47
+
48
+ result
49
+ end
50
+
51
+ def directions(v)
52
+ dirs = {
53
+ :four => [
54
+ [ 0, -1],
55
+ [ 1, 0],
56
+ [ 0, 1],
57
+ [-1, 0]
58
+ ],
59
+ :eight => [
60
+ [ 0, -1],
61
+ [ 1, -1],
62
+ [ 1, 0],
63
+ [ 1, 1],
64
+ [ 0, 1],
65
+ [-1, 1],
66
+ [-1, 0],
67
+ [-1, -1]
68
+ ],
69
+ :six => [
70
+ [-1, -1],
71
+ [ 1, -1],
72
+ [ 2, 0],
73
+ [ 1, 1],
74
+ [-1, 1],
75
+ [-2, 0]
76
+ ]
77
+ }
78
+
79
+ dirs[v]
80
+ end
81
+
82
+
83
+ end
@@ -0,0 +1,22 @@
1
+ class Game
2
+ def initialize(display, screen_manager, input)
3
+ raise 'Unable to initialize game when display is nil' unless display
4
+ raise 'Unable to initialize game when screen manager is nil' unless screen_manager
5
+ raise 'Unable to initalize game when input is nil' unless input
6
+ @display = display
7
+ @screen_manager = screen_manager
8
+ @input = input
9
+ end
10
+
11
+ def start
12
+ raise 'Unable to start game when screen_manager is empty' if @screen_manager.empty?
13
+ quit = false
14
+ while !quit
15
+ @screen_manager.render @display
16
+ @display.render
17
+
18
+ quit = @screen_manager.update @input
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,111 @@
1
+ require 'delve/generator/map'
2
+
3
+ class CellularGenerator < Map
4
+
5
+ def initialize(width=nil, height=nil, opts=Hash.new)
6
+ super width, height
7
+ @options = {
8
+ born: [5, 6, 7, 8],
9
+ survive: [4, 5, 6, 7, 8],
10
+ topology: :eight
11
+ }
12
+
13
+ set_options opts
14
+
15
+ @dirs = directions @options[:topology]
16
+ @map = fill 0
17
+ end
18
+
19
+ def randomize(probablility)
20
+ (0..@width-1).each do |i|
21
+ (0..@height-1).each do |j|
22
+ @map[i][j] = rand < probablility ? 1 : 0
23
+ end
24
+ end
25
+ end
26
+
27
+ def set_options(opts)
28
+ opts.keys.each { |key| @options[key] = opts[key] }
29
+ end
30
+
31
+ def set(x, y, value)
32
+ @map[x][y] = value
33
+ end
34
+
35
+ def generate
36
+ new_map = fill 0
37
+ randomize 0.45
38
+ born = @options[:born]
39
+ survive = @options[:survive]
40
+
41
+ (0..@height-1).each do |j|
42
+ width_step = 1
43
+ width_start = 0
44
+ if @options[:topology] == 6
45
+ width_step = 2
46
+ width_start = j%2
47
+ end
48
+
49
+ width_start.step(@width-1, width_step) do |i|
50
+ curr = @map[i][j]
51
+ ncount = get_neighbours i, j
52
+
53
+ if curr > 0 and survive.index(ncount) != nil
54
+ new_map[i][j] = 1
55
+ elsif curr <= 0 and born.index(ncount) != nil
56
+ new_map[i][j] = 1
57
+ end
58
+
59
+ yield i, j, new_map[i][j]
60
+ end
61
+ end
62
+
63
+ @map = new_map
64
+ end
65
+
66
+ private
67
+ def get_neighbours cx, cy
68
+ result = 0
69
+ @dirs.each do |dir|
70
+ x = cx + dir[0]
71
+ y = cy + dir[1]
72
+
73
+ next if x < 0 or x >= (@width - 1) or y < 0 or y >= (@height - 1)
74
+ result += @map[x][y] == 1 ? 1 :0
75
+ end
76
+
77
+ result
78
+ end
79
+
80
+ def directions(v)
81
+ dirs = {
82
+ :four => [
83
+ [ 0, -1],
84
+ [ 1, 0],
85
+ [ 0, 1],
86
+ [-1, 0]
87
+ ],
88
+ :eight => [
89
+ [ 0, -1],
90
+ [ 1, -1],
91
+ [ 1, 0],
92
+ [ 1, 1],
93
+ [ 0, 1],
94
+ [-1, 1],
95
+ [-1, 0],
96
+ [-1, -1]
97
+ ],
98
+ :six => [
99
+ [-1, -1],
100
+ [ 1, -1],
101
+ [ 2, 0],
102
+ [ 1, 1],
103
+ [-1, 1],
104
+ [-2, 0]
105
+ ]
106
+ }
107
+
108
+ dirs[v]
109
+ end
110
+
111
+ end
@@ -0,0 +1,19 @@
1
+ require 'delve/generator/map'
2
+
3
+ class Dungeon < Map
4
+
5
+ def initialize(width=nil, height=nil)
6
+ super width, height
7
+ @rooms = Array.new
8
+ @corridors = Array.new
9
+ end
10
+
11
+ def rooms
12
+ @rooms
13
+ end
14
+
15
+ def corridors
16
+ @corridors
17
+ end
18
+
19
+ end
@@ -0,0 +1,30 @@
1
+ class Map
2
+
3
+ @@default_width = 80
4
+ @@default_height = 24
5
+
6
+ def initialize(width=nil, height=nil)
7
+ @width = width || @@default_width
8
+ @height = height || @@default_height
9
+ end
10
+
11
+ def width
12
+ @width
13
+ end
14
+
15
+ def height
16
+ @height
17
+ end
18
+
19
+ def fill(value)
20
+ map = Array.new
21
+ (0..@width-1).each do |x|
22
+ map << Array.new
23
+ (0..@height-1).each do
24
+ map[x] << value
25
+ end
26
+ end
27
+ map
28
+ end
29
+
30
+ end
@@ -0,0 +1,33 @@
1
+ require 'perlin_noise'
2
+ require 'delve/generator/map'
3
+
4
+ class Noise < Map
5
+
6
+ @@grains = {
7
+ :fine => 0.03,
8
+ :coarse => 0.1
9
+ }
10
+
11
+ def initialize width, height, grain
12
+ raise 'Cannot initialize noise generator when width is less than zero' if width < 0
13
+ raise 'Cannot initialize noise generator when height is less than zero' if height < 0
14
+ raise 'Cannot initialize noise generator when grain is not defined' unless grain
15
+ raise 'Cannot initialize noise generator with unknown grain' unless @@grains.include?(grain)
16
+
17
+ super width, height
18
+
19
+ @grain = @@grains[grain]
20
+ @inverse = 1/@grain
21
+ end
22
+
23
+ def generate
24
+ noise = Perlin::Noise.new 2
25
+
26
+ 0.step((@width-1) * @grain, @grain) do |x|
27
+ 0.step((@height-1) * @grain, @grain).each do |y|
28
+ yield(x*@inverse, y*@inverse, noise[x, y])
29
+ end
30
+ end
31
+ end
32
+
33
+ end