实现潜水僵尸

This commit is contained in:
星外之神 2022-05-26 23:51:44 +08:00
parent c1d0505fd9
commit b281094b44
5 changed files with 124 additions and 9 deletions

View File

@ -5,7 +5,7 @@
**本项目为个人python语言学习的练习项目仅供个人学习和研究使用不得用于其他用途。如果这个游戏侵犯了版权请联系我删除**
* 已有的植物:向日葵,豌豆射手,坚果墙,寒冰射手,樱桃炸弹,双发射手,三线射手,大嘴花,小喷菇,土豆雷,地刺,胆小菇,倭瓜,火爆辣椒,阳光菇,寒冰菇,魅惑菇,火炬树桩,睡莲,杨桃,咖啡豆,海蘑菇,高坚果,缠绕水草,毁灭菇,墓碑吞噬者,大喷菇
* 已有的僵尸:普通僵尸,旗帜僵尸,路障僵尸,铁桶僵尸,读报僵尸,橄榄球僵尸,鸭子救生圈僵尸,铁门僵尸,撑杆跳僵尸,冰车僵尸
* 已有的僵尸:普通僵尸,旗帜僵尸,路障僵尸,铁桶僵尸,读报僵尸,橄榄球僵尸,鸭子救生圈僵尸,铁门僵尸,撑杆跳僵尸,冰车僵尸,潜水僵尸
* 使用 JSON 文件记录关卡信息数据
* 支持选择植物卡片
* 支持白昼模式,夜晚模式,泳池模式,传送带模式和坚果保龄球模式

View File

@ -16,7 +16,8 @@
{"time":0, "map_y":1, "name":"NewspaperZombie"},
{"time":0, "map_y":0, "name":"PoleVaultingZombie"},
{"time":6000, "map_y":0, "name":"FootballZombie"},
{"time":0, "map_y":2, "name":"ConeheadDuckyTubeZombie"},
{"time":0, "map_y":3, "name":"ConeheadDuckyTubeZombie"},
{"time":0, "map_y":2, "name":"SnorkelZombie"},
{"time":90000, "map_y":2, "name":"ConeheadDuckyTubeZombie"}
]
}

View File

@ -320,9 +320,11 @@ class Plant(pg.sprite.Sprite):
self.image.set_alpha(255)
def canAttack(self, zombie):
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):
return True
self.rect.x <= zombie.rect.right and zombie.rect.left <= c.SCREEN_WIDTH):
return True
return False
def setAttack(self):
@ -620,6 +622,8 @@ class Chomper(Plant):
def canAttack(self, zombie):
if (zombie.name in {c.POLE_VAULTING_ZOMBIE}) and (not zombie.jumped):
return False
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 + c.GRID_X_SIZE*2.7 >= zombie.rect.centerx)):
@ -694,6 +698,8 @@ class PuffShroom(Plant):
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "puff.ogg")).play()
def canAttack(self, zombie):
if (zombie.name == c.SNORKELZOMBIE) and (zombie.frames == zombie.swim_frames):
return False
if (self.rect.x <= zombie.rect.right and
(self.rect.x + c.GRID_X_SIZE * 4 >= zombie.rect.x) and (zombie.rect.left <= c.SCREEN_WIDTH + 10)):
return True
@ -741,7 +747,7 @@ class PotatoMine(Plant):
self.changeFrames(self.idle_frames)
self.is_init = False
def canAttack(self, zombie):
def canAttack(self, zombie): # 土豆雷不可能遇上潜水僵尸
if (zombie.name == c.POLE_VAULTING_ZOMBIE and (not zombie.jumped)):
return False
# 这里碰撞应当比碰撞一般更容易就设置成圆形或矩形模式不宜采用mask
@ -1310,6 +1316,8 @@ class StarFruit(Plant):
self.map_x, self.map_y = self.level.map.getMapIndex(x, y)
def canAttack(self, zombie):
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)
if (self.rect.x >= zombie.rect.x) and (self.map_y == zombieMapY): # 对于同行且在杨桃后的僵尸
@ -1418,6 +1426,8 @@ class SeaShroom(Plant):
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "puff.ogg")).play()
def canAttack(self, zombie):
if (zombie.name == c.SNORKELZOMBIE) and (zombie.frames == zombie.swim_frames):
return False
if (self.rect.x <= zombie.rect.right and
(self.rect.x + c.GRID_X_SIZE * 4 >= zombie.rect.x) and (zombie.rect.left <= c.SCREEN_WIDTH + 10)):
return True
@ -1707,6 +1717,8 @@ class FumeShroom(Plant):
self.frames = self.idle_frames
def canAttack(self, zombie):
if (zombie.name == c.SNORKELZOMBIE) and (zombie.frames == zombie.swim_frames):
return False
if (self.rect.x <= zombie.rect.right and
(self.rect.x + c.GRID_X_SIZE * 5 >= zombie.rect.x) and (zombie.rect.left <= c.SCREEN_WIDTH + 10)):
return True

View File

