Merge branch 'master' into opengl

This commit is contained in:
星外之神 2022-08-01 14:49:22 +08:00
commit c2992ebbbf
14 changed files with 723 additions and 664 deletions

2
.gitignore vendored
View File

@ -7,4 +7,4 @@ release/
__pycache__/
*/__pycache__/
# 忽略测试文件
test.py
test*.py

View File

@ -33,9 +33,10 @@
* 支持自定义游戏速度倍率
* 保存在游戏存档文件中,可以通过修改`game rate`值更改速度倍率
* 游戏完成成就显示
* 冒险模式全部完成显示银向日葵奖杯
* 任意一游戏模式全部完成显示银向日葵奖杯
* 所有模式全部完成显示金向日葵奖杯
* 光标移动到向日葵奖杯上是显示当前各个模式通关次数
* 含有游戏帮助界面 QwQ
## 环境要求
@ -312,6 +313,8 @@ pyinstaller -F pypvz.py `
![截屏19](/demo/demo19.webp)
![截屏20](/demo/demo20.webp)
![截屏21](/demo/demo21.webp)
![截屏22](/demo/demo22.webp)
![截屏23](/demo/demo23.webp)
## 关于日志与反馈

BIN
demo/demo22.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
demo/demo23.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -32,6 +32,7 @@ if __name__=="__main__":
c.GAME_LOSE: screen.GameLoseScreen(),
c.LEVEL: level.Level(),
c.AWARD_SCREEN: screen.AwardScreen(),
c.HELP_SCREEN: screen.HelpScreen(),
}
game.setup_states(state_dict, c.MAIN_MENU)
game.run()

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -9,27 +9,27 @@ class Map():
if self.background_type in c.POOL_EQUIPPED_BACKGROUNDS:
self.width = c.GRID_POOL_X_LEN
self.height = c.GRID_POOL_Y_LEN
self.gridHeightSize = c.GRID_POOL_Y_SIZE
self.grid_height_size = c.GRID_POOL_Y_SIZE
self.map = [[(self.initMapGrid(c.MAP_GRASS), self.initMapGrid(c.MAP_WATER))[y in {2, 3}] for x in range(self.width)] for y in range(self.height)]
elif self.background_type in c.ON_ROOF_BACKGROUNDS:
self.width = c.GRID_ROOF_X_LEN
self.height = c.GRID_ROOF_Y_LEN
self.gridHeightSize = c.GRID_ROOF_Y_SIZE
self.grid_height_size = c.GRID_ROOF_Y_SIZE
self.map = [[self.initMapGrid(c.MAP_TILE) for x in range(self.width)] for y in range(self.height)]
elif self.background_type == c.BACKGROUND_SINGLE:
self.width = c.GRID_X_LEN
self.height = c.GRID_Y_LEN
self.gridHeightSize = c.GRID_Y_SIZE
self.grid_height_size = c.GRID_Y_SIZE
self.map = [[(self.initMapGrid(c.MAP_UNAVAILABLE), self.initMapGrid(c.MAP_GRASS))[y == 2] for x in range(self.width)] for y in range(self.height)]
elif self.background_type == c.BACKGROUND_TRIPLE:
self.width = c.GRID_X_LEN
self.height = c.GRID_Y_LEN
self.gridHeightSize = c.GRID_Y_SIZE
self.grid_height_size = c.GRID_Y_SIZE
self.map = [[(self.initMapGrid(c.MAP_UNAVAILABLE), self.initMapGrid(c.MAP_GRASS))[y in {1, 2, 3}] for x in range(self.width)] for y in range(self.height)]
else:
self.width = c.GRID_X_LEN
self.height = c.GRID_Y_LEN
self.gridHeightSize = c.GRID_Y_SIZE
self.grid_height_size = c.GRID_Y_SIZE
self.map = [[self.initMapGrid(c.MAP_GRASS) for x in range(self.width)] for y in range(self.height)]
def isValid(self, map_x, map_y):
@ -47,14 +47,14 @@ class Map():
# 判断位置是否可用
# 暂时没有写紫卡植物的判断方法
# 由于紫卡植物需要移除以前的植物,所以可用另外定义一个函数
def isAvailable(self, map_x, map_y, plantName):
def isAvailable(self, map_x, map_y, plant_name):
# 咖啡豆和墓碑吞噬者的判别最为特殊
if plantName == c.COFFEEBEAN:
if self.map[map_y][map_x][c.MAP_SLEEP] and (plantName not in self.map[map_y][map_x][c.MAP_PLANT]):
if plant_name == c.COFFEEBEAN:
if self.map[map_y][map_x][c.MAP_SLEEP] and (plant_name not in self.map[map_y][map_x][c.MAP_PLANT]):
return True
else:
return False
if plantName == c.GRAVEBUSTER:
if plant_name == c.GRAVEBUSTER:
if (c.GRAVE in self.map[map_y][map_x][c.MAP_PLANT]):
return True
else:
@ -64,13 +64,13 @@ class Map():
return False
if self.map[map_y][map_x][c.MAP_PLOT_TYPE] == c.MAP_GRASS: # 草地
# 首先需要判断植物是否是水生植物,水生植物不能种植在陆地上
if plantName not in c.WATER_PLANTS:
if plant_name not in c.WATER_PLANTS:
if not self.map[map_y][map_x][c.MAP_PLANT]: # 没有植物肯定可以种植
return True
elif (all((i in {"花盆(未实现)", c.PUMPKINHEAD}) 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 (plant_name not in self.map[map_y][map_x][c.MAP_PLANT])): # 例外植物:集合中填花盆和南瓜头,只要这里没有这种植物就能种植
return True
elif (plantName == c.PUMPKINHEAD) and (c.PUMPKINHEAD not in self.map[map_y][map_x][c.MAP_PLANT]): # 没有南瓜头就能种南瓜头
elif (plant_name == c.PUMPKINHEAD) and (c.PUMPKINHEAD not in self.map[map_y][map_x][c.MAP_PLANT]): # 没有南瓜头就能种南瓜头
return True
else:
return False
@ -78,26 +78,26 @@ class Map():
return False
elif self.map[map_y][map_x][c.MAP_PLOT_TYPE] == c.MAP_TILE: # 屋顶
# 首先需要判断植物是否是水生植物,水生植物不能种植在陆地上
if plantName not in c.WATER_PLANTS:
if plant_name not in c.WATER_PLANTS:
if "花盆(未实现)" in self.map[map_y][map_x][c.MAP_PLANT]:
if (all((i in {"花盆(未实现)", c.PUMPKINHEAD}) 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])): # 例外植物:集合中填花盆和南瓜头,只要这里没有这种植物就能种植
if plantName in {c.SPIKEWEED}: # 不能在花盆上种植的植物
and (plant_name not in self.map[map_y][map_x][c.MAP_PLANT])): # 例外植物:集合中填花盆和南瓜头,只要这里没有这种植物就能种植
if plant_name in {c.SPIKEWEED}: # 不能在花盆上种植的植物
return False
else:
return True
elif (plantName == c.PUMPKINHEAD) and (c.PUMPKINHEAD not in self.map[map_y][map_x][c.MAP_PLANT]): # 有花盆且没有南瓜头就能种南瓜头
elif (plant_name == c.PUMPKINHEAD) and (c.PUMPKINHEAD not in self.map[map_y][map_x][c.MAP_PLANT]): # 有花盆且没有南瓜头就能种南瓜头
return True
else:
return False
elif plantName == "花盆(未实现)": # 这一格本来没有花盆而且新来的植物是花盆,可以种
elif plant_name == "花盆(未实现)": # 这一格本来没有花盆而且新来的植物是花盆,可以种
return True
else:
return False
else:
return False
elif self.map[map_y][map_x][c.MAP_PLOT_TYPE] == c.MAP_WATER: # 水里
if plantName in c.WATER_PLANTS: # 是水生植物
if plant_name in c.WATER_PLANTS: # 是水生植物
if not self.map[map_y][map_x][c.MAP_PLANT]: # 只有无植物时才能在水里种植水生植物
return True
else:
@ -105,12 +105,12 @@ class Map():
else: # 非水生植物,依赖睡莲
if c.LILYPAD in self.map[map_y][map_x][c.MAP_PLANT]:
if (all((i in {c.LILYPAD, c.PUMPKINHEAD}) 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])):
if plantName in {c.SPIKEWEED, c.POTATOMINE, "花盆(未实现)"}: # 不能在睡莲上种植的植物
and (plant_name not in self.map[map_y][map_x][c.MAP_PLANT])):
if plant_name in {c.SPIKEWEED, c.POTATOMINE, "花盆(未实现)"}: # 不能在睡莲上种植的植物
return False
else:
return True
elif (plantName == c.PUMPKINHEAD) and (c.PUMPKINHEAD not in self.map[map_y][map_x][c.MAP_PLANT]): # 在睡莲上且没有南瓜头就能种南瓜头
elif (plant_name == c.PUMPKINHEAD) and (c.PUMPKINHEAD not in self.map[map_y][map_x][c.MAP_PLANT]): # 在睡莲上且没有南瓜头就能种南瓜头
return True
else:
return False
@ -152,22 +152,22 @@ class Map():
def setMapGridType(self, map_x, map_y, plot_type):
self.map[map_y][map_x][c.MAP_PLOT_TYPE] = plot_type
def addMapPlant(self, map_x, map_y, plantName, sleep=False):
self.map[map_y][map_x][c.MAP_PLANT].add(plantName)
def addMapPlant(self, map_x, map_y, plant_name, sleep=False):
self.map[map_y][map_x][c.MAP_PLANT].add(plant_name)
self.map[map_y][map_x][c.MAP_SLEEP] = sleep
def removeMapPlant(self, map_x, map_y, plantName):
self.map[map_y][map_x][c.MAP_PLANT].discard(plantName)
def removeMapPlant(self, map_x, map_y, plant_name):
self.map[map_y][map_x][c.MAP_PLANT].discard(plant_name)
def getRandomMapIndex(self):
map_x = random.randint(0, self.width-1)
map_y = random.randint(0, self.height-1)
return (map_x, map_y)
def checkPlantToSeed(self, x, y, plantName):
def checkPlantToSeed(self, x, y, plant_name):
pos = None
map_x, map_y = self.getMapIndex(x, y)
if self.isValid(map_x, map_y) and self.isAvailable(map_x, map_y, plantName):
if self.isValid(map_x, map_y) and self.isAvailable(map_x, map_y, plant_name):
pos = self.getMapGridPos(map_x, map_y)
return pos
@ -212,7 +212,7 @@ LEVEL_MAP_DATA = (
# 第2关三行草皮
{
c.BACKGROUND_TYPE: 8,
c.INIT_SUN_NAME: 150,
c.INIT_SUN_NAME: 50,
c.SHOVEL: 1,
c.SPAWN_ZOMBIES:c.SPAWN_ZOMBIES_AUTO,
c.INCLUDED_ZOMBIES:(c.NORMAL_ZOMBIE,),
@ -412,7 +412,7 @@ LITTLE_GAME_MAP_DATA = (
c.SPIKEWEED: 100,
c.SQUASH: 100,
c.JALAPENO: 50,
c.THREEPEASHOOTER: 400,}
c.THREEPEASHOOTER: 300,}
},
)

View File

@ -37,7 +37,9 @@ class Car(pg.sprite.Sprite):
# 豌豆及孢子类普通子弹
class Bullet(pg.sprite.Sprite):
def __init__(self, x, start_y, dest_y, name, damage, effect=None, passedTorchWood=None, damageType=c.ZOMBIE_DEAFULT_DAMAGE):
def __init__( self, x, start_y, dest_y, name, damage,
effect=None, passed_torchwood_x=None,
damageType=c.ZOMBIE_DEAFULT_DAMAGE):
pg.sprite.Sprite.__init__(self)
self.name = name
@ -60,7 +62,7 @@ class Bullet(pg.sprite.Sprite):
self.current_time = 0
self.animate_timer = 0
self.animate_interval = 70
self.passedTorchWood = passedTorchWood # 记录最近通过的火炬树横坐标如果没有缺省为None
self.passed_torchwood_x = passed_torchwood_x # 记录最近通过的火炬树横坐标如果没有缺省为None
def loadFrames(self, frames, name):
frame_list = tool.GFX[name]
@ -104,7 +106,7 @@ class Bullet(pg.sprite.Sprite):
if self.y_vel * (self.dest_y - self.rect.y) < 0:
self.rect.y = self.dest_y
self.rect.x += self.x_vel
if self.rect.x >= c.SCREEN_WIDTH + 60:
if self.rect.x >= c.SCREEN_WIDTH + 20:
self.kill()
elif self.state == c.EXPLODE:
if (self.current_time - self.explode_timer) > 250:
@ -206,7 +208,7 @@ class StarBullet(Bullet):
else:
self.rect.x -= 10
self.handleMapYPosition()
if ((self.rect.x > c.SCREEN_WIDTH + 60) or (self.rect.x < -60)
if ((self.rect.x > c.SCREEN_WIDTH + 20) or (self.rect.right < -20)
or (self.rect.y > c.SCREEN_HEIGHT) or (self.rect.y < 0)):
self.kill()
elif self.state == c.EXPLODE:
@ -248,7 +250,7 @@ class Plant(pg.sprite.Sprite):
self.animate_interval = 70 # 帧播放间隔
self.hit_timer = 0
# 被铲子指向时间
self.highlightTime = 0
self.highlight_time = 0
def loadFrames(self, frames, name, scale=1, color=c.BLACK):
frame_list = tool.GFX[name]
@ -267,7 +269,7 @@ class Plant(pg.sprite.Sprite):
self.loadFrames(self.frames, name, scale)
def changeFrames(self, frames):
"""change image frames and modify rect position"""
# change image frames and modify rect position
self.frames = frames
self.frame_num = len(self.frames)
self.frame_index = 0
@ -311,7 +313,7 @@ class Plant(pg.sprite.Sprite):
self.image = self.frames[self.frame_index]
self.mask = pg.mask.from_surface(self.image)
if (self.current_time - self.highlightTime < 100):
if (self.current_time - self.highlight_time < 100):
self.image.set_alpha(150)
elif ((self.current_time - self.hit_timer) < 200):
self.image.set_alpha(192)
@ -322,7 +324,7 @@ class Plant(pg.sprite.Sprite):
if (zombie.name == c.SNORKELZOMBIE) and (zombie.frames == zombie.swim_frames):
return False
if (self.state != c.SLEEP and zombie.state != c.DIE and
self.rect.x <= zombie.rect.right and zombie.rect.left <= c.SCREEN_WIDTH):
self.rect.x <= zombie.rect.right and zombie.rect.x <= c.SCREEN_WIDTH - 24):
return True
return False
@ -338,7 +340,7 @@ class Plant(pg.sprite.Sprite):
self.changeFrames(self.sleep_frames)
def setDamage(self, damage, zombie):
if not zombie.lostHead:
if not zombie.losthead:
self.health -= damage
self.hit_timer = self.current_time
if ((self.name == c.HYPNOSHROOM) and
@ -429,20 +431,20 @@ class RepeaterPea(Plant):
self.shoot_timer = 0
# 是否发射第一颗
self.firstShot = False
self.first_shot = False
def attacking(self):
if self.shoot_timer == 0:
self.shoot_timer = self.current_time - 700
elif (self.current_time - self.shoot_timer >= 1400):
self.firstShot = True
self.first_shot = True
self.bullet_group.add(Bullet(self.rect.right - 15, self.rect.y, self.rect.y,
c.BULLET_PEA, c.BULLET_DAMAGE_NORMAL, effect=None))
self.shoot_timer = self.current_time
# 播放发射音效
c.SOUND_SHOOT.play()
elif self.firstShot and (self.current_time - self.shoot_timer) > 100:
self.firstShot = False
elif self.first_shot and (self.current_time - self.shoot_timer) > 100:
self.first_shot = 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))
# 播放发射音效
@ -580,7 +582,7 @@ class CherryBomb(Plant):
self.image = self.frames[self.frame_index]
self.mask = pg.mask.from_surface(self.image)
if (self.current_time - self.highlightTime < 100):
if (self.current_time - self.highlight_time < 100):
self.image.set_alpha(150)
elif ((self.current_time - self.hit_timer) < 200):
self.image.set_alpha(192)
@ -596,7 +598,7 @@ class Chomper(Plant):
self.digest_interval = 15000
self.attack_zombie = None
self.zombie_group = None
self.shouldDiggest = False
self.should_diggest = False
def loadImages(self, name, scale):
self.idle_frames = []
@ -624,7 +626,7 @@ class Chomper(Plant):
if (zombie.name == c.SNORKELZOMBIE) and (zombie.frames == zombie.swim_frames):
return False
elif (self.state == c.IDLE and zombie.state != c.DIGEST and
self.rect.x <= zombie.rect.centerx and (not zombie.lostHead) and
self.rect.x <= zombie.rect.centerx and (not zombie.losthead) and
(self.rect.x + c.GRID_X_SIZE*2.7 >= zombie.rect.centerx)):
return True
return False
@ -648,12 +650,12 @@ class Chomper(Plant):
# 播放吞的音效
c.SOUND_BIGCHOMP.play()
if self.attack_zombie.alive():
self.shouldDiggest = True
self.should_diggest = True
self.attack_zombie.kill()
if (self.frame_index + 1) == self.frame_num:
if self.shouldDiggest:
if self.should_diggest:
self.setDigest()
self.shouldDiggest = False
self.should_diggest = False
else:
self.setIdle()
@ -751,7 +753,7 @@ class PotatoMine(Plant):
return False
# 这里碰撞应当比碰撞一般更容易就设置成圆形或矩形模式不宜采用mask
elif (pg.sprite.collide_circle_ratio(0.7)(zombie, self) and
(not self.is_init) and (not zombie.lostHead)):
(not self.is_init) and (not zombie.losthead)):
return True
return False
@ -767,12 +769,12 @@ class PotatoMine(Plant):
class Squash(Plant):
def __init__(self, x, y, mapPlantsSet):
def __init__(self, x, y, map_plant_set):
Plant.__init__(self, x, y, c.SQUASH, c.PLANT_HEALTH, None)
self.orig_pos = (x, y)
self.aim_timer = 0
self.start_boom = False # 和灰烬等植物统一变量名,在这里表示倭瓜是否跳起
self.mapPlantsSet = mapPlantsSet
self.map_plant_set = map_plant_set
def loadImages(self, name, scale):
self.idle_frames = []
@ -816,7 +818,7 @@ class Squash(Plant):
if self.canAttack(zombie):
zombie.setDamage(1800, damageType=c.ZOMBIE_RANGE_DAMAGE)
self.health = 0 # 避免僵尸在原位啃食
self.mapPlantsSet.remove(c.SQUASH)
self.map_plant_set.remove(c.SQUASH)
self.kill()
# 播放碾压音效
c.SOUND_SQUASHING.play()
@ -870,7 +872,7 @@ class Spikeweed(Plant):
if self.canAttack(zombie):
# 有车的僵尸
if zombie.name in {c.ZOMBONI}:
zombie.health = zombie.lostHeadHealth
zombie.health = zombie.losthead_health
killSelf = True
else:
zombie.setDamage(20, damageType=c.ZOMBIE_COMMON_DAMAGE)
@ -924,7 +926,7 @@ class Jalapeno(Plant):
self.image = self.frames[self.frame_index]
self.mask = pg.mask.from_surface(self.image)
if (self.current_time - self.highlightTime < 100):
if (self.current_time - self.highlight_time < 100):
self.image.set_alpha(150)
elif ((self.current_time - self.hit_timer) < 200):
self.image.set_alpha(192)
@ -1091,7 +1093,7 @@ class IceShroom(Plant):
self.image = self.frames[self.frame_index]
self.mask = pg.mask.from_surface(self.image)
if (self.current_time - self.highlightTime < 100):
if (self.current_time - self.highlight_time < 100):
self.image.set_alpha(150)
elif ((self.current_time - self.hit_timer) < 200):
self.image.set_alpha(192)
@ -1288,16 +1290,16 @@ class TorchWood(Plant):
def idling(self):
for i in self.bullet_group:
if i.name == c.BULLET_PEA:
if i.passedTorchWood != self.rect.centerx:
if i.passed_torchwood_x != self.rect.centerx:
if abs(i.rect.centerx - self.rect.centerx) <= 20:
self.bullet_group.add(Bullet(i.rect.x, i.rect.y, i.dest_y,
c.BULLET_FIREBALL, c.BULLET_DAMAGE_FIREBALL_BODY, effect=c.BULLET_EFFECT_UNICE, passedTorchWood=self.rect.centerx))
c.BULLET_FIREBALL, c.BULLET_DAMAGE_FIREBALL_BODY, effect=c.BULLET_EFFECT_UNICE, passed_torchwood_x=self.rect.centerx))
i.kill()
elif i.name == c.BULLET_PEA_ICE:
if i.passedTorchWood != self.rect.centerx:
if i.passed_torchwood_x != self.rect.centerx:
if abs(i.rect.centerx - self.rect.centerx) <= 20:
self.bullet_group.add(Bullet(i.rect.x, i.rect.y, i.dest_y,
c.BULLET_PEA, c.BULLET_DAMAGE_NORMAL, effect=None, passedTorchWood=self.rect.centerx))
c.BULLET_PEA, c.BULLET_DAMAGE_NORMAL, effect=None, passed_torchwood_x=self.rect.centerx))
i.kill()
class StarFruit(Plant):
@ -1311,8 +1313,8 @@ class StarFruit(Plant):
if (zombie.name == c.SNORKELZOMBIE) and (zombie.frames == zombie.swim_frames):
return False
if zombie.state != c.DIE:
zombieMapY = self.level.map.getMapIndex(zombie.rect.centerx, zombie.rect.bottom)[1]
if (self.rect.x >= zombie.rect.x) and (self.map_y == zombieMapY): # 对于同行且在杨桃后的僵尸
zombie_map_y = self.level.map.getMapIndex(zombie.rect.centerx, zombie.rect.bottom)[1]
if (self.rect.x >= zombie.rect.x) and (self.map_y == zombie_map_y): # 对于同行且在杨桃后的僵尸
return True
# 斜向上理想直线方程为f(zombie.rect.x) = -0.75*(zombie.rect.x - (self.rect.right - 5)) + self.rect.y - 10
# 注意实际上为射线
@ -1348,10 +1350,10 @@ class StarFruit(Plant):
class CoffeeBean(Plant):
def __init__(self, x, y, plant_group, mapContent, map, map_x):
def __init__(self, x, y, plant_group, map_content, map, map_x):
Plant.__init__(self, x, y, c.COFFEEBEAN, c.PLANT_HEALTH, None)
self.plant_group = plant_group
self.mapContent = mapContent
self.map_content = map_content
self.map = map
self.map_x = map_x
@ -1360,7 +1362,7 @@ class CoffeeBean(Plant):
self.frame_index += 1
if self.frame_index >= self.frame_num:
self.mapContent[c.MAP_SLEEP] = False
self.map_content[c.MAP_SLEEP] = False
for plant in self.plant_group:
if plant.can_sleep:
if plant.state == c.SLEEP:
@ -1371,7 +1373,7 @@ class CoffeeBean(Plant):
plant.changeFrames(plant.idle_frames)
# 播放唤醒音效
c.SOUND_MUSHROOM_WAKEUP.play()
self.mapContent[c.MAP_PLANT].remove(self.name)
self.map_content[c.MAP_PLANT].remove(self.name)
self.kill()
self.frame_index = self.frame_num - 1
@ -1379,7 +1381,7 @@ class CoffeeBean(Plant):
self.image = self.frames[self.frame_index]
self.mask = pg.mask.from_surface(self.image)
if (self.current_time - self.highlightTime < 100):
if (self.current_time - self.highlight_time < 100):
self.image.set_alpha(150)
elif ((self.current_time - self.hit_timer) < 200):
self.image.set_alpha(192)
@ -1480,7 +1482,7 @@ class TangleKlep(Plant):
self.frames = self.idle_frames
def canAttack(self, zombie):
if zombie.state != c.DIE and (not zombie.lostHead):
if zombie.state != c.DIE and (not zombie.losthead):
# 这里碰撞应当比碰撞一般更容易就设置成圆形或矩形模式不宜采用mask
if pg.sprite.collide_circle_ratio(0.7)(zombie, self):
return True
@ -1510,17 +1512,17 @@ class TangleKlep(Plant):
# 坑形态的毁灭菇同地刺一样不可以被啃食
# 爆炸时杀死同一格的所有植物
class DoomShroom(Plant):
def __init__(self, x, y, mapPlantsSet, explode_y_range):
def __init__(self, x, y, map_plant_set, explode_y_range):
Plant.__init__(self, x, y, c.DOOMSHROOM, c.PLANT_HEALTH, None)
self.can_sleep = True
self.mapPlantSet = mapPlantsSet
self.map_plant_set = map_plant_set
self.bomb_timer = 0
self.explode_y_range = explode_y_range
self.explode_x_range = 250
self.start_boom = False
self.boomed = False
self.originalX = x
self.originalY = y
self.original_x = x
self.original_y = y
def loadImages(self, name, scale):
self.idle_frames = []
@ -1556,7 +1558,7 @@ class DoomShroom(Plant):
if self.frame_index >= self.frame_num:
self.health = 0
self.frame_index = self.frame_num - 1
self.mapPlantSet.add(c.HOLE)
self.map_plant_set.add(c.HOLE)
# 睡觉状态
elif self.state == c.SLEEP:
if (self.current_time - self.animate_timer) > self.animate_interval:
@ -1576,7 +1578,7 @@ class DoomShroom(Plant):
self.image = self.frames[self.frame_index]
self.mask = pg.mask.from_surface(self.image)
if (self.current_time - self.highlightTime < 100):
if (self.current_time - self.highlight_time < 100):
self.image.set_alpha(150)
elif ((self.current_time - self.hit_timer) < 200):
self.image.set_alpha(192)
@ -1668,8 +1670,8 @@ class GraveBuster(Plant):
self.frame_index = self.frame_num - 1
for item in self.plant_group:
if item.name == c.GRAVE:
itemMapX, _ = self.map.getMapIndex(item.rect.centerx, item.rect.bottom)
if itemMapX == self.map_x:
item_map_x, _ = self.map.getMapIndex(item.rect.centerx, item.rect.bottom)
if item_map_x == self.map_x:
item.health = 0
self.health = 0
self.animate_timer = self.current_time
@ -1677,7 +1679,7 @@ class GraveBuster(Plant):
self.image = self.frames[self.frame_index]
self.mask = pg.mask.from_surface(self.image)
if (self.current_time - self.highlightTime < 100):
if (self.current_time - self.highlight_time < 100):
self.image.set_alpha(150)
elif ((self.current_time - self.hit_timer) < 200):
self.image.set_alpha(192)
@ -1689,7 +1691,7 @@ class FumeShroom(Plant):
Plant.__init__(self, x, y, c.FUMESHROOM, c.PLANT_HEALTH, bullet_group)
self.can_sleep = True
self.shoot_timer = 0
self.showAttackFrames = True
self.show_attack_frames = True
self.zombie_group = zombie_group
def loadImages(self, name, scale):
@ -1726,18 +1728,18 @@ class FumeShroom(Plant):
if self.shoot_timer == 0:
self.shoot_timer = self.current_time - 700
elif self.current_time - self.shoot_timer >= 1100:
if self.showAttackFrames:
self.showAttackFrames = False
if self.show_attack_frames:
self.show_attack_frames = False
self.changeFrames(self.attack_frames)
if self.current_time - self.shoot_timer >= 1400:
self.bullet_group.add(Fume(self.rect.right - 35, self.rect.y))
# 烟雾只是个动画,实际伤害由本身完成
for targetZombie in self.zombie_group:
if self.canAttack(targetZombie):
targetZombie.setDamage(c.BULLET_DAMAGE_NORMAL, damageType=c.ZOMBIE_RANGE_DAMAGE)
for target_zombie in self.zombie_group:
if self.canAttack(target_zombie):
target_zombie.setDamage(c.BULLET_DAMAGE_NORMAL, damageType=c.ZOMBIE_RANGE_DAMAGE)
self.shoot_timer = self.current_time
self.showAttackFrames = True
self.show_attack_frames = True
# 播放发射音效
c.SOUND_FUME.play()
@ -1754,7 +1756,7 @@ class FumeShroom(Plant):
self.image = self.frames[self.frame_index]
self.mask = pg.mask.from_surface(self.image)
if (self.current_time - self.highlightTime < 100):
if (self.current_time - self.highlight_time < 100):
self.image.set_alpha(150)
elif ((self.current_time - self.hit_timer) < 200):
self.image.set_alpha(192)

View File

@ -5,7 +5,10 @@ from .. import constants as c
class Zombie(pg.sprite.Sprite):
def __init__(self, x, y, name, head_group=None, helmetHealth=0, helmetType2Health=0, bodyHealth=c.NORMAL_HEALTH, lostHeadHealth=c.LOSTHEAD_HEALTH, damage=c.ZOMBIE_ATTACK_DAMAGE, canSwim=False):
def __init__( self, x, y, name, head_group=None,
helmet_health=0, helmet_type2_health=0,
body_health=c.NORMAL_HEALTH, losthead_health=c.LOSTHEAD_HEALTH,
damage=c.ZOMBIE_ATTACK_DAMAGE, can_swim=False):
pg.sprite.Sprite.__init__(self)
self.name = name
@ -15,26 +18,26 @@ class Zombie(pg.sprite.Sprite):
self.frame_num = len(self.frames)
self.image = self.frames[self.frame_index]
self.mask = pg.mask.from_surface(self.image)
self.rect = self.image.get_rect()
self.mask = pg.mask.from_surface(self.image)
self.rect.x = x
self.rect.bottom = y
# 大蒜换行移动像素值,< 0时向上= 0时不变> 0时向上
self.targetYChange = 0
self.originalY = y
self.toChangeGroup = False
self.target_y_change = 0
self.original_y = y
self.to_change_group = False
self.helmetHealth = helmetHealth
self.helmetType2Health = helmetType2Health
self.health = bodyHealth + lostHeadHealth
self.lostHeadHealth = lostHeadHealth
self.helmet_health = helmet_health
self.helmet_type2_health = helmet_type2_health
self.health = body_health + losthead_health
self.losthead_health = losthead_health
self.damage = damage
self.dead = False
self.lostHead = False
self.canSwim = canSwim
self.losthead = False
self.can_swim = can_swim
self.swimming = False
self.helmet = (self.helmetHealth > 0)
self.helmetType2 = (self.helmetType2Health > 0)
self.helmet = (self.helmet_health > 0)
self.helmet_type2 = (self.helmet_type2_health > 0)
self.head_group = head_group
self.walk_timer = 0
@ -44,7 +47,7 @@ class Zombie(pg.sprite.Sprite):
self.animate_interval = 150
self.walk_animate_interval = 180
self.attack_animate_interval = 100
self.lostHead_animate_interval = 180
self.losthead_animate_interval = 180
self.die_animate_interval = 50
self.boomDie_animate_interval = 100
self.ice_slow_ratio = 1
@ -88,8 +91,8 @@ class Zombie(pg.sprite.Sprite):
if self.health <= 0:
self.setDie()
return True
elif self.health <= self.lostHeadHealth:
if not self.lostHead:
elif self.health <= self.losthead_health:
if not self.losthead:
self.changeFrames(framesKind)
self.setLostHead()
return True
@ -105,7 +108,7 @@ class Zombie(pg.sprite.Sprite):
return
# 能游泳的僵尸
if self.canSwim:
if self.can_swim:
# 在水池范围内
# 在右侧岸左
if self.rect.right <= c.MAP_POOL_FRONT_X:
@ -119,25 +122,25 @@ class Zombie(pg.sprite.Sprite):
c.SOUND_ZOMBIE_ENTERING_WATER.play()
# 同样没有兼容双防具
if self.helmet:
if self.helmetHealth <= 0:
if self.helmet_health <= 0:
self.helmet = False
else:
self.changeFrames(self.helmet_swim_frames)
if self.helmetType2:
if self.helmetType2Health <= 0:
self.helmetType2 = False
if self.helmet_type2:
if self.helmet_type2_health <= 0:
self.helmet_type2 = False
else:
self.changeFrames(self.helmet_swim_frames)
# 已经进入游泳状态
else:
if self.helmet:
if self.helmetHealth <= 0:
if self.helmet_health <= 0:
self.changeFrames(self.swim_frames)
self.helmet = False
if self.helmetType2:
if self.helmetType2Health <= 0:
if self.helmet_type2:
if self.helmet_type2_health <= 0:
self.changeFrames(self.swim_frames)
self.helmetType2 = False
self.helmet_type2 = False
# 水生僵尸已经接近家门口并且上岸
else:
if self.swimming:
@ -145,55 +148,55 @@ class Zombie(pg.sprite.Sprite):
self.swimming = False
# 同样没有兼容双防具
if self.helmet:
if self.helmetHealth <= 0:
if self.helmet_health <= 0:
self.helmet = False
else:
self.changeFrames(self.helmet_walk_frames)
if self.helmetType2:
if self.helmetType2Health <= 0:
self.helmetType2 = False
if self.helmet_type2:
if self.helmet_type2_health <= 0:
self.helmet_type2 = False
else:
self.changeFrames(self.helmet_walk_frames)
if self.helmet:
if self.helmetHealth <= 0:
if self.helmet_health <= 0:
self.helmet = False
self.changeFrames(self.walk_frames)
if self.helmetType2:
if self.helmetType2Health <= 0:
self.helmetType2 = False
if self.helmet_type2:
if self.helmet_type2_health <= 0:
self.helmet_type2 = False
self.changeFrames(self.walk_frames)
elif self.is_hypno and self.rect.right > c.MAP_POOL_FRONT_X + 55: # 常数拟合暂时缺乏检验
if self.swimming:
self.changeFrames(self.walk_frames)
if self.helmet:
if self.helmetHealth <= 0:
if self.helmet_health <= 0:
self.changeFrames(self.walk_frames)
self.helmet = False
elif self.swimming: # 游泳状态需要改为步行
self.changeFrames(self.helmet_walk_frames)
if self.helmetType2:
if self.helmetType2Health <= 0:
if self.helmet_type2:
if self.helmet_type2_health <= 0:
self.changeFrames(self.walk_frames)
self.helmetType2 = False
self.helmet_type2 = False
elif self.swimming: # 游泳状态需要改为步行
self.changeFrames(self.helmet_walk_frames)
self.swimming = False
# 尚未进入水池
else:
if self.helmetHealth <= 0 and self.helmet:
if self.helmet_health <= 0 and self.helmet:
self.changeFrames(self.walk_frames)
self.helmet = False
if self.helmetType2Health <= 0 and self.helmetType2:
if self.helmet_type2_health <= 0 and self.helmet_type2:
self.changeFrames(self.walk_frames)
self.helmetType2 = False
self.helmet_type2 = False
# 不能游泳的一般僵尸
else:
if self.helmetHealth <= 0 and self.helmet:
if self.helmet_health <= 0 and self.helmet:
self.changeFrames(self.walk_frames)
self.helmet = False
if self.helmetType2Health <= 0 and self.helmetType2:
if self.helmet_type2_health <= 0 and self.helmet_type2:
self.changeFrames(self.walk_frames)
self.helmetType2 = False
self.helmet_type2 = False
if (self.current_time - self.walk_timer) > (c.ZOMBIE_WALK_INTERVAL * self.getTimeRatio()):
self.handleGarlicYChange()
@ -204,48 +207,48 @@ class Zombie(pg.sprite.Sprite):
self.rect.x -= 1
def handleGarlicYChange(self):
if self.targetYChange < 0:
if self.rect.bottom > self.originalY + self.targetYChange: # 注意这里加的是负数
if self.target_y_change < 0:
if self.rect.bottom > self.original_y + self.target_y_change: # 注意这里加的是负数
self.rect.bottom -= 3
# 过半时换行
if ((self.toChangeGroup) and
(self.rect.bottom >= self.originalY + 0.5*self.targetYChange)):
self.level.zombie_groups[self.mapY].remove(self)
self.level.zombie_groups[self.targetMapY].add(self)
self.toChangeGroup = False
if ((self.to_change_group) and
(self.rect.bottom >= self.original_y + 0.5*self.target_y_change)):
self.level.zombie_groups[self.map_y].remove(self)
self.level.zombie_groups[self.target_map_y].add(self)
self.to_change_group = False
else:
self.rect.bottom = self.originalY + self.targetYChange
self.originalY = self.rect.bottom
self.targetYChange = 0
elif self.targetYChange > 0:
if self.rect.bottom < self.originalY + self.targetYChange: # 注意这里加的是负数
self.rect.bottom = self.original_y + self.target_y_change
self.original_y = self.rect.bottom
self.target_y_change = 0
elif self.target_y_change > 0:
if self.rect.bottom < self.original_y + self.target_y_change: # 注意这里加的是负数
self.rect.bottom += 3
# 过半时换行
if ((self.toChangeGroup) and
(self.rect.bottom <= self.originalY + 0.5*self.targetYChange)):
self.level.zombie_groups[self.mapY].remove(self)
self.level.zombie_groups[self.targetMapY].add(self)
self.toChangeGroup = False
if ((self.to_change_group) and
(self.rect.bottom <= self.original_y + 0.5*self.target_y_change)):
self.level.zombie_groups[self.map_y].remove(self)
self.level.zombie_groups[self.target_map_y].add(self)
self.to_change_group = False
else:
self.rect.bottom = self.originalY + self.targetYChange
self.originalY = self.rect.bottom
self.targetYChange = 0
self.rect.bottom = self.original_y + self.target_y_change
self.original_y = self.rect.bottom
self.target_y_change = 0
def attacking(self):
if self.checkToDie(self.losthead_attack_frames):
return
if self.helmetHealth <= 0 and self.helmet:
if self.helmet_health <= 0 and self.helmet:
self.changeFrames(self.attack_frames)
self.helmet = False
if self.helmetType2Health <= 0 and self.helmetType2:
if self.helmet_type2_health <= 0 and self.helmet_type2:
self.changeFrames(self.attack_frames)
self.helmetType2 = False
self.helmet_type2 = False
if self.name == c.NEWSPAPER_ZOMBIE:
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)):
and (not self.losthead)):
if self.prey.health > 0:
if self.prey_is_plant:
self.prey.setDamage(self.damage, self)
@ -281,8 +284,8 @@ class Zombie(pg.sprite.Sprite):
def setLostHead(self):
self.losthead_timer = self.current_time
self.lostHead = True
self.animate_interval = self.lostHead_animate_interval
self.losthead = True
self.animate_interval = self.losthead_animate_interval
if self.head_group is not None:
self.head_group.add(ZombieHead(self.rect.centerx, self.rect.bottom))
@ -347,87 +350,87 @@ class Zombie(pg.sprite.Sprite):
# 冰冻减速效果
if effect == c.BULLET_EFFECT_ICE:
if damageType == c.ZOMBIE_DEAFULT_DAMAGE: # 寒冰射手不能穿透二类防具进行减速
if not self.helmetType2:
if not self.helmet_type2:
self.setIceSlow()
else:
self.setIceSlow()
# 解冻
elif effect == c.BULLET_EFFECT_UNICE:
if damageType == c.ZOMBIE_DEAFULT_DAMAGE: # 寒冰射手不能穿透二类防具进行减速
if not self.helmetType2:
if not self.helmet_type2:
self.ice_slow_ratio = 1
else:
self.ice_slow_ratio = 1
if damageType == c.ZOMBIE_DEAFULT_DAMAGE: # 不穿透二类防具的攻击
# 从第二类防具开始逐级传递
if self.helmetType2:
self.helmetType2Health -= damage
if self.helmetType2Health <= 0:
if self.helmet_type2:
self.helmet_type2_health -= damage
if self.helmet_type2_health <= 0:
if self.helmet:
self.helmetHealth += self.helmetType2Health # 注意self.helmetType2Health已经带有正负
self.helmetType2Health = 0 # 注意合并后清零
if self.helmetHealth <= 0:
self.health += self.helmetHealth
self.helmetHealth = 0 # 注意合并后清零
self.helmet_health += self.helmet_type2_health # 注意self.helmet_type2_health已经带有正负
self.helmet_type2_health = 0 # 注意合并后清零
if self.helmet_health <= 0:
self.health += self.helmet_health
self.helmet_health = 0 # 注意合并后清零
else:
self.health += self.helmetType2Health
self.helmetType2Health = 0
self.health += self.helmet_type2_health
self.helmet_type2_health = 0
elif self.helmet: # 不存在二类防具,但是存在一类防具
self.helmetHealth -= damage
if self.helmetHealth <= 0:
self.health += self.helmetHealth
self.helmetHealth = 0 # 注意合并后清零
self.helmet_health -= damage
if self.helmet_health <= 0:
self.health += self.helmet_health
self.helmet_health = 0 # 注意合并后清零
else: # 没有防具
self.health -= damage
elif damageType == c.ZOMBIE_COMMON_DAMAGE: # 无视二类防具,将攻击一类防具与本体视为整体的攻击
if self.helmet: # 存在一类防具
self.helmetHealth -= damage
if self.helmetHealth <= 0:
self.health += self.helmetHealth
self.helmetHealth = 0 # 注意合并后清零
self.helmet_health -= damage
if self.helmet_health <= 0:
self.health += self.helmet_health
self.helmet_health = 0 # 注意合并后清零
else: # 没有一类防具
self.health -= damage
elif damageType == c.ZOMBIE_RANGE_DAMAGE:
# 从第二类防具开始逐级传递
if self.helmetType2:
self.helmetType2Health -= damage
if self.helmetType2Health <= 0:
if self.helmet_type2:
self.helmet_type2_health -= damage
if self.helmet_type2_health <= 0:
if self.helmet:
self.helmetHealth -= damage # 注意范围伤害中这里还有一个攻击
self.helmetHealth += self.helmetType2Health # 注意self.helmetType2Health已经带有正负
self.helmetType2Health = 0 # 注意合并后清零
if self.helmetHealth <= 0:
self.health += self.helmetHealth
self.helmetHealth = 0 # 注意合并后清零
self.helmet_health -= damage # 注意范围伤害中这里还有一个攻击
self.helmet_health += self.helmet_type2_health # 注意self.helmet_type2_health已经带有正负
self.helmet_type2_health = 0 # 注意合并后清零
if self.helmet_health <= 0:
self.health += self.helmet_health
self.helmet_health = 0 # 注意合并后清零
else:
self.health -= damage # 注意范围伤害中这里还有一个攻击
self.health += self.helmetType2Health
self.helmetType2Health = 0
self.health += self.helmet_type2_health
self.helmet_type2_health = 0
else:
if self.helmet:
self.helmetHealth -= damage
if self.helmetHealth <= 0:
self.health += self.helmetHealth
self.helmetHealth = 0 # 注意合并后清零
self.helmet_health -= damage
if self.helmet_health <= 0:
self.health += self.helmet_health
self.helmet_health = 0 # 注意合并后清零
else:
self.health -= damage
elif self.helmet: # 不存在二类防具,但是存在一类防具
self.helmetHealth -= damage
if self.helmetHealth <= 0:
self.health += self.helmetHealth
self.helmetHealth = 0 # 注意合并后清零
self.helmet_health -= damage
if self.helmet_health <= 0:
self.health += self.helmet_health
self.helmet_health = 0 # 注意合并后清零
else: # 没有防具
self.health -= damage
elif damageType == c.ZOMBIE_ASH_DAMAGE:
self.health -= damage # 无视任何防具
elif damageType == c.ZOMBIE_WALLNUT_BOWLING_DANMAGE:
# 逻辑:对防具的多余伤害不传递
if self.helmetType2:
if self.helmet_type2:
# 对二类防具伤害较一般情况低拟合铁门需要砸3次的设定
self.helmetType2Health -= int(damage * 0.8)
self.helmet_type2_health -= int(damage * 0.8)
elif self.helmet: # 不存在二类防具,但是存在一类防具
self.helmetHealth -= damage
self.helmet_health -= damage
else: # 没有防具
self.health -= damage
else:
@ -441,28 +444,28 @@ class Zombie(pg.sprite.Sprite):
self.state = c.WALK
self.animate_interval = self.walk_animate_interval
if self.helmet or self.helmetType2: # 这里暂时没有考虑同时有两种防具的僵尸
if self.helmet or self.helmet_type2: # 这里暂时没有考虑同时有两种防具的僵尸
self.changeFrames(self.helmet_walk_frames)
elif self.lostHead:
elif self.losthead:
self.changeFrames(self.losthead_walk_frames)
else:
self.changeFrames(self.walk_frames)
if self.canSwim:
if self.can_swim:
if self.rect.right <= c.MAP_POOL_FRONT_X:
self.swimming = True
self.changeFrames(self.swim_frames)
# 同样没有兼容双防具
if self.helmet:
if self.helmetHealth <= 0:
if self.helmet_health <= 0:
self.changeFrames(self.swim_frames)
self.helmet = False
else:
self.changeFrames(self.helmet_swim_frames)
if self.helmetType2:
if self.helmetType2Health <= 0:
if self.helmet_type2:
if self.helmet_type2_health <= 0:
self.changeFrames(self.swim_frames)
self.helmetType2 = False
self.helmet_type2 = False
else:
self.changeFrames(self.helmet_swim_frames)
@ -473,9 +476,9 @@ class Zombie(pg.sprite.Sprite):
self.attack_timer = self.current_time
self.animate_interval = self.attack_animate_interval
if self.helmet or self.helmetType2: # 这里暂时没有考虑同时有两种防具的僵尸
if self.helmet or self.helmet_type2: # 这里暂时没有考虑同时有两种防具的僵尸
self.changeFrames(self.helmet_attack_frames)
elif self.lostHead:
elif self.losthead:
self.changeFrames(self.losthead_attack_frames)
else:
self.changeFrames(self.attack_frames)
@ -558,7 +561,7 @@ class NormalZombie(Zombie):
# 路障僵尸
class ConeHeadZombie(Zombie):
def __init__(self, x, y, head_group):
Zombie.__init__(self, x, y, c.CONEHEAD_ZOMBIE, head_group, helmetHealth=c.CONEHEAD_HEALTH)
Zombie.__init__(self, x, y, c.CONEHEAD_ZOMBIE, head_group, helmet_health=c.CONEHEAD_HEALTH)
def loadImages(self):
self.helmet_walk_frames = []
@ -594,7 +597,7 @@ class ConeHeadZombie(Zombie):
class BucketHeadZombie(Zombie):
def __init__(self, x, y, head_group):
Zombie.__init__(self, x, y, c.BUCKETHEAD_ZOMBIE, head_group, helmetHealth=c.BUCKETHEAD_HEALTH)
Zombie.__init__(self, x, y, c.BUCKETHEAD_ZOMBIE, head_group, helmet_health=c.BUCKETHEAD_HEALTH)
def loadImages(self):
self.helmet_walk_frames = []
@ -661,8 +664,8 @@ class FlagZombie(Zombie):
class NewspaperZombie(Zombie):
def __init__(self, x, y, head_group):
Zombie.__init__(self, x, y, c.NEWSPAPER_ZOMBIE, head_group, helmetType2Health=c.NEWSPAPER_HEALTH)
self.speedUp = False
Zombie.__init__(self, x, y, c.NEWSPAPER_ZOMBIE, head_group, helmet_type2_health=c.NEWSPAPER_HEALTH)
self.speed_up = False
def loadImages(self):
self.helmet_walk_frames = []
@ -707,9 +710,9 @@ class NewspaperZombie(Zombie):
if self.checkToDie(self.losthead_walk_frames):
return
if self.helmetType2Health <= 0 and self.helmetType2:
if self.helmet_type2_health <= 0 and self.helmet_type2:
self.changeFrames(self.lostnewspaper_frames)
self.helmetType2 = False
self.helmet_type2 = False
# 触发报纸撕裂音效
c.SOUND_NEWSPAPER_RIP.play()
if ((self.current_time - self.walk_timer) > (c.ZOMBIE_WALK_INTERVAL * self.getTimeRatio())):
@ -733,9 +736,9 @@ class NewspaperZombie(Zombie):
if self.state == c.DIE:
self.kill()
return
elif self.frames == self.lostnewspaper_frames and (not self.speedUp):
elif self.frames == self.lostnewspaper_frames and (not self.speed_up):
self.changeFrames(self.walk_frames)
self.speedUp = True
self.speed_up = True
self.speed = 2.65
self.walk_animate_interval = 300
# 触发报纸僵尸暴走音效
@ -755,12 +758,12 @@ class NewspaperZombie(Zombie):
class FootballZombie(Zombie):
def __init__(self, x, y, head_group):
Zombie.__init__(self, x, y, c.FOOTBALL_ZOMBIE, head_group, helmetHealth=c.FOOTBALL_HELMET_HEALTH)
Zombie.__init__(self, x, y, c.FOOTBALL_ZOMBIE, head_group, helmet_health=c.FOOTBALL_HELMET_HEALTH)
self.speed = 1.88
self.animate_interval = 50
self.walk_animate_interval = 50
self.attack_animate_interval = 60
self.lostHead_animate_interval = 180
self.losthead_animate_interval = 180
self.die_animate_interval = 150
def loadImages(self):
@ -796,7 +799,7 @@ class FootballZombie(Zombie):
class DuckyTubeZombie(Zombie):
def __init__(self, x, y, head_group):
Zombie.__init__(self, x, y, c.DUCKY_TUBE_ZOMBIE, head_group, canSwim=True)
Zombie.__init__(self, x, y, c.DUCKY_TUBE_ZOMBIE, head_group, can_swim=True)
def loadImages(self):
self.walk_frames = []
@ -827,7 +830,7 @@ class DuckyTubeZombie(Zombie):
class ConeHeadDuckyTubeZombie(Zombie):
def __init__(self, x, y, head_group):
Zombie.__init__(self, x, y, c.CONEHEAD_DUCKY_TUBE_ZOMBIE, head_group, helmetHealth=c.CONEHEAD_HEALTH ,canSwim=True)
Zombie.__init__(self, x, y, c.CONEHEAD_DUCKY_TUBE_ZOMBIE, head_group, helmet_health=c.CONEHEAD_HEALTH ,can_swim=True)
def loadImages(self):
self.helmet_walk_frames = []
@ -865,7 +868,7 @@ class ConeHeadDuckyTubeZombie(Zombie):
class BucketHeadDuckyTubeZombie(Zombie):
def __init__(self, x, y, head_group):
Zombie.__init__(self, x, y, c.BUCKETHEAD_DUCKY_TUBE_ZOMBIE, head_group, helmetHealth=c.BUCKETHEAD_HEALTH ,canSwim=True)
Zombie.__init__(self, x, y, c.BUCKETHEAD_DUCKY_TUBE_ZOMBIE, head_group, helmet_health=c.BUCKETHEAD_HEALTH ,can_swim=True)
def loadImages(self):
self.helmet_walk_frames = []
@ -903,7 +906,7 @@ class BucketHeadDuckyTubeZombie(Zombie):
class ScreenDoorZombie(Zombie):
def __init__(self, x, y, head_group):
Zombie.__init__(self, x, y, c.SCREEN_DOOR_ZOMBIE, head_group, helmetType2Health=c.SCREEN_DOOR_HEALTH)
Zombie.__init__(self, x, y, c.SCREEN_DOOR_ZOMBIE, head_group, helmet_type2_health=c.SCREEN_DOOR_HEALTH)
def loadImages(self):
self.helmet_walk_frames = []
@ -939,7 +942,7 @@ class ScreenDoorZombie(Zombie):
class PoleVaultingZombie(Zombie):
def __init__(self, x, y, head_group):
Zombie.__init__(self, x, y, c.POLE_VAULTING_ZOMBIE, head_group=head_group, bodyHealth=c.POLE_VAULTING_HEALTH, lostHeadHealth=c.POLE_VAULTING_LOSTHEAD_HEALTH)
Zombie.__init__(self, x, y, c.POLE_VAULTING_ZOMBIE, head_group=head_group, body_health=c.POLE_VAULTING_HEALTH, losthead_health=c.POLE_VAULTING_LOSTHEAD_HEALTH)
self.speed = 1.88
self.jumped = False
self.jumping = False
@ -975,12 +978,12 @@ class PoleVaultingZombie(Zombie):
self.frames = self.walk_before_jump_frames
def setJump(self, successfullyJumped, jumpX):
def setJump(self, successfullyJumped, jump_x):
if not self.jumping:
self.jumping = True
self.changeFrames(self.jump_frames)
self.successfullyJumped = successfullyJumped
self.jumpX = jumpX
self.jump_x = jump_x
# 播放跳跃音效
c.SOUND_POLEVAULT_JUMP.play()
@ -1005,7 +1008,7 @@ class PoleVaultingZombie(Zombie):
if self.jumping and (not self.jumped):
self.changeFrames(self.walk_frames)
if self.successfullyJumped:
self.rect.centerx = self.jumpX
self.rect.centerx = self.jump_x
self.jumped = True
self.speed = 1.04
self.animate_timer = self.current_time
@ -1043,7 +1046,7 @@ class PoleVaultingZombie(Zombie):
# 注意:冰车僵尸移动变速
class Zomboni(Zombie):
def __init__(self, x, y, plant_group, map, IceFrozenPlot):
Zombie.__init__(self, x, y, c.ZOMBONI, bodyHealth=c.ZOMBONI_HEALTH)
Zombie.__init__(self, x, y, c.ZOMBONI, body_health=c.ZOMBONI_HEALTH)
self.plant_group = plant_group
self.map = map
self.IceFrozenPlot = IceFrozenPlot
@ -1095,7 +1098,7 @@ class Zomboni(Zombie):
elif self.health <= c.ZOMBONI_DAMAGED1_HEALTH:
self.changeFrames(self.walk_damaged1_frames)
if (self.current_time - self.walk_timer) > (c.ZOMBIE_WALK_INTERVAL * self.getTimeRatio()) and (not self.lostHead):
if (self.current_time - self.walk_timer) > (c.ZOMBIE_WALK_INTERVAL * self.getTimeRatio()) and (not self.losthead):
self.walk_timer = self.current_time
if self.is_hypno:
self.rect.x += 1
@ -1111,14 +1114,14 @@ class Zomboni(Zombie):
plant.health -= 8000
# 造冰
mapX, mapY = self.map.getMapIndex(self.rect.right - 40, self.rect.bottom)
if 0 <= mapX < c.GRID_X_LEN:
if c.ICEFROZENPLOT not in self.map.map[mapY][mapX]:
x, y = self.map.getMapGridPos(mapX, mapY)
map_x, map_y = self.map.getMapIndex(self.rect.right - 40, self.rect.bottom)
if 0 <= map_x < c.GRID_X_LEN:
if c.ICEFROZENPLOT not in self.map.map[map_y][map_x]:
x, y = self.map.getMapGridPos(map_x, map_y)
self.plant_group.add(self.IceFrozenPlot(x, y))
self.map.map[mapY][mapX][c.MAP_PLANT].add(c.ICEFROZENPLOT)
self.map.map[map_y][map_x][c.MAP_PLANT].add(c.ICEFROZENPLOT)
self.speed = max(0.6, 1.5 - (c.GRID_X_LEN + 1 - mapX)*0.225)
self.speed = max(0.6, 1.5 - (c.GRID_X_LEN + 1 - map_x)*0.225)
def setDie(self):
self.state = c.DIE
@ -1130,7 +1133,7 @@ class Zomboni(Zombie):
class SnorkelZombie(Zombie):
def __init__(self, x, y, head_group):
Zombie.__init__(self, x, y, c.SNORKELZOMBIE, canSwim=True)
Zombie.__init__(self, x, y, c.SNORKELZOMBIE, can_swim=True)
self.speed = 1.175
self.walk_animate_interval = 60
self.canSetAttack = True
@ -1250,7 +1253,7 @@ class SnorkelZombie(Zombie):
self.prey_is_plant = is_plant
self.animate_interval = self.attack_animate_interval
if self.lostHead:
if self.losthead:
self.changeFrames(self.losthead_attack_frames)
elif self.canSetAttack:
self.changeFrames(self.float_frames)

View File

@ -71,6 +71,7 @@ PARCHMENT_YELLOW = (207, 146, 83)
# 退出游戏按钮
EXIT = "exit"
HELP = "help"
# 游戏界面可选的菜单
LITTLE_MENU = "littleMenu"
BIG_MENU = "bigMenu"
@ -110,6 +111,7 @@ GAME_LOSE = "game lose"
GAME_VICTORY = "game victory"
LEVEL = "level"
AWARD_SCREEN = "award screen"
HELP_SCREEN = "help screen"
# 界面图片文件名
MAIN_MENU_IMAGE = "MainMenu"
@ -117,6 +119,7 @@ OPTION_ADVENTURE = "Adventure"
GAME_LOSE_IMAGE = "GameLose"
GAME_VICTORY_IMAGE = "GameVictory"
AWARD_SCREEN_IMAGE = "AwardScreen"
HELP_SCREEN_IMAGE = "HelpScreen"
# 地图相关内容
BACKGROUND_NAME = "Background"
@ -166,8 +169,8 @@ GRAVES_GRADE_INFO = (0, 4, 7, 11)
# 僵尸生成方式
SPAWN_ZOMBIES = "spawn_zombies"
SPAWN_ZOMBIES_AUTO = "auto"
SPAWN_ZOMBIES_LIST = "list"
SPAWN_ZOMBIES_AUTO = 1
SPAWN_ZOMBIES_LIST = 0
INCLUDED_ZOMBIES = "included_zombies"
NUM_FLAGS = "num_flags"
INEVITABLE_ZOMBIE_DICT = "inevitable_zombie_list"
@ -302,7 +305,7 @@ PLANT_CARD_INFO = (# 元组 (植物名称, 卡片名称, 阳光, 冷却时间)
50000),
(DOOMSHROOM := "DoomShroom",
CARD_DOOMSHROOM := "card_doomshroom",
75,
125,
50000),
(LILYPAD := "LilyPad",
CARD_LILYPAD := "card_lilypad",
@ -509,8 +512,7 @@ SNORKELZOMBIE = "SnorkelZombie"
BOOMDIE = "BoomDie"
# 对僵尸的攻击类型设置
ZOMBIE_DEAFULT_DAMAGE = "helmet2First"
ZOMBIE_HELMET_2_FIRST = "helmet2First" # 优先攻击二类防具
ZOMBIE_DEAFULT_DAMAGE = ZOMBIE_HELMET_2_FIRST = "helmet2First" # 优先攻击二类防具
ZOMBIE_COMMON_DAMAGE = "commonDamage" # 优先攻击僵尸与一类防具的整体
ZOMBIE_RANGE_DAMAGE = "rangeDamage" # 范围攻击,同时伤害二类防具与(僵尸与一类防具的整体)
ZOMBIE_ASH_DAMAGE = "ashDamage" # 灰烬植物攻击,直接伤害本体

File diff suppressed because it is too large Load Diff

View File

@ -37,23 +37,27 @@ class Menu(tool.State):
def setupOptions(self):
# 冒险模式
self.adventure_frames = []
frame_names = (f"{c.OPTION_ADVENTURE}_0", f"{c.OPTION_ADVENTURE}_1")
frame_rect = (0, 0, 330, 144)
for name in frame_names:
self.adventure_frames.append(tool.get_image_menu(tool.GFX[name], *frame_rect, c.BLACK, 1))
# 写成列表生成器方便IDE识别与自动补全
self.adventure_frames = [tool.get_image_alpha(tool.GFX[f"{c.OPTION_ADVENTURE}_{i}"], *frame_rect) for i in range(2)]
self.adventure_image = self.adventure_frames[0]
self.adventure_rect = self.adventure_image.get_rect()
self.adventure_rect.x = 400
self.adventure_rect.y = 60
self.adventure_highlight_time = 0
# 小游戏
littleGame_frame_rect = (0, 7, 317, 135)
self.littleGame_frames = [tool.get_image_alpha(tool.GFX[f"{c.LITTLEGAME_BUTTON}_{i}"], *littleGame_frame_rect) for i in range(2)]
self.littleGame_image = self.littleGame_frames[0]
self.littleGame_rect = self.littleGame_image.get_rect()
self.littleGame_rect.x = 397
self.littleGame_rect.y = 175
self.littleGame_highlight_time = 0
# 退出按钮
self.exit_frames = []
exit_frame_names = (f"{c.EXIT}_0", f"{c.EXIT}_1")
exit_frame_rect = (0, 0, 47, 27)
for name in exit_frame_names:
self.exit_frames.append(tool.get_image_menu(tool.GFX[name], *exit_frame_rect, c.BLACK, 1.1))
self.exit_frames = [tool.get_image_alpha(tool.GFX[f"{c.EXIT}_{i}"], *exit_frame_rect, scale=1.1) for i in range(2)]
self.exit_image = self.exit_frames[0]
self.exit_rect = self.exit_image.get_rect()
self.exit_rect.x = 730
@ -61,29 +65,24 @@ class Menu(tool.State):
self.exit_highlight_time = 0
# 选项按钮
self.option_button_frames = []
option_button_frame_names = (f"{c.OPTION_BUTTON}_0", f"{c.OPTION_BUTTON}_1")
option_button_frame_rect = (0, 0, 81, 31)
for name in option_button_frame_names:
self.option_button_frames.append(tool.get_image_menu(tool.GFX[name], *option_button_frame_rect, c.BLACK))
self.option_button_frames = [tool.get_image_alpha(tool.GFX[f"{c.OPTION_BUTTON}_{i}"], *option_button_frame_rect) for i in range(2)]
self.option_button_image = self.option_button_frames[0]
self.option_button_rect = self.option_button_image.get_rect()
self.option_button_rect.x = 560
self.option_button_rect.y = 490
self.option_button_hightlight_time = 0
self.option_button_highlight_time = 0
# 小游戏
self.littleGame_frames = []
littleGame_frame_names = (c.LITTLEGAME_BUTTON + "_0", c.LITTLEGAME_BUTTON + "_1")
littleGame_frame_rect = (0, 7, 317, 135)
for name in littleGame_frame_names:
self.littleGame_frames.append(tool.get_image_menu(tool.GFX[name], *littleGame_frame_rect, c.BLACK, 1))
self.littleGame_image = self.littleGame_frames[0]
self.littleGame_rect = self.littleGame_image.get_rect()
self.littleGame_rect.x = 397
self.littleGame_rect.y = 175
self.littleGame_highlight_time = 0
# 帮助菜单
help_frame_rect = (0, 0, 48, 22)
self.help_frames = [tool.get_image_alpha(tool.GFX[f"{c.HELP}_{i}"], *help_frame_rect) for i in range(2)]
self.help_image = self.help_frames[0]
self.help_rect = self.help_image.get_rect()
self.help_rect.x = 653
self.help_rect.y = 520
self.help_hilight_time = 0
# 计时器与点击信号记录器
self.adventure_start = 0
self.adventure_timer = 0
self.adventure_clicked = False
@ -93,21 +92,25 @@ class Menu(tool.State):
# 高亮冒险模式按钮
if self.inArea(self.adventure_rect, x, y):
self.adventure_highlight_time = self.current_time
# 高亮小游戏按钮
elif self.inArea(self.littleGame_rect, x, y):
self.littleGame_highlight_time = self.current_time
# 高亮退出按钮
elif self.inArea(self.exit_rect, x, y):
self.exit_highlight_time = self.current_time
# 高亮选项按钮
elif self.inArea(self.option_button_rect, x, y):
self.option_button_hightlight_time = self.current_time
# 高亮小游戏按钮
elif self.inArea(self.littleGame_rect, x, y):
self.littleGame_highlight_time = self.current_time
self.option_button_highlight_time = self.current_time
# 高亮帮助按钮
elif self.inArea(self.help_rect, x, y):
self.help_hilight_time = self.current_time
# 处理按钮高亮情况
self.adventure_image = self.chooseHilightImage(self.adventure_highlight_time, self.adventure_frames)
self.exit_image = self.chooseHilightImage(self.exit_highlight_time, self.exit_frames)
self.option_button_image = self.chooseHilightImage(self.option_button_hightlight_time, self.option_button_frames)
self.option_button_image = self.chooseHilightImage(self.option_button_highlight_time, self.option_button_frames)
self.littleGame_image = self.chooseHilightImage(self.littleGame_highlight_time, self.littleGame_frames)
self.help_image = self.chooseHilightImage(self.help_hilight_time, self.help_frames)
def chooseHilightImage(self, hilightTime, frames):
if (self.current_time - hilightTime) < 80:
@ -116,9 +119,7 @@ class Menu(tool.State):
index = 0
return frames[index]
def checkAdventureClick(self, mouse_pos):
x, y = mouse_pos
if self.inArea(self.adventure_rect, x, y):
def respondAdventureClick(self):
self.adventure_clicked = True
self.adventure_timer = self.adventure_start = self.current_time
self.persist[c.GAME_MODE] = c.MODE_ADVENTURE
@ -126,26 +127,27 @@ class Menu(tool.State):
c.SOUND_EVILLAUGH.play()
c.SOUND_LOSE.play()
# 点击到按钮修改转态的done属性
def checkExitClick(self, mouse_pos):
x, y = mouse_pos
if self.inArea(self.exit_rect, x, y):
self.done = True
self.next = c.EXIT
# 检查有没有按到小游戏
def checkLittleGameClick(self, mouse_pos):
x, y = mouse_pos
if self.inArea(self.littleGame_rect, x, y):
# 按到小游戏
def respondLittleGameClick(self):
self.done = True
self.persist[c.GAME_MODE] = c.MODE_LITTLEGAME
# 播放点击音效
c.SOUND_BUTTON_CLICK.play()
# 点击到退出按钮修改转态的done属性
def respondExitClick(self):
self.done = True
self.next = c.EXIT
# 帮助按钮点击
def respondHelpClick(self):
self.done = True
self.next = c.HELP_SCREEN
def setupOptionMenu(self):
# 选项菜单框
frame_rect = (0, 0, 500, 500)
self.big_menu = tool.get_image_menu(tool.GFX[c.BIG_MENU], *frame_rect, c.BLACK, 1.1)
self.big_menu = tool.get_image_alpha(tool.GFX[c.BIG_MENU], *frame_rect, c.BLACK, 1.1)
self.big_menu_rect = self.big_menu.get_rect()
self.big_menu_rect.x = 150
self.big_menu_rect.y = 0
@ -170,7 +172,7 @@ class Menu(tool.State):
font = pg.font.Font(c.FONT_PATH, 35)
font.bold = True
# 音量+
self.sound_volume_plus_button = tool.get_image_menu(tool.GFX[c.SOUND_VOLUME_BUTTON], *frame_rect, c.BLACK)
self.sound_volume_plus_button = tool.get_image_alpha(tool.GFX[c.SOUND_VOLUME_BUTTON], *frame_rect, c.BLACK)
sign = font.render("+", True, c.YELLOWGREEN)
sign_rect = sign.get_rect()
sign_rect.x = 8
@ -179,7 +181,7 @@ class Menu(tool.State):
self.sound_volume_plus_button_rect = self.sound_volume_plus_button.get_rect()
self.sound_volume_plus_button_rect.x = 500
# 音量-
self.sound_volume_minus_button = tool.get_image_menu(tool.GFX[c.SOUND_VOLUME_BUTTON], *frame_rect, c.BLACK)
self.sound_volume_minus_button = tool.get_image_alpha(tool.GFX[c.SOUND_VOLUME_BUTTON], *frame_rect, c.BLACK)
sign = font.render("-", True, c.YELLOWGREEN)
sign_rect = sign.get_rect()
sign_rect.x = 12
@ -197,7 +199,7 @@ class Menu(tool.State):
frame_rect = (157, 0, 157, 269)
else:
frame_rect = (0, 0, 157, 269)
self.sunflower_trophy = tool.get_image_menu(tool.GFX[c.TROPHY_SUNFLOWER], *frame_rect, c.BLACK)
self.sunflower_trophy = tool.get_image_alpha(tool.GFX[c.TROPHY_SUNFLOWER], *frame_rect, c.BLACK)
self.sunflower_trophy_rect = self.sunflower_trophy.get_rect()
self.sunflower_trophy_rect.x = 0
self.sunflower_trophy_rect.y = 280
@ -216,13 +218,11 @@ class Menu(tool.State):
infoText = f"目前您一共完成了:玩玩小游戏{self.game_info[c.LITTLEGAME_COMPLETIONS]}轮;完成其他所有游戏模式以获得金向日葵奖杯!"
infoImg = font.render(infoText , True, c.BLACK, c.LIGHTYELLOW)
infoImg_rect = infoImg.get_rect()
infoImg_rect.x = x
infoImg_rect.y = y
infoImg_rect.x = self.sunflower_trophy_rect.x
infoImg_rect.y = self.sunflower_trophy_rect.bottom - 14
surface.blit(infoImg, infoImg_rect)
def checkOptionButtonClick(self, mouse_pos):
x, y = mouse_pos
if self.inArea(self.option_button_rect, x, y):
def respondOptionButtonClick(self):
self.option_button_clicked = True
# 播放点击音效
c.SOUND_BUTTON_CLICK.play()
@ -241,9 +241,10 @@ class Menu(tool.State):
surface.blit(self.bg_image, self.bg_rect)
surface.blit(self.adventure_image, self.adventure_rect)
surface.blit(self.littleGame_image, self.littleGame_rect)
surface.blit(self.exit_image, self.exit_rect)
surface.blit(self.option_button_image, self.option_button_rect)
surface.blit(self.littleGame_image, self.littleGame_rect)
surface.blit(self.help_image, self.help_rect)
if self.game_info[c.LEVEL_COMPLETIONS] or self.game_info[c.LITTLEGAME_COMPLETIONS]:
surface.blit(self.sunflower_trophy, self.sunflower_trophy_rect)
@ -294,7 +295,13 @@ class Menu(tool.State):
if (self.game_info[c.LEVEL_COMPLETIONS] or self.game_info[c.LITTLEGAME_COMPLETIONS]):
self.checkSunflowerTrophyInfo(surface, x, y)
if mouse_pos:
self.checkExitClick(mouse_pos)
self.checkOptionButtonClick(mouse_pos)
self.checkLittleGameClick(mouse_pos)
self.checkAdventureClick(mouse_pos)
if self.inArea(self.adventure_rect, *mouse_pos):
self.respondAdventureClick()
elif self.inArea(self.littleGame_rect, *mouse_pos):
self.respondLittleGameClick()
elif self.inArea(self.option_button_rect, *mouse_pos):
self.respondOptionButtonClick()
elif self.inArea(self.exit_rect, *mouse_pos):
self.respondExitClick()
elif self.inArea(self.help_rect, *mouse_pos):
self.respondHelpClick()

View File

@ -20,7 +20,7 @@ class Screen(tool.State):
# 按钮
frame_rect = (0, 0, 111, 26)
## 主菜单按钮
self.main_menu_button_image = tool.get_image_menu(tool.GFX[c.UNIVERSAL_BUTTON], *frame_rect)
self.main_menu_button_image = tool.get_image_alpha(tool.GFX[c.UNIVERSAL_BUTTON], *frame_rect)
self.main_menu_button_image_rect = self.main_menu_button_image.get_rect()
self.main_menu_button_image_rect.x = 620
### 主菜单按钮上的文字
@ -29,7 +29,7 @@ class Screen(tool.State):
main_menu_text_rect = main_menu_text.get_rect()
main_menu_text_rect.x = 29
## 继续按钮
self.next_button_image = tool.get_image_menu(tool.GFX[c.UNIVERSAL_BUTTON], *frame_rect)
self.next_button_image = tool.get_image_alpha(tool.GFX[c.UNIVERSAL_BUTTON], *frame_rect)
self.next_button_image_rect = self.next_button_image.get_rect()
self.next_button_image_rect.x = 70
### 继续按钮上的文字
@ -116,7 +116,7 @@ class AwardScreen(tool.State):
frame_rect = (0, 0, 111, 26)
if self.show_only_one_option:
## 主菜单按钮
self.main_menu_button_image = tool.get_image_menu(tool.GFX[c.UNIVERSAL_BUTTON], *frame_rect)
self.main_menu_button_image = tool.get_image_alpha(tool.GFX[c.UNIVERSAL_BUTTON], *frame_rect)
self.main_menu_button_image_rect = self.main_menu_button_image.get_rect()
self.main_menu_button_image_rect.x = 343
self.main_menu_button_image_rect.y = 520
@ -127,32 +127,7 @@ class AwardScreen(tool.State):
main_menu_text_rect.x = 29
self.main_menu_button_image.blit(main_menu_text, main_menu_text_rect)
self.image.blit(self.main_menu_button_image, self.main_menu_button_image_rect)
else:
## 继续按钮
self.next_button_image = tool.get_image_menu(tool.GFX[c.UNIVERSAL_BUTTON], *frame_rect)
self.next_button_image_rect = self.next_button_image.get_rect()
self.next_button_image_rect.x = 70
### 继续按钮上的文字
font = pg.font.Font(c.FONT_PATH, 18)
next_text = font.render("继续", True, c.NAVYBLUE)
next_text_rect = next_text.get_rect()
next_text_rect.x = 37
## 主菜单按钮
self.main_menu_button_image = tool.get_image_menu(tool.GFX[c.UNIVERSAL_BUTTON], *frame_rect)
self.main_menu_button_image_rect = self.main_menu_button_image.get_rect()
self.main_menu_button_image_rect.x = 620
self.next_button_image_rect.y = self.main_menu_button_image_rect.y = 540
### 主菜单按钮上的文字
main_menu_text = font.render("主菜单", True, c.NAVYBLUE)
main_menu_text_rect = main_menu_text.get_rect()
main_menu_text_rect.x = 29
self.next_button_image.blit(next_text, next_text_rect)
self.main_menu_button_image.blit(main_menu_text, main_menu_text_rect)
self.image.blit(self.next_button_image, self.next_button_image_rect)
self.image.blit(self.main_menu_button_image, self.main_menu_button_image_rect)
# 显示向日葵奖杯的情况
if self.show_only_one_option:
# 绘制向日葵奖杯
if (self.game_info[c.LEVEL_COMPLETIONS] and self.game_info[c.LITTLEGAME_COMPLETIONS]):
frame_rect = (157, 0, 157, 269)
@ -165,7 +140,7 @@ class AwardScreen(tool.State):
intro_content = "您已完成冒险模式,获得此奖励!"
else:
intro_content = "您已完成玩玩小游戏,获得此奖励!"
sunflower_trophy_image = tool.get_image_menu(tool.GFX[c.TROPHY_SUNFLOWER], *frame_rect, scale=0.7)
sunflower_trophy_image = tool.get_image_alpha(tool.GFX[c.TROPHY_SUNFLOWER], *frame_rect, scale=0.7)
sunflower_trophy_rect = sunflower_trophy_image.get_rect()
sunflower_trophy_rect.x = 348
sunflower_trophy_rect.y = 108
@ -186,7 +161,29 @@ class AwardScreen(tool.State):
intro_content_rect.x = 290
intro_content_rect.y = 370
self.image.blit(intro_content_img, intro_content_rect)
else:
## 继续按钮
self.next_button_image = tool.get_image_alpha(tool.GFX[c.UNIVERSAL_BUTTON], *frame_rect)
self.next_button_image_rect = self.next_button_image.get_rect()
self.next_button_image_rect.x = 70
### 继续按钮上的文字
font = pg.font.Font(c.FONT_PATH, 18)
next_text = font.render("继续", True, c.NAVYBLUE)
next_text_rect = next_text.get_rect()
next_text_rect.x = 37
## 主菜单按钮
self.main_menu_button_image = tool.get_image_alpha(tool.GFX[c.UNIVERSAL_BUTTON], *frame_rect)
self.main_menu_button_image_rect = self.main_menu_button_image.get_rect()
self.main_menu_button_image_rect.x = 620
self.next_button_image_rect.y = self.main_menu_button_image_rect.y = 540
### 主菜单按钮上的文字
main_menu_text = font.render("主菜单", True, c.NAVYBLUE)
main_menu_text_rect = main_menu_text.get_rect()
main_menu_text_rect.x = 29
self.next_button_image.blit(next_text, next_text_rect)
self.main_menu_button_image.blit(main_menu_text, main_menu_text_rect)
self.image.blit(self.next_button_image, self.next_button_image_rect)
self.image.blit(self.main_menu_button_image, self.main_menu_button_image_rect)
def startup(self, current_time, persist):
self.start_time = current_time
@ -214,3 +211,46 @@ class AwardScreen(tool.State):
if self.inArea(self.next_button_image_rect, *mouse_pos):
self.next = c.LEVEL
self.done = True
class HelpScreen(tool.State):
def __init__(self):
tool.State.__init__(self)
def startup(self, current_time, persist):
self.start_time = current_time
self.persist = persist
self.game_info = persist
self.setupImage()
pg.display.set_caption("pypvz: 帮助")
pg.mixer.music.stop()
def setupImage(self):
# 主体
frame_rect = (-100, -50, 800, 600)
self.image = tool.get_image(tool.GFX[c.HELP_SCREEN_IMAGE], *frame_rect, colorkey=(0, 255, 255))
self.rect = self.image.get_rect()
self.rect.x = 0
self.rect.y = 0
# 主菜单按钮
frame_rect = (0, 0, 111, 26)
self.main_menu_button_image = tool.get_image_alpha(tool.GFX[c.UNIVERSAL_BUTTON], *frame_rect)
self.main_menu_button_image_rect = self.main_menu_button_image.get_rect()
self.main_menu_button_image_rect.x = 343
self.main_menu_button_image_rect.y = 500
### 主菜单按钮上的文字
font = pg.font.Font(c.FONT_PATH, 18)
main_menu_text = font.render("主菜单", True, c.NAVYBLUE)
main_menu_text_rect = main_menu_text.get_rect()
main_menu_text_rect.x = 29
self.main_menu_button_image.blit(main_menu_text, main_menu_text_rect)
self.image.blit(self.main_menu_button_image, self.main_menu_button_image_rect)
def update(self, surface, current_time, mouse_pos, mouse_click):
surface.fill(c.BLACK)
surface.blit(self.image, self.rect)
if mouse_pos:
# 检查主菜单点击
if self.inArea(self.main_menu_button_image_rect, *mouse_pos):
self.next = c.MAIN_MENU
self.done = True

View File

@ -5,7 +5,7 @@ import pygame as pg
from pygame.locals import *
from . import constants as c
# an abstract class, one state of automata
# 状态机 抽象基类
class State():
def __init__(self):
self.start_time = 0
@ -17,7 +17,7 @@ class State():
# 当从其他状态进入这个状态时,需要进行的初始化操作
@abstractmethod
def startup(self, current_time, persist):
# abstract method
# 前面加了@abstractmethod表示抽象基类中必须要重新定义的methodmethod是对象和函数的结合
pass
# 当从这个状态退出时,需要进行的清除操作
def cleanup(self):
@ -26,7 +26,7 @@ class State():
# 在这个状态运行时进行的更新操作
@abstractmethod
def update(self, surface, keys, current_time):
# abstract method
# 前面加了@abstractmethod表示抽象基类中必须要重新定义的method
pass
# 工具:范围判断函数,用于判断点击
@ -47,7 +47,7 @@ class State():
data_to_save = json.dumps(userdata, sort_keys=True, indent=4)
f.write(data_to_save)
# control this game. do event loops
# 进行游戏控制 循环 事件响应
class Control():
def __init__(self):
self.screen = pg.display.get_surface()
@ -100,7 +100,7 @@ class Control():
self.state.startup(self.current_time, self.game_info)
def update(self):
# 返回自 pygame_init() 调用以来的毫秒数 * 游戏速度倍率
# 自 pygame_init() 调用以来的毫秒数 * 游戏速度倍率,即游戏时间
self.current_time = pg.time.get_ticks() * self.game_info[c.GAME_RATE]
if self.state.done:
@ -116,7 +116,6 @@ class Control():
if self.state.next == c.EXIT:
pg.quit()
os._exit(0)
# previous, self.state_name = self.state_name, self.state.next
self.state_name = self.state.next
persist = self.state.cleanup()
self.state = self.state_dict[self.state_name]
@ -162,7 +161,7 @@ def get_image(sheet, x, y, width, height, colorkey=c.BLACK, scale=1):
int(rect.height*scale)))
return image
def get_image_menu(sheet, x, y, width, height, colorkey=c.BLACK, scale=1):
def get_image_alpha(sheet, x, y, width, height, colorkey=c.BLACK, scale=1):
# 保留alpha通道的图片导入
image = pg.Surface([width, height], SRCALPHA)
rect = image.get_rect()
@ -198,7 +197,7 @@ def load_image_frames(directory, image_name, colorkey, accept):
return frame_list
# colorkeys 是设置图像中的某个颜色值为透明,这里用来消除白边
def load_all_gfx(directory, colorkey=c.WHITE, accept=(".png", ".jpg", ".bmp", ".gif", "webp")):
def load_all_gfx(directory, colorkey=c.WHITE, accept=(".png", ".jpg", ".bmp", ".gif", ".webp")):
graphics = {}
for name1 in os.listdir(directory):
# subfolders under the folder resources\graphics