实现潜水僵尸
This commit is contained in:
parent
c1d0505fd9
commit
b281094b44
@ -5,7 +5,7 @@
|
|||||||
**本项目为个人python语言学习的练习项目,仅供个人学习和研究使用,不得用于其他用途。如果这个游戏侵犯了版权,请联系我删除**
|
**本项目为个人python语言学习的练习项目,仅供个人学习和研究使用,不得用于其他用途。如果这个游戏侵犯了版权,请联系我删除**
|
||||||
|
|
||||||
* 已有的植物:向日葵,豌豆射手,坚果墙,寒冰射手,樱桃炸弹,双发射手,三线射手,大嘴花,小喷菇,土豆雷,地刺,胆小菇,倭瓜,火爆辣椒,阳光菇,寒冰菇,魅惑菇,火炬树桩,睡莲,杨桃,咖啡豆,海蘑菇,高坚果,缠绕水草,毁灭菇,墓碑吞噬者,大喷菇
|
* 已有的植物:向日葵,豌豆射手,坚果墙,寒冰射手,樱桃炸弹,双发射手,三线射手,大嘴花,小喷菇,土豆雷,地刺,胆小菇,倭瓜,火爆辣椒,阳光菇,寒冰菇,魅惑菇,火炬树桩,睡莲,杨桃,咖啡豆,海蘑菇,高坚果,缠绕水草,毁灭菇,墓碑吞噬者,大喷菇
|
||||||
* 已有的僵尸:普通僵尸,旗帜僵尸,路障僵尸,铁桶僵尸,读报僵尸,橄榄球僵尸,鸭子救生圈僵尸,铁门僵尸,撑杆跳僵尸,冰车僵尸
|
* 已有的僵尸:普通僵尸,旗帜僵尸,路障僵尸,铁桶僵尸,读报僵尸,橄榄球僵尸,鸭子救生圈僵尸,铁门僵尸,撑杆跳僵尸,冰车僵尸,潜水僵尸
|
||||||
* 使用 JSON 文件记录关卡信息数据
|
* 使用 JSON 文件记录关卡信息数据
|
||||||
* 支持选择植物卡片
|
* 支持选择植物卡片
|
||||||
* 支持白昼模式,夜晚模式,泳池模式,传送带模式和坚果保龄球模式
|
* 支持白昼模式,夜晚模式,泳池模式,传送带模式和坚果保龄球模式
|
||||||
|
|||||||
@ -16,7 +16,8 @@
|
|||||||
{"time":0, "map_y":1, "name":"NewspaperZombie"},
|
{"time":0, "map_y":1, "name":"NewspaperZombie"},
|
||||||
{"time":0, "map_y":0, "name":"PoleVaultingZombie"},
|
{"time":0, "map_y":0, "name":"PoleVaultingZombie"},
|
||||||
{"time":6000, "map_y":0, "name":"FootballZombie"},
|
{"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"}
|
{"time":90000, "map_y":2, "name":"ConeheadDuckyTubeZombie"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -320,9 +320,11 @@ class Plant(pg.sprite.Sprite):
|
|||||||
self.image.set_alpha(255)
|
self.image.set_alpha(255)
|
||||||
|
|
||||||
def canAttack(self, zombie):
|
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
|
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.left <= c.SCREEN_WIDTH):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def setAttack(self):
|
def setAttack(self):
|
||||||
@ -620,6 +622,8 @@ class Chomper(Plant):
|
|||||||
def canAttack(self, zombie):
|
def canAttack(self, zombie):
|
||||||
if (zombie.name in {c.POLE_VAULTING_ZOMBIE}) and (not zombie.jumped):
|
if (zombie.name in {c.POLE_VAULTING_ZOMBIE}) and (not zombie.jumped):
|
||||||
return False
|
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
|
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)):
|
(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()
|
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):
|
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
|
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)):
|
(self.rect.x + c.GRID_X_SIZE * 4 >= zombie.rect.x) and (zombie.rect.left <= c.SCREEN_WIDTH + 10)):
|
||||||
return True
|
return True
|
||||||
@ -741,7 +747,7 @@ class PotatoMine(Plant):
|
|||||||
self.changeFrames(self.idle_frames)
|
self.changeFrames(self.idle_frames)
|
||||||
self.is_init = False
|
self.is_init = False
|
||||||
|
|
||||||
def canAttack(self, zombie):
|
def canAttack(self, zombie): # 土豆雷不可能遇上潜水僵尸
|
||||||
if (zombie.name == c.POLE_VAULTING_ZOMBIE and (not zombie.jumped)):
|
if (zombie.name == c.POLE_VAULTING_ZOMBIE and (not zombie.jumped)):
|
||||||
return False
|
return False
|
||||||
# 这里碰撞应当比碰撞一般更容易,就设置成圆形或矩形模式,不宜采用mask
|
# 这里碰撞应当比碰撞一般更容易,就设置成圆形或矩形模式,不宜采用mask
|
||||||
@ -1310,6 +1316,8 @@ class StarFruit(Plant):
|
|||||||
self.map_x, self.map_y = self.level.map.getMapIndex(x, y)
|
self.map_x, self.map_y = self.level.map.getMapIndex(x, y)
|
||||||
|
|
||||||
def canAttack(self, zombie):
|
def canAttack(self, zombie):
|
||||||
|
if (zombie.name == c.SNORKELZOMBIE) and (zombie.frames == zombie.swim_frames):
|
||||||
|
return False
|
||||||
if zombie.state != c.DIE:
|
if zombie.state != c.DIE:
|
||||||
_, zombieMapY = self.level.map.getMapIndex(zombie.rect.centerx, zombie.rect.bottom)
|
_, zombieMapY = self.level.map.getMapIndex(zombie.rect.centerx, zombie.rect.bottom)
|
||||||
if (self.rect.x >= zombie.rect.x) and (self.map_y == zombieMapY): # 对于同行且在杨桃后的僵尸
|
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()
|
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):
|
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
|
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)):
|
(self.rect.x + c.GRID_X_SIZE * 4 >= zombie.rect.x) and (zombie.rect.left <= c.SCREEN_WIDTH + 10)):
|
||||||
return True
|
return True
|
||||||
@ -1707,6 +1717,8 @@ class FumeShroom(Plant):
|
|||||||
self.frames = self.idle_frames
|
self.frames = self.idle_frames
|
||||||
|
|
||||||
def canAttack(self, zombie):
|
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
|
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)):
|
(self.rect.x + c.GRID_X_SIZE * 5 >= zombie.rect.x) and (zombie.rect.left <= c.SCREEN_WIDTH + 10)):
|
||||||
return True
|
return True
|
||||||
|
|||||||
@ -210,7 +210,8 @@ class Zombie(pg.sprite.Sprite):
|
|||||||
self.helmetType2 = False
|
self.helmetType2 = False
|
||||||
if self.name == c.NEWSPAPER_ZOMBIE:
|
if self.name == c.NEWSPAPER_ZOMBIE:
|
||||||
self.speed = 2.5
|
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.health > 0:
|
||||||
if self.prey_is_plant:
|
if self.prey_is_plant:
|
||||||
self.prey.setDamage(self.damage, self)
|
self.prey.setDamage(self.damage, self)
|
||||||
@ -1093,6 +1094,8 @@ class SnorkelZombie(Zombie):
|
|||||||
def __init__(self, x, y, head_group):
|
def __init__(self, x, y, head_group):
|
||||||
Zombie.__init__(self, x, y, c.SNORKELZOMBIE, canSwim=True)
|
Zombie.__init__(self, x, y, c.SNORKELZOMBIE, canSwim=True)
|
||||||
self.speed = 1.175
|
self.speed = 1.175
|
||||||
|
self.walk_animate_interval = 60
|
||||||
|
self.canSetAttack = True
|
||||||
|
|
||||||
def loadImages(self):
|
def loadImages(self):
|
||||||
self.walk_frames = []
|
self.walk_frames = []
|
||||||
@ -1100,7 +1103,7 @@ class SnorkelZombie(Zombie):
|
|||||||
self.attack_frames = []
|
self.attack_frames = []
|
||||||
self.jump_frames = []
|
self.jump_frames = []
|
||||||
self.float_frames = []
|
self.float_frames = []
|
||||||
self.sink_frame = []
|
self.sink_frames = []
|
||||||
self.losthead_walk_frames = []
|
self.losthead_walk_frames = []
|
||||||
self.losthead_attack_frames = []
|
self.losthead_attack_frames = []
|
||||||
self.die_frames = []
|
self.die_frames = []
|
||||||
@ -1115,4 +1118,99 @@ class SnorkelZombie(Zombie):
|
|||||||
losthead_walk_name = self.name + 'LostHead'
|
losthead_walk_name = self.name + 'LostHead'
|
||||||
losthead_attack_name = self.name + 'LostHeadAttack'
|
losthead_attack_name = self.name + 'LostHeadAttack'
|
||||||
die_name = self.name + 'Die'
|
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
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,7 @@ class Level(tool.State):
|
|||||||
f = open(file_path)
|
f = open(file_path)
|
||||||
self.map_data = json.load(f)
|
self.map_data = json.load(f)
|
||||||
f.close()
|
f.close()
|
||||||
finally:
|
except Exception:
|
||||||
print("成功通关!")
|
print("成功通关!")
|
||||||
if self.game_info[c.GAME_MODE] == c.MODE_LITTLEGAME:
|
if self.game_info[c.GAME_MODE] == c.MODE_LITTLEGAME:
|
||||||
self.game_info[c.LEVEL_NUM] = c.START_LEVEL_NUM
|
self.game_info[c.LEVEL_NUM] = c.START_LEVEL_NUM
|
||||||
@ -826,6 +826,8 @@ class Level(tool.State):
|
|||||||
elif name == c.ZOMBONI:
|
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))
|
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)
|
# 先判断位置是否合法 isValid(map_x, map_y)
|
||||||
@ -999,6 +1001,8 @@ class Level(tool.State):
|
|||||||
if bullet.state == c.FLY:
|
if bullet.state == c.FLY:
|
||||||
# 利用循环而非内建精灵组碰撞判断函数,处理更加灵活,可排除已死亡僵尸
|
# 利用循环而非内建精灵组碰撞判断函数,处理更加灵活,可排除已死亡僵尸
|
||||||
for zombie in self.zombie_groups[i]:
|
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 collided_func(zombie, bullet):
|
||||||
if zombie.state != c.DIE:
|
if zombie.state != c.DIE:
|
||||||
zombie.setDamage(bullet.damage, effect=bullet.effect, damageType=c.ZOMBIE_DEAFULT_DAMAGE)
|
zombie.setDamage(bullet.damage, effect=bullet.effect, damageType=c.ZOMBIE_DEAFULT_DAMAGE)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user