加入坚果保龄球2

This commit is contained in:
星外之神 2022-08-02 12:52:46 +08:00
parent 72cad4a3fd
commit 9fa8fbb847
7 changed files with 145 additions and 93 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -344,9 +344,7 @@ LEVEL_MAP_DATA = (
# 玩玩小游戏地图 # 玩玩小游戏地图
LITTLE_GAME_MAP_DATA = ( LITTLE_GAME_MAP_DATA = (
# 第0关 测试 目前空缺 # 第0关 测试
{},
# 第1关 坚果保龄球
{ {
c.BACKGROUND_TYPE: 6, c.BACKGROUND_TYPE: 6,
c.CHOOSEBAR_TYPE: c.CHOOSEBAR_BOWLING, c.CHOOSEBAR_TYPE: c.CHOOSEBAR_BOWLING,
@ -354,8 +352,21 @@ LITTLE_GAME_MAP_DATA = (
c.SPAWN_ZOMBIES:c.SPAWN_ZOMBIES_AUTO, c.SPAWN_ZOMBIES:c.SPAWN_ZOMBIES_AUTO,
c.INCLUDED_ZOMBIES: ( c.NORMAL_ZOMBIE, c.CONEHEAD_ZOMBIE, c.INCLUDED_ZOMBIES: ( c.NORMAL_ZOMBIE, c.CONEHEAD_ZOMBIE,
c.POLE_VAULTING_ZOMBIE, c.BUCKETHEAD_ZOMBIE, c.POLE_VAULTING_ZOMBIE, c.BUCKETHEAD_ZOMBIE,
c.NEWSPAPER_ZOMBIE), c.NEWSPAPER_ZOMBIE, c.SCREEN_DOOR_ZOMBIE),
c.NUM_FLAGS:3, c.NUM_FLAGS:3,
c.CARD_POOL: { c.WALLNUTBOWLING: 0,
c.REDWALLNUTBOWLING: 0,
c.GIANTWALLNUT:100,}
},
# 第1关 坚果保龄球
{
c.BACKGROUND_TYPE: 6,
c.CHOOSEBAR_TYPE: c.CHOOSEBAR_BOWLING,
c.SHOVEL: 0,
c.SPAWN_ZOMBIES:c.SPAWN_ZOMBIES_AUTO,
c.INCLUDED_ZOMBIES: ( c.NORMAL_ZOMBIE, c.CONEHEAD_ZOMBIE,
c.POLE_VAULTING_ZOMBIE, c.BUCKETHEAD_ZOMBIE,),
c.NUM_FLAGS:2,
c.CARD_POOL: { c.WALLNUTBOWLING: 300, c.CARD_POOL: { c.WALLNUTBOWLING: 300,
c.REDWALLNUTBOWLING: 100,} c.REDWALLNUTBOWLING: 100,}
}, },
@ -414,6 +425,20 @@ LITTLE_GAME_MAP_DATA = (
c.JALAPENO: 50, c.JALAPENO: 50,
c.THREEPEASHOOTER: 300,} c.THREEPEASHOOTER: 300,}
}, },
# 第6关 坚果保龄球2
{
c.BACKGROUND_TYPE: 6,
c.CHOOSEBAR_TYPE: c.CHOOSEBAR_BOWLING,
c.SHOVEL: 0,
c.SPAWN_ZOMBIES:c.SPAWN_ZOMBIES_AUTO,
c.INCLUDED_ZOMBIES: ( c.NORMAL_ZOMBIE, c.CONEHEAD_ZOMBIE,
c.POLE_VAULTING_ZOMBIE, c.BUCKETHEAD_ZOMBIE,
c.NEWSPAPER_ZOMBIE, c.SCREEN_DOOR_ZOMBIE),
c.NUM_FLAGS:3,
c.CARD_POOL: { c.WALLNUTBOWLING: 300,
c.REDWALLNUTBOWLING: 100,
c.GIANTWALLNUT:100,}
},
) )
# 总关卡数 # 总关卡数

View File

