diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f9d2060..83fde3b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,6 +45,7 @@ jobs: --distpath ./out ` --noconsole ` --add-data="resources;./resources" ` + --add-data="pypvz-exec-logo.png;./pypvz-exec-logo.png" ` -i ./pypvz.ico - name: Release the version built by pyinstaller @@ -76,8 +77,8 @@ jobs: --include-data-file=c:\hostedtoolcache\windows\python\${{ matrix.python_version }}*\x64\lib\site-packages\pygame\libvorbisfile-3.dll=libvorbisfile-3.dll ` --include-data-file=c:\hostedtoolcache\windows\python\${{ matrix.python_version }}*\x64\lib\site-packages\pygame\libvorbis-0.dll=libvorbis-0.dll ` --windows-disable-console ` + -o ./out/pypvz-with-python${{ matrix.python_version }}-nuitka-msvc-x64.exe ` main.py - mv ./out/main.exe ./out/pypvz-with-python${{ matrix.python_version }}-nuitka-msvc-x64.exe # artifact压缩包处上传包含exe和运行环境的文件夹 - name: "Upload binaries" diff --git a/pypvz-exec-logo.png b/pypvz-exec-logo.png new file mode 100644 index 0000000..77bfe15 Binary files /dev/null and b/pypvz-exec-logo.png differ diff --git a/source/component/map.py b/source/component/map.py index ba5884e..89e994b 100755 --- a/source/component/map.py +++ b/source/component/map.py @@ -44,7 +44,6 @@ class Map(): # 判断位置是否可用 # 暂时没有写紫卡植物的判断方法 # 由于紫卡植物需要移除以前的植物,所以可用另外定义一个函数 - # 注意咖啡豆生效后需要同时将植物的睡眠状态和格子的睡眠记录改变 def isAvailable(self, map_x, map_y, plantName): # 咖啡豆和墓碑吞噬者的判别最为特殊 if plantName == c.COFFEEBEAN: @@ -97,7 +96,7 @@ class Map(): else: # 非水生植物,依赖睡莲 if c.LILYPAD in self.map[map_y][map_x][c.MAP_PLANT]: if (all((i in {c.LILYPAD, '南瓜头(未实现)'}) for i in self.map[map_y][map_x][c.MAP_PLANT]) - and (plantName not in self.map[map_y][map_x][c.MAP_PLANT])): # 例外植物:集合中填花盆和南瓜头,只要这里没有这种植物就能种植;判断方法:并集 + and (plantName not in self.map[map_y][map_x][c.MAP_PLANT])): if plantName in {c.SPIKEWEED, c.POTATOMINE, '花盆(未实现)'}: # 不能在睡莲上种植的植物 return False else: diff --git a/source/component/menubar.py b/source/component/menubar.py index b82b049..0fb6188 100755 --- a/source/component/menubar.py +++ b/source/component/menubar.py @@ -4,133 +4,6 @@ import pygame as pg from .. import tool from .. import constants as c -plantInfo = (# 元组 (植物名称, 卡片名称, 阳光, 冷却时间) - (c.PEASHOOTER, - c.CARD_PEASHOOTER, - 100, - 7500), - (c.SUNFLOWER, - c.CARD_SUNFLOWER, - 50, - 7500), - (c.CHERRYBOMB, - c.CARD_CHERRYBOMB, - 150, - 50000), - (c.WALLNUT, - c.CARD_WALLNUT, - 50, - 30000), - (c.POTATOMINE, - c.CARD_POTATOMINE, - 25, - 30000), - (c.SNOWPEASHOOTER, - c.CARD_SNOWPEASHOOTER, - 175, - 7500), - (c.CHOMPER, - c.CARD_CHOMPER, - 150, - 7500), - (c.REPEATERPEA, - c.CARD_REPEATERPEA, - 200, - 7500), - (c.PUFFSHROOM, - c.CARD_PUFFSHROOM, - 0, - 7500), - (c.SUNSHROOM, - c.CARD_SUNSHROOM, - 25, - 7500), - (c.FUMESHROOM, - c.CARD_FUMESHROOM, - 75, - 7500), - (c.GRAVEBUSTER, - c.CARD_GRAVEBUSTER, - 75, - 7500), - (c.HYPNOSHROOM, - c.CARD_HYPNOSHROOM, - 75, - 30000), - (c.SCAREDYSHROOM, - c.CARD_SCAREDYSHROOM, - 25, - 7500), - (c.ICESHROOM, - c.CARD_ICESHROOM, - 75, - 50000), - (c.DOOMSHROOM, - c.CARD_DOOMSHROOM, - 75, - 50000), - (c.LILYPAD, - c.CARD_LILYPAD, - 25, - 7500), - (c.SQUASH, - c.CARD_SQUASH, - 50, - 30000), - (c.TANGLEKLEP, - c.CARD_TANGLEKLEP, - 25, - 30000), - (c.THREEPEASHOOTER, - c.CARD_THREEPEASHOOTER, - 325, - 7500), - (c.JALAPENO, - c.CARD_JALAPENO, - 125, - 50000), - (c.SPIKEWEED, - c.CARD_SPIKEWEED, - 100, - 7500), - (c.TORCHWOOD, - c.CARD_TORCHWOOD, - 175, - 7500), - (c.TALLNUT, - c.CARD_TALLNUT, - 125, - 30000), - (c.SEASHROOM, - c.CARD_SEASHROOM, - 125, - 30000), - (c.STARFRUIT, - c.CARD_STARFRUIT, - 125, - 7500), - (c.COFFEEBEAN, - c.CARD_COFFEEBEAN, - 75, - 7500), - (c.GARLIC, - c.CARD_GARLIC, - 50, - 7500), - # 应当保证这两个在一般模式下不可选的特殊植物恒在最后 - (c.WALLNUTBOWLING, - c.CARD_WALLNUT, - 0, - 0), - (c.REDWALLNUTBOWLING, - c.CARD_REDWALLNUT, - 0, - 0), - ) - -# 指定了哪些卡可选 -cards_to_choose = range(len(plantInfo) - 2) - def getSunValueImage(sun_value): # for pack, must include ttf @@ -152,7 +25,7 @@ def getSunValueImage(sun_value): def getCardPool(data): card_pool = [] for card in data: - for i,name in enumerate(plantInfo): + for i,name in enumerate(c.PLANT_CARD_INFO): if name[c.PLANT_NAME_INDEX] == card: card_pool.append(i) break @@ -160,14 +33,14 @@ def getCardPool(data): class Card(): def __init__(self, x, y, index, scale=0.5): - self.loadFrame(plantInfo[index][c.CARD_INDEX], scale) + self.loadFrame(c.PLANT_CARD_INFO[index][c.CARD_INDEX], scale) self.rect = self.orig_image.get_rect() self.rect.x = x self.rect.y = y self.index = index - self.sun_cost = plantInfo[index][c.SUN_INDEX] - self.frozen_time = plantInfo[index][c.FROZEN_INDEX] + self.sun_cost = c.PLANT_CARD_INFO[index][c.SUN_INDEX] + self.frozen_time = c.PLANT_CARD_INFO[index][c.FROZEN_INDEX] self.frozen_timer = -self.frozen_time self.refresh_timer = 0 self.select = True @@ -289,7 +162,7 @@ class MenuBar(): for card in self.card_list: if card.checkMouseClick(mouse_pos): if card.canClick(self.sun_value, self.current_time): - result = (plantInfo[card.index][c.PLANT_NAME_INDEX], card) + result = (c.PLANT_CARD_INFO[card.index][c.PLANT_NAME_INDEX], card) break return result @@ -310,7 +183,7 @@ class MenuBar(): def setCardFrozenTime(self, plant_name): for card in self.card_list: - if plantInfo[card.index][c.PLANT_NAME_INDEX] == plant_name: + if c.PLANT_CARD_INFO[card.index][c.PLANT_NAME_INDEX] == plant_name: card.setFrozenTime(self.current_time) break @@ -440,7 +313,7 @@ class Panel(): if self.selected_num >= c.CARD_LIST_NUM: surface.blit(self.button_image, self.button_rect) -# 传送带模式 +# 传送带模式的卡片 class MoveCard(): def __init__(self, x, y, card_name, plant_name, scale=0.5): self.loadFrame(card_name, scale) @@ -492,7 +365,7 @@ class MoveCard(): elif (current_time - self.move_timer) >= c.CARD_MOVE_TIME: if self.rect.x > left_x: self.rect.x -= 1 - self.image = self.createShowImage() + self.image = self.createShowImage() self.move_timer += c.CARD_MOVE_TIME def draw(self, surface): @@ -526,8 +399,8 @@ class MoveBar(): y = 6 index = random.randint(0, len(self.card_pool) - 1) card_index = self.card_pool[index] - card_name = plantInfo[card_index][c.CARD_INDEX] + '_move' - plant_name = plantInfo[card_index][c.PLANT_NAME_INDEX] + card_name = c.PLANT_CARD_INFO[card_index][c.CARD_INDEX] + '_move' + plant_name = c.PLANT_CARD_INFO[card_index][c.PLANT_NAME_INDEX] self.card_list.append(MoveCard(x, y, card_name, plant_name)) return True diff --git a/source/component/plant.py b/source/component/plant.py index e702363..7d91866 100755 --- a/source/component/plant.py +++ b/source/component/plant.py @@ -1,4 +1,4 @@ -from random import randint +import random import pygame as pg import os from .. import tool @@ -1143,7 +1143,7 @@ class WallNutBowling(Plant): self.animate_interval = 200 self.move_timer = 0 self.move_interval = 70 - self.vel_x = randint(12, 15) + self.vel_x = random.randint(12, 15) self.vel_y = 0 self.disable_hit_y = -1 @@ -1192,7 +1192,7 @@ class WallNutBowling(Plant): elif self.map_y == (c.GRID_Y_LEN - 1): # 坚果保龄球显然没有泳池的6行情形 self.vel_y = -self.vel_x else: - if randint(0, 1): + if random.randint(0, 1): self.vel_y = self.vel_x else: self.vel_y = -self.vel_x @@ -1227,7 +1227,7 @@ class RedWallNutBowling(Plant): self.animate_interval = 200 self.move_timer = 0 self.move_interval = 70 - self.vel_x = randint(12, 15) + self.vel_x = random.randint(12, 15) self.start_boom = False self.boomed = False @@ -1648,7 +1648,7 @@ class Hole(Plant): class Grave(Plant): def __init__(self, x, y): Plant.__init__(self, x, y, c.GRAVE, c.INF, None) - self.frame_index = randint(0, self.frame_num - 1) + self.frame_index = random.randint(0, self.frame_num - 1) self.image = self.frames[self.frame_index] self.mask = pg.mask.from_surface(self.image) diff --git a/source/component/zombie.py b/source/component/zombie.py index 7e91988..4efb72d 100755 --- a/source/component/zombie.py +++ b/source/component/zombie.py @@ -1,6 +1,6 @@ import pygame as pg import os -from random import randint +import random from .. import tool from .. import constants as c @@ -245,7 +245,8 @@ class Zombie(pg.sprite.Sprite): self.changeFrames(self.attack_frames) self.helmetType2 = False if self.name == c.NEWSPAPER_ZOMBIE: - self.speed = 2.5 + self.speed = 2.65 + self.walk_animate_interval = 300 if (((self.current_time - self.attack_timer) > (c.ATTACK_INTERVAL * self.getAttackTimeRatio())) and (not self.lostHead)): if self.prey.health > 0: @@ -273,7 +274,7 @@ class Zombie(pg.sprite.Sprite): if self.checkToDie(self.losthead_attack_frames): return - if (self.current_time - self.freeze_timer) >= c.MIN_FREEZE_TIME + randint(0, 2000): + if (self.current_time - self.freeze_timer) >= c.MIN_FREEZE_TIME + random.randint(0, 2000): self.setWalk() # 注意寒冰菇解冻后还有减速 self.ice_slow_timer = self.freeze_timer + 10000 # 每次冰冻冻结 + 减速时间为20 s,而减速有10 s计时,故这里+10 s @@ -737,6 +738,7 @@ class NewspaperZombie(Zombie): self.changeFrames(self.walk_frames) self.speedUp = True self.speed = 2.65 + self.walk_animate_interval = 300 # 触发报纸僵尸暴走音效 pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "newspaperZombieAngry.ogg")).play() return @@ -1252,3 +1254,10 @@ class SnorkelZombie(Zombie): self.changeFrames(self.float_frames) self.canSetAttack = False + def setWalk(self): + self.state = c.WALK + self.animate_interval = self.walk_animate_interval + + if self.rect.right <= c.MAP_POOL_FRONT_X: + self.swimming = True + self.changeFrames(self.sink_frames) diff --git a/source/constants.py b/source/constants.py index fd541c8..432f562 100755 --- a/source/constants.py +++ b/source/constants.py @@ -8,6 +8,8 @@ GAME_RATE = 1 # 窗口标题 ORIGINAL_CAPTION = 'pypvz' +# 窗口图标 +ORIGINAL_LOGO = "pypvz-exec-logo.png" # 游戏模式 GAME_MODE = 'mode' @@ -55,8 +57,6 @@ GREEN = ( 0, 255, 0) # 退出游戏按钮 EXIT = 'exit' -# 当想要一个特殊值时用 -NULL = 'null' # 游戏界面可选的菜单 LITTLE_MENU = 'littleMenu' BIG_MENU = 'bigMenu' @@ -342,7 +342,7 @@ ICE_SLOW_TIME = 10000 MIN_FREEZE_TIME = 4000 ICETRAP = 'IceTrap' -# 植物卡片信息 +# 植物卡片名称 CARD_SUNFLOWER = 'card_sunflower' CARD_PEASHOOTER = 'card_peashooter' CARD_SNOWPEASHOOTER = 'card_snowpea' @@ -373,6 +373,136 @@ CARD_GRAVEBUSTER = 'card_gravebuster' CARD_FUMESHROOM = 'card_fumeshroom' CARD_GARLIC = 'card_garlic' + +# 植物卡片信息汇总(包括植物名称, 卡片名称, 阳光, 冷却时间) +PLANT_CARD_INFO = (# 元组 (植物名称, 卡片名称, 阳光, 冷却时间) + (PEASHOOTER, + CARD_PEASHOOTER, + 100, + 7500), + (SUNFLOWER, + CARD_SUNFLOWER, + 50, + 7500), + (CHERRYBOMB, + CARD_CHERRYBOMB, + 150, + 50000), + (WALLNUT, + CARD_WALLNUT, + 50, + 30000), + (POTATOMINE, + CARD_POTATOMINE, + 25, + 30000), + (SNOWPEASHOOTER, + CARD_SNOWPEASHOOTER, + 175, + 7500), + (CHOMPER, + CARD_CHOMPER, + 150, + 7500), + (REPEATERPEA, + CARD_REPEATERPEA, + 200, + 7500), + (PUFFSHROOM, + CARD_PUFFSHROOM, + 0, + 7500), + (SUNSHROOM, + CARD_SUNSHROOM, + 25, + 7500), + (FUMESHROOM, + CARD_FUMESHROOM, + 75, + 7500), + (GRAVEBUSTER, + CARD_GRAVEBUSTER, + 75, + 7500), + (HYPNOSHROOM, + CARD_HYPNOSHROOM, + 75, + 30000), + (SCAREDYSHROOM, + CARD_SCAREDYSHROOM, + 25, + 7500), + (ICESHROOM, + CARD_ICESHROOM, + 75, + 50000), + (DOOMSHROOM, + CARD_DOOMSHROOM, + 75, + 50000), + (LILYPAD, + CARD_LILYPAD, + 25, + 7500), + (SQUASH, + CARD_SQUASH, + 50, + 30000), + (TANGLEKLEP, + CARD_TANGLEKLEP, + 25, + 30000), + (THREEPEASHOOTER, + CARD_THREEPEASHOOTER, + 325, + 7500), + (JALAPENO, + CARD_JALAPENO, + 125, + 50000), + (SPIKEWEED, + CARD_SPIKEWEED, + 100, + 7500), + (TORCHWOOD, + CARD_TORCHWOOD, + 175, + 7500), + (TALLNUT, + CARD_TALLNUT, + 125, + 30000), + (SEASHROOM, + CARD_SEASHROOM, + 125, + 30000), + (STARFRUIT, + CARD_STARFRUIT, + 125, + 7500), + (COFFEEBEAN, + CARD_COFFEEBEAN, + 75, + 7500), + (c.GARLIC, + c.CARD_GARLIC, + 50, + 7500), + # 应当保证这两个在一般模式下不可选的特殊植物恒在最后 + (WALLNUTBOWLING, + CARD_WALLNUT, + 0, + 0), + (REDWALLNUTBOWLING, + CARD_REDWALLNUT, + 0, + 0), + ) + +# 指定了哪些卡可选(排除坚果保龄球特殊植物) +CARDS_TO_CHOOSE = range(len(PLANT_CARD_INFO) - 2) + + # 子弹信息 # 子弹类型 BULLET_PEA = 'PeaNormal' diff --git a/source/state/level.py b/source/state/level.py index e4e5c91..7e800fa 100644 --- a/source/state/level.py +++ b/source/state/level.py @@ -2,7 +2,7 @@ import os import json import sys import pygame as pg -from random import randint, uniform, choices +import random from .. import tool from .. import constants as c from ..component import map, plant, zombie, menubar @@ -41,7 +41,7 @@ class Level(tool.State): f = open(file_path) self.map_data = json.load(f) f.close() - except Exception: + except: print("成功通关!") if self.game_info[c.GAME_MODE] == c.MODE_LITTLEGAME: self.game_info[c.LEVEL_NUM] = c.START_LEVEL_NUM @@ -147,7 +147,7 @@ class Level(tool.State): minCost = c.CREATE_ZOMBIE_DICT[min(useableZombies, key=lambda x:c.CREATE_ZOMBIE_DICT[x][0])][0] while (volume >= minCost) and (len(zombieList) < 50): - newZombie = choices(useableZombies, weights)[0] + newZombie = random.choices(useableZombies, weights)[0] # 普通僵尸、路障僵尸、铁桶僵尸有概率生成水中变种 if self.background_type in c.POOL_EQUIPPED_BACKGROUNDS: # 有泳池第一轮的第四波设定上生成水生僵尸 @@ -155,7 +155,7 @@ class Level(tool.State): if newZombie in c.CONVERT_ZOMBIE_IN_POOL: newZombie = c.CONVERT_ZOMBIE_IN_POOL[newZombie] elif survivalRounds > 0 or wave > 4: - if randint(1, 3) == 1: # 1/3概率水上,暂时人为设定 + if random.randint(1, 3) == 1: # 1/3概率水上,暂时人为设定 if newZombie in c.CONVERT_ZOMBIE_IN_POOL: newZombie = c.CONVERT_ZOMBIE_IN_POOL[newZombie] # 首先几轮不出水生僵尸 @@ -165,7 +165,7 @@ class Level(tool.State): zombieList.append(newZombie) volume -= c.CREATE_ZOMBIE_DICT[newZombie][0] waves.append(zombieList) - #print(wave, zombieList, len(zombieList)) + # print(wave, zombieList, len(zombieList)) self.waves = waves @@ -201,20 +201,20 @@ class Level(tool.State): elif c.GRAVE not in self.map.map[mapY][mapX][c.MAP_PLANT]: occupied.append((mapX, mapY)) if unoccupied: - target = unoccupied[randint(0, len(unoccupied) - 1)] + target = unoccupied[random.randint(0, len(unoccupied) - 1)] mapX, mapY = target posX, posY = self.map.getMapGridPos(mapX, mapY) self.plant_groups[mapY].add(plant.Grave(posX, posY)) self.map.map[mapY][mapX][c.MAP_PLANT].add(c.GRAVE) self.graveSet.add((mapX, mapY)) elif occupied: - target = occupied[randint(0, len(occupied) - 1)] + target = occupied[random.randint(0, len(occupied) - 1)] mapX, mapY = target posX, posY = self.map.getMapGridPos(mapX, mapY) for i in self.plant_groups[mapY]: checkMapX, _ = self.map.getMapIndex(i.rect.centerx, i.rect.bottom) if mapX == checkMapX: - # 不能杀死毁灭菇坑和冰道 + # 不杀死毁灭菇坑和冰道 if i.name not in exceptionObjects: i.health = 0 self.plant_groups[mapY].add(plant.Grave(posX, posY)) @@ -227,7 +227,7 @@ class Level(tool.State): for item in self.graveSet: itemX, itemY = self.map.getMapGridPos(*item) # 目前设定:2/3概率普通僵尸,1/3概率路障僵尸 - if randint(0, 2): + if random.randint(0, 2): self.zombie_groups[item[1]].add(zombie.NormalZombie(itemX, itemY, self.head_group)) else: self.zombie_groups[item[1]].add(zombie.ConeHeadZombie(itemX, itemY, self.head_group)) @@ -237,11 +237,11 @@ class Level(tool.State): if current_time - self.waveTime > 1500: for i in range(3): # 水中倒数四列内可以在此时产生僵尸。共产生3个 - mapX, mapY = randint(5, 8), randint(2, 3) + mapX, mapY = random.randint(5, 8), random.randint(2, 3) itemX, itemY = self.map.getMapGridPos(mapX, mapY) # 用随机数指定产生的僵尸类型 # 带有权重 - zombieType = randint(1, 6) + zombieType = random.randint(1, 6) if zombieType == 1: self.zombie_groups[mapY].add(zombie.BucketHeadDuckyTubeZombie(itemX, itemY, self.head_group)) elif zombieType <= 3: @@ -272,7 +272,7 @@ class Level(tool.State): pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "zombieComing.ogg")).play() 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))): + if ((current_time - self.waveTime >= 25000 + random.randint(0, 6000)) or (self.bar_type != c.CHOOSEBAR_STATIC and current_time - self.waveTime >= 12500 + random.randint(0, 3000))): self.waveNum += 1 self.waveTime = current_time self.waveZombies = self.waves[self.waveNum - 1] @@ -293,7 +293,7 @@ class Level(tool.State): 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): + if (numZombies / self.numZombie < random.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 @@ -364,7 +364,7 @@ class Level(tool.State): def initChoose(self): self.state = c.CHOOSE - self.panel = menubar.Panel(menubar.cards_to_choose, self.map_data[c.INIT_SUN_NAME]) + self.panel = menubar.Panel(c.CARDS_TO_CHOOSE, self.map_data[c.INIT_SUN_NAME]) # 播放选卡音乐 pg.mixer.music.stop() @@ -417,7 +417,7 @@ class Level(tool.State): self.removeMouseImage() self.setupGroups() - if (c.ZOMBIE_LIST in self.map_data) and self.map_data[c.SPAWN_ZOMBIES] == c.SPAWN_ZOMBIES_LIST: + if self.map_data[c.SPAWN_ZOMBIES] == c.SPAWN_ZOMBIES_LIST: self.setupZombies() else: # 僵尸波数数据及僵尸生成数据 @@ -468,8 +468,8 @@ class Level(tool.State): graveVolume = c.GRAVES_GRADE_INFO[gradeGraves] self.graveSet = set() while len(self.graveSet) < graveVolume: - mapX = randint(4, 8) # 注意是从0开始编号 - mapY = randint(0, 4) + mapX = random.randint(4, 8) # 注意是从0开始编号 + mapY = random.randint(0, 4) self.graveSet.add((mapX, mapY)) if self.graveSet: for i in self.graveSet: @@ -656,7 +656,7 @@ class Level(tool.State): self.pauseAndCheckLittleMenuOptions(mouse_pos, mouse_click) return - if (c.ZOMBIE_LIST in self.map_data) and self.map_data[c.SPAWN_ZOMBIES] == c.SPAWN_ZOMBIES_LIST: + if self.map_data[c.SPAWN_ZOMBIES] == c.SPAWN_ZOMBIES_LIST: # 旧僵尸生成方式 if self.zombie_start_time == 0: self.zombie_start_time = self.current_time @@ -694,7 +694,7 @@ class Level(tool.State): 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): + if (self.current_time - self.sun_timer) > min(c.PRODUCE_SUN_INTERVAL + 100*self.fallenSun, 9500) + random.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) @@ -728,8 +728,8 @@ class Level(tool.State): clickedCardsOrMap = True self.clickResult[1].clicked = False elif mouse_click[0]: - self.clickResult[1].clicked = False if self.menubar.checkMenuBarClick(mouse_pos): + self.clickResult[1].clicked = False self.removeMouseImage() else: self.addPlant() @@ -778,22 +778,22 @@ class Level(tool.State): # 情况复杂:分水路和陆路,不能简单实现,需要另外加判断 # 0, 1, 4, 5路为陆路,2, 3路为水路 if self.map_data[c.BACKGROUND_TYPE] in c.POOL_EQUIPPED_BACKGROUNDS: - if name in c.WATER_ZOMBIE: # 水生僵尸集合 - map_y = randint(2, 3) + if name in c.WATER_ZOMBIE: + map_y = random.randint(2, 3) elif name == '这里应该换成气球僵尸的名字(最好写调用的变量名,最好不要直接写,保持风格统一)': - map_y = randint(0, 5) + map_y = random.randint(0, 5) else: # 陆生僵尸 - map_y = randint(0, 3) + map_y = random.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) + map_y = random.randint(1, 3) else: - map_y = randint(0, 4) + map_y = random.randint(0, 4) - if not ((c.ZOMBIE_LIST in self.map_data) and self.map_data[c.SPAWN_ZOMBIES] == c.SPAWN_ZOMBIES_LIST): + if self.map_data[c.SPAWN_ZOMBIES] == c.SPAWN_ZOMBIES_AUTO: # 旗帜波出生点右移 if self.waveNum % 10: hugeWaveMove = 0 @@ -802,35 +802,37 @@ class Level(tool.State): else: hugeWaveMove = 0 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)) + self.zombie_groups[map_y].add(zombie.NormalZombie(c.ZOMBIE_START_X + random.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)) + self.zombie_groups[map_y].add(zombie.ConeHeadZombie(c.ZOMBIE_START_X + random.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)) + self.zombie_groups[map_y].add(zombie.BucketHeadZombie(c.ZOMBIE_START_X + random.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)) + self.zombie_groups[map_y].add(zombie.NewspaperZombie(c.ZOMBIE_START_X + random.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)) + self.zombie_groups[map_y].add(zombie.FootballZombie(c.ZOMBIE_START_X + random.randint(-20, 20) + hugeWaveMove, y, self.head_group)) elif name == c.DUCKY_TUBE_ZOMBIE: - self.zombie_groups[map_y].add(zombie.DuckyTubeZombie(c.ZOMBIE_START_X + randint(-20, 20) + hugeWaveMove, y, self.head_group)) + self.zombie_groups[map_y].add(zombie.DuckyTubeZombie(c.ZOMBIE_START_X + random.randint(-20, 20) + hugeWaveMove, y, self.head_group)) elif name == c.CONEHEAD_DUCKY_TUBE_ZOMBIE: - self.zombie_groups[map_y].add(zombie.ConeHeadDuckyTubeZombie(c.ZOMBIE_START_X + randint(-20, 20) + hugeWaveMove, y, self.head_group)) + self.zombie_groups[map_y].add(zombie.ConeHeadDuckyTubeZombie(c.ZOMBIE_START_X + random.randint(-20, 20) + hugeWaveMove, y, self.head_group)) elif name == c.BUCKETHEAD_DUCKY_TUBE_ZOMBIE: - self.zombie_groups[map_y].add(zombie.BucketHeadDuckyTubeZombie(c.ZOMBIE_START_X + randint(-20, 20) + hugeWaveMove, y, self.head_group)) + self.zombie_groups[map_y].add(zombie.BucketHeadDuckyTubeZombie(c.ZOMBIE_START_X + random.randint(-20, 20) + hugeWaveMove, y, self.head_group)) elif name == c.SCREEN_DOOR_ZOMBIE: - self.zombie_groups[map_y].add(zombie.ScreenDoorZombie(c.ZOMBIE_START_X + randint(-20, 20) + hugeWaveMove, y, self.head_group)) + self.zombie_groups[map_y].add(zombie.ScreenDoorZombie(c.ZOMBIE_START_X + random.randint(-20, 20) + hugeWaveMove, y, self.head_group)) elif name == c.POLE_VAULTING_ZOMBIE: # 本来撑杆跳生成位置不同,对齐左端可认为修正了一部分(看作移动了70),只需要相对修改即可 - self.zombie_groups[map_y].add(zombie.PoleVaultingZombie(c.ZOMBIE_START_X + randint(0, 10) + hugeWaveMove, y, self.head_group)) + self.zombie_groups[map_y].add(zombie.PoleVaultingZombie(c.ZOMBIE_START_X + random.randint(0, 10) + hugeWaveMove, y, self.head_group)) elif name == c.ZOMBONI: # 冰车僵尸生成位置不同 - self.zombie_groups[map_y].add(zombie.Zomboni(c.ZOMBIE_START_X + randint(0, 10) + hugeWaveMove, y, self.plant_groups[map_y], self.map, plant.IceFrozenPlot)) + self.zombie_groups[map_y].add(zombie.Zomboni(c.ZOMBIE_START_X + random.randint(0, 10) + hugeWaveMove, y, self.plant_groups[map_y], self.map, plant.IceFrozenPlot)) elif name == c.SNORKELZOMBIE: - self.zombie_groups[map_y].add(zombie.SnorkelZombie(c.ZOMBIE_START_X + randint(0, 10) + hugeWaveMove, y, self.head_group)) + # 潜水僵尸生成位置不同 + self.zombie_groups[map_y].add(zombie.SnorkelZombie(c.ZOMBIE_START_X + random.randint(0, 10) + hugeWaveMove, y, self.head_group)) # 能否种植物的判断: # 先判断位置是否合法 isValid(map_x, map_y) @@ -845,6 +847,9 @@ class Level(tool.State): if pos is None: return + # 恢复植物卡片样式 + self.clickResult[1].clicked = False + if self.hint_image is None: self.setupHintImage() x, y = self.hint_rect.centerx, self.hint_rect.bottom @@ -924,7 +929,7 @@ class Level(tool.State): mushroomSleep = False self.plant_groups[map_y].add(new_plant) # 种植植物后应当刷新僵尸的攻击对象 - # 这里用植物名称代替布尔值,保存更多信息 + # 用元组表示植物的名称和格子坐标 self.newPlantAndPositon = (new_plant.name, (map_x, map_y)) if self.bar_type == c.CHOOSEBAR_STATIC: self.menubar.decreaseSunValue(self.select_plant.sun_cost) @@ -932,8 +937,8 @@ class Level(tool.State): 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) + if self.bar_type != c.CHOSSEBAR_BOWLING: # 坚果保龄球关卡无需考虑格子被占用的情况 + self.map.addMapPlant(map_x, map_y, self.plant_name, sleep=mushroomSleep) self.removeMouseImage() # print(self.newPlantAndPositon) @@ -1149,8 +1154,8 @@ class Level(tool.State): 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) + 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 @@ -1372,7 +1377,7 @@ class Level(tool.State): self.killPlant(plant) def checkVictory(self): - if (c.ZOMBIE_LIST in self.map_data) and self.map_data[c.SPAWN_ZOMBIES] == c.SPAWN_ZOMBIES_LIST: + if 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): @@ -1522,7 +1527,7 @@ class Level(tool.State): 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) and self.map_data[c.SPAWN_ZOMBIES] == c.SPAWN_ZOMBIES_LIST): + if self.map_data[c.SPAWN_ZOMBIES] == c.SPAWN_ZOMBIES_AUTO: self.showLevelProgress(surface) if self.current_time - self.showHugeWaveApprochingTime <= 2000: surface.blit(self.huge_wave_approching_image, self.huge_wave_approching_image_rect) diff --git a/source/tool.py b/source/tool.py index 4961fd2..d35852e 100755 --- a/source/tool.py +++ b/source/tool.py @@ -97,7 +97,7 @@ class Control(): while not self.done: self.event_loop() self.update() - pg.display.flip() + pg.display.update() self.clock.tick(self.fps) print('game over') @@ -106,7 +106,7 @@ def get_image(sheet, x, y, width, height, colorkey=c.BLACK, scale=1): rect = image.get_rect() image.blit(sheet, (0, 0), (x, y, width, height)) - if colorkey != c.NULL: + if colorkey: image.set_colorkey(colorkey) image = pg.transform.scale(image, (int(rect.width*scale), @@ -203,6 +203,10 @@ def loadPlantImageRect(): pg.init() pg.display.set_caption(c.ORIGINAL_CAPTION) # 设置标题 SCREEN = pg.display.set_mode(c.SCREEN_SIZE) # 设置初始屏幕 +try: # 设置窗口图标,仅对非Nuitka时生效,Nuitka不需要包括额外的图标文件,自动跳过这一过程即可 + pg.display.set_icon(pg.image.load(os.path.join(os.path.dirname(os.path.dirname(__file__)), c.ORIGINAL_LOGO))) +except: + pass GFX = load_all_gfx(os.path.join(os.path.dirname(os.path.dirname(__file__)) ,os.path.join("resources","graphics"))) ZOMBIE_RECT = loadZombieImageRect()