diff --git a/1.txt b/1.txt new file mode 100644 index 00000000..e69de29b diff --git a/save_data.json b/save_data.json new file mode 100644 index 00000000..50f1802f --- /dev/null +++ b/save_data.json @@ -0,0 +1,8 @@ +{ + "gold": 0, + "upgrades": { + "gold_shovel": false, + "extra_slot": false, + "zombie_encyclopedia": false + } +} \ No newline at end of file diff --git a/source/1.txt b/source/1.txt new file mode 100644 index 00000000..4f69b6a4 --- /dev/null +++ b/source/1.txt @@ -0,0 +1,980 @@ + + + + +我需要了解当前的关卡系统实现,以便添加黑夜关卡类型。让我搜索关卡相关的代码。 + + +toolName: search_codebase + +status: success + +query: level state game mode implementation + +我需要查看常量文件,了解当前的背景类型和游戏模式定义,以便添加黑夜关卡相关的常量。 + + +toolName: search_codebase + +status: success + +query: BACKGROUND_DAY BACKGROUND_TYPE game mode constants + +我需要查看植物类的实现,了解阳光蘑菇、喷气蘑菇和墓碑吞噬者的当前状态。 + + +toolName: search_codebase + +status: success + +query: SunShroom PuffShroom GraveBuster implementation + +我需要查看阳光蘑菇的完整实现代码,以了解其当前的行为和需要修改的地方。 + + +toolName: view_files + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\component\plant.py + +我需要查看地图组件的实现,以了解如何添加墓碑机制。 + + +toolName: view_files + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\component\map.py + +现在我已经了解了游戏的基本结构,让我开始实现黑夜关卡和新植物。首先,我需要修改level.py文件来添加黑夜环境的支持。 + + +toolName: edit_file_update + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\state\level.py + +changes: + +newStr: def setupBackground(self): + img_index = self.map_data[c.BACKGROUND_TYPE] + self.background_type = img_index + self.background = tool.GFX[c.BACKGROUND_NAME][img_index] + self.bg_rect = self.background.get_rect() + + self.level = pg.Surface((self.bg_rect.w, self.bg_rect.h)).convert() + self.viewport = tool.SCREEN.get_rect(bottom=self.bg_rect.bottom) + self.viewport.x += c.BACKGROUND_OFFSET_X + + # Add night blue filter for night levels + if self.background_type == c.BACKGROUND_NIGHT: + self.night_filter = pg.Surface((self.bg_rect.w, self.bg_rect.h)) + self.night_filter.fill((0, 0, 100)) # Dark blue color + self.night_filter.set_alpha(80) # Transparency level + +oldStr: def setupBackground(self): + img_index = self.map_data[c.BACKGROUND_TYPE] + self.background_type = img_index + self.background = tool.GFX[c.BACKGROUND_NAME][img_index] + self.bg_rect = self.background.get_rect() + + self.level = pg.Surface((self.bg_rect.w, self.bg_rect.h)).convert() + self.viewport = tool.SCREEN.get_rect(bottom=self.bg_rect.bottom) + self.viewport.x += c.BACKGROUND_OFFSET_X + + +现在我需要修改draw方法来应用黑夜滤镜。 + + +toolName: edit_file_update + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\state\level.py + +changes: + +newStr: def draw(self, surface): + self.level.blit(self.background, self.viewport, self.viewport) + + # Apply night filter for night levels + if self.background_type == c.BACKGROUND_NIGHT: + self.level.blit(self.night_filter, (0, 0)) + + surface.blit(self.level, (0,0), self.viewport) + +oldStr: def draw(self, surface): + self.level.blit(self.background, self.viewport, self.viewport) + surface.blit(self.level, (0,0), self.viewport) + + +现在我需要修改阳光蘑菇的实现,使其符合需求:小蘑菇每15秒产生15阳光,60秒后长大,之后每15秒产生25阳光。 + + +toolName: edit_file_update + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\component\plant.py + +changes: + +newStr: class SunShroom(Plant): + def __init__(self, x, y, sun_group): + Plant.__init__(self, x, y, c.SUNSHROOM, c.PLANT_HEALTH, None) + self.can_sleep = True + self.animate_interval = 200 + self.sun_timer = 0 + self.sun_group = sun_group + self.is_big = False + self.change_timer = 0 + + def loadImages(self, name, scale): + self.idle_frames = [] + self.big_frames = [] + self.sleep_frames = [] + + idle_name = name + big_name = name + 'Big' + sleep_name = name + 'Sleep' + + frame_list = [self.idle_frames, self.big_frames, self.sleep_frames] + name_list = [idle_name, big_name, sleep_name] + + for i, name in enumerate(name_list): + self.loadFrames(frame_list[i], name, 1, c.WHITE) + + self.frames = self.idle_frames + + def idling(self): + if not self.is_big: + if self.change_timer == 0: + self.change_timer = self.current_time + elif (self.current_time - self.change_timer) > 60000: # 60 seconds to grow + self.changeFrames(self.big_frames) + self.is_big = True + + # Produce sun every 15 seconds + if self.sun_timer == 0: + self.sun_timer = self.current_time + elif (self.current_time - self.sun_timer) > 15000: + # Small sunshroom produces 15 sun, big one produces 25 + sun_value = 25 if self.is_big else 15 + # Create sun with appropriate value + sun = Sun(self.rect.centerx, self.rect.bottom, self.rect.right, + self.rect.bottom + self.rect.h // 2, self.is_big) + sun.sun_value = sun_value + self.sun_group.add(sun) + self.sun_timer = self.current_time + +oldStr: class SunShroom(Plant): + def __init__(self, x, y, sun_group): + Plant.__init__(self, x, y, c.SUNSHROOM, c.PLANT_HEALTH, None) + self.can_sleep = True + self.animate_interval = 200 + self.sun_timer = 0 + self.sun_group = sun_group + self.is_big = False + self.change_timer = 0 + + def loadImages(self, name, scale): + self.idle_frames = [] + self.big_frames = [] + self.sleep_frames = [] + + idle_name = name + big_name = name + 'Big' + sleep_name = name + 'Sleep' + + frame_list = [self.idle_frames, self.big_frames, self.sleep_frames] + name_list = [idle_name, big_name, sleep_name] + + for i, name in enumerate(name_list): + self.loadFrames(frame_list[i], name, 1, c.WHITE) + + self.frames = self.idle_frames + + def idling(self): + if not self.is_big: + if self.change_timer == 0: + self.change_timer = self.current_time + elif (self.current_time - self.change_timer) > 25000: + self.changeFrames(self.big_frames) + self.is_big = True + + if self.sun_timer == 0: + self.sun_timer = self.current_time - (c.FLOWER_SUN_INTERVAL - 6000) + elif (self.current_time - self.sun_timer) > c.FLOWER_SUN_INTERVAL: + self.sun_group.add(Sun(self.rect.centerx, self.rect.bottom, self.rect.right, + self.rect.bottom + self.rect.h // 2, self.is_big)) + self.sun_timer = self.current_time + + +现在我需要修改喷气蘑菇的实现,使其符合需求:近距离(3格)喷射攻击,30秒后自动消失。 + + +toolName: edit_file_update + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\component\plant.py + +changes: + +newStr: class PuffShroom(Plant): + def __init__(self, x, y, bullet_group): + Plant.__init__(self, x, y, c.PUFFSHROOM, c.PLANT_HEALTH, bullet_group) + self.can_sleep = True + self.shoot_timer = 0 + self.life_timer = 0 # Timer for automatic disappearance + self.life_duration = 30000 # 30 seconds + + def loadImages(self, name, scale): + self.idle_frames = [] + self.sleep_frames = [] + + idle_name = name + sleep_name = name + 'Sleep' + + frame_list = [self.idle_frames, self.sleep_frames] + name_list = [idle_name, sleep_name] + + for i, name in enumerate(name_list): + self.loadFrames(frame_list[i], name, 1) + + self.frames = self.idle_frames + + def handleState(self): + # Check if PuffShroom should disappear + if self.life_timer == 0: + self.life_timer = self.current_time + elif (self.current_time - self.life_timer) > self.life_duration: + self.health = 0 # Set health to 0 to trigger removal + else: + super().handleState() + + def attacking(self): + if (self.current_time - self.shoot_timer) > 3000: + self.bullet_group.add(Bullet(self.rect.right, self.rect.y + 10, self.rect.y + 10, + c.BULLET_MUSHROOM, c.BULLET_DAMAGE_NORMAL, True)) + self.shoot_timer = self.current_time + + def canAttack(self, zombie): + # Close range attack (3 grids) + if (self.rect.x <= zombie.rect.right and + (self.rect.right + c.GRID_X_SIZE * 3 >= zombie.rect.x)): + return True + return False + +oldStr: class PuffShroom(Plant): + def __init__(self, x, y, bullet_group): + Plant.__init__(self, x, y, c.PUFFSHROOM, c.PLANT_HEALTH, bullet_group) + self.can_sleep = True + self.shoot_timer = 0 + + def loadImages(self, name, scale): + self.idle_frames = [] + self.sleep_frames = [] + + idle_name = name + sleep_name = name + 'Sleep' + + frame_list = [self.idle_frames, self.sleep_frames] + name_list = [idle_name, sleep_name] + + for i, name in enumerate(name_list): + self.loadFrames(frame_list[i], name, 1) + + self.frames = self.idle_frames + + def attacking(self): + if (self.current_time - self.shoot_timer) > 3000: + self.bullet_group.add(Bullet(self.rect.right, self.rect.y + 10, self.rect.y + 10, + c.BULLET_MUSHROOM, c.BULLET_DAMAGE_NORMAL, True)) + self.shoot_timer = self.current_time + + def canAttack(self, zombie): + if (self.rect.x <= zombie.rect.right and + (self.rect.right + c.GRID_X_SIZE * 4 >= zombie.rect.x)): + return True + return False + + +现在我需要实现墓碑和墓碑吞噬者。首先,我需要在constants.py中添加相关常量。 + + +toolName: view_files + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\constants.py + +我需要在constants.py中添加墓碑和墓碑吞噬者的相关常量。 + + +toolName: edit_file_update + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\constants.py + +changes: + +newStr: #BACKGROUND +BACKGROUND_DAY = 0 +BACKGROUND_NIGHT = 1 + +#GRAVE +GRAVE = 'Grave' +GRAVE_BUSTER = 'GraveBuster' +GRAVE_BUSTER_EAT_TIME = 5000 # 5 seconds to eat grave + +#PLANT CARD INFO +CARD_GRAVE_BUSTER = 'card_grave_buster' + +oldStr: #BACKGROUND +BACKGROUND_DAY = 0 +BACKGROUND_NIGHT = 1 + + +现在我需要在menubar.py中添加墓碑吞噬者的卡片信息。 + + +toolName: edit_file_update + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\component\menubar.py + +changes: + +newStr: card_name_list = [c.CARD_SUNFLOWER, c.CARD_PEASHOOTER, c.CARD_SNOWPEASHOOTER, c.CARD_WALLNUT, + c.CARD_CHERRYBOMB, c.CARD_THREEPEASHOOTER, c.CARD_REPEATERPEA, c.CARD_CHOMPER, + c.CARD_PUFFSHROOM, c.CARD_POTATOMINE, c.CARD_SQUASH, c.CARD_SPIKEWEED, + c.CARD_JALAPENO, c.CARD_SCAREDYSHROOM, c.CARD_SUNSHROOM, c.CARD_ICESHROOM, + c.CARD_HYPNOSHROOM, c.CARD_WALLNUT, c.CARD_REDWALLNUT, c.CARD_GRAVE_BUSTER] +plant_name_list = [c.SUNFLOWER, c.PEASHOOTER, c.SNOWPEASHOOTER, c.WALLNUT, + c.CHERRYBOMB, c.THREEPEASHOOTER, c.REPEATERPEA, c.CHOMPER, + c.PUFFSHROOM, c.POTATOMINE, c.SQUASH, c.SPIKEWEED, + c.JALAPENO, c.SCAREDYSHROOM, c.SUNSHROOM, c.ICESHROOM, + c.HYPNOSHROOM, c.WALLNUTBOWLING, c.REDWALLNUTBOWLING, c.GRAVE_BUSTER] +plant_sun_list = [50, 100, 175, 50, 150, 325, 200, 150, 0, 25, 50, 100, + 125, 25, 25, 75, 75, 0, 0, 75] +plant_frozen_time_list = [7500, 7500, 7500, 30000, 50000, 7500, 7500, 7500, 7500, 30000, + 30000, 7500, 50000, 7500, 7500, 50000, 30000, 0, 0, 7500] +all_card_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] + +oldStr: card_name_list = [c.CARD_SUNFLOWER, c.CARD_PEASHOOTER, c.CARD_SNOWPEASHOOTER, c.CARD_WALLNUT, + c.CARD_CHERRYBOMB, c.CARD_THREEPEASHOOTER, c.CARD_REPEATERPEA, c.CARD_CHOMPER, + c.CARD_PUFFSHROOM, c.CARD_POTATOMINE, c.CARD_SQUASH, c.CARD_SPIKEWEED, + c.CARD_JALAPENO, c.CARD_SCAREDYSHROOM, c.CARD_SUNSHROOM, c.CARD_ICESHROOM, + c.CARD_HYPNOSHROOM, c.CARD_WALLNUT, c.CARD_REDWALLNUT] +plant_name_list = [c.SUNFLOWER, c.PEASHOOTER, c.SNOWPEASHOOTER, c.WALLNUT, + c.CHERRYBOMB, c.THREEPEASHOOTER, c.REPEATERPEA, c.CHOMPER, + c.PUFFSHROOM, c.POTATOMINE, c.SQUASH, c.SPIKEWEED, + c.JALAPENO, c.SCAREDYSHROOM, c.SUNSHROOM, c.ICESHROOM, + c.HYPNOSHROOM, c.WALLNUTBOWLING, c.REDWALLNUTBOWLING] +plant_sun_list = [50, 100, 175, 50, 150, 325, 200, 150, 0, 25, 50, 100, + 125, 25, 25, 75, 75, 0, 0] +plant_frozen_time_list = [7500, 7500, 7500, 30000, 50000, 7500, 7500, 7500, 7500, 30000, + 30000, 7500, 50000, 7500, 7500, 50000, 30000, 0, 0] +all_card_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + + +现在我需要实现墓碑类。我将在map.py中添加墓碑相关的功能,或者创建一个新的组件类。 + + +toolName: write_to_file + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\component\grave.py + +content: __author__ = 'marble_xu' + +import pygame as pg +from .. import tool +from .. import constants as c + +class Grave(pg.sprite.Sprite): + def __init__(self, x, y): + pg.sprite.Sprite.__init__(self) + + self.name = c.GRAVE + self.frames = [] + self.frame_index = 0 + self.loadImages() + self.frame_num = len(self.frames) + + self.image = self.frames[self.frame_index] + self.rect = self.image.get_rect() + self.rect.centerx = x + self.rect.bottom = y + + self.health = 10 # Grave health + self.dead = False + + self.animate_timer = 0 + self.animate_interval = 150 + + def loadImages(self): + # Load grave images + grave_name = self.name + self.loadFrames(self.frames, grave_name, 1) + + def loadFrames(self, frame_list, name, scale): + # Load frames from sprite sheet + frame_rect = tool.GRAVE_RECT[name] + for i in range(frame_rect['frame_num']): + x = frame_rect['x'] + i * frame_rect['width'] + y = frame_rect['y'] + width = frame_rect['width'] + height = frame_rect['height'] + image = tool.get_image(tool.GFX[name], x, y, width, height, c.BLACK, scale) + frame_list.append(image) + + def update(self, game_info): + self.current_time = game_info[c.CURRENT_TIME] + self.animation() + + def animation(self): + if (self.current_time - self.animate_timer) > self.animate_interval: + self.frame_index += 1 + if self.frame_index >= self.frame_num: + self.frame_index = 0 + self.animate_timer = self.current_time + self.image = self.frames[self.frame_index] + + def setDamage(self, damage): + self.health -= damage + if self.health <= 0: + self.dead = True + self.kill() + +class GraveBuster(pg.sprite.Sprite): + def __init__(self, x, y, grave): + pg.sprite.Sprite.__init__(self) + + self.name = c.GRAVE_BUSTER + self.frames = [] + self.frame_index = 0 + self.loadImages() + self.frame_num = len(self.frames) + + self.image = self.frames[self.frame_index] + self.rect = self.image.get_rect() + self.rect.centerx = x + self.rect.bottom = y + + self.grave = grave + self.eat_timer = 0 + self.eat_duration = c.GRAVE_BUSTER_EAT_TIME + self.state = c.IDLE + + self.animate_timer = 0 + self.animate_interval = 150 + + def loadImages(self): + # Load grave buster images + buster_name = self.name + self.loadFrames(self.frames, buster_name, 1) + + def loadFrames(self, frame_list, name, scale): + # Load frames from sprite sheet + frame_rect = tool.PLANT_RECT[name] + for i in range(frame_rect['frame_num']): + x = frame_rect['x'] + i * frame_rect['width'] + y = frame_rect['y'] + width = frame_rect['width'] + height = frame_rect['height'] + image = tool.get_image(tool.GFX[name], x, y, width, height, c.BLACK, scale) + frame_list.append(image) + + def update(self, game_info): + self.current_time = game_info[c.CURRENT_TIME] + self.handleState() + self.animation() + + def handleState(self): + if self.state == c.IDLE: + self.idling() + elif self.state == c.ATTACK: + self.attacking() + + def idling(self): + # Start eating grave + self.state = c.ATTACK + self.eat_timer = self.current_time + + def attacking(self): + # Check if done eating + if (self.current_time - self.eat_timer) > self.eat_duration: + # Destroy both grave and grave buster + self.grave.setDamage(10) # Enough damage to destroy grave + self.kill() + + def animation(self): + if (self.current_time - self.animate_timer) > self.animate_interval: + self.frame_index += 1 + if self.frame_index >= self.frame_num: + self.frame_index = self.frame_num - 1 # Stay on last frame + self.animate_timer = self.current_time + self.image = self.frames[self.frame_index] + + def canAttack(self, zombie): + # Grave buster doesn't attack zombies + return False + +现在我需要在level.py中添加墓碑的生成和管理功能。 + + +toolName: edit_file_update + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\state\level.py + +changes: + +newStr: from .. import tool +from .. import constants as c +from ..component import map, plant, zombie, menubar, grave + +oldStr: from .. import tool +from .. import constants as c +from ..component import map, plant, zombie, menubar + + +现在我需要在setupGroups方法中添加墓碑组。 + + +toolName: edit_file_update + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\state\level.py + +changes: + +newStr: def setupGroups(self): + self.sun_group = pg.sprite.Group() + self.head_group = pg.sprite.Group() + self.grave_group = pg.sprite.Group() + + self.plant_groups = [] + self.zombie_groups = [] + self.hypno_zombie_groups = [] #zombies who are hypno after eating hypnoshroom + self.bullet_groups = [] + for i in range(self.map_y_len): + self.plant_groups.append(pg.sprite.Group()) + self.zombie_groups.append(pg.sprite.Group()) + self.hypno_zombie_groups.append(pg.sprite.Group()) + self.bullet_groups.append(pg.sprite.Group()) + +oldStr: def setupGroups(self): + self.sun_group = pg.sprite.Group() + self.head_group = pg.sprite.Group() + + self.plant_groups = [] + self.zombie_groups = [] + self.hypno_zombie_groups = [] #zombies who are hypno after eating hypnoshroom + self.bullet_groups = [] + for i in range(self.map_y_len): + self.plant_groups.append(pg.sprite.Group()) + self.zombie_groups.append(pg.sprite.Group()) + self.hypno_zombie_groups.append(pg.sprite.Group()) + self.bullet_groups.append(pg.sprite.Group()) + + +现在我需要在initPlay方法中添加墓碑的生成逻辑。 + + +toolName: edit_file_update + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\state\level.py + +changes: + +newStr: self.removeMouseImage() + self.setupGroups() + self.setupZombies() + self.setupCars() + + # Generate graves for night levels + if self.background_type == c.BACKGROUND_NIGHT: + self.generateGraves() + +oldStr: self.removeMouseImage() + self.setupGroups() + self.setupZombies() + self.setupCars() + + +现在我需要实现generateGraves方法,在黑夜关卡的2-5列随机生成5-8个墓碑。 + + +toolName: edit_file_update + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\state\level.py + +changes: + +newStr: def generateGraves(self): + import random + num_graves = random.randint(5, 8) + + for _ in range(num_graves): + # Random column between 2 and 5 (inclusive) + map_x = random.randint(2, 5) + # Random row between 0 and 4 (inclusive) + map_y = random.randint(0, self.map_y_len - 1) + + # Get the position for the grave + x, y = self.map.getMapGridPos(map_x, map_y) + + # Create and add the grave + new_grave = grave.Grave(x, y) + self.grave_group.add(new_grave) + + def createZombie(self, name, map_y): + x, y = self.map.getMapGridPos(0, map_y) + if name == c.NORMAL_ZOMBIE: + self.zombie_groups[map_y].add(zombie.NormalZombie(c.ZOMBIE_START_X, y, self.head_group)) + elif name == c.CONEHEAD_ZOMBIE: + self.zombie_groups[map_y].add(zombie.ConeHeadZombie(c.ZOMBIE_START_X, y, self.head_group)) + elif name == c.BUCKETHEAD_ZOMBIE: + self.zombie_groups[map_y].add(zombie.BucketHeadZombie(c.ZOMBIE_START_X, y, self.head_group)) + elif name == c.FLAG_ZOMBIE: + self.zombie_groups[map_y].add(zombie.FlagZombie(c.ZOMBIE_START_X, y, self.head_group)) + elif name == c.NEWSPAPER_ZOMBIE: + self.zombie_groups[map_y].add(zombie.NewspaperZombie(c.ZOMBIE_START_X, y, self.head_group)) + +oldStr: def createZombie(self, name, map_y): + x, y = self.map.getMapGridPos(0, map_y) + if name == c.NORMAL_ZOMBIE: + self.zombie_groups[map_y].add(zombie.NormalZombie(c.ZOMBIE_START_X, y, self.head_group)) + elif name == c.CONEHEAD_ZOMBIE: + self.zombie_groups[map_y].add(zombie.ConeHeadZombie(c.ZOMBIE_START_X, y, self.head_group)) + elif name == c.BUCKETHEAD_ZOMBIE: + self.zombie_groups[map_y].add(zombie.BucketHeadZombie(c.ZOMBIE_START_X, y, self.head_group)) + elif name == c.FLAG_ZOMBIE: + self.zombie_groups[map_y].add(zombie.FlagZombie(c.ZOMBIE_START_X, y, self.head_group)) + elif name == c.NEWSPAPER_ZOMBIE: + self.zombie_groups[map_y].add(zombie.NewspaperZombie(c.ZOMBIE_START_X, y, self.head_group)) + + +现在我需要在addPlant方法中添加墓碑吞噬者的支持。 + + +toolName: edit_file_update + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\state\level.py + +changes: + +newStr: elif self.plant_name == c.HYPNOSHROOM: + new_plant = plant.HypnoShroom(x, y) + elif self.plant_name == c.WALLNUTBOWLING: + new_plant = plant.WallNutBowling(x, y, map_y, self) + elif self.plant_name == c.REDWALLNUTBOWLING: + new_plant = plant.RedWallNutBowling(x, y) + elif self.plant_name == c.GRAVE_BUSTER: + # Check if there's a grave at this position + for g in self.grave_group: + if g.rect.collidepoint(x, y): + new_plant = grave.GraveBuster(x, y, g) + break + else: + # No grave found, can't plant + return + +oldStr: elif self.plant_name == c.HYPNOSHROOM: + new_plant = plant.HypnoShroom(x, y) + elif self.plant_name == c.WALLNUTBOWLING: + new_plant = plant.WallNutBowling(x, y, map_y, self) + elif self.plant_name == c.REDWALLNUTBOWLING: + new_plant = plant.RedWallNutBowling(x, y) + + +现在我需要在update方法中更新墓碑组,并在draw方法中绘制墓碑。 + + +toolName: edit_file_update + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\state\level.py + +changes: + +newStr: self.head_group.update(self.game_info) + self.sun_group.update(self.game_info) + self.grave_group.update(self.game_info) + +oldStr: self.head_group.update(self.game_info) + self.sun_group.update(self.game_info) + + +现在我需要在draw方法中绘制墓碑。 + + +toolName: edit_file_update + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\state\level.py + +changes: + +newStr: # Draw graves + self.grave_group.draw(surface) + + for i in range(self.map_y_len): + self.plant_groups[i].draw(surface) + self.zombie_groups[i].draw(surface) + self.hypno_zombie_groups[i].draw(surface) + self.bullet_groups[i].draw(surface) + self.drawZombieFreezeTrap(i, surface) + +oldStr: for i in range(self.map_y_len): + self.plant_groups[i].draw(surface) + self.zombie_groups[i].draw(surface) + self.hypno_zombie_groups[i].draw(surface) + self.bullet_groups[i].draw(surface) + self.drawZombieFreezeTrap(i, surface) + + +现在我需要创建一个测试关卡来验证这些功能是否正常工作。我将创建一个新的level_4.json文件作为黑夜关卡。 + + +toolName: write_to_file + +status: failed + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\data\map\level_4.json + + +文件已存在,我需要先查看其内容,然后再修改。 + + +toolName: view_files + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\data\map\level_4.json + +我需要创建一个新的黑夜关卡文件,让我使用level_5.json。 + + +toolName: write_to_file + +status: failed + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\data\map\level_5.json + + +让我查看现有的关卡文件,然后选择一个合适的编号创建新的黑夜关卡。 + + +toolName: view_folder + +status: success + +e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\data\map + +让我检查level_3.json是否已经是黑夜关卡。 + + +toolName: view_files + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\data\map\level_3.json + +让我检查是否有现有的测试文件结构。 + + +toolName: view_folder + +status: success + +e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1 + +让我查看main.py文件,了解如何启动游戏和特定关卡。 + + +toolName: view_files + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\main.py + +让我查看source/main.py文件,了解游戏的启动流程。 + + +toolName: view_files + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\main.py + +现在我需要运行游戏来测试黑夜关卡的功能。让我执行main.py来启动游戏。 + + +toolName: run_command + +status: running + +command: python main.py + +让我检查游戏的运行状态,看看是否有任何错误或异常信息。 + + +toolName: check_command_status + +status: success + + +现在我需要创建一个测试用例来验证所有功能是否正常工作。让我创建一个测试脚本。 + + +toolName: write_to_file + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\test_night_level.py + +content: import pygame as pg +import sys +import os + +# 添加source目录到路径 +sys.path.append(os.path.join(os.path.dirname(__file__), 'source')) + +from source import tool +from source import constants as c +from source.state import level + +def test_night_level(): + """测试黑夜关卡功能""" + print("=== 测试黑夜关卡功能 ===") + + # 初始化pygame + pg.init() + screen = pg.display.set_mode((c.SCREEN_WIDTH, c.SCREEN_HEIGHT)) + clock = pg.time.Clock() + + # 创建游戏控制 + game = tool.Control() + + # 创建黑夜关卡 + level_state = level.Level() + + # 模拟加载黑夜关卡 + level_state.setupLevel(3) # level_3.json是黑夜关卡 + + print("1. 测试背景类型是否为黑夜:") + print(f" background_type = {level_state.background_type}") + print(f" 预期: {c.BACKGROUND_NIGHT}") + print(f" 结果: {'通过' if level_state.background_type == c.BACKGROUND_NIGHT else '失败'}") + + print("\n2. 测试是否不生成阳光:") + print(f" produce_sun = {level_state.produce_sun}") + print(f" 预期: False") + print(f" 结果: {'通过' if not level_state.produce_sun else '失败'}") + + print("\n3. 测试墓碑生成:") + print(f" 墓碑数量: {len(level_state.grave_group)}") + print(f" 预期: 5-8个墓碑") + print(f" 结果: {'通过' if 5 <= len(level_state.grave_group) <= 8 else '失败'}") + + # 显示墓碑位置 + for i, grave in enumerate(level_state.grave_group): + print(f" 墓碑{i+1}: 位置 ({grave.map_x}, {grave.map_y})") + + print("\n4. 测试黑夜滤镜是否创建:") + print(f" night_filter存在: {hasattr(level_state, 'night_filter')}") + print(f" 结果: {'通过' if hasattr(level_state, 'night_filter') else '失败'}") + + if hasattr(level_state, 'night_filter'): + print(f" night_filter alpha: {level_state.night_filter.get_alpha()}") + print(f" 预期: 80") + print(f" 结果: {'通过' if level_state.night_filter.get_alpha() == 80 else '失败'}") + + print("\n=== 测试完成 ===") + + # 清理 + pg.quit() + +if __name__ == "__main__": + test_night_level() + +现在我需要运行测试脚本,看看是否有任何错误或异常信息。 + + +toolName: run_command + +status: success + +command: python test_night_level.py + +让我检查Level类的方法名。 + + +toolName: search_by_regex + +status: success + +query: def setup + +让我查看Level类的初始化方法,了解如何正确初始化关卡。 + + +toolName: view_files + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\state\level.py + +让我查看Level类的__init__方法,了解如何正确初始化关卡。 + + +toolName: view_files + +status: success + + +filePath: e:\my_code\git\shujvbiaozhu\PythonPlantsVsZombies-master1\source\state\level.pyModel thinking limit reached, please enter 'Continue' to get more. \ No newline at end of file diff --git a/source/__pycache__/__init__.cpython-39.pyc b/source/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 00000000..669a2f48 Binary files /dev/null and b/source/__pycache__/__init__.cpython-39.pyc differ diff --git a/source/__pycache__/constants.cpython-39.pyc b/source/__pycache__/constants.cpython-39.pyc new file mode 100644 index 00000000..01acdd03 Binary files /dev/null and b/source/__pycache__/constants.cpython-39.pyc differ diff --git a/source/__pycache__/main.cpython-39.pyc b/source/__pycache__/main.cpython-39.pyc new file mode 100644 index 00000000..c527a5ac Binary files /dev/null and b/source/__pycache__/main.cpython-39.pyc differ diff --git a/source/__pycache__/tool.cpython-39.pyc b/source/__pycache__/tool.cpython-39.pyc new file mode 100644 index 00000000..4f3ce0a6 Binary files /dev/null and b/source/__pycache__/tool.cpython-39.pyc differ diff --git a/source/component/__pycache__/__init__.cpython-39.pyc b/source/component/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 00000000..4c2349fd Binary files /dev/null and b/source/component/__pycache__/__init__.cpython-39.pyc differ diff --git a/source/component/__pycache__/grave.cpython-39.pyc b/source/component/__pycache__/grave.cpython-39.pyc new file mode 100644 index 00000000..1c93475a Binary files /dev/null and b/source/component/__pycache__/grave.cpython-39.pyc differ diff --git a/source/component/__pycache__/map.cpython-39.pyc b/source/component/__pycache__/map.cpython-39.pyc new file mode 100644 index 00000000..73be486f Binary files /dev/null and b/source/component/__pycache__/map.cpython-39.pyc differ diff --git a/source/component/__pycache__/menubar.cpython-39.pyc b/source/component/__pycache__/menubar.cpython-39.pyc new file mode 100644 index 00000000..2354b080 Binary files /dev/null and b/source/component/__pycache__/menubar.cpython-39.pyc differ diff --git a/source/component/__pycache__/plant.cpython-39.pyc b/source/component/__pycache__/plant.cpython-39.pyc new file mode 100644 index 00000000..195862da Binary files /dev/null and b/source/component/__pycache__/plant.cpython-39.pyc differ diff --git a/source/component/__pycache__/zombie.cpython-39.pyc b/source/component/__pycache__/zombie.cpython-39.pyc new file mode 100644 index 00000000..1d941c1b Binary files /dev/null and b/source/component/__pycache__/zombie.cpython-39.pyc differ diff --git a/source/component/coin.py b/source/component/coin.py new file mode 100644 index 00000000..3e15ca81 --- /dev/null +++ b/source/component/coin.py @@ -0,0 +1,55 @@ +__author__ = 'marble_xu' + +import pygame as pg +import random +from .. import tool +from .. import constants as c + +class Coin(pg.sprite.Sprite): + def __init__(self, x, y): + pg.sprite.Sprite.__init__(self) + + # 加载金币图像 + self.frames = [] + self.frame_index = 0 + self.load_images() + + self.image = self.frames[self.frame_index] + self.rect = self.image.get_rect() + self.rect.centerx = x + self.rect.bottom = y + + self.animate_timer = 0 + self.animate_interval = 100 # 动画间隔时间 + self.float_speed = 1 # 向上浮动的速度 + self.float_distance = 50 # 浮动的总距离 + self.start_y = y # 初始Y坐标 + self.collected = False + + def load_images(self): + """加载金币动画帧""" + # 尝试从GFX中获取金币图像 + if 'Coin' in tool.GFX: + self.frames = tool.GFX['Coin'] + else: + # 如果没有金币图像资源,创建一个临时的黄色圆形 + for i in range(8): + surface = pg.Surface((30, 30), pg.SRCALPHA) + pg.draw.circle(surface, c.GOLD, (15, 15), 15) + self.frames.append(surface) + + def update(self, current_time): + """更新金币状态""" + # 动画更新 + if (current_time - self.animate_timer) > self.animate_interval: + self.frame_index = (self.frame_index + 1) % len(self.frames) + self.image = self.frames[self.frame_index] + self.animate_timer = current_time + + # 向上浮动 + if self.rect.bottom > self.start_y - self.float_distance: + self.rect.y -= self.float_speed + + def draw(self, surface): + """绘制金币""" + surface.blit(self.image, self.rect) \ No newline at end of file diff --git a/source/component/grave.py b/source/component/grave.py new file mode 100644 index 00000000..592ba560 --- /dev/null +++ b/source/component/grave.py @@ -0,0 +1,134 @@ +__author__ = 'marble_xu' + +import pygame as pg +from .. import tool +from .. import constants as c + +class Grave(pg.sprite.Sprite): + def __init__(self, x, y): + pg.sprite.Sprite.__init__(self) + + self.name = c.GRAVE + self.frames = [] + self.frame_index = 0 + self.loadImages() + self.frame_num = len(self.frames) + + self.image = self.frames[self.frame_index] + self.rect = self.image.get_rect() + self.rect.centerx = x + self.rect.bottom = y + + self.health = 10 # Grave health + self.dead = False + + self.animate_timer = 0 + self.animate_interval = 150 + + def loadImages(self): + # Load grave images + grave_name = self.name + self.loadFrames(self.frames, grave_name, 1) + + def loadFrames(self, frame_list, name, scale): + # Load frames from sprite sheet + frame_rect = tool.GRAVE_RECT[name] + for i in range(frame_rect['frame_num']): + x = frame_rect['x'] + i * frame_rect['width'] + y = frame_rect['y'] + width = frame_rect['width'] + height = frame_rect['height'] + image = tool.get_image(tool.GFX[name], x, y, width, height, c.BLACK, scale) + frame_list.append(image) + + def update(self, game_info): + self.current_time = game_info[c.CURRENT_TIME] + self.animation() + + def animation(self): + if (self.current_time - self.animate_timer) > self.animate_interval: + self.frame_index += 1 + if self.frame_index >= self.frame_num: + self.frame_index = 0 + self.animate_timer = self.current_time + self.image = self.frames[self.frame_index] + + def setDamage(self, damage): + self.health -= damage + if self.health <= 0: + self.dead = True + self.kill() + +class GraveBuster(pg.sprite.Sprite): + def __init__(self, x, y, grave): + pg.sprite.Sprite.__init__(self) + + self.name = c.GRAVE_BUSTER + self.frames = [] + self.frame_index = 0 + self.loadImages() + self.frame_num = len(self.frames) + + self.image = self.frames[self.frame_index] + self.rect = self.image.get_rect() + self.rect.centerx = x + self.rect.bottom = y + + self.grave = grave + self.eat_timer = 0 + self.eat_duration = c.GRAVE_BUSTER_EAT_TIME + self.state = c.IDLE + + self.animate_timer = 0 + self.animate_interval = 150 + + def loadImages(self): + # Load grave buster images + buster_name = self.name + self.loadFrames(self.frames, buster_name, 1) + + def loadFrames(self, frame_list, name, scale): + # Load frames from sprite sheet + frame_rect = tool.PLANT_RECT[name] + for i in range(frame_rect['frame_num']): + x = frame_rect['x'] + i * frame_rect['width'] + y = frame_rect['y'] + width = frame_rect['width'] + height = frame_rect['height'] + image = tool.get_image(tool.GFX[name], x, y, width, height, c.BLACK, scale) + frame_list.append(image) + + def update(self, game_info): + self.current_time = game_info[c.CURRENT_TIME] + self.handleState() + self.animation() + + def handleState(self): + if self.state == c.IDLE: + self.idling() + elif self.state == c.ATTACK: + self.attacking() + + def idling(self): + # Start eating grave + self.state = c.ATTACK + self.eat_timer = self.current_time + + def attacking(self): + # Check if done eating + if (self.current_time - self.eat_timer) > self.eat_duration: + # Destroy both grave and grave buster + self.grave.setDamage(10) # Enough damage to destroy grave + self.kill() + + def animation(self): + if (self.current_time - self.animate_timer) > self.animate_interval: + self.frame_index += 1 + if self.frame_index >= self.frame_num: + self.frame_index = self.frame_num - 1 # Stay on last frame + self.animate_timer = self.current_time + self.image = self.frames[self.frame_index] + + def canAttack(self, zombie): + # Grave buster doesn't attack zombies + return False \ No newline at end of file diff --git a/source/constants.py b/source/constants.py index 0c4e2b6e..a7c39822 100644 --- a/source/constants.py +++ b/source/constants.py @@ -36,9 +36,11 @@ GAME_LOSE = 'game los' GAME_VICTORY = 'game victory' LEVEL = 'level' +ENDLESS_MODE = 'endless mode' MAIN_MENU_IMAGE = 'MainMenu' OPTION_ADVENTURE = 'Adventure' +OPTION_ENDLESS = 'Endless' GAME_LOOSE_IMAGE = 'GameLoose' GAME_VICTORY_IMAGE = 'GameVictory' diff --git a/source/main.py b/source/main.py index 92fcdc19..8eca2e68 100644 --- a/source/main.py +++ b/source/main.py @@ -2,13 +2,14 @@ from . import tool from . import constants as c -from .state import mainmenu, screen, level +from .state import mainmenu, screen, level, endless def main(): game = tool.Control() state_dict = {c.MAIN_MENU: mainmenu.Menu(), c.GAME_VICTORY: screen.GameVictoryScreen(), c.GAME_LOSE: screen.GameLoseScreen(), - c.LEVEL: level.Level()} + c.LEVEL: level.Level(), + c.ENDLESS_MODE: endless.EndlessMode()} game.setup_states(state_dict, c.MAIN_MENU) game.main() \ No newline at end of file diff --git a/source/state/__pycache__/__init__.cpython-39.pyc b/source/state/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 00000000..6191044b Binary files /dev/null and b/source/state/__pycache__/__init__.cpython-39.pyc differ diff --git a/source/state/__pycache__/endless.cpython-39.pyc b/source/state/__pycache__/endless.cpython-39.pyc new file mode 100644 index 00000000..acbb24cc Binary files /dev/null and b/source/state/__pycache__/endless.cpython-39.pyc differ diff --git a/source/state/__pycache__/level.cpython-39.pyc b/source/state/__pycache__/level.cpython-39.pyc new file mode 100644 index 00000000..a9edf274 Binary files /dev/null and b/source/state/__pycache__/level.cpython-39.pyc differ diff --git a/source/state/__pycache__/mainmenu.cpython-39.pyc b/source/state/__pycache__/mainmenu.cpython-39.pyc new file mode 100644 index 00000000..38ea6ca8 Binary files /dev/null and b/source/state/__pycache__/mainmenu.cpython-39.pyc differ diff --git a/source/state/__pycache__/screen.cpython-39.pyc b/source/state/__pycache__/screen.cpython-39.pyc new file mode 100644 index 00000000..07753079 Binary files /dev/null and b/source/state/__pycache__/screen.cpython-39.pyc differ diff --git a/source/state/encyclopedia.py b/source/state/encyclopedia.py new file mode 100644 index 00000000..40edf330 --- /dev/null +++ b/source/state/encyclopedia.py @@ -0,0 +1,149 @@ +__author__ = 'marble_xu' + +import pygame as pg +from .. import tool +from .. import constants as c + +class Encyclopedia(tool.State): + def __init__(self): + tool.State.__init__(self) + self.setup_background() + self.setup_buttons() + self.setup_zombie_list() + self.setup_text() + self.scroll_offset = 0 + + def setup_background(self): + """设置背景""" + self.bg_color = c.NAVYBLUE + + def setup_buttons(self): + """设置按钮""" + self.back_button_rect = pg.Rect(600, 500, 150, 50) + + def setup_zombie_list(self): + """设置僵尸列表""" + # 这里可以根据实际游戏中的僵尸类型进行扩展 + self.zombies = [ + { + "name": "普通僵尸", + "description": "最基本的僵尸,移动速度慢,攻击力低", + "image_key": "Zombie" + }, + { + "name": "路障僵尸", + "description": "头上戴着路障,防御力更高", + "image_key": "ConeheadZombie" + }, + { + "name": "铁桶僵尸", + "description": "头上戴着铁桶,防御力非常高", + "image_key": "BucketheadZombie" + } + ] + + # 计算每个僵尸条目的位置 + self.zombie_entries = [] + for i, zombie in enumerate(self.zombies): + rect = pg.Rect(50, 100 + i * 150, 300, 120) + self.zombie_entries.append((zombie, rect)) + + def setup_text(self): + """设置文本""" + self.font = pg.font.Font(None, 36) + self.small_font = pg.font.Font(None, 24) + + def startup(self, current_time, persist): + """初始化状态""" + self.next = c.SHOP + self.persist = persist + self.game_info = persist + self.current_time = current_time + self.mouse_pos = None + self.mouse_click = False + self.selected_zombie = None + + def update(self, surface, current_time, mouse_pos, mouse_click): + """更新状态""" + self.current_time = current_time + self.mouse_pos = mouse_pos + self.mouse_click = mouse_click + + if self.mouse_click and self.mouse_pos: + self.handle_click(self.mouse_pos) + + self.draw(surface) + + def handle_click(self, mouse_pos): + """处理鼠标点击""" + x, y = mouse_pos + + # 处理返回按钮 + if self.back_button_rect.collidepoint(x, y): + self.done = True + return + + # 处理僵尸列表点击 + for zombie, rect in self.zombie_entries: + if rect.collidepoint(x, y): + self.selected_zombie = zombie + break + + def draw(self, surface): + """绘制图鉴界面""" + # 填充背景 + surface.fill(self.bg_color) + + # 绘制标题 + title_text = self.font.render("僵尸图鉴", True, c.GOLD) + title_rect = title_text.get_rect(center=(c.SCREEN_WIDTH // 2, 50)) + surface.blit(title_text, title_rect) + + # 绘制僵尸列表 + for zombie, rect in self.zombie_entries: + # 绘制条目框 + pg.draw.rect(surface, c.WHITE, rect, 2) + + # 绘制僵尸名称 + name_text = self.font.render(zombie['name'], True, c.WHITE) + name_rect = name_text.get_rect(topleft=(rect.x + 10, rect.y + 10)) + surface.blit(name_text, name_rect) + + # 绘制僵尸描述 + desc_text = self.small_font.render(zombie['description'], True, c.LIGHTYELLOW) + desc_rect = desc_text.get_rect(topleft=(rect.x + 10, rect.y + 50)) + surface.blit(desc_text, desc_rect) + + # 绘制返回按钮 + pg.draw.rect(surface, c.RED, self.back_button_rect) + back_text = self.font.render("返回商店", True, c.WHITE) + back_rect = back_text.get_rect(center=self.back_button_rect.center) + surface.blit(back_text, back_rect) + + # 绘制选中的僵尸详细信息 + if self.selected_zombie: + self.draw_zombie_detail(surface, self.selected_zombie) + + def draw_zombie_detail(self, surface, zombie): + """绘制选中僵尸的详细信息""" + # 绘制详细信息面板 + detail_rect = pg.Rect(400, 100, 350, 350) + pg.draw.rect(surface, c.WHITE, detail_rect, 3) + + # 绘制僵尸名称 + name_text = self.font.render(zombie['name'], True, c.GOLD) + name_rect = name_text.get_rect(center=(detail_rect.centerx, detail_rect.y + 30)) + surface.blit(name_text, name_rect) + + # 绘制僵尸图像 + if zombie['image_key'] in tool.GFX: + zombie_image = tool.GFX[zombie['image_key']][0] # 取第一张帧 + # 缩放图像 + scaled_image = pg.transform.scale(zombie_image, (150, 150)) + image_rect = scaled_image.get_rect(center=(detail_rect.centerx, detail_rect.y + 150)) + surface.blit(scaled_image, image_rect) + + # 绘制僵尸描述 + desc_text = self.small_font.render(zombie['description'], True, c.LIGHTYELLOW) + desc_rect = desc_text.get_rect(center=(detail_rect.centerx, detail_rect.y + 300)) + surface.blit(desc_text, desc_rect) \ No newline at end of file diff --git a/source/state/endless.py b/source/state/endless.py new file mode 100644 index 00000000..6a661fb5 --- /dev/null +++ b/source/state/endless.py @@ -0,0 +1,311 @@ +__author__ = 'marble_xu' + +import os +import json +import pygame as pg +import math +from .. import tool +from .. import constants as c +from ..component import map, plant, zombie, menubar + +class EndlessMode(tool.State): + def __init__(self): + tool.State.__init__(self) + + def startup(self, current_time, persist): + self.game_info = persist + self.persist = self.game_info + self.game_info[c.CURRENT_TIME] = current_time + self.map_y_len = c.GRID_Y_LEN + self.map = map.Map(c.GRID_X_LEN, self.map_y_len) + + # Endless mode specific variables + self.current_wave = 1 + self.threat_level = 1.0 + self.wave_in_progress = False + self.interval_timer = 0 + self.interval_duration = 10000 # 10 seconds + self.zombies_spawned = 0 + self.zombies_killed = 0 + self.zombies_in_wave = 0 + + self.loadMap() + self.setupBackground() + self.initState() + + def loadMap(self): + # For endless mode, we'll use a default map + self.map_data = { + c.BACKGROUND_TYPE: c.BACKGROUND_DAY, + c.CHOOSEBAR_TYPE: c.CHOOSEBAR_STATIC, + c.INIT_SUN_NAME: 50, + c.CARD_POOL: ['SunFlower', 'Peashooter', 'SnowPea', 'WallNut', 'CherryBomb'], + c.ZOMBIE_LIST: [] # We'll generate zombies dynamically + } + + def setupBackground(self): + img_index = self.map_data[c.BACKGROUND_TYPE] + self.background_type = img_index + self.background = tool.GFX[c.BACKGROUND_NAME][img_index] + self.bg_rect = self.background.get_rect() + + self.level = pg.Surface((self.bg_rect.w, self.bg_rect.h)).convert() + self.viewport = tool.SCREEN.get_rect(bottom=self.bg_rect.bottom) + self.viewport.x += c.BACKGROUND_OFFSET_X + + def setupGroups(self): + self.sun_group = pg.sprite.Group() + self.head_group = pg.sprite.Group() + + self.plant_groups = [] + self.zombie_groups = [] + self.hypno_zombie_groups = [] #zombies who are hypno after eating hypnoshroom + self.bullet_groups = [] + for i in range(self.map_y_len): + self.plant_groups.append(pg.sprite.Group()) + self.zombie_groups.append(pg.sprite.Group()) + self.hypno_zombie_groups.append(pg.sprite.Group()) + self.bullet_groups.append(pg.sprite.Group()) + + def initState(self): + if c.CHOOSEBAR_TYPE in self.map_data: + self.bar_type = self.map_data[c.CHOOSEBAR_TYPE] + else: + self.bar_type = c.CHOOSEBAR_STATIC + + if self.bar_type == c.CHOOSEBAR_STATIC: + self.initChoose() + else: + card_pool = menubar.getCardPool(self.map_data[c.CARD_POOL]) + self.initPlay(card_pool) + if self.bar_type == c.CHOSSEBAR_BOWLING: + self.initBowlingMap() + + def initChoose(self): + self.state = c.CHOOSE + self.panel = menubar.Panel(menubar.all_card_list, self.map_data[c.INIT_SUN_NAME]) + + def choose(self, mouse_pos, mouse_click): + if mouse_pos and mouse_click[0]: + self.panel.checkCardClick(mouse_pos) + if self.panel.checkStartButtonClick(mouse_pos): + self.initPlay(self.panel.getSelectedCards()) + + def initPlay(self, card_list): + self.state = c.PLAY + if self.bar_type == c.CHOOSEBAR_STATIC: + self.menubar = menubar.MenuBar(card_list, self.map_data[c.INIT_SUN_NAME]) + else: + self.menubar = menubar.MoveBar(card_list) + self.drag_plant = False + self.hint_image = None + self.hint_plant = False + if self.background_type == c.BACKGROUND_DAY and self.bar_type == c.CHOOSEBAR_STATIC: + self.produce_sun = True + else: + self.produce_sun = False + self.sun_timer = self.current_time + + self.removeMouseImage() + self.setupGroups() + self.setupCars() + + # Start first wave + self.startNewWave() + + def setupCars(self): + self.cars = [] + for i in range(self.map_y_len): + _, y = self.map.getMapGridPos(0, i) + self.cars.append(plant.Car(-25, y+20, i)) + + def startNewWave(self): + self.wave_in_progress = True + self.zombies_spawned = 0 + self.zombies_killed = 0 + + # Calculate number of zombies in this wave + self.zombies_in_wave = max(5, int(3 * self.current_wave * self.threat_level)) + + # Generate zombie list for this wave + self.generateZombieWave() + + print(f"Wave {self.current_wave} started! Threat Level: {self.threat_level:.1f}") + + def generateZombieWave(self): + self.zombie_list = [] + spawn_interval = 1000 # Base spawn interval + + for i in range(self.zombies_in_wave): + # Determine zombie type based on threat level + zombie_type = self.getZombieType() + # Random row + map_y = i % self.map_y_len + # Calculate spawn time + spawn_time = i * spawn_interval + + self.zombie_list.append((spawn_time, zombie_type, map_y)) + + def getZombieType(self): + import random + + if self.threat_level < 5: + # Only normal and conehead zombies + return random.choice([c.NORMAL_ZOMBIE, c.CONEHEAD_ZOMBIE]) + elif 5 <= self.threat_level < 10: + # High frequency buckethead zombies + zombie_types = [c.NORMAL_ZOMBIE, c.CONEHEAD_ZOMBIE, c.BUCKETHEAD_ZOMBIE] + weights = [0.3, 0.3, 0.4] # 40% buckethead + return random.choices(zombie_types, weights=weights)[0] + else: + # Squad generation + return self.generateSquad() + + def generateSquad(self): + import random + + # Randomly choose a squad type + squad_types = [ + [c.BUCKETHEAD_ZOMBIE, c.NORMAL_ZOMBIE, c.NORMAL_ZOMBIE], # Tank with guards + [c.CONEHEAD_ZOMBIE, c.CONEHEAD_ZOMBIE, c.NORMAL_ZOMBIE], # Mixed squad + [c.BUCKETHEAD_ZOMBIE, c.CONEHEAD_ZOMBIE, c.NORMAL_ZOMBIE] # Stronger mixed + ] + + return random.choice(random.choice(squad_types)) + + def createZombie(self, zombie_name, map_y): + # Calculate enhanced attributes based on threat level + if zombie_name == c.NORMAL_ZOMBIE: + base_health = c.NORMAL_HEALTH + elif zombie_name == c.CONEHEAD_ZOMBIE: + base_health = c.CONEHEAD_HEALTH + elif zombie_name == c.BUCKETHEAD_ZOMBIE: + base_health = c.BUCKETHEAD_HEALTH + elif zombie_name == c.FLAG_ZOMBIE: + base_health = c.FLAG_HEALTH + elif zombie_name == c.NEWSPAPER_ZOMBIE: + base_health = c.NEWSPAPER_HEALTH + else: + base_health = c.NORMAL_HEALTH + + # Enhanced health and speed + enhanced_health = int(base_health * self.threat_level) + enhanced_speed = 1 * math.sqrt(self.threat_level) + + # Create zombie + x, y = self.map.getMapGridPos(c.GRID_X_LEN, map_y) + new_zombie = zombie.Zombie(x, y, zombie_name, enhanced_health, self.head_group) + new_zombie.speed = enhanced_speed + + self.zombie_groups[map_y].add(new_zombie) + self.zombies_spawned += 1 + + def checkWaveComplete(self): + # Check if all zombies in wave are killed + total_zombies = sum(len(group) for group in self.zombie_groups) + sum(len(group) for group in self.hypno_zombie_groups) + + if total_zombies == 0 and self.zombies_spawned >= self.zombies_in_wave: + self.wave_in_progress = False + self.interval_timer = self.current_time + + # Increase threat level + self.threat_level += 0.1 + self.current_wave += 1 + + print(f"Wave {self.current_wave - 1} completed! Next wave in 10 seconds...") + + def update(self, surface, current_time, mouse_pos, mouse_click): + self.current_time = self.game_info[c.CURRENT_TIME] = current_time + + if self.state == c.CHOOSE: + self.choose(mouse_pos, mouse_click) + elif self.state == c.PLAY: + self.play(mouse_pos, mouse_click) + + self.draw(surface) + + def play(self, mouse_pos, mouse_click): + # Handle sun production + if self.produce_sun: + if (self.current_time - self.sun_timer) > c.PRODUCE_SUN_INTERVAL: + self.sun_timer = self.current_time + # Create sun + pass + + # Handle zombie spawning + if self.wave_in_progress and len(self.zombie_list) > 0: + data = self.zombie_list[0] + if data[0] <= (self.current_time - self.interval_timer): + self.createZombie(data[1], data[2]) + self.zombie_list.remove(data) + + # Handle interval between waves + elif not self.wave_in_progress: + if (self.current_time - self.interval_timer) > self.interval_duration: + self.startNewWave() + + # Update all groups + for i in range(self.map_y_len): + self.bullet_groups[i].update(self.game_info) + self.plant_groups[i].update(self.game_info) + self.zombie_groups[i].update(self.game_info) + self.hypno_zombie_groups[i].update(self.game_info) + for zombie in self.hypno_zombie_groups[i]: + if zombie.rect.x > c.SCREEN_WIDTH: + zombie.kill() + + self.head_group.update(self.game_info) + self.sun_group.update(self.game_info) + + # Check wave completion + self.checkWaveComplete() + + def draw(self, surface): + surface.blit(self.background, self.bg_rect) + + # Draw wave information + font = pg.font.Font(None, 36) + wave_text = font.render(f"Wave: {self.current_wave}", True, c.WHITE) + threat_text = font.render(f"Threat: {self.threat_level:.1f}", True, c.WHITE) + + surface.blit(wave_text, (10, 10)) + surface.blit(threat_text, (10, 50)) + + # Draw map and plants + self.map.draw(self.level) + for i in range(self.map_y_len): + for plant in self.plant_groups[i]: + plant.draw(self.level) + + # Draw zombies + for i in range(self.map_y_len): + for zombie in self.zombie_groups[i]: + zombie.draw(self.level) + for zombie in self.hypno_zombie_groups[i]: + zombie.draw(self.level) + + # Draw bullets + for i in range(self.map_y_len): + for bullet in self.bullet_groups[i]: + bullet.draw(self.level) + + # Draw UI elements + surface.blit(self.level, self.viewport, self.viewport) + + if self.state == c.CHOOSE: + self.panel.draw(surface) + else: + self.menubar.update(self.game_info) + self.menubar.draw(surface) + + # Draw sun + for sun in self.sun_group: + sun.draw(surface) + + # Draw hint + if self.hint_image: + surface.blit(self.hint_image, self.hint_rect) + + # Draw mouse image + if self.mouse_image: + surface.blit(self.mouse_image, self.mouse_rect) \ No newline at end of file diff --git a/source/state/mainmenu.py b/source/state/mainmenu.py index 6ac345fe..88b0e021 100644 --- a/source/state/mainmenu.py +++ b/source/state/mainmenu.py @@ -24,46 +24,83 @@ def setupBackground(self): self.bg_rect.y = 0 def setupOption(self): - self.option_frames = [] - frame_names = [c.OPTION_ADVENTURE + '_0', c.OPTION_ADVENTURE + '_1'] + # Adventure mode option + self.adventure_option_frames = [] + adventure_frame_names = [c.OPTION_ADVENTURE + '_0', c.OPTION_ADVENTURE + '_1'] frame_rect = [0, 0, 165, 77] - for name in frame_names: - self.option_frames.append(tool.get_image(tool.GFX[name], *frame_rect, c.BLACK, 1.7)) + for name in adventure_frame_names: + self.adventure_option_frames.append(tool.get_image(tool.GFX[name], *frame_rect, c.BLACK, 1.7)) - self.option_frame_index = 0 - self.option_image = self.option_frames[self.option_frame_index] - self.option_rect = self.option_image.get_rect() - self.option_rect.x = 435 - self.option_rect.y = 75 + self.adventure_option_frame_index = 0 + self.adventure_option_image = self.adventure_option_frames[self.adventure_option_frame_index] + self.adventure_option_rect = self.adventure_option_image.get_rect() + self.adventure_option_rect.x = 435 + self.adventure_option_rect.y = 75 + + # Endless mode option + self.endless_option_frames = [] + endless_frame_names = [c.OPTION_ENDLESS + '_0', c.OPTION_ENDLESS + '_1'] + + for name in endless_frame_names: + self.endless_option_frames.append(tool.get_image(tool.GFX[name], *frame_rect, c.BLACK, 1.7)) + + self.endless_option_frame_index = 0 + self.endless_option_image = self.endless_option_frames[self.endless_option_frame_index] + self.endless_option_rect = self.endless_option_image.get_rect() + self.endless_option_rect.x = 435 + self.endless_option_rect.y = 180 self.option_start = 0 self.option_timer = 0 - self.option_clicked = False + self.adventure_option_clicked = False + self.endless_option_clicked = False def checkOptionClick(self, mouse_pos): x, y = mouse_pos - if(x >= self.option_rect.x and x <= self.option_rect.right and - y >= self.option_rect.y and y <= self.option_rect.bottom): - self.option_clicked = True + # Check adventure mode click + if(x >= self.adventure_option_rect.x and x <= self.adventure_option_rect.right and + y >= self.adventure_option_rect.y and y <= self.adventure_option_rect.bottom): + self.adventure_option_clicked = True + self.option_timer = self.option_start = self.current_time + self.next = c.LEVEL + return True + # Check endless mode click + elif(x >= self.endless_option_rect.x and x <= self.endless_option_rect.right and + y >= self.endless_option_rect.y and y <= self.endless_option_rect.bottom): + self.endless_option_clicked = True self.option_timer = self.option_start = self.current_time + self.next = c.ENDLESS_MODE + return True return False def update(self, surface, current_time, mouse_pos, mouse_click): self.current_time = self.game_info[c.CURRENT_TIME] = current_time - if not self.option_clicked: + # Handle adventure mode button + if not self.adventure_option_clicked and not self.endless_option_clicked: if mouse_pos: self.checkOptionClick(mouse_pos) else: - if(self.current_time - self.option_timer) > 200: - self.option_frame_index += 1 - if self.option_frame_index >= 2: - self.option_frame_index = 0 - self.option_timer = self.current_time - self.option_image = self.option_frames[self.option_frame_index] + # Animate clicked button + if self.adventure_option_clicked: + if(self.current_time - self.option_timer) > 200: + self.adventure_option_frame_index += 1 + if self.adventure_option_frame_index >= 2: + self.adventure_option_frame_index = 0 + self.option_timer = self.current_time + self.adventure_option_image = self.adventure_option_frames[self.adventure_option_frame_index] + elif self.endless_option_clicked: + if(self.current_time - self.option_timer) > 200: + self.endless_option_frame_index += 1 + if self.endless_option_frame_index >= 2: + self.endless_option_frame_index = 0 + self.option_timer = self.current_time + self.endless_option_image = self.endless_option_frames[self.endless_option_frame_index] + if(self.current_time - self.option_start) > 1300: self.done = True surface.blit(self.bg_image, self.bg_rect) - surface.blit(self.option_image, self.option_rect) \ No newline at end of file + surface.blit(self.adventure_option_image, self.adventure_option_rect) + surface.blit(self.endless_option_image, self.endless_option_rect) \ No newline at end of file diff --git a/source/state/shop.py b/source/state/shop.py new file mode 100644 index 00000000..6ad1205c --- /dev/null +++ b/source/state/shop.py @@ -0,0 +1,186 @@ +__author__ = 'marble_xu' + +import pygame as pg +import json +import os +from .. import tool +from .. import constants as c + +class Shop(tool.State): + def __init__(self): + tool.State.__init__(self) + self.save_file_path = 'save_data.json' + self.load_save_data() + self.setup_shop_items() + self.setup_background() + self.setup_buttons() + self.setup_text() + + def load_save_data(self): + """加载保存数据""" + if os.path.exists(self.save_file_path): + with open(self.save_file_path, 'r') as f: + self.save_data = json.load(f) + else: + self.save_data = { + "gold": 0, + "upgrades": { + "gold_shovel": False, + "extra_slot": False, + "zombie_encyclopedia": False + } + } + self.save_save_data() + + def save_save_data(self): + """保存数据到文件""" + with open(self.save_file_path, 'w') as f: + json.dump(self.save_data, f, indent=2) + + def setup_shop_items(self): + """设置商店商品""" + self.items = [ + { + "name": "黄金铲子", + "description": "铲除植物时返还25%的阳光消耗", + "price": 1000, + "upgrade_key": "gold_shovel", + "rect": pg.Rect(100, 100, 200, 100) + }, + { + "name": "额外卡槽", + "description": "永久解锁第7个植物卡槽", + "price": 5000, + "upgrade_key": "extra_slot", + "rect": pg.Rect(400, 100, 200, 100) + }, + { + "name": "僵尸图鉴", + "description": "解锁僵尸图鉴功能", + "price": 500, + "upgrade_key": "zombie_encyclopedia", + "rect": pg.Rect(100, 250, 200, 100) + } + ] + + def setup_background(self): + """设置背景""" + self.bg_color = c.NAVYBLUE + + def setup_buttons(self): + """设置按钮""" + self.back_button_rect = pg.Rect(600, 500, 150, 50) + self.encyclopedia_button_rect = pg.Rect(400, 250, 200, 100) + + def setup_text(self): + """设置文本""" + self.font = pg.font.Font(None, 36) + self.small_font = pg.font.Font(None, 24) + + def startup(self, current_time, persist): + """初始化状态""" + self.next = c.MAIN_MENU + self.persist = persist + self.game_info = persist + self.current_time = current_time + self.mouse_pos = None + self.mouse_click = False + + def update(self, surface, current_time, mouse_pos, mouse_click): + """更新状态""" + self.current_time = current_time + self.mouse_pos = mouse_pos + self.mouse_click = mouse_click + + if self.mouse_click and self.mouse_pos: + self.handle_click(self.mouse_pos) + + self.draw(surface) + + def handle_click(self, mouse_pos): + """处理鼠标点击""" + x, y = mouse_pos + + # 处理返回按钮 + if self.back_button_rect.collidepoint(x, y): + self.done = True + return + + # 处理图鉴按钮(如果已购买) + if self.save_data['upgrades']['zombie_encyclopedia'] and \ + self.encyclopedia_button_rect.collidepoint(x, y): + self.next = c.ENCYCLOPEDIA + self.done = True + return + + # 处理商品购买 + for item in self.items: + if item['rect'].collidepoint(x, y): + self.attempt_purchase(item) + break + + def attempt_purchase(self, item): + """尝试购买商品""" + upgrade_key = item['upgrade_key'] + if not self.save_data['upgrades'][upgrade_key]: + if self.save_data['gold'] >= item['price']: + # 购买成功 + self.save_data['gold'] -= item['price'] + self.save_data['upgrades'][upgrade_key] = True + self.save_save_data() + + def draw(self, surface): + """绘制商店界面""" + # 填充背景 + surface.fill(self.bg_color) + + # 绘制标题 + title_text = self.font.render("疯狂戴夫的商店", True, c.GOLD) + title_rect = title_text.get_rect(center=(c.SCREEN_WIDTH // 2, 50)) + surface.blit(title_text, title_rect) + + # 绘制金币数量 + gold_text = self.font.render(f"金币: {self.save_data['gold']}", True, c.YELLOW) + gold_rect = gold_text.get_rect(topleft=(50, 50)) + surface.blit(gold_text, gold_rect) + + # 绘制商品 + for item in self.items: + is_purchased = self.save_data['upgrades'][item['upgrade_key']] + + # 绘制商品框 + if is_purchased: + pg.draw.rect(surface, c.GREEN, item['rect'], 3) + else: + pg.draw.rect(surface, c.WHITE, item['rect'], 3) + + # 绘制商品名称 + name_text = self.font.render(item['name'], True, c.WHITE) + name_rect = name_text.get_rect(center=(item['rect'].centerx, item['rect'].y + 30)) + surface.blit(name_text, name_rect) + + # 绘制商品价格 + if not is_purchased: + price_text = self.small_font.render(f"价格: {item['price']} 金币", True, c.YELLOW) + else: + price_text = self.small_font.render("已购买", True, c.GREEN) + price_rect = price_text.get_rect(center=(item['rect'].centerx, item['rect'].y + 60)) + surface.blit(price_text, price_rect) + + # 绘制商品描述 + desc_text = self.small_font.render(item['description'], True, c.LIGHTYELLOW) + desc_rect = desc_text.get_rect(center=(item['rect'].centerx, item['rect'].y + 90)) + surface.blit(desc_text, desc_rect) + + # 绘制返回按钮 + pg.draw.rect(surface, c.RED, self.back_button_rect) + back_text = self.font.render("返回主菜单", True, c.WHITE) + back_rect = back_text.get_rect(center=self.back_button_rect.center) + surface.blit(back_text, back_rect) + + # 如果已购买图鉴,绘制图鉴按钮 + if self.save_data['upgrades']['zombie_encyclopedia']: + pg.draw.rect(surface, c.BLUE, self.encyclopedia_button_rect) + ency_text = self.font.render("僵尸图鉴", True, c.WHITE) + ency_rect = ency_text.get_rect(center=self.encyclopedia_button_rect.center) + surface.blit(ency_text, ency_rect) \ No newline at end of file diff --git a/test_endless_mode.py b/test_endless_mode.py new file mode 100644 index 00000000..9015ee38 --- /dev/null +++ b/test_endless_mode.py @@ -0,0 +1,128 @@ +import pygame as pg +import sys +import os + +# Add the source directory to the path +sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'source')) + +from source import constants as c +from source.state.endless import EndlessMode +from source import tool + +def test_endless_mode_initialization(): + """Test that endless mode initializes correctly""" + print("Testing endless mode initialization...") + + # Create game info + game_info = { + c.CURRENT_TIME: pg.time.get_ticks(), + c.LEVEL_NUM: 1 + } + + # Create endless mode state + endless_mode = EndlessMode() + endless_mode.startup(pg.time.get_ticks(), game_info) + + # Verify initial state + assert endless_mode.current_wave == 1 + assert endless_mode.threat_level == 1.0 + assert not endless_mode.wave_in_progress + + print("✓ Endless mode initialization test passed") + +def test_threat_level_scaling(): + """Test that threat level scales correctly""" + print("\nTesting threat level scaling...") + + # Create game info + game_info = { + c.CURRENT_TIME: pg.time.get_ticks(), + c.LEVEL_NUM: 1 + } + + # Create endless mode state + endless_mode = EndlessMode() + endless_mode.startup(pg.time.get_ticks(), game_info) + + # Simulate wave completion + initial_threat = endless_mode.threat_level + endless_mode.threat_level += 0.1 + + assert endless_mode.threat_level == initial_threat + 0.1 + assert endless_mode.threat_level > 1.0 + + print("✓ Threat level scaling test passed") + +def test_zombie_generation(): + """Test that zombies are generated with correct attributes""" + print("\nTesting zombie generation...") + + # Create game info + game_info = { + c.CURRENT_TIME: pg.time.get_ticks(), + c.LEVEL_NUM: 1 + } + + # Create endless mode state + endless_mode = EndlessMode() + endless_mode.startup(pg.time.get_ticks(), game_info) + + # Test zombie type selection + zombie_type = endless_mode.getZombieType() + assert zombie_type in [c.NORMAL_ZOMBIE, c.CONEHEAD_ZOMBIE, c.BUCKETHEAD_ZOMBIE] + + print("✓ Zombie generation test passed") + +def test_zombie_attribute_scaling(): + """Test that zombie attributes scale with threat level""" + print("\nTesting zombie attribute scaling...") + + # Create game info + game_info = { + c.CURRENT_TIME: pg.time.get_ticks(), + c.LEVEL_NUM: 1 + } + + # Create endless mode state + endless_mode = EndlessMode() + endless_mode.startup(pg.time.get_ticks(), game_info) + + # Test with different threat levels + test_cases = [1.0, 5.0, 10.0, 15.0] + + for threat in test_cases: + endless_mode.threat_level = threat + + # Test normal zombie + normal_health = int(c.NORMAL_HEALTH * threat) + normal_speed = 1 * (threat ** 0.5) + + # Test conehead zombie + conehead_health = int(c.CONEHEAD_HEALTH * threat) + conehead_speed = 1 * (threat ** 0.5) + + assert normal_health > 0 + assert normal_speed > 0 + assert conehead_health > 0 + assert conehead_speed > 0 + + print("✓ Zombie attribute scaling test passed") + +if __name__ == "__main__": + print("Running endless mode tests...") + print("=" * 50) + + # Initialize pygame once for all tests + pg.init() + pg.display.set_mode(c.SCREEN_SIZE) + + try: + test_endless_mode_initialization() + test_threat_level_scaling() + test_zombie_generation() + test_zombie_attribute_scaling() + + print("=" * 50) + print("All tests passed! ✓") + finally: + pg.quit() \ No newline at end of file diff --git a/test_night_level.py b/test_night_level.py new file mode 100644 index 00000000..4754f9f2 --- /dev/null +++ b/test_night_level.py @@ -0,0 +1,64 @@ +import pygame as pg +import sys +import os + +# 添加source目录到路径 +sys.path.append(os.path.join(os.path.dirname(__file__), 'source')) + +from source import tool +from source import constants as c +from source.state import level + +def test_night_level(): + """测试黑夜关卡功能""" + print("=== 测试黑夜关卡功能 ===") + + # 初始化pygame + pg.init() + screen = pg.display.set_mode((c.SCREEN_WIDTH, c.SCREEN_HEIGHT)) + clock = pg.time.Clock() + + # 创建游戏控制 + game = tool.Control() + + # 创建黑夜关卡 + level_state = level.Level() + + # 模拟加载黑夜关卡 + level_state.setupLevel(3) # level_3.json是黑夜关卡 + + print("1. 测试背景类型是否为黑夜:") + print(f" background_type = {level_state.background_type}") + print(f" 预期: {c.BACKGROUND_NIGHT}") + print(f" 结果: {'通过' if level_state.background_type == c.BACKGROUND_NIGHT else '失败'}") + + print("\n2. 测试是否不生成阳光:") + print(f" produce_sun = {level_state.produce_sun}") + print(f" 预期: False") + print(f" 结果: {'通过' if not level_state.produce_sun else '失败'}") + + print("\n3. 测试墓碑生成:") + print(f" 墓碑数量: {len(level_state.grave_group)}") + print(f" 预期: 5-8个墓碑") + print(f" 结果: {'通过' if 5 <= len(level_state.grave_group) <= 8 else '失败'}") + + # 显示墓碑位置 + for i, grave in enumerate(level_state.grave_group): + print(f" 墓碑{i+1}: 位置 ({grave.map_x}, {grave.map_y})") + + print("\n4. 测试黑夜滤镜是否创建:") + print(f" night_filter存在: {hasattr(level_state, 'night_filter')}") + print(f" 结果: {'通过' if hasattr(level_state, 'night_filter') else '失败'}") + + if hasattr(level_state, 'night_filter'): + print(f" night_filter alpha: {level_state.night_filter.get_alpha()}") + print(f" 预期: 80") + print(f" 结果: {'通过' if level_state.night_filter.get_alpha() == 80 else '失败'}") + + print("\n=== 测试完成 ===") + + # 清理 + pg.quit() + +if __name__ == "__main__": + test_night_level() \ No newline at end of file