import os import json import sys import pygame as pg from random import randint, uniform, choices from .. import tool from .. import constants as c from ..component import map, plant, zombie, menubar class Level(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.pause = False self.pauseTime = 0 # 默认显然不用显示菜单 self.showLittleMenu = False # 导入地图参数 self.loadMap() self.map = map.Map(self.map_data[c.BACKGROUND_TYPE]) self.map_y_len = self.map.height self.setupBackground() self.initState() def loadMap(self): if c.LITTLEGAME_BUTTON in self.game_info and self.game_info[c.LITTLEGAME_BUTTON]: map_file = 'littleGame_' + str(self.game_info[c.LITTLEGAME_NUM]) + '.json' self.mode = c.MODE_LITTLEGAME else: map_file = 'level_' + str(self.game_info[c.LEVEL_NUM]) + '.json' self.mode = c.MODE_ADVENTURE file_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))),'resources' , 'data', 'map', map_file) # 最后一关之后应该结束了 try: f = open(file_path) self.map_data = json.load(f) f.close() except Exception as e: print("游戏结束") if self.mode == c.MODE_ADVENTURE: self.game_info[c.LEVEL_NUM] = c.START_LEVEL_NUM elif self.mode == c.MODE_LITTLEGAME: self.game_info[c.LITTLEGAME_NUM] = c.START_LITTLE_GAME_NUM self.done = True self.next = c.MAIN_MENU pg.mixer.music.stop() pg.mixer.music.load(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "music", "intro.opus")) pg.mixer.music.play(-1, 0) return if self.map_data[c.SHOVEL] == 0: self.hasShovel = False else: self.hasShovel = True # 同时播放音乐 global bgm if self.mode == c.MODE_ADVENTURE: # 冒险模式 if self.game_info[c.LEVEL_NUM] in {0, 1, 2}: # 白天关卡 bgm = 'dayLevel.opus' elif self.game_info[c.LEVEL_NUM] in {3}: # 夜晚关卡 bgm = 'nightLevel.opus' elif self.game_info[c.LEVEL_NUM] in {4}: bgm = 'poolLevel.opus' elif self.mode == c.MODE_LITTLEGAME: # 小游戏模式 if self.game_info[c.LITTLEGAME_NUM] in {1}: # 传送带大战 bgm = 'battle.opus' elif self.game_info[c.LITTLEGAME_NUM] in {2}: # 坚果保龄球 bgm = 'bowling.opus' pg.mixer.music.stop() pg.mixer.music.load(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "music", bgm)) pg.mixer.music.play(-1, 0) 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()) # 按照规则生成每一波僵尸 # 可以考虑将波刷新和一波中的僵尸生成分开 # useableZombie是指可用的僵尸种类的元组 # inevitableZombie指在本轮必然出现的僵尸,输入形式为字典: {波数1:(僵尸1, 僵尸2……), 波数2:(僵尸1, 僵尸2……)……} def createWaves(self, useableZombies, numFlags, survivalRounds=0, inevitableZombieDict=None): waves = [] self.numFlags = numFlags # 权重值 weights = [] for zombie in useableZombies: weights.append(self.createZombieInfo[zombie][1]) # 按照原版pvz设计的僵尸容量函数,是从无尽解析的,但是普通关卡也可以遵循 for wave in range(1, 10 * numFlags + 1): volume = int(int((wave + survivalRounds*20)*0.8)/2) + 1 zombieList = [] # 传送带模式应当增大僵尸容量 if (self.bar_type != c.CHOOSEBAR_STATIC): volume += volume # 大波僵尸情况 if wave % 10 == 0: # 容量增大至2.5倍 volume = int(volume*2.5) # 先生成旗帜僵尸 zombieList.append(c.FLAG_ZOMBIE) volume -= self.createZombieInfo[c.FLAG_ZOMBIE][0] if inevitableZombieDict and (str(wave) in inevitableZombieDict.keys()): for newZombie in inevitableZombieDict[str(wave)]: zombieList.append(newZombie) volume -= self.createZombieInfo[newZombie][0] if volume < 0: print('警告:第{}波中手动设置的僵尸级别总数超过上限!'.format(wave)) while (volume > 0) and (len(zombieList) < 50): newZombie = choices(useableZombies, weights)[0] if self.createZombieInfo[newZombie][0] <= volume: zombieList.append(newZombie) volume -= self.createZombieInfo[newZombie][0] waves.append(zombieList) self.waves = waves # 僵尸的刷新机制 def refreshWaves(self, current_time, survivalRounds=0): if self.waveNum >= self.map_data[c.NUM_FLAGS] * 10: return if (self.waveNum == 0): # 还未开始出现僵尸 if (self.waveTime == 0): # 表明刚刚开始游戏 self.waveTime = current_time else: if (survivalRounds == 0) and (self.bar_type == c.CHOOSEBAR_STATIC): # 首次选卡等待时间较长 if current_time - self.waveTime >= 18000: self.waveNum += 1 self.waveTime = current_time self.waveZombies = self.waves[self.waveNum - 1] self.numZombie = len(self.waveZombies) else: if (current_time - self.waveTime >= 6000): self.waveNum += 1 self.waveTime = current_time self.waveZombies = self.waves[self.waveNum - 1] self.numZombie = len(self.waveZombies) return if (self.waveNum % 10 != 9): if ((current_time - self.waveTime >= 25000 + randint(0, 6000)) or (self.bar_type != c.CHOOSEBAR_STATIC and current_time - self.waveTime >= 12500 + randint(0, 3000))): self.waveNum += 1 self.waveTime = current_time self.waveZombies = self.waves[self.waveNum - 1] self.numZombie = len(self.waveZombies) return else: if ((current_time - self.waveTime >= 45000) or (self.bar_type != c.CHOOSEBAR_STATIC and current_time - self.waveTime >= 25000)): self.waveNum += 1 self.waveTime = current_time self.waveZombies = self.waves[self.waveNum - 1] self.numZombie = len(self.waveZombies) return elif ((current_time - self.waveTime >= 43000) or (self.bar_type != c.CHOOSEBAR_STATIC and current_time - self.waveTime >= 23000)): self.showHugeWaveApprochingTime = current_time numZombies = 0 for i in range(self.map_y_len): numZombies += len(self.zombie_groups[i]) if (numZombies / self.numZombie < uniform(0.15, 0.25)) and (current_time - self.waveTime > 4000): # 当僵尸所剩无几并且时间过了4000 ms以上时,改变时间记录,使得2000 ms后刷新僵尸(所以需要判断剩余时间是否大于2000 ms) if self.bar_type == c.CHOOSEBAR_STATIC: if current_time - 43000 < self.waveTime: # 判断剩余时间是否有2000 ms self.waveTime = current_time - 43000 # 即倒计时2000 ms else: if current_time - 23000 < self.waveTime: # 判断剩余时间是否有2000 ms self.waveTime = current_time - 23000 # 即倒计时2000 ms def setupZombies(self): def takeTime(element): return element[0] self.zombie_list = [] # 旧机制,目前仅用于调试 for data in self.map_data[c.ZOMBIE_LIST]: if 'map_y' in data.keys(): self.zombie_list.append((data['time'], data['name'], data['map_y'])) else: self.zombie_list.append((data['time'], data['name'])) self.zombie_start_time = 0 self.zombie_list.sort(key=takeTime) 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 update(self, surface, current_time, mouse_pos, mouse_click): self.current_time = self.game_info[c.CURRENT_TIME] = self.pvzTime(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 pvzTime(self, current_time): # 扣除暂停时间 if not self.pause: self.beforePauseTime = current_time - self.pauseTime else: self.pauseTime = current_time - self.beforePauseTime return self.beforePauseTime def initBowlingMap(self): print('initBowlingMap') for x in range(3, self.map.width): for y in range(self.map.height): self.map.setMapGridType(x, y, c.MAP_STATE_UNAVAILABLE) # 将坚果保龄球红线右侧设置为不可种植任何植物 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.cards_to_choose, 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.drag_shovel = False self.hint_image = None self.hint_plant = False # 种植植物后应当刷新僵尸的攻击对象,当然,默认初始时不用刷新 self.refreshZombieAttack = False # 0:白天 1:夜晚 2:泳池 3:浓雾 4:屋顶 5:月夜 6:坚果保龄球 # 还准备加入 7:单行草皮 8:三行草皮 但是目前没有找到图( if self.background_type in {0, 2, 4, 7, 8} 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() if (c.ZOMBIE_LIST in self.map_data.keys()) and self.map_data[c.SPAWN_ZOMBIES] == c.SPAWN_ZOMBIES_LIST: self.setupZombies() else: # 僵尸波数数据及僵尸生成数据 self.waveNum = 0 # 还未出现僵尸时定义为0 self.waveTime = 0 self.waveZombies = [] self.numZombie = 0 # 新的僵尸生成机制:级别——权重生成 self.createZombieInfo = {# 生成僵尸:(级别, 权重) c.NORMAL_ZOMBIE:(1, 4000), c.FLAG_ZOMBIE:(1, 0), c.CONEHEAD_ZOMBIE:(2, 4000), c.BUCKETHEAD_ZOMBIE:(4, 3000), c.NEWSPAPER_ZOMBIE:(2, 1000), c.FOOTBALL_ZOMBIE:(2, 2000) } # 暂时没有生存模式,所以 survivalRounds = 0 if c.INEVITABLE_ZOMBIE_DICT in self.map_data.keys(): self.createWaves( useableZombies=self.map_data[c.INCLUDED_ZOMBIES], numFlags=self.map_data[c.NUM_FLAGS], survivalRounds=0, inevitableZombieDict=self.map_data[c.INEVITABLE_ZOMBIE_DICT]) else: self.createWaves( useableZombies=self.map_data[c.INCLUDED_ZOMBIES], numFlags=self.map_data[c.NUM_FLAGS], survivalRounds=0) self.setupCars() # 地图有铲子才添加铲子 if self.hasShovel: # 导入小铲子 frame_rect = [0, 0, 71, 67] self.shovel = tool.get_image_menu(tool.GFX[c.SHOVEL], *frame_rect, c.BLACK, 1.1) self.shovel_rect = self.shovel.get_rect() frame_rect = [0, 0, 77, 75] self.shovel_positon = (608, 1) self.shovel_box = tool.get_image_menu(tool.GFX[c.SHOVEL_BOX], *frame_rect, c.BLACK, 1.1) self.shovel_box_rect = self.shovel_box.get_rect() self.shovel_rect.x = self.shovel_box_rect.x = self.shovel_positon[0] self.shovel_rect.y = self.shovel_box_rect.y = self.shovel_positon[1] self.setupLittleMenu() self.setupLevelProgressBarImage() self.setupHugeWaveApprochingImage() self.showHugeWaveApprochingTime = -2000 # 防止设置为0时刚刚打开游戏就已经启动红字 # 小菜单 def setupLittleMenu(self): # 具体运行游戏必定有个小菜单, 导入菜单和选项 frame_rect = (0, 0, 108, 31) self.little_menu = tool.get_image_menu(tool.GFX[c.LITTLE_MENU], *frame_rect, c.BLACK, 1.1) self.little_menu_rect = self.little_menu.get_rect() self.little_menu_rect.x = 690 self.little_menu_rect.y = 0 frame_rect = (0, 0, 500, 500) self.big_menu = tool.get_image_menu(tool.GFX[c.BIG_MENU], *frame_rect, c.BLACK, 1.1) self.big_menu_rect = self.big_menu.get_rect() self.big_menu_rect.x = 150 self.big_menu_rect.y = 0 frame_rect = (0, 0, 342, 87) self.return_button = tool.get_image_menu(tool.GFX[c.RETURN_BUTTON], *frame_rect, c.BLACK, 1.1) self.return_button_rect = self.return_button.get_rect() self.return_button_rect.x = 220 self.return_button_rect.y = 440 frame_rect = (0, 0, 207, 45) self.restart_button = tool.get_image_menu(tool.GFX[c.RESTART_BUTTON], *frame_rect, c.BLACK, 1.1) self.restart_button_rect = self.restart_button.get_rect() self.restart_button_rect.x = 295 self.restart_button_rect.y = 325 frame_rect = (0, 0, 206, 43) self.mainMenu_button = tool.get_image_menu(tool.GFX[c.MAINMENU_BUTTON], *frame_rect, c.BLACK, 1.1) self.mainMenu_button_rect = self.mainMenu_button.get_rect() self.mainMenu_button_rect.x = 299 self.mainMenu_button_rect.y = 372 # 一大波僵尸来袭图片显示 def setupHugeWaveApprochingImage(self): frame_rect = (0, 0, 492, 80) self.huge_wave_approching_image = tool.get_image_menu(tool.GFX[c.HUGE_WAVE_APPROCHING], *frame_rect, c.BLACK, 1) self.huge_wave_approching_image_rect = self.huge_wave_approching_image.get_rect() self.huge_wave_approching_image_rect.x = 140 # 猜的 self.huge_wave_approching_image_rect.y = 250 # 猜的 # 关卡进程显示设置 def setupLevelProgressBarImage(self): # 注意:定位一律采用与主进度条的相对位置 # 主进度条 frame_rect = (0, 0, 158, 26) self.level_progress_bar_image = tool.get_image_menu(tool.GFX[c.LEVEL_PROGRESS_BAR], *frame_rect, c.BLACK, 1) self.level_progress_bar_image_rect = self.level_progress_bar_image.get_rect() self.level_progress_bar_image_rect.x = 600 # 猜的 self.level_progress_bar_image_rect.y = 565 # 猜的 # 僵尸头 frame_rect = (0, 0, 23, 25) self.level_progress_zombie_head_image = tool.get_image_menu(tool.GFX[c.LEVEL_PROGRESS_ZOMBIE_HEAD], *frame_rect, c.BLACK, 1) self.level_progress_zombie_head_image_rect = self.level_progress_zombie_head_image.get_rect() self.level_progress_zombie_head_image_rect.x = self.level_progress_bar_image_rect.x + 75 # 猜的 self.level_progress_zombie_head_image_rect.y = self.level_progress_bar_image_rect.y - 3 # 猜的 # 旗帜(这里只包括最后一面) frame_rect = (0, 0, 20, 18) self.level_progress_flag = tool.get_image_menu(tool.GFX[c.LEVEL_PROGRESS_FLAG], *frame_rect, c.BLACK, 1) self.level_progress_flag_rect = self.level_progress_flag.get_rect() self.level_progress_flag_rect.x = self.level_progress_bar_image_rect.x - 78 # 猜的 self.level_progress_flag_rect.y = self.level_progress_bar_image_rect.y - 3 # 猜的 # 检查小菜单有没有被点击 def checkLittleMenuClick(self, mouse_pos): x, y = mouse_pos if(x >= self.little_menu_rect.x and x <= self.little_menu_rect.right and y >= self.little_menu_rect.y and y <= self.little_menu_rect.bottom): return True return False # 检查小菜单的返回有没有被点击 def checkReturnClick(self, mouse_pos): x, y = mouse_pos if(x >= self.return_button_rect.x and x <= self.return_button_rect.right and y >= self.return_button_rect.y and y <= self.return_button_rect.bottom): return True return False # 检查小菜单的重新开始有没有被点击 def checkRestartClick(self, mouse_pos): x, y = mouse_pos if(x >= self.restart_button_rect.x and x <= self.restart_button_rect.right and y >= self.restart_button_rect.y and y <= self.restart_button_rect.bottom): return True return False # 检查小菜单的主菜单有没有被点击 def checkMainMenuClick(self, mouse_pos): x, y = mouse_pos if(x >= self.mainMenu_button_rect.x and x <= self.mainMenu_button_rect.right and y >= self.mainMenu_button_rect.y and y <= self.mainMenu_button_rect.bottom): return True return False # 用小铲子移除植物 def shovelRemovePlant(self, mouse_pos): x, y = mouse_pos map_x, map_y = self.map.getMapIndex(x, y) for i in self.plant_groups[map_y]: if (x >= i.rect.x and x <= i.rect.right and y >= i.rect.y and y <= i.rect.bottom): # 优先移除花盆、睡莲上的植物而非花盆、睡莲本身 if len(self.map.map[map_y][map_x][c.MAP_PLANT]) >= 2: if c.LILYPAD in self.map.map[map_y][map_x][c.MAP_PLANT]: if i.name == c.LILYPAD: continue elif '花盆(未实现)' in self.map.map[map_y][map_x][c.MAP_PLANT]: if i.name == '花盆(未实现)': continue self.killPlant(i, shovel=True) # 使用后默认铲子复原 self.drag_shovel = not self.drag_shovel self.removeMouseImagePlus() return # 检查小铲子的位置有没有被点击 # 方便放回去 def checkShovelClick(self, mouse_pos): x, y = mouse_pos if( self.hasShovel and x >= self.shovel_box_rect.x and x <= self.shovel_box_rect.right and y >= self.shovel_box_rect.y and y <= self.shovel_box_rect.bottom): return True return False def play(self, mouse_pos, mouse_click): # 原版阳光掉落机制需要 # 已掉落的阳光 self.fallenSun = 0 # 如果暂停 if self.showLittleMenu: # 设置暂停状态 self.pause = True # 暂停播放音乐 pg.mixer.music.pause() if mouse_click[0]: if self.checkReturnClick(mouse_pos): # 终止暂停,停止显示菜单 self.pause = False self.showLittleMenu = False # 继续播放音乐 pg.mixer.music.unpause() elif self.checkRestartClick(mouse_pos): self.done = True self.next = c.LEVEL pg.mixer.music.stop() pg.mixer.music.load(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "music", bgm)) pg.mixer.music.play(-1, 0) elif self.checkMainMenuClick(mouse_pos): self.done = True self.next = c.MAIN_MENU #self.persist = {c.CURRENT_TIME:0, c.LEVEL_NUM:c.START_LEVEL_NUM} # 应该不能用c.LEVEL_NUM:c.START_LEVEL_NUM self.persist = {c.CURRENT_TIME:0, c.LEVEL_NUM:self.persist[c.LEVEL_NUM], c.LITTLEGAME_NUM:self.persist[c.LITTLEGAME_NUM]} pg.mixer.music.stop() pg.mixer.music.load(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "music", "intro.opus")) pg.mixer.music.play(-1, 0) return if (c.ZOMBIE_LIST in self.map_data.keys()) and self.map_data[c.SPAWN_ZOMBIES] == c.SPAWN_ZOMBIES_LIST: # 旧僵尸生成方式 if self.zombie_start_time == 0: self.zombie_start_time = self.current_time elif len(self.zombie_list) > 0: data = self.zombie_list[0] # 因此要求僵尸列表按照时间顺序排列 # data内容排列:[0]:时间 [1]:名称 [2]:坐标 if data[0] <= (self.current_time - self.zombie_start_time): if len(data) == 3: self.createZombie(data[1], data[2]) self.zombie_list.remove(data) else: # len(data) == 2 没有指定map_y self.createZombie(data[1]) self.zombie_list.remove(data) else: # 新僵尸生成方式 self.refreshWaves(self.current_time) for i in self.waveZombies: self.createZombie(i) else: self.waveZombies = [] 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) if self.produce_sun: # 原版阳光掉落机制:(已掉落阳光数*100 ms + 4250 ms) 与 9500 ms的最小值,再加 0 ~ 2750 ms 之间的一个数 if (self.current_time - self.sun_timer) > min(c.PRODUCE_SUN_INTERVAL + 100*self.fallenSun, 9500) + randint(0, 2750): self.sun_timer = self.current_time map_x, map_y = self.map.getRandomMapIndex() x, y = self.map.getMapGridPos(map_x, map_y) self.sun_group.add(plant.Sun(x, 0, x, y)) self.fallenSun += 1 # wcb 添加 # 检查有没有捡到阳光 clickedSun = False clickedCardsOrMap = False if not self.drag_plant and not self.drag_shovel and mouse_pos and mouse_click[0]: for sun in self.sun_group: if sun.checkCollision(mouse_pos[0], mouse_pos[1]): self.menubar.increaseSunValue(sun.sun_value) clickedSun = True # 拖动植物或者铲子 if not self.drag_plant and mouse_pos and mouse_click[0] and not clickedSun: result = self.menubar.checkCardClick(mouse_pos) if result: self.setupMouseImage(result[0], result[1]) clickedCardsOrMap = True elif self.drag_plant: if mouse_click[1]: self.removeMouseImage() clickedCardsOrMap = True elif mouse_click[0]: if self.menubar.checkMenuBarClick(mouse_pos): self.removeMouseImage() else: self.addPlant() elif mouse_pos is None: self.setupHintImage() elif self.drag_shovel: if mouse_click[1]: self.removeMouseImagePlus() # 检查是否点击菜单 if mouse_click[0] and (not clickedSun) and (not clickedCardsOrMap): if self.checkLittleMenuClick(mouse_pos): # 暂停 显示菜单 self.showLittleMenu = True elif self.checkShovelClick(mouse_pos): self.drag_shovel = not self.drag_shovel if not self.drag_shovel: self.removeMouseImagePlus() elif self.drag_shovel: # 移出这地方的植物 self.shovelRemovePlant(mouse_pos) for car in self.cars: car.update(self.game_info) self.menubar.update(self.current_time) # 检查碰撞啥的 self.checkBulletCollisions() self.checkZombieCollisions() self.checkPlants() self.checkCarCollisions() self.checkGameState() def createZombie(self, name, map_y=None): # 有指定时按照指定生成,无指定时随机位置生成 # 0:白天 1:夜晚 2:泳池 3:浓雾 4:屋顶 5:月夜 6:坚果保龄球 if map_y == None: # 情况复杂:分水路和陆路,不能简单实现,需要另外加判断 # 0, 1, 4, 5路为陆路,2, 3路为水路 if self.map_data[c.BACKGROUND_TYPE] in {c.BACKGROUND_POOL, c.BACKGROUND_FOG}: if name in {}: # 这里还没填,以后加了泳池模式填:水生僵尸集合 map_y = randint(2, 3) elif name == '这里应该换成气球僵尸的名字(最好写调用的变量名,最好不要直接写,保持风格统一)': map_y = randint(0, 5) else: # 陆生僵尸 map_y = randint(0, 3) if map_y >= 2: # 后两路的map_y应当+2 map_y += 2 elif self.map_data[c.BACKGROUND_TYPE] == c.BACKGROUND_SINGLE: map_y = 2 elif self.map_data[c.BACKGROUND_TYPE] == c.BACKGROUND_TRIPLE: map_y = randint(1, 3) else: map_y = randint(0, 4) # 旗帜波出生点右移 if self.waveNum % 10: hugeWaveMove = 0 else: hugeWaveMove = 40 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 + randint(-20, 20) + hugeWaveMove, y, self.head_group)) elif name == c.CONEHEAD_ZOMBIE: self.zombie_groups[map_y].add(zombie.ConeHeadZombie(c.ZOMBIE_START_X + randint(-20, 20) + hugeWaveMove, y, self.head_group)) elif name == c.BUCKETHEAD_ZOMBIE: self.zombie_groups[map_y].add(zombie.BucketHeadZombie(c.ZOMBIE_START_X + randint(-20, 20) + hugeWaveMove, 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 + randint(-20, 20) + hugeWaveMove, y, self.head_group)) elif name == c.FOOTBALL_ZOMBIE: self.zombie_groups[map_y].add(zombie.FootballZombie(c.ZOMBIE_START_X + randint(-20, 20) + hugeWaveMove, y, self.head_group)) # 能否种植物的判断: # 先判断位置是否合法 isValid(map_x, map_y) # 再判断位置是否可用 isMovable(map_x, map_y) def canSeedPlant(self, plantName): x, y = pg.mouse.get_pos() return self.map.checkPlantToSeed(x, y, plantName) # 种植物 def addPlant(self): pos = self.canSeedPlant(self.plant_name) if pos is None: return if self.hint_image is None: self.setupHintImage() x, y = self.hint_rect.centerx, self.hint_rect.bottom map_x, map_y = self.map.getMapIndex(x, y) # 新植物也需要在这里声明 if self.plant_name == c.SUNFLOWER: new_plant = plant.SunFlower(x, y, self.sun_group) elif self.plant_name == c.PEASHOOTER: new_plant = plant.PeaShooter(x, y, self.bullet_groups[map_y]) elif self.plant_name == c.SNOWPEASHOOTER: new_plant = plant.SnowPeaShooter(x, y, self.bullet_groups[map_y]) elif self.plant_name == c.WALLNUT: new_plant = plant.WallNut(x, y) elif self.plant_name == c.CHERRYBOMB: new_plant = plant.CherryBomb(x, y) elif self.plant_name == c.THREEPEASHOOTER: new_plant = plant.ThreePeaShooter(x, y, self.bullet_groups, map_y, self.map.background_type) elif self.plant_name == c.REPEATERPEA: new_plant = plant.RepeaterPea(x, y, self.bullet_groups[map_y]) elif self.plant_name == c.CHOMPER: new_plant = plant.Chomper(x, y) elif self.plant_name == c.PUFFSHROOM: new_plant = plant.PuffShroom(x, y, self.bullet_groups[map_y]) elif self.plant_name == c.POTATOMINE: new_plant = plant.PotatoMine(x, y) elif self.plant_name == c.SQUASH: new_plant = plant.Squash(x, y, self.map.map[map_y][map_x][c.MAP_PLANT]) elif self.plant_name == c.SPIKEWEED: new_plant = plant.Spikeweed(x, y) elif self.plant_name == c.JALAPENO: new_plant = plant.Jalapeno(x, y) elif self.plant_name == c.SCAREDYSHROOM: new_plant = plant.ScaredyShroom(x, y, self.bullet_groups[map_y]) elif self.plant_name == c.SUNSHROOM: new_plant = plant.SunShroom(x, y, self.sun_group) elif self.plant_name == c.ICESHROOM: new_plant = plant.IceShroom(x, y) 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.LILYPAD: new_plant = plant.LilyPad(x, y) elif self.plant_name == c.TORCHWOOD: new_plant = plant.TorchWood(x, y, self.bullet_groups[map_y]) elif self.plant_name == c.STARFRUIT: new_plant = plant.StarFruit(x, y, self.bullet_groups[map_y], self) if new_plant.can_sleep and self.background_type in {c.BACKGROUND_DAY, c.BACKGROUND_POOL, c.BACKGROUND_ROOF, c.BACKGROUND_WALLNUTBOWLING, c.BACKGROUND_SINGLE, c.BACKGROUND_TRIPLE}: new_plant.setSleep() mushroomSleep = True else: mushroomSleep = False self.plant_groups[map_y].add(new_plant) # 种植植物后应当刷新僵尸的攻击对象 self.refreshZombieAttack = True if self.bar_type == c.CHOOSEBAR_STATIC: self.menubar.decreaseSunValue(self.select_plant.sun_cost) self.menubar.setCardFrozenTime(self.plant_name) else: self.menubar.deleateCard(self.select_plant) if self.bar_type != c.CHOSSEBAR_BOWLING: self.map.addMapPlant(map_x, map_y, self.plant_name, sleep=mushroomSleep) self.removeMouseImage() #print('addPlant map[%d,%d], grid pos[%d, %d] pos[%d, %d]' % (map_x, map_y, x, y, pos[0], pos[1])) def setupHintImage(self): pos = self.canSeedPlant(self.plant_name) if pos and self.mouse_image: if (self.hint_image and pos[0] == self.hint_rect.x and pos[1] == self.hint_rect.y): return width, height = self.mouse_rect.w, self.mouse_rect.h image = pg.Surface([width, height]) image.blit(self.mouse_image, (0, 0), (0, 0, width, height)) image.set_colorkey(c.BLACK) image.set_alpha(128) self.hint_image = image self.hint_rect = image.get_rect() # 花盆、睡莲图片应当下移一些 if self.plant_name in {c.LILYPAD, '花盆(未实现)'}: self.hint_rect.centerx = pos[0] self.hint_rect.bottom = pos[1] + 25 else: self.hint_rect.centerx = pos[0] self.hint_rect.bottom = pos[1] self.hint_plant = True else: self.hint_plant = False def setupMouseImage(self, plant_name, select_plant): frame_list = tool.GFX[plant_name] if plant_name in tool.PLANT_RECT: data = tool.PLANT_RECT[plant_name] x, y, width, height = data['x'], data['y'], data['width'], data['height'] else: x, y = 0, 0 rect = frame_list[0].get_rect() width, height = rect.w, rect.h if (plant_name == c.POTATOMINE or plant_name == c.SQUASH or plant_name == c.SPIKEWEED or plant_name == c.JALAPENO or plant_name == c.SCAREDYSHROOM or plant_name == c.SUNSHROOM or plant_name == c.ICESHROOM or plant_name == c.HYPNOSHROOM or plant_name == c.WALLNUTBOWLING or plant_name == c.REDWALLNUTBOWLING): color = c.WHITE else: color = c.BLACK self.mouse_image = tool.get_image(frame_list[0], x, y, width, height, color, 1) self.mouse_rect = self.mouse_image.get_rect() self.drag_plant = True self.plant_name = plant_name self.select_plant = select_plant def removeMouseImage(self): self.drag_plant = False self.mouse_image = None self.hint_image = None self.hint_plant = False # 移除小铲子 def removeMouseImagePlus(self): self.drag_shovel = False self.shovel_rect.x = self.shovel_positon[0] self.shovel_rect.y = self.shovel_positon[1] def checkBulletCollisions(self): for i in range(self.map_y_len): for bullet in self.bullet_groups[i]: if bullet.name == c.BULLET_STAR: collided_func = pg.sprite.collide_circle_ratio(1) else: collided_func = pg.sprite.collide_circle_ratio(0.7) if bullet.state == c.FLY: zombie = pg.sprite.spritecollideany(bullet, self.zombie_groups[i], collided_func) if zombie and zombie.state != c.DIE: # 这里生效代表已经发生了碰撞 zombie.setDamage(bullet.damage, effect=bullet.effect, damageType=c.ZOMBIE_DEAFULT_DAMAGE) bullet.setExplode() # 火球有溅射伤害 if bullet.name == c.BULLET_FIREBALL: for rangeZombie in self.zombie_groups[i]: if abs(rangeZombie.rect.x - bullet.rect.x) <= (c.GRID_X_SIZE // 2): rangeZombie.setDamage(c.BULLET_DAMAGE_FIREBALL_RANGE, effect=False, damageType=c.ZOMBIE_DEAFULT_DAMAGE) def checkZombieCollisions(self): if self.bar_type == c.CHOSSEBAR_BOWLING: ratio = 0.6 else: ratio = 0.5 collided_func = pg.sprite.collide_circle_ratio(ratio) for i in range(self.map_y_len): hypo_zombies = [] for zombie in self.zombie_groups[i]: if zombie.state != c.WALK: if not self.refreshZombieAttack: continue plant = pg.sprite.spritecollideany(zombie, self.plant_groups[i], collided_func) if plant: if plant.name == c.WALLNUTBOWLING: if plant.canHit(i): zombie.setDamage(c.WALLNUT_BOWLING_DAMAGE, damageType=c.ZOMBIE_WALLNUT_BOWLING_DANMAGE) # 注意:以上语句为通用处理,以后加入了铁门僵尸需要单独设置直接冲撞就直接杀死 # 可以给坚果保龄球设置attacked属性,如果attacked就秒杀(setDamage的攻击类型此时设置为COMMMON)铁门 plant.changeDirection(i) elif plant.name == c.REDWALLNUTBOWLING: if plant.state == c.IDLE: plant.setAttack() elif plant.name == c.SPIKEWEED: continue # 在睡莲、花盆上有植物时应当优先攻击其上的植物 elif plant.name in {c.LILYPAD, '花盆(未实现)'}: map_x, map_y = self.map.getMapIndex(plant.rect.centerx, plant.rect.bottom) if len(self.map.map[map_y][map_x][c.MAP_PLANT]) >= 2: # 这里暂时没有南瓜头优先攻击逻辑,这整个模块都没有对南瓜头的设计 for actualTargetPlant in self.plant_groups[i]: # 检测同一格的其他植物 if self.map.getMapIndex(actualTargetPlant.rect.centerx, actualTargetPlant.rect.bottom) == (map_x, map_y): if actualTargetPlant.name != plant.name: zombie.setAttack(actualTargetPlant) break else: zombie.setAttack(plant) else: zombie.setAttack(plant) for hypno_zombie in self.hypno_zombie_groups[i]: if hypno_zombie.health <= 0: continue zombie_list = pg.sprite.spritecollide(hypno_zombie, self.zombie_groups[i], False,collided_func) for zombie in zombie_list: if zombie.state == c.DIE: continue if zombie.state == c.WALK: zombie.setAttack(hypno_zombie, False) if hypno_zombie.state == c.WALK: hypno_zombie.setAttack(zombie, False) else: self.refreshZombieAttack = False # 生效后需要解除刷新设置 def checkCarCollisions(self): for car in self.cars: for zombie in self.zombie_groups[car.map_y]: if zombie and zombie.state != c.DIE and (not zombie.lostHead) and zombie.rect.x <= 0: car.setWalk() if zombie.rect.x <= car.rect.x: zombie.health = 0 zombie.kill() if car.dead: self.cars.remove(car) def boomZombies(self, x, map_y, y_range, x_range, effect=False): for i in range(self.map_y_len): if abs(i - map_y) > y_range: continue for zombie in self.zombie_groups[i]: if ((abs(zombie.rect.centerx - x) <= x_range) or ((zombie.rect.right - (x-x_range) > 20) or (zombie.rect.right - (x-x_range))/zombie.rect.width > 0.2, ((x+x_range) - zombie.rect.left > 20) or ((x+x_range) - zombie.rect.left)/zombie.rect.width > 0.2)[zombie.rect.x > x]): # 这代码不太好懂,后面是一个判断僵尸在左还是在右,前面是一个元组,[0]是在左边的情况,[1]是在右边的情况 if effect == c.BULLET_EFFECT_UNICE: zombie.ice_slow_ratio = 1 zombie.setDamage(1800, damageType=c.ZOMBIE_ASH_DAMAGE) if zombie.health <= 0: zombie.setBoomDie() def freezeZombies(self, plant): for i in range(self.map_y_len): for zombie in self.zombie_groups[i]: if zombie.rect.left <= c.SCREEN_WIDTH: zombie.setFreeze(plant.trap_frames[0]) zombie.setDamage(20, damageType=c.ZOMBIE_RANGE_DAMAGE) # 寒冰菇还有全场20的伤害 def killPlant(self, plant, shovel=False): x, y = plant.getPosition() map_x, map_y = self.map.getMapIndex(x, y) if self.bar_type != c.CHOSSEBAR_BOWLING: try: # 避免炸弹等本身就不在集合里面的问题 self.map.removeMapPlant(map_x, map_y, plant.name) except KeyError: pass # 将睡眠植物移除后更新睡眠状态 if plant.state == c.SLEEP: self.map.map[map_y][map_x][c.MAP_SLEEP] = False # 用铲子铲不用触发植物功能 if not shovel: if (plant.name == c.CHERRYBOMB or plant.name == c.REDWALLNUTBOWLING): self.boomZombies(plant.rect.centerx, map_y, plant.explode_y_range, plant.explode_x_range) elif plant.name == c.JALAPENO: self.boomZombies(plant.rect.centerx, map_y, plant.explode_y_range, plant.explode_x_range, effect=c.BULLET_EFFECT_UNICE) elif plant.name == c.ICESHROOM and plant.state != c.SLEEP: self.freezeZombies(plant) elif plant.name == c.HYPNOSHROOM and plant.state != c.SLEEP: zombie = plant.kill_zombie zombie.setHypno() _, map_y = self.map.getMapIndex(zombie.rect.centerx, zombie.rect.bottom) self.zombie_groups[map_y].remove(zombie) self.hypno_zombie_groups[map_y].add(zombie) elif (plant.name == c.POTATOMINE and not plant.is_init): # 土豆雷不是灰烬植物,不能用Boom for i in range(self.map_y_len): if abs(i - map_y) > plant.explode_y_range: continue for zombie in self.zombie_groups[i]: if ((abs(zombie.rect.centerx - x) <= plant.explode_y_range) or ((zombie.rect.right - (x-plant.explode_x_range) > 20) or (zombie.rect.right - (x-plant.explode_x_range))/zombie.rect.width > 0.2, ((x+plant.explode_x_range) - zombie.rect.left > 20) or ((x+plant.explode_x_range) - zombie.rect.left)/zombie.rect.width > 0.2)[zombie.rect.x > x]): # 这代码不太好懂,后面是一个判断僵尸在左还是在右,前面是一个元组,[0]是在左边的情况,[1]是在右边的情况 zombie.setDamage(1800, damageType=c.ZOMBIE_RANGE_DAMAGE) # 避免僵尸在用铲子移除植物后还在原位啃食 plant.health = 0 plant.kill() def checkPlant(self, plant, i): zombie_len = len(self.zombie_groups[i]) if plant.name == c.THREEPEASHOOTER: if plant.state == c.IDLE: if zombie_len > 0: plant.setAttack() elif (i-1) >= 0 and len(self.zombie_groups[i-1]) > 0: plant.setAttack() elif (i+1) < self.map_y_len and len(self.zombie_groups[i+1]) > 0: plant.setAttack() elif plant.state == c.ATTACK: if zombie_len > 0: pass elif (i-1) >= 0 and len(self.zombie_groups[i-1]) > 0: pass elif (i+1) < self.map_y_len and len(self.zombie_groups[i+1]) > 0: pass else: plant.setIdle() elif plant.name == c.CHOMPER: for zombie in self.zombie_groups[i]: if plant.canAttack(zombie): plant.setAttack(zombie, self.zombie_groups[i]) break elif plant.name == c.POTATOMINE: for zombie in self.zombie_groups[i]: if plant.canAttack(zombie): plant.setAttack() break elif plant.name == c.SQUASH: for zombie in self.zombie_groups[i]: if plant.canAttack(zombie): plant.setAttack(zombie, self.zombie_groups[i]) break elif plant.name == c.SPIKEWEED: can_attack = False for zombie in self.zombie_groups[i]: if plant.canAttack(zombie): can_attack = True break if plant.state == c.IDLE and can_attack: plant.setAttack(self.zombie_groups[i]) elif plant.state == c.ATTACK and not can_attack: plant.setIdle() elif plant.name == c.SCAREDYSHROOM: need_cry = False can_attack = False for zombie in self.zombie_groups[i]: if plant.needCry(zombie): need_cry = True break elif plant.canAttack(zombie): can_attack = True if need_cry: if plant.state != c.CRY: plant.setCry() elif can_attack: if plant.state != c.ATTACK: plant.setAttack() elif plant.state != c.IDLE: plant.setIdle() elif plant.name == c.STARFRUIT: can_attack = False if (plant.state == c.IDLE): for zombie_group in self.zombie_groups: # 遍历循环所有僵尸 for zombie in zombie_group: if plant.canAttack(zombie): can_attack = True break if plant.state == c.IDLE and can_attack: plant.setAttack() elif (plant.state == c.ATTACK and not can_attack): plant.setIdle() elif(plant.name == c.WALLNUTBOWLING or plant.name == c.REDWALLNUTBOWLING): pass else: can_attack = False if (plant.state == c.IDLE and zombie_len > 0): for zombie in self.zombie_groups[i]: if plant.canAttack(zombie): can_attack = True break if plant.state == c.IDLE and can_attack: plant.setAttack() elif (plant.state == c.ATTACK and not can_attack): plant.setIdle() def checkPlants(self): for i in range(self.map_y_len): for plant in self.plant_groups[i]: if plant.state != c.SLEEP: self.checkPlant(plant, i) if plant.health <= 0: self.killPlant(plant) def checkVictory(self): if (c.ZOMBIE_LIST in self.map_data.keys()) and self.map_data[c.SPAWN_ZOMBIES] == c.SPAWN_ZOMBIES_LIST: if len(self.zombie_list) > 0: return False for i in range(self.map_y_len): if len(self.zombie_groups[i]) > 0: return False return True else: if self.waveNum < self.map_data[c.NUM_FLAGS] * 10: return False for i in range(self.map_y_len): if len(self.zombie_groups[i]) > 0: return False return True def checkLose(self): for i in range(self.map_y_len): for zombie in self.zombie_groups[i]: if zombie.rect.right < -10 and (not zombie.lostHead): return True return False def checkGameState(self): if self.checkVictory(): if c.LITTLEGAME_BUTTON in self.game_info: self.game_info[c.LITTLEGAME_NUM] += 1 else: self.game_info[c.LEVEL_NUM] += 1 self.next = c.GAME_VICTORY self.done = True elif self.checkLose(): self.next = c.GAME_LOSE self.done = True def drawMouseShow(self, surface): if self.hint_plant: surface.blit(self.hint_image, self.hint_rect) x, y = pg.mouse.get_pos() self.mouse_rect.centerx = x self.mouse_rect.centery = y surface.blit(self.mouse_image, self.mouse_rect) def drawMouseShowPlus(self, surface): # 拖动铲子时的显示 x, y = pg.mouse.get_pos() self.shovel_rect.centerx = x self.shovel_rect.centery = y # 铲子接近植物时会高亮提示 map_x, map_y = self.map.getMapIndex(x, y) surface.blit(self.shovel, self.shovel_rect) for i in self.plant_groups[map_y]: if (x >= i.rect.x and x <= i.rect.right and y >= i.rect.y and y <= i.rect.bottom): # 优先选中睡莲、花盆上的植物 if len(self.map.map[map_y][map_x][c.MAP_PLANT]) >= 2: if c.LILYPAD in self.map.map[map_y][map_x][c.MAP_PLANT]: if i.name == c.LILYPAD: continue elif '花盆(未实现)' in self.map.map[map_y][map_x][c.MAP_PLANT]: if i.name == '花盆(未实现)': continue i.highlightTime = self.current_time return def drawZombieFreezeTrap(self, i, surface): for zombie in self.zombie_groups[i]: zombie.drawFreezeTrap(surface) def showLevelProgress(self, surface): # 画进度条框 surface.blit(self.level_progress_bar_image, self.level_progress_bar_image_rect) # 按照当前波数生成僵尸头位置 self.level_progress_zombie_head_image_rect.x = self.level_progress_bar_image_rect.x - int((150 * self.waveNum) / (self.map_data[c.NUM_FLAGS] * 10)) + 145 # 常数为预计值 self.level_progress_zombie_head_image_rect.y = self.level_progress_bar_image_rect.y - 3 # 常数为预计值 # 填充的进度条信息 # 常数为预计值 filledBarRect = (self.level_progress_zombie_head_image_rect.x + 3, self.level_progress_bar_image_rect.y + 6, int((150 * self.waveNum) / (self.map_data[c.NUM_FLAGS] * 10)) + 5, 9) # 画填充的进度条 pg.draw.rect(surface, c.GREEN, filledBarRect) # 画旗帜 for i in range(self.numFlags): self.level_progress_flag_rect.x = self.level_progress_bar_image_rect.x + int((150*i)/self.numFlags) + 5 # 常数是猜的 # 当指示进度的僵尸头在旗帜左侧时升高旗帜 if self.level_progress_flag_rect.x - 7 >= self.level_progress_zombie_head_image_rect.x: self.level_progress_flag_rect.y = self.level_progress_bar_image_rect.y - 15 # 常数是猜的 else: self.level_progress_flag_rect.y = self.level_progress_bar_image_rect.y - 3 # 常数是猜的 surface.blit(self.level_progress_flag, self.level_progress_flag_rect) # 画僵尸头 surface.blit(self.level_progress_zombie_head_image, self.level_progress_zombie_head_image_rect) def draw(self, surface): self.level.blit(self.background, self.viewport, self.viewport) surface.blit(self.level, (0,0), self.viewport) if self.state == c.CHOOSE: self.panel.draw(surface) # 以后可能需要插入一个预备的状态(预览显示僵尸、返回战场) elif self.state == c.PLAY: if self.hasShovel: # 画铲子 surface.blit(self.shovel_box, self.shovel_box_rect) surface.blit(self.shovel, self.shovel_rect) # 画小菜单 surface.blit(self.little_menu, self.little_menu_rect) self.menubar.draw(surface) for car in self.cars: car.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) self.head_group.draw(surface) self.sun_group.draw(surface) if self.drag_plant: self.drawMouseShow(surface) if self.hasShovel and self.drag_shovel: self.drawMouseShowPlus(surface) if self.showLittleMenu: surface.blit(self.big_menu, self.big_menu_rect) surface.blit(self.return_button, self.return_button_rect) surface.blit(self.restart_button, self.restart_button_rect) surface.blit(self.mainMenu_button, self.mainMenu_button_rect) if not ((c.ZOMBIE_LIST in self.map_data.keys()) and self.map_data[c.SPAWN_ZOMBIES] == c.SPAWN_ZOMBIES_LIST): if self.current_time - self.showHugeWaveApprochingTime <= 2000: surface.blit(self.huge_wave_approching_image, self.huge_wave_approching_image_rect) self.showLevelProgress(surface)