Merge branch 'master' into opengl

This commit is contained in:
wszqkzqk 2022-09-15 16:23:55 +08:00
commit ae3f77c2b4
9 changed files with 283 additions and 151 deletions

View File

@ -87,10 +87,11 @@ python pypvz.py
* 泳池模式9~11
* 浓雾模式暂时没有雾12
* 小游戏模式:
* 坚果保龄球模式1
* 坚果保龄球1
* 传送带模式白天2
* 传送带模式黑夜3
* 传送带模式泳池4
* 坚果保龄球(II)5
* 目前暂时按照以上设定,未与原版相符
* 可以通过修改存档JSON文件中的`game rate`值来调节游戏速度倍率

View File

@ -11,13 +11,14 @@ from source import tool
from source import constants as c
from source.state import mainmenu, screen, level
if __name__=="__main__":
if __name__ == "__main__":
# 日志设置
if not os.path.exists(os.path.dirname(c.USERLOG_PATH)):
os.makedirs(os.path.dirname(c.USERLOG_PATH))
logger = logging.getLogger("main")
formatter = logging.Formatter("%(asctime)s - %(levelname)s: %(message)s")
fileHandler = RotatingFileHandler(c.USERLOG_PATH, "a", 1024*1024, 0, "utf-8")
fileHandler = RotatingFileHandler(c.USERLOG_PATH, "a", 1_000_000, 0, "utf-8")
os.chmod(c.USERLOG_PATH, 420) # 设置日志文件权限Unix为644Windows为可读、可写
fileHandler.setFormatter(formatter)
streamHandler = logging.StreamHandler()
streamHandler.setFormatter(formatter)

View File

