You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

294 lines
12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

from ..components import info
import pygame
from .. import tools, setup
from .. import constants as C
from .. components import player,stuff,brick,box,enemy
import os
import json
class Level:
def start(self,game_info):
self.game_info = game_info
self.finished = False
self.next = 'game_over'
self.info = info.Info('level',self.game_info)
self.load_map_data()
self.setup_background()
self.setup_start_positions()
self.setup_player()
self.setup_ground_items()
self.setup_bricks_and_boxes()
self.setup_enemies()
self.setup_checkpoints()
def load_map_data(self):
file_name = 'level_1.json'
file_path = os.path.join('source/data/maps',file_name)
with open(file_path) as f:
self.map_data = json.load(f)
def setup_background(self):
self.image_name = self.map_data['image_name']
self.background = setup.GRAPHICS[self.image_name]
rect = self.background.get_rect()
self.background = pygame.transform.scale(self.background,(int(rect.width * C.BG_MULTI),
int(rect.height * C.BG_MULTI)))
self.background_rect = self.background.get_rect()
self.game_window = setup.SCREEN.get_rect()
self.game_ground = pygame.Surface((self.background_rect.width,self.background_rect.height))
def setup_start_positions(self):
self.positions = []
for data in self.map_data['maps']:
self.positions.append((data['start_x'],data['end_x'],data['player_x'],data['player_y']))
self.start_x,self.end_x,self.player_x,self.player_y = self.positions[0]
def setup_player(self):
self.player = player.Player('mario')
self.player.rect.x = self.game_window.x + self.player_x
self.player.rect.bottom = self.player_y
def setup_ground_items(self):
self.ground_items_group = pygame.sprite.Group() # 组存放多个精灵类,方便处理
for name in ['ground','pipe','step']:
for item in self.map_data[name]:
self.ground_items_group.add(stuff.Item(item['x'],item['y'],item['width'],item['height'],name))
def setup_bricks_and_boxes(self):
self.brick_group = pygame.sprite.Group()
self.box_group = pygame.sprite.Group()
self.coin_group = pygame.sprite.Group()
self.powerup_group = pygame.sprite.Group()
if'brick' in self.map_data:
for brick_data in self.map_data['brick']: # 遍历得到xy坐标以及type
x,y = brick_data['x'],brick_data['y']
brick_type = brick_data['type']
if brick_type == 0:
if 'brick_num' in brick_data:
#TODO Batch bricks
pass
else:
self.brick_group.add(brick.Brick(x, y, brick_type,None))
elif brick_type == 1:
self.brick_group.add(brick.Brick(x, y, brick_type, self.coin_group))
else:
self.brick_group.add(brick.Brick(x, y, brick_type, self.powerup_group))
if 'box' in self.map_data:
for box_data in self.map_data['box']: # 遍历得到xy坐标以及type
x, y = box_data['x'], box_data['y']
box_type = box_data['type']
if box_type == 1:
self.box_group.add(box.Box(x, y, box_type, self.coin_group))
else:
self.box_group.add(box.Box(x, y, box_type,self.powerup_group))
def setup_enemies(self):
self.dying_group = pygame.sprite.Group()
self.shell_group = pygame.sprite.Group()
self.enemy_group = pygame.sprite.Group() # 初始化
self.enemy_group_dict = {}
for enemy_group_data in self.map_data['enemy']: # 字典存放
group = pygame.sprite.Group()
for enemy_group_id,enemy_list in enemy_group_data.items():
for enemy_data in enemy_list:
group.add(enemy.create_enemy(enemy_data)) # 调用 ce
self.enemy_group_dict[enemy_group_id] = group
def setup_checkpoints(self):
self.checkpoint_group = pygame.sprite.Group()
for item in self.map_data['checkpoint']:
x,y,w,h = item['x'],item['y'],item['width'],item['height']
checkpoint_type = item['type']
enemy_groupid = item.get('enemy_groupid')
self.checkpoint_group.add(stuff.Checkpoint(x,y,w,h,checkpoint_type,enemy_groupid))
def update(self, surface, keys):
self.current_time = pygame.time.get_ticks()
self.player.update(keys)
if self.player.dead:
if self.current_time - self.player.death_timer > 3000:
self.finished = True
self.update_game_info()
else:
self.update_player_position()
self.check_checkpoints()
self.check_if_go_die()
self.update_game_window()
self.info.update()
self.brick_group.update()
self.box_group.update()
self.enemy_group.update(self)
self.dying_group.update(self)
self.shell_group.update(self)
self.coin_group.update()
self.box_group.update()
self.draw(surface)
def update_player_position(self):
# x判定
self.player.rect.x += self.player.x_vel
if self.player.rect.x < self.start_x:
self.player.rect.x = self.start_x
elif self.player.rect.right > self.end_x:
self.player.rect.right = self.end_x
self.check_x_collisions()
# y判定
if not self.player.dead:
self.player.rect.y += self.player.y_vel
self.check_y_collisions()
def check_x_collisions(self): # x方向
check_group = pygame.sprite.Group(self.ground_items_group,self.brick_group,self.box_group)
collided_sprite = pygame.sprite.spritecollideany(self.player,check_group) # 检测碰撞
if collided_sprite:
self.adjust_player_x(collided_sprite)
enemy = pygame.sprite.spritecollideany(self.player, self.enemy_group)
if enemy:
self.player.go_die()
shell = pygame.sprite.spritecollideany(self.player, self.shell_group) # 碰撞检测
if shell:
if shell.state == 'slide':
self.player.go_die()
else:
if self.player.rect.x < shell.rect.x: # 龟壳弹开方向以及速度
shell.x_vel = 10
shell.rect.x += 40
shell.direction = 1
else:
shell.x_vel = -10
shell.rect.x -= 40
shell.direction = 0
shell.state = 'slide'
def check_y_collisions(self): # y方向
ground_item = pygame.sprite.spritecollideany(self.player,self.ground_items_group)
brick = pygame.sprite.spritecollideany(self.player, self.brick_group)
box = pygame.sprite.spritecollideany(self.player, self.box_group)
enemy = pygame.sprite.spritecollideany(self.player, self.enemy_group)
if brick and box: # 判断离哪个砖块进哪个顶起
to_brick = abs(self.player.rect.centerx - brick.rect.centerx)
to_box = abs(self.player.rect.centerx - box.rect.centerx)
if to_brick > to_box:
brick = None
else:
box = None
if ground_item:
self.adjust_player_y(ground_item)
elif brick:
self.adjust_player_y(brick)
elif box:
self.adjust_player_y(box)
elif enemy:
self.enemy_group.remove(enemy)
if enemy.name == 'koopa':
self.shell_group.add(enemy) # 龟壳状态进龟壳组
else:
self.dying_group.add(enemy) # 由敌人组 → 死亡组
if self.player.y_vel < 0:
how = 'bumped'
else:
how = 'trampled'
self.player.state = 'jump'
self.player.rect.bottom = enemy.rect.top
self.player.y_vel = self.player.jump_vel * 0.8 # 力道
enemy.go_die(how)
self.check_will_fall(self.player) # 坠落检测
def adjust_player_x(self,sprite):
if self.player.rect.x < sprite.rect.x:
self.player.rect.right = sprite.rect.left # 重置位置为最左边
else:
self.player.rect.left = sprite.rect.right
self.player.x_vel = 0
def adjust_player_y(self,sprite):
if self.player.rect.bottom < sprite.rect.bottom:
self.player.y_vel = 0
self.player.rect.bottom = sprite.rect.top
self.player.state = 'walk'
else:
self.player.y_vel = 7
self.player.rect.top = sprite.rect.bottom
self.player.state = 'fall'
if sprite.name == 'box':
if sprite.state == 'rest':
sprite.go_bumped()
if sprite.name == 'brick':
if sprite.state == 'rest':
sprite.go_bumped()
def check_will_fall(self,sprite):
sprite.rect.y += 1 # 下落一个像素
check_group = pygame.sprite.Group(self.ground_items_group,self.brick_group,self.box_group) # 检测是否碰撞
collided_sprite = pygame.sprite.spritecollideany(sprite,check_group)
if not collided_sprite and sprite.state != 'jump': # 如果没有或者处于跳起则不坠落
sprite.state = 'fall'
sprite.rect.y -= 1
def update_game_window(self):
third = self.game_window.x + self.game_window.width/3
if self.player.x_vel > 0 and self.player.rect.centerx > third and self.game_window.right < self.end_x: # 判断移动
self.game_window.x += self.player.x_vel # 用窗口更新代替主角移动
self.start_x = self.game_window.x
def draw(self, surface):
self.game_ground.blit(self.background,self.game_window,self.game_window)
self.game_ground.blit(self.player.image,self.player.rect) # 将背景与人物化境
self.brick_group.draw(self.game_ground)
self.box_group.draw(self.game_ground)
self.enemy_group.draw(self.game_ground)
self.dying_group.draw(self.game_ground)
self.shell_group.draw(self.game_ground)
self.coin_group.draw(self.game_ground)
self.powerup_group.draw(self.game_ground)
surface.blit(self.game_ground,(0,0), self.game_window) # 渲染
self.info.draw(surface)
def check_checkpoints(self):
checkpoint = pygame.sprite.spritecollideany(self.player,self.checkpoint_group)
if checkpoint:
if checkpoint.checkpoint_type == 0: # checkpoint for enemy appearance # 如果检查点类型为0 释放对应敌人
self.enemy_group.add(self.enemy_group_dict[str(checkpoint.enemy_groupid)]) # 一旦某个点被触发就把对应敌人放入此组
checkpoint.kill() # 被触碰后消失
def check_if_go_die(self):
if self.player.rect.y > C.SCREEN_H:
self.player.go_die() # 判断是否掉落屏幕外
def update_game_info(self):
if self.player.dead:
self.game_info['lives'] -= 1
if self.game_info['lives'] == 0:
self.next = 'game_over'
else:
self.next = 'load_screen'