petermorphose 2.0.0

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