@ -10,33 +10,56 @@ class Map():
self.width = c.GRID_POOL_X_LEN
self.height = c.GRID_POOL_Y_LEN
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)]
self.map = [ [self.initMapGrid(c.MAP_WATER) if 2 <= y <= 3
else self.initMapGrid(c.MAP_GRASS)
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.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)]
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.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)]
self.map = [ [self.initMapGrid(c.MAP_GRASS) if y ==2
else self.initMapGrid(c.MAP_UNAVAILABLE)
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.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)]
self.map = [ [self.initMapGrid(c.MAP_GRASS) if 1 <= y <= 3
else self.initMapGrid(c.MAP_UNAVAILABLE)
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.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)]
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):
if (map_x < 0 or map_x >= self.width or
map_y < 0 or map_y >= self.height):
return False
return True
if ((0 <= map_x < self.width)
and (0 <= map_y < self.height)):
return True
return False
# 地图单元格状态
# 注意是可变对象,不能直接引用
@ -50,12 +73,14 @@ class Map():
def isAvailable(self, map_x, map_y, plant_name):
# 咖啡豆和墓碑吞噬者的判别最为特殊
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]):
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 plant_name == c.GRAVEBUSTER:
if (c.GRAVE in self.map[map_y][map_x][c.MAP_PLANT]):
if (c.GRAVE 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
else:
return False
@ -70,7 +95,8 @@ class Map():
elif (all((i in {"花盆(未实现)", c.PUMPKINHEAD}) for i 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 (plant_name == 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
@ -86,7 +112,8 @@ class Map():
return False
else:
return True
elif (plant_name == 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
@ -110,7 +137,8 @@ class Map():
return False
else:
return True
elif (plant_name == 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
@ -248,7 +276,8 @@ LEVEL_MAP_DATA = (
c.INIT_SUN_NAME: 50,
c.SHOVEL: 1,
c.SPAWN_ZOMBIES:c.SPAWN_ZOMBIES_AUTO,
c.INCLUDED_ZOMBIES: (c.NORMAL_ZOMBIE, c.CONEHEAD_ZOMBIE, c.POLE_VAULTING_ZOMBIE, c.BUCKETHEAD_ZOMBIE),
c.INCLUDED_ZOMBIES: ( c.NORMAL_ZOMBIE, c.CONEHEAD_ZOMBIE,
c.POLE_VAULTING_ZOMBIE, c.BUCKETHEAD_ZOMBIE),
c.NUM_FLAGS:3
},
# 第6关 目前夜晚第一关
@ -420,9 +449,9 @@ LITTLE_GAME_MAP_DATA = (
c.NUM_FLAGS:3,
c.CARD_POOL: { c.PUFFSHROOM: 100,
c.SCAREDYSHROOM: 100,
c.ICESHROOM: 100,
c.ICESHROOM: 70,
c.HYPNOSHROOM: 100,
c.DOOMSHROOM: 100,
c.DOOMSHROOM: 50,
c.GRAVEBUSTER: 100,
c.FUMESHROOM: 200},
c.GRADE_GRAVES:3
@ -445,7 +474,7 @@ LITTLE_GAME_MAP_DATA = (
c.SPIKEWEED: 100,
c.SQUASH: 100,
c.JALAPENO: 50,
c.THREEPEASHOOTER: 300,}
c.THREEPEASHOOTER: 400,}
},
# 第5关 坚果保龄球2
{

View File

@ -22,31 +22,40 @@ def getSunValueImage(sun_value):
return image
def getCardPool(data):
card_pool = {}
for cardName in data:
card_pool[c.PLANT_CARD_INFO[c.PLANT_CARD_INDEX[cardName]]] = data[cardName]
card_pool = {c.PLANT_CARD_INFO[c.PLANT_CARD_INDEX[card_name]]: data[card_name]
for card_name in data}
return card_pool
class Card():
def __init__(self, x, y, index, scale=0.5):
self.loadFrame(c.PLANT_CARD_INFO[index][c.CARD_INDEX], scale)
def __init__(self, x:int, y:int, index:int, scale:float=0.5, not_recommend=0):
self.info = c.PLANT_CARD_INFO[index]
self.loadFrame(self.info[c.CARD_INDEX], scale)
self.rect = self.orig_image.get_rect()
self.rect.x = x
self.rect.y = y
# 绘制植物阳光消耗大小
font = pg.font.Font(c.FONT_PATH, 12)
self.sun_cost_img = font.render(str(c.PLANT_CARD_INFO[index][c.SUN_INDEX]), True, c.BLACK)
self.sun_cost_img = font.render(str(self.info[c.SUN_INDEX]), True, c.BLACK)
self.sun_cost_img_rect = self.sun_cost_img.get_rect()
sun_cost_img_x = 32 - self.sun_cost_img_rect.w
self.orig_image.blit(self.sun_cost_img, (sun_cost_img_x, 52, self.sun_cost_img_rect.w, self.sun_cost_img_rect.h))
self.orig_image.blit(self.sun_cost_img,
(sun_cost_img_x, 52, self.sun_cost_img_rect.w, self.sun_cost_img_rect.h))
self.index = index
self.sun_cost = c.PLANT_CARD_INFO[index][c.SUN_INDEX]
self.frozen_time = c.PLANT_CARD_INFO[index][c.FROZEN_TIME_INDEX]
self.sun_cost = self.info[c.SUN_INDEX]
self.frozen_time = self.info[c.FROZEN_TIME_INDEX]
self.frozen_timer = -self.frozen_time
self.refresh_timer = 0
self.select = True
self.clicked = False
self.not_recommend = not_recommend
if self.not_recommend:
self.orig_image.set_alpha(128)
self.image = pg.Surface((self.rect.w, self.rect.h)) # 黑底
self.image.blit(self.orig_image, (0,0), (0, 0, self.rect.w, self.rect.h))
else:
self.image = self.orig_image
self.image.set_alpha(255)
def loadFrame(self, name, scale):
frame = tool.GFX[name]
@ -58,8 +67,8 @@ class Card():
def checkMouseClick(self, mouse_pos):
x, y = mouse_pos
if(x >= self.rect.x and x <= self.rect.right and
y >= self.rect.y and y <= self.rect.bottom):
if (self.rect.x <= x <= self.rect.right and
self.rect.y <= y <= self.rect.bottom):
return True
return False
@ -74,9 +83,17 @@ class Card():
def setSelect(self, can_select):
self.select = can_select
if can_select:
self.image.set_alpha(255)
if self.not_recommend % 2:
self.orig_image.set_alpha(128)
self.image = pg.Surface((self.rect.w, self.rect.h)) # 黑底
self.image.blit(self.orig_image, (0,0), (0, 0, self.rect.w, self.rect.h))
else:
self.image = self.orig_image
self.image.set_alpha(255)
else:
self.image.set_alpha(128)
self.orig_image.set_alpha(64)
self.image = pg.Surface((self.rect.w, self.rect.h)) # 黑底
self.image.blit(self.orig_image, (0,0), (0, 0, self.rect.w, self.rect.h))
def setFrozenTime(self, current_time):
self.frozen_timer = current_time
@ -85,25 +102,28 @@ class Card():
# 有关是否满足冷却与阳光条件的图片形式
time = current_time - self.frozen_timer
if time < self.frozen_time: #cool down status
image = pg.Surface([self.rect.w, self.rect.h])
# 在冷却时间不足且阳光也不足时,叠加两者效果显示,即同时改变透明度与图像覆盖
if self.sun_cost > sun_value:
image.set_alpha(192)
frozen_image = self.orig_image.copy()
image = pg.Surface((self.rect.w, self.rect.h)) # 黑底
frozen_image = self.orig_image
frozen_image.set_alpha(128)
frozen_height = (self.frozen_time - time)/self.frozen_time * self.rect.h
frozen_height = ((self.frozen_time - time)/self.frozen_time) * self.rect.h
image.blit(frozen_image, (0,0), (0, 0, self.rect.w, frozen_height))
self.orig_image.set_alpha(192)
image.blit(self.orig_image, (0,frozen_height),
(0, frozen_height, self.rect.w, self.rect.h - frozen_height))
elif self.sun_cost > sun_value: #disable status
image = self.orig_image.copy()
image.set_alpha(192)
image = pg.Surface((self.rect.w, self.rect.h)) # 黑底
self.orig_image.set_alpha(192)
image.blit(self.orig_image, (0,0), (0, 0, self.rect.w, self.rect.h))
elif self.clicked:
image = self.orig_image.copy()
image.set_alpha(128)
image = pg.Surface((self.rect.w, self.rect.h)) # 黑底
chosen_image = self.orig_image
chosen_image.set_alpha(128)
image.blit(chosen_image, (0,0), (0, 0, self.rect.w, self.rect.h))
else:
image = self.orig_image
image.set_alpha(255)
return image
def update(self, sun_value, current_time):
@ -176,8 +196,8 @@ class MenuBar():
def checkMenuBarClick(self, mouse_pos):
x, y = mouse_pos
if (x >= self.rect.x and x <= self.rect.right and
y >= self.rect.y and y <= self.rect.bottom):
if (self.rect.x <= x <= self.rect.right and
self.rect.y <= y <= self.rect.bottom):
return True
return False
@ -211,10 +231,11 @@ class MenuBar():
# 关卡模式选植物的界面
class Panel():
def __init__(self, card_list, sun_value):
def __init__(self, card_list, sun_value, background_type=c.BACKGROUND_DAY):
self.loadImages(sun_value)
self.selected_cards = []
self.selected_num = 0
self.background_type = background_type
self.setupCards(card_list)
def loadFrame(self, name):
@ -255,7 +276,20 @@ class Panel():
x = c.PANEL_X_START - c.PANEL_X_INTERNAL
y += c.PANEL_Y_INTERNAL
x += c.PANEL_X_INTERNAL
self.card_list.append(Card(x, y, index, 0.5))
plant_name = c.PLANT_CARD_INFO[index][c.PLANT_NAME_INDEX]
if (plant_name in c.WATER_PLANTS
and self.background_type not in c.POOL_EQUIPPED_BACKGROUNDS):
not_recommend = c.REASON_OTHER
elif (plant_name == c.GRAVEBUSTER
and self.background_type != c.BACKGROUND_NIGHT):
not_recommend = c.REASON_OTHER
elif (plant_name in c.CAN_SLEEP_PLANTS
and self.background_type in c.DAYTIME_BACKGROUNDS):
not_recommend = c.REASON_WILL_SLEEP
# 还有屋顶场景,以及其他植物没有实现的植物没有写进来
else:
not_recommend = 0
self.card_list.append(Card(x, y, index, 0.5, not_recommend))
def checkCardClick(self, mouse_pos):
delete_card = None
@ -271,6 +305,13 @@ class Panel():
self.selected_num -= 1
# 播放点击音效
c.SOUND_TAPPING_CARD.play()
if delete_card.info[c.PLANT_NAME_INDEX] == c.COFFEEBEAN:
for i in self.card_list:
if i.not_recommend == c.REASON_SLEEP_BUT_COFFEE_BEAN:
i.not_recommend = c.REASON_WILL_SLEEP
i.orig_image.set_alpha(128)
i.image = pg.Surface((i.rect.w, i.rect.h)) # 黑底
i.image.blit(i.orig_image, (0,0), (0, 0, i.rect.w, i.rect.h))
if self.selected_num >= c.CARD_MAX_NUM:
return
@ -281,9 +322,15 @@ class Panel():
self.addCard(card)
# 播放点击音效
c.SOUND_TAPPING_CARD.play()
if card.info[c.PLANT_NAME_INDEX] == c.COFFEEBEAN:
for i in self.card_list:
if i.not_recommend == c.REASON_WILL_SLEEP:
i.not_recommend = c.REASON_SLEEP_BUT_COFFEE_BEAN
i.image = i.orig_image
i.image.set_alpha(255)
break
def addCard(self, card):
def addCard(self, card:Card):
card.setSelect(False)
y = 8
x = 77 + self.selected_num * c.BAR_CARD_X_INTERNAL
@ -298,8 +345,8 @@ class Panel():
return False
x, y = mouse_pos
if (x >= self.button_rect.x and x <= self.button_rect.right and
y >= self.button_rect.y and y <= self.button_rect.bottom):
if (self.button_rect.x <= x <= self.button_rect.right and
self.button_rect.y <= y <= self.button_rect.bottom):
return True
return False
@ -348,8 +395,8 @@ class MoveCard():
def checkMouseClick(self, mouse_pos):
x, y = mouse_pos
if (x >= self.rect.x and x <= self.rect.right and
y >= self.rect.y and y <= self.rect.bottom):
if (self.rect.x <= x <= self.rect.right and
self.rect.y <= y <= self.rect.bottom):
return True
return False
@ -357,14 +404,21 @@ class MoveCard():
# 新增卡片时显示图片
if self.rect.w < self.orig_rect.w: #create a part card image
image = pg.Surface([self.rect.w, self.rect.h])
if self.clicked:
self.orig_image.set_alpha(128)
else:
self.orig_image.set_alpha(255)
image.blit(self.orig_image, (0, 0), (0, 0, self.rect.w, self.rect.h))
self.rect.w += 1
else:
image = self.orig_image
if self.clicked:
image.set_alpha(192)
else:
image.set_alpha(255)
if self.clicked:
image = pg.Surface([self.rect.w, self.rect.h]) # 黑底
self.orig_image.set_alpha(128)
image.blit(self.orig_image, (0,0), (0, 0, self.rect.w, self.rect.h))
else:
self.orig_image.set_alpha(255)
image = self.orig_image
return image
def update(self, left_x, current_time):
@ -418,7 +472,7 @@ class MoveBar():
card.update(left_x, self.current_time)
left_x = card.rect.right + 1
if(self.current_time - self.create_timer) > c.MOVEBAR_CARD_FRESH_TIME:
if (self.current_time - self.create_timer) > c.MOVEBAR_CARD_FRESH_TIME:
if self.createCard():
self.create_timer = self.current_time
@ -432,8 +486,8 @@ class MoveBar():
def checkMenuBarClick(self, mouse_pos):
x, y = mouse_pos
if (x >= self.rect.x and x <= self.rect.right and
y >= self.rect.y and y <= self.rect.bottom):
if (self.rect.x <= x <= self.rect.right and
self.rect.y <= y <= self.rect.bottom):
return True
return False

View File

@ -178,9 +178,12 @@ class Fume(pg.sprite.Sprite):
# 杨桃的子弹
class StarBullet(Bullet):
def __init__(self, x, start_y, damage, direction, level, damage_type = c.ZOMBIE_DEAFULT_DAMAGE): # direction指星星飞行方向
Bullet.__init__(self, x, start_y, start_y, c.BULLET_STAR, damage, damage_type = damage_type)
def __init__( self, x, start_y,
damage, direction,
level, damage_type = c.ZOMBIE_DEAFULT_DAMAGE): # direction指星星飞行方向
Bullet.__init__( self, x, start_y,
start_y, c.BULLET_STAR,
damage, damage_type = damage_type)
self.level = level
self.map_y = self.level.map.getMapIndex(self.rect.x, self.rect.centery)[1]
self.direction = direction
@ -238,7 +241,6 @@ class Plant(pg.sprite.Sprite):
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
@ -394,7 +396,8 @@ class SunFlower(Plant):
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))
Sun( self.rect.centerx, self.rect.bottom,
self.rect.right, self.rect.bottom + self.rect.h // 2))
self.sun_timer = self.current_time
@ -666,7 +669,6 @@ class Chomper(Plant):
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):
@ -697,8 +699,9 @@ class PuffShroom(Plant):
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)):
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
@ -796,7 +799,8 @@ class Squash(Plant):
return True
# 攻击状态
elif (self.state == c.ATTACK):
if pg.sprite.collide_rect_ratio(0.5)(zombie, self) or pg.sprite.collide_mask(zombie, self):
if (pg.sprite.collide_rect_ratio(0.5)(zombie, self)
or pg.sprite.collide_mask(zombie, self)):
return True
return False
@ -844,9 +848,12 @@ class Spikeweed(Plant):
self.state = c.IDLE
def canAttack(self, zombie):
# 地刺能不能扎的判据:僵尸中心与地刺中心的距离或僵尸包括了地刺中心和右端(平衡得到合理的攻击范围,"僵尸包括了地刺中心和右端"是为以后巨人做准备)
# 地刺能不能扎的判据:
# 僵尸中心与地刺中心的距离或僵尸包括了地刺中心和右端(平衡得到合理的攻击范围,"僵尸包括了地刺中心和右端"是为以后巨人做准备)
# 暂时不能用碰撞判断,平衡性不好
if ((-40 <= zombie.rect.centerx - self.rect.centerx <= 40) or (zombie.rect.left <= self.rect.x <= zombie.rect.right and zombie.rect.left <= self.rect.right <= zombie.rect.right)):
if ((-40 <= zombie.rect.centerx - self.rect.centerx <= 40)
or (zombie.rect.left <= self.rect.x <= zombie.rect.right
and zombie.rect.left <= self.rect.right <= zombie.rect.right)):
return True
return False
@ -936,7 +943,6 @@ class Jalapeno(Plant):
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
@ -990,7 +996,6 @@ class ScaredyShroom(Plant):
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
@ -1034,7 +1039,6 @@ class SunShroom(Plant):
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
@ -1050,8 +1054,10 @@ class IceShroom(Plant):
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]
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):
@ -1103,7 +1109,6 @@ class IceShroom(Plant):
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
@ -1279,18 +1284,20 @@ class TorchWood(Plant):
def idling(self):
for i in self.bullet_group:
if i.name == c.BULLET_PEA:
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, passed_torchwood_x=self.rect.centerx))
i.kill()
elif i.name == c.BULLET_PEA_ICE:
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, passed_torchwood_x=self.rect.centerx))
i.kill()
if (i.name == c.BULLET_PEA
and i.passed_torchwood_x != self.rect.centerx
and 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, passed_torchwood_x=self.rect.centerx))
i.kill()
elif (i.name == c.BULLET_PEA_ICE
and i.passed_torchwood_x != self.rect.centerx
and abs(i.rect.centerx - self.rect.centerx)):
self.bullet_group.add(Bullet(i.rect.x, i.rect.y, i.dest_y,
c.BULLET_PEA, c.BULLET_DAMAGE_NORMAL,
effect=None, passed_torchwood_x=self.rect.centerx))
i.kill()
class StarFruit(Plant):
def __init__(self, x, y, bullet_group, level):
@ -1306,13 +1313,17 @@ class StarFruit(Plant):
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
# 斜向上,理想直线方程为:
# 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):
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):
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
@ -1322,13 +1333,23 @@ class StarFruit(Plant):
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, damage_type = c.ZOMBIE_COMMON_DAMAGE))
# pypvz特有设定向后打的杨桃子弹无视铁门与报纸防具
self.bullet_group.add(StarBullet( self.rect.left - 10, self.rect.y + 15,
c.BULLET_DAMAGE_NORMAL, c.STAR_BACKWARD,
self.level, damage_type = c.ZOMBIE_COMMON_DAMAGE))
# 其他方向的杨桃子弹伤害效果与豌豆等同
self.bullet_group.add(StarBullet(self.rect.centerx - 20, self.rect.bottom - self.rect.h - 15, c.BULLET_DAMAGE_NORMAL, c.STAR_UPWARD, self.level))
self.bullet_group.add(StarBullet(self.rect.centerx - 20, self.rect.bottom - 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.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
# 播放发射音效
c.SOUND_SHOOT.play()
@ -1354,10 +1375,10 @@ class CoffeeBean(Plant):
if self.frame_index >= self.frame_num:
self.map_content[c.MAP_SLEEP] = False
for plant in self.plant_group:
if plant.can_sleep:
if plant.name in c.CAN_SLEEP_PLANTS:
if plant.state == c.SLEEP:
plantMapX, _ = self.map.getMapIndex(plant.rect.centerx, plant.rect.bottom)
if plantMapX == self.map_x:
plant_map_x, _ = self.map.getMapIndex(plant.rect.centerx, plant.rect.bottom)
if plant_map_x == self.map_x:
plant.state = c.IDLE
plant.setIdle()
plant.changeFrames(plant.idle_frames)
@ -1382,7 +1403,6 @@ class CoffeeBean(Plant):
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):
@ -1413,8 +1433,9 @@ class SeaShroom(Plant):
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)):
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
@ -1474,7 +1495,7 @@ class TangleKlep(Plant):
def canAttack(self, zombie):
if zombie.state != c.DIE and (not zombie.losthead):
# 这里碰撞应当比碰撞一般更容易就设置成圆形或矩形模式不宜采用mask
if pg.sprite.collide_circle_ratio(0.7)(zombie, self):
if pg.sprite.collide_rect_ratio(1)(zombie, self):
return True
return False
@ -1504,7 +1525,6 @@ class TangleKlep(Plant):
class DoomShroom(Plant):
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.map_plant_set = map_plant_set
self.bomb_timer = 0
self.explode_y_range = explode_y_range
@ -1578,9 +1598,9 @@ class DoomShroom(Plant):
# 用于描述毁灭菇的坑
class Hole(Plant):
def __init__(self, x, y, plotType):
def __init__(self, x, y, plot_type):
# 指定区域类型这一句必须放在前面,否则加载图片判断将会失败
self.plotType = plotType
self.plot_type = plot_type
Plant.__init__(self, x, y, c.HOLE, c.INF, None)
self.timer = 0
self.shallow = False
@ -1610,9 +1630,9 @@ class Hole(Plant):
for i, name in enumerate(name_list):
self.loadFrames(frame_list[i], name)
if self.plotType == c.MAP_TILE:
if self.plot_type == c.MAP_TILE:
self.frames = self.roof_frames
elif self.plotType == c.MAP_WATER:
elif self.plot_type == c.MAP_WATER:
self.frames = self.water_frames
else:
self.frames = self.idle_frames
@ -1621,9 +1641,9 @@ class Hole(Plant):
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:
if self.plot_type == c.MAP_TILE:
self.frames = self.roof2_frames
elif self.plotType == c.MAP_WATER:
elif self.plot_type == c.MAP_WATER:
self.frames = self.water2_frames
else:
self.frames = self.idle2_frames
@ -1679,7 +1699,6 @@ class GraveBuster(Plant):
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.show_attack_frames = True
self.zombie_group = zombie_group
@ -1704,8 +1723,9 @@ class FumeShroom(Plant):
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)):
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