@ -23,7 +23,7 @@ class Car(pg.sprite.Sprite):
self.current_time = game_info[c.CURRENT_TIME] self.current_time = game_info[c.CURRENT_TIME]
if self.state == c.WALK: if self.state == c.WALK:
self.rect.x += 6 self.rect.x += 6
if self.rect.x > c.SCREEN_WIDTH + 60: if self.rect.x > c.SCREEN_WIDTH + 25:
self.dead = True self.dead = True
def setWalk(self): def setWalk(self):
@ -39,7 +39,7 @@ class Car(pg.sprite.Sprite):
class Bullet(pg.sprite.Sprite): class Bullet(pg.sprite.Sprite):
def __init__( self, x, start_y, dest_y, name, damage, def __init__( self, x, start_y, dest_y, name, damage,
effect=None, passed_torchwood_x=None, effect=None, passed_torchwood_x=None,
damageType=c.ZOMBIE_DEAFULT_DAMAGE): damage_type=c.ZOMBIE_DEAFULT_DAMAGE):
pg.sprite.Sprite.__init__(self) pg.sprite.Sprite.__init__(self)
self.name = name self.name = name
@ -56,7 +56,7 @@ class Bullet(pg.sprite.Sprite):
self.y_vel = 15 if (dest_y > start_y) else -15 self.y_vel = 15 if (dest_y > start_y) else -15
self.x_vel = 10 self.x_vel = 10
self.damage = damage self.damage = damage
self.damageType = damageType self.damage_type = damage_type
self.effect = effect self.effect = effect
self.state = c.FLY self.state = c.FLY
self.current_time = 0 self.current_time = 0
@ -82,14 +82,8 @@ class Bullet(pg.sprite.Sprite):
self.explode_frames = [] self.explode_frames = []
fly_name = self.name fly_name = self.name
if self.name == c.BULLET_MUSHROOM: if self.name in c.BULLET_INDEPENDENT_BOOM_IMG:
explode_name = "BulletMushRoomExplode" explode_name = f"{self.name}Explode"
elif self.name == c.BULLET_PEA_ICE:
explode_name = "PeaIceExplode"
elif self.name == c.BULLET_SEASHROOM:
explode_name = "BulletSeaShroomExplode"
elif self.name == c.BULLET_STAR:
explode_name = "StarBulletExplode"
else: else:
explode_name = "PeaNormalExplode" explode_name = "PeaNormalExplode"
@ -184,8 +178,8 @@ class Fume(pg.sprite.Sprite):
# 杨桃的子弹 # 杨桃的子弹
class StarBullet(Bullet): class StarBullet(Bullet):
def __init__(self, x, start_y, damage, direction, level, damageType = c.ZOMBIE_DEAFULT_DAMAGE): # direction指星星飞行方向 def __init__(self, x, start_y, damage, direction, level, damage_type = c.ZOMBIE_DEAFULT_DAMAGE): # direction指星星飞行方向
Bullet.__init__(self, x, start_y, start_y, c.BULLET_STAR, damage, damageType = damageType) Bullet.__init__(self, x, start_y, start_y, c.BULLET_STAR, damage, damage_type = damage_type)
self.level = level self.level = level
self.map_y = self.level.map.getMapIndex(self.rect.x, self.rect.centery)[1] self.map_y = self.level.map.getMapIndex(self.rect.x, self.rect.centery)[1]
@ -819,7 +813,7 @@ class Squash(Plant):
if (self.frame_index + 1) == self.frame_num: if (self.frame_index + 1) == self.frame_num:
for zombie in self.zombie_group: for zombie in self.zombie_group:
if self.canAttack(zombie): if self.canAttack(zombie):
zombie.setDamage(1800, damageType=c.ZOMBIE_RANGE_DAMAGE) zombie.setDamage(1800, damage_type=c.ZOMBIE_RANGE_DAMAGE)
self.health = 0 # 避免僵尸在原位啃食 self.health = 0 # 避免僵尸在原位啃食
self.map_plant_set.remove(c.SQUASH) self.map_plant_set.remove(c.SQUASH)
self.kill() self.kill()
@ -878,7 +872,7 @@ class Spikeweed(Plant):
zombie.health = zombie.losthead_health zombie.health = zombie.losthead_health
killSelf = True killSelf = True
else: else:
zombie.setDamage(20, damageType=c.ZOMBIE_COMMON_DAMAGE) zombie.setDamage(20, damage_type=c.ZOMBIE_COMMON_DAMAGE)
if killSelf: if killSelf:
self.health = 0 self.health = 0
# 播放攻击音效,同子弹打击 # 播放攻击音效,同子弹打击
@ -1161,7 +1155,7 @@ class WallNutBowling(Plant):
self.handleMapYPosition() self.handleMapYPosition()
if self.shouldChangeDirection(): if self.shouldChangeDirection():
self.changeDirection(-1) self.changeDirection(-1)
if self.init_rect.x > c.SCREEN_WIDTH + 60: if self.init_rect.x > c.SCREEN_WIDTH + 25:
self.health = 0 self.health = 0
self.move_timer += self.move_interval self.move_timer += self.move_interval
@ -1203,12 +1197,6 @@ class WallNutBowling(Plant):
self.disable_hit_y = map_y self.disable_hit_y = map_y
def animation(self): def animation(self):
if (self.current_time - self.animate_timer) > self.animate_interval:
self.frame_index += 1
if self.frame_index >= self.frame_num:
self.frame_index = 0
self.animate_timer = self.current_time
image = self.frames[self.frame_index] image = self.frames[self.frame_index]
self.image = pg.transform.rotate(image, self.rotate_degree) self.image = pg.transform.rotate(image, self.rotate_degree)
self.mask = pg.mask.from_surface(self.image) self.mask = pg.mask.from_surface(self.image)
@ -1249,7 +1237,7 @@ class RedWallNutBowling(Plant):
elif (self.current_time - self.move_timer) >= self.move_interval: elif (self.current_time - self.move_timer) >= self.move_interval:
self.rotate_degree = (self.rotate_degree - 30) % 360 self.rotate_degree = (self.rotate_degree - 30) % 360
self.init_rect.x += self.vel_x self.init_rect.x += self.vel_x
if self.init_rect.x > c.SCREEN_WIDTH + 60: if self.init_rect.x > c.SCREEN_WIDTH + 25:
self.health = 0 self.health = 0
self.move_timer += self.move_interval self.move_timer += self.move_interval
@ -1336,7 +1324,7 @@ class StarFruit(Plant):
self.shoot_timer = self.current_time - 700 self.shoot_timer = self.current_time - 700
elif (self.current_time - self.shoot_timer) >= 1400: elif (self.current_time - self.shoot_timer) >= 1400:
# 向后打的杨桃子弹无视铁门与报纸防具 # 向后打的杨桃子弹无视铁门与报纸防具
self.bullet_group.add(StarBullet(self.rect.left - 10, self.rect.y + 15, c.BULLET_DAMAGE_NORMAL, c.STAR_BACKWARD, self.level, damageType = c.ZOMBIE_COMMON_DAMAGE)) self.bullet_group.add(StarBullet(self.rect.left - 10, self.rect.y + 15, c.BULLET_DAMAGE_NORMAL, c.STAR_BACKWARD, self.level, damage_type = c.ZOMBIE_COMMON_DAMAGE))
# 其他方向的杨桃子弹伤害效果与豌豆等同 # 其他方向的杨桃子弹伤害效果与豌豆等同
self.bullet_group.add(StarBullet(self.rect.centerx - 20, self.rect.bottom - self.rect.h - 15, c.BULLET_DAMAGE_NORMAL, c.STAR_UPWARD, self.level)) self.bullet_group.add(StarBullet(self.rect.centerx - 20, self.rect.bottom - self.rect.h - 15, c.BULLET_DAMAGE_NORMAL, c.STAR_UPWARD, self.level))
self.bullet_group.add(StarBullet(self.rect.centerx - 20, self.rect.bottom - 5, c.BULLET_DAMAGE_NORMAL, c.STAR_DOWNWARD, self.level)) self.bullet_group.add(StarBullet(self.rect.centerx - 20, self.rect.bottom - 5, c.BULLET_DAMAGE_NORMAL, c.STAR_DOWNWARD, self.level))
@ -1740,7 +1728,7 @@ class FumeShroom(Plant):
# 烟雾只是个动画,实际伤害由本身完成 # 烟雾只是个动画,实际伤害由本身完成
for target_zombie in self.zombie_group: for target_zombie in self.zombie_group:
if self.canAttack(target_zombie): if self.canAttack(target_zombie):
target_zombie.setDamage(c.BULLET_DAMAGE_NORMAL, damageType=c.ZOMBIE_RANGE_DAMAGE) target_zombie.setDamage(c.BULLET_DAMAGE_NORMAL, damage_type=c.ZOMBIE_RANGE_DAMAGE)
self.shoot_timer = self.current_time self.shoot_timer = self.current_time
self.show_attack_frames = True self.show_attack_frames = True
# 播放发射音效 # 播放发射音效
@ -1829,3 +1817,30 @@ class PumpkinHead(Plant):
elif not self.cracked2 and self.health <= c.WALLNUT_CRACKED2_HEALTH: elif not self.cracked2 and self.health <= c.WALLNUT_CRACKED2_HEALTH:
self.changeFrames(self.cracked2_frames) self.changeFrames(self.cracked2_frames)
self.cracked2 = True self.cracked2 = True
class GiantWallNut(Plant):
def __init__(self, x, y):
Plant.__init__(self, x, y, c.GIANTWALLNUT, 1, None)
self.init_rect = self.rect.copy()
self.rotate_degree = 0
self.animate_interval = 200
self.move_timer = 0
self.move_interval = 70
self.vel_x = random.randint(12, 15)
def idling(self):
if self.move_timer == 0:
self.move_timer = self.current_time
elif (self.current_time - self.move_timer) >= self.move_interval:
self.rotate_degree = (self.rotate_degree - 30) % 360
self.init_rect.x += self.vel_x
if self.init_rect.x > c.SCREEN_WIDTH:
self.health = 0
self.move_timer += self.move_interval
def animation(self):
image = self.frames[self.frame_index]
self.image = pg.transform.rotate(image, self.rotate_degree)
self.mask = pg.mask.from_surface(self.image)
# must keep the center postion of image when rotate
self.rect = self.image.get_rect(center=self.init_rect.center)

