petermorphose 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/COPYING +3 -0
  2. data/README.md +31 -0
  3. data/bin/petermorphose +3 -0
  4. data/levels/flgr_Der_Alkohol_und_seine_Folgen.pml +447 -0
  5. data/levels/jr_Am_Tempel_des_Harlow-Karlow.pml +413 -0
  6. data/levels/jr_Auf_der_Flucht.pml +456 -0
  7. data/levels/jr_Die_zwei_Baeume.pml +447 -0
  8. data/levels/jr_Feuertauchen.pml +343 -0
  9. data/levels/jr_Gemuetlicher_Aufstieg.pml +329 -0
  10. data/levels/jr_Gruenhuegelshausen.pml +423 -0
  11. data/levels/jr_Gruselgrotte.pml +421 -0
  12. data/levels/jr_Hoch_hinaus.pml +265 -0
  13. data/levels/jr_Vom_Ozean_in_die_Traufe.pml +342 -0
  14. data/levels/jr_Weg_durchs_Feuer.pml +544 -0
  15. data/levels/sl_Heimweg_zu_Henk.pml +307 -0
  16. data/media/arg1.wav +0 -0
  17. data/media/arg2.wav +0 -0
  18. data/media/arrow_hit.wav +0 -0
  19. data/media/blocker_break.wav +0 -0
  20. data/media/bow.wav +0 -0
  21. data/media/break1.wav +0 -0
  22. data/media/break2.wav +0 -0
  23. data/media/buttons.png +0 -0
  24. data/media/collect_ammo.wav +0 -0
  25. data/media/collect_freeze.wav +0 -0
  26. data/media/collect_health.wav +0 -0
  27. data/media/collect_key.wav +0 -0
  28. data/media/collect_points.wav +0 -0
  29. data/media/collect_star.wav +0 -0
  30. data/media/danger.png +0 -0
  31. data/media/death.wav +0 -0
  32. data/media/dialogs.bmp +0 -0
  33. data/media/door1.wav +0 -0
  34. data/media/door2.wav +0 -0
  35. data/media/eat.wav +0 -0
  36. data/media/effects.bmp +0 -0
  37. data/media/enemies.bmp +0 -0
  38. data/media/explosion.wav +0 -0
  39. data/media/game.ogg +0 -0
  40. data/media/gui.bmp +0 -0
  41. data/media/help1.wav +0 -0
  42. data/media/help2.wav +0 -0
  43. data/media/jump.wav +0 -0
  44. data/media/lava.wav +0 -0
  45. data/media/lever.wav +0 -0
  46. data/media/menu.ogg +0 -0
  47. data/media/morph.wav +0 -0
  48. data/media/player.bmp +0 -0
  49. data/media/player_arg.wav +0 -0
  50. data/media/shshsh.wav +0 -0
  51. data/media/skies.png +0 -0
  52. data/media/slime1.wav +0 -0
  53. data/media/slime2.wav +0 -0
  54. data/media/slime3.wav +0 -0
  55. data/media/stairs.wav +0 -0
  56. data/media/stairs_steps.wav +0 -0
  57. data/media/stuff.bmp +0 -0
  58. data/media/sword_whoosh.wav +0 -0
  59. data/media/tiles.bmp +0 -0
  60. data/media/title.png +0 -0
  61. data/media/title_dark.png +0 -0
  62. data/media/turbo.wav +0 -0
  63. data/media/water1.wav +0 -0
  64. data/media/water2.wav +0 -0
  65. data/media/whoosh.wav +0 -0
  66. data/media/whoosh_back.wav +0 -0
  67. data/media/won.bmp +0 -0
  68. data/media/yippie.wav +0 -0
  69. data/src/const.rb +301 -0
  70. data/src/en.yml +62 -0
  71. data/src/gosu-preview.rb +116 -0
  72. data/src/helpers/audio.rb +9 -0
  73. data/src/helpers/graphics.rb +24 -0
  74. data/src/helpers/input.rb +28 -0
  75. data/src/ini_file.rb +25 -0
  76. data/src/level_info.rb +63 -0
  77. data/src/localization.rb +19 -0
  78. data/src/main.rb +86 -0
  79. data/src/map.rb +136 -0
  80. data/src/objects/collectible_object.rb +174 -0
  81. data/src/objects/effect_object.rb +120 -0
  82. data/src/objects/game_object.rb +363 -0
  83. data/src/objects/living_object.rb +657 -0
  84. data/src/objects/object_def.rb +45 -0
  85. data/src/script.rb +207 -0
  86. data/src/states/credits.rb +24 -0
  87. data/src/states/game.rb +463 -0
  88. data/src/states/help.rb +268 -0
  89. data/src/states/level_selection.rb +43 -0
  90. data/src/states/menu.rb +45 -0
  91. data/src/states/options.rb +81 -0
  92. data/src/states/state.rb +11 -0
  93. data/src/states/title.rb +17 -0
  94. data/src/states/victory.rb +20 -0
  95. metadata +188 -0
