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