View File

@ -9,12 +9,14 @@ else: # 非Windows系统存储路径
USERDATA_PATH = os.path.expanduser(os.path.join("~", ".config", "wszqkzqk.dev", "pypvz", "userdata.json"))
USERLOG_PATH = os.path.expanduser(os.path.join("~", ".config", "wszqkzqk.dev", "pypvz", "run.log"))
# 窗口图标
ORIGINAL_LOGO = os.path.join(os.path.dirname(os.path.dirname(__file__)), "pypvz-exec-logo.png")
# 游戏图片资源路径
PATH_IMG_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "resources", "graphics")
# 游戏音乐文件夹路径
PATH_MUSIC_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "resources","music")
# 窗口图标
ORIGINAL_LOGO = os.path.join(os.path.dirname(os.path.dirname(__file__)), "pypvz-exec-logo.png")
# 字体路径
FONT_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "resources", "DroidSansFallback.ttf")
# 窗口标题
ORIGINAL_CAPTION = "pypvz"
@ -29,8 +31,6 @@ SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
SCREEN_SIZE = (SCREEN_WIDTH, SCREEN_HEIGHT)
# 字体路径
FONT_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "resources", "DroidSansFallback.ttf")
# 选卡数量
# 最大数量
@ -180,7 +180,7 @@ SURVIVAL_ROUNDS = "survival_rounds"
# 地图单元格属性
MAP_PLANT = "plantnames"
MAP_SLEEP = "sleep" # 有没有休眠的蘑菇,作是否能种植咖啡豆的判断
MAP_PLOT_TYPE = "plotType"
MAP_PLOT_TYPE = "plot_type"
# 地图单元格区域类型
MAP_GRASS = "grass"
MAP_WATER = "water"
@ -231,7 +231,7 @@ CARD_MOVE_TIME = 60
CAR = "car"
SUN = "Sun"
# plant子类非植物对象
# plant子类非植物对象(这里的是不包括阳光、子弹的拟植物对象)
NON_PLANT_OBJECTS = {
HOLE := "Hole",
ICEFROZENPLOT := "IceFrozenPlot",
@ -376,15 +376,14 @@ PLANT_CARD_INFO = (# 元组 (植物名称, 卡片名称, 阳光, 冷却时间)
)
# 卡片中的植物名称与索引序号的对应关系,指定名称以得到索引值
PLANT_CARD_INDEX={}
for i, item in enumerate(PLANT_CARD_INFO):
PLANT_CARD_INDEX[item[PLANT_NAME_INDEX]] = i
PLANT_CARD_INDEX={item[PLANT_NAME_INDEX]: index for (index, item) in enumerate(PLANT_CARD_INFO)}
# 指定了哪些卡可选(排除坚果保龄球特殊植物)
CARDS_TO_CHOOSE = range(len(PLANT_CARD_INFO) - 3)
# 植物集体属性集合
# 也许以后有必要的可以重新加入到对象的属性中
# 在生效时不用与僵尸进行碰撞检测的对象(即生效时不可发生被僵尸啃食的事件)
SKIP_ZOMBIE_COLLISION_CHECK_WHEN_WORKING = {
# 注意爆炸坚果的触发也是啃食类碰撞,因此只能算作爆炸后不检测
@ -445,6 +444,18 @@ ASH_PLANTS_AND_ICESHROOM = {
ICESHROOM,
}
# 白天要睡觉的植物
CAN_SLEEP_PLANTS = {
PUFFSHROOM, SUNSHROOM,
FUMESHROOM, HYPNOSHROOM,
SCAREDYSHROOM, ICESHROOM,
DOOMSHROOM, SEASHROOM,
}
# 选卡不推荐选择理由
REASON_WILL_SLEEP = 1
REASON_SLEEP_BUT_COFFEE_BEAN = 2
REASON_OTHER = 3
# 植物生命值
PLANT_HEALTH = 300
@ -482,7 +493,7 @@ FUME = "Fume"
# 子弹伤害
BULLET_DAMAGE_NORMAL = 20
BULLET_DAMAGE_FIREBALL_BODY = 27 # 这是火球本体的伤害注意不是40本体(27) + 溅射(13)才是40
BULLET_DAMAGE_FIREBALL_RANGE = 13
BULLET_DAMAGE_FIREBALL_RANGE = 13 # 原版溅射伤害会随着僵尸数量增多而减少,这里相当于做了一个增强
# 子弹效果
BULLET_EFFECT_ICE = "ice"
BULLET_EFFECT_UNICE = "unice"

View File

@ -26,6 +26,7 @@ class Level(tool.State):
# 导入地图参数
self.loadMap()
self.map = map.Map(self.map_data[c.BACKGROUND_TYPE])
self.map_x_len = self.map.width
self.map_y_len = self.map.height
self.setupBackground()
self.initState()
@ -101,7 +102,7 @@ class Level(tool.State):
# 按照规则生成每一波僵尸
# 可以考虑将波刷新和一波中的僵尸生成分开
# 将波刷新和一波中的僵尸生成分开
# useableZombie是指可用的僵尸种类的元组
# inevitableZombie指在本轮必然出现的僵尸输入形式为字典: {波数1:(僵尸1, 僵尸2……), 波数2:(僵尸1, 僵尸2……)……}
def createWaves(self, useable_zombies, num_flags, survival_rounds=0, inevitable_zombie_dict=None):
@ -265,7 +266,7 @@ class Level(tool.State):
c.SOUND_ZOMBIE_COMING.play()
return
if (self.wave_num % 10 != 9):
if ((current_time - self.wave_time >= 25000 + random.randint(0, 6000)) or (self.bar_type != c.CHOOSEBAR_STATIC and current_time - self.wave_time >= 12500 + random.randint(0, 3000))):
if ((current_time - self.wave_time >= 25000 + random.randint(0, 6000)) or (self.bar_type == c.CHOOSEBAR_BOWLING and current_time - self.wave_time >= 12500 + random.randint(0, 3000))):
self.wave_num += 1
self.wave_time = current_time
self.wave_zombies = self.waves[self.wave_num - 1]
@ -332,8 +333,8 @@ class Level(tool.State):
return self.before_pause_time
def initBowlingMap(self):
for x in range(3, self.map.width):
for y in range(self.map.height):
for x in range(3, self.map_x_len):
for y in range(self.map_y_len):
self.map.setMapGridType(x, y, c.MAP_UNAVAILABLE) # 将坚果保龄球红线右侧设置为不可种植任何植物
def initState(self):
@ -354,7 +355,7 @@ class Level(tool.State):
def initChoose(self):
self.state = c.CHOOSE
self.panel = menubar.Panel(c.CARDS_TO_CHOOSE, self.map_data[c.INIT_SUN_NAME])
self.panel = menubar.Panel(c.CARDS_TO_CHOOSE, self.map_data[c.INIT_SUN_NAME], self.background_type)
# 播放选卡音乐
pg.mixer.music.stop()
@ -631,6 +632,8 @@ class Level(tool.State):
def shovelRemovePlant(self, mouse_pos):
x, y = mouse_pos
map_x, map_y = self.map.getMapIndex(x, y)
if not self.map.isValid(map_x, map_y):
return
for i in self.plant_groups[map_y]:
if (x >= i.rect.x and x <= i.rect.right and
y >= i.rect.y and y <= i.rect.bottom):
@ -924,7 +927,8 @@ class Level(tool.State):
new_plant = plant.GiantWallNut(x, y)
if new_plant.can_sleep and self.background_type in c.DAYTIME_BACKGROUNDS:
if ((new_plant.name in c.CAN_SLEEP_PLANTS)
and (self.background_type in c.DAYTIME_BACKGROUNDS)):
new_plant.setSleep()
mushroom_sleep = True
else:
@ -1081,7 +1085,7 @@ class Level(tool.State):
# 默认为最右侧的一个植物
target_plant = max(attackable_common_plants, key=lambda i: i.rect.x)
map_x, map_y = self.map.getMapIndex(target_plant.rect.centerx, target_plant.rect.centery)
if not (map_x >= self.map.width or map_y >= self.map.height):
if self.map.isValid(map_x, map_y):
if c.PUMPKINHEAD in self.map.map[map_y][map_x][c.MAP_PLANT]:
for actual_target_plant in self.plant_groups[i]:
# 检测同一格的其他植物
@ -1115,7 +1119,8 @@ class Level(tool.State):
zombie.health = 0
c.SOUND_BOWLING_IMPACT.play()
elif not zombie.jumping:
zombie.jump_map_x, zombie.jump_map_y = min(c.GRID_X_LEN - 1, zombie.prey_map_x), min(self.map_y_len - 1, zombie.prey_map_y)
zombie.jump_map_x = min(self.map_x_len - 1, zombie.prey_map_x)
zombie.jump_map_y = min(self.map_y_len - 1, zombie.prey_map_y)
jump_x = target_plant.rect.x - c.GRID_X_SIZE * 0.6
if c.TALLNUT in self.map.map[zombie.jump_map_y][zombie.jump_map_x][c.MAP_PLANT]:
zombie.setJump(False, jump_x)
@ -1467,6 +1472,8 @@ class Level(tool.State):
# 铲子接近植物时会高亮提示
map_x, map_y = self.map.getMapIndex(x, y)
surface.blit(self.shovel, self.shovel_rect)
if not self.map.isValid(map_x, map_y):
return
for i in self.plant_groups[map_y]:
if (x >= i.rect.x and x <= i.rect.right and
y >= i.rect.y and y <= i.rect.bottom):

View File

@ -1,5 +1,6 @@
import os
import pygame as pg
from abc import abstractmethod
from .. import tool
from .. import constants as c
@ -7,6 +8,7 @@ class Screen(tool.State):
def __init__(self):
tool.State.__init__(self)
@abstractmethod
def startup(self, current_time, persist):
pass

View File

@ -1,9 +1,11 @@
import logging
import os
import json
from abc import abstractmethod
import pygame as pg
from pygame.locals import *
from . import constants as c
logger = logging.getLogger("main")
# 状态机 抽象基类
class State():
@ -31,15 +33,15 @@ class State():
# 工具:范围判断函数,用于判断点击
def inArea(self, rect, x, y):
if (x >= rect.x and x <= rect.right and
y >= rect.y and y <= rect.bottom):
if (rect.x <= x <= rect.right and
rect.y <= y <= rect.bottom):
return True
else:
return False
# 工具:用户数据保存函数
def saveUserData(self):
with open(c.USERDATA_PATH, "w") as f:
with open(c.USERDATA_PATH, "w", encoding="utf-8") as f:
userdata = {}
for i in self.game_info:
if i in c.INIT_USERDATA:
@ -60,12 +62,18 @@ class Control():
self.state_dict = {}
self.state_name = None
self.state = None
# 这里需要考虑多种情况如文件不存在、文件不可读、文件不符合JSON语法要求这些情况目前暂定统一进行新建文件操作
# 因此仍然采用try-except实现而非if-else实现
try:
# 存在存档即导入
with open(c.USERDATA_PATH) as f:
# 先自动修复读写权限(Python权限规则和Unix不一样420表示unix的644Windows自动忽略不支持项)
os.chmod(c.USERDATA_PATH, 420)
with open(c.USERDATA_PATH, encoding="utf-8") as f:
userdata = json.load(f)
except FileNotFoundError:
self.setupUserData()
except json.JSONDecodeError:
logger.warning("用户存档解码错误!程序将新建初始存档!\n")
self.setupUserData()
else: # 没有引发异常才执行
self.game_info = {}
# 导入数据,保证了可运行性,但是放弃了数据向后兼容性,即假如某些变量在以后改名,在导入时可能会被重置
need_to_rewrite = False
@ -76,23 +84,23 @@ class Control():
self.game_info[key] = c.INIT_USERDATA[key]
need_to_rewrite = True
if need_to_rewrite:
with open(c.USERDATA_PATH, "w") as f:
with open(c.USERDATA_PATH, "w", encoding="utf-8") as f:
savedata = json.dumps(self.game_info, sort_keys=True, indent=4)
f.write(savedata)
except:
if not os.path.exists(os.path.dirname(c.USERDATA_PATH)):
os.makedirs(os.path.dirname(c.USERDATA_PATH))
with open(c.USERDATA_PATH, "w") as f:
savedata = json.dumps(c.INIT_USERDATA, sort_keys=True, indent=4)
f.write(savedata)
self.game_info = c.INIT_USERDATA.copy() # 内部全是不可变对象,浅拷贝即可
# 存档内不包含即时游戏时间信息,需要新建
self.game_info[c.CURRENT_TIME] = 0
# 50为目前的基础帧率乘以倍率即是游戏帧率
self.fps = 50 * self.game_info[c.GAME_RATE]
def setupUserData(self):
if not os.path.exists(os.path.dirname(c.USERDATA_PATH)):
os.makedirs(os.path.dirname(c.USERDATA_PATH))
with open(c.USERDATA_PATH, "w", encoding="utf-8") as f:
savedata = json.dumps(c.INIT_USERDATA, sort_keys=True, indent=4)
f.write(savedata)
self.game_info = c.INIT_USERDATA.copy() # 内部全是不可变对象,浅拷贝即可
def setup_states(self, state_dict, start_state):
self.state_dict = state_dict
self.state_name = start_state
@ -137,8 +145,7 @@ class Control():
self.mouse_pos = pg.mouse.get_pos()
self.mouse_click[0], _, self.mouse_click[1] = pg.mouse.get_pressed()
# self.mouse_click[0]表示左键self.mouse_click[1]表示右键
print( f"点击位置: ({self.mouse_pos[0]:3}, {self.mouse_pos[1]:3})",
f"左右键点击情况: {self.mouse_click}")
print(f"点击位置: ({self.mouse_pos[0]:3}, {self.mouse_pos[1]:3}) 左右键点击情况: {self.mouse_click}")
def run(self):