View File

@ -346,23 +346,23 @@ class Zombie(pg.sprite.Sprite):
if (self.current_time - self.ice_slow_timer) > c.ICE_SLOW_TIME: if (self.current_time - self.ice_slow_timer) > c.ICE_SLOW_TIME:
self.ice_slow_ratio = 1 self.ice_slow_ratio = 1
def setDamage(self, damage, effect=None, damageType=c.ZOMBIE_COMMON_DAMAGE): def setDamage(self, damage, effect=None, damage_type=c.ZOMBIE_COMMON_DAMAGE):
# 冰冻减速效果 # 冰冻减速效果
if effect == c.BULLET_EFFECT_ICE: if effect == c.BULLET_EFFECT_ICE:
if damageType == c.ZOMBIE_DEAFULT_DAMAGE: # 寒冰射手不能穿透二类防具进行减速 if damage_type == c.ZOMBIE_DEAFULT_DAMAGE: # 寒冰射手不能穿透二类防具进行减速
if not self.helmet_type2: if not self.helmet_type2:
self.setIceSlow() self.setIceSlow()
else: else:
self.setIceSlow() self.setIceSlow()
# 解冻 # 解冻
elif effect == c.BULLET_EFFECT_UNICE: elif effect == c.BULLET_EFFECT_UNICE:
if damageType == c.ZOMBIE_DEAFULT_DAMAGE: # 寒冰射手不能穿透二类防具进行减速 if damage_type == c.ZOMBIE_DEAFULT_DAMAGE: # 寒冰射手不能穿透二类防具进行减速
if not self.helmet_type2: if not self.helmet_type2:
self.ice_slow_ratio = 1 self.ice_slow_ratio = 1
else: else:
self.ice_slow_ratio = 1 self.ice_slow_ratio = 1
if damageType == c.ZOMBIE_DEAFULT_DAMAGE: # 不穿透二类防具的攻击 if damage_type == c.ZOMBIE_DEAFULT_DAMAGE: # 不穿透二类防具的攻击
# 从第二类防具开始逐级传递 # 从第二类防具开始逐级传递
if self.helmet_type2: if self.helmet_type2:
self.helmet_type2_health -= damage self.helmet_type2_health -= damage
@ -383,7 +383,7 @@ class Zombie(pg.sprite.Sprite):
self.helmet_health = 0 # 注意合并后清零 self.helmet_health = 0 # 注意合并后清零
else: # 没有防具 else: # 没有防具
self.health -= damage self.health -= damage
elif damageType == c.ZOMBIE_COMMON_DAMAGE: # 无视二类防具,将攻击一类防具与本体视为整体的攻击 elif damage_type == c.ZOMBIE_COMMON_DAMAGE: # 无视二类防具,将攻击一类防具与本体视为整体的攻击
if self.helmet: # 存在一类防具 if self.helmet: # 存在一类防具
self.helmet_health -= damage self.helmet_health -= damage
if self.helmet_health <= 0: if self.helmet_health <= 0:
@ -391,7 +391,7 @@ class Zombie(pg.sprite.Sprite):
self.helmet_health = 0 # 注意合并后清零 self.helmet_health = 0 # 注意合并后清零
else: # 没有一类防具 else: # 没有一类防具
self.health -= damage self.health -= damage
elif damageType == c.ZOMBIE_RANGE_DAMAGE: elif damage_type == c.ZOMBIE_RANGE_DAMAGE:
# 从第二类防具开始逐级传递 # 从第二类防具开始逐级传递
if self.helmet_type2: if self.helmet_type2:
self.helmet_type2_health -= damage self.helmet_type2_health -= damage
@ -422,9 +422,9 @@ class Zombie(pg.sprite.Sprite):
self.helmet_health = 0 # 注意合并后清零 self.helmet_health = 0 # 注意合并后清零
else: # 没有防具 else: # 没有防具
self.health -= damage self.health -= damage
elif damageType == c.ZOMBIE_ASH_DAMAGE: elif damage_type == c.ZOMBIE_ASH_DAMAGE:
self.health -= damage # 无视任何防具 self.health -= damage # 无视任何防具
elif damageType == c.ZOMBIE_WALLNUT_BOWLING_DANMAGE: elif damage_type == c.ZOMBIE_WALLNUT_BOWLING_DANMAGE:
# 逻辑:对防具的多余伤害不传递 # 逻辑:对防具的多余伤害不传递
if self.helmet_type2: if self.helmet_type2:
# 对二类防具伤害较一般情况低拟合铁门需要砸3次的设定 # 对二类防具伤害较一般情况低拟合铁门需要砸3次的设定
@ -435,7 +435,7 @@ class Zombie(pg.sprite.Sprite):
self.health -= damage self.health -= damage
else: else:
print("警告:植物攻击类型错误,现在默认进行类豌豆射手型攻击") print("警告:植物攻击类型错误,现在默认进行类豌豆射手型攻击")
self.setDamage(damage, effect=effect, damageType=c.ZOMBIE_DEAFULT_DAMAGE) self.setDamage(damage, effect=effect, damage_type=c.ZOMBIE_DEAFULT_DAMAGE)
# 记录攻击时间 # 记录攻击时间
self.hit_timer = self.current_time self.hit_timer = self.current_time

View File

@ -359,7 +359,7 @@ PLANT_CARD_INFO = (# 元组 (植物名称, 卡片名称, 阳光, 冷却时间)
CARD_GARLIC := "card_garlic", CARD_GARLIC := "card_garlic",
50, 50,
7500), 7500),
# 应当保证这个在一般模式下不可选的特殊植物恒在最后 # 应当保证这3个在一般模式下不可选的特殊植物恒在最后
(WALLNUTBOWLING := "WallNutBowling", (WALLNUTBOWLING := "WallNutBowling",
CARD_WALLNUT := "card_wallnut", CARD_WALLNUT := "card_wallnut",
0, 0,
@ -368,6 +368,10 @@ PLANT_CARD_INFO = (# 元组 (植物名称, 卡片名称, 阳光, 冷却时间)
CARD_REDWALLNUT := "card_redwallnut", CARD_REDWALLNUT := "card_redwallnut",
0, 0,
0), 0),
(GIANTWALLNUT := "GiantWallNut",
CARD_GIANTWALLNUT := "card_giantwallnut",
0,
0),
) )
# 卡片中的植物名称与索引序号的对应关系,指定名称以得到索引值 # 卡片中的植物名称与索引序号的对应关系,指定名称以得到索引值
@ -376,7 +380,7 @@ for i, item in enumerate(PLANT_CARD_INFO):
PLANT_CARD_INDEX[item[PLANT_NAME_INDEX]] = i PLANT_CARD_INDEX[item[PLANT_NAME_INDEX]] = i
# 指定了哪些卡可选(排除坚果保龄球特殊植物) # 指定了哪些卡可选(排除坚果保龄球特殊植物)
CARDS_TO_CHOOSE = range(len(PLANT_CARD_INFO) - 2) CARDS_TO_CHOOSE = range(len(PLANT_CARD_INFO) - 3)
# 植物集体属性集合 # 植物集体属性集合
@ -408,6 +412,7 @@ PLANT_DIE_SOUND_EXCEPTIONS = {
ICEFROZENPLOT, HOLE, ICEFROZENPLOT, HOLE,
GRAVE, JALAPENO, GRAVE, JALAPENO,
REDWALLNUTBOWLING, CHERRYBOMB, REDWALLNUTBOWLING, CHERRYBOMB,
GIANTWALLNUT,
} }
# 直接水生植物 # 直接水生植物
@ -426,7 +431,7 @@ PLANT_NON_CHECK_ATTACK_STATE = ( # 这里运用了集合运算
SUNSHROOM, COFFEEBEAN, SUNSHROOM, COFFEEBEAN,
GRAVEBUSTER, LILYPAD, GRAVEBUSTER, LILYPAD,
HYPNOSHROOM, GARLIC, HYPNOSHROOM, GARLIC,
PUMPKINHEAD, PUMPKINHEAD, GIANTWALLNUT,
} | } |
# 非植物类 # 非植物类
NON_PLANT_OBJECTS NON_PLANT_OBJECTS
@ -492,6 +497,11 @@ STAR_BACKWARD = "backward" #向后
STAR_UPWARD = "upward" # 向上 STAR_UPWARD = "upward" # 向上
STAR_DOWNWARD = "downward" # 向下 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_IMAGE_RECT = "zombie_image_rect"
ZOMBIE_HEAD = "ZombieHead" ZOMBIE_HEAD = "ZombieHead"

View File

