From 6508bdcdb7225ace863673979c983ccebb3c2b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=9F=E5=A4=96=E4=B9=8B=E7=A5=9E?= Date: Thu, 28 Jul 2022 15:16:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=A8=E9=9D=A2=E6=9B=B4=E6=94=B9=E9=9F=B3?= =?UTF-8?q?=E4=B9=90=E6=9C=BA=E5=88=B6=EF=BC=8C=E6=94=AF=E6=8C=81=E8=B0=83?= =?UTF-8?q?=E8=8A=82=E9=9F=B3=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pypvz.py | 4 ++ source/component/menubar.py | 4 +- source/component/plant.py | 55 +++++++++++++------------- source/component/zombie.py | 20 +++++----- source/constants.py | 78 ++++++++++++++++++++++++++++++++++++- source/state/level.py | 36 +++++++++-------- source/state/mainmenu.py | 59 ++++++++++++++++++++-------- source/state/screen.py | 6 +-- source/tool.py | 2 +- 9 files changed, 185 insertions(+), 79 deletions(-) diff --git a/pypvz.py b/pypvz.py index 03ee84c..f630e6f 100755 --- a/pypvz.py +++ b/pypvz.py @@ -2,7 +2,11 @@ import logging import traceback import os +import pygame as pg from logging.handlers import RotatingFileHandler +# 由于在后续本地模块中存在对pygame的调用,因此必须在这里完成pygame的初始化 +pg.init() + from source import tool from source import constants as c from source.state import mainmenu, screen, level diff --git a/source/component/menubar.py b/source/component/menubar.py index 44cfd47..344b78e 100755 --- a/source/component/menubar.py +++ b/source/component/menubar.py @@ -265,7 +265,7 @@ class Panel(): self.selected_cards.remove(delete_card) self.selected_num -= 1 # 播放点击音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "tap.ogg")).play() + c.SOUND_TAPPING_CARD.play() if self.selected_num >= c.CARD_MAX_NUM: return @@ -275,7 +275,7 @@ class Panel(): if card.canSelect(): self.addCard(card) # 播放点击音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "tap.ogg")).play() + c.SOUND_TAPPING_CARD.play() break def addCard(self, card): diff --git a/source/component/plant.py b/source/component/plant.py index 1a51c69..533aa2c 100755 --- a/source/component/plant.py +++ b/source/component/plant.py @@ -31,7 +31,7 @@ class Car(pg.sprite.Sprite): if self.state == c.IDLE: self.state = c.WALK # 播放音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "carWalking.ogg")).play() + c.SOUND_CAR_WALKING.play() def draw(self, surface): surface.blit(self.image, self.rect) @@ -127,9 +127,9 @@ class Bullet(pg.sprite.Sprite): # 播放子弹爆炸音效 if self.name == c.BULLET_FIREBALL: - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "firepea.ogg")).play() + c.SOUND_FIREPEA_EXPLODE.play() else: - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "bulletExplode.ogg")).play() + c.SOUND_BULLET_EXPLODE.play() def draw(self, surface): surface.blit(self.image, self.rect) @@ -417,7 +417,7 @@ class PeaShooter(Plant): c.BULLET_PEA, c.BULLET_DAMAGE_NORMAL, effect=None)) self.shoot_timer = self.current_time # 播放发射音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "shoot.ogg")).play() + c.SOUND_SHOOT.play() def setAttack(self): self.state = c.ATTACK @@ -441,13 +441,13 @@ class RepeaterPea(Plant): c.BULLET_PEA, c.BULLET_DAMAGE_NORMAL, effect=None)) self.shoot_timer = self.current_time # 播放发射音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "shoot.ogg")).play() + c.SOUND_SHOOT.play() elif self.firstShot and (self.current_time - self.shoot_timer) > 100: self.firstShot = False self.bullet_group.add(Bullet(self.rect.right - 15, self.rect.y, self.rect.y, c.BULLET_PEA, c.BULLET_DAMAGE_NORMAL, effect=None)) # 播放发射音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "shoot.ogg")).play() + c.SOUND_SHOOT.play() def setAttack(self): self.state = c.ATTACK @@ -483,7 +483,7 @@ class ThreePeaShooter(Plant): c.BULLET_PEA, c.BULLET_DAMAGE_NORMAL, effect=None)) self.shoot_timer = self.current_time # 播放发射音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "shoot.ogg")).play() + c.SOUND_SHOOT.play() def setAttack(self): self.state = c.ATTACK @@ -503,9 +503,9 @@ class SnowPeaShooter(Plant): c.BULLET_PEA_ICE, c.BULLET_DAMAGE_NORMAL, effect=c.BULLET_EFFECT_ICE)) self.shoot_timer = self.current_time # 播放发射音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "shoot.ogg")).play() + c.SOUND_SHOOT.play() # 播放冰子弹音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "snowPeaSparkles.ogg")).play() + c.SOUND_SNOWPEA_SPARKLES().play() def setAttack(self): self.state = c.ATTACK @@ -567,7 +567,7 @@ class CherryBomb(Plant): if self.bomb_timer == 0: self.bomb_timer = self.current_time # 播放爆炸音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "bomb.ogg")).play() + c.SOUND_BOMB.play() elif (self.current_time - self.bomb_timer) > 500: self.health = 0 else: @@ -647,7 +647,7 @@ class Chomper(Plant): def attacking(self): if self.frame_index == (self.frame_num - 3): # 播放吞的音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "bigchomp.ogg")).play() + c.SOUND_BIGCHOMP.play() if self.attack_zombie.alive(): self.shouldDiggest = True self.attack_zombie.kill() @@ -695,7 +695,7 @@ class PuffShroom(Plant): c.BULLET_MUSHROOM, c.BULLET_DAMAGE_NORMAL, effect=None)) self.shoot_timer = self.current_time # 播放音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "puff.ogg")).play() + c.SOUND_PUFF.play() def canAttack(self, zombie): if (zombie.name == c.SNORKELZOMBIE) and (zombie.frames == zombie.swim_frames): @@ -760,7 +760,7 @@ class PotatoMine(Plant): if self.bomb_timer == 0: self.bomb_timer = self.current_time # 播放音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "potatomine.ogg")).play() + c.SOUND_POTATOMINE.play() self.changeFrames(self.explode_frames) self.start_boom = True elif (self.current_time - self.bomb_timer) > 500: @@ -820,10 +820,10 @@ class Squash(Plant): self.mapPlantsSet.remove(c.SQUASH) self.kill() # 播放碾压音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "squashing.ogg")).play() + c.SOUND_SQUASHING.play() elif self.aim_timer == 0: # 锁定目标时播放音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "squashHmm.ogg")).play() + c.SOUND_SQUASH_HMM.play() self.aim_timer = self.current_time self.changeFrames(self.aim_frames) elif (self.current_time - self.aim_timer) > 1000: @@ -881,7 +881,7 @@ class Spikeweed(Plant): if killSelf: self.health = 0 # 播放攻击音效,同子弹打击 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "bulletExplode.ogg")).play() + c.SOUND_BULLET_EXPLODE.play() class Jalapeno(Plant): @@ -912,7 +912,7 @@ class Jalapeno(Plant): if (self.current_time - self.animate_timer) > 100: if self.frame_index == 1: # 播放爆炸音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "bomb.ogg")).play() + c.SOUND_BOMB.play() self.frame_index += 1 if self.frame_index >= self.frame_num: self.health = 0 @@ -990,7 +990,7 @@ class ScaredyShroom(Plant): c.BULLET_MUSHROOM, c.BULLET_DAMAGE_NORMAL, effect=None)) self.shoot_timer = self.current_time # 播放音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "puff.ogg")).play() + c.SOUND_PUFF.play() class SunShroom(Plant): @@ -1028,7 +1028,7 @@ class SunShroom(Plant): self.changeFrames(self.big_frames) self.is_big = True # 播放长大音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "plantGrow.ogg")).play() + c.SOUND_PLANT_GROW.play() 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: @@ -1262,7 +1262,7 @@ class RedWallNutBowling(Plant): self.explode_timer = self.current_time self.changeFrames(self.explode_frames) # 播放爆炸音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "bomb.ogg")).play() + c.SOUND_BOMB.play() elif (self.current_time - self.explode_timer) > 500: self.health = 0 @@ -1347,7 +1347,7 @@ class StarFruit(Plant): self.bullet_group.add(StarBullet(self.rect.right - 5, self.rect.y - 10, c.BULLET_DAMAGE_NORMAL, c.STAR_FORWARD_UP, self.level)) self.shoot_timer = self.current_time # 播放发射音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "shoot.ogg")).play() + c.SOUND_SHOOT.play() def setAttack(self): self.state = c.ATTACK @@ -1378,7 +1378,7 @@ class CoffeeBean(Plant): plant.setIdle() plant.changeFrames(plant.idle_frames) # 播放唤醒音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "mushroomWakeup.ogg")).play() + c.SOUND_MUSHROOM_WAKEUP.play() self.mapContent[c.MAP_PLANT].remove(self.name) self.kill() self.frame_index = self.frame_num - 1 @@ -1424,7 +1424,7 @@ class SeaShroom(Plant): c.BULLET_SEASHROOM, c.BULLET_DAMAGE_NORMAL, effect=None)) self.shoot_timer = self.current_time # 播放发射音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "puff.ogg")).play() + c.SOUND_PUFF.play() def canAttack(self, zombie): if (zombie.name == c.SNORKELZOMBIE) and (zombie.frames == zombie.swim_frames): @@ -1505,7 +1505,7 @@ class TangleKlep(Plant): self.changeFrames(self.splash_frames) self.attack_zombie.kill() # 播放拖拽音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "tangleKelpDrag.ogg")).play() + c.SOUND_TANGLE_KELP_DRAG.play() # 这里必须用elif排除尚未进入splash阶段,以免误触 elif (self.frame_index + 1) >= self.frame_num: self.health = 0 @@ -1558,7 +1558,7 @@ class DoomShroom(Plant): self.rect.x -= 80 self.rect.y += 30 # 播放爆炸音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "doomshroom.ogg")).play() + c.SOUND_DOOMSHROOM.play() if (self.current_time - self.animate_timer) > self.animate_interval: self.frame_index += 1 if self.frame_index >= self.frame_num: @@ -1667,8 +1667,7 @@ class GraveBuster(Plant): self.plant_group = plant_group self.animate_interval = 100 # 播放吞噬音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "gravebusterchomp.ogg")).play() - + c.SOUND_GRAVEBUSTER_CHOMP.play() def animation(self): if (self.current_time - self.animate_timer) > self.animate_interval: @@ -1748,7 +1747,7 @@ class FumeShroom(Plant): self.shoot_timer = self.current_time self.showAttackFrames = True # 播放发射音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "fume.ogg")).play() + c.SOUND_FUME.play() def animation(self): if (self.current_time - self.animate_timer) > self.animate_interval: diff --git a/source/component/zombie.py b/source/component/zombie.py index 2b0adab..33d1f23 100755 --- a/source/component/zombie.py +++ b/source/component/zombie.py @@ -117,7 +117,7 @@ class Zombie(pg.sprite.Sprite): self.swimming = True self.changeFrames(self.swim_frames) # 播放入水音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "zombieEnteringWater.ogg")).play() + c.SOUND_ZOMBIE_ENTERING_WATER.play() # 同样没有兼容双防具 if self.helmet: if self.helmetHealth <= 0: @@ -256,7 +256,7 @@ class Zombie(pg.sprite.Sprite): self.prey.setDamage(self.damage) # 播放啃咬音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "zombieAttack.ogg")).play() + c.SOUND_ZOMBIE_ATTACKING.play() self.attack_timer = self.current_time if self.prey.health <= 0: @@ -333,7 +333,7 @@ class Zombie(pg.sprite.Sprite): def setIceSlow(self): # 在转入冰冻减速状态时播放冰冻音效 if self.ice_slow_ratio == 1: - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "freeze.ogg")).play() + c.SOUND_FREEZE.play() # when get a ice bullet damage, slow the attack or walk speed of the zombie self.ice_slow_timer = self.current_time @@ -509,7 +509,7 @@ class Zombie(pg.sprite.Sprite): self.is_hypno = True self.setWalk() # 播放魅惑音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "hypnoed.ogg")).play() + c.SOUND_HYPNOED.play() class ZombieHead(Zombie): @@ -712,7 +712,7 @@ class NewspaperZombie(Zombie): self.changeFrames(self.lostnewspaper_frames) self.helmetType2 = False # 触发报纸撕裂音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "newspaperRip.ogg")).play() + c.SOUND_NEWSPAPER_RIP.play() if ((self.current_time - self.walk_timer) > (c.ZOMBIE_WALK_INTERVAL * self.getTimeRatio())): self.handleGarlicYChange() self.walk_timer = self.current_time @@ -740,7 +740,7 @@ class NewspaperZombie(Zombie): 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() + c.SOUND_NEWSPAPER_ZOMBIE_ANGRY.play() return self.frame_index = 0 self.animate_timer = self.current_time @@ -983,7 +983,7 @@ class PoleVaultingZombie(Zombie): self.successfullyJumped = successfullyJumped self.jumpX = jumpX # 播放跳跃音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "polevaultjump.ogg")).play() + c.SOUND_POLEVAULT_JUMP.play() def animation(self): if self.state == c.FREEZE: @@ -1051,7 +1051,7 @@ class Zomboni(Zombie): self.die_animate_interval = 70 self.boomDie_animate_interval = 150 # 播放冰车生成音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "zomboni.ogg")).play() + c.SOUND_ZOMBONI.play() def loadImages(self): self.walk_frames = [] @@ -1126,7 +1126,7 @@ class Zomboni(Zombie): self.animate_interval = self.die_animate_interval self.changeFrames(self.die_frames) # 播放冰车爆炸音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "zomboniExplosion.ogg")).play() + c.SOUND_ZOMBONI_EXPLOSION.play() class SnorkelZombie(Zombie): @@ -1189,7 +1189,7 @@ class SnorkelZombie(Zombie): self.swimming = True self.changeFrames(self.jump_frames) # 播放入水音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "zombieEnteringWater.ogg")).play() + c.SOUND_ZOMBIE_ENTERING_WATER.play() # 已经接近家门口并且上岸 else: if self.swimming: diff --git a/source/constants.py b/source/constants.py index 351d01d..c5536df 100755 --- a/source/constants.py +++ b/source/constants.py @@ -1,4 +1,6 @@ import os +import pygame as pg + # 用户数据及日志存储路径 if os.name == 'nt': # Windows系统存储路径 USERDATA_PATH = os.path.expandvars(os.path.join("%APPDATA%", "wszqkzqk.dev", "pypvz", "userdata.json")) @@ -58,7 +60,8 @@ RED = (255, 0, 0) PURPLE = (255, 0, 255) GOLD = (255, 215, 0) GREEN = ( 0, 255, 0) -YELLOWGREEN = ( 55, 200, 0) +YELLOWGREEN = ( 55, 200, 0) +LIGHTGRAY = (107, 108, 145) # 退出游戏按钮 EXIT = 'exit' @@ -91,6 +94,7 @@ LITTLEGAME_NUM = 'littleGame num' LEVEL_COMPLETIONS = 'level completions' LITTLEGAME_COMPLETIONS = 'littleGame completions' GAME_RATE = 'game rate' +VOLUME = 'volume' # 整个游戏的状态 MAIN_MENU = 'main menu' @@ -662,6 +666,77 @@ SLEEP = 'sleep' CHOOSE = 'choose' PLAY = 'play' +# 音效 +def _getSound(filename): + return pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(__file__)) ,"resources", "sound", filename)) +# 程序交互等 +SOUND_TAPPING_CARD = _getSound("tap.ogg") +# 植物 +SOUND_FIREPEA_EXPLODE = _getSound("firepea.ogg") +SOUND_BULLET_EXPLODE = _getSound("bulletExplode.ogg") +SOUND_SHOOT = _getSound("shoot.ogg") +SOUND_SNOWPEA_SPARKLES = _getSound("snowPeaSparkles.ogg") +SOUND_BOMB = _getSound("bomb.ogg") +SOUND_BIGCHOMP = _getSound("bigchomp.ogg") +SOUND_PUFF = _getSound("puff.ogg") +SOUND_POTATOMINE = _getSound("potatomine.ogg") +SOUND_SQUASHING = _getSound("squashing.ogg") +SOUND_SQUASH_HMM = _getSound("squashHmm.ogg") +SOUND_PLANT_GROW = _getSound("plantGrow.ogg") +SOUND_MUSHROOM_WAKEUP = _getSound("mushroomWakeup.ogg") +SOUND_TANGLE_KELP_DRAG = _getSound("tangleKelpDrag.ogg") +SOUND_DOOMSHROOM = _getSound("doomshroom.ogg") +SOUND_GRAVEBUSTER_CHOMP = _getSound("gravebusterchomp.ogg") +SOUND_FUME = _getSound("fume.ogg") +# 僵尸 +SOUND_ZOMBIE_ENTERING_WATER = _getSound("zombieEnteringWater.ogg") +SOUND_ZOMBIE_ATTACKING = _getSound("zombieAttack.ogg") +SOUND_FREEZE = _getSound("freeze.ogg") +SOUND_HYPNOED = _getSound("hypnoed.ogg") +SOUND_NEWSPAPER_RIP = _getSound("newspaperRip.ogg") +SOUND_NEWSPAPER_ZOMBIE_ANGRY = _getSound("newspaperZombieAngry.ogg") +SOUND_POLEVAULT_JUMP = _getSound("polevaultjump.ogg") +SOUND_ZOMBONI = _getSound("zomboni.ogg") +SOUND_ZOMBONI_EXPLOSION = _getSound("zomboniExplosion.ogg") +# 关卡中 +SOUND_CAR_WALKING = _getSound("carWalking.ogg") +SOUND_ZOMBIE_COMING = _getSound("zombieComing.ogg") +SOUND_ZOMBIE_VOICE = _getSound("zombieVoice.ogg") +SOUND_HUGE_WAVE_APPROCHING = _getSound("hugeWaveApproching.ogg") +SOUND_BUTTON_CLICK = _getSound("buttonclick.ogg") +SOUND_COLLECT_SUN = _getSound("collectSun.ogg") +SOUND_CLICK_CARD = _getSound("clickCard.ogg") +SOUND_SHOVEL = _getSound("shovel.ogg") +SOUND_PLANT = _getSound("plant.ogg") +SOUND_BOWLING_IMPACT = _getSound("bowlingimpact.ogg") +SOUND_PLANT_DIE = _getSound("plantDie.ogg") +SOUND_EVILLAUGH = _getSound("evillaugh.ogg") +SOUND_LOSE = _getSound("lose.ogg") +SOUND_WIN = _getSound("win.ogg") +SOUND_SCREAM = _getSound("scream.ogg") +# 所有音效的元组 +SOUNDS = ( SOUND_TAPPING_CARD, SOUND_CAR_WALKING, + SOUND_FIREPEA_EXPLODE, SOUND_BULLET_EXPLODE, + SOUND_BOMB, SOUND_BIGCHOMP, + SOUND_PUFF, SOUND_POTATOMINE, + SOUND_SQUASHING, SOUND_SQUASH_HMM, + SOUND_PLANT_GROW, SOUND_MUSHROOM_WAKEUP, + SOUND_TANGLE_KELP_DRAG, SOUND_DOOMSHROOM, + SOUND_GRAVEBUSTER_CHOMP, SOUND_FUME, + SOUND_ZOMBIE_ENTERING_WATER, SOUND_ZOMBIE_ATTACKING, + SOUND_FREEZE, SOUND_HYPNOED, + SOUND_NEWSPAPER_RIP, SOUND_NEWSPAPER_ZOMBIE_ANGRY, + SOUND_POLEVAULT_JUMP, SOUND_ZOMBONI, + SOUND_ZOMBONI_EXPLOSION, SOUND_ZOMBIE_COMING, + SOUND_ZOMBIE_VOICE, SOUND_HUGE_WAVE_APPROCHING, + SOUND_BUTTON_CLICK, SOUND_COLLECT_SUN, + SOUND_CLICK_CARD, SOUND_SHOVEL, + SOUND_PLANT, SOUND_BOWLING_IMPACT, + SOUND_PLANT_DIE, SOUND_EVILLAUGH, + SOUND_LOSE, SOUND_WIN, + SOUND_SCREAM, + ) + # 记录本地存储文件初始值 INIT_USERDATA = { LEVEL_NUM: 1, @@ -669,6 +744,7 @@ INIT_USERDATA = { LEVEL_COMPLETIONS: 0, LITTLEGAME_COMPLETIONS: 0, GAME_RATE: 1, + VOLUME: 1.0, } # 无穷大常量 diff --git a/source/state/level.py b/source/state/level.py index 5865aca..88ad016 100644 --- a/source/state/level.py +++ b/source/state/level.py @@ -270,14 +270,14 @@ class Level(tool.State): self.waveTime = current_time self.waveZombies = self.waves[self.waveNum - 1] self.numZombie = len(self.waveZombies) - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "zombieComing.ogg")).play() + c.SOUND_ZOMBIE_COMING.play() 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) - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "zombieComing.ogg")).play() + c.SOUND_ZOMBIE_COMING.play() return if (self.waveNum % 10 != 9): 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))): @@ -285,7 +285,7 @@ class Level(tool.State): self.waveTime = current_time self.waveZombies = self.waves[self.waveNum - 1] self.numZombie = len(self.waveZombies) - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "zombieVoice.ogg")).play() + c.SOUND_ZOMBIE_VOICE.play() else: if ((current_time - self.waveTime >= 45000) or (self.bar_type != c.CHOOSEBAR_STATIC and current_time - self.waveTime >= 25000)): self.waveNum += 1 @@ -293,7 +293,7 @@ class Level(tool.State): self.waveZombies = self.waves[self.waveNum - 1] self.numZombie = len(self.waveZombies) # 一大波时播放音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "hugeWaveApproching.ogg")).play() + c.SOUND_HUGE_WAVE_APPROCHING.play() return elif ((current_time - self.waveTime >= 43000) or (self.bar_type != c.CHOOSEBAR_STATIC and current_time - self.waveTime >= 23000)): self.showHugeWaveApprochingTime = current_time @@ -378,6 +378,7 @@ class Level(tool.State): pg.mixer.music.stop() pg.mixer.music.load(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "music", "chooseYourSeeds.opus")) pg.mixer.music.play(-1, 0) + pg.mixer.music.set_volume(self.game_info[c.VOLUME]) def choose(self, mouse_pos, mouse_click): # 如果暂停 @@ -391,7 +392,7 @@ class Level(tool.State): self.initPlay(self.panel.getSelectedCards()) elif self.checkLittleMenuClick(mouse_pos): self.showLittleMenu = True - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "buttonclick.ogg")).play() + c.SOUND_BUTTON_CLICK.play() def initPlay(self, card_list): @@ -399,6 +400,7 @@ class Level(tool.State): pg.mixer.music.stop() pg.mixer.music.load(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "music", self.bgm)) pg.mixer.music.play(-1, 0) + pg.mixer.music.set_volume(self.game_info[c.VOLUME]) self.state = c.PLAY if self.bar_type == c.CHOOSEBAR_STATIC: @@ -535,19 +537,19 @@ class Level(tool.State): # 继续播放音乐 pg.mixer.music.unpause() # 播放点击音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "buttonclick.ogg")).play() + c.SOUND_BUTTON_CLICK.play() elif self.checkRestartClick(mouse_pos): self.done = True self.next = c.LEVEL # 播放点击音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "buttonclick.ogg")).play() + c.SOUND_BUTTON_CLICK.play() elif self.checkMainMenuClick(mouse_pos): self.done = True self.next = c.MAIN_MENU self.persist = self.game_info self.persist[c.CURRENT_TIME] = 0 # 播放点击音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "buttonclick.ogg")).play() + c.SOUND_BUTTON_CLICK.play() # 一大波僵尸来袭图片显示 @@ -712,7 +714,7 @@ class Level(tool.State): self.menubar.increaseSunValue(sun.sun_value) clickedSun = True # 播放收集阳光的音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "collectSun.ogg")).play() + c.SOUND_COLLECT_SUN.play() # 拖动植物或者铲子 if not self.drag_plant and mouse_pos and mouse_click[0] and not clickedSun: @@ -722,7 +724,7 @@ class Level(tool.State): self.clickResult[1].clicked = True clickedCardsOrMap = True # 播放音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "clickCard.ogg")).play() + c.SOUND_CLICK_CARD.play() elif self.drag_plant: if mouse_click[1]: self.removeMouseImage() @@ -746,13 +748,13 @@ class Level(tool.State): # 暂停 显示菜单 self.showLittleMenu = True # 播放点击音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "buttonclick.ogg")).play() + c.SOUND_BUTTON_CLICK.play() elif self.checkShovelClick(mouse_pos): self.drag_shovel = not self.drag_shovel if not self.drag_shovel: self.removeMouseImagePlus() # 播放点击铲子的音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "shovel.ogg")).play() + c.SOUND_SHOVEL.play() elif self.drag_shovel: # 移出这地方的植物 self.shovelRemovePlant(mouse_pos) @@ -946,7 +948,7 @@ class Level(tool.State): # print(self.newPlantAndPositon) # 播放种植音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "plant.ogg")).play() + c.SOUND_PLANT.play() def setupHintImage(self): pos = self.canSeedPlant(self.plant_name) @@ -1130,7 +1132,7 @@ class Level(tool.State): zombie.setDamage(c.WALLNUT_BOWLING_DAMAGE, damageType=c.ZOMBIE_WALLNUT_BOWLING_DANMAGE) targetPlant.changeDirection(i) # 播放撞击音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "bowlingimpact.ogg")).play() + c.SOUND_BOWLING_IMPACT.play() elif targetPlant.name == c.REDWALLNUTBOWLING: if targetPlant.state == c.IDLE: targetPlant.setAttack() @@ -1200,7 +1202,7 @@ class Level(tool.State): def freezeZombies(self, plant): # 播放冻结音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "freeze.ogg")).play() + c.SOUND_FREEZE.play() for i in range(self.map_y_len): for zombie in self.zombie_groups[i]: @@ -1231,10 +1233,10 @@ class Level(tool.State): self.plant_groups[map_y].add(plant.Hole(targetPlant.originalX, targetPlant.originalY, self.map.map[map_y][map_x][c.MAP_PLOT_TYPE])) elif targetPlant.name not in c.PLANT_DIE_SOUND_EXCEPTIONS: # 触发植物死亡音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "plantDie.ogg")).play() + c.SOUND_PLANT_DIE.play() else: # 用铲子移除植物时播放音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "plant.ogg")).play() + c.SOUND_PLANT.play() # 整理地图信息 if self.bar_type != c.CHOSSEBAR_BOWLING: diff --git a/source/state/mainmenu.py b/source/state/mainmenu.py index 70592a9..6c418f7 100644 --- a/source/state/mainmenu.py +++ b/source/state/mainmenu.py @@ -20,6 +20,7 @@ class Menu(tool.State): 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) pg.display.set_caption(c.ORIGINAL_CAPTION) + pg.mixer.music.set_volume(self.game_info[c.VOLUME]) def setupBackground(self): frame_rect = (80, 0, 800, 600) @@ -141,8 +142,8 @@ class Menu(tool.State): self.adventure_timer = self.adventure_start = self.current_time self.persist[c.GAME_MODE] = c.MODE_ADVENTURE # 播放进入音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "evillaugh.ogg")).play() - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "lose.ogg")).play() + c.SOUND_EVILLAUGH.play() + c.SOUND_LOSE.play() # 点击到按钮,修改转态的done属性 def checkExitClick(self, mouse_pos): @@ -158,7 +159,7 @@ class Menu(tool.State): self.done = True self.persist[c.GAME_MODE] = c.MODE_LITTLEGAME # 播放点击音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "buttonclick.ogg")).play() + c.SOUND_BUTTON_CLICK.play() def setupOptionMenu(self): # 选项菜单框 @@ -187,7 +188,7 @@ class Menu(tool.State): sign_rect.y = -4 self.volume_plus_button.blit(sign, sign_rect) self.volume_plus_button_rect = self.volume_plus_button.get_rect() - self.volume_plus_button_rect.x = 480 + self.volume_plus_button_rect.x = 500 # 音量- self.volume_minus_button = tool.get_image_menu(tool.GFX[c.VOLUME_BUTTON], *frame_rect, c.BLACK) sign = font.render("-", True, c.YELLOWGREEN) @@ -196,7 +197,8 @@ class Menu(tool.State): sign_rect.y = -6 self.volume_minus_button.blit(sign, sign_rect) self.volume_minus_button_rect = self.volume_minus_button.get_rect() - self.volume_minus_button_rect.x = 300 + self.volume_minus_button_rect.x = 450 + # 音量+、-应当处于同一高度 self.volume_minus_button_rect.y = self.volume_plus_button_rect.y = 250 def setupSunflowerTrophy(self): @@ -210,21 +212,22 @@ class Menu(tool.State): self.sunflower_trophy_rect = self.sunflower_trophy.get_rect() self.sunflower_trophy_rect.x = 0 self.sunflower_trophy_rect.y = 280 - + def checkOptionButtonClick(self, mouse_pos): x, y = mouse_pos if self.inArea(self.option_button_rect, x, y): self.option_button_clicked = True # 播放点击音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "buttonclick.ogg")).play() + c.SOUND_BUTTON_CLICK.play() - # 在选项菜单打开时,检测是否点击到返回 - 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 showCurrentVolumeImage(self, surface): + # 由于音量可变,因此这一内容不能在一开始就结束加载,而应当不断刷新不断显示 + font = pg.font.Font(c.FONT_PATH, 30) + volume_tips = font.render(f"音量:{round(self.game_info[c.VOLUME]*100):3}%", True, c.LIGHTGRAY) + volume_tips_rect = volume_tips.get_rect() + volume_tips_rect.x = 275 + volume_tips_rect.y = 247 + surface.blit(volume_tips, volume_tips_rect) def update(self, surface, current_time, mouse_pos, mouse_click): self.current_time = self.game_info[c.CURRENT_TIME] = current_time @@ -253,9 +256,31 @@ class Menu(tool.State): surface.blit(self.return_button, self.return_button_rect) surface.blit(self.volume_plus_button, self.volume_plus_button_rect) surface.blit(self.volume_minus_button, self.volume_minus_button_rect) - if (mouse_pos and self.checkReturnClick(mouse_pos)): - self.option_button_clicked = False - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "buttonclick.ogg")).play() + self.showCurrentVolumeImage(surface) + if mouse_pos: + playButtonClickedSound = False + # 返回 + if self.inArea(self.return_button_rect, *mouse_pos): + self.option_button_clicked = False + playButtonClickedSound = True + # 音量+ + elif self.inArea(self.volume_plus_button_rect, *mouse_pos): + self.game_info[c.VOLUME] = min(self.game_info[c.VOLUME] + 0.1, 1) + # 一般不会有人想把音乐和音效分开设置,故pg.mixer.Sound.set_volume()和pg.mixer.music.set_volume()需要一起用 + pg.mixer.music.set_volume(self.game_info[c.VOLUME]) + for i in c.SOUNDS: + i.set_volume(self.game_info[c.VOLUME]) + playButtonClickedSound = True + # 音量- + elif self.inArea(self.volume_minus_button_rect, *mouse_pos): + self.game_info[c.VOLUME] = max(self.game_info[c.VOLUME] - 0.1, 0) + # 一般不会有人想把音乐和音效分开设置,故pg.mixer.Sound.set_volume()和pg.mixer.music.set_volume()需要一起用 + pg.mixer.music.set_volume(self.game_info[c.VOLUME]) + for i in c.SOUNDS: + i.set_volume(self.game_info[c.VOLUME]) + playButtonClickedSound = True + if playButtonClickedSound: + c.SOUND_BUTTON_CLICK.play() # 没有点到前两者时常规行检测所有按钮的点击和高亮 else: # 先检查选项高亮预览 diff --git a/source/state/screen.py b/source/state/screen.py index 6d83d35..4a80c78 100644 --- a/source/state/screen.py +++ b/source/state/screen.py @@ -51,7 +51,7 @@ class GameVictoryScreen(Screen): self.next = self.set_next_state() pg.display.set_caption("pypvz: 战斗胜利!") # 播放胜利音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "win.ogg")).play() + c.SOUND_WIN.play() class GameLoseScreen(Screen): def __init__(self): @@ -73,5 +73,5 @@ class GameLoseScreen(Screen): self.next = self.set_next_state() pg.display.set_caption("pypvz: 战斗失败!") # 播放失败音效 - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "lose.ogg")).play() - pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "scream.ogg")).play() + c.SOUND_LOSE.play() + c.SOUND_SCREAM.play() diff --git a/source/tool.py b/source/tool.py index 79fe5f1..6ad36f1 100755 --- a/source/tool.py +++ b/source/tool.py @@ -224,9 +224,9 @@ def loadPlantImageRect(): data = json.load(f) return data[c.PLANT_IMAGE_RECT] -pg.init() pg.display.set_caption(c.ORIGINAL_CAPTION) # 设置标题 SCREEN = pg.display.set_mode(c.SCREEN_SIZE) # 设置初始屏幕 +pg.mixer.set_num_channels(255) # 设置可以同时播放的音频数量,默认为8,经常不够用 try: # 设置窗口图标,仅对非Nuitka时生效,Nuitka不需要包括额外的图标文件,自动跳过这一过程即可 pg.display.set_icon(pg.image.load(os.path.join(os.path.dirname(os.path.dirname(__file__)), c.ORIGINAL_LOGO))) except: