retrograph_easy 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 MenTaLguY <mental@rydia.net>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,13 @@
1
+ == What is Retrograph? ==
2
+
3
+ Retrograph is a Ruby library which emulates the video
4
+ display unit from a hypothetical late-80s 8-bit game
5
+ console. It is similar in capability to the Sega Master
6
+ System's VDP, with some additional features and direct
7
+ support for text modes.
8
+
9
+ == What is Retrograph/EASY? ==
10
+
11
+ Retrograph/EASY is a simple API for creating games based
12
+ on Retrograph. Currently, it is based atop Ruby/SDL, but
13
+ support for other backends may be possible in the future.
data/Rakefile ADDED
@@ -0,0 +1,64 @@
1
+ require 'rake'
2
+ require 'rake/gempackagetask'
3
+ begin
4
+ require 'spec/rake/spectask'
5
+ have_rspec = true
6
+ rescue LoadError
7
+ $stderr.puts "*** The rspec gem is required to run specs. ***"
8
+ have_rspec = false
9
+ end
10
+ require 'rake/clean'
11
+
12
+ GEM_VERSION = '0.1'
13
+
14
+ task :clobber => [:clean]
15
+
16
+ gemspec = Gem::Specification.new do |gemspec|
17
+ gemspec.name = "retrograph_easy"
18
+ gemspec.version = GEM_VERSION
19
+ gemspec.author = "MenTaLguY <mental@rydia.net>"
20
+ gemspec.summary = "Retrograph/EASY, a simple API for Retrograph games."
21
+ gemspec.description = <<EOS
22
+ Retrograph/EASY is a simple API built atop Retrograph, a software renderer
23
+ which simulates a late-era 8-bit display with capabilities similar to those
24
+ of the Sega Master System.
25
+ EOS
26
+ gemspec.homepage = "http://rubyforge.org/projects/retrograph"
27
+ gemspec.email = "mental@rydia.net"
28
+ gemspec.rubyforge_project = 'retrograph'
29
+ gemspec.files = FileList['Rakefile', 'README', 'COPYING', 'lib/**/*.rb',
30
+ 'spec/**/*_spec.rb', 'spec/**/*.case',
31
+ 'spec/**/*.png', 'demos/**/*.rb']
32
+ gemspec.require_paths = ['lib']
33
+ gemspec.platform = Gem::Platform::RUBY
34
+ gemspec.add_dependency("retrograph", ">= 0.5")
35
+ end
36
+
37
+ Rake::GemPackageTask.new(gemspec) do |pkg|
38
+ pkg.need_tar = true
39
+ end
40
+
41
+ def make_demo_task(taskname, filename)
42
+ namespace :demo do
43
+ desc "Run the #{taskname} demo"
44
+ task taskname.intern do
45
+ system 'ruby', '-Ilib', filename
46
+ end
47
+ end
48
+ end
49
+
50
+ for filename in Dir.glob('demos/*.rb')
51
+ basename = File.basename(filename)
52
+ extension = File.extname(basename)
53
+ taskname = basename[0...(-extension.size)]
54
+ make_demo_task(taskname, filename)
55
+ end
56
+
57
+ if have_rspec
58
+ Spec::Rake::SpecTask.new do |task|
59
+ task.ruby_opts << '-rrubygems'
60
+ task.libs << 'lib'
61
+ task.spec_files = FileList["spec/**/*_spec.rb"]
62
+ end
63
+ task :default => [:clean, :spec]
64
+ end
data/demos/defense.rb ADDED
@@ -0,0 +1,413 @@
1
+ # Earth Defense
2
+ #
3
+ # Copyright (c) 2009 MenTaLguY <mental@rydia.net>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+ #
23
+ require 'rubygems'
24
+ require 'retrograph/easy'
25
+
26
+ SHIP_PATTERN = [
27
+ 0x00022000,
28
+ 0x00222200,
29
+ 0x02200220,
30
+ 0x22200222,
31
+ 0x22222222,
32
+ 0x22033022,
33
+ 0x20033002,
34
+ 0x00033000
35
+ ].pack("V8")
36
+
37
+ SHOT_PATTERN = [
38
+ 0x00000000,
39
+ 0x00044000,
40
+ 0x00044000,
41
+ 0x00044000,
42
+ 0x00044000,
43
+ 0x00044000,
44
+ 0x00044000,
45
+ 0x00000000
46
+ ].pack("V8")
47
+
48
+ ENEMY_PATTERN = [
49
+ 0x00000000,
50
+ 0x00000000,
51
+ 0x11000011,
52
+ 0x01100110,
53
+ 0x00111100,
54
+ 0x11100111,
55
+ 0x01111110,
56
+ 0x00100100
57
+ ].pack("V8")
58
+
59
+ ENEMY_PATTERN2 = [
60
+ 0x00000000,
61
+ 0x01000010,
62
+ 0x01100110,
63
+ 0x01100110,
64
+ 0x00111100,
65
+ 0x11100111,
66
+ 0x01111110,
67
+ 0x01000010
68
+ ].pack("V8")
69
+
70
+ EXPLOSION_PATTERN = [
71
+ 0x00030000,
72
+ 0x30030030,
73
+ 0x03300300,
74
+ 0x00333033,
75
+ 0x30333330,
76
+ 0x03303300,
77
+ 0x03000330,
78
+ 0x30000030
79
+ ].pack("V8")
80
+
81
+ AMMO_FULL_PATTERN = [
82
+ 0x00000000,
83
+ 0x04444440,
84
+ 0x04444440,
85
+ 0x04444440,
86
+ 0x04444440,
87
+ 0x04444440,
88
+ 0x04444440,
89
+ 0x00000000
90
+ ].pack("V8")
91
+
92
+ AMMO_EMPTY_PATTERN = [
93
+ 0x00000000,
94
+ 0x04444440,
95
+ 0x04000040,
96
+ 0x04000040,
97
+ 0x04000040,
98
+ 0x04000040,
99
+ 0x04444440,
100
+ 0x00000000
101
+ ].pack("V8")
102
+
103
+ DIGIT_PATTERNS = [
104
+ 0x00555550,
105
+ 0x05500055,
106
+ 0x05550055,
107
+ 0x05505055,
108
+ 0x05500555,
109
+ 0x05500055,
110
+ 0x00555550,
111
+ 0x00000000,
112
+
113
+ 0x00055000,
114
+ 0x00055500,
115
+ 0x00055000,
116
+ 0x00055000,
117
+ 0x00055000,
118
+ 0x00055000,
119
+ 0x00555500,
120
+ 0x00000000,
121
+
122
+ 0x00555550,
123
+ 0x05500055,
124
+ 0x05500000,
125
+ 0x00550000,
126
+ 0x00005500,
127
+ 0x00000550,
128
+ 0x05555555,
129
+ 0x00000000,
130
+
131
+ 0x00555550,
132
+ 0x05500055,
133
+ 0x05500000,
134
+ 0x00555000,
135
+ 0x05500000,
136
+ 0x05500055,
137
+ 0x00555550,
138
+ 0x00000000,
139
+
140
+ 0x00550000,
141
+ 0x00555000,
142
+ 0x00550500,
143
+ 0x00550550,
144
+ 0x05555555,
145
+ 0x00550000,
146
+ 0x05555000,
147
+ 0x00000000,
148
+
149
+ 0x05555555,
150
+ 0x00000055,
151
+ 0x00555555,
152
+ 0x05500055,
153
+ 0x05500000,
154
+ 0x05500055,
155
+ 0x00555550,
156
+ 0x00000000,
157
+
158
+ 0x00555550,
159
+ 0x05500055,
160
+ 0x00000055,
161
+ 0x00555555,
162
+ 0x05500055,
163
+ 0x05500055,
164
+ 0x00555550,
165
+ 0x00000000,
166
+
167
+ 0x05555555,
168
+ 0x00550055,
169
+ 0x00055000,
170
+ 0x00055000,
171
+ 0x00005500,
172
+ 0x00005500,
173
+ 0x00055550,
174
+ 0x00000000,
175
+
176
+ 0x00555550,
177
+ 0x05500055,
178
+ 0x05500055,
179
+ 0x00555550,
180
+ 0x05500055,
181
+ 0x05500055,
182
+ 0x00555550,
183
+ 0x00000000,
184
+
185
+ 0x00555550,
186
+ 0x05500055,
187
+ 0x05500055,
188
+ 0x05555550,
189
+ 0x05500000,
190
+ 0x05500055,
191
+ 0x00555550,
192
+ 0x00000000
193
+ ].pack("V*")
194
+
195
+ MAX_SHOTS = 3
196
+ MAX_LIVES = 5
197
+ SCORE_DIGITS = 7
198
+
199
+ SHIP_PATTERN_INDEX = 1
200
+ SHOT_PATTERN_INDEX = 2
201
+ ENEMY_PATTERN_INDEX = 3
202
+ EXPLOSION_PATTERN_INDEX = 5
203
+ AMMO_PATTERN_INDEX = 6
204
+ DIGIT_PATTERN_INDEX = 8
205
+
206
+ BG_COLOR = 0
207
+ ENEMY_COLOR = 17
208
+ SHIP_COLOR = 18
209
+ FIRE_COLOR = 19
210
+ SHOT_COLOR = 20
211
+ SCORE_COLOR = 21
212
+
213
+ class Entity < Struct.new :x, :y, :health
214
+ def alive?
215
+ health > 0
216
+ end
217
+
218
+ def dead?
219
+ not alive?
220
+ end
221
+
222
+ def kill
223
+ self.health = 0
224
+ self
225
+ end
226
+ end
227
+
228
+ def main_game
229
+ lives = MAX_LIVES
230
+ score = 0
231
+ ship = Entity.new 128, 200, 1
232
+ shots = []
233
+ enemies = []
234
+ escaped_enemies = []
235
+ explosions = []
236
+
237
+ controller = controllers[0]
238
+
239
+ start :spawning_enemies, 50
240
+ start :enemy_animation, 10, 2
241
+ start :flickering, 3, 2
242
+
243
+ lives_name_offset = 0x0800 + (26 * 32 + 2) * 2
244
+ ammo_name_offset = 0x0800 + (26 * 32 + (32 - 2 - MAX_SHOTS)) * 2
245
+ score_name_offset = 0x0800 + (2 * 32 + (32 - 2 - SCORE_DIGITS)) * 2
246
+
247
+ for life in 0...MAX_LIVES
248
+ write_vdu_byte(lives_name_offset+life*2+1, 0b01001001)
249
+ end
250
+ for shot in 0...MAX_SHOTS
251
+ write_vdu_byte(ammo_name_offset+shot*2+1, 0b01001001)
252
+ end
253
+ for digit in 0...SCORE_DIGITS
254
+ write_vdu_byte(score_name_offset+digit*2+1, 0b01001001)
255
+ end
256
+
257
+ each_frame do
258
+ break unless lives >= 0
259
+
260
+ if controller.b_pressed?
261
+ start :firing, 6 unless already? :firing
262
+ else
263
+ stop :firing
264
+ end
265
+
266
+ hvelocity = controller.horizontal * 3
267
+ ship.x += hvelocity
268
+ if hvelocity > 0
269
+ ship.x = 256-4 if ship.x >= (256-4)
270
+ elsif hvelocity < 0
271
+ ship.x = 4 if ship.x <= 4
272
+ end
273
+
274
+ explosions.each do |explosion|
275
+ explosion.health -= 1
276
+ end
277
+ shots.each do |shot|
278
+ shot.y -= 6
279
+ end
280
+ enemies.each do |enemy|
281
+ enemy.y += 1
282
+ end
283
+ escaped_enemies.each do |enemy|
284
+ enemy.y += 1
285
+ end
286
+
287
+ enemies.each do |enemy|
288
+ next unless enemy.alive?
289
+ shots.each do |shot|
290
+ next unless shot.alive?
291
+ if (shot.x - enemy.x).abs < 4 and (shot.y - enemy.y).abs < 4
292
+ shot.kill
293
+ enemy.kill
294
+ explosions.push Entity.new(enemy.x, enemy.y, 5)
295
+ score += 15
296
+ break
297
+ end
298
+ end
299
+ if enemy.alive?
300
+ if (ship.x - enemy.x).abs < 8 and (ship.y - enemy.y).abs < 6
301
+ lives -= 1
302
+ enemy.kill
303
+ explosions.push Entity.new(enemy.x, enemy.y, 5)
304
+ score -= 100
305
+ score = 0 if score < 0
306
+ elsif enemy.y > (ship.y + 4)
307
+ lives -= 1
308
+ enemy.kill
309
+ escaped_enemies.push Entity.new(enemy.x, enemy.y, 1)
310
+ score -= 50
311
+ score = 0 if score < 0
312
+ end
313
+ end
314
+ end
315
+
316
+ explosions.reject! { |e| e.dead? }
317
+ shots.reject! { |s| s.y < 0 or s.dead? }
318
+ enemies.reject! { |e| e.dead? }
319
+ escaped_enemies.reject! { |e| e.y >= 224 }
320
+
321
+ if frame_for? :firing and shots.size < MAX_SHOTS
322
+ shots.push Entity.new(ship.x, ship.y, 1)
323
+ end
324
+
325
+ if frame_for? :spawning_enemies
326
+ enemies.push Entity.new(rand(224)+16, 0, 1)
327
+ end
328
+
329
+ for life in 0...lives
330
+ write_vdu_byte(lives_name_offset+life*2, SHIP_PATTERN_INDEX)
331
+ end
332
+ for life in lives...MAX_LIVES
333
+ write_vdu_byte(lives_name_offset+life*2, 0)
334
+ end
335
+
336
+ for shot in 0...(shots.size)
337
+ write_vdu_byte(ammo_name_offset+shot*2, AMMO_PATTERN_INDEX+1)
338
+ end
339
+ for shot in (shots.size)...MAX_SHOTS
340
+ write_vdu_byte(ammo_name_offset+shot*2, AMMO_PATTERN_INDEX)
341
+ end
342
+
343
+ score_accum = score
344
+ for digit in 0...SCORE_DIGITS
345
+ offset = (SCORE_DIGITS-digit-1)*2
346
+ digit_value = score_accum % 10
347
+ write_vdu_byte(score_name_offset+offset, DIGIT_PATTERN_INDEX + digit_value)
348
+ score_accum /= 10
349
+ end
350
+
351
+ clear_sprites
352
+ explosions.each do |explosion|
353
+ sprite explosion.x - 4, explosion.y - 4, EXPLOSION_PATTERN_INDEX, 0
354
+ end
355
+ sprite ship.x - 4, ship.y - 4, SHIP_PATTERN_INDEX, 0
356
+ shots.each do |shot|
357
+ sprite shot.x - 4, shot.y - 4, SHOT_PATTERN_INDEX, 0
358
+ end
359
+ enemy_phase = phase_of :enemy_animation
360
+ enemies.each do |enemy|
361
+ sprite enemy.x - 4, enemy.y - 4, ENEMY_PATTERN_INDEX + enemy_phase, 0
362
+ end
363
+ escaped_enemies.each do |enemy|
364
+ sprite enemy.x - 4, enemy.y - 4, ENEMY_PATTERN_INDEX + enemy_phase, 0
365
+ end
366
+
367
+ if frame_for? :flickering
368
+ case phase_of :flickering
369
+ when 0
370
+ palette_entry_rgb FIRE_COLOR, 0xff, 0xaa, 0x00
371
+ palette_entry_rgb SHOT_COLOR, 0x55, 0xaa, 0xff
372
+ when 1
373
+ palette_entry_rgb FIRE_COLOR, 0xff, 0xff, 0x00
374
+ palette_entry_rgb SHOT_COLOR, 0x00, 0x55, 0xff
375
+ end
376
+ end
377
+
378
+ wait_scanlines 208
379
+
380
+ palette_entry_rgb FIRE_COLOR, 0xff, 0xaa, 0x00
381
+ palette_entry_rgb SHOT_COLOR, 0x00, 0x55, 0xff
382
+ end
383
+ end
384
+
385
+ Retrograph.app do
386
+ title "Earth Defense"
387
+
388
+ show_retrograph_logo
389
+ each_of_n_frames(25) do
390
+ break if controllers.first.start_pressed?
391
+ end
392
+
393
+ reset_vdu
394
+ vdu_mode 0x0f # mode 3 + sprites + bg
395
+
396
+ sprite_pattern(0x0000, SHIP_PATTERN_INDEX, SHIP_PATTERN)
397
+ sprite_pattern(0x0000, SHOT_PATTERN_INDEX, SHOT_PATTERN)
398
+ sprite_pattern(0x0000, ENEMY_PATTERN_INDEX, ENEMY_PATTERN)
399
+ sprite_pattern(0x0000, ENEMY_PATTERN_INDEX+1, ENEMY_PATTERN2)
400
+ sprite_pattern(0x0000, EXPLOSION_PATTERN_INDEX, EXPLOSION_PATTERN)
401
+ sprite_pattern(0x0000, AMMO_PATTERN_INDEX, AMMO_FULL_PATTERN)
402
+ sprite_pattern(0x0000, AMMO_PATTERN_INDEX+1, AMMO_EMPTY_PATTERN)
403
+ sprite_pattern(0x0000, DIGIT_PATTERN_INDEX, DIGIT_PATTERNS)
404
+
405
+ palette_entry_rgb BG_COLOR, 0x55, 0x55, 0x55
406
+ palette_entry_rgb ENEMY_COLOR, 0xaa, 0x00, 0x00
407
+ palette_entry_rgb SHIP_COLOR, 0xaa, 0xaa, 0xaa
408
+ palette_entry_rgb SCORE_COLOR, 0xff, 0xff, 0xff
409
+
410
+ loop do
411
+ main_game
412
+ end
413
+ end
data/demos/logo.rb ADDED
@@ -0,0 +1,34 @@
1
+ # Retrograph logo demo
2
+ #
3
+ # Copyright (c) 2009 MenTaLguY <mental@rydia.net>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+ #
23
+ require 'rubygems'
24
+ require 'retrograph/easy'
25
+
26
+ Retrograph.app do
27
+ title "Retrograph Logo"
28
+ loop do
29
+ show_retrograph_logo
30
+ each_frame do
31
+ break if controllers.first.start_pressed?
32
+ end
33
+ end
34
+ end