@ -102,28 +102,28 @@ class Level(tool.State):
# 可以考虑将波刷新和一波中的僵尸生成分开 # 可以考虑将波刷新和一波中的僵尸生成分开
# useableZombie是指可用的僵尸种类的元组 # useableZombie是指可用的僵尸种类的元组
# inevitableZombie指在本轮必然出现的僵尸输入形式为字典: {波数1:(僵尸1, 僵尸2……), 波数2:(僵尸1, 僵尸2……)……} # inevitableZombie指在本轮必然出现的僵尸输入形式为字典: {波数1:(僵尸1, 僵尸2……), 波数2:(僵尸1, 僵尸2……)……}
def createWaves(self, useableZombies, numFlags, survivalRounds=0, inevitable_zombie_dict=None): def createWaves(self, useable_zombies, num_flags, survival_rounds=0, inevitable_zombie_dict=None):
waves = [] waves = []
self.numFlags = numFlags self.num_flags = num_flags
# 权重值 # 权重值
weights = [] weights = []
for zombie in useableZombies: for zombie in useable_zombies:
weights.append(c.CREATE_ZOMBIE_DICT[zombie][1]) weights.append(c.CREATE_ZOMBIE_DICT[zombie][1])
# 按照原版pvz设计的僵尸容量函数是从无尽解析的但是普通关卡也可以遵循 # 按照原版pvz设计的僵尸容量函数是从无尽解析的但是普通关卡也可以遵循
for wave in range(1, 10 * numFlags + 1): for wave in range(1, 10 * num_flags + 1):
volume = int(int((wave + survivalRounds*20)*0.8)/2) + 1 volume = int(int((wave + survival_rounds*20)*0.8)/2) + 1
zombieList = [] zombie_list = []
# 大波僵尸情况 # 大波僵尸情况
if wave % 10 == 0: if wave % 10 == 0:
# 容量增大至2.5倍 # 容量增大至2.5倍
volume = int(volume*2.5) volume = int(volume*2.5)
# 先生成旗帜僵尸 # 先生成旗帜僵尸
zombieList.append(c.FLAG_ZOMBIE) zombie_list.append(c.FLAG_ZOMBIE)
volume -= c.CREATE_ZOMBIE_DICT[c.FLAG_ZOMBIE][0] volume -= c.CREATE_ZOMBIE_DICT[c.FLAG_ZOMBIE][0]
# 传送带模式应当增大僵尸容量 # 传送带模式应当增大僵尸容量
@ -132,23 +132,23 @@ class Level(tool.State):
if inevitable_zombie_dict and (wave in inevitable_zombie_dict): if inevitable_zombie_dict and (wave in inevitable_zombie_dict):
for new_zombie in inevitable_zombie_dict[wave]: for new_zombie in inevitable_zombie_dict[wave]:
zombieList.append(new_zombie) zombie_list.append(new_zombie)
volume -= c.CREATE_ZOMBIE_DICT[new_zombie][0] volume -= c.CREATE_ZOMBIE_DICT[new_zombie][0]
if volume < 0: if volume < 0:
logger.warning(f"{wave}波中手动设置的僵尸级别总数超过上限!") logger.warning(f"{wave}波中手动设置的僵尸级别总数超过上限!")
# 防止因为僵尸最小等级过大,使得总容量无法完全利用,造成死循环的检查机制 # 防止因为僵尸最小等级过大,使得总容量无法完全利用,造成死循环的检查机制
minCost = c.CREATE_ZOMBIE_DICT[min(useableZombies, key=lambda x:c.CREATE_ZOMBIE_DICT[x][0])][0] min_cost = c.CREATE_ZOMBIE_DICT[min(useable_zombies, key=lambda x:c.CREATE_ZOMBIE_DICT[x][0])][0]
while (volume >= minCost) and (len(zombieList) < 50): while (volume >= min_cost) and (len(zombie_list) < 50):
new_zombie = random.choices(useableZombies, weights)[0] new_zombie = random.choices(useable_zombies, weights)[0]
# 普通僵尸、路障僵尸、铁桶僵尸有概率生成水中变种 # 普通僵尸、路障僵尸、铁桶僵尸有概率生成水中变种
if self.background_type in c.POOL_EQUIPPED_BACKGROUNDS: if self.background_type in c.POOL_EQUIPPED_BACKGROUNDS:
# 有泳池第一轮的第四波设定上生成水生僵尸 # 有泳池第一轮的第四波设定上生成水生僵尸
if survivalRounds == 0 and wave == 4: if survival_rounds == 0 and wave == 4:
if new_zombie in c.CONVERT_ZOMBIE_IN_POOL: if new_zombie in c.CONVERT_ZOMBIE_IN_POOL:
new_zombie = c.CONVERT_ZOMBIE_IN_POOL[new_zombie] new_zombie = c.CONVERT_ZOMBIE_IN_POOL[new_zombie]
elif survivalRounds > 0 or wave > 4: elif survival_rounds > 0 or wave > 4:
if random.randint(1, 3) == 1: # 1/3概率水上暂时人为设定 if random.randint(1, 3) == 1: # 1/3概率水上暂时人为设定
if new_zombie in c.CONVERT_ZOMBIE_IN_POOL: if new_zombie in c.CONVERT_ZOMBIE_IN_POOL:
new_zombie = c.CONVERT_ZOMBIE_IN_POOL[new_zombie] new_zombie = c.CONVERT_ZOMBIE_IN_POOL[new_zombie]
@ -156,22 +156,22 @@ class Level(tool.State):
elif new_zombie in c.WATER_ZOMBIE: elif new_zombie in c.WATER_ZOMBIE:
continue continue
if c.CREATE_ZOMBIE_DICT[new_zombie][0] <= volume: if c.CREATE_ZOMBIE_DICT[new_zombie][0] <= volume:
zombieList.append(new_zombie) zombie_list.append(new_zombie)
volume -= c.CREATE_ZOMBIE_DICT[new_zombie][0] volume -= c.CREATE_ZOMBIE_DICT[new_zombie][0]
waves.append(zombieList) waves.append(zombie_list)
# print(wave, zombieList, len(zombieList)) # print(wave, zombie_list, len(zombie_list))
self.waves = waves self.waves = waves
# 针对有泳池的关卡 # 针对有泳池的关卡
# 表示尚未生成最后一波中从水里冒出来的僵尸 # 表示尚未生成最后一波中从水里冒出来的僵尸
self.createdZombieFromPool = False self.created_zombie_from_pool = False
# 僵尸的刷新机制 # 僵尸的刷新机制
def refreshWaves(self, current_time, survivalRounds=0): def refreshWaves(self, current_time, survival_rounds=0):
# 最后一波或者大于最后一波 # 最后一波或者大于最后一波
# 如果在夜晚按需从墓碑生成僵尸 # 如果在夜晚按需从墓碑生成僵尸 有泳池时从水中生成僵尸
# 否则直接return # 否则直接return
if self.wave_num >= self.map_data[c.NUM_FLAGS] * 10: if self.wave_num >= self.map_data[c.NUM_FLAGS] * 10:
if self.map_data[c.BACKGROUND_TYPE] == c.BACKGROUND_NIGHT: if self.map_data[c.BACKGROUND_TYPE] == c.BACKGROUND_NIGHT:
@ -227,7 +227,7 @@ class Level(tool.State):
self.zombie_groups[item[1]].add(zombie.ConeHeadZombie(item_x, item_y, self.head_group)) self.zombie_groups[item[1]].add(zombie.ConeHeadZombie(item_x, item_y, self.head_group))
self.grave_zombie_created = True self.grave_zombie_created = True
elif self.map_data[c.BACKGROUND_TYPE] in c.POOL_EQUIPPED_BACKGROUNDS: elif self.map_data[c.BACKGROUND_TYPE] in c.POOL_EQUIPPED_BACKGROUNDS:
if not self.createdZombieFromPool: if not self.created_zombie_from_pool:
if current_time - self.wave_time > 1500: if current_time - self.wave_time > 1500:
for i in range(3): for i in range(3):
# 水中倒数四列内可以在此时产生僵尸。共产生3个 # 水中倒数四列内可以在此时产生僵尸。共产生3个
@ -235,14 +235,14 @@ class Level(tool.State):
item_x, item_y = self.map.getMapGridPos(map_x, map_y) item_x, item_y = self.map.getMapGridPos(map_x, map_y)
# 用随机数指定产生的僵尸类型 # 用随机数指定产生的僵尸类型
# 暂时设定为生成概率相同 # 暂时设定为生成概率相同
zombieType = random.randint(1, 3) zombie_type = random.randint(1, 3)
if zombieType == 1: if zombie_type == 1:
self.zombie_groups[map_y].add(zombie.BucketHeadDuckyTubeZombie(item_x, item_y, self.head_group)) self.zombie_groups[map_y].add(zombie.BucketHeadDuckyTubeZombie(item_x, item_y, self.head_group))
elif zombieType == 2: elif zombie_type == 2:
self.zombie_groups[map_y].add(zombie.ConeHeadDuckyTubeZombie(item_x, item_y, self.head_group)) self.zombie_groups[map_y].add(zombie.ConeHeadDuckyTubeZombie(item_x, item_y, self.head_group))
else: else:
self.zombie_groups[map_y].add(zombie.DuckyTubeZombie(item_x, item_y, self.head_group)) self.zombie_groups[map_y].add(zombie.DuckyTubeZombie(item_x, item_y, self.head_group))
self.createdZombieFromPool = True self.created_zombie_from_pool = True
return return
# 还未开始出现僵尸 # 还未开始出现僵尸
@ -250,7 +250,7 @@ class Level(tool.State):
if (self.wave_time == 0): # 表明刚刚开始游戏 if (self.wave_time == 0): # 表明刚刚开始游戏
self.wave_time = current_time self.wave_time = current_time
else: else:
if (survivalRounds == 0) and (self.bar_type == c.CHOOSEBAR_STATIC): # 首次选卡等待时间较长 if (survival_rounds == 0) and (self.bar_type == c.CHOOSEBAR_STATIC): # 首次选卡等待时间较长
if current_time - self.wave_time >= 18000: if current_time - self.wave_time >= 18000:
self.wave_num += 1 self.wave_num += 1
self.wave_time = current_time self.wave_time = current_time
@ -404,6 +404,7 @@ class Level(tool.State):
if self.background_type in c.DAYTIME_BACKGROUNDS and self.bar_type == c.CHOOSEBAR_STATIC: if self.background_type in c.DAYTIME_BACKGROUNDS and self.bar_type == c.CHOOSEBAR_STATIC:
self.produce_sun = True self.produce_sun = True
self.fallen_sun = 0 # 已掉落的阳光
else: else:
self.produce_sun = False self.produce_sun = False
self.sun_timer = self.current_time self.sun_timer = self.current_time
@ -419,16 +420,16 @@ class Level(tool.State):
self.wave_zombies = [] self.wave_zombies = []
self.zombie_num = 0 self.zombie_num = 0
# 暂时没有生存模式,所以 survivalRounds = 0 # 暂时没有生存模式,所以 survival_rounds = 0
if c.INEVITABLE_ZOMBIE_DICT in self.map_data: if c.INEVITABLE_ZOMBIE_DICT in self.map_data:
self.createWaves( useableZombies=self.map_data[c.INCLUDED_ZOMBIES], self.createWaves( useable_zombies=self.map_data[c.INCLUDED_ZOMBIES],
numFlags=self.map_data[c.NUM_FLAGS], num_flags=self.map_data[c.NUM_FLAGS],
survivalRounds=0, survival_rounds=0,
inevitable_zombie_dict=self.map_data[c.INEVITABLE_ZOMBIE_DICT]) inevitable_zombie_dict=self.map_data[c.INEVITABLE_ZOMBIE_DICT])
else: else:
self.createWaves( useableZombies=self.map_data[c.INCLUDED_ZOMBIES], self.createWaves( useable_zombies=self.map_data[c.INCLUDED_ZOMBIES],
numFlags=self.map_data[c.NUM_FLAGS], num_flags=self.map_data[c.NUM_FLAGS],
survivalRounds=0) survival_rounds=0)
self.setupCars() self.setupCars()
# 地图有铲子才添加铲子 # 地图有铲子才添加铲子
@ -458,9 +459,9 @@ class Level(tool.State):
else: else:
grade_graves = 1 grade_graves = 1
graveVolume = c.GRAVES_GRADE_INFO[grade_graves] grave_volume = c.GRAVES_GRADE_INFO[grade_graves]
self.grave_set = set() self.grave_set = set()
while len(self.grave_set) < graveVolume: while len(self.grave_set) < grave_volume:
map_x = random.randint(4, 8) # 注意是从0开始编号 map_x = random.randint(4, 8) # 注意是从0开始编号
map_y = random.randint(0, 4) map_y = random.randint(0, 4)
self.grave_set.add((map_x, map_y)) self.grave_set.add((map_x, map_y))
@ -654,10 +655,6 @@ class Level(tool.State):
return return
def play(self, mouse_pos, mouse_click): def play(self, mouse_pos, mouse_click):
# 原版阳光掉落机制需要
# 已掉落的阳光
self.fallen_sun = 0
# 如果暂停 # 如果暂停
if self.show_game_menu: if self.show_game_menu:
self.pauseAndCheckMenuOptions(mouse_pos, mouse_click) self.pauseAndCheckMenuOptions(mouse_pos, mouse_click)
@ -704,13 +701,12 @@ class Level(tool.State):
self.sun_group.add(plant.Sun(x, 0, x, y)) self.sun_group.add(plant.Sun(x, 0, x, y))
self.fallen_sun += 1 self.fallen_sun += 1
# wcb 添加
# 检查有没有捡到阳光 # 检查有没有捡到阳光
clicked_sun = False clicked_sun = False
clicked_cards_or_map = False clicked_cards_or_map = False
if not self.drag_plant and not self.drag_shovel and mouse_pos and mouse_click[0]: if not self.drag_plant and not self.drag_shovel and mouse_pos and mouse_click[0]:
for sun in self.sun_group: for sun in self.sun_group:
if sun.checkCollision(mouse_pos[0], mouse_pos[1]): if sun.checkCollision(*mouse_pos):
self.menubar.increaseSunValue(sun.sun_value) self.menubar.increaseSunValue(sun.sun_value)
clicked_sun = True clicked_sun = True
# 播放收集阳光的音效 # 播放收集阳光的音效
@ -767,7 +763,7 @@ class Level(tool.State):
self.menubar.update(self.current_time) self.menubar.update(self.current_time)
# 检查碰撞啥的 # 检查碰撞
self.checkBulletCollisions() self.checkBulletCollisions()
self.checkZombieCollisions() self.checkZombieCollisions()
self.checkPlants() self.checkPlants()
@ -925,6 +921,8 @@ class Level(tool.State):
new_plant = plant.Garlic(x, y) new_plant = plant.Garlic(x, y)
elif self.plant_name == c.PUMPKINHEAD: elif self.plant_name == c.PUMPKINHEAD:
new_plant = plant.PumpkinHead(x, y) new_plant = plant.PumpkinHead(x, y)
elif self.plant_name == c.GIANTWALLNUT:
new_plant = plant.GiantWallNut(x, y)
if new_plant.can_sleep and self.background_type in c.DAYTIME_BACKGROUNDS: if new_plant.can_sleep and self.background_type in c.DAYTIME_BACKGROUNDS:
@ -1016,13 +1014,13 @@ class Level(tool.State):
continue continue
if collided_func(zombie, bullet): if collided_func(zombie, bullet):
if zombie.state != c.DIE: if zombie.state != c.DIE:
zombie.setDamage(bullet.damage, effect=bullet.effect, damageType=bullet.damageType) zombie.setDamage(bullet.damage, effect=bullet.effect, damage_type=bullet.damage_type)
bullet.setExplode() bullet.setExplode()
# 火球有溅射伤害 # 火球有溅射伤害
if bullet.name == c.BULLET_FIREBALL: if bullet.name == c.BULLET_FIREBALL:
for rangeZombie in self.zombie_groups[i]: for rangeZombie in self.zombie_groups[i]:
if abs(rangeZombie.rect.x - bullet.rect.x) <= (c.GRID_X_SIZE // 2): if abs(rangeZombie.rect.x - bullet.rect.x) <= (c.GRID_X_SIZE // 2):
rangeZombie.setDamage(c.BULLET_DAMAGE_FIREBALL_RANGE, effect=None, damageType=c.ZOMBIE_DEAFULT_DAMAGE) rangeZombie.setDamage(c.BULLET_DAMAGE_FIREBALL_RANGE, effect=None, damage_type=c.ZOMBIE_DEAFULT_DAMAGE)
break break
@ -1105,7 +1103,9 @@ class Level(tool.State):
zombie.prey_map_x, zombie.prey_map_y = self.map.getMapIndex(target_plant.rect.centerx, target_plant.rect.centery) zombie.prey_map_x, zombie.prey_map_y = self.map.getMapIndex(target_plant.rect.centerx, target_plant.rect.centery)
# 撑杆跳的特殊情况 # 撑杆跳的特殊情况
if zombie.name in {c.POLE_VAULTING_ZOMBIE} and (not zombie.jumped): if zombie.name in {c.POLE_VAULTING_ZOMBIE} and (not zombie.jumped):
if not zombie.jumping: if target_plant.name == c.GIANTWALLNUT:
zombie.health = 0
elif not zombie.jumping:
zombie.jump_map_x, zombie.jump_map_y = min(c.GRID_X_LEN - 1, zombie.prey_map_x), min(self.map_y_len - 1, zombie.prey_map_y) zombie.jump_map_x, zombie.jump_map_y = min(c.GRID_X_LEN - 1, zombie.prey_map_x), min(self.map_y_len - 1, zombie.prey_map_y)
jump_x = target_plant.rect.x - c.GRID_X_SIZE * 0.6 jump_x = target_plant.rect.x - c.GRID_X_SIZE * 0.6
if c.TALLNUT in self.map.map[zombie.jump_map_y][zombie.jump_map_x][c.MAP_PLANT]: if c.TALLNUT in self.map.map[zombie.jump_map_y][zombie.jump_map_x][c.MAP_PLANT]:
@ -1123,15 +1123,17 @@ class Level(tool.State):
if target_plant.canHit(i): if target_plant.canHit(i):
# target_plant.vel_y不为0有纵向速度表明已经发生过碰撞对铁门秒杀这里实现为忽略二类防具攻击 # target_plant.vel_y不为0有纵向速度表明已经发生过碰撞对铁门秒杀这里实现为忽略二类防具攻击
if target_plant.vel_y and zombie.name == c.SCREEN_DOOR_ZOMBIE: if target_plant.vel_y and zombie.name == c.SCREEN_DOOR_ZOMBIE:
zombie.setDamage(c.WALLNUT_BOWLING_DAMAGE, damageType=c.ZOMBIE_COMMON_DAMAGE) zombie.setDamage(c.WALLNUT_BOWLING_DAMAGE, damage_type=c.ZOMBIE_COMMON_DAMAGE)
else: else:
zombie.setDamage(c.WALLNUT_BOWLING_DAMAGE, damageType=c.ZOMBIE_WALLNUT_BOWLING_DANMAGE) zombie.setDamage(c.WALLNUT_BOWLING_DAMAGE, damage_type=c.ZOMBIE_WALLNUT_BOWLING_DANMAGE)
target_plant.changeDirection(i) target_plant.changeDirection(i)
# 播放撞击音效 # 播放撞击音效
c.SOUND_BOWLING_IMPACT.play() c.SOUND_BOWLING_IMPACT.play()
elif target_plant.name == c.REDWALLNUTBOWLING: elif target_plant.name == c.REDWALLNUTBOWLING:
if target_plant.state == c.IDLE: if target_plant.state == c.IDLE:
target_plant.setAttack() target_plant.setAttack()
elif target_plant.name == c.GIANTWALLNUT:
zombie.health = 0
elif zombie.target_y_change: elif zombie.target_y_change:
# 大蒜作用正在生效的僵尸不进行传递 # 大蒜作用正在生效的僵尸不进行传递
continue continue
@ -1194,7 +1196,7 @@ class Level(tool.State):
((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]是在右边的情况 ((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: if effect == c.BULLET_EFFECT_UNICE:
zombie.ice_slow_ratio = 1 zombie.ice_slow_ratio = 1
zombie.setDamage(1800, damageType=c.ZOMBIE_ASH_DAMAGE) zombie.setDamage(1800, damage_type=c.ZOMBIE_ASH_DAMAGE)
if zombie.health <= 0: if zombie.health <= 0:
zombie.setBoomDie() zombie.setBoomDie()
@ -1205,7 +1207,7 @@ class Level(tool.State):
for i in range(self.map_y_len): for i in range(self.map_y_len):
for zombie in self.zombie_groups[i]: for zombie in self.zombie_groups[i]:
zombie.setFreeze(plant.trap_frames[0]) zombie.setFreeze(plant.trap_frames[0])
zombie.setDamage(20, damageType=c.ZOMBIE_RANGE_DAMAGE) # 寒冰菇还有全场20的伤害 zombie.setDamage(20, damage_type=c.ZOMBIE_RANGE_DAMAGE) # 寒冰菇还有全场20的伤害
def killPlant(self, target_plant, shovel=False): def killPlant(self, target_plant, shovel=False):
x, y = target_plant.getPosition() x, y = target_plant.getPosition()
@ -1284,7 +1286,7 @@ class Level(tool.State):
# 双判断:发生碰撞或在攻击范围内 # 双判断:发生碰撞或在攻击范围内
if ((pg.sprite.collide_mask(zombie, target_plant)) or if ((pg.sprite.collide_mask(zombie, target_plant)) or
(abs(zombie.rect.centerx - target_plant.rect.centerx) <= target_plant.explode_x_range)): (abs(zombie.rect.centerx - target_plant.rect.centerx) <= target_plant.explode_x_range)):
zombie.setDamage(1800, damageType=c.ZOMBIE_RANGE_DAMAGE) zombie.setDamage(1800, damage_type=c.ZOMBIE_RANGE_DAMAGE)
target_plant.boomed = True target_plant.boomed = True
elif target_plant.name == c.SQUASH: elif target_plant.name == c.SQUASH:
for zombie in self.zombie_groups[i]: for zombie in self.zombie_groups[i]:
@ -1494,8 +1496,8 @@ class Level(tool.State):
pg.draw.rect(surface, c.YELLOWGREEN, filledBarRect) pg.draw.rect(surface, c.YELLOWGREEN, filledBarRect)
# 画旗帜 # 画旗帜
for i in range(self.numFlags): for i in range(self.num_flags):
self.level_progress_flag_rect.x = self.level_progress_bar_image_rect.x + int((150*i)/self.numFlags) + 5 # 常数是猜的 self.level_progress_flag_rect.x = self.level_progress_bar_image_rect.x + int((150*i)/self.num_flags) + 5 # 常数是猜的
# 当指示进度的僵尸头在旗帜左侧时升高旗帜 # 当指示进度的僵尸头在旗帜左侧时升高旗帜
if self.level_progress_flag_rect.x - 7 >= self.level_progress_zombie_head_image_rect.x: 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 # 常数是猜的 self.level_progress_flag_rect.y = self.level_progress_bar_image_rect.y - 15 # 常数是猜的