@ -210,7 +210,8 @@ class Zombie(pg.sprite.Sprite):
self.helmetType2 = False
if self.name == c.NEWSPAPER_ZOMBIE:
self.speed = 2.5
if ((self.current_time - self.attack_timer) > (c.ATTACK_INTERVAL * self.getAttackTimeRatio())) and (not self.lostHead):
if (((self.current_time - self.attack_timer) > (c.ATTACK_INTERVAL * self.getAttackTimeRatio()))
and (not self.lostHead)):
if self.prey.health > 0:
if self.prey_is_plant:
self.prey.setDamage(self.damage, self)
@ -1093,6 +1094,8 @@ class SnorkelZombie(Zombie):
def __init__(self, x, y, head_group):
Zombie.__init__(self, x, y, c.SNORKELZOMBIE, canSwim=True)
self.speed = 1.175
self.walk_animate_interval = 60
self.canSetAttack = True
def loadImages(self):
self.walk_frames = []
@ -1100,7 +1103,7 @@ class SnorkelZombie(Zombie):
self.attack_frames = []
self.jump_frames = []
self.float_frames = []
self.sink_frame = []
self.sink_frames = []
self.losthead_walk_frames = []
self.losthead_attack_frames = []
self.die_frames = []
@ -1115,4 +1118,99 @@ class SnorkelZombie(Zombie):
losthead_walk_name = self.name + 'LostHead'
losthead_attack_name = self.name + 'LostHeadAttack'
die_name = self.name + 'Die'
self.boomdie_name = c.BOOMDIE
boomdie_name = c.BOOMDIE
frame_list = [ self.walk_frames, self.swim_frames,
self.attack_frames, self.jump_frames,
self.float_frames, self.sink_frames,
self.losthead_walk_frames, self.losthead_attack_frames,
self.die_frames, self.boomdie_frames]
name_list = [ walk_name, swim_name,
attack_name, jump_name,
float_name, sink_name,
losthead_walk_name, losthead_attack_name,
die_name, boomdie_name]
for i, name in enumerate(name_list):
self.loadFrames(frame_list[i], name)
self.frames = self.walk_frames
def walking(self):
# 在水池范围内
# 在右侧岸左
if self.rect.centerx <= c.MAP_POOL_FRONT_X - 25:
# 在左侧岸右,左侧岸位置为预估
if self.rect.right - 25 >= c.MAP_POOL_OFFSET_X:
# 还未进入游泳状态
if not self.swimming:
self.swimming = True
self.changeFrames(self.jump_frames)
# 播放入水音效
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "zombieEnteringWater.ogg")).play()
# 已经接近家门口并且上岸
else:
if self.swimming:
self.changeFrames(self.walk_frames)
self.swimming = False
# 被魅惑时走到岸上需要起立
elif self.is_hypno and (self.rect.right > c.MAP_POOL_FRONT_X + 55): # 常数拟合暂时缺乏检验
if self.swimming:
self.changeFrames(self.walk_frames)
self.swimming = False
if (self.current_time - self.walk_timer) > (c.ZOMBIE_WALK_INTERVAL * self.getTimeRatio()):
self.walk_timer = self.current_time
# 正在上浮或者下潜不用移动
if (self.frames == self.float_frames) or (self.frames == self.sink_frames):
pass
elif self.is_hypno:
self.rect.x += 1
else:
self.rect.x -= 1
def animation(self):
if self.state == c.FREEZE:
self.image.set_alpha(192)
return
if (self.current_time - self.animate_timer) > (self.animate_interval * self.getTimeRatio()):
self.frame_index += 1
if self.frame_index >= self.frame_num:
if self.state == c.DIE:
self.kill()
return
elif (self.frames == self.jump_frames):
self.changeFrames(self.swim_frames)
elif (self.frames == self.sink_frames):
self.changeFrames(self.swim_frames)
# 还需要改回原来的可进入攻击状态的设定
self.canSetAttack = True
elif self.frames == self.float_frames:
self.state = c.ATTACK
self.attack_timer = self.current_time
self.changeFrames(self.attack_frames)
self.frame_index = 0
self.animate_timer = self.current_time
self.image = self.frames[self.frame_index]
if self.is_hypno:
self.image = pg.transform.flip(self.image, True, False)
self.mask = pg.mask.from_surface(self.image)
if (self.current_time - self.hit_timer) >= 200:
self.image.set_alpha(255)
else:
self.image.set_alpha(192)
# 注意潜水僵尸较为特殊这里的setAttack并没有直接触发攻击状态而是触发从水面浮起
def setAttack(self, prey, is_plant=True):
self.prey = prey # prey can be plant or other zombies
self.prey_is_plant = is_plant
self.animate_interval = self.attack_animate_interval
if self.lostHead:
self.changeFrames(self.losthead_attack_frames)
elif self.canSetAttack:
self.changeFrames(self.float_frames)
self.canSetAttack = False

View File

@ -41,7 +41,7 @@ class Level(tool.State):
f = open(file_path)
self.map_data = json.load(f)
f.close()
finally:
except Exception:
print("成功通关!")
if self.game_info[c.GAME_MODE] == c.MODE_LITTLEGAME:
self.game_info[c.LEVEL_NUM] = c.START_LEVEL_NUM
@ -826,6 +826,8 @@ class Level(tool.State):
elif name == c.ZOMBONI:
# 冰车僵尸生成位置不同
self.zombie_groups[map_y].add(zombie.Zomboni(c.ZOMBIE_START_X + randint(0, 10) + hugeWaveMove, y, self.plant_groups[map_y], self.map, plant.IceFrozenPlot))
elif name == c.SNORKELZOMBIE:
self.zombie_groups[map_y].add(zombie.SnorkelZombie(c.ZOMBIE_START_X + randint(0, 10) + hugeWaveMove, y, self.head_group))
# 能否种植物的判断:
# 先判断位置是否合法 isValid(map_x, map_y)
@ -999,6 +1001,8 @@ class Level(tool.State):
if bullet.state == c.FLY:
# 利用循环而非内建精灵组碰撞判断函数,处理更加灵活,可排除已死亡僵尸
for zombie in self.zombie_groups[i]:
if (zombie.name == c.SNORKELZOMBIE) and (zombie.frames == zombie.swim_frames):
continue
if collided_func(zombie, bullet):
if zombie.state != c.DIE:
zombie.setDamage(bullet.damage, effect=bullet.effect, damageType=c.ZOMBIE_DEAFULT_DAMAGE)