Merge branch 'master' into 面向对象化
6
.github/workflows/build-pr.yml
vendored
@ -15,7 +15,6 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
python_version:
|
python_version:
|
||||||
- "3.11"
|
- "3.11"
|
||||||
- "3.10"
|
|
||||||
name: Windows Python ${{ matrix.python_version }}
|
name: Windows Python ${{ matrix.python_version }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@ -75,7 +74,7 @@ jobs:
|
|||||||
--include-data-file=c:\hostedtoolcache\windows\python\${{ matrix.python_version }}*\x64\lib\site-packages\pygame\libvorbisfile-3.dll=libvorbisfile-3.dll `
|
--include-data-file=c:\hostedtoolcache\windows\python\${{ matrix.python_version }}*\x64\lib\site-packages\pygame\libvorbisfile-3.dll=libvorbisfile-3.dll `
|
||||||
--include-data-file=c:\hostedtoolcache\windows\python\${{ matrix.python_version }}*\x64\lib\site-packages\pygame\libvorbis-0.dll=libvorbis-0.dll `
|
--include-data-file=c:\hostedtoolcache\windows\python\${{ matrix.python_version }}*\x64\lib\site-packages\pygame\libvorbis-0.dll=libvorbis-0.dll `
|
||||||
--windows-disable-console `
|
--windows-disable-console `
|
||||||
-o ./out/pypvz-with-python${{ matrix.python_version }}-nuitka-windows-x64.exe `
|
-o pypvz-with-python${{ matrix.python_version }}-nuitka-windows-x64.exe `
|
||||||
pypvz.py
|
pypvz.py
|
||||||
|
|
||||||
- name: Release the version built by nuitka
|
- name: Release the version built by nuitka
|
||||||
@ -83,7 +82,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
allowUpdates: true
|
allowUpdates: true
|
||||||
tag: Dev
|
tag: Dev
|
||||||
artifacts: ./out/*nuitka*.exe
|
artifacts: ./*nuitka*.exe
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
@ -93,7 +92,6 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python_version:
|
python_version:
|
||||||
- "3.10"
|
|
||||||
- "3.11"
|
- "3.11"
|
||||||
name: Ubuntu Python ${{ matrix.python_version }}
|
name: Ubuntu Python ${{ matrix.python_version }}
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
6
.github/workflows/build.yml
vendored
@ -17,7 +17,6 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
python_version:
|
python_version:
|
||||||
- "3.11"
|
- "3.11"
|
||||||
- "3.10"
|
|
||||||
name: Windows Python ${{ matrix.python_version }}
|
name: Windows Python ${{ matrix.python_version }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@ -77,7 +76,7 @@ jobs:
|
|||||||
--include-data-file=c:\hostedtoolcache\windows\python\${{ matrix.python_version }}*\x64\lib\site-packages\pygame\libvorbisfile-3.dll=libvorbisfile-3.dll `
|
--include-data-file=c:\hostedtoolcache\windows\python\${{ matrix.python_version }}*\x64\lib\site-packages\pygame\libvorbisfile-3.dll=libvorbisfile-3.dll `
|
||||||
--include-data-file=c:\hostedtoolcache\windows\python\${{ matrix.python_version }}*\x64\lib\site-packages\pygame\libvorbis-0.dll=libvorbis-0.dll `
|
--include-data-file=c:\hostedtoolcache\windows\python\${{ matrix.python_version }}*\x64\lib\site-packages\pygame\libvorbis-0.dll=libvorbis-0.dll `
|
||||||
--windows-disable-console `
|
--windows-disable-console `
|
||||||
-o ./out/pypvz-with-python${{ matrix.python_version }}-nuitka-windows-x64.exe `
|
-o pypvz-with-python${{ matrix.python_version }}-nuitka-windows-x64.exe `
|
||||||
pypvz.py
|
pypvz.py
|
||||||
|
|
||||||
|
|
||||||
@ -86,7 +85,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
allowUpdates: true
|
allowUpdates: true
|
||||||
tag: Latest
|
tag: Latest
|
||||||
artifacts: ./out/*nuitka*.exe
|
artifacts: ./*nuitka*.exe
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
linux:
|
linux:
|
||||||
@ -95,7 +94,6 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python_version:
|
python_version:
|
||||||
- "3.10"
|
|
||||||
- "3.11"
|
- "3.11"
|
||||||
name: Ubuntu Python ${{ matrix.python_version }}
|
name: Ubuntu Python ${{ matrix.python_version }}
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
46
README.md
@ -295,29 +295,29 @@ pyinstaller -F pypvz.py `
|
|||||||
|
|
||||||
## 截屏
|
## 截屏
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
## 关于日志与反馈
|
## 关于日志与反馈
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 143 KiB |
|
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 119 KiB |
@ -5,7 +5,7 @@ from .. import constants as c
|
|||||||
|
|
||||||
|
|
||||||
class Car(pg.sprite.Sprite):
|
class Car(pg.sprite.Sprite):
|
||||||
def __init__(self, x, y, map_y):
|
def __init__(self, x:int, y:int, map_y:int):
|
||||||
pg.sprite.Sprite.__init__(self)
|
pg.sprite.Sprite.__init__(self)
|
||||||
|
|
||||||
rect = tool.GFX[c.CAR].get_rect()
|
rect = tool.GFX[c.CAR].get_rect()
|
||||||
@ -19,7 +19,7 @@ class Car(pg.sprite.Sprite):
|
|||||||
self.state = c.IDLE
|
self.state = c.IDLE
|
||||||
self.dead = False
|
self.dead = False
|
||||||
|
|
||||||
def update(self, game_info):
|
def update(self, game_info:dict):
|
||||||
self.current_time = game_info[c.CURRENT_TIME]
|
self.current_time = game_info[c.CURRENT_TIME]
|
||||||
if self.state == c.WALK:
|
if self.state == c.WALK:
|
||||||
self.rect.x += 5
|
self.rect.x += 5
|
||||||
@ -37,9 +37,9 @@ class Car(pg.sprite.Sprite):
|
|||||||
|
|
||||||
# 豌豆及孢子类普通子弹
|
# 豌豆及孢子类普通子弹
|
||||||
class Bullet(pg.sprite.Sprite):
|
class Bullet(pg.sprite.Sprite):
|
||||||
def __init__( self, x, start_y, dest_y, name, damage,
|
def __init__( self, x:int, start_y:int, dest_y:int, name:str, damage:int,
|
||||||
effect=None, passed_torchwood_x=None,
|
effect:str=None, passed_torchwood_x:int=None,
|
||||||
damage_type=c.ZOMBIE_DEAFULT_DAMAGE):
|
damage_type:str=c.ZOMBIE_DEAFULT_DAMAGE):
|
||||||
pg.sprite.Sprite.__init__(self)
|
pg.sprite.Sprite.__init__(self)
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|||||||
@ -8,7 +8,7 @@ class Menu(tool.State):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
tool.State.__init__(self)
|
tool.State.__init__(self)
|
||||||
|
|
||||||
def startup(self, current_time, persist):
|
def startup(self, current_time:int, persist):
|
||||||
self.next = c.LEVEL
|
self.next = c.LEVEL
|
||||||
self.persist = persist
|
self.persist = persist
|
||||||
self.game_info = persist
|
self.game_info = persist
|
||||||
@ -88,7 +88,7 @@ class Menu(tool.State):
|
|||||||
self.adventure_clicked = False
|
self.adventure_clicked = False
|
||||||
self.option_button_clicked = False
|
self.option_button_clicked = False
|
||||||
|
|
||||||
def checkHilight(self, x, y):
|
def checkHilight(self, x:int, y:int):
|
||||||
# 高亮冒险模式按钮
|
# 高亮冒险模式按钮
|
||||||
if self.inArea(self.adventure_rect, x, y):
|
if self.inArea(self.adventure_rect, x, y):
|
||||||
self.adventure_highlight_time = self.current_time
|
self.adventure_highlight_time = self.current_time
|
||||||
@ -112,7 +112,7 @@ class Menu(tool.State):
|
|||||||
self.littleGame_image = self.chooseHilightImage(self.littleGame_highlight_time, self.littleGame_frames)
|
self.littleGame_image = self.chooseHilightImage(self.littleGame_highlight_time, self.littleGame_frames)
|
||||||
self.help_image = self.chooseHilightImage(self.help_hilight_time, self.help_frames)
|
self.help_image = self.chooseHilightImage(self.help_hilight_time, self.help_frames)
|
||||||
|
|
||||||
def chooseHilightImage(self, hilightTime, frames):
|
def chooseHilightImage(self, hilightTime:int, frames):
|
||||||
if (self.current_time - hilightTime) < 80:
|
if (self.current_time - hilightTime) < 80:
|
||||||
index= 1
|
index= 1
|
||||||
else:
|
else:
|
||||||
@ -206,7 +206,7 @@ class Menu(tool.State):
|
|||||||
self.sunflower_trophy_rect.y = 280
|
self.sunflower_trophy_rect.y = 280
|
||||||
self.sunflower_trophy_show_info_time = 0
|
self.sunflower_trophy_show_info_time = 0
|
||||||
|
|
||||||
def checkSunflowerTrophyInfo(self, surface, x, y):
|
def checkSunflowerTrophyInfo(self, surface:pg.Surface, x:int, y:int):
|
||||||
if self.inArea(self.sunflower_trophy_rect, x, y):
|
if self.inArea(self.sunflower_trophy_rect, x, y):
|
||||||
self.sunflower_trophy_show_info_time = self.current_time
|
self.sunflower_trophy_show_info_time = self.current_time
|
||||||
if (self.current_time - self.sunflower_trophy_show_info_time) < 80:
|
if (self.current_time - self.sunflower_trophy_show_info_time) < 80:
|
||||||
@ -228,7 +228,7 @@ class Menu(tool.State):
|
|||||||
# 播放点击音效
|
# 播放点击音效
|
||||||
c.SOUND_BUTTON_CLICK.play()
|
c.SOUND_BUTTON_CLICK.play()
|
||||||
|
|
||||||
def showCurrentVolumeImage(self, surface):
|
def showCurrentVolumeImage(self, surface:pg.Surface):
|
||||||
# 由于音量可变,因此这一内容不能在一开始就结束加载,而应当不断刷新不断显示
|
# 由于音量可变,因此这一内容不能在一开始就结束加载,而应当不断刷新不断显示
|
||||||
font = pg.font.Font(c.FONT_PATH, 30)
|
font = pg.font.Font(c.FONT_PATH, 30)
|
||||||
volume_tips = font.render(f"音量:{round(self.game_info[c.SOUND_VOLUME]*100):3}%", True, c.LIGHTGRAY)
|
volume_tips = font.render(f"音量:{round(self.game_info[c.SOUND_VOLUME]*100):3}%", True, c.LIGHTGRAY)
|
||||||
@ -237,7 +237,7 @@ class Menu(tool.State):
|
|||||||
volume_tips_rect.y = 247
|
volume_tips_rect.y = 247
|
||||||
surface.blit(volume_tips, volume_tips_rect)
|
surface.blit(volume_tips, volume_tips_rect)
|
||||||
|
|
||||||
def update(self, surface, current_time, mouse_pos, mouse_click):
|
def update(self, surface:pg.Surface, current_time:int, mouse_pos:list, mouse_click):
|
||||||
self.current_time = self.game_info[c.CURRENT_TIME] = current_time
|
self.current_time = self.game_info[c.CURRENT_TIME] = current_time
|
||||||
|
|
||||||
surface.blit(self.bg_image, self.bg_rect)
|
surface.blit(self.bg_image, self.bg_rect)
|
||||||
|
|||||||
@ -18,7 +18,7 @@ class State():
|
|||||||
|
|
||||||
# 当从其他状态进入这个状态时,需要进行的初始化操作
|
# 当从其他状态进入这个状态时,需要进行的初始化操作
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def startup(self, current_time, persist):
|
def startup(self, current_time:int, persist:dict):
|
||||||
# 前面加了@abstractmethod表示抽象基类中必须要重新定义的method(method是对象和函数的结合)
|
# 前面加了@abstractmethod表示抽象基类中必须要重新定义的method(method是对象和函数的结合)
|
||||||
pass
|
pass
|
||||||
# 当从这个状态退出时,需要进行的清除操作
|
# 当从这个状态退出时,需要进行的清除操作
|
||||||
@ -27,12 +27,12 @@ class State():
|
|||||||
return self.persist
|
return self.persist
|
||||||
# 在这个状态运行时进行的更新操作
|
# 在这个状态运行时进行的更新操作
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def update(self, surface, keys, current_time):
|
def update(self, surface:pg.Surface, keys, current_time:int):
|
||||||
# 前面加了@abstractmethod表示抽象基类中必须要重新定义的method
|
# 前面加了@abstractmethod表示抽象基类中必须要重新定义的method
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# 工具:范围判断函数,用于判断点击
|
# 工具:范围判断函数,用于判断点击
|
||||||
def inArea(self, rect, x, y):
|
def inArea(self, rect:pg.Rect, x:int, y:int):
|
||||||
if (rect.x <= x <= rect.right and
|
if (rect.x <= x <= rect.right and
|
||||||
rect.y <= y <= rect.bottom):
|
rect.y <= y <= rect.bottom):
|
||||||
return True
|
return True
|
||||||
@ -101,7 +101,7 @@ class Control():
|
|||||||
f.write(savedata)
|
f.write(savedata)
|
||||||
self.game_info = c.INIT_USERDATA.copy() # 内部全是不可变对象,浅拷贝即可
|
self.game_info = c.INIT_USERDATA.copy() # 内部全是不可变对象,浅拷贝即可
|
||||||
|
|
||||||
def setup_states(self, state_dict, start_state):
|
def setup_states(self, state_dict:dict, start_state):
|
||||||
self.state_dict = state_dict
|
self.state_dict = state_dict
|
||||||
self.state_name = start_state
|
self.state_name = start_state
|
||||||
self.state = self.state_dict[self.state_name]
|
self.state = self.state_dict[self.state_name]
|
||||||
@ -155,7 +155,8 @@ class Control():
|
|||||||
pg.display.update()
|
pg.display.update()
|
||||||
self.clock.tick(self.fps)
|
self.clock.tick(self.fps)
|
||||||
|
|
||||||
def get_image(sheet, x, y, width, height, colorkey=c.BLACK, scale=1):
|
def get_image( sheet:pg.Surface, x:int, y:int, width:int, height:int,
|
||||||
|
colorkey:tuple[int]=c.BLACK, scale:int=1) -> pg.Surface:
|
||||||
# 不保留alpha通道的图片导入
|
# 不保留alpha通道的图片导入
|
||||||
image = pg.Surface([width, height])
|
image = pg.Surface([width, height])
|
||||||
rect = image.get_rect()
|
rect = image.get_rect()
|
||||||
@ -168,19 +169,21 @@ def get_image(sheet, x, y, width, height, colorkey=c.BLACK, scale=1):
|
|||||||
int(rect.height*scale)))
|
int(rect.height*scale)))
|
||||||
return image
|
return image
|
||||||
|
|
||||||
def get_image_alpha(sheet, x, y, width, height, colorkey=c.BLACK, scale=1):
|
def get_image_alpha(sheet:pg.Surface, x:int, y:int, width:int, height:int,
|
||||||
# 保留alpha通道的图片导入
|
colorkey:tuple[int]=c.BLACK, scale:int=1) -> pg.Surface:
|
||||||
image = pg.Surface([width, height], SRCALPHA)
|
# 保留alpha通道的图片导入
|
||||||
rect = image.get_rect()
|
image = pg.Surface([width, height], SRCALPHA)
|
||||||
|
rect = image.get_rect()
|
||||||
|
|
||||||
image.blit(sheet, (0, 0), (x, y, width, height))
|
image.blit(sheet, (0, 0), (x, y, width, height))
|
||||||
image.set_colorkey(colorkey)
|
image.set_colorkey(colorkey)
|
||||||
image = pg.transform.scale(image,
|
image = pg.transform.scale(image,
|
||||||
(int(rect.width*scale),
|
(int(rect.width*scale),
|
||||||
int(rect.height*scale)))
|
int(rect.height*scale)))
|
||||||
return image
|
return image
|
||||||
|
|
||||||
def load_image_frames(directory, image_name, colorkey, accept):
|
def load_image_frames( directory:str, image_name:str,
|
||||||
|
colorkey:tuple[int], accept:tuple[str]) -> list[pg.Surface]:
|
||||||
frame_list = []
|
frame_list = []
|
||||||
tmp = {}
|
tmp = {}
|
||||||
# image_name is "Peashooter", pic name is "Peashooter_1", get the index 1
|
# image_name is "Peashooter", pic name is "Peashooter_1", get the index 1
|
||||||
@ -204,7 +207,8 @@ def load_image_frames(directory, image_name, colorkey, accept):
|
|||||||
return frame_list
|
return frame_list
|
||||||
|
|
||||||
# colorkeys 是设置图像中的某个颜色值为透明,这里用来消除白边
|
# colorkeys 是设置图像中的某个颜色值为透明,这里用来消除白边
|
||||||
def load_all_gfx(directory, colorkey=c.WHITE, accept=(".png", ".jpg", ".bmp", ".gif", ".webp")):
|
def load_all_gfx( directory:str, colorkey:tuple[int]=c.WHITE,
|
||||||
|
accept:tuple[str]=(".png", ".jpg", ".bmp", ".gif", ".webp")) -> dict[str:pg.Surface]:
|
||||||
graphics = {}
|
graphics = {}
|
||||||
for name1 in os.listdir(directory):
|
for name1 in os.listdir(directory):
|
||||||
# subfolders under the folder resources\graphics
|
# subfolders under the folder resources\graphics
|
||||||
|
|||||||