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
@@ -0,0 +1,174 @@
|
|
1
|
+
class CollectibleObject < GameObject
|
2
|
+
def update
|
3
|
+
if not [ID_EDIBLE_FISH, ID_EDIBLE_FISH_2].include? pmid and not xdata.nil? and xdata[1, 1] == '1' then
|
4
|
+
fall
|
5
|
+
check_tile
|
6
|
+
end
|
7
|
+
|
8
|
+
# Burn in lava
|
9
|
+
if y + ObjectDef[pmid].rect.bottom > game.map.lava_pos then
|
10
|
+
game.cast_fx 2, 2, 0, x, y, 16, 16, 0, -3, 1
|
11
|
+
kill
|
12
|
+
emit_sound :shshsh
|
13
|
+
game.lose("Du hast verloren, weil eine Gefangene verbrannt ist.") if pmid == ID_CAROLIN
|
14
|
+
end
|
15
|
+
|
16
|
+
if pmid == ID_CAROLIN and game.frame % 20 == 0 and rand(4) == 0 then
|
17
|
+
emit_sound "help#{rand(2) + 1}"
|
18
|
+
end
|
19
|
+
|
20
|
+
if [ID_EDIBLE_FISH, ID_EDIBLE_FISH_2].include? pmid then
|
21
|
+
if not in_water? then
|
22
|
+
if xdata.is_a? String and xdata[0, 1] == '1' then
|
23
|
+
fall
|
24
|
+
check_tile
|
25
|
+
end
|
26
|
+
else
|
27
|
+
if pmid == ID_EDIBLE_FISH then
|
28
|
+
self.x -= 2
|
29
|
+
self.pmid = ID_EDIBLE_FISH_2 if blocked? DIR_LEFT
|
30
|
+
else
|
31
|
+
self.x += 2
|
32
|
+
self.pmid = ID_EDIBLE_FISH if blocked? DIR_RIGHT
|
33
|
+
end
|
34
|
+
game.create_object ID_FX_WATER_BUBBLE, x, y - 3, nil if rand(30) == 0
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Cannot be collected...
|
39
|
+
return if game.player.action >= ACT_DEAD
|
40
|
+
|
41
|
+
if collide_with? game.player.rect(2, 2) then
|
42
|
+
case pmid
|
43
|
+
when ID_CAROLIN then
|
44
|
+
game.cast_objects ID_FX_FLYING_CHAIN, 8, 0, -1, 3, rect(1, -1)
|
45
|
+
fc = game.create_object ID_FX_FLYING_CAROLIN, x, y, nil
|
46
|
+
fc.vx, fc.vy = -7 + rand(15), -15,
|
47
|
+
sound(:yippie).play
|
48
|
+
game.score += 100
|
49
|
+
kill
|
50
|
+
when ID_KEY then
|
51
|
+
sound(:collect_key).play
|
52
|
+
game.player.emit_text "#{t ObjectDef[pmid].name}!"
|
53
|
+
game.cast_objects ID_FX_SPARKLE, 2, 0, 0, 0, rect
|
54
|
+
game.score += 2
|
55
|
+
game.keys += 1
|
56
|
+
kill
|
57
|
+
when ID_EDIBLE_FISH, ID_EDIBLE_FISH_2 then
|
58
|
+
sound(:collect_health).play
|
59
|
+
sound(:eat).play
|
60
|
+
game.player.emit_text '+1'
|
61
|
+
game.score += 2
|
62
|
+
game.player.life += 1
|
63
|
+
kill
|
64
|
+
when ID_MORE_TIME, ID_MORE_TIME_2 then
|
65
|
+
return if game.player.pmid == ID_PLAYER
|
66
|
+
sound(:morph).play
|
67
|
+
game.cast_objects ID_FX_SPARKLE, 2, 0, 0, 0, rect
|
68
|
+
if pmid == ID_MORE_TIME then
|
69
|
+
game.player.emit_text "+1 #{t 'Sekunde'}"
|
70
|
+
game.score += 5
|
71
|
+
game.time_left += 30
|
72
|
+
else
|
73
|
+
game.player.emit_text "+3,5 #{t 'Sekunden'}"
|
74
|
+
game.score += 10
|
75
|
+
game.time_left += 110
|
76
|
+
end
|
77
|
+
kill
|
78
|
+
when ID_HEALTH, ID_HEALTH_2 then
|
79
|
+
if pmid == ID_HEALTH then amount = 1 else amount = 4 end
|
80
|
+
sound(:collect_health).play
|
81
|
+
game.player.emit_text '+1'
|
82
|
+
game.cast_objects ID_FX_SPARKLE, 2, 0, 0, 0, rect
|
83
|
+
game.score += amount
|
84
|
+
game.player.life += amount
|
85
|
+
kill
|
86
|
+
when ID_STAR..ID_STAR_3 then
|
87
|
+
sound(:collect_star).play Gosu::random(0.5, 0.7), Gosu::random(0.9, 1.1)
|
88
|
+
game.score += 2
|
89
|
+
game.stars += 1
|
90
|
+
if game.stars < game.stars_goal then
|
91
|
+
game.player.emit_text t("Noch %d") % (game.stars_goal - game.stars)
|
92
|
+
elsif game.stars == game.stars_goal then
|
93
|
+
game.player.emit_text t("Genug gesammelt!")
|
94
|
+
end
|
95
|
+
game.cast_objects ID_FX_SPARKLE, 2, 0, 0, 0, rect
|
96
|
+
kill
|
97
|
+
when ID_POINTS..ID_POINTS_MAX then
|
98
|
+
sound(:collect_points).play
|
99
|
+
game.player.emit_text "*#{ObjectDef[pmid].life}*"
|
100
|
+
game.cast_objects ID_FX_SPARKLE, 3, 0, 0, 0, rect
|
101
|
+
game.score += ObjectDef[pmid].life
|
102
|
+
kill
|
103
|
+
when ID_MUNITION_GUN, ID_MUNITION_GUN_2 then
|
104
|
+
sound(:collect_ammo).play
|
105
|
+
amount = 1 + (pmid - ID_MUNITION_GUN) * 2
|
106
|
+
game.player.emit_text "+#{amount}"
|
107
|
+
game.cast_objects ID_FX_SPARKLE, 2, 0, 0, 0, rect
|
108
|
+
game.score += amount
|
109
|
+
game.ammo += amount
|
110
|
+
kill
|
111
|
+
when ID_MUNITION_BOMBER, ID_MUNITION_BOMBER_2 then
|
112
|
+
sound(:collect_ammo).play
|
113
|
+
amount = 1 + (pmid - ID_MUNITION_BOMBER) * 2
|
114
|
+
game.player.emit_text "+#{amount}"
|
115
|
+
game.cast_objects ID_FX_SPARKLE, 2, 0, 0, 0, rect
|
116
|
+
game.score += amount
|
117
|
+
game.bombs += amount
|
118
|
+
kill
|
119
|
+
when ID_COOKIE then
|
120
|
+
sound(:eat).play
|
121
|
+
game.cast_objects ID_FX_SPARKLE, 1, 0, 0, 0, rect
|
122
|
+
emit_text t(xdata.split('|')[1] || 'Der Keks war leer...'), :slow
|
123
|
+
game.score += 10
|
124
|
+
kill
|
125
|
+
when ID_SLOW_DOWN then
|
126
|
+
if game.map.lava_mode == 0 and game.map.lava_speed == 48 then
|
127
|
+
elsif game.map.lava_mode == 1 and game.map.lava_speed == 1 then
|
128
|
+
game.map.lava_mode = 0
|
129
|
+
game.map.lava_speed = 2
|
130
|
+
elsif game.map.lava_mode == 0 then
|
131
|
+
game.map.lava_speed += 1
|
132
|
+
else
|
133
|
+
game.map.lava_speed -= 1
|
134
|
+
end
|
135
|
+
sound(:collect_freeze).play
|
136
|
+
game.player.emit_text t('Lava verlangsamt!')
|
137
|
+
game.cast_objects ID_FX_SPARKLE, 3, 0, 0, 0, rect
|
138
|
+
kill
|
139
|
+
when ID_CRYSTAL then
|
140
|
+
sound(:collect_freeze).play
|
141
|
+
game.player.emit_text t('Lava angehalten!'), :slow
|
142
|
+
game.map.lava_time_left += 80
|
143
|
+
game.cast_objects ID_FX_SPARKLE, 4, 0, 0, 0, rect
|
144
|
+
kill
|
145
|
+
when ID_MORPH_FIGHTER..ID_MORPH_MAX then
|
146
|
+
target_id = ID_PLAYER_FIGHTER + pmid - ID_MORPH_FIGHTER
|
147
|
+
if game.player.pmid != target_id then
|
148
|
+
sound(:morph).play
|
149
|
+
game.player.pmid = target_id
|
150
|
+
game.player.emit_text "#{t ObjectDef[game.player.pmid].name}!", :slow
|
151
|
+
game.player.action = ACT_JUMP
|
152
|
+
game.cast_fx 8, 0, 0, x, y, 24, 24, 0, -1, 4
|
153
|
+
game.time_left = ObjectDef[game.player.pmid].life
|
154
|
+
game.cast_objects ID_FX_SPARKLE, 5, 0, 0, 0, rect
|
155
|
+
kill
|
156
|
+
end
|
157
|
+
when ID_SPEED, ID_JUMP, ID_FLY then
|
158
|
+
sound(:morph).play
|
159
|
+
game.player.emit_text "#{t ObjectDef[pmid].name}!", :slow
|
160
|
+
case pmid
|
161
|
+
when ID_SPEED then game.speed_time_left = 330
|
162
|
+
when ID_JUMP then game.jump_time_left = 330
|
163
|
+
when ID_FLY then game.fly_time_left = 88
|
164
|
+
end
|
165
|
+
game.cast_fx 8, 0, 0, game.player.x, game.player.y - 10, 24, 24, 0, -1, 4
|
166
|
+
game.cast_objects ID_FX_SPARKLE, 2, 0, 0, 0, rect
|
167
|
+
kill
|
168
|
+
when ID_SEAMINE then
|
169
|
+
game.explosion x, y, 50, false
|
170
|
+
kill
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
class EffectObject < GameObject
|
2
|
+
def initialize *args
|
3
|
+
super
|
4
|
+
|
5
|
+
@phase = 0
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.images
|
9
|
+
@images ||= Gosu::Image.load_tiles 'media/effects.bmp', -7, -7
|
10
|
+
end
|
11
|
+
|
12
|
+
def draw
|
13
|
+
case pmid
|
14
|
+
when ID_FX_SMOKE then
|
15
|
+
EffectObject.images[[ 0, @phase - 1].max].draw x - 11, y - 11 - game.view_pos, Z_EFFECTS, 1, 1, alpha(128), :additive
|
16
|
+
when ID_FX_FLAME then
|
17
|
+
EffectObject.images[[ 7, @phase + 6].max].draw x - 11, y - 11 - game.view_pos, Z_EFFECTS, 1, 1, alpha(160), :additive
|
18
|
+
when ID_FX_SPARK then
|
19
|
+
EffectObject.images[[14, @phase + 13].max].draw x - 11, y - 11 - game.view_pos, Z_EFFECTS, 1, 1, alpha(128), :additive
|
20
|
+
when ID_FX_BUBBLE then
|
21
|
+
EffectObject.images[[21, @phase + 20].max].draw x - 11, y - 11 - game.view_pos, Z_EFFECTS, 1, 1, alpha(192), :additive
|
22
|
+
when ID_FX_RICOCHET then
|
23
|
+
EffectObject.images[19 + xdata.to_i].draw x - 11, y - 11 - game.view_pos, Z_EFFECTS, 1, 1, alpha([255 - @phase * 3, 0].max)
|
24
|
+
when ID_FX_LINE then
|
25
|
+
EffectObject.images[28].draw x, y - 11 - game.view_pos, Z_EFFECTS, xdata.to_f / EffectObject.images.first.width, 1, alpha(255 - @phase), :additive
|
26
|
+
when ID_FX_BLOCKER_PARTS then
|
27
|
+
EffectObject.images[29].draw_rot x, y - game.view_pos, Z_EFFECTS, x * 10, 0.5, 0.5, 1, 1, alpha(255 - @phase), :additive
|
28
|
+
when ID_FX_BREAK, ID_FX_BREAK_2 then
|
29
|
+
EffectObject.images[30 + (ID_FX_BREAK_2 - pmid)].draw x - 11, y - 11 - game.view_pos, Z_EFFECTS, 1, 1, alpha(@phase) # TODO :subtractive
|
30
|
+
when ID_FX_BREAKING_PARTS then
|
31
|
+
EffectObject.images[32].draw_rot x, y - game.view_pos, Z_EFFECTS, x * 10, 0.5, 0.5, 1, 1, alpha(255 - @phase)
|
32
|
+
when ID_FX_BLOOD then
|
33
|
+
EffectObject.images[33].draw x - 11, y - 11 - game.view_pos, Z_EFFECTS, 1, 1, alpha(250 - @phase)
|
34
|
+
when ID_FX_FIRE then
|
35
|
+
EffectObject.images[34].draw x, y - game.view_pos, Z_EFFECTS, 1, 1, alpha(@phase)
|
36
|
+
when ID_FX_FLYING_CAROLIN then
|
37
|
+
EffectObject.images[35].draw x - 11, y - 11 - game.view_pos, Z_EFFECTS
|
38
|
+
when ID_FX_FLYING_CHAIN then
|
39
|
+
EffectObject.images[36].draw_rot x, y - game.view_pos, Z_EFFECTS, x * 10 % 360, 0.5, 0.5, 1, 1, alpha(255 - @phase)
|
40
|
+
when ID_FX_FLYING_BLOB then
|
41
|
+
EffectObject.images[37].draw_rot x, y - game.view_pos, Z_EFFECTS, x * 10, 0.5, 0.5, 1, 1, alpha(255 - @phase)
|
42
|
+
when ID_FX_WATER_BUBBLE then
|
43
|
+
EffectObject.images[46].draw x - 11, y - 11 - game.view_pos, Z_EFFECTS, 1, 1, alpha(100 + rand(29))
|
44
|
+
when ID_FX_WATER then
|
45
|
+
EffectObject.images[47].draw_rot x, y - game.view_pos, Z_EFFECTS, x * 10, 0.5, 0.5, 1, 1, alpha(255 - @phase)
|
46
|
+
when ID_FX_SPARKLE then
|
47
|
+
EffectObject.images[48].draw x - 11, y - 11 - game.view_pos, Z_EFFECTS, 1, 1, alpha(255 - @phase), :additive
|
48
|
+
when ID_FX_TEXT, ID_FX_SLOW_TEXT then
|
49
|
+
# TODO: Force text inside portion @ x=0..576
|
50
|
+
draw_centered_string xdata, x, y - 7 - game.view_pos, 255 - @phase
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def update
|
55
|
+
case pmid
|
56
|
+
when ID_FX_SMOKE, ID_FX_FLAME then
|
57
|
+
self.x += vx
|
58
|
+
self.y += vy
|
59
|
+
@phase += 1
|
60
|
+
kill if @phase > 7
|
61
|
+
when ID_FX_SPARK then
|
62
|
+
self.x += vx
|
63
|
+
self.y += vy
|
64
|
+
@phase += 1 if game.frame % 3 == 0
|
65
|
+
kill if @phase > 5
|
66
|
+
when ID_FX_BUBBLE then
|
67
|
+
self.x += vx
|
68
|
+
self.y = game.map.lava_pos - 12
|
69
|
+
@phase += 1 if game.frame % 2 == 0
|
70
|
+
kill if @phase > 7
|
71
|
+
when ID_FX_BLOCKER_PARTS then
|
72
|
+
self.x += vx
|
73
|
+
self.y += vy
|
74
|
+
self.vy += 1
|
75
|
+
@phase += 15
|
76
|
+
kill if @phase == 255
|
77
|
+
when ID_FX_WATER then
|
78
|
+
self.x += vx
|
79
|
+
self.vy += 1
|
80
|
+
self.y += vy
|
81
|
+
@phase += 25
|
82
|
+
kill if @phase == 250
|
83
|
+
when ID_FX_BREAK, ID_FX_BREAK_2 then
|
84
|
+
@phase += 15
|
85
|
+
if @phase == 255 then
|
86
|
+
game.map[x / TILE_SIZE, y / TILE_SIZE] = TILE_HOLE + ((y / TILE_SIZE % 2 + x / TILE_SIZE) % 2) * 16
|
87
|
+
game.cast_objects ID_FX_BREAKING_PARTS, 20, 0, 5, 2,
|
88
|
+
ObjectDef::Rect.new(x / (TILE_SIZE/2) * (TILE_SIZE/2), y / (TILE_SIZE/2) * (TILE_SIZE/2), 24, 24)
|
89
|
+
emit_sound "break#{rand(2) + 1}"
|
90
|
+
kill
|
91
|
+
return
|
92
|
+
end
|
93
|
+
when ID_FX_FIRE then
|
94
|
+
@phase += 15
|
95
|
+
if @phase == 255 then
|
96
|
+
game.map[x / TILE_SIZE, y / TILE_SIZE] = TILE_BIG_BLOCKER_BROKEN
|
97
|
+
game.explosion x + 12, y + 12, 30, true
|
98
|
+
kill
|
99
|
+
end
|
100
|
+
when ID_FX_BREAKING_PARTS, ID_FX_TEXT, ID_FX_RICOCHET, ID_FX_LINE,
|
101
|
+
ID_FX_BLOOD, ID_FX_FLYING_CHAIN, ID_FX_FLYING_BLOB, ID_FX_SLOW_TEXT then
|
102
|
+
self.x += vx
|
103
|
+
self.y += vy
|
104
|
+
@phase += 15
|
105
|
+
@phase -= 10 if pmid == ID_FX_SLOW_TEXT
|
106
|
+
kill if @phase == 255
|
107
|
+
when ID_FX_FLYING_CAROLIN then
|
108
|
+
self.x += vx
|
109
|
+
self.y += vy
|
110
|
+
kill if y < game.view_pos - HEIGHT
|
111
|
+
when ID_FX_WATER_BUBBLE then
|
112
|
+
self.y -= 1
|
113
|
+
self.x += 1 - rand(3) if rand(4) == 0
|
114
|
+
kill if not in_water?
|
115
|
+
when ID_FX_SPARKLE then
|
116
|
+
@phase += 25
|
117
|
+
kill if @phase == 250
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,363 @@
|
|
1
|
+
class NilClass
|
2
|
+
def existence_as_int
|
3
|
+
0
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class GameObject
|
8
|
+
attr_reader :game
|
9
|
+
attr_accessor :pmid, :x, :y, :xdata, :vx, :vy
|
10
|
+
|
11
|
+
def marked?
|
12
|
+
@marked
|
13
|
+
end
|
14
|
+
|
15
|
+
def existence_as_int
|
16
|
+
marked? ? 0 : 1
|
17
|
+
end
|
18
|
+
|
19
|
+
def kill
|
20
|
+
0.upto(game.obj_vars.size) do |i|
|
21
|
+
game.obj_vars[i] = nil if game.obj_vars[i] == self
|
22
|
+
end
|
23
|
+
@marked = true
|
24
|
+
end
|
25
|
+
|
26
|
+
def emit_text string, speed = :fast
|
27
|
+
if speed == :slow then id = ID_FX_SLOW_TEXT else id = ID_FX_TEXT end
|
28
|
+
game.create_object(id, x, y - 10, string).vy = -1
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize game, pmid, x, y, xdata
|
32
|
+
@game, @pmid, @x, @y, @xdata = game, pmid, x, y, xdata
|
33
|
+
@vx = @vy = 0
|
34
|
+
@last_frame_in_water = in_water?
|
35
|
+
end
|
36
|
+
|
37
|
+
def emit_sound name
|
38
|
+
game.emit_sound name, y
|
39
|
+
end
|
40
|
+
|
41
|
+
ALL_WATER_TILES = (TILE_WATER..TILE_WATER_4).to_a + [TILE_WATER_5]
|
42
|
+
def in_water?
|
43
|
+
ALL_WATER_TILES.include? game.map[x / TILE_SIZE, y / TILE_SIZE]
|
44
|
+
end
|
45
|
+
|
46
|
+
def update
|
47
|
+
if [ID_FIREWALL_1, ID_FIREWALL_2, ID_FIRE].include? pmid then
|
48
|
+
if y + ObjectDef[pmid].rect.bottom - 11 > game.map.lava_pos then
|
49
|
+
game.cast_fx 4, 4, 0, x, y, 16, 16, 0, -3, 1
|
50
|
+
kill
|
51
|
+
emit_sound(:shshsh)
|
52
|
+
else
|
53
|
+
self.xdata = ((game.frame * 7.5).to_i % 256 - 128).abs.to_s
|
54
|
+
rect = self.rect
|
55
|
+
game.objects.each do |obj|
|
56
|
+
if obj.pmid <= ID_LIVING_MAX and obj.pmid != ID_ENEMY_BERSERKER and rect.include? obj then
|
57
|
+
obj.hit
|
58
|
+
game.cast_fx rand(3), rand(2), 0, x, y, 12, 12, 0, 0, 2
|
59
|
+
end
|
60
|
+
end
|
61
|
+
return
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Hint arrows
|
66
|
+
kill if pmid == ID_HELP_ARROW and rect(10, 20).include? game.player
|
67
|
+
|
68
|
+
# Fish
|
69
|
+
if pmid == ID_FISH or pmid == ID_FISH_2 then
|
70
|
+
if not in_water? then
|
71
|
+
fall
|
72
|
+
check_tile
|
73
|
+
else
|
74
|
+
if pmid == ID_FISH then
|
75
|
+
self.x -= 2
|
76
|
+
self.pmid = ID_FISH_2 if blocked? DIR_LEFT
|
77
|
+
else
|
78
|
+
self.x += 2
|
79
|
+
self.pmid = ID_FISH if blocked? DIR_RIGHT
|
80
|
+
end
|
81
|
+
if rand(30) == 0 then
|
82
|
+
game.create_object ID_FX_WATER_BUBBLE, x, y - 3, nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Fused bomb
|
88
|
+
if pmid == ID_FUSING_BOMB then
|
89
|
+
# Count up and possibly explode
|
90
|
+
time = (xdata || 1).to_i + 1
|
91
|
+
self.xdata = time.to_s
|
92
|
+
if time >= 25 then
|
93
|
+
kill
|
94
|
+
game.explosion x, y, 50, true
|
95
|
+
return
|
96
|
+
end
|
97
|
+
|
98
|
+
# Also explode on touching an enemy
|
99
|
+
game.objects.each do |obj|
|
100
|
+
if obj.pmid >= ID_ENEMY and obj.pmid <= ID_ENEMY_MAX and obj.collide_with? rect(1, 1) and
|
101
|
+
not [ACT_DEAD, ACT_INV_UP, ACT_INV_UP].include? obj.action then
|
102
|
+
obj.hurt true
|
103
|
+
kill
|
104
|
+
game.explosion x, y, 50, true
|
105
|
+
return
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
fall
|
110
|
+
check_tile
|
111
|
+
end
|
112
|
+
|
113
|
+
# Rocks fall down
|
114
|
+
if (ID_TRASH..ID_TRASH_4).include? pmid then
|
115
|
+
fall
|
116
|
+
check_tile
|
117
|
+
end
|
118
|
+
|
119
|
+
# Get roasted by lava
|
120
|
+
if y + ObjectDef[pmid].rect.bottom > game.map.lava_pos then
|
121
|
+
game.cast_fx 4, 4, 0, x, y, 16, 16, 0, -3, 1
|
122
|
+
kill
|
123
|
+
emit_sound :shshsh
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def draw
|
128
|
+
@@stuff_images ||= Gosu::Image.load_tiles 'media/stuff.bmp', -16, -3
|
129
|
+
color = 0xffffffff
|
130
|
+
mode = :default
|
131
|
+
|
132
|
+
if [ID_FIREWALL_1, ID_FIREWALL_2, ID_FIRE].include? pmid then
|
133
|
+
color = alpha(127 + (xdata && xdata.to_i || 128))
|
134
|
+
mode = :additive
|
135
|
+
elsif pmid == ID_HELP_ARROW then
|
136
|
+
color = alpha(127 + (game.frame / 8 % 2) * 64)
|
137
|
+
end
|
138
|
+
@@stuff_images[pmid - ID_OTHER_OBJECTS_MIN].draw x - 11, y - 11 - game.view_pos, 0, 1, 1, color, mode
|
139
|
+
if pmid == ID_CAROLIN then
|
140
|
+
if xdata and xdata.length > 2 then
|
141
|
+
name = xdata[2..-1]
|
142
|
+
else
|
143
|
+
name = 'Carolin'
|
144
|
+
end
|
145
|
+
draw_centered_string name, x, y + 16 - game.view_pos, 128
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def fall
|
150
|
+
if in_water? and not @last_frame_in_water then
|
151
|
+
game.cast_objects ID_FX_WATER, 5, -vx / 2, -5, 3, rect(1, 1)
|
152
|
+
emit_sound "water#{rand(2) + 1}"
|
153
|
+
end
|
154
|
+
@last_frame_in_water = in_water?
|
155
|
+
|
156
|
+
# Gravity
|
157
|
+
self.vy += 1 if (pmid > ID_PLAYER_MAX or game.fly_time_left == 0)# and not in_water?
|
158
|
+
|
159
|
+
if in_water? then
|
160
|
+
self.vy -= 1 if vy > +1
|
161
|
+
self.vy += 1 if vy < -1
|
162
|
+
self.vx -= 1 if vx > +2
|
163
|
+
self.vx += 1 if vx < -2
|
164
|
+
end
|
165
|
+
|
166
|
+
if pmid <= ID_PLAYER_MAX and not blocked? DIR_DOWN then
|
167
|
+
if vx.abs < 5 then
|
168
|
+
self.vx = (self.vx / 2.0).to_i
|
169
|
+
else
|
170
|
+
self.vx = (self.vx / 1.03).to_i if game.frame % 3 == 0
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Velocity is limited to +- TILE_SIZE
|
175
|
+
self.vx = [[vx, -TILE_SIZE].max, TILE_SIZE].min
|
176
|
+
self.vy = [[vy, -TILE_SIZE].max, TILE_SIZE].min
|
177
|
+
|
178
|
+
if vy > 0 then
|
179
|
+
vy.times do
|
180
|
+
break if blocked? DIR_DOWN
|
181
|
+
self.y += 1
|
182
|
+
end
|
183
|
+
elsif vy < 0 then
|
184
|
+
vy.abs.times do
|
185
|
+
break if blocked? DIR_UP
|
186
|
+
self.y -= 1
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
if blocked? DIR_DOWN then
|
191
|
+
if is_a? LivingObject and vx > 10 and not [ACT_DEAD, ACT_ACTION_1, ACT_ACTION_2].include? action then
|
192
|
+
self.action = ACT_IMPACT_1 + [vy - 11, 4].min
|
193
|
+
end
|
194
|
+
self.vy = 0
|
195
|
+
# Conveyor belts are neither implemented nor used
|
196
|
+
# if (Data.Map.Tile(PosX + Data.Defs[ID].Rect.Left, PosY + Data.Defs[ID].Rect.Top + Data.Defs[ID].Rect.Bottom + 1) = Tile_PullLeft) and (not Blocked(Dir_Left)) then Dec(PosX);
|
197
|
+
# if (Data.Map.Tile(PosX + Data.Defs[ID].Rect.Left, PosY + Data.Defs[ID].Rect.Top + Data.Defs[ID].Rect.Bottom + 1) = Tile_PullRight) and (not Blocked(Dir_Right)) then Inc(PosX);
|
198
|
+
# if (Data.Map.Tile(PosX + Data.Defs[ID].Rect.Left + Data.Defs[ID].Rect.Right, PosY + Data.Defs[ID].Rect.Top + Data.Defs[ID].Rect.Bottom + 1) = Tile_PullLeft) and (not Blocked(Dir_Left)) then Dec(PosX);
|
199
|
+
# if (Data.Map.Tile(PosX + Data.Defs[ID].Rect.Left + Data.Defs[ID].Rect.Right, PosY + Data.Defs[ID].Rect.Top + Data.Defs[ID].Rect.Bottom + 1) = Tile_PullRight) and (not Blocked(Dir_Right)) then Inc(PosX);
|
200
|
+
end
|
201
|
+
|
202
|
+
if blocked? DIR_UP and game.fly_time_left == 0 then
|
203
|
+
self.vy = 1
|
204
|
+
#self.vx /= +2
|
205
|
+
end
|
206
|
+
|
207
|
+
if vx < 0 then
|
208
|
+
vx.abs.times do
|
209
|
+
if blocked? DIR_LEFT then
|
210
|
+
self.vx = 0
|
211
|
+
break
|
212
|
+
else
|
213
|
+
self.x -= 1
|
214
|
+
end
|
215
|
+
end
|
216
|
+
elsif vx > 0 then
|
217
|
+
vx.times do
|
218
|
+
if blocked? DIR_RIGHT then
|
219
|
+
self.vx = 0
|
220
|
+
break
|
221
|
+
else
|
222
|
+
self.x += 1
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
if (pmid > ID_PLAYER_MAX or game.fly_time_left == 0) and blocked? DIR_DOWN then
|
228
|
+
self.vx -= 1 if vx > 0
|
229
|
+
self.vx -= 1 if vx > +1
|
230
|
+
self.vx -= 1 if pmid <= ID_ENEMY_MAX and vx > +ObjectDef[pmid].speed
|
231
|
+
self.vx += 1 if vx < 0
|
232
|
+
self.vx += 1 if vx < -1
|
233
|
+
self.vx += 1 if pmid <= ID_ENEMY_MAX and vx < -ObjectDef[pmid].speed
|
234
|
+
|
235
|
+
if game.map[x / TILE_SIZE, (y + ObjectDef[pmid].rect.bottom + 1) / TILE_SIZE].between? TILE_SLIME, TILE_SLIME_3 then
|
236
|
+
(4 + game.frame % 2).times do
|
237
|
+
self.vx -= 1 if vx > 0
|
238
|
+
self.vx += 1 if vx < 0
|
239
|
+
end
|
240
|
+
(2 + game.frame % 2).times do
|
241
|
+
self.vx -= 1 if pmid <= ID_ENEMY_MAX and vx > +ObjectDef[pmid].speed
|
242
|
+
self.vx += 1 if pmid <= ID_ENEMY_MAX and vx < -ObjectDef[pmid].speed
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def fling vx, vy, randomness, fixed, malign
|
249
|
+
# TODO the "randomness" here smells because it's only towards the bottom right?!
|
250
|
+
|
251
|
+
return if pmid <= ID_PLAYER_MAX and malign and (game.inv_time_left > 0 or pmid == ID_PLAYER_BERSERKER)
|
252
|
+
|
253
|
+
randomness += 1 # what
|
254
|
+
|
255
|
+
if fixed then
|
256
|
+
self.vx = vx + rand(randomness)
|
257
|
+
self.vy = vy + rand(randomness)
|
258
|
+
else
|
259
|
+
self.vx += vx + rand(randomness)
|
260
|
+
self.vy += vy + rand(randomness)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def stuck?
|
265
|
+
rect = self.rect
|
266
|
+
map.solid?(rect.left, rect.top) or map.solid?(rect.right, rect.top) or
|
267
|
+
map.solid?(rect.left, rect.bottom) or map.solid?(rect.right, rect.bottom)
|
268
|
+
end
|
269
|
+
|
270
|
+
def blocked? direction
|
271
|
+
rect = ObjectDef[pmid].rect
|
272
|
+
case direction
|
273
|
+
when DIR_LEFT then
|
274
|
+
game.map.solid? x + rect.left - 1, y + rect.top or
|
275
|
+
game.map.solid? x + rect.left - 1, y + rect.bottom
|
276
|
+
when DIR_RIGHT then
|
277
|
+
game.map.solid? x + rect.right + 1, y + rect.top or
|
278
|
+
game.map.solid? x + rect.right + 1, y + rect.bottom
|
279
|
+
when DIR_UP then
|
280
|
+
game.map.solid? x + rect.left, y + rect.top - 1 or
|
281
|
+
game.map.solid? x + rect.right, y + rect.top - 1
|
282
|
+
when DIR_DOWN then
|
283
|
+
game.map.solid? x + rect.left, y + rect.bottom + 1 or
|
284
|
+
game.map.solid? x + rect.right, y + rect.bottom + 1
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def check_tile
|
289
|
+
case game.map[x / TILE_SIZE, y / TILE_SIZE]
|
290
|
+
when TILE_AIR_ROCKET_UP, TILE_AIR_ROCKET_UP_2, TILE_AIR_ROCKET_UP_3 then
|
291
|
+
emit_sound :turbo
|
292
|
+
fling 0, -21, 0, true, false
|
293
|
+
self.y -= 1 unless blocked? DIR_UP
|
294
|
+
self.x = x / 24 * 24 + 11
|
295
|
+
self.vx = direction.dir_to_vx if pmid.between? ID_ENEMY, ID_ENEMY_MAX
|
296
|
+
game.cast_fx 0, 0, 10, x, y, 24, 24, 0, -10, 1
|
297
|
+
when TILE_AIR_ROCKET_UP_LEFT then
|
298
|
+
emit_sound :turbo
|
299
|
+
self.y -= 1 unless blocked? DIR_UP
|
300
|
+
fling -10, -16, 0, true, false
|
301
|
+
self.y = y / TILE_SIZE + TILE_SIZE + 11
|
302
|
+
TILE_SIZE.times { if stuck? then self.vy -= 1 else break end }
|
303
|
+
game.cast_fx 0, 0, 10, x, y, 24, 24, -8, -8, 1
|
304
|
+
when TILE_AIR_ROCKET_UP_RIGHT then
|
305
|
+
emit_sound :turbo
|
306
|
+
self.y -= 1 unless blocked? DIR_UP
|
307
|
+
fling +10, -16, 0, true, false
|
308
|
+
self.y = y / TILE_SIZE + TILE_SIZE + 11
|
309
|
+
TILE_SIZE.times { if stuck? then self.vy -= 1 else break end }
|
310
|
+
game.cast_fx 0, 0, 10, x, y, 24, 24, +8, -8, 1
|
311
|
+
when TILE_AIR_ROCKET_LEFT then
|
312
|
+
emit_sound :turbo
|
313
|
+
fling -20, -3, 0, true, false
|
314
|
+
self.y = y / TILE_SIZE + TILE_SIZE + 11
|
315
|
+
TILE_SIZE.times { if stuck? then self.vy -= 1 else break end }
|
316
|
+
game.cast_fx 0, 0, 10, x, y, 24, 24, -10, 0, 1
|
317
|
+
when TILE_AIR_ROCKET_RIGHT then
|
318
|
+
emit_sound :turbo
|
319
|
+
fling +20, -3, 0, true, false
|
320
|
+
self.y = y / TILE_SIZE + TILE_SIZE + 11
|
321
|
+
TILE_SIZE.times { if stuck? then self.vy -= 1 else break end }
|
322
|
+
game.cast_fx 0, 0, 10, x, y, 24, 24, +10, 0, 1
|
323
|
+
when TILE_AIR_ROCKET_DOWN then
|
324
|
+
emit_sound :turbo
|
325
|
+
fling 0, 15, 0, true, false
|
326
|
+
self.y = y / TILE_SIZE + TILE_SIZE + 11
|
327
|
+
self.vx = direction.dir_to_vx if pmid.between? ID_ENEMY, ID_ENEMY_MAX
|
328
|
+
game.cast_fx 0, 0, 10, x, y, 24, 24, -10, 0, 1
|
329
|
+
when TILE_SLOW_ROCKET_UP then
|
330
|
+
game.cast_fx 0, 0, 1, x, y, 24, 24, 0, -2, 1
|
331
|
+
self.vy -= 4
|
332
|
+
if pmid.between? ID_ENEMY, ID_ENEMY_MAX then
|
333
|
+
self.vx = direction.dir_to_vx
|
334
|
+
else
|
335
|
+
self.vx /= 2
|
336
|
+
end
|
337
|
+
self.action = ACT_JUMP if pmid <= ID_LIVING_MAX
|
338
|
+
when TILE_SPIKES then
|
339
|
+
if pmid <= ID_LIVING_MAX and (y + ObjectDef[pmid].rect.bottom) % 24 > 8 then
|
340
|
+
hit
|
341
|
+
self.vx = 0
|
342
|
+
self.vy = -10
|
343
|
+
end
|
344
|
+
when TILE_SPIKES_TOP then
|
345
|
+
if pmid <= ID_LIVING_MAX and (y + ObjectDef[pmid].rect.bottom) % 24 < 14 then
|
346
|
+
hit
|
347
|
+
self.vx = 0
|
348
|
+
self.vy = 5
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def collide_with? other
|
354
|
+
other = other.rect unless other.is_a? ObjectDef::Rect
|
355
|
+
rect.collide_with? other
|
356
|
+
end
|
357
|
+
|
358
|
+
def rect(extra_width = 0, extra_height = 0)
|
359
|
+
rect = ObjectDef[pmid].rect
|
360
|
+
ObjectDef::Rect.new(x + rect.left - extra_width, y + rect.top - extra_height,
|
361
|
+
rect.width + extra_width * 2, rect.height + extra_height * 2)
|
362
|
+
end
|
363
|
+
end
|