commit b6799a240bb2674f6b835b8402da609e22059030
Author: 星外之神 <wszqkzqk@qq.com>
Date: Fri Jun 3 20:03:55 2022 +0800
修改异常处理方式
commit dcf925ca526e66166c5b19b0b8442a32120a332d
Author: 星外之神 <wszqkzqk@qq.com>
Date: Fri Jun 3 19:51:06 2022 +0800
暂时放弃OpenGL
commit 2c0dc94c75d902aa356ee0b8350ea0aa02c84fdb
Author: 星外之神 <wszqkzqk@qq.com>
Date: Fri Jun 3 19:41:50 2022 +0800
更改非Nuitka程序图标设定
commit 1dd0894adffdafd61bd696807a6eb9d2735d4d25
Author: 星外之神 <wszqkzqk@qq.com>
Date: Fri Jun 3 19:16:21 2022 +0800
维护清理
commit b8a7fc0a66a22fae0ead9a9e4f88aa024276f078
Author: 星外之神 <wszqkzqk@qq.com>
Date: Fri Jun 3 14:43:51 2022 +0800
修复传送带模式中点击到达传送带底部的卡片没有透明度变化的bug
commit 9303304e94aabd4f955c3c186ff73525057bb133
Author: 星外之神 <wszqkzqk@qq.com>
Date: Fri Jun 3 14:21:47 2022 +0800
优化报纸僵尸设定
commit 0b9dbdc14513916dcb15dc717060faf380c1b681
Author: 星外之神 <wszqkzqk@qq.com>
Date: Fri Jun 3 14:17:26 2022 +0800
微调报纸僵尸速度
commit 44e6ce77e77fba19ebec22d08cdccf6d07246905
Author: 星外之神 <wszqkzqk@qq.com>
Date: Fri Jun 3 13:58:31 2022 +0800
清理
commit e00ed2f061ff148cbe0b38991a6ab585963ec9b3
Author: 星外之神 <wszqkzqk@qq.com>
Date: Fri Jun 3 13:58:05 2022 +0800
修复潜水僵尸只能攻击一次的bug
commit 790bc0bc5eeb206ebe769d91a4f339d050e86100
Author: 星外之神 <wszqkzqk@qq.com>
Date: Fri Jun 3 10:15:33 2022 +0800
更改构建命令
commit db86b3144ced0603c4db1e7220dbc47559b9dd87
Author: 星外之神 <wszqkzqk@qq.com>
Date: Thu Jun 2 19:03:13 2022 +0800
更改模块调用方式
commit 885f9902af79b2e23590956626e6eabe17e71931
Author: 星外之神 <wszqkzqk@qq.com>
Date: Wed Jun 1 14:15:50 2022 +0800
更新null值引用方式
commit d054ff498ba58356e9886064ae9c1a62fb0585a5
Author: 星外之神 <wszqkzqk@qq.com>
Date: Wed Jun 1 13:39:56 2022 +0800
进一步将常数性内容汇总到constants.py中
commit c4a756756b922cba8f8224b247bce7e7e7838808
Author: 星外之神 <wszqkzqk@qq.com>
Date: Fri May 27 15:23:52 2022 +0800
说明
1807 lines
68 KiB
Python
Executable File
1807 lines
68 KiB
Python
Executable File
import random
|
||
import pygame as pg
|
||
import os
|
||
from .. import tool
|
||
from .. import constants as c
|
||
|
||
|
||
class Car(pg.sprite.Sprite):
|
||
def __init__(self, x, y, map_y):
|
||
pg.sprite.Sprite.__init__(self)
|
||
|
||
rect = tool.GFX[c.CAR].get_rect()
|
||
width, height = rect.w, rect.h
|
||
self.image = tool.get_image(tool.GFX[c.CAR], 0, 0, width, height)
|
||
self.mask = pg.mask.from_surface(self.image)
|
||
self.rect = self.image.get_rect()
|
||
self.rect.x = x
|
||
self.rect.bottom = y
|
||
self.map_y = map_y
|
||
self.state = c.IDLE
|
||
self.dead = False
|
||
|
||
def update(self, game_info):
|
||
self.current_time = game_info[c.CURRENT_TIME]
|
||
if self.state == c.WALK:
|
||
self.rect.x += 4
|
||
if self.rect.x > c.SCREEN_WIDTH + 60:
|
||
self.dead = True
|
||
|
||
def setWalk(self):
|
||
if self.state == c.IDLE:
|
||
self.state = c.WALK
|
||
# 播放音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "carWalking.ogg")).play()
|
||
|
||
def draw(self, surface):
|
||
surface.blit(self.image, self.rect)
|
||
|
||
# 豌豆及孢子类普通子弹
|
||
class Bullet(pg.sprite.Sprite):
|
||
def __init__(self, x, start_y, dest_y, name, damage, effect=None, passedTorchWood=None):
|
||
pg.sprite.Sprite.__init__(self)
|
||
|
||
self.name = name
|
||
self.frames = []
|
||
self.frame_index = 0
|
||
self.load_images()
|
||
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.rect.x = x
|
||
self.rect.y = start_y
|
||
self.dest_y = dest_y
|
||
self.y_vel = 15 if (dest_y > start_y) else -15
|
||
self.x_vel = 10
|
||
self.damage = damage
|
||
self.effect = effect
|
||
self.state = c.FLY
|
||
self.current_time = 0
|
||
self.animate_timer = 0
|
||
self.animate_interval = 70
|
||
self.passedTorchWood = passedTorchWood # 记录最近通过的火炬树横坐标,如果没有缺省为None
|
||
|
||
def loadFrames(self, frames, name):
|
||
frame_list = tool.GFX[name]
|
||
if name in tool.PLANT_RECT:
|
||
data = tool.PLANT_RECT[name]
|
||
x, y, width, height = data['x'], data['y'], data['width'], data['height']
|
||
else:
|
||
x, y = 0, 0
|
||
rect = frame_list[0].get_rect()
|
||
width, height = rect.w, rect.h
|
||
|
||
for frame in frame_list:
|
||
frames.append(tool.get_image(frame, x, y, width, height))
|
||
|
||
def load_images(self):
|
||
self.fly_frames = []
|
||
self.explode_frames = []
|
||
|
||
fly_name = self.name
|
||
if self.name == c.BULLET_MUSHROOM:
|
||
explode_name = 'BulletMushRoomExplode'
|
||
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:
|
||
explode_name = 'PeaNormalExplode'
|
||
|
||
self.loadFrames(self.fly_frames, fly_name)
|
||
self.loadFrames(self.explode_frames, explode_name)
|
||
|
||
self.frames = self.fly_frames
|
||
|
||
def update(self, game_info):
|
||
self.current_time = game_info[c.CURRENT_TIME]
|
||
if self.state == c.FLY:
|
||
if self.rect.y != self.dest_y:
|
||
self.rect.y += self.y_vel
|
||
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:
|
||
self.kill()
|
||
elif self.state == c.EXPLODE:
|
||
if (self.current_time - self.explode_timer) > 250:
|
||
self.kill()
|
||
if self.current_time - self.animate_timer >= self.animate_interval:
|
||
self.frame_index += 1
|
||
self.animate_timer = self.current_time
|
||
if self.frame_index >= self.frame_num:
|
||
self.frame_index = 0
|
||
self.image = self.frames[self.frame_index]
|
||
|
||
def setExplode(self):
|
||
self.state = c.EXPLODE
|
||
self.explode_timer = self.current_time
|
||
self.frames = self.explode_frames
|
||
self.frame_num = len(self.frames)
|
||
self.image = self.frames[0]
|
||
self.mask = pg.mask.from_surface(self.image)
|
||
|
||
# 播放子弹爆炸音效
|
||
if self.name == c.BULLET_FIREBALL:
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "firepea.ogg")).play()
|
||
else:
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "bulletExplode.ogg")).play()
|
||
|
||
def draw(self, surface):
|
||
surface.blit(self.image, self.rect)
|
||
|
||
# 大喷菇的烟雾
|
||
# 仅有动画效果,不参与攻击运算
|
||
class Fume(pg.sprite.Sprite):
|
||
def __init__(self, x, y):
|
||
pg.sprite.Sprite.__init__(self)
|
||
self.name = c.FUME
|
||
self.timer = 0
|
||
self.frame_index = 0
|
||
self.load_images()
|
||
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.rect.x = x
|
||
self.rect.y = y
|
||
|
||
def load_images(self):
|
||
self.fly_frames = []
|
||
|
||
fly_name = self.name
|
||
|
||
self.loadFrames(self.fly_frames, fly_name)
|
||
|
||
self.frames = self.fly_frames
|
||
|
||
def draw(self, surface):
|
||
surface.blit(self.image, self.rect)
|
||
|
||
def update(self, game_info):
|
||
self.current_time = game_info[c.CURRENT_TIME]
|
||
if self.current_time - self.timer >= 100:
|
||
self.frame_index += 1
|
||
if self.frame_index >= self.frame_num:
|
||
self.frame_index = self.frame_num - 1
|
||
self.kill()
|
||
self.timer = self.current_time
|
||
self.image = self.frames[self.frame_index]
|
||
|
||
def loadFrames(self, frames, name):
|
||
frame_list = tool.GFX[name]
|
||
x, y = 0, 0
|
||
rect = frame_list[0].get_rect()
|
||
width, height = rect.w, rect.h
|
||
|
||
for frame in frame_list:
|
||
frames.append(tool.get_image(frame, x, y, width, height))
|
||
|
||
# 杨桃的子弹
|
||
class StarBullet(Bullet):
|
||
def __init__(self, x, start_y, damage, direction, level): # direction指星星飞行方向
|
||
Bullet.__init__(self, x, start_y, start_y, c.BULLET_STAR, damage)
|
||
|
||
self.level = level
|
||
_, self.map_y = self.level.map.getMapIndex(self.rect.x, self.rect.centery)
|
||
self.direction = direction
|
||
|
||
def update(self, game_info):
|
||
self.current_time = game_info[c.CURRENT_TIME]
|
||
if self.state == c.FLY:
|
||
if self.direction == c.STAR_FORWARD_UP:
|
||
self.rect.x += 8
|
||
self.rect.y -= 6
|
||
elif self.direction == c.STAR_FORWARD_DOWN:
|
||
self.rect.x += 7
|
||
self.rect.y += 7
|
||
elif self.direction == c.STAR_UPWARD:
|
||
# 实际上不知道为什么向上飞的看起来要快一些,所以拟合的时候把速度调小了一点
|
||
self.rect.y -= 9
|
||
elif self.direction == c.STAR_DOWNWARD:
|
||
self.rect.y += 10
|
||
else:
|
||
self.rect.x -= 10
|
||
self.handleMapYPosition()
|
||
if ((self.rect.x > c.SCREEN_WIDTH + 60) or (self.rect.x < -40)
|
||
or (self.rect.y > c.SCREEN_HEIGHT) or (self.rect.y < 0)):
|
||
self.kill()
|
||
elif self.state == c.EXPLODE:
|
||
if (self.current_time - self.explode_timer) > 250:
|
||
self.kill()
|
||
|
||
# 这里用的是坚果保龄球的代码改一下,实现子弹换行
|
||
def handleMapYPosition(self):
|
||
if self.direction == c.STAR_UPWARD:
|
||
_, map_y1 = self.level.map.getMapIndex(self.rect.x, self.rect.centery + 40)
|
||
else:
|
||
_, map_y1 = self.level.map.getMapIndex(self.rect.x, self.rect.centery + 20)
|
||
# _, map_y2 = self.level.map.getMapIndex(self.rect.x, self.rect.bottom +20)
|
||
if (self.map_y != map_y1) and (0 <= map_y1 <= self.level.map_y_len-1): # 换行
|
||
self.level.bullet_groups[self.map_y].remove(self)
|
||
self.level.bullet_groups[map_y1].add(self)
|
||
self.map_y = map_y1
|
||
|
||
|
||
class Plant(pg.sprite.Sprite):
|
||
def __init__(self, x, y, name, health, bullet_group, scale=1):
|
||
pg.sprite.Sprite.__init__(self)
|
||
|
||
self.frames = []
|
||
self.frame_index = 0
|
||
self.loadImages(name, scale)
|
||
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.rect.centerx = x
|
||
self.rect.bottom = y
|
||
|
||
self.name = name
|
||
self.health = health
|
||
self.state = c.IDLE
|
||
self.bullet_group = bullet_group
|
||
self.can_sleep = False
|
||
self.animate_timer = 0
|
||
self.animate_interval = 70 # 帧播放间隔
|
||
self.hit_timer = 0
|
||
# 被铲子指向时间
|
||
self.highlightTime = 0
|
||
|
||
def loadFrames(self, frames, name, scale, color=c.BLACK):
|
||
frame_list = tool.GFX[name]
|
||
if name in tool.PLANT_RECT:
|
||
data = tool.PLANT_RECT[name]
|
||
x, y, width, height = data['x'], data['y'], data['width'], data['height']
|
||
else:
|
||
x, y = 0, 0
|
||
rect = frame_list[0].get_rect()
|
||
width, height = rect.w, rect.h
|
||
|
||
for frame in frame_list:
|
||
frames.append(tool.get_image(frame, x, y, width, height, color, scale))
|
||
|
||
def loadImages(self, name, scale):
|
||
self.loadFrames(self.frames, name, scale)
|
||
|
||
def changeFrames(self, frames):
|
||
'''change image frames and modify rect position'''
|
||
self.frames = frames
|
||
self.frame_num = len(self.frames)
|
||
self.frame_index = 0
|
||
|
||
bottom = self.rect.bottom
|
||
x = self.rect.x
|
||
self.image = self.frames[self.frame_index]
|
||
self.mask = pg.mask.from_surface(self.image)
|
||
self.rect = self.image.get_rect()
|
||
self.rect.bottom = bottom
|
||
self.rect.x = x
|
||
|
||
def update(self, game_info):
|
||
self.current_time = game_info[c.CURRENT_TIME]
|
||
self.handleState()
|
||
self.animation()
|
||
|
||
def handleState(self):
|
||
if self.state == c.IDLE:
|
||
self.idling()
|
||
elif self.state == c.ATTACK:
|
||
self.attacking()
|
||
elif self.state == c.DIGEST:
|
||
self.digest()
|
||
|
||
def idling(self):
|
||
pass
|
||
|
||
def attacking(self):
|
||
pass
|
||
|
||
def digest(self):
|
||
pass
|
||
|
||
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
|
||
|
||
self.image = self.frames[self.frame_index]
|
||
self.mask = pg.mask.from_surface(self.image)
|
||
if (self.current_time - self.highlightTime < 100):
|
||
self.image.set_alpha(150)
|
||
elif ((self.current_time - self.hit_timer) < 200):
|
||
self.image.set_alpha(192)
|
||
else:
|
||
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
|
||
return False
|
||
|
||
def setAttack(self):
|
||
self.state = c.ATTACK
|
||
|
||
def setIdle(self):
|
||
self.state = c.IDLE
|
||
self.is_attacked = False
|
||
|
||
def setSleep(self):
|
||
self.state = c.SLEEP
|
||
self.changeFrames(self.sleep_frames)
|
||
|
||
def setDamage(self, damage, zombie):
|
||
if not zombie.lostHead:
|
||
self.health -= damage
|
||
self.hit_timer = self.current_time
|
||
if ((self.name == c.HYPNOSHROOM) and
|
||
(self.state != c.SLEEP) and
|
||
(zombie.name not in {c.ZOMBONI, "投石车僵尸(未实现)", "加刚特尔(未实现)"})):
|
||
self.zombie_to_hypno = zombie
|
||
|
||
def getPosition(self):
|
||
return self.rect.centerx, self.rect.bottom
|
||
|
||
|
||
class Sun(Plant):
|
||
def __init__(self, x, y, dest_x, dest_y, is_big=True):
|
||
if is_big:
|
||
scale = 0.9
|
||
self.sun_value = c.SUN_VALUE
|
||
else:
|
||
scale = 0.6
|
||
self.sun_value = 15
|
||
Plant.__init__(self, x, y, c.SUN, 0, None, scale)
|
||
self.move_speed = 1
|
||
self.dest_x = dest_x
|
||
self.dest_y = dest_y
|
||
self.die_timer = 0
|
||
|
||
def handleState(self):
|
||
if self.rect.centerx != self.dest_x:
|
||
self.rect.centerx += self.move_speed if self.rect.centerx < self.dest_x else -self.move_speed
|
||
if self.rect.bottom != self.dest_y:
|
||
self.rect.bottom += self.move_speed if self.rect.bottom < self.dest_y else -self.move_speed
|
||
|
||
if self.rect.centerx == self.dest_x and self.rect.bottom == self.dest_y:
|
||
if self.die_timer == 0:
|
||
self.die_timer = self.current_time
|
||
elif (self.current_time - self.die_timer) > c.SUN_LIVE_TIME:
|
||
self.state = c.DIE
|
||
self.kill()
|
||
|
||
def checkCollision(self, x, y):
|
||
if self.state == c.DIE:
|
||
return False
|
||
if (x >= self.rect.x and x <= self.rect.right and
|
||
y >= self.rect.y and y <= self.rect.bottom):
|
||
self.state = c.DIE
|
||
self.kill()
|
||
return True
|
||
return False
|
||
|
||
|
||
class SunFlower(Plant):
|
||
def __init__(self, x, y, sun_group):
|
||
Plant.__init__(self, x, y, c.SUNFLOWER, c.PLANT_HEALTH, None)
|
||
self.sun_timer = 0
|
||
self.sun_group = sun_group
|
||
|
||
def idling(self):
|
||
if self.sun_timer == 0:
|
||
self.sun_timer = self.current_time - (c.FLOWER_SUN_INTERVAL - 6000)
|
||
elif (self.current_time - self.sun_timer) > c.FLOWER_SUN_INTERVAL:
|
||
self.sun_group.add(
|
||
Sun(self.rect.centerx, self.rect.bottom, self.rect.right, self.rect.bottom + self.rect.h // 2))
|
||
self.sun_timer = self.current_time
|
||
|
||
|
||
class PeaShooter(Plant):
|
||
def __init__(self, x, y, bullet_group):
|
||
Plant.__init__(self, x, y, c.PEASHOOTER, c.PLANT_HEALTH, bullet_group)
|
||
self.shoot_timer = 0
|
||
|
||
def attacking(self):
|
||
if self.shoot_timer == 0:
|
||
self.shoot_timer = self.current_time - 700
|
||
elif (self.current_time - self.shoot_timer) >= 1400:
|
||
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
|
||
# 播放发射音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "shoot.ogg")).play()
|
||
|
||
def setAttack(self):
|
||
self.state = c.ATTACK
|
||
if self.shoot_timer != 0:
|
||
self.shoot_timer = self.current_time - 700
|
||
|
||
class RepeaterPea(Plant):
|
||
def __init__(self, x, y, bullet_group):
|
||
Plant.__init__(self, x, y, c.REPEATERPEA, c.PLANT_HEALTH, bullet_group)
|
||
self.shoot_timer = 0
|
||
|
||
# 是否发射第一颗
|
||
self.firstShot = 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.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
|
||
# 播放发射音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "shoot.ogg")).play()
|
||
elif self.firstShot and (self.current_time - self.shoot_timer) > 100:
|
||
self.firstShot = 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))
|
||
# 播放发射音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "shoot.ogg")).play()
|
||
|
||
def setAttack(self):
|
||
self.state = c.ATTACK
|
||
if self.shoot_timer != 0:
|
||
self.shoot_timer = self.current_time - 700
|
||
|
||
class ThreePeaShooter(Plant):
|
||
def __init__(self, x, y, bullet_groups, map_y, background_type):
|
||
Plant.__init__(self, x, y, c.THREEPEASHOOTER, c.PLANT_HEALTH, None)
|
||
self.shoot_timer = 0
|
||
self.map_y = map_y
|
||
self.bullet_groups = bullet_groups
|
||
self.background_type = background_type
|
||
|
||
def attacking(self):
|
||
if self.shoot_timer == 0:
|
||
self.shoot_timer = self.current_time - 700
|
||
if (self.current_time - self.shoot_timer) >= 1400:
|
||
offset_y = 9 # modify bullet in the same y position with bullets of other plants
|
||
for i in range(3):
|
||
tmp_y = self.map_y + (i - 1)
|
||
if self.background_type in c.POOL_EQUIPPED_BACKGROUNDS:
|
||
if tmp_y < 0 or tmp_y >= c.GRID_POOL_Y_LEN:
|
||
continue
|
||
else:
|
||
if tmp_y < 0 or tmp_y >= c.GRID_Y_LEN:
|
||
continue
|
||
if self.background_type in {c.BACKGROUND_POOL, c.BACKGROUND_FOG, c.BACKGROUND_ROOF, c.BACKGROUND_ROOFNIGHT}:
|
||
dest_y = self.rect.y + (i - 1) * c.GRID_POOL_Y_SIZE + offset_y
|
||
else:
|
||
dest_y = self.rect.y + (i - 1) * c.GRID_Y_SIZE + offset_y
|
||
self.bullet_groups[tmp_y].add(Bullet(self.rect.right - 15, self.rect.y, dest_y,
|
||
c.BULLET_PEA, c.BULLET_DAMAGE_NORMAL, effect=None))
|
||
self.shoot_timer = self.current_time
|
||
# 播放发射音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "shoot.ogg")).play()
|
||
|
||
def setAttack(self):
|
||
self.state = c.ATTACK
|
||
if self.shoot_timer != 0:
|
||
self.shoot_timer = self.current_time - 700
|
||
|
||
class SnowPeaShooter(Plant):
|
||
def __init__(self, x, y, bullet_group):
|
||
Plant.__init__(self, x, y, c.SNOWPEASHOOTER, c.PLANT_HEALTH, bullet_group)
|
||
self.shoot_timer = 0
|
||
|
||
def attacking(self):
|
||
if self.shoot_timer == 0:
|
||
self.shoot_timer = self.current_time - 700
|
||
elif (self.current_time - self.shoot_timer) >= 1400:
|
||
self.bullet_group.add(Bullet(self.rect.right - 15, self.rect.y, self.rect.y,
|
||
c.BULLET_PEA_ICE, c.BULLET_DAMAGE_NORMAL, effect=c.BULLET_EFFECT_ICE))
|
||
self.shoot_timer = self.current_time
|
||
# 播放发射音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "shoot.ogg")).play()
|
||
# 播放冰子弹音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "snowPeaSparkles.ogg")).play()
|
||
|
||
def setAttack(self):
|
||
self.state = c.ATTACK
|
||
if self.shoot_timer != 0:
|
||
self.shoot_timer = self.current_time - 700
|
||
|
||
class WallNut(Plant):
|
||
def __init__(self, x, y):
|
||
Plant.__init__(self, x, y, c.WALLNUT, c.WALLNUT_HEALTH, None)
|
||
self.load_images()
|
||
self.cracked1 = False
|
||
self.cracked2 = False
|
||
|
||
def load_images(self):
|
||
self.cracked1_frames = []
|
||
self.cracked2_frames = []
|
||
|
||
cracked1_frames_name = self.name + '_cracked1'
|
||
cracked2_frames_name = self.name + '_cracked2'
|
||
|
||
self.loadFrames(self.cracked1_frames, cracked1_frames_name, 1)
|
||
self.loadFrames(self.cracked2_frames, cracked2_frames_name, 1)
|
||
|
||
def idling(self):
|
||
if (not self.cracked1) and self.health <= c.WALLNUT_CRACKED1_HEALTH:
|
||
self.changeFrames(self.cracked1_frames)
|
||
self.cracked1 = True
|
||
elif (not self.cracked2) and self.health <= c.WALLNUT_CRACKED2_HEALTH:
|
||
self.changeFrames(self.cracked2_frames)
|
||
self.cracked2 = True
|
||
|
||
|
||
class CherryBomb(Plant):
|
||
def __init__(self, x, y):
|
||
Plant.__init__(self, x, y, c.CHERRYBOMB, c.INF, None)
|
||
self.state = c.ATTACK
|
||
self.start_boom = False
|
||
self.boomed = False
|
||
self.bomb_timer = 0
|
||
self.explode_y_range = 1
|
||
self.explode_x_range = c.GRID_X_SIZE * 1.5
|
||
|
||
def setBoom(self):
|
||
frame = tool.GFX[c.CHERRY_BOOM_IMAGE]
|
||
rect = frame.get_rect()
|
||
width, height = rect.w, rect.h
|
||
|
||
old_rect = self.rect
|
||
image = tool.get_image(frame, 0, 0, width, height, c.BLACK, 1)
|
||
self.image = image
|
||
self.mask = pg.mask.from_surface(self.image)
|
||
self.rect = image.get_rect()
|
||
self.rect.centerx = old_rect.centerx
|
||
self.rect.centery = old_rect.centery
|
||
self.start_boom = True
|
||
|
||
def animation(self):
|
||
if self.start_boom:
|
||
if self.bomb_timer == 0:
|
||
self.bomb_timer = self.current_time
|
||
# 播放爆炸音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "bomb.ogg")).play()
|
||
elif (self.current_time - self.bomb_timer) > 500:
|
||
self.health = 0
|
||
else:
|
||
if (self.current_time - self.animate_timer) > 100:
|
||
self.frame_index += 1
|
||
if self.frame_index >= self.frame_num:
|
||
self.setBoom()
|
||
return
|
||
self.animate_timer = self.current_time
|
||
|
||
self.image = self.frames[self.frame_index]
|
||
self.mask = pg.mask.from_surface(self.image)
|
||
|
||
if (self.current_time - self.highlightTime < 100):
|
||
self.image.set_alpha(150)
|
||
elif ((self.current_time - self.hit_timer) < 200):
|
||
self.image.set_alpha(192)
|
||
else:
|
||
self.image.set_alpha(255)
|
||
|
||
|
||
class Chomper(Plant):
|
||
def __init__(self, x, y):
|
||
Plant.__init__(self, x, y, c.CHOMPER, c.PLANT_HEALTH, None)
|
||
self.animate_interval = 140
|
||
self.digest_timer = 0
|
||
self.digest_interval = 15000
|
||
self.attack_zombie = None
|
||
self.zombie_group = None
|
||
self.shouldDiggest = False
|
||
|
||
def loadImages(self, name, scale):
|
||
self.idle_frames = []
|
||
self.attack_frames = []
|
||
self.digest_frames = []
|
||
self.animate_interval = 100 # 本身动画播放较慢
|
||
|
||
idle_name = name
|
||
attack_name = name + 'Attack'
|
||
digest_name = name + 'Digest'
|
||
|
||
frame_list = [self.idle_frames, self.attack_frames, self.digest_frames]
|
||
name_list = [idle_name, attack_name, digest_name]
|
||
scale_list = [1, 1, 1]
|
||
rect_list = [(0, 0, 100, 114), None, None]
|
||
|
||
for i, name in enumerate(name_list):
|
||
self.loadFrames(frame_list[i], name, scale_list[i])
|
||
|
||
self.frames = self.idle_frames
|
||
|
||
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)):
|
||
return True
|
||
return False
|
||
|
||
def setIdle(self):
|
||
self.state = c.IDLE
|
||
self.changeFrames(self.idle_frames)
|
||
|
||
def setAttack(self, zombie, zombie_group):
|
||
self.attack_zombie = zombie
|
||
self.zombie_group = zombie_group
|
||
self.state = c.ATTACK
|
||
self.changeFrames(self.attack_frames)
|
||
|
||
def setDigest(self):
|
||
self.state = c.DIGEST
|
||
self.changeFrames(self.digest_frames)
|
||
|
||
def attacking(self):
|
||
if self.frame_index == (self.frame_num - 3):
|
||
# 播放吞的音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "bigchomp.ogg")).play()
|
||
if self.attack_zombie.alive():
|
||
self.shouldDiggest = True
|
||
self.attack_zombie.kill()
|
||
if (self.frame_index + 1) == self.frame_num:
|
||
if self.shouldDiggest:
|
||
self.setDigest()
|
||
self.shouldDiggest = False
|
||
else:
|
||
self.setIdle()
|
||
|
||
def digest(self):
|
||
if self.digest_timer == 0:
|
||
self.digest_timer = self.current_time
|
||
elif (self.current_time - self.digest_timer) > self.digest_interval:
|
||
self.digest_timer = 0
|
||
self.setIdle()
|
||
|
||
|
||
class PuffShroom(Plant):
|
||
def __init__(self, x, y, bullet_group):
|
||
Plant.__init__(self, x, y, c.PUFFSHROOM, c.PLANT_HEALTH, bullet_group)
|
||
self.can_sleep = True
|
||
self.shoot_timer = 0
|
||
|
||
def loadImages(self, name, scale):
|
||
self.idle_frames = []
|
||
self.sleep_frames = []
|
||
|
||
idle_name = name
|
||
sleep_name = name + 'Sleep'
|
||
|
||
frame_list = [self.idle_frames, self.sleep_frames]
|
||
name_list = [idle_name, sleep_name]
|
||
|
||
for i, name in enumerate(name_list):
|
||
self.loadFrames(frame_list[i], name, 1)
|
||
|
||
self.frames = self.idle_frames
|
||
|
||
def attacking(self):
|
||
if self.shoot_timer == 0:
|
||
self.shoot_timer = self.current_time - 700
|
||
elif (self.current_time - self.shoot_timer) >= 1400:
|
||
self.bullet_group.add(Bullet(self.rect.right, self.rect.y + 10, self.rect.y + 10,
|
||
c.BULLET_MUSHROOM, c.BULLET_DAMAGE_NORMAL, effect=None))
|
||
self.shoot_timer = self.current_time
|
||
# 播放音效
|
||
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
|
||
return False
|
||
|
||
def setAttack(self):
|
||
self.state = c.ATTACK
|
||
if self.shoot_timer != 0:
|
||
self.shoot_timer = self.current_time - 700
|
||
|
||
|
||
class PotatoMine(Plant):
|
||
def __init__(self, x, y):
|
||
Plant.__init__(self, x, y, c.POTATOMINE, c.PLANT_HEALTH, None)
|
||
self.animate_interval = 300
|
||
self.is_init = True
|
||
self.init_timer = 0
|
||
self.bomb_timer = 0
|
||
self.explode_x_range = c.GRID_X_SIZE / 2
|
||
self.start_boom = False
|
||
self.boomed = False
|
||
|
||
def loadImages(self, name, scale):
|
||
self.init_frames = []
|
||
self.idle_frames = []
|
||
self.explode_frames = []
|
||
|
||
init_name = name + 'Init'
|
||
idle_name = name
|
||
explode_name = name + 'Explode'
|
||
|
||
frame_list = [self.init_frames, self.idle_frames, self.explode_frames]
|
||
name_list = [init_name, idle_name, explode_name]
|
||
|
||
for i, name in enumerate(name_list):
|
||
self.loadFrames(frame_list[i], name, 1, c.WHITE)
|
||
|
||
self.frames = self.init_frames
|
||
|
||
def idling(self):
|
||
if self.is_init:
|
||
if self.init_timer == 0:
|
||
self.init_timer = self.current_time
|
||
elif (self.current_time - self.init_timer) > 15000:
|
||
self.changeFrames(self.idle_frames)
|
||
self.is_init = False
|
||
|
||
def canAttack(self, zombie): # 土豆雷不可能遇上潜水僵尸
|
||
if (zombie.name == c.POLE_VAULTING_ZOMBIE and (not zombie.jumped)):
|
||
return False
|
||
# 这里碰撞应当比碰撞一般更容易,就设置成圆形或矩形模式,不宜采用mask
|
||
elif (pg.sprite.collide_circle_ratio(0.7)(zombie, self) and
|
||
(not self.is_init) and (not zombie.lostHead)):
|
||
return True
|
||
return False
|
||
|
||
def attacking(self):
|
||
if self.bomb_timer == 0:
|
||
self.bomb_timer = self.current_time
|
||
# 播放音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "potatomine.ogg")).play()
|
||
self.changeFrames(self.explode_frames)
|
||
self.start_boom = True
|
||
elif (self.current_time - self.bomb_timer) > 500:
|
||
self.health = 0
|
||
|
||
|
||
class Squash(Plant):
|
||
def __init__(self, x, y, mapPlantsSet):
|
||
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
|
||
|
||
def loadImages(self, name, scale):
|
||
self.idle_frames = []
|
||
self.aim_frames = []
|
||
self.attack_frames = []
|
||
|
||
idle_name = name
|
||
aim_name = name + 'Aim'
|
||
attack_name = name + 'Attack'
|
||
|
||
frame_list = [self.idle_frames, self.aim_frames, self.attack_frames]
|
||
name_list = [idle_name, aim_name, attack_name]
|
||
|
||
for i, name in enumerate(name_list):
|
||
self.loadFrames(frame_list[i], name, 1, c.WHITE)
|
||
|
||
self.frames = self.idle_frames
|
||
|
||
def canAttack(self, zombie):
|
||
# 普通状态
|
||
if (self.state == c.IDLE and self.rect.x <= zombie.rect.right and
|
||
(self.rect.right + c.GRID_X_SIZE >= zombie.rect.x)):
|
||
return True
|
||
# 攻击状态
|
||
elif (self.state == c.ATTACK):
|
||
if pg.sprite.collide_rect_ratio(0.5)(zombie, self) or pg.sprite.collide_mask(zombie, self):
|
||
return True
|
||
return False
|
||
|
||
def setAttack(self, zombie, zombie_group):
|
||
self.attack_zombie = zombie
|
||
self.zombie_group = zombie_group
|
||
self.state = c.ATTACK
|
||
# 攻击状态下生命值无敌
|
||
self.health = c.INF
|
||
|
||
def attacking(self):
|
||
if self.start_boom:
|
||
if (self.frame_index + 1) == self.frame_num:
|
||
for zombie in self.zombie_group:
|
||
if self.canAttack(zombie):
|
||
zombie.setDamage(1800, damageType=c.ZOMBIE_RANGE_DAMAGE)
|
||
self.health = 0 # 避免僵尸在原位啃食
|
||
self.mapPlantsSet.remove(c.SQUASH)
|
||
self.kill()
|
||
# 播放碾压音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "squashing.ogg")).play()
|
||
elif self.aim_timer == 0:
|
||
# 锁定目标时播放音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "squashHmm.ogg")).play()
|
||
self.aim_timer = self.current_time
|
||
self.changeFrames(self.aim_frames)
|
||
elif (self.current_time - self.aim_timer) > 1000:
|
||
self.changeFrames(self.attack_frames)
|
||
self.rect.centerx = self.attack_zombie.rect.centerx
|
||
self.start_boom = True
|
||
self.animate_interval = 300
|
||
|
||
def getPosition(self):
|
||
return self.orig_pos
|
||
|
||
|
||
class Spikeweed(Plant):
|
||
def __init__(self, x, y):
|
||
Plant.__init__(self, x, y, c.SPIKEWEED, c.PLANT_HEALTH, None)
|
||
self.animate_interval = 70
|
||
self.attack_timer = 0
|
||
|
||
def loadImages(self, name, scale):
|
||
self.loadFrames(self.frames, name, 0.9, c.WHITE)
|
||
|
||
def setIdle(self):
|
||
self.animate_interval = 70
|
||
self.state = c.IDLE
|
||
|
||
def canAttack(self, zombie):
|
||
# 地刺能不能扎的判据:僵尸中心与地刺中心的距离或僵尸包括了地刺中心和右端(平衡得到合理的攻击范围,"僵尸包括了地刺中心和右端"是为以后巨人做准备)
|
||
# 暂时不能用碰撞判断,平衡性不好
|
||
if ((-45 <= zombie.rect.x - self.rect.x <= 30) or (zombie.rect.left <= self.rect.x <= zombie.rect.right and zombie.rect.left <= self.rect.right <= zombie.rect.right)):
|
||
return True
|
||
return False
|
||
|
||
def setAttack(self, zombie_group):
|
||
self.zombie_group = zombie_group
|
||
self.animate_interval = 35
|
||
self.state = c.ATTACK
|
||
if self.hit_timer != 0:
|
||
self.hit_timer = self.current_time - 500
|
||
|
||
def attacking(self):
|
||
if self.hit_timer == 0:
|
||
self.hit_timer = self.current_time - 500
|
||
elif (self.current_time - self.attack_timer) >= 700:
|
||
self.attack_timer = self.current_time
|
||
# 最后再来判断攻击是否要杀死自己
|
||
killSelf = False
|
||
for zombie in self.zombie_group:
|
||
if self.canAttack(zombie):
|
||
# 有车的僵尸
|
||
if zombie.name in {c.ZOMBONI}:
|
||
zombie.health = zombie.lostHeadHealth
|
||
killSelf = True
|
||
else:
|
||
zombie.setDamage(20, damageType=c.ZOMBIE_COMMON_DAMAGE)
|
||
if killSelf:
|
||
self.health = 0
|
||
# 播放攻击音效,同子弹打击
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "bulletExplode.ogg")).play()
|
||
|
||
|
||
class Jalapeno(Plant):
|
||
def __init__(self, x, y):
|
||
Plant.__init__(self, x, y, c.JALAPENO, c.INF, None)
|
||
self.orig_pos = (x, y)
|
||
self.state = c.ATTACK
|
||
self.start_boom = False
|
||
self.boomed = False
|
||
self.explode_y_range = 0
|
||
self.explode_x_range = 500
|
||
|
||
def loadImages(self, name, scale):
|
||
self.explode_frames = []
|
||
explode_name = name + 'Explode'
|
||
self.loadFrames(self.explode_frames, explode_name, 1, c.WHITE)
|
||
|
||
self.loadFrames(self.frames, name, 1, c.WHITE)
|
||
|
||
def setExplode(self):
|
||
self.changeFrames(self.explode_frames)
|
||
self.animate_timer = self.current_time
|
||
self.rect.x = c.MAP_OFFSET_X
|
||
self.start_boom = True
|
||
|
||
def animation(self):
|
||
if self.start_boom:
|
||
if (self.current_time - self.animate_timer) > 100:
|
||
if self.frame_index == 1:
|
||
# 播放爆炸音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "bomb.ogg")).play()
|
||
self.frame_index += 1
|
||
if self.frame_index >= self.frame_num:
|
||
self.health = 0
|
||
return
|
||
self.animate_timer = self.current_time
|
||
else:
|
||
if (self.current_time - self.animate_timer) > 100:
|
||
self.frame_index += 1
|
||
if self.frame_index >= self.frame_num:
|
||
self.setExplode()
|
||
return
|
||
self.animate_timer = self.current_time
|
||
self.image = self.frames[self.frame_index]
|
||
self.mask = pg.mask.from_surface(self.image)
|
||
|
||
if (self.current_time - self.highlightTime < 100):
|
||
self.image.set_alpha(150)
|
||
elif ((self.current_time - self.hit_timer) < 200):
|
||
self.image.set_alpha(192)
|
||
else:
|
||
self.image.set_alpha(255)
|
||
|
||
def getPosition(self):
|
||
return self.orig_pos
|
||
|
||
|
||
class ScaredyShroom(Plant):
|
||
def __init__(self, x, y, bullet_group):
|
||
Plant.__init__(self, x, y, c.SCAREDYSHROOM, c.PLANT_HEALTH, bullet_group)
|
||
self.can_sleep = True
|
||
self.shoot_timer = 0
|
||
self.cry_x_range = c.GRID_X_SIZE * 1.5
|
||
|
||
def loadImages(self, name, scale):
|
||
self.idle_frames = []
|
||
self.cry_frames = []
|
||
self.sleep_frames = []
|
||
|
||
idle_name = name
|
||
cry_name = name + 'Cry'
|
||
sleep_name = name + 'Sleep'
|
||
|
||
frame_list = [self.idle_frames, self.cry_frames, self.sleep_frames]
|
||
name_list = [idle_name, cry_name, sleep_name]
|
||
|
||
for i, name in enumerate(name_list):
|
||
self.loadFrames(frame_list[i], name, 1, c.WHITE)
|
||
|
||
self.frames = self.idle_frames
|
||
|
||
def needCry(self, zombie):
|
||
if (zombie.state != c.DIE and abs(self.rect.x - zombie.rect.x) < self.cry_x_range):
|
||
return True
|
||
return False
|
||
|
||
def setCry(self):
|
||
self.state = c.CRY
|
||
self.changeFrames(self.cry_frames)
|
||
|
||
def setAttack(self):
|
||
self.state = c.ATTACK
|
||
self.changeFrames(self.idle_frames)
|
||
if self.shoot_timer != 0:
|
||
self.shoot_timer = self.current_time - 700
|
||
|
||
def setIdle(self):
|
||
self.state = c.IDLE
|
||
self.changeFrames(self.idle_frames)
|
||
|
||
def attacking(self):
|
||
if self.shoot_timer == 0:
|
||
self.shoot_timer = self.current_time - 700
|
||
elif (self.current_time - self.shoot_timer) >= 1400:
|
||
self.bullet_group.add(Bullet(self.rect.right - 15, self.rect.y + 40, self.rect.y + 40,
|
||
c.BULLET_MUSHROOM, c.BULLET_DAMAGE_NORMAL, effect=None))
|
||
self.shoot_timer = self.current_time
|
||
# 播放音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "puff.ogg")).play()
|
||
|
||
|
||
class SunShroom(Plant):
|
||
def __init__(self, x, y, sun_group):
|
||
Plant.__init__(self, x, y, c.SUNSHROOM, c.PLANT_HEALTH, None)
|
||
self.can_sleep = True
|
||
self.animate_interval = 140
|
||
self.sun_timer = 0
|
||
self.sun_group = sun_group
|
||
self.is_big = False
|
||
self.change_timer = 0
|
||
|
||
def loadImages(self, name, scale):
|
||
self.idle_frames = []
|
||
self.big_frames = []
|
||
self.sleep_frames = []
|
||
|
||
idle_name = name
|
||
big_name = name + 'Big'
|
||
sleep_name = name + 'Sleep'
|
||
|
||
frame_list = [self.idle_frames, self.big_frames, self.sleep_frames]
|
||
name_list = [idle_name, big_name, sleep_name]
|
||
|
||
for i, name in enumerate(name_list):
|
||
self.loadFrames(frame_list[i], name, 1, c.WHITE)
|
||
|
||
self.frames = self.idle_frames
|
||
|
||
def idling(self):
|
||
if not self.is_big:
|
||
if self.change_timer == 0:
|
||
self.change_timer = self.current_time
|
||
elif (self.current_time - self.change_timer) > 100000:
|
||
self.changeFrames(self.big_frames)
|
||
self.is_big = True
|
||
# 播放长大音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "plantGrow.ogg")).play()
|
||
if self.sun_timer == 0:
|
||
self.sun_timer = self.current_time - (c.FLOWER_SUN_INTERVAL - 6000)
|
||
elif (self.current_time - self.sun_timer) > c.FLOWER_SUN_INTERVAL:
|
||
self.sun_group.add(Sun(self.rect.centerx, self.rect.bottom, self.rect.right,
|
||
self.rect.bottom + self.rect.h // 2, self.is_big))
|
||
self.sun_timer = self.current_time
|
||
|
||
|
||
class IceShroom(Plant):
|
||
def __init__(self, x, y):
|
||
Plant.__init__(self, x, y, c.ICESHROOM, c.PLANT_HEALTH, None)
|
||
self.can_sleep = True
|
||
self.orig_pos = (x, y)
|
||
self.start_boom = False
|
||
self.boomed = False
|
||
|
||
def loadImages(self, name, scale):
|
||
self.idle_frames = []
|
||
self.snow_frames = []
|
||
self.sleep_frames = []
|
||
self.trap_frames = []
|
||
|
||
idle_name = name
|
||
snow_name = name + 'Snow'
|
||
sleep_name = name + 'Sleep'
|
||
trap_name = name + 'Trap'
|
||
|
||
frame_list = [self.idle_frames, self.snow_frames, self.sleep_frames, self.trap_frames]
|
||
name_list = [idle_name, snow_name, sleep_name, trap_name]
|
||
scale_list = [1, 1.5, 1, 1]
|
||
|
||
for i, name in enumerate(name_list):
|
||
self.loadFrames(frame_list[i], name, scale_list[i], c.WHITE)
|
||
|
||
self.frames = self.idle_frames
|
||
|
||
def setFreeze(self):
|
||
self.changeFrames(self.snow_frames)
|
||
self.animate_timer = self.current_time
|
||
self.rect.x = c.MAP_OFFSET_X
|
||
self.rect.y = c.MAP_OFFSET_Y
|
||
self.start_boom = True
|
||
|
||
def animation(self):
|
||
if self.start_boom:
|
||
if (self.current_time - self.animate_timer) > 500:
|
||
self.frame_index += 1
|
||
if self.frame_index >= self.frame_num:
|
||
self.health = 0
|
||
return
|
||
self.animate_timer = self.current_time
|
||
else:
|
||
if self.state != c.SLEEP:
|
||
self.health = c.INF
|
||
if (self.current_time - self.animate_timer) > 100:
|
||
self.frame_index += 1
|
||
if self.frame_index >= self.frame_num:
|
||
if self.state == c.SLEEP:
|
||
self.frame_index = 0
|
||
else:
|
||
self.setFreeze()
|
||
return
|
||
self.animate_timer = self.current_time
|
||
self.image = self.frames[self.frame_index]
|
||
self.mask = pg.mask.from_surface(self.image)
|
||
|
||
if (self.current_time - self.highlightTime < 100):
|
||
self.image.set_alpha(150)
|
||
elif ((self.current_time - self.hit_timer) < 200):
|
||
self.image.set_alpha(192)
|
||
else:
|
||
self.image.set_alpha(255)
|
||
|
||
def getPosition(self):
|
||
return self.orig_pos
|
||
|
||
|
||
class HypnoShroom(Plant):
|
||
def __init__(self, x, y):
|
||
Plant.__init__(self, x, y, c.HYPNOSHROOM, c.PLANT_HEALTH, None)
|
||
self.can_sleep = True
|
||
self.animate_interval = 80
|
||
self.zombie_to_hypno = None
|
||
|
||
def loadImages(self, name, scale):
|
||
self.idle_frames = []
|
||
self.sleep_frames = []
|
||
|
||
idle_name = name
|
||
sleep_name = name + 'Sleep'
|
||
|
||
frame_list = [self.idle_frames, self.sleep_frames]
|
||
name_list = [idle_name, sleep_name]
|
||
|
||
for i, name in enumerate(name_list):
|
||
self.loadFrames(frame_list[i], name, 1, c.WHITE)
|
||
|
||
self.frames = self.idle_frames
|
||
|
||
def idling(self):
|
||
if self.health < c.PLANT_HEALTH and self.zombie_to_hypno:
|
||
self.health = 0
|
||
|
||
|
||
class WallNutBowling(Plant):
|
||
def __init__(self, x, y, map_y, level):
|
||
Plant.__init__(self, x, y, c.WALLNUTBOWLING, 1, None)
|
||
self.map_y = map_y
|
||
self.level = level
|
||
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)
|
||
self.vel_y = 0
|
||
self.disable_hit_y = -1
|
||
|
||
def loadImages(self, name, scale):
|
||
self.loadFrames(self.frames, name, 1, c.WHITE)
|
||
|
||
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
|
||
self.init_rect.y += self.vel_y
|
||
self.handleMapYPosition()
|
||
if self.shouldChangeDirection():
|
||
self.changeDirection(-1)
|
||
if self.init_rect.x > c.SCREEN_WIDTH:
|
||
self.health = 0
|
||
self.move_timer += self.move_interval
|
||
|
||
def canHit(self, map_y):
|
||
if self.disable_hit_y == map_y:
|
||
return False
|
||
return True
|
||
|
||
def handleMapYPosition(self):
|
||
_, map_y1 = self.level.map.getMapIndex(self.init_rect.x, self.init_rect.centery)
|
||
_, map_y2 = self.level.map.getMapIndex(self.init_rect.x, self.init_rect.bottom)
|
||
if self.map_y != map_y1 and map_y1 == map_y2:
|
||
# wallnut bowls to another row, should modify which plant group it belongs to
|
||
self.level.plant_groups[self.map_y].remove(self)
|
||
self.level.plant_groups[map_y1].add(self)
|
||
self.map_y = map_y1
|
||
|
||
def shouldChangeDirection(self):
|
||
if self.init_rect.centery <= c.MAP_OFFSET_Y:
|
||
return True
|
||
elif self.init_rect.bottom + 20 >= c.SCREEN_HEIGHT:
|
||
return True
|
||
return False
|
||
|
||
def changeDirection(self, map_y):
|
||
if self.vel_y == 0:
|
||
if self.map_y == 0:
|
||
self.vel_y = self.vel_x
|
||
elif self.map_y == (c.GRID_Y_LEN - 1): # 坚果保龄球显然没有泳池的6行情形
|
||
self.vel_y = -self.vel_x
|
||
else:
|
||
if random.randint(0, 1):
|
||
self.vel_y = self.vel_x
|
||
else:
|
||
self.vel_y = -self.vel_x
|
||
else:
|
||
self.vel_y = -self.vel_y
|
||
|
||
self.disable_hit_y = map_y
|
||
|
||
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]
|
||
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)
|
||
|
||
|
||
class RedWallNutBowling(Plant):
|
||
def __init__(self, x, y):
|
||
Plant.__init__(self, x, y, c.REDWALLNUTBOWLING, 1, None)
|
||
self.orig_y = y
|
||
self.explode_timer = 0
|
||
self.explode_y_range = 1
|
||
self.explode_x_range = c.GRID_X_SIZE * 1.5
|
||
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)
|
||
self.start_boom = False
|
||
self.boomed = False
|
||
|
||
def loadImages(self, name, scale):
|
||
self.idle_frames = []
|
||
self.explode_frames = []
|
||
|
||
idle_name = name
|
||
explode_name = name + 'Explode'
|
||
|
||
frame_list = [self.idle_frames, self.explode_frames]
|
||
name_list = [idle_name, explode_name]
|
||
|
||
for i, name in enumerate(name_list):
|
||
self.loadFrames(frame_list[i], name, 1, c.WHITE)
|
||
|
||
self.frames = self.idle_frames
|
||
|
||
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 attacking(self):
|
||
if self.explode_timer == 0:
|
||
self.start_boom = True
|
||
self.explode_timer = self.current_time
|
||
self.changeFrames(self.explode_frames)
|
||
# 播放爆炸音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "bomb.ogg")).play()
|
||
elif (self.current_time - self.explode_timer) > 500:
|
||
self.health = 0
|
||
|
||
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]
|
||
if self.state == c.IDLE:
|
||
self.image = pg.transform.rotate(image, self.rotate_degree)
|
||
else:
|
||
self.image = image
|
||
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)
|
||
|
||
def getPosition(self):
|
||
return (self.rect.centerx, self.orig_y)
|
||
|
||
class LilyPad(Plant):
|
||
def __init__(self, x, y):
|
||
Plant.__init__(self, x, y, c.LILYPAD, c.PLANT_HEALTH, None)
|
||
|
||
class TorchWood(Plant):
|
||
def __init__(self, x, y, bullet_group):
|
||
Plant.__init__(self, x, y, c.TORCHWOOD, c.PLANT_HEALTH, bullet_group)
|
||
|
||
def idling(self):
|
||
for i in self.bullet_group:
|
||
if i.name == c.BULLET_PEA:
|
||
if i.passedTorchWood != 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))
|
||
i.kill()
|
||
elif i.name == c.BULLET_PEA_ICE:
|
||
if i.passedTorchWood != 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))
|
||
i.kill()
|
||
|
||
class StarFruit(Plant):
|
||
def __init__(self, x, y, bullet_group, level):
|
||
Plant.__init__(self, x, y, c.STARFRUIT, c.PLANT_HEALTH, bullet_group)
|
||
self.shoot_timer = 0
|
||
self.level = level
|
||
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): # 对于同行且在杨桃后的僵尸
|
||
return True
|
||
# 斜向上,理想直线方程为:f(zombie.rect.x) = -0.75*(zombie.rect.x - (self.rect.right - 5)) + self.rect.y - 10
|
||
# 注意实际上为射线
|
||
elif -100 <= (zombie.rect.y - (-0.75*(zombie.rect.x - (self.rect.right - 5)) + self.rect.y - 10)) <= 70 and (zombie.rect.left <= c.SCREEN_WIDTH) and (zombie.rect.x >= self.rect.x):
|
||
return True
|
||
# 斜向下,理想直线方程为:f(zombie.rect.x) = zombie.rect.x + self.rect.y - self.rect.right - 15
|
||
# 注意实际上为射线
|
||
elif abs(zombie.rect.y - (zombie.rect.x + self.rect.y - self.rect.right - 15)) <= 70 and (zombie.rect.left <= c.SCREEN_WIDTH) and (zombie.rect.x >= self.rect.x):
|
||
return True
|
||
elif zombie.rect.left <= self.rect.x <= zombie.rect.right:
|
||
return True
|
||
return 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.bullet_group.add(StarBullet(self.rect.left - 10, self.rect.y + 15, c.BULLET_DAMAGE_NORMAL, c.STAR_BACKWARD, 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.right - 5, self.rect.bottom - 20, c.BULLET_DAMAGE_NORMAL, c.STAR_FORWARD_DOWN, self.level))
|
||
self.bullet_group.add(StarBullet(self.rect.right - 5, self.rect.y - 10, c.BULLET_DAMAGE_NORMAL, c.STAR_FORWARD_UP, self.level))
|
||
self.shoot_timer = self.current_time
|
||
# 播放发射音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "shoot.ogg")).play()
|
||
|
||
def setAttack(self):
|
||
self.state = c.ATTACK
|
||
if self.shoot_timer != 0:
|
||
self.shoot_timer = self.current_time - 700
|
||
|
||
|
||
class CoffeeBean(Plant):
|
||
def __init__(self, x, y, plant_group, mapContent, map, map_x):
|
||
Plant.__init__(self, x, y, c.COFFEEBEAN, c.PLANT_HEALTH, None)
|
||
self.plant_group = plant_group
|
||
self.mapContent = mapContent
|
||
self.map = map
|
||
self.map_x = map_x
|
||
|
||
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.mapContent[c.MAP_SLEEP] = False
|
||
for plant in self.plant_group:
|
||
if plant.can_sleep:
|
||
if plant.state == c.SLEEP:
|
||
plantMapX, _ = self.map.getMapIndex(plant.rect.centerx, plant.rect.bottom)
|
||
if plantMapX == self.map_x:
|
||
plant.state = c.IDLE
|
||
plant.setIdle()
|
||
plant.changeFrames(plant.idle_frames)
|
||
# 播放唤醒音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "mushroomWakeup.ogg")).play()
|
||
self.mapContent[c.MAP_PLANT].remove(self.name)
|
||
self.kill()
|
||
self.frame_index = self.frame_num - 1
|
||
|
||
self.animate_timer = self.current_time
|
||
|
||
self.image = self.frames[self.frame_index]
|
||
self.mask = pg.mask.from_surface(self.image)
|
||
if (self.current_time - self.highlightTime < 100):
|
||
self.image.set_alpha(150)
|
||
elif ((self.current_time - self.hit_timer) < 200):
|
||
self.image.set_alpha(192)
|
||
else:
|
||
self.image.set_alpha(255)
|
||
|
||
|
||
class SeaShroom(Plant):
|
||
def __init__(self, x, y, bullet_group):
|
||
Plant.__init__(self, x, y, c.SEASHROOM, c.PLANT_HEALTH, bullet_group)
|
||
self.can_sleep = True
|
||
self.shoot_timer = 0
|
||
|
||
def loadImages(self, name, scale):
|
||
self.idle_frames = []
|
||
self.sleep_frames = []
|
||
|
||
idle_name = name
|
||
sleep_name = name + 'Sleep'
|
||
|
||
frame_list = [self.idle_frames, self.sleep_frames]
|
||
name_list = [idle_name, sleep_name]
|
||
|
||
for i, name in enumerate(name_list):
|
||
self.loadFrames(frame_list[i], name, 1)
|
||
|
||
self.frames = self.idle_frames
|
||
|
||
def attacking(self):
|
||
if self.shoot_timer == 0:
|
||
self.shoot_timer = self.current_time - 700
|
||
elif (self.current_time - self.shoot_timer) >= 1400:
|
||
self.bullet_group.add(Bullet(self.rect.right, self.rect.y + 50, self.rect.y + 50,
|
||
c.BULLET_SEASHROOM, c.BULLET_DAMAGE_NORMAL, effect=None))
|
||
self.shoot_timer = self.current_time
|
||
# 播放发射音效
|
||
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
|
||
return False
|
||
|
||
def setAttack(self):
|
||
self.state = c.ATTACK
|
||
if self.shoot_timer != 0:
|
||
self.shoot_timer = self.current_time - 700
|
||
|
||
|
||
class TallNut(Plant):
|
||
def __init__(self, x, y):
|
||
Plant.__init__(self, x, y, c.TALLNUT, c.TALLNUT_HEALTH, None)
|
||
self.load_images()
|
||
self.cracked1 = False
|
||
self.cracked2 = False
|
||
|
||
def load_images(self):
|
||
self.cracked1_frames = []
|
||
self.cracked2_frames = []
|
||
|
||
cracked1_frames_name = self.name + '_cracked1'
|
||
cracked2_frames_name = self.name + '_cracked2'
|
||
|
||
self.loadFrames(self.cracked1_frames, cracked1_frames_name, 1)
|
||
self.loadFrames(self.cracked2_frames, cracked2_frames_name, 1)
|
||
|
||
def idling(self):
|
||
if not self.cracked1 and self.health <= c.TALLNUT_CRACKED1_HEALTH:
|
||
self.changeFrames(self.cracked1_frames)
|
||
self.cracked1 = True
|
||
elif not self.cracked2 and self.health <= c.TALLNUT_CRACKED2_HEALTH:
|
||
self.changeFrames(self.cracked2_frames)
|
||
self.cracked2 = True
|
||
|
||
|
||
class TangleKlep(Plant):
|
||
def __init__(self, x, y):
|
||
Plant.__init__(self, x, y, c.TANGLEKLEP, c.PLANT_HEALTH, None)
|
||
self.load_images()
|
||
self.splashing = False
|
||
|
||
def load_images(self):
|
||
self.idle_frames = []
|
||
self.splash_frames = []
|
||
|
||
idle_name = self.name
|
||
splash_name = self.name + 'Splash'
|
||
|
||
frame_list = [self.idle_frames, self.splash_frames]
|
||
name_list = [idle_name, splash_name]
|
||
|
||
for i, name in enumerate(name_list):
|
||
self.loadFrames(frame_list[i], name, 1)
|
||
|
||
self.frames = self.idle_frames
|
||
|
||
def canAttack(self, zombie):
|
||
if zombie.state != c.DIE and (not zombie.lostHead):
|
||
# 这里碰撞应当比碰撞一般更容易,就设置成圆形或矩形模式,不宜采用mask
|
||
if pg.sprite.collide_circle_ratio(0.7)(zombie, self):
|
||
return True
|
||
return False
|
||
|
||
def setAttack(self, zombie, zombie_group):
|
||
self.attack_zombie = zombie
|
||
self.zombie_group = zombie_group
|
||
self.state = c.ATTACK
|
||
|
||
def attacking(self):
|
||
if not self.splashing:
|
||
self.splashing = True
|
||
self.changeFrames(self.splash_frames)
|
||
self.attack_zombie.kill()
|
||
# 播放拖拽音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "tangleKelpDrag.ogg")).play()
|
||
# 这里必须用elif排除尚未进入splash阶段,以免误触
|
||
elif (self.frame_index + 1) >= self.frame_num:
|
||
self.health = 0
|
||
|
||
|
||
# 毁灭菇的处理办法:
|
||
# 爆炸后留下的坑看作另一种形态的毁灭菇
|
||
# 当存在这种形态的毁灭菇时不可以种植物
|
||
# 坑形态的毁灭菇存在时不可种植物
|
||
# 坑形态的毁灭菇同地刺一样不可以被啃食
|
||
# 爆炸时杀死同一格的所有植物
|
||
class DoomShroom(Plant):
|
||
def __init__(self, x, y, mapPlantsSet, explode_y_range):
|
||
Plant.__init__(self, x, y, c.DOOMSHROOM, c.PLANT_HEALTH, None)
|
||
self.can_sleep = True
|
||
self.mapPlantSet = mapPlantsSet
|
||
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
|
||
|
||
def loadImages(self, name, scale):
|
||
self.idle_frames = []
|
||
self.sleep_frames = []
|
||
self.boom_frames = []
|
||
|
||
idle_name = name
|
||
sleep_name = name + 'Sleep'
|
||
boom_name = name + 'Boom'
|
||
|
||
frame_list = [self.idle_frames, self.sleep_frames, self.boom_frames]
|
||
name_list = [idle_name, sleep_name, boom_name]
|
||
|
||
for i, name in enumerate(name_list):
|
||
self.loadFrames(frame_list[i], name, 1)
|
||
|
||
self.frames = self.idle_frames
|
||
|
||
def setBoom(self):
|
||
self.changeFrames(self.boom_frames)
|
||
self.start_boom = True
|
||
|
||
def animation(self):
|
||
# 发生了爆炸
|
||
if self.start_boom:
|
||
if self.frame_index == 1:
|
||
self.rect.x -= 80
|
||
self.rect.y += 30
|
||
# 播放爆炸音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "doomshroom.ogg")).play()
|
||
if (self.current_time - self.animate_timer) > self.animate_interval:
|
||
self.frame_index += 1
|
||
if self.frame_index >= self.frame_num:
|
||
self.health = 0
|
||
self.frame_index = self.frame_num - 1
|
||
self.mapPlantSet.add(c.HOLE)
|
||
# 睡觉状态
|
||
elif self.state == c.SLEEP:
|
||
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
|
||
# 正常状态
|
||
else:
|
||
self.health = c.INF
|
||
if (self.current_time - self.animate_timer) > 100:
|
||
self.frame_index += 1
|
||
if self.frame_index >= self.frame_num:
|
||
self.setBoom()
|
||
return
|
||
self.animate_timer = self.current_time
|
||
self.image = self.frames[self.frame_index]
|
||
self.mask = pg.mask.from_surface(self.image)
|
||
|
||
if (self.current_time - self.highlightTime < 100):
|
||
self.image.set_alpha(150)
|
||
elif ((self.current_time - self.hit_timer) < 200):
|
||
self.image.set_alpha(192)
|
||
else:
|
||
self.image.set_alpha(255)
|
||
|
||
# 用于描述毁灭菇的坑
|
||
class Hole(Plant):
|
||
def __init__(self, x, y, plotType):
|
||
# 指定区域类型这一句必须放在前面,否则加载图片判断将会失败
|
||
self.plotType = plotType
|
||
Plant.__init__(self, x, y, c.HOLE, c.INF, None)
|
||
self.timer = 0
|
||
self.shallow = False
|
||
|
||
def loadImages(self, name, scale):
|
||
self.idle_frames = []
|
||
self.idle2_frames = []
|
||
self.water_frames = []
|
||
self.water2_frames = []
|
||
self.roof_frames = []
|
||
self.roof2_frames = []
|
||
|
||
idle_name = name
|
||
idle2_name = name + 'Shallow'
|
||
water_name = name + 'Water'
|
||
water2_name = name + 'WaterShallow'
|
||
roof_name = name + 'Roof'
|
||
roof2_name = name + 'RoofShallow'
|
||
|
||
frame_list = [ self.idle_frames, self.idle2_frames,
|
||
self.water_frames, self.water2_frames,
|
||
self.roof_frames, self.roof2_frames]
|
||
name_list = [ idle_name, idle2_name,
|
||
water_name, water2_name,
|
||
roof_name, roof2_name]
|
||
|
||
for i, name in enumerate(name_list):
|
||
self.loadFrames(frame_list[i], name, 1)
|
||
|
||
if self.plotType == c.MAP_TILE:
|
||
self.frames = self.roof_frames
|
||
elif self.plotType == c.MAP_WATER:
|
||
self.frames = self.water_frames
|
||
else:
|
||
self.frames = self.idle_frames
|
||
|
||
def idling(self):
|
||
if self.timer == 0:
|
||
self.timer = self.current_time
|
||
elif (not self.shallow) and (self.current_time - self.timer >= 90000):
|
||
if self.plotType == c.MAP_TILE:
|
||
self.frames = self.roof2_frames
|
||
elif self.plotType == c.MAP_WATER:
|
||
self.frames = self.water2_frames
|
||
else:
|
||
self.frames = self.idle2_frames
|
||
self.shallow = True
|
||
elif self.current_time - self.timer >= 180000:
|
||
self.health = 0
|
||
|
||
|
||
class Grave(Plant):
|
||
def __init__(self, x, y):
|
||
Plant.__init__(self, x, y, c.GRAVE, c.INF, None)
|
||
self.frame_index = random.randint(0, self.frame_num - 1)
|
||
self.image = self.frames[self.frame_index]
|
||
self.mask = pg.mask.from_surface(self.image)
|
||
|
||
def animation(self):
|
||
pass
|
||
|
||
|
||
class GraveBuster(Plant):
|
||
def __init__(self, x, y, plant_group, map, map_x):
|
||
Plant.__init__(self, x, y, c.GRAVEBUSTER, c.PLANT_HEALTH, None)
|
||
self.map = map
|
||
self.map_x = map_x
|
||
self.plant_group = plant_group
|
||
self.animate_interval = 100
|
||
# 播放吞噬音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "gravebusterchomp.ogg")).play()
|
||
|
||
|
||
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 = 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.health = 0
|
||
self.health = 0
|
||
self.animate_timer = self.current_time
|
||
|
||
self.image = self.frames[self.frame_index]
|
||
self.mask = pg.mask.from_surface(self.image)
|
||
|
||
if (self.current_time - self.highlightTime < 100):
|
||
self.image.set_alpha(150)
|
||
elif ((self.current_time - self.hit_timer) < 200):
|
||
self.image.set_alpha(192)
|
||
else:
|
||
self.image.set_alpha(255)
|
||
|
||
class FumeShroom(Plant):
|
||
def __init__(self, x, y, bullet_group, zombie_group):
|
||
Plant.__init__(self, x, y, c.FUMESHROOM, c.PLANT_HEALTH, bullet_group)
|
||
self.can_sleep = True
|
||
self.shoot_timer = 0
|
||
self.showAttackFrames = True
|
||
self.zombie_group = zombie_group
|
||
|
||
def loadImages(self, name, scale):
|
||
self.idle_frames = []
|
||
self.sleep_frames = []
|
||
self.attack_frames = []
|
||
|
||
idle_name = name
|
||
sleep_name = name + 'Sleep'
|
||
attack_name = name + 'Attack'
|
||
|
||
frame_list = [self.idle_frames, self.sleep_frames, self.attack_frames]
|
||
name_list = [idle_name, sleep_name, attack_name]
|
||
|
||
for i, name in enumerate(name_list):
|
||
self.loadFrames(frame_list[i], name, 1, c.BLACK)
|
||
|
||
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
|
||
return False
|
||
|
||
def setAttack(self):
|
||
self.state = c.ATTACK
|
||
if self.shoot_timer != 0:
|
||
self.shoot_timer = self.current_time - 700
|
||
|
||
def attacking(self):
|
||
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
|
||
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)
|
||
self.shoot_timer = self.current_time
|
||
self.showAttackFrames = True
|
||
# 播放发射音效
|
||
pg.mixer.Sound(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,"resources", "sound", "fume.ogg")).play()
|
||
|
||
def animation(self):
|
||
if (self.current_time - self.animate_timer) > self.animate_interval:
|
||
self.frame_index += 1
|
||
if self.frame_index >= self.frame_num:
|
||
if self.frames == self.attack_frames:
|
||
self.changeFrames(self.idle_frames)
|
||
else:
|
||
self.frame_index = 0
|
||
self.animate_timer = self.current_time
|
||
|
||
self.image = self.frames[self.frame_index]
|
||
self.mask = pg.mask.from_surface(self.image)
|
||
|
||
if (self.current_time - self.highlightTime < 100):
|
||
self.image.set_alpha(150)
|
||
elif ((self.current_time - self.hit_timer) < 200):
|
||
self.image.set_alpha(192)
|
||
else:
|
||
self.image.set_alpha(255)
|
||
|
||
|
||
class IceFrozenPlot(Plant):
|
||
def __init__(self, x, y):
|
||
Plant.__init__(self, x, y, c.ICE_FROZEN_PLOT, c.INF, None)
|
||
self.timer = 0
|
||
|
||
def idling(self):
|
||
if self.timer == 0:
|
||
self.timer = self.current_time
|
||
elif self.current_time - self.timer >= 30000:
|
||
self.health = 0
|
||
|
||
|
||
class Garlic(Plant):
|
||
def __init__(self, x, y):
|
||
Plant.__init__(self, x, y, c.GARLIC, c.GARLIC_HEALTH, None)
|
||
self.load_images()
|
||
self.cracked1 = False
|
||
self.cracked2 = False
|
||
|
||
def load_images(self):
|
||
self.cracked1_frames = []
|
||
self.cracked2_frames = []
|
||
|
||
cracked1_frames_name = self.name + '_cracked1'
|
||
cracked2_frames_name = self.name + '_cracked2'
|
||
|
||
self.loadFrames(self.cracked1_frames, cracked1_frames_name, 1)
|
||
self.loadFrames(self.cracked2_frames, cracked2_frames_name, 1)
|
||
|
||
def idling(self):
|
||
if (not self.cracked1) and self.health <= c.GARLIC_CRACKED1_HEALTH:
|
||
self.changeFrames(self.cracked1_frames)
|
||
self.cracked1 = True
|
||
elif (not self.cracked2) and self.health <= c.GARLIC_CRACKED2_HEALTH:
|
||
self.changeFrames(self.cracked2_frames)
|
||
self.cracked2 = True
|