import os import pygame as pg # 用户数据及日志存储路径 if os.name == 'nt': # Windows系统存储路径 USERDATA_PATH = os.path.expandvars( os.path.join('%APPDATA%', 'pypvz', 'userdata.json') ) USERLOG_PATH = os.path.expandvars( os.path.join('%APPDATA%', 'pypvz', 'run.log') ) else: # 非Windows系统存储路径 USERDATA_PATH = os.path.expanduser( os.path.join('~', '.config', 'pypvz', 'userdata.json') ) USERLOG_PATH = os.path.expanduser( os.path.join('~', '.config', 'pypvz', 'run.log') ) # 游戏图片资源路径 PATH_IMG_DIR = os.path.join( os.path.dirname(os.path.dirname(__file__)), 'resources', 'graphics' ) # 游戏音乐文件夹路径 PATH_MUSIC_DIR = os.path.join( os.path.dirname(os.path.dirname(__file__)), 'resources', 'music' ) # 窗口图标 ORIGINAL_LOGO = os.path.join( os.path.dirname(os.path.dirname(__file__)), 'pypvz-exec-logo.png' ) # 字体路径 FONT_PATH = os.path.join( os.path.dirname(os.path.dirname(__file__)), 'resources', 'DroidSansFallback.ttf', ) # 窗口标题 ORIGINAL_CAPTION = 'pypvz' # 游戏模式 GAME_MODE = 'mode' MODE_ADVENTURE = 'adventure' MODE_LITTLEGAME = 'littleGame' # 窗口大小 SCREEN_WIDTH = 800 SCREEN_HEIGHT = 600 SCREEN_SIZE = (SCREEN_WIDTH, SCREEN_HEIGHT) # 选卡数量 # 最大数量 CARD_MAX_NUM = 10 # 这里以后可以增加解锁功能,从最初的6格逐渐解锁到10格 # 最小数量 CARD_LIST_NUM = CARD_MAX_NUM # 方格数据 # 一般 GRID_X_LEN = 9 GRID_Y_LEN = 5 GRID_X_SIZE = 80 GRID_Y_SIZE = 100 # 带有泳池 GRID_POOL_X_LEN = GRID_X_LEN GRID_POOL_Y_LEN = 6 GRID_POOL_X_SIZE = GRID_X_SIZE GRID_POOL_Y_SIZE = 85 # 屋顶 GRID_ROOF_X_LEN = GRID_X_LEN GRID_ROOF_Y_LEN = GRID_Y_LEN GRID_ROOF_X_SIZE = GRID_X_SIZE GRID_ROOF_Y_SIZE = 85 # 颜色 WHITE = (255, 255, 255) NAVYBLUE = (60, 60, 100) SKY_BLUE = (39, 145, 251) BLACK = (0, 0, 0) LIGHTYELLOW = (234, 233, 171) RED = (255, 0, 0) PURPLE = (255, 0, 255) GOLD = (255, 215, 0) GREEN = (0, 255, 0) YELLOWGREEN = (55, 200, 0) LIGHTGRAY = (107, 108, 145) PARCHMENT_YELLOW = (207, 146, 83) # 退出游戏按钮 EXIT = 'exit' HELP = 'help' # 游戏界面可选的菜单 LITTLE_MENU = 'littleMenu' BIG_MENU = 'bigMenu' RESTART_BUTTON = 'restartButton' MAINMENU_BUTTON = 'mainMenuButton' LITTLEGAME_BUTTON = 'littleGameButton' OPTION_BUTTON = 'optionButton' SOUND_VOLUME_BUTTON = 'volumeButton' UNIVERSAL_BUTTON = 'universalButton' # 金银向日葵奖杯 TROPHY_SUNFLOWER = 'sunflowerTrophy' # 小铲子 SHOVEL = 'shovel' SHOVEL_BOX = 'shovelBox' # 一大波僵尸来袭图片 HUGE_WAVE_APPROCHING = 'Approching' # 关卡进程图片 LEVEL_PROGRESS_BAR = 'LevelProgressBar' LEVEL_PROGRESS_ZOMBIE_HEAD = 'LevelProgressZombieHead' LEVEL_PROGRESS_FLAG = 'LevelProgressFlag' # GAME INFO字典键值 CURRENT_TIME = 'current time' PASSED_ALL = 'passed all' # 已完成该模式下的所有游戏,应当显示向日葵奖杯获得界面 LEVEL_NUM = 'level num' LITTLEGAME_NUM = 'littleGame num' LEVEL_COMPLETIONS = 'level completions' LITTLEGAME_COMPLETIONS = 'littleGame completions' GAME_RATE = 'game rate' SOUND_VOLUME = 'volume' # 整个游戏的状态 MAIN_MENU = 'main menu' LOAD_SCREEN = 'load screen' GAME_LOSE = 'game lose' GAME_VICTORY = 'game victory' LEVEL = 'level' AWARD_SCREEN = 'award screen' HELP_SCREEN = 'help screen' # 界面图片文件名 MAIN_MENU_IMAGE = 'MainMenu' OPTION_ADVENTURE = 'Adventure' GAME_LOSE_IMAGE = 'GameLose' GAME_VICTORY_IMAGE = 'GameVictory' AWARD_SCREEN_IMAGE = 'AwardScreen' HELP_SCREEN_IMAGE = 'HelpScreen' # 地图相关内容 BACKGROUND_NAME = 'Background' BACKGROUND_TYPE = 'background_type' INIT_SUN_NAME = 'init_sun_value' ZOMBIE_LIST = 'zombie_list' GAME_TITLE = 'title' # 地图类型 BACKGROUND_DAY = 0 BACKGROUND_NIGHT = 1 BACKGROUND_POOL = 2 BACKGROUND_FOG = 3 BACKGROUND_ROOF = 4 BACKGROUND_ROOFNIGHT = 5 BACKGROUND_WALLNUTBOWLING = 6 BACKGROUND_SINGLE = 7 BACKGROUND_TRIPLE = 8 # 地图类型集合 # 白天场地(泛指蘑菇睡觉的场地) DAYTIME_BACKGROUNDS = { BACKGROUND_DAY, BACKGROUND_POOL, BACKGROUND_ROOF, BACKGROUND_WALLNUTBOWLING, BACKGROUND_SINGLE, BACKGROUND_TRIPLE, } # 带有泳池的场地 POOL_EQUIPPED_BACKGROUNDS = { BACKGROUND_POOL, BACKGROUND_FOG, } # 屋顶上的场地 ON_ROOF_BACKGROUNDS = { BACKGROUND_ROOF, BACKGROUND_ROOFNIGHT, } # BACKGROUND_DAY场地的变体 BACKGROUND_DAY_LIKE_BACKGROUNDS = { BACKGROUND_DAY, BACKGROUND_SINGLE, BACKGROUND_TRIPLE, } # 夜晚地图的墓碑数量等级 GRADE_GRAVES = 'grade_graves' # 不同墓碑等级对应的信息,列表位置对应的是墓碑等级 GRAVES_GRADE_INFO = (0, 4, 7, 11) # 僵尸生成方式 SPAWN_ZOMBIES = 'spawn_zombies' SPAWN_ZOMBIES_AUTO = 1 SPAWN_ZOMBIES_LIST = 0 INCLUDED_ZOMBIES = 'included_zombies' NUM_FLAGS = 'num_flags' INEVITABLE_ZOMBIE_DICT = 'inevitable_zombie_list' SURVIVAL_ROUNDS = 'survival_rounds' # 地图单元格属性 MAP_PLANT = 'plantnames' MAP_SLEEP = 'sleep' # 有没有休眠的蘑菇,作是否能种植咖啡豆的判断 MAP_PLOT_TYPE = 'plot_type' # 地图单元格区域类型 MAP_GRASS = 'grass' MAP_WATER = 'water' MAP_TILE = 'tile' # 指屋顶上的瓦片 MAP_UNAVAILABLE = 'unavailable' # 指完全不能种植物的地方,包括无草皮的荒地和坚果保龄球等红线右侧 # 地图相关像素数据 BACKGROUND_OFFSET_X = 220 MAP_OFFSET_X = 35 MAP_OFFSET_Y = 100 MAP_POOL_OFFSET_X = 42 MAP_POOL_OFFSET_Y = 115 MAP_ROOF_OFFSET_X = 35 # 暂时还不清楚数据 MAP_ROOF_OFFSET_Y = 105 # 暂时还不清楚数据 # 泳池前端陆地部分 MAP_POOL_FRONT_X = SCREEN_WIDTH - 15 # 植物选择菜单栏、传送带菜单栏等类型设定 CHOOSEBAR_TYPE = 'choosebar_type' CHOOSEBAR_STATIC = 0 CHOOSEBAR_MOVE = 1 CHOOSEBAR_BOWLING = 2 MENUBAR_BACKGROUND = 'ChooserBackground' MOVEBAR_BACKGROUND = 'MoveBackground' PANEL_BACKGROUND = 'PanelBackground' START_BUTTON = 'StartButton' CARD_POOL = 'card_pool' # 关于植物栏的像素设置 PANEL_Y_START = 87 PANEL_X_START = 22 PANEL_Y_INTERNAL = 69 PANEL_X_INTERNAL = 53 BAR_CARD_X_INTERNAL = 51 # 植物卡片信息索引 PLANT_NAME_INDEX = 0 CARD_INDEX = 1 SUN_INDEX = 2 FROZEN_TIME_INDEX = 3 # 传送带模式中的刷新间隔和移动速率 MOVEBAR_CARD_FRESH_TIME = 6000 CARD_MOVE_TIME = 60 # 其他显示物 CAR = 'car' SUN = 'Sun' # plant子类非植物对象(这里的是不包括阳光、子弹的拟植物对象) NON_PLANT_OBJECTS = { HOLE := 'Hole', ICEFROZENPLOT := 'IceFrozenPlot', GRAVE := 'Grave', } # 植物相关信息 PLANT_IMAGE_RECT = 'plant_image_rect' BOOM_IMAGE = 'Boom' # 植物卡片信息汇总(包括植物名称, 卡片名称, 阳光, 冷却时间) PLANT_CARD_INFO = ( # 元组 (植物名称, 卡片名称, 阳光, 冷却时间) ( PEASHOOTER := 'Peashooter', CARD_PEASHOOTER := 'card_peashooter', 100, 7500, ), (SUNFLOWER := 'SunFlower', CARD_SUNFLOWER := 'card_sunflower', 50, 7500), ( CHERRYBOMB := 'CherryBomb', CARD_CHERRYBOMB := 'card_cherrybomb', 150, 50000, ), (WALLNUT := 'WallNut', CARD_WALLNUT := 'card_wallnut', 50, 30000), ( POTATOMINE := 'PotatoMine', CARD_POTATOMINE := 'card_potatomine', 25, 30000, ), ( SNOWPEASHOOTER := 'SnowPea', CARD_SNOWPEASHOOTER := 'card_snowpea', 175, 7500, ), (CHOMPER := 'Chomper', CARD_CHOMPER := 'card_chomper', 150, 7500), ( REPEATERPEA := 'RepeaterPea', CARD_REPEATERPEA := 'card_repeaterpea', 200, 7500, ), ( PUFFSHROOM := 'PuffShroom', CARD_PUFFSHROOM := 'card_puffshroom', 0, 7500, ), (SUNSHROOM := 'SunShroom', CARD_SUNSHROOM := 'card_sunshroom', 25, 7500), ( FUMESHROOM := 'FumeShroom', CARD_FUMESHROOM := 'card_fumeshroom', 75, 7500, ), ( GRAVEBUSTER := 'GraveBuster', CARD_GRAVEBUSTER := 'card_gravebuster', 75, 7500, ), ( HYPNOSHROOM := 'HypnoShroom', CARD_HYPNOSHROOM := 'card_hypnoshroom', 75, 30000, ), ( SCAREDYSHROOM := 'ScaredyShroom', CARD_SCAREDYSHROOM := 'card_scaredyshroom', 25, 7500, ), (ICESHROOM := 'IceShroom', CARD_ICESHROOM := 'card_iceshroom', 75, 50000), ( DOOMSHROOM := 'DoomShroom', CARD_DOOMSHROOM := 'card_doomshroom', 125, 50000, ), (LILYPAD := 'LilyPad', CARD_LILYPAD := 'card_lilypad', 25, 7500), (SQUASH := 'Squash', CARD_SQUASH := 'card_squash', 50, 30000), ( TANGLEKLEP := 'TangleKlep', CARD_TANGLEKLEP := 'card_tangleklep', 25, 30000, ), ( THREEPEASHOOTER := 'Threepeater', CARD_THREEPEASHOOTER := 'card_threepeashooter', 325, 7500, ), (JALAPENO := 'Jalapeno', CARD_JALAPENO := 'card_jalapeno', 125, 50000), (SPIKEWEED := 'Spikeweed', CARD_SPIKEWEED := 'card_spikeweed', 100, 7500), (TORCHWOOD := 'TorchWood', CARD_TORCHWOOD := 'card_torchwood', 175, 7500), (TALLNUT := 'TallNut', CARD_TALLNUT := 'card_tallnut', 125, 30000), (SEASHROOM := 'SeaShroom', CARD_SEASHROOM := 'card_seashroom', 0, 30000), (STARFRUIT := 'StarFruit', CARD_STARFRUIT := 'card_starfruit', 125, 7500), ( PUMPKINHEAD := 'PumpkinHead', CARD_PUMPKINHEAD := 'card_pumpkinhead', 125, 30000, ), ( COFFEEBEAN := 'CoffeeBean', CARD_COFFEEBEAN := 'card_coffeebean', 75, 7500, ), (GARLIC := 'Garlic', CARD_GARLIC := 'card_garlic', 50, 7500), # 应当保证这3个在一般模式下不可选的特殊植物恒在最后 (WALLNUTBOWLING := 'WallNutBowling', CARD_WALLNUT := 'card_wallnut', 0, 0), ( REDWALLNUTBOWLING := 'RedWallNutBowling', CARD_REDWALLNUT := 'card_redwallnut', 0, 0, ), ( GIANTWALLNUT := 'GiantWallNut', CARD_GIANTWALLNUT := 'card_giantwallnut', 0, 0, ), ) # 卡片中的植物名称与索引序号的对应关系,指定名称以得到索引值 PLANT_CARD_INDEX = { item[PLANT_NAME_INDEX]: index for (index, item) in enumerate(PLANT_CARD_INFO) } # 指定了哪些卡可选(排除坚果保龄球特殊植物) CARDS_TO_CHOOSE = range(len(PLANT_CARD_INFO) - 3) # 植物集体属性集合 # 也许以后有必要的可以重新加入到对象的属性中 # 在生效时不用与僵尸进行碰撞检测的对象(即生效时不可发生被僵尸啃食的事件) SKIP_ZOMBIE_COLLISION_CHECK_WHEN_WORKING = { # 注意爆炸坚果的触发也是啃食类碰撞,因此只能算作爆炸后不检测 SQUASH, ICESHROOM, REDWALLNUTBOWLING, CHERRYBOMB, JALAPENO, DOOMSHROOM, POTATOMINE, } # 所有可能不用与僵尸进行碰撞检测的对象 CAN_SKIP_ZOMBIE_COLLISION_CHECK = ( # 这里运用了集合运算 # 注意这个外围的小括号是用来换行的 # 各个部分末!尾!千!万!不!能!加!逗!号!!! # 生效时不检测的植物 SKIP_ZOMBIE_COLLISION_CHECK_WHEN_WORKING | # 非植物对象 NON_PLANT_OBJECTS | # 地刺类 { SPIKEWEED, } ) # 死亡时不触发音效的对象 PLANT_DIE_SOUND_EXCEPTIONS = { WALLNUTBOWLING, TANGLEKLEP, ICEFROZENPLOT, HOLE, GRAVE, JALAPENO, REDWALLNUTBOWLING, CHERRYBOMB, GIANTWALLNUT, } # 直接水生植物 WATER_PLANTS = { LILYPAD, SEASHROOM, TANGLEKLEP, } # 攻击状态检查类型 CHECK_ATTACK_NEVER = 0 CHECK_ATTACK_ALWAYS = 1 # 范围爆炸植物,即灰烬植物与寒冰菇 ASH_PLANTS_AND_ICESHROOM = { REDWALLNUTBOWLING, CHERRYBOMB, JALAPENO, DOOMSHROOM, ICESHROOM, } # 白天要睡觉的植物 CAN_SLEEP_PLANTS = { PUFFSHROOM, SUNSHROOM, FUMESHROOM, HYPNOSHROOM, SCAREDYSHROOM, ICESHROOM, DOOMSHROOM, SEASHROOM, } # 选卡不推荐选择理由 REASON_WILL_SLEEP = 1 REASON_SLEEP_BUT_COFFEE_BEAN = 2 REASON_OTHER = 3 # 植物生命值 PLANT_HEALTH = 300 WALLNUT_HEALTH = 4000 WALLNUT_CRACKED1_HEALTH = WALLNUT_HEALTH // 3 * 2 WALLNUT_CRACKED2_HEALTH = WALLNUT_HEALTH // 3 TALLNUT_HEALTH = 8000 TALLNUT_CRACKED1_HEALTH = TALLNUT_HEALTH // 3 * 2 TALLNUT_CRACKED2_HEALTH = TALLNUT_HEALTH // 3 GARLIC_HEALTH = 450 GARLIC_CRACKED1_HEALTH = GARLIC_HEALTH // 3 * 2 GARLIC_CRACKED2_HEALTH = GARLIC_HEALTH // 3 # 坚果保龄球攻击伤害 WALLNUT_BOWLING_DAMAGE = 550 # 阳光生成属性 PRODUCE_SUN_INTERVAL = 4250 # 基准 FLOWER_SUN_INTERVAL = 24000 SUN_LIVE_TIME = 10000 SUN_VALUE = 25 # 僵尸冷冻 ICE_SLOW_TIME = 10000 MIN_FREEZE_TIME = 4000 ICETRAP = 'IceTrap' # 子弹信息 # 子弹类型 BULLET_PEA = 'PeaNormal' BULLET_PEA_ICE = 'PeaIce' BULLET_FIREBALL = 'Fireball' BULLET_MUSHROOM = 'BulletMushRoom' BULLET_SEASHROOM = 'BulletSeaShroom' FUME = 'Fume' # 子弹伤害 BULLET_DAMAGE_NORMAL = 20 BULLET_DAMAGE_FIREBALL_BODY = 27 # 这是火球本体的伤害,注意不是40,本体(27) + 溅射(13)才是40 BULLET_DAMAGE_FIREBALL_RANGE = 13 # 原版溅射伤害会随着僵尸数量增多而减少,这里相当于做了一个增强 # 子弹效果 BULLET_EFFECT_ICE = 'ice' BULLET_EFFECT_UNICE = 'unice' # 特殊子弹 # 杨桃子弹 # 子弹名称 BULLET_STAR = 'StarBullet' # 子弹方向 STAR_FORWARD_UP = 'forwardUp' # 向前上方 STAR_FORWARD_DOWN = 'forwardDown' # 向前下方 STAR_BACKWARD = 'backward' # 向后 STAR_UPWARD = 'upward' # 向上 STAR_DOWNWARD = 'downward' # 向下 # 有爆炸图片的子弹 BULLET_INDEPENDENT_BOOM_IMG = { BULLET_PEA, BULLET_PEA_ICE, BULLET_MUSHROOM, BULLET_SEASHROOM, BULLET_STAR, } # 僵尸信息 ZOMBIE_IMAGE_RECT = 'zombie_image_rect' ZOMBIE_HEAD = 'ZombieHead' NORMAL_ZOMBIE = 'Zombie' CONEHEAD_ZOMBIE = 'ConeheadZombie' BUCKETHEAD_ZOMBIE = 'BucketheadZombie' FLAG_ZOMBIE = 'FlagZombie' NEWSPAPER_ZOMBIE = 'NewspaperZombie' FOOTBALL_ZOMBIE = 'FootballZombie' DUCKY_TUBE_ZOMBIE = 'DuckyTubeZombie' CONEHEAD_DUCKY_TUBE_ZOMBIE = 'ConeheadDuckyTubeZombie' BUCKETHEAD_DUCKY_TUBE_ZOMBIE = 'BucketheadDuckyTubeZombie' SCREEN_DOOR_ZOMBIE = 'ScreenDoorZombie' POLE_VAULTING_ZOMBIE = 'PoleVaultingZombie' ZOMBONI = 'Zomboni' SNORKELZOMBIE = 'SnorkelZombie' BOOMDIE = 'BoomDie' # 对僵尸的攻击类型设置 ZOMBIE_DEAFULT_DAMAGE = ZOMBIE_HELMET_2_FIRST = 'helmet2First' # 优先攻击二类防具 ZOMBIE_COMMON_DAMAGE = 'commonDamage' # 优先攻击僵尸与一类防具的整体 ZOMBIE_RANGE_DAMAGE = 'rangeDamage' # 范围攻击,同时伤害二类防具与(僵尸与一类防具的整体) ZOMBIE_ASH_DAMAGE = 'ashDamage' # 灰烬植物攻击,直接伤害本体 ZOMBIE_WALLNUT_BOWLING_DANMAGE = 'wallnutBowlingDamage' # 坚果保龄球冲撞伤害 # 僵尸生命值设置 # 有关本体 NORMAL_HEALTH = 200 # 普通僵尸生命值 POLE_VAULTING_HEALTH = 333 ZOMBONI_HEALTH = 1280 # 冰车损坏点 ZOMBONI_DAMAGED1_HEALTH = 2 * ZOMBONI_HEALTH // 3 + 70 ZOMBONI_DAMAGED2_HEALTH = ZOMBONI_HEALTH // 3 + 70 # 掉头后僵尸的生命值 LOSTHEAD_HEALTH = 70 POLE_VAULTING_LOSTHEAD_HEALTH = 167 # 有关一类防具 CONEHEAD_HEALTH = 370 BUCKETHEAD_HEALTH = 1100 FOOTBALL_HELMET_HEALTH = 1400 # 有关二类防具 NEWSPAPER_HEALTH = 150 SCREEN_DOOR_HEALTH = 1100 # 僵尸行动信息 ATTACK_INTERVAL = 500 ZOMBIE_ATTACK_DAMAGE = 50 ZOMBIE_WALK_INTERVAL = 60 # 僵尸步行间隔 # 僵尸生成位置 ZOMBIE_START_X = SCREEN_WIDTH + 30 # 场宽度不一样,用于拟合 # 僵尸集体属性集合 # 僵尸生成信息字典:包含生成僵尸名称、僵尸级别、生成权重 CREATE_ZOMBIE_DICT = { # 生成僵尸:(级别, 权重) NORMAL_ZOMBIE: (1, 4000), FLAG_ZOMBIE: (1, 0), CONEHEAD_ZOMBIE: (2, 4000), BUCKETHEAD_ZOMBIE: (4, 3000), NEWSPAPER_ZOMBIE: (2, 1000), FOOTBALL_ZOMBIE: (7, 2000), DUCKY_TUBE_ZOMBIE: (1, 0), # 作为变种,不主动生成 CONEHEAD_DUCKY_TUBE_ZOMBIE: (2, 0), # 作为变种,不主动生成 BUCKETHEAD_DUCKY_TUBE_ZOMBIE: (4, 0), # 作为变种,不主动生成 SCREEN_DOOR_ZOMBIE: (4, 3500), POLE_VAULTING_ZOMBIE: (2, 2000), ZOMBONI: (7, 2000), SNORKELZOMBIE: (3, 2000), } # 记录陆生僵尸的水生变种 CONVERT_ZOMBIE_IN_POOL = { NORMAL_ZOMBIE: DUCKY_TUBE_ZOMBIE, CONEHEAD_ZOMBIE: CONEHEAD_DUCKY_TUBE_ZOMBIE, BUCKETHEAD_ZOMBIE: BUCKETHEAD_DUCKY_TUBE_ZOMBIE, } # 水上僵尸集合 WATER_ZOMBIE = { DUCKY_TUBE_ZOMBIE, CONEHEAD_DUCKY_TUBE_ZOMBIE, BUCKETHEAD_DUCKY_TUBE_ZOMBIE, SNORKELZOMBIE, } # 状态类型 IDLE = 'idle' FLY = 'fly' EXPLODE = 'explode' ATTACK = 'attack' ATTACKED = 'attacked' DIGEST = 'digest' WALK = 'walk' DIE = 'die' CRY = 'cry' FREEZE = 'freeze' SLEEP = 'sleep' # 关卡状态 CHOOSE = 'choose' PLAY = 'play' # 加载矩形碰撞范围 用于消除文件边框影响 # 植物 PLANT_RECT = { BULLET_PEA: {'x': 28, 'y': 0, 'width': 28, 'height': 34}, BULLET_PEA_ICE: {'x': 26, 'y': 0, 'width': 30, 'height': 34}, CHOMPER: {'x': 0, 'y': 0, 'width': 100, 'height': 114}, PUFFSHROOM: {'x': 0, 'y': 28, 'width': 35, 'height': 38}, f'{PUFFSHROOM}Sleep': {'x': 1, 'y': 0, 'width': 39, 'height': 65}, BULLET_MUSHROOM: {'x': 0, 'y': 1, 'width': 55, 'height': 21}, BULLET_SEASHROOM: {'x': 0, 'y': 1, 'width': 55, 'height': 21}, POTATOMINE: {'x': 0, 'y': 0, 'width': 75, 'height': 55}, SQUASH: {'x': 10, 'y': 140, 'width': 80, 'height': 86}, f'{SQUASH}Aim': {'x': 10, 'y': 140, 'width': 80, 'height': 86}, SPIKEWEED: {'x': 3, 'y': 0, 'width': 80, 'height': 35}, } # 僵尸 ZOMBIE_RECT = { NORMAL_ZOMBIE: {'x': 62, 'width': 90}, f'{NORMAL_ZOMBIE}Attack': {'x': 62, 'width': 90}, f'{NORMAL_ZOMBIE}LostHead': {'x': 62, 'width': 90}, f'{NORMAL_ZOMBIE}LostHeadAttack': {'x': 62, 'width': 90}, f'{NORMAL_ZOMBIE}Die': {'x': 0, 'width': 164}, BOOMDIE: {'x': 68, 'width': 80}, CONEHEAD_ZOMBIE: {'x': 80, 'width': 80}, f'{CONEHEAD_ZOMBIE}Attack': {'x': 79, 'width': 87}, BUCKETHEAD_ZOMBIE: {'x': 54, 'width': 90}, f'{BUCKETHEAD_ZOMBIE}Attack': {'x': 46, 'width': 90}, FLAG_ZOMBIE: {'x': 56, 'width': 110}, f'{FLAG_ZOMBIE}Attack': {'x': 60, 'width': 100}, f'{FLAG_ZOMBIE}LostHead': {'x': 55, 'width': 110}, f'{FLAG_ZOMBIE}LostHeadAttack': {'x': 55, 'width': 110}, NEWSPAPER_ZOMBIE: {'x': 48, 'width': 92}, f'{NEWSPAPER_ZOMBIE}Attack': {'x': 48, 'width': 92}, f'{NEWSPAPER_ZOMBIE}NoPaper': {'x': 40, 'width': 98}, f'{NEWSPAPER_ZOMBIE}NoPaperAttack': {'x': 48, 'width': 92}, f'{NEWSPAPER_ZOMBIE}LostHead': {'x': 44, 'width': 96}, f'{NEWSPAPER_ZOMBIE}LostHeadAttack': {'x': 48, 'width': 92}, f'{NEWSPAPER_ZOMBIE}Die': {'x': 0, 'width': 100}, f'{DUCKY_TUBE_ZOMBIE}Die': {'x': 55, 'width': 105}, f'{DUCKY_TUBE_ZOMBIE}LostHead': {'x': 55, 'width': 105}, SCREEN_DOOR_ZOMBIE: {'x': 41, 'width': 100}, f'{SCREEN_DOOR_ZOMBIE}Attack': {'x': 41, 'width': 100}, } # 这里还有懒得写代码的补加,用循环实现 for _part1 in ( DUCKY_TUBE_ZOMBIE, CONEHEAD_DUCKY_TUBE_ZOMBIE, BUCKETHEAD_DUCKY_TUBE_ZOMBIE, ): for _part2 in ('', 'Attack', 'Swim'): ZOMBIE_RECT[f'{_part1}{_part2}'] = {'x': 55, 'width': 105} # 音效 def _getSound(filename): return pg.mixer.Sound( os.path.join( os.path.dirname(os.path.dirname(__file__)), 'resources', 'sound', filename, ) ) # 所有音效的元组,用一波海象算子表达,免得要维护两个 SOUNDS = ( # 程序交互等 SOUND_TAPPING_CARD := _getSound('tap.ogg'), SOUND_HELP_SCREEN := _getSound('helpScreen.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'), SOUND_CANNOT_CHOOSE_WARNING := _getSound('cannotChooseWarning.ogg'), SOUND_FINAL_FANFARE := _getSound('finalfanfare.ogg'), ) # 记录本地存储文件初始值 INIT_USERDATA = { LEVEL_NUM: 1, LITTLEGAME_NUM: 1, LEVEL_COMPLETIONS: 0, LITTLEGAME_COMPLETIONS: 0, GAME_RATE: 1, SOUND_VOLUME: 1, } # 无穷大常量 INF = float('inf') # python传递字符串性能较低,故在这里对inf声明一次,以后仅需调用即可,虽然真正的用处是可以自动补全(