petermorphose 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +3 -0
- data/README.md +31 -0
- data/bin/petermorphose +3 -0
- data/levels/flgr_Der_Alkohol_und_seine_Folgen.pml +447 -0
- data/levels/jr_Am_Tempel_des_Harlow-Karlow.pml +413 -0
- data/levels/jr_Auf_der_Flucht.pml +456 -0
- data/levels/jr_Die_zwei_Baeume.pml +447 -0
- data/levels/jr_Feuertauchen.pml +343 -0
- data/levels/jr_Gemuetlicher_Aufstieg.pml +329 -0
- data/levels/jr_Gruenhuegelshausen.pml +423 -0
- data/levels/jr_Gruselgrotte.pml +421 -0
- data/levels/jr_Hoch_hinaus.pml +265 -0
- data/levels/jr_Vom_Ozean_in_die_Traufe.pml +342 -0
- data/levels/jr_Weg_durchs_Feuer.pml +544 -0
- data/levels/sl_Heimweg_zu_Henk.pml +307 -0
- data/media/arg1.wav +0 -0
- data/media/arg2.wav +0 -0
- data/media/arrow_hit.wav +0 -0
- data/media/blocker_break.wav +0 -0
- data/media/bow.wav +0 -0
- data/media/break1.wav +0 -0
- data/media/break2.wav +0 -0
- data/media/buttons.png +0 -0
- data/media/collect_ammo.wav +0 -0
- data/media/collect_freeze.wav +0 -0
- data/media/collect_health.wav +0 -0
- data/media/collect_key.wav +0 -0
- data/media/collect_points.wav +0 -0
- data/media/collect_star.wav +0 -0
- data/media/danger.png +0 -0
- data/media/death.wav +0 -0
- data/media/dialogs.bmp +0 -0
- data/media/door1.wav +0 -0
- data/media/door2.wav +0 -0
- data/media/eat.wav +0 -0
- data/media/effects.bmp +0 -0
- data/media/enemies.bmp +0 -0
- data/media/explosion.wav +0 -0
- data/media/game.ogg +0 -0
- data/media/gui.bmp +0 -0
- data/media/help1.wav +0 -0
- data/media/help2.wav +0 -0
- data/media/jump.wav +0 -0
- data/media/lava.wav +0 -0
- data/media/lever.wav +0 -0
- data/media/menu.ogg +0 -0
- data/media/morph.wav +0 -0
- data/media/player.bmp +0 -0
- data/media/player_arg.wav +0 -0
- data/media/shshsh.wav +0 -0
- data/media/skies.png +0 -0
- data/media/slime1.wav +0 -0
- data/media/slime2.wav +0 -0
- data/media/slime3.wav +0 -0
- data/media/stairs.wav +0 -0
- data/media/stairs_steps.wav +0 -0
- data/media/stuff.bmp +0 -0
- data/media/sword_whoosh.wav +0 -0
- data/media/tiles.bmp +0 -0
- data/media/title.png +0 -0
- data/media/title_dark.png +0 -0
- data/media/turbo.wav +0 -0
- data/media/water1.wav +0 -0
- data/media/water2.wav +0 -0
- data/media/whoosh.wav +0 -0
- data/media/whoosh_back.wav +0 -0
- data/media/won.bmp +0 -0
- data/media/yippie.wav +0 -0
- data/src/const.rb +301 -0
- data/src/en.yml +62 -0
- data/src/gosu-preview.rb +116 -0
- data/src/helpers/audio.rb +9 -0
- data/src/helpers/graphics.rb +24 -0
- data/src/helpers/input.rb +28 -0
- data/src/ini_file.rb +25 -0
- data/src/level_info.rb +63 -0
- data/src/localization.rb +19 -0
- data/src/main.rb +86 -0
- data/src/map.rb +136 -0
- data/src/objects/collectible_object.rb +174 -0
- data/src/objects/effect_object.rb +120 -0
- data/src/objects/game_object.rb +363 -0
- data/src/objects/living_object.rb +657 -0
- data/src/objects/object_def.rb +45 -0
- data/src/script.rb +207 -0
- data/src/states/credits.rb +24 -0
- data/src/states/game.rb +463 -0
- data/src/states/help.rb +268 -0
- data/src/states/level_selection.rb +43 -0
- data/src/states/menu.rb +45 -0
- data/src/states/options.rb +81 -0
- data/src/states/state.rb +11 -0
- data/src/states/title.rb +17 -0
- data/src/states/victory.rb +20 -0
- 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'
|
data/src/gosu-preview.rb
ADDED
@@ -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,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
|
data/src/localization.rb
ADDED
@@ -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
|