diff --git a/level.py b/level.py new file mode 100644 index 0000000..5b6c7b2 --- /dev/null +++ b/level.py @@ -0,0 +1,246 @@ +import pygame +from settings import * +from tile import Tile +from player import Player + +from support import * +from random import choice, randint +from weapon import Weapon +from ui import UI +from enemy import Enemy +from particles import AnimationPlayer +from magic import MagicPlayer +from upgrade import Upgrade + +class Level: + def __init__(self): + + # 获取游戏窗口的显示表面(surface) + self.display_surface = pygame.display.get_surface() + # 标记游戏是否处于暂停状态 + self.game_paused = False + + # 可见精灵组,用于管理在摄像机视野内可见的精灵 + self.visible_sprites = YSortCameraGroup() + # 障碍物精灵组,用于管理所有的障碍物精灵 + self.obstacle_sprites = pygame.sprite.Group() + + # 当前攻击对象 + self.current_attack = None + # 攻击精灵组,用于管理所有的攻击精灵 + self.attack_sprites = pygame.sprite.Group() + # 可被攻击的精灵组,用于管理所有可以被攻击的精灵 + self.attackable_sprites = pygame.sprite.Group() + + # 创建地图 + self.create_map() + + # UI 对象,用于管理游戏界面的显示和交互 + self.ui = UI() + # 升级对象,与玩家角色相关联,用于管理升级和技能 + self.upgrade = Upgrade(self.player) + + # 动画播放器对象,用于管理游戏中的动画效果 + self.animation_player = AnimationPlayer() + # 魔法播放器对象,与动画播放器相关联,用于管理游戏中的魔法效果 + self.magic_player = MagicPlayer(self.animation_player) + + def create_map(self): + # 定义不同类型的地图布局和图形资源路径 + layouts = { + 'boundary': import_csv_layout('../map/map_FloorBlocks.csv'), + 'grass': import_csv_layout('../map/map_Grass.csv'), + 'object': import_csv_layout('../map/map_Objects.csv'), + 'entities': import_csv_layout('../map/map_Entities.csv') + } + graphics = { + 'grass': import_folder('../graphics/Grass'), + 'objects': import_folder('../graphics/objects') + } + # 遍历不同的地图布局和样式 + for style,layout in layouts.items(): + # 遍历地图布局的每一行 + for row_index,row in enumerate(layout): + # 遍历每行中的每个元素(列 + for col_index, col in enumerate(row): + # 如果元素不为'-1',表示有地图元素需要创建 + if col != '-1': + # 计算元素在屏幕上的位置(像素坐标) + x = col_index * TILESIZE + y = row_index * TILESIZE + # 边界类型的地图元素 + if style == 'boundary': + # 创建障碍物精灵对象,设置为'invisible'类型(不可见) + Tile((x,y),[self.obstacle_sprites],'invisible') + # 草地类型的地图元素 + if style == 'grass': + # 从草地图形资源中随机选择一个图像 + random_grass_image = choice(graphics['grass']) + # 创建草地精灵对象,加入可见精灵组、障碍物精灵组和可攻击精灵组 + Tile( + (x,y), + [self.visible_sprites,self.obstacle_sprites,self.attackable_sprites], + 'grass', + random_grass_image) + # 物体类型的地图元素 + if style == 'object': + # 根据列数获取物体图形资源,创建物体精灵对象 + surf = graphics['objects'][int(col)] + Tile((x,y),[self.visible_sprites,self.obstacle_sprites],'object',surf) + # 实体类型的地图元素(角色和敌人) + if style == 'entities': + # 玩家角色 + if col == '394': + # 创建玩家对象,并加入可见精灵组 + self.player = Player( + (x,y), + [self.visible_sprites], + self.obstacle_sprites, + self.create_attack, + self.destroy_attack, + self.create_magic) + # 敌人角色 + else: + # 根据不同的角色编号选择对应的怪物名称 + if col == '390': monster_name = 'bamboo' + elif col == '391': monster_name = 'spirit' + elif col == '392': monster_name ='raccoon' + else: monster_name = 'squid' + # 创建敌人对象,并加入可见精灵组和可攻击精灵组 + Enemy( + monster_name, + (x,y), + [self.visible_sprites,self.attackable_sprites], + self.obstacle_sprites, + self.damage_player, + self.trigger_death_particles, + self.add_exp) + + # 创建武器对象,将其设为当前攻击对象 + def create_attack(self): + # 该方法用于创建武器对象,并将其设为当前攻击对象。 + self.current_attack = Weapon(self.player,[self.visible_sprites,self.attack_sprites]) + + def create_magic(self,style,strength,cost): + if style == 'heal': + # 创建治疗魔法效果 + self.magic_player.heal(self.player,strength,cost,[self.visible_sprites]) + + if style == 'flame': + # 创建火焰魔法效果 + self.magic_player.flame(self.player,cost,[self.visible_sprites,self.attack_sprites]) + + def destroy_attack(self): + # 检查当前是否存在攻击对象 + if self.current_attack: + # 销毁当前攻击对象 + self.current_attack.kill() + # 将当前攻击对象设置为 None + self.current_attack = None + + def player_attack_logic(self): + # 检查是否存在攻击精灵 + if self.attack_sprites: + # 遍历每个攻击精灵 + for attack_sprite in self.attack_sprites: + # 检测当前攻击精灵与可攻击精灵组中的精灵是否发生碰撞 + collision_sprites = pygame.sprite.spritecollide(attack_sprite,self.attackable_sprites,False) + # 如果发生碰撞,遍历碰撞到的每个精灵 + if collision_sprites: + for target_sprite in collision_sprites: + # 如果碰撞到的是草地精灵 + if target_sprite.sprite_type == 'grass': + # 获取草地精灵的中心位置和偏移量 + pos = target_sprite.rect.center + offset = pygame.math.Vector2(0,75) + # 随机生成一定数量的草叶粒子效果 + for leaf in range(randint(3,6)): + self.animation_player.create_grass_particles(pos - offset,[self.visible_sprites]) + # 销毁草地精灵 + target_sprite.kill() + # 如果碰撞到的是其他类型的精灵,执行造成伤害的操作 + else: + target_sprite.get_damage(self.player,attack_sprite.sprite_type) + + def damage_player(self,amount,attack_type): + # 如果玩家处于可受伤状态 + if self.player.vulnerable: + # 减少玩家的健康值 + self.player.health -= amount + # 设置玩家为无敌状态 + self.player.vulnerable = False + # 记录玩家受伤的时间 + self.player.hurt_time = pygame.time.get_ticks() + # 创建攻击类型的粒子效果 + self.animation_player.create_particles(attack_type,self.player.rect.center,[self.visible_sprites]) + + def trigger_death_particles(self,pos,particle_type): + # 创建指定类型的死亡粒子效果 + self.animation_player.create_particles(particle_type,pos,self.visible_sprites) + + def add_exp(self,amount): + # 增加玩家的经验值 + self.player.exp += amount + + def toggle_menu(self): + # 切换游戏菜单状态(暂停或继续) + self.game_paused = not self.game_paused + + def run(self): + # 绘制可见精灵和玩家 + self.visible_sprites.custom_draw(self.player) + # 显示用户界面(UI) + self.ui.display(self.player) + # 如果游戏暂停,显示升级界面 + if self.game_paused: + self.upgrade.display() + # 如果游戏未暂停,更新可见精灵组 + else: + self.visible_sprites.update() + # 更新敌人精灵状态 + self.visible_sprites.enemy_update(self.player) + # 处理玩家攻击逻辑 + self.player_attack_logic() + + +class YSortCameraGroup(pygame.sprite.Group): + def __init__(self): + # 调用父类的初始化方法,初始化精灵组 + super().__init__() + # 获取当前 Pygame 显示的表面(屏幕) + self.display_surface = pygame.display.get_surface() + # 计算屏幕宽度的一半 + self.half_width = self.display_surface.get_size()[0] // 2 + # 计算屏幕高度的一半 + self.half_height = self.display_surface.get_size()[1] // 2 + # 创建一个空的 Vector2 对象,用于表示摄像机的偏移量 + self.offset = pygame.math.Vector2() + # 加载地板图像并转换为适合显示的格式 + self.floor_surf = pygame.image.load('../graphics/tilemap/ground.png').convert() + # 获取地板图像的矩形区域(左上角位置为 (0, 0)) + self.floor_rect = self.floor_surf.get_rect(topleft = (0,0)) + + def custom_draw(self,player): + + # 获取摄像机偏移量 + self.offset.x = player.rect.centerx - self.half_width + self.offset.y = player.rect.centery - self.half_height + + # 绘制地板 + floor_offset_pos = self.floor_rect.topleft - self.offset + self.display_surface.blit(self.floor_surf,floor_offset_pos) + + # 按精灵的中心 Y 坐标排序并绘制精灵 + for sprite in sorted(self.sprites(),key = lambda sprite: sprite.rect.centery): + # 计算精灵在屏幕上的绘制位置 + offset_pos = sprite.rect.topleft - self.offset + # 在屏幕上绘制精灵图像 + self.display_surface.blit(sprite.image,offset_pos) + + def enemy_update(self,player): + # 从精灵组中筛选出所有的敌人精灵 + enemy_sprites = [sprite for sprite in self.sprites() if hasattr(sprite,'sprite_type') and sprite.sprite_type == 'enemy'] + # 遍历所有的敌人精灵 + for enemy in enemy_sprites: + # 调用每个敌人精灵的特定更新方法,传递玩家角色作为参数 + enemy.enemy_update(player)