data/src/en.yml ADDED
@@ -0,0 +1,62 @@
1
+ 'Am Tempel des Harlow-Karlow': At Harlow-Karlow’s Shrine
2
+ 'Mittel': 'intermediate'
3
+ 'Stürme den Tempel der Harlow-Karlow-Sekte!': 'Ambush the secret cult of Harlow-Karlow!'
4
+ 'Sterne einsammeln': 'stars to collect'
5
+ 'Auf der Flucht': 'On the Run'
6
+ 'Schwer und lang': 'tough and long'
7
+ 'Auch wenn das Level lang ist: Immer fleißig Sterne sammeln...': 'Even if the level is long - keep collecting those stars...'
8
+ 'Die zwei Bäume': 'The Two Trees'
9
+ 'Folge den beiden Bäumen durch den Verlauf der Karte.': 'Follow the trees towards the sky.'
10
+ 'Feuertauchen': 'Fire Diving'
11
+ 'Leicht bis mittel': 'easy/intermediate'
12
+ 'Rette die Geisel ganz unten vor der Lava, danach dich selbst.': 'Rescue the hostage deep down, then yourself.'
13
+ 'Gemütlicher Aufstieg': 'The Easy Stroll Skywards'
14
+ 'Sehr einfach': 'very easy'
15
+ 'Leichte Runde ohne große Rätsel. Empfohlende Einstiegsrunde!': 'Easy map without any puzzles. Recommended first level.'
16
+ 'und': '& need to rescue'
17
+ 'Gefangene retten': 'hostages'
18
+ 'retten': ''
19
+ 'Durchkommen': 'Survive'
20
+
21
+ 'Wähle mit den Pfeiltasten ein Level aus und starte es mit Enter.': 'Select a level with the arrow keys, then press enter.'
22
+
23
+ 'Punkte': 'Score'
24
+ 'Noch %d': '%d to go'
25
+ 'Flitzebogenpeter': 'Archer Peter'
26
+ 'Schlüssel': 'Key'
27
+ 'Genug gesammelt!': 'Enough to pass!'
28
+ 'Ritterpeter': 'Knight Peter'
29
+ 'Lava angehalten!': 'Lava frozen!'
30
+ 'Bombenlegerpeter': 'Bomber Peter'
31
+ 'Adlerstiefel': 'Eagly Boots'
32
+ 'Heimweg zu Henk': 'Home is where Henk is'
33
+ 'Leicht': 'easy'
34
+ 'Leichtes, kurzes Level ohne viele Gegner.': 'Easy and short level without too many baddies.'
35
+ 'Gravialistan': 'Gravialistan'
36
+ 'Weg durchs Feuer': 'Through Fire'
37
+ 'Vom Ozean in die Traufe': 'From Ocean to Worse'
38
+ 'Hektische Runde mit einigen kniffligen Stellen.': 'A hectic journey that requires some head-scratching.'
39
+ 'Schwere, aber schaffbare Karte mit viel Wasser.': 'Hard map with plenty water.'
40
+ 'Hoch hinaus': 'Higher and Higher'
41
+ 'Mittel und kurz': 'intermediate, short'
42
+ 'Leichte und kurze Runde mit vielen Flugeinlagen.': 'Short and easy map with a lot of airborne acrobatics.'
43
+ 'Gruselgrotte': 'Creepy Cavern'
44
+ 'Sehr schwer': 'very tough'
45
+ 'Keine Lava, viele Gegner. Achte auf die Sterne!': 'No lava, many enemies, few stars.'
46
+ 'Grünhügelshausen': 'Greenhillyvillyvalley'
47
+ 'Gemütliche Runde für fortgeschrittene Spieler.': 'Relaxed map for intermediate players.'
48
+ 'Der Alkohol und seine Folgen': 'The Day After'
49
+ 'Schwer': 'tough'
50
+ 'Das passiert, wenn kleine Peter auf Partys gehen...': 'What happens when little Peter goes to party...'
51
+ 'Sekunde': 'sec'
52
+ 'Sekunden': 'sec'
53
+
54
+ 'Du hast verloren, weil eine Gefangene verbrannt ist.': 'You lost because a hostage was burned.'
55
+ 'Du hast verloren, weil du nicht genug Sterne gesammelt hast.': 'You lost because you haven’t collected enough stars.'
56
+ 'Du hast verloren, weil du nicht alle Gefangenen befreit hast.': 'You lost because you haven’t rescue all hostages.'
57
+ 'Du bist gestorben.': 'You died.'
58
+ 'Abbrechen per Leertaste.': 'Cancel by pressing space.'
59
+ 'Den Baum des Lebens findet man oft vergebens': 'The fullest fruit will fall to the deepest death.'
60
+ 'Engelsflügel': 'Angel Wings'
61
+ 'Henks Haustür!': 'Henk’s front door!'
62
+ 'Siebenmeilenstiefel': 'Booster Boots'
@@ -0,0 +1,116 @@
1
+ require 'gosu'
2
+
3
+ # Wrapper around Gosu 0.7 that provides the work-in-progress 0.8 interface
4
+
5
+ module Gosu
6
+ class Font
7
+ alias :initialize07 :initialize
8
+
9
+ def initialize *args
10
+ if args.first.is_a? Gosu::Window then
11
+ initialize07 *args
12
+ else
13
+ height = args[0]
14
+ options = args[1] || {}
15
+ name = options[:name] || Gosu::default_font_name
16
+ initialize07 $window, name, height
17
+ end
18
+ end
19
+ end
20
+
21
+ class Window
22
+ alias :initialize07 :initialize
23
+
24
+ def initialize width, height, *args
25
+ if args.empty? or args.first.is_a? Hash then
26
+ options = args.first || {}
27
+ fullscreen = !!options[:fullscreen]
28
+ update_interval = options[:update_interval] || 16.66
29
+ else
30
+ fullscreen, update_interval = *args
31
+ end
32
+ $window = initialize07 width, height, fullscreen, update_interval
33
+ end
34
+ end
35
+
36
+ class Image
37
+ alias :initialize07 :initialize
38
+
39
+ def initialize *args
40
+ if args.first.is_a? Gosu::Window then
41
+ initialize07 *args
42
+ else
43
+ source = args[0]
44
+ tileable = !args[1] || args[1][:tileable]
45
+ initialize07 $window, source, !!tileable
46
+ end
47
+ end
48
+
49
+ class <<self
50
+ alias load_tiles07 load_tiles
51
+ end
52
+
53
+ def self.load_tiles *args
54
+ if args.first.is_a? Gosu::Window then
55
+ load_tiles07 *args
56
+ else
57
+ source = args[0]
58
+ x, y = args[1..2]
59
+ tileable = !args[3] || args[3][:tileable]
60
+ load_tiles07 $window, source, x, y, !!tileable
61
+ end
62
+ end
63
+
64
+ def self.from_text *args
65
+ if args.first.is_a? Gosu::Window then
66
+ args.size == 4 ? from_text4(*args) : from_text7(*args)
67
+ else
68
+ text = args[0]
69
+ height = args[1]
70
+ options = args[2] || {}
71
+ font = options[:font] || Gosu::default_font_name
72
+ if width = options[:width] then
73
+ spacing = options[:spacing] || 0
74
+ align = options[:align] || :left
75
+ Gosu::Image.from_text7 $window, text, font, height, spacing, width, align
76
+ else
77
+ Gosu::Image.from_text4 $window, text, font, height
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ def self.draw_quad *args
84
+ $window.draw_quad *args
85
+ end
86
+
87
+ def self.clip_to *args
88
+ $window.clip_to *args do
89
+ yield
90
+ end
91
+ end
92
+
93
+ def self.translate *args
94
+ $window.translate *args do
95
+ yield
96
+ end
97
+ end
98
+
99
+ def self.scale *args
100
+ $window.scale *args do
101
+ yield
102
+ end
103
+ end
104
+
105
+ def self.rotate *args
106
+ $window.rotate *args do
107
+ yield
108
+ end
109
+ end
110
+
111
+ def self.transform *args
112
+ $window.transform *args do
113
+ yield
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,9 @@
1
+ SONGS, SAMPLES = {}, {}
2
+
3
+ def song(name)
4
+ SONGS[name.to_sym] ||= Gosu::Song.new("media/#{name}.ogg")
5
+ end
6
+
7
+ def sound(name)
8
+ SAMPLES[name.to_sym] ||= Gosu::Sample.new("media/#{name}.wav")
9
+ end
@@ -0,0 +1,24 @@
1
+ def font
2
+ @font ||= Gosu::Font.new(16)
3
+ end
4
+
5
+ def draw_string string, x, y, a = 255
6
+ font.draw string, x, y, Z_TEXT, 1, 1, alpha(a)
7
+ end
8
+
9
+ def draw_centered_string string, x, y, a = 255
10
+ font.draw_rel string, x, y, Z_TEXT, 0.5, 0.0, 1, 1, alpha(a)
11
+ end
12
+
13
+ def draw_right_aligned_string string, x, y, a = 255
14
+ font.draw_rel string, x, y, Z_TEXT, 1, 0.0, 1, 1, alpha(a)
15
+ end
16
+
17
+ def draw_rect x, y, w, h, color, z = 0
18
+ Gosu::draw_quad x, y, color, x + w, y, color,
19
+ x, y + h, color, x + w, y + h, color, z
20
+ end
21
+
22
+ def alpha n
23
+ n << 24 | 0xffffff
24
+ end
@@ -0,0 +1,28 @@
1
+ CONTROLS = {
2
+ :menu_prev => [:KbUp, :GpUp, :KbLeft, :GpLeft],
3
+ :menu_next => [:KbDown, :GpDown, :KbRight, :GpRight],
4
+ :menu_confirm => [:KbEnter, :KbReturn, :KbSpace, :GpButton0],
5
+ :menu_cancel => [:KbEscape, :GpButton2],
6
+
7
+ :up => [:KbUp, :GpUp],
8
+ :down => [:KbDown, :GpDown],
9
+ :left => [:KbLeft, :GpLeft],
10
+ :right => [:KbRight, :GpRight],
11
+ :jump => [:KbUp, :GpButton0],
12
+ :action => [:KbSpace, :GpButton1],
13
+ :use => [:KbDown, :GpDown]
14
+ }
15
+
16
+ class Object
17
+ CONTROLS.keys.each do |key|
18
+ ids = CONTROLS[key].map &Gosu.method(:const_get)
19
+
20
+ define_method "#{key}?" do |id|
21
+ ids.include? id
22
+ end
23
+
24
+ define_method "#{key}_pressed?" do
25
+ ids.any? { |id| $window.button_down? id }
26
+ end
27
+ end
28
+ end
data/src/ini_file.rb ADDED
@@ -0,0 +1,25 @@
1
+ # A wrapper for INI files, used for levels and other stuff.
2
+
3
+ require 'iconv'
4
+
5
+ class INIFile
6
+ def initialize filename
7
+ @sections = {}
8
+ current_section = nil
9
+
10
+ File.open(filename) do |file|
11
+ file.each_line do |line|
12
+ case Iconv.conv('utf-8', 'ISO-8859-1', line.chomp)
13
+ when /^\[(.+)\]$/ then
14
+ current_section = @sections[$1] = {}
15
+ when /^([^=]*)=(.*)$/ then
16
+ current_section[$1] = $2
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ def [](section, name)
23
+ (@sections[section] || {})[name.to_s]
24
+ end
25
+ end
data/src/level_info.rb ADDED
@@ -0,0 +1,63 @@
1
+ class LevelInfo
2
+ attr_accessor :filename, :difficulty, :ini_file
3
+
4
+ def initialize filename
5
+ @filename = filename
6
+ @ini_file = INIFile.new(filename)
7
+ @title = t(@ini_file['Info', 'Title'] || 'Unbenannte Karte')
8
+ @difficulty = t(@ini_file['Info', 'Skill'] || '')
9
+ @description = t(@ini_file['Info', 'Desc'] || '')
10
+ @author = (@ini_file['Info', 'Author'] || '')
11
+
12
+ stars_goal = (@ini_file['Map', 'StarsGoal'] || 100).to_i
13
+ if stars_goal == 0 then
14
+ @goal = t "Durchkommen"
15
+ else
16
+ @goal = "#{stars_goal} #{t 'Sterne einsammeln'}"
17
+ end
18
+
19
+ hostages = []
20
+ obj = 0
21
+ while obj_desc = @ini_file['Objects', obj] do
22
+ if obj_desc[0, 2] == ('%02X' % ID_CAROLIN) then
23
+ obj_desc_extended = @ini_file['Objects', "#{obj}Y"] || '|'
24
+ hostages << (obj_desc_extended.split('|').last || 'Carolin')
25
+ end
26
+ obj += 1
27
+ end
28
+ if hostages.size == 1 then
29
+ @goal += " #{t 'und'} #{hostages[0]} #{t 'retten'}"
30
+ elsif hostages.size > 1 then
31
+ @goal += " #{t 'und'} #{hostages.size} #{t 'Gefangene retten'}"
32
+ end
33
+
34
+ # TODO highscore = ...
35
+ end
36
+
37
+ def draw y, active
38
+ if active then
39
+ draw_rect 0, y + 1, 631, 98, 0xff603000
40
+ end
41
+
42
+ draw_rect 0, y, 631, 1, 0x003000
43
+ # TODO (Highscore) / (not beaten yet)
44
+ draw_string @title, 5, y + 7, 255
45
+ draw_right_aligned_string @difficulty, 626, y + 7, 255
46
+ draw_string @description, 5, y + 30, 192
47
+ draw_string @goal, 5, y + 53, 128
48
+ draw_string @author, 5, y + 76, 80
49
+ draw_rect 0, y + 99, 631, 1, 0x006000
50
+ end
51
+
52
+ FIRST_LEVEL = 'jr_Gemuetlicher_Aufstieg.pml'
53
+
54
+ def <=> other
55
+ if File.basename(self.filename) == FIRST_LEVEL then
56
+ -1
57
+ elsif File.basename(other.filename) == FIRST_LEVEL then
58
+ +1
59
+ else
60
+ difficulty <=> other.difficulty
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,19 @@
1
+ require 'locale'
2
+ require 'yaml'
3
+
4
+ if Locale.current.language == 'de' then
5
+ def t string
6
+ string
7
+ end
8
+ else
9
+ LOCALIZATION_FILE = File.expand_path("#{__FILE__}/../en.yml")
10
+ TRANSLATIONS = YAML.load_file(LOCALIZATION_FILE)
11
+
12
+ def t string
13
+ TRANSLATIONS[string] ||= begin
14
+ @localization_file ||= (File.open(LOCALIZATION_FILE, 'a') rescue $stdout)
15
+ @localization_file.puts "'#{string}': '#{string}' # TODO"
16
+ string
17
+ end
18
+ end
19
+ end
data/src/main.rb ADDED
@@ -0,0 +1,86 @@
1
+ if RUBY_VERSION < '1.9' then
2
+ require 'rubygems'
3
+ require 'require_relative'
4
+ end
5
+
6
+ require 'gosu'
7
+ require_relative 'gosu-preview' # upcoming Gosu 0.8 interface wrapper
8
+
9
+ # Gosu related polish
10
+ # TODO Proper scaling
11
+ # TODO Packaging as gem
12
+ # TODO Deployment tasks
13
+ # TODO Better resource handling
14
+
15
+ # Polish
16
+ # TODO "Gosu" splash screen
17
+ # TODO Remaining Menu
18
+ # TODO High Scores
19
+
20
+ def debug binding
21
+ require 'pry'
22
+ Pry.start binding
23
+ end
24
+
25
+ # Save ARGV[1] if existing
26
+ ARGV[0] = File.expand_path(ARGV[0]) if ARGV[0]
27
+
28
+ # For resource loading.
29
+ Dir.chdir File.expand_path("#{__FILE__}/../..")
30
+
31
+ %w(localization script const helpers/graphics helpers/audio helpers/input
32
+ states/state states/title states/menu states/level_selection states/game
33
+ objects/object_def objects/game_object objects/living_object objects/collectible_object objects/effect_object
34
+ ini_file level_info map).each { |fn| require_relative fn }
35
+
36
+ # Not yet part of gosu-preview
37
+ Gosu::enable_undocumented_retrofication rescue nil
38
+
39
+ WIDTH, HEIGHT = 640, 480
40
+ TARGET_FPS = 30
41
+
42
+ # Z Order
43
+ Z_EFFECTS, Z_LAVA, Z_UI, Z_TEXT = *0..255
44
+
45
+ # Simple implementation of the Gosu "State-Based" game pattern
46
+ class Window < Gosu::Window
47
+ def initialize
48
+ super WIDTH*3/2, HEIGHT*3/2, :update_interval => 1000.0 / TARGET_FPS
49
+
50
+ self.caption = 'Peter Morphose'
51
+
52
+ if ARGV[0] then
53
+ State.current = Game.new LevelInfo.new(ARGV[0])
54
+ else
55
+ State.current = Title.new
56
+ end
57
+ end
58
+
59
+ def update
60
+ if State.current then
61
+ State.current.update
62
+ else
63
+ close
64
+ end
65
+ end
66
+
67
+ def draw
68
+ scale(1.5) do
69
+ State.current.draw if State.current
70
+ end
71
+ end
72
+
73
+ def button_down id
74
+ State.current.button_down id if State.current
75
+ end
76
+
77
+ def button_up id
78
+ State.current.button_up id if State.current
79
+ end
80
+
81
+ def needs_cursor?
82
+ State.current.needs_cursor? if State.current
83
+ end
84
+ end
85
+
86
+ Window.new.show
data/src/map.rb ADDED
@@ -0,0 +1,136 @@
1
+ class Map
2
+ class OverriddenTile < Struct.new(:to_blob)
3
+ def columns; TILE_SIZE; end
4
+ def rows; TILE_SIZE; end
5
+ end
6
+
7
+ attr_reader :level_top, :level_bottom
8
+ attr_reader :scripts, :timers, :vars
9
+ attr_accessor :lava_pos, :lava_time_left, :lava_mode, :lava_speed, :lava_frame
10
+
11
+ def inspect
12
+ "#<Map>"
13
+ end
14
+
15
+ def initialize game, ini_file
16
+ @@tiles ||= Gosu::Image.load_tiles 'media/tiles.bmp', -16, -16
17
+ @@skies ||= Gosu::Image.load_tiles 'media/skies.png', -4, -2
18
+
19
+ @game = game
20
+ @tiles = []
21
+ @scripts = []
22
+ @timers = []
23
+ @vars = [0] * 16
24
+
25
+ (0...TILES_Y).each do |y|
26
+ row = ini_file['Map', y] || '00' * TILES_X
27
+ (0...TILES_X).each do |x|
28
+ self[x, y] = row[x * 2, 2].to_i(16)
29
+ end
30
+ @scripts[y] = ini_file['Scripts', y]
31
+ end
32
+
33
+ SCRIPT_TIMERS.times do |i|
34
+ @timers << ini_file['Scripts', "Timer#{i}"]
35
+ end
36
+
37
+ @sky = (ini_file['Map', 'Sky'] || 0).to_i
38
+
39
+ @lava_speed = (ini_file['Map', 'LavaSpeed'] || 1).to_i
40
+ @lava_mode = (ini_file['Map', 'LavaMode'] || 0).to_i
41
+ @lava_pos = (ini_file['Map', 'LavaPos'] || TILES_Y).to_i * TILE_SIZE
42
+ @lava_score = (ini_file['Map', 'LavaScore'] || 1).to_i
43
+ @lava_frame = @lava_time_left = 0
44
+
45
+ @level_top = (ini_file['Map', 'LevelTop'] || 0).to_i * TILE_SIZE
46
+ @level_bottom = [1024, @lava_pos / TILE_SIZE].min
47
+
48
+ # Overloaded map tiles
49
+ @tile_images = []
50
+ @@tiles.each_with_index do |original_tile, index|
51
+ transparency = "\0\0\0\0"
52
+ if (override = ini_file['Tiles', '%02X' % index]) and override.length == 3456 then
53
+ data = "\xff" * (TILE_SIZE * TILE_SIZE * 4)
54
+
55
+ (TILE_SIZE * TILE_SIZE).times do |i|
56
+ inv_x = i % TILE_SIZE
57
+ inv_y = i / TILE_SIZE
58
+ src_i = inv_x * TILE_SIZE + inv_y
59
+ rrggbb = override[src_i * 6, 6]
60
+ if rrggbb == 'FF00FF' then
61
+ data[i * 4, 4] = transparency
62
+ else
63
+ data[i * 4 + 0] = rrggbb[0, 2].to_i(16).chr
64
+ data[i * 4 + 1] = rrggbb[2, 2].to_i(16).chr
65
+ data[i * 4 + 2] = rrggbb[4, 2].to_i(16).chr
66
+ end
67
+ end
68
+
69
+ @tile_images[index] = Gosu::Image.new(OverriddenTile.new(data))
70
+ else
71
+ @tile_images[index] = original_tile
72
+ end
73
+ end
74
+ end
75
+
76
+ def [](x, y)
77
+ if x.between? 0, TILES_X - 1 and y.between? 0, TILES_Y - 1 then
78
+ @tiles[y * TILES_X + x]
79
+ else
80
+ 0x70
81
+ end
82
+ end
83
+
84
+ def []=(x, y, tile)
85
+ if (0...TILES_X).include? x and (0...TILES_Y).include? y then
86
+ @tiles[y * TILES_X + x] = tile
87
+ end
88
+ end
89
+
90
+ def solid? x, y
91
+ y > @lava_pos or (0x70...0xe0).include? self[x / TILE_SIZE, y / TILE_SIZE]
92
+ end
93
+
94
+ ALL_STAIRS_UP = [TILE_STAIRS_UP, TILE_STAIRS_UP_2]
95
+ ALL_STAIRS_DOWN = [TILE_STAIRS_DOWN, TILE_STAIRS_DOWN_2]
96
+
97
+ def stairs_passable? x, y
98
+ if ALL_STAIRS_DOWN.include? self[x, y] then
99
+ loop do
100
+ y += 1
101
+ return false if y >= TILES_Y
102
+ return true if ALL_STAIRS_UP.include? self[x, y]
103
+ end
104
+ else
105
+ loop do
106
+ y -= 1
107
+ return false if y < 0
108
+ return true if ALL_STAIRS_DOWN.include? self[x, y]
109
+ end
110
+ end
111
+ end
112
+
113
+ def draw
114
+ if @sky == 0 then
115
+ 4.times do |y|
116
+ 4.times do |x|
117
+ @@skies.first.draw x * 144, y * 120, 0
118
+ end
119
+ end
120
+ else
121
+ 5.times do |y|
122
+ 4.times do |x|
123
+ @@skies[@sky].draw x * 144, y * 120 - @game.view_pos % 120, 0
124
+ end
125
+ end
126
+ end
127
+
128
+ offset = @game.view_pos % TILE_SIZE
129
+ row = @game.view_pos / TILE_SIZE
130
+ (HEIGHT / TILE_SIZE + 1).times do |y|
131
+ TILES_X.times do |x|
132
+ @tile_images[self[x, row + y] || 0].draw x * TILE_SIZE, y * TILE_SIZE - offset, 0
133
+ end
134
+ end
135
+ end
136
+ end