This repository has been archived by the owner on Mar 9, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
block.py
216 lines (195 loc) · 9.39 KB
/
block.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
from animate import Animator
from coin import Coin
from item import Mushroom, FireFlower, StarMan, OneUp
from pygame import image
from pygame import mixer
from pygame.sprite import Sprite
class Block(Sprite):
"""Generic sprite for a block/wall"""
def __init__(self, x, y, initial_image, screen):
super(Block, self).__init__()
self.image = initial_image
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = x, y
self.screen = screen
def check_hit(self, other):
pass
def blit(self):
"""Blit the block to the screen"""
self.screen.blit(self.image, self.rect)
class BlockRubble(Sprite):
"""Sprite for pieces of a destroyed block"""
def __init__(self, x, y, initial_image, speed_x, speed_y, screen):
super(BlockRubble, self).__init__()
self.image = initial_image
self.rect = self.image.get_rect()
self.rect.x, self.rect.y = x, y
self.speed_x, self.speed_y = speed_x, speed_y
self.screen = screen
def update(self):
self.rect.x += self.speed_x
self.rect.y += self.speed_y
if self.speed_x > 0:
self.speed_x -= 1
else:
self.speed_x += 1
if self.rect.y > self.screen.get_height():
self.kill()
class CoinBlock(Block):
"""A block which contains a number of items"""
STD_STATE = 'std'
HIT_STATE = 'hit'
MOVE_UP_STATE = 'move-up'
MOVE_DOWN_STATE = 'move-down'
def __init__(self, x, y, initial_image, screen, map_group, rubble_group=None, coins=0, allow_hits=False,
sound=None):
super(CoinBlock, self).__init__(x, y, initial_image, screen)
self.coin_counter = int(coins)
self.blank_img = image.load('map/super-mario-empty-block.png') if self.coin_counter > 0 else None
self.coins = []
self.sound = sound if sound else mixer.Sound('audio/Coin.wav')
self.break_sound = mixer.Sound('audio/Break-block.wav')
self.map_group = map_group
self.rubble_group = rubble_group
self.allow_hits = allow_hits
self.state = {
'meta': CoinBlock.STD_STATE,
'move-state': None,
'blank': not self.allow_hits
}
self.std_location = self.rect.top
self.hit_location = self.rect.top - int(self.rect.height * 0.5)
# speed is a function of distance from hit location
self.speed = -int(abs(self.rect.top - self.hit_location) * 0.25)
self.item_location = self.rect.top - 1
@classmethod
def coin_block_from_tmx_obj(cls, obj, screen, map_group, game_objects):
"""Create a CoinBlock object from a tmx data object"""
return cls(obj.x, obj.y, obj.image, screen, map_group, coins=obj.properties.get('coins', 0),
allow_hits=obj.properties.get('allow_hits', False), rubble_group=game_objects['rubble'])
def set_blank(self):
"""Set the block to a blank block, which cannot be hit any longer"""
self.state['blank'] = True
self.allow_hits = False
def check_hit(self, other):
"""Check if the block has been hit in a way that will 'bump' its location"""
if not self.state['blank'] or self.allow_hits:
hit = False
if self.rect.collidepoint(other.rect.midtop):
hit = True
if hit: # leave other parameter as None to force hit state (for testing)
other.rect.y = self.rect.bottom
other.y_vel = 7
if not self.state['meta'] == CoinBlock.HIT_STATE:
self.state['meta'] = CoinBlock.HIT_STATE
self.state['move-state'] = CoinBlock.MOVE_UP_STATE
if self.coin_counter > 0:
self.coin_counter -= 1 # deduct coin counter
x_pos = self.rect.left + int(self.rect.width * 0.25)
n_coin = Coin(x_pos, 0, self.screen) # create new coin and move it to above the block
n_coin.rect.bottom = self.item_location
self.coins.append([n_coin, self.speed * 2]) # coin object, and speed
self.map_group.add(n_coin)
if not self.coin_counter > 0:
self.set_blank()
self.sound.play()
return n_coin.points
elif self.rubble_group is not None and other.state_info['big']:
rubble_img = image.load('images/environment/super-mario-bricks-rubble.png')
speeds = [(-15, 5), (-10, 5), (10, 5), (15, 5)]
for speed in speeds:
rubble = BlockRubble(self.rect.x, self.rect.y, rubble_img, speed[0], speed[1], self.screen)
self.rubble_group.add(rubble)
self.map_group.add(rubble)
self.break_sound.play()
self.kill()
def update_coins(self):
"""Update all coins in the air over the block"""
remove = []
for coin_num in range(len(self.coins)):
self.coins[coin_num][0].update()
self.coins[coin_num][0].rect.y += self.coins[coin_num][1] # move with each coin's speed value
self.coins[coin_num][1] += 1 # update the speed for 'gravity'
if self.coins[coin_num][0].rect.y >= self.rect.top:
self.coins[coin_num][0].kill()
remove.append(coin_num)
if remove:
for num in sorted(remove, reverse=True):
del self.coins[num]
def update(self):
"""Update the block location and any coins"""
if self.state['meta'] == CoinBlock.HIT_STATE:
if self.state['move-state'] == CoinBlock.MOVE_UP_STATE:
if self.rect.top <= self.hit_location:
self.state['move-state'] = CoinBlock.MOVE_DOWN_STATE
else:
self.rect.top += self.speed
else:
if self.rect.top >= self.std_location:
self.rect.top = self.std_location # ensure the position is exactly the same as original
self.state['move-state'] = None
self.state['meta'] = CoinBlock.STD_STATE
else:
self.rect.top -= self.speed
if self.state['blank'] and self.blank_img:
self.image = self.blank_img
self.update_coins()
class QuestionBlock(CoinBlock):
"""Represents a question block which can be hit to release an item"""
MUSHROOM = 'mushroom'
ONE_UP = '1-up'
FIRE_FLOWER = 'fire-flower'
STARMAN = 'starman'
def __init__(self, x, y, screen, map_group, game_objects, item=MUSHROOM, static_img=None):
if not static_img:
images = ['map/Question-Block-1.png', 'map/Question-Block-2.png', 'map/Question-Block-3.png']
self.animator = Animator(images)
initial_image = self.animator.get_image()
else:
initial_image = static_img
self.animator = None
self.game_objects = game_objects
if item in (QuestionBlock.MUSHROOM, QuestionBlock.FIRE_FLOWER, QuestionBlock.STARMAN, QuestionBlock.ONE_UP):
self.item = item # TODO: items
coins = None
else:
self.item = None
coins = 1
super(QuestionBlock, self).__init__(x, y, initial_image, screen, map_group, coins=coins if coins else 0)
if self.item:
self.sound = mixer.Sound('audio/Powerup-Appear.wav')
self.blank_img = image.load('map/super-mario-empty-block.png') # force blank image
self.state['blank'] = False
@classmethod
def q_block_from_tmx_obj(cls, obj, screen, map_group, game_objects):
"""Create a question block using tmx data"""
item_type = obj.properties.get('item', None)
if obj.properties.get('invisible', None):
return cls(obj.x, obj.y, screen, map_group, game_objects, item_type, static_img=obj.image)
return cls(obj.x, obj.y, screen, map_group, game_objects, item_type)
def check_hit(self, other):
points = super(QuestionBlock, self).check_hit(other)
if self.item and self.state['meta'] == CoinBlock.HIT_STATE:
obstacles, floor = self.game_objects['collide_objs'], self.game_objects['floors']
if self.item == QuestionBlock.MUSHROOM and not other.state_info['big']:
n_item = Mushroom(self.rect.x, self.rect.y, obstacles, floor, rise_from=self)
elif self.item == QuestionBlock.ONE_UP:
n_item = OneUp(self.rect.x, self.rect.y, obstacles, floor, rise_from=self)
elif self.item == QuestionBlock.FIRE_FLOWER or self.item == QuestionBlock.MUSHROOM:
n_item = FireFlower(self.rect.x, self.rect.y, obstacles, floor, rise_from=self)
else:
n_item = StarMan(self.rect.x, self.rect.y, obstacles, floor, rise_from=self)
self.game_objects['items'].add(n_item)
self.map_group.add(n_item)
self.item = None
self.state['blank'] = True
self.sound.play()
elif points:
return points
def update(self):
"""Update the question block to its next animated image"""
if not self.state['blank'] and self.animator:
self.image = self.animator.get_image()
elif self.state['blank']:
self.image = self.blank_img
super(QuestionBlock, self).update()