diff --git a/modules/TankGame.py b/modules/TankGame.py new file mode 100644 index 0000000..92f9013 --- /dev/null +++ b/modules/TankGame.py @@ -0,0 +1,147 @@ +import threading + +import pygame +import sys +import os +import random + + +class TankGame(object): + _instance_lock = threading.Lock() + _init_flag = False + + def __init__(self, config=None): + if not self._init_flag: + if not config: + raise Exception('Config was not specified while initializing game instance!') + + self.__screen = None + self.__levels = None + self.__sounds = {} + self.__multiplayer_mode = False + self.__config = config + self.__is_win = False + self.__quit_game_flag = False + self._init_flag = True + self.__last_level = None + self.__start() + + def __new__(cls, *args, **kwargs): + if not hasattr(TankGame, "_instance"): + with TankGame._instance_lock: + if not hasattr(TankGame, "_instance"): + TankGame._instance = object.__new__(cls) + return TankGame._instance + + @property + def sounds(self): + return self.__sounds + + @property + def screen(self): + return self.__screen + + @property + def quit_game_flag(self): + return self.__quit_game_flag + + @quit_game_flag.setter + def quit_game_flag(self, quit_game_flag): + self.__quit_game_flag = quit_game_flag + + @property + def config(self): + return self.__config + + @property + def level(self): + return self.__level + + @property + def level_file(self): + + weights = [1] * len(self.__levels) + if self.__last_level is not None: + weights[self.__last_level] = 0 + + self.__last_level = random.choices(range(len(self.__levels)), weights=weights)[0] + return self.__levels[self.__last_level] + # return self.__levels[self.__level] + + @property + def is_win(self): + return self.__is_win + + @is_win.setter + def is_win(self, is_win): + self.__is_win = is_win + + @property + def multiplayer_mode(self): + return self.__multiplayer_mode + + @multiplayer_mode.setter + def multiplayer_mode(self, multiplayer_mode): + self.__multiplayer_mode = multiplayer_mode + + def init_game_window(self, size_tuple=None): + if size_tuple is None: + self.__screen = pygame.display.set_mode( + (self.config.WIDTH, self.config.HEIGHT) + ) + else: + self.__screen = pygame.display.set_mode(size_tuple) + + def __init_sounds(self): + for sound, file in self.config.AUDIO_PATHS.items(): + self.__sounds[sound] = pygame.mixer.Sound(file) + self.__sounds[sound].set_volume(1) + + def __load_level_templates(self): + self.__levels = [ + os.path.join( + self.config.LEVELFILEDIR, + filename + ) for filename in sorted(os.listdir(self.config.LEVELFILEDIR)) + ] + + def __enter_loop(self): + from modules.views import ViewManager + ViewManager().show('GameStart') + level = 0 + while True: + self.__level = level + ViewManager().show('SwitchLevel') + ViewManager().show('GameLevelView') + if not self.is_win: + ViewManager().show('GameOver') + level = 0 + else: + level += 1 + if self.quit_game_flag: + break + + + # while True: + # for level in range(len(self.__levels)): + # self.__level = level + # ViewManager().show('SwitchLevel') + # ViewManager().show('GameLevelView') + # if not self.is_win: + # break + # + # ViewManager().show('GameOver') + # if self.quit_game_flag: + # break + + def __init_game(self): + pygame.init() + pygame.mixer.init() + pygame.display.set_caption(self.config.TITLE) + self.init_game_window() + self.__init_sounds() + self.__load_level_templates() + + def __start(self): + self.__init_game() + self.__enter_loop() diff --git a/__init__.py b/modules/__init__.py similarity index 100% rename from __init__.py rename to modules/__init__.py diff --git a/modules/__pycache__/TankGame.cpython-310.pyc b/modules/__pycache__/TankGame.cpython-310.pyc new file mode 100644 index 0000000..dad2021 Binary files /dev/null and b/modules/__pycache__/TankGame.cpython-310.pyc differ diff --git a/modules/__pycache__/TankGame.cpython-312.pyc b/modules/__pycache__/TankGame.cpython-312.pyc new file mode 100644 index 0000000..e113caa Binary files /dev/null and b/modules/__pycache__/TankGame.cpython-312.pyc differ diff --git a/modules/__pycache__/TankGame.cpython-38.pyc b/modules/__pycache__/TankGame.cpython-38.pyc new file mode 100644 index 0000000..2813a14 Binary files /dev/null and b/modules/__pycache__/TankGame.cpython-38.pyc differ diff --git a/modules/__pycache__/__init__.cpython-310.pyc b/modules/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..d36ddcf Binary files /dev/null and b/modules/__pycache__/__init__.cpython-310.pyc differ diff --git a/modules/__pycache__/__init__.cpython-312.pyc b/modules/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..701af12 Binary files /dev/null and b/modules/__pycache__/__init__.cpython-312.pyc differ diff --git a/modules/__pycache__/__init__.cpython-38.pyc b/modules/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..843167c Binary files /dev/null and b/modules/__pycache__/__init__.cpython-38.pyc differ diff --git a/modules/levels/1.lvl b/modules/levels/1.lvl new file mode 100644 index 0000000..732bbda --- /dev/null +++ b/modules/levels/1.lvl @@ -0,0 +1,39 @@ +############################################## +# Explain: # +# Grid: 26 * 26 # +# Grid SIZE: 24 * 24 pixels # +# S: Space B: Brick I: Iron # +# R: River1/2 C: Ice T: Tree # +############################################## +%TOTALENEMYNUM:1 +%MAXENEMYNUM:6 +%HOMEPOS:12,24 +%HOMEAROUNDPOS:11,23 12,23 13,23 14,23 11,24 14,24 11,25 14,25 +%PLAYERTANKPOS:8,24 16,24 +%ENEMYTANKPOS:0,0 12,0 24,0 +S S S S S S S S S S S S S S S S S S S S S S S S S S +S S S S S S S S S S S S S S S S S S S S S S S S S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B I I B B S S B B S S B B S S +S S B B S S B B S S B B I I B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S S S S S S S S S B B S S B B S S +S S B B S S B B S S S S S S S S S S B B S S B B S S +S S S S S S S S S S B B S S B B S S S S S S S S S S +S S S S S S S S S S B B S S B B S S S S S S S S S S +S S S S B B B B S S S S S S S S S S B B B B S S S S +I I S S B B B B S S S S S S S S S S B B B B S S I I +S S S S S S S S S S B B S S B B S S S S S S S S S S +S S S S S S S S S S B B B B B B S S S S S S S S S S +S S B B S S B B S S B B B B B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S S S S S S S S S B B S S B B S S +S S B B S S B B S S S S S S S S S S B B S S B B S S +S S B B S S B B S S S B B B B S S S B B S S B B S S +S S S S S S S S S S S B S S B S S S S S S S S S S S +S S S S S S S S S S S B S S B S S S S S S S S S S S \ No newline at end of file diff --git a/modules/levels/2.lvl b/modules/levels/2.lvl new file mode 100644 index 0000000..65277c2 --- /dev/null +++ b/modules/levels/2.lvl @@ -0,0 +1,39 @@ +############################################## +# Explain: # +# Grid: 26 * 26 # +# Grid SIZE: 24 * 24 pixels # +# S: Space B: Brick I: Iron # +# R: River1/2 C: Ice T: Tree # +############################################## +%TOTALENEMYNUM:45 +%MAXENEMYNUM:9 +%HOMEPOS:12,24 +%HOMEAROUNDPOS:11,23 12,23 13,23 14,23 11,24 14,24 11,25 14,25 +%PLAYERTANKPOS:8,24 16,24 +%ENEMYTANKPOS:0,0 12,0 24,0 +S S S S S S S S S S S S S S S S S S S S S S S S S S +S S S S S S S S S S S S S S S S S S S S S S S S S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B I I B B S S B B S S B B S S +S S B B S S B B S S B B I I B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S S S S S S S S S B B S S B B S S +S S B B S S B B S S S S S S S S S S B B S S B B S S +S S S S S S S S S S R R R R R R S S S S S S S S S S +S S S S S S S S S S R R R R R R S S S S S S S S S S +S S S S T T T T S S S S S S S S S S T T T T S S S S +I I S S T T T T S S S S S S S S S S T T T T S S I I +S S S S S S S S S S B B S S B B S S S S S S S S S S +S S S S S S S S S S B B B B B B S S S S S S S S S S +S S B B S S B B S S B B B B B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S S S S S S S S S B B S S B B S S +S S B B S S B B S S S S S S S S S S B B S S B B S S +S S B B S S B B S S S B B B B S S S B B S S B B S S +S S S S S S S S S S S B S S B S S S S S S S S S S S +S S S S S S S S S S S B S S B S S S S S S S S S S S \ No newline at end of file diff --git a/modules/levels/3.lvl b/modules/levels/3.lvl new file mode 100644 index 0000000..139e10f --- /dev/null +++ b/modules/levels/3.lvl @@ -0,0 +1,39 @@ +############################################## +# Explain: # +# Grid: 26 * 26 # +# Grid SIZE: 24 * 24 pixels # +# S: Space B: Brick I: Iron # +# R: River1/2 C: Ice T: Tree # +############################################## +%TOTALENEMYNUM:60 +%MAXENEMYNUM:12 +%HOMEPOS:12,24 +%HOMEAROUNDPOS:11,23 12,23 13,23 14,23 11,24 14,24 11,25 14,25 +%PLAYERTANKPOS:8,24 16,24 +%ENEMYTANKPOS:0,0 12,0 24,0 +S S S S S S S S S S S S S S S S S S S S S S S S S S +S S S S S S S S S S S S S S S S S S S S S S S S S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B I I B B S S B B S S B B S S +S S B B S S B B S S B B I I B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S S S S S S S S S B B S S B B S S +S S B B S S B B S S S S S S S S S S B B S S B B S S +S S S S S S S S S S R R R R R R S S S S S S S S S S +S S S S S S S S S S R R R R R R S S S S S S S S S S +S S S S C C C C S S S S S S S S S S C C C C S S S S +I I S S C C C C S S S S S S S S S S C C C C S S I I +S S S S S S S S S S B B S S B B S S S S S S S S S S +S S S S S S S S S S B B B B B B S S S S S S S S S S +S S B B S S B B S S B B B B B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S S S S S S S S S B B S S B B S S +S S B B S S B B S S S S S S S S S S B B S S B B S S +S S B B S S B B S S S B B B B S S S B B S S B B S S +S S S S S S S S S S S B S S B S S S S S S S S S S S +S S S S S S S S S S S B S S B S S S S S S S S S S S \ No newline at end of file diff --git a/modules/levels/4.lvl b/modules/levels/4.lvl new file mode 100644 index 0000000..bd1a454 --- /dev/null +++ b/modules/levels/4.lvl @@ -0,0 +1,39 @@ +############################################## +# Explain: # +# Grid: 26 * 26 # +# Grid SIZE: 24 * 24 pixels # +# S: Space B: Brick I: Iron # +# R: River1/2 C: Ice T: Tree # +############################################## +%TOTALENEMYNUM:60 +%MAXENEMYNUM:12 +%HOMEPOS:12,24 +%HOMEAROUNDPOS:11,23 12,23 13,23 14,23 11,24 14,24 11,25 14,25 +%PLAYERTANKPOS:8,24 16,24 +%ENEMYTANKPOS:0,0 12,0 24,0 +S S S S S S S S S S S S S S S S S S S S S S S S S S +S S S S S S S S S S S S S S S S S S S S S S S S S S +S S T T S S T T S S T T S S T T S S T T S S T T S S +S S T T S S T T S S T T S S T T S S T T S S T T S S +S S T T S S T T S S T T S S T T S S T T S S T T S S +S S T T S S T T S S T T S S T T S S T T S S T T S S +S S T T S S T T S S T T I I T T S S T T S S T T S S +S S T T S S T T S S T T I I T T S S T T S S T T S S +S S T T S S T T S S T T S S T T S S T T S S T T S S +T T T T T T T T T T T T T T T T T T T T T T T T T T +T T T T T T T T T T T T T T T T T T T T T T T T T T +R R R R R R R R R R R R R R R R R R R R R R R R R R +R R R R R R R R R R R R R R R R R R R R R R R R R R +S S S S C C C C S S S S S S S S S S C C C C S S S S +S S S S C C C C S S S S S S S S S S C C C C S S S S +S S S S S S S S S S B B S S B B S S S S S S S S S S +S S S S S S S S S S B B B B B B S S S S S S S S S S +S S B B S S B B S S B B B B B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S B B S S B B S S B B S S B B S S +S S B B S S B B S S S S S S S S S S B B S S B B S S +S S B B S S B B S S S S S S S S S S B B S S B B S S +S S B B S S B B S S S B B B B S S S B B S S B B S S +S S S S S S S S S S S B S S B S S S S S S S S S S S +S S S S S S S S S S S B S S B S S S S S S S S S S S \ No newline at end of file diff --git a/modules/levels/5.lvl b/modules/levels/5.lvl new file mode 100644 index 0000000..3c2797d --- /dev/null +++ b/modules/levels/5.lvl @@ -0,0 +1,39 @@ +############################################## +# Explain: # +# Grid: 26 * 26 # +# Grid SIZE: 24 * 24 pixels # +# S: Space B: Brick I: Iron # +# R: River1/2 C: Ice T: Tree # +############################################## +%TOTALENEMYNUM:60 +%MAXENEMYNUM:12 +%HOMEPOS:12,24 +%HOMEAROUNDPOS:11,23 12,23 13,23 14,23 11,24 14,24 11,25 14,25 +%PLAYERTANKPOS:8,24 16,24 +%ENEMYTANKPOS:0,0 12,0 24,0 +S S S S S S S S S S S S S S S S S S S S S S S S S S +S S S S S S S S S S S S S S S S S S S S S S S S S S +S S R R S S R R S S R R S S R R S S R R S S R R S S +S S R R S S R R S S R R S S R R S S R R S S R R S S +S S R R S S R R S S R R S S R R S S R R S S R R S S +S S R R S S R R S S R R S S R R S S R R S S R R S S +S S R R S S R R S S R R S S R R S S R R S S R R S S +S S R R S S R R S S R R S S R R S S R R S S R R S S +S S R R S S R R S S R R S S R R S S R R S S R R S S +C C C C C C C C C C C C C C C C C C C C C C C C C C +C C C C C C C C C C C C C C C C C C C C C C C C C C +S S S S S S S S S S S S S S S S S S S S S S S S S S +S S S S S S S S S S S S S S S S S S S S S S S S S S +S S S S T T T T S S S S R R S S S S T T T T S S S S +S S S S T T T T S S S S R R S S S S T T T T S S S S +S S S S S S S S S S C C R R C C S S S S S S S S S S +S S S S S S S S S S C C C C C C S S S S S S S S S S +S S C C S S C C S S C C C C C C S S C C S S C C S S +S S C C S S C C S S C C S S C C S S C C S S C C S S +S S C C S S C C S S C C S S C C S S C C S S C C S S +S S C C S S C C S S C C S S C C S S C C S S C C S S +S S C C S S C C S S S S S S S S S S C C S S C C S S +S S B B S S B B S S S S S S S S S S B B S S B B S S +S S B B S S B B S S S B B B B S S S B B S S B B S S +C C C C C C C C C C C B S S B C C C C C C C C C C C +C C C C C C C C C C C B S S B C C C C C C C C C C C \ No newline at end of file diff --git a/modules/sprites/__init__.py b/modules/sprites/__init__.py new file mode 100644 index 0000000..8f3128c --- /dev/null +++ b/modules/sprites/__init__.py @@ -0,0 +1,7 @@ +from modules.sprites.bullet import Bullet +from modules.sprites.foods import Foods +from modules.sprites.home import Home +from modules.sprites.scenes import SceneFactory, SceneElement, Ice, Brick, Tree, River, Iron +from modules.sprites.tanks import TankFactory, EnemyTank, PlayerTank, Tank +from modules.sprites.groups import SceneElementsGroup, EntityGroup + diff --git a/modules/sprites/__pycache__/__init__.cpython-310.pyc b/modules/sprites/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..d4b755a Binary files /dev/null and b/modules/sprites/__pycache__/__init__.cpython-310.pyc differ diff --git a/modules/sprites/__pycache__/__init__.cpython-312.pyc b/modules/sprites/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..7b852d5 Binary files /dev/null and b/modules/sprites/__pycache__/__init__.cpython-312.pyc differ diff --git a/modules/sprites/__pycache__/__init__.cpython-38.pyc b/modules/sprites/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..ba41f0c Binary files /dev/null and b/modules/sprites/__pycache__/__init__.cpython-38.pyc differ diff --git a/modules/sprites/__pycache__/bullet.cpython-310.pyc b/modules/sprites/__pycache__/bullet.cpython-310.pyc new file mode 100644 index 0000000..700399b Binary files /dev/null and b/modules/sprites/__pycache__/bullet.cpython-310.pyc differ diff --git a/modules/sprites/__pycache__/bullet.cpython-312.pyc b/modules/sprites/__pycache__/bullet.cpython-312.pyc new file mode 100644 index 0000000..61168cb Binary files /dev/null and b/modules/sprites/__pycache__/bullet.cpython-312.pyc differ diff --git a/modules/sprites/__pycache__/bullet.cpython-38.pyc b/modules/sprites/__pycache__/bullet.cpython-38.pyc new file mode 100644 index 0000000..77a505b Binary files /dev/null and b/modules/sprites/__pycache__/bullet.cpython-38.pyc differ diff --git a/modules/sprites/__pycache__/foods.cpython-310.pyc b/modules/sprites/__pycache__/foods.cpython-310.pyc new file mode 100644 index 0000000..d56765c Binary files /dev/null and b/modules/sprites/__pycache__/foods.cpython-310.pyc differ diff --git a/modules/sprites/__pycache__/foods.cpython-312.pyc b/modules/sprites/__pycache__/foods.cpython-312.pyc new file mode 100644 index 0000000..384b78e Binary files /dev/null and b/modules/sprites/__pycache__/foods.cpython-312.pyc differ diff --git a/modules/sprites/__pycache__/foods.cpython-38.pyc b/modules/sprites/__pycache__/foods.cpython-38.pyc new file mode 100644 index 0000000..d197221 Binary files /dev/null and b/modules/sprites/__pycache__/foods.cpython-38.pyc differ diff --git a/modules/sprites/__pycache__/groups.cpython-310.pyc b/modules/sprites/__pycache__/groups.cpython-310.pyc new file mode 100644 index 0000000..b1a69ec Binary files /dev/null and b/modules/sprites/__pycache__/groups.cpython-310.pyc differ diff --git a/modules/sprites/__pycache__/groups.cpython-312.pyc b/modules/sprites/__pycache__/groups.cpython-312.pyc new file mode 100644 index 0000000..ca364fa Binary files /dev/null and b/modules/sprites/__pycache__/groups.cpython-312.pyc differ diff --git a/modules/sprites/__pycache__/groups.cpython-38.pyc b/modules/sprites/__pycache__/groups.cpython-38.pyc new file mode 100644 index 0000000..64c5bdf Binary files /dev/null and b/modules/sprites/__pycache__/groups.cpython-38.pyc differ diff --git a/modules/sprites/__pycache__/home.cpython-310.pyc b/modules/sprites/__pycache__/home.cpython-310.pyc new file mode 100644 index 0000000..0a48816 Binary files /dev/null and b/modules/sprites/__pycache__/home.cpython-310.pyc differ diff --git a/modules/sprites/__pycache__/home.cpython-312.pyc b/modules/sprites/__pycache__/home.cpython-312.pyc new file mode 100644 index 0000000..c9d58e9 Binary files /dev/null and b/modules/sprites/__pycache__/home.cpython-312.pyc differ diff --git a/modules/sprites/__pycache__/home.cpython-38.pyc b/modules/sprites/__pycache__/home.cpython-38.pyc new file mode 100644 index 0000000..f46ef0a Binary files /dev/null and b/modules/sprites/__pycache__/home.cpython-38.pyc differ diff --git a/modules/sprites/__pycache__/scenes.cpython-310.pyc b/modules/sprites/__pycache__/scenes.cpython-310.pyc new file mode 100644 index 0000000..aad67d5 Binary files /dev/null and b/modules/sprites/__pycache__/scenes.cpython-310.pyc differ diff --git a/modules/sprites/__pycache__/scenes.cpython-312.pyc b/modules/sprites/__pycache__/scenes.cpython-312.pyc new file mode 100644 index 0000000..12c7213 Binary files /dev/null and b/modules/sprites/__pycache__/scenes.cpython-312.pyc differ diff --git a/modules/sprites/__pycache__/scenes.cpython-38.pyc b/modules/sprites/__pycache__/scenes.cpython-38.pyc new file mode 100644 index 0000000..28af3e8 Binary files /dev/null and b/modules/sprites/__pycache__/scenes.cpython-38.pyc differ diff --git a/modules/sprites/__pycache__/tanks.cpython-310.pyc b/modules/sprites/__pycache__/tanks.cpython-310.pyc new file mode 100644 index 0000000..ed0e542 Binary files /dev/null and b/modules/sprites/__pycache__/tanks.cpython-310.pyc differ diff --git a/modules/sprites/__pycache__/tanks.cpython-312.pyc b/modules/sprites/__pycache__/tanks.cpython-312.pyc new file mode 100644 index 0000000..9115deb Binary files /dev/null and b/modules/sprites/__pycache__/tanks.cpython-312.pyc differ diff --git a/modules/sprites/__pycache__/tanks.cpython-38.pyc b/modules/sprites/__pycache__/tanks.cpython-38.pyc new file mode 100644 index 0000000..2c9e028 Binary files /dev/null and b/modules/sprites/__pycache__/tanks.cpython-38.pyc differ diff --git a/modules/sprites/bullet.py b/modules/sprites/bullet.py new file mode 100644 index 0000000..a9d77fb --- /dev/null +++ b/modules/sprites/bullet.py @@ -0,0 +1,36 @@ + +import pygame + +'''子弹''' + + +class Bullet(pygame.sprite.Sprite): + + def __init__(self, direction, position, config, tank, enhanced=False, speed=20): + pygame.sprite.Sprite.__init__(self) + self.image = pygame.image.load(config.BULLET_IMAGE_PATHS.get(direction.value)) + self.width, self.height = config.WIDTH, config.HEIGHT + self.border_len = config.BORDER_LEN + self.direction = direction + self.rect = self.image.get_rect() + self.position = self.rect.center = position + self.tank = tank + + # 地图边缘宽度 + + # 是否为加强版子弹(加强版可碎铁墙) + self.enhanced = enhanced + # 子弹速度 + self.speed = speed + + def move(self): + # 移动子弹, 若子弹越界, 则返回True, 否则为False + self.rect = self.rect.move(self.direction.value[0] * self.speed, self.direction.value[1] * self.speed) + return (self.rect.top < self.border_len) or (self.rect.bottom > self.height) or ( + self.rect.left < self.border_len) or (self.rect.right > self.width) + + def kill(self): + if not self.tank.infinity_bullet: + self.tank.bullet_count -= 20 + + super().kill() \ No newline at end of file diff --git a/modules/sprites/foods.py b/modules/sprites/foods.py new file mode 100644 index 0000000..1958720 --- /dev/null +++ b/modules/sprites/foods.py @@ -0,0 +1,30 @@ +import pygame +import random +from .scenes import SceneElement + + +class Foods(SceneElement): + BOOM = 'boom' + IRON = 'iron' + CLOCK = 'clock' + GUN = 'gun' + TANK = 'tank' + PROTECT = 'protect' + STAR = 'star' + + # 食物类. 用于获得奖励 + def __init__(self, food_image_paths, screensize): + random_position = (random.randint(100, screensize[0]-100), random.randint(100, screensize[1]-100)) + random_food = random.choice(list(food_image_paths.keys())) + self.__name = random_food + super().__init__(random_position, food_image_paths.get(self.__name)) + + self.exist_time = 1000 + + @property + def type(self): + return self.__name + + def update(self): + self.exist_time -= 1 + return True if self.exist_time < 0 else False \ No newline at end of file diff --git a/modules/sprites/groups.py b/modules/sprites/groups.py new file mode 100644 index 0000000..2f44f7b --- /dev/null +++ b/modules/sprites/groups.py @@ -0,0 +1,158 @@ +import pygame + +from modules.sprites import tanks +from modules.sprites.bullet import Bullet +from modules.sprites.foods import Foods +from modules.sprites.home import Home +from modules.sprites import SceneElement, Ice, Brick, Tree, River, Iron + + +class SceneElementsGroup(object): + def __init__(self): + self.__ice_group = pygame.sprite.Group() + self.__iron_group = pygame.sprite.Group() + self.__brick_group = pygame.sprite.Group() + self.__tree_group = pygame.sprite.Group() + self.__river_group = pygame.sprite.Group() + + @property + def ice_group(self): + return self.__ice_group + + @property + def iron_group(self): + return self.__iron_group + + @property + def brick_group(self): + return self.__brick_group + + @property + def tree_group(self): + return self.__tree_group + + @property + def river_group(self): + return self.__river_group + + def add(self, scene_element: SceneElement): + if isinstance(scene_element, Ice): + self.ice_group.add(scene_element) + elif isinstance(scene_element, Brick): + self.brick_group.add(scene_element) + elif isinstance(scene_element, Tree): + self.tree_group.add(scene_element) + elif isinstance(scene_element, River): + self.river_group.add(scene_element) + elif isinstance(scene_element, Iron): + self.iron_group.add(scene_element) + + def draw(self, screen: pygame.Surface, layer: int): + if layer == 1: + self.ice_group.draw(screen) + self.river_group.draw(screen) + elif layer == 2: + self.brick_group.draw(screen) + self.iron_group.draw(screen) + self.tree_group.draw(screen) + + +class EntityGroup(object): + def __init__(self): + self.__player_tanks = pygame.sprite.Group() + self.__enemy_tanks = pygame.sprite.Group() + self.__player_bullets = pygame.sprite.Group() + self.__enemy_bullets = pygame.sprite.Group() + self.__foods = pygame.sprite.Group() + + @property + def player_tanks(self): + return self.__player_tanks + + @property + def enemy_tanks(self): + return self.__enemy_tanks + + @property + def player_bullets(self): + return self.__player_bullets + + @property + def enemy_bullets(self): + return self.__enemy_bullets + + @property + def foods(self): + return self.__foods + + def add(self, entity: pygame.sprite): + if isinstance(entity, tanks.PlayerTank): + self.player_tanks.add(entity) + elif isinstance(entity, tanks.EnemyTank): + self.enemy_tanks.add(entity) + elif isinstance(entity, Foods): + self.foods.add(entity) + elif isinstance(entity, Bullet): + if isinstance(entity.tank, tanks.PlayerTank): + self.player_bullets.add(entity) + elif isinstance(entity.tank, tanks.EnemyTank): + self.enemy_bullets.add(entity) + else: + raise TypeError('Unknown Entity Type') + + def clear_enemy_tanks(self): + self.enemy_tanks.empty() + + def remove(self, entity: pygame.sprite): + if isinstance(entity, tanks.PlayerTank): + self.player_tanks.remove(entity) + elif isinstance(entity, tanks.EnemyTank): + self.enemy_tanks.remove(entity) + elif isinstance(entity, Foods): + self.foods.remove(entity) + elif isinstance(entity, Bullet): + if isinstance(entity.tank, tanks.PlayerTank): + self.player_bullets.remove(entity) + elif isinstance(entity.tank, tanks.EnemyTank): + self.enemy_bullets.remove(entity) + else: + raise TypeError('Unknown Entity Type') + + def draw(self, screen: pygame.Surface, layer: int): + if layer == 1: + self.player_bullets.draw(screen) + self.enemy_bullets.draw(screen) + self.player_tanks.draw(screen) + for tank in self.player_tanks: + tank.draw(screen) + self.enemy_tanks.draw(screen) + elif layer == 2: + self.foods.draw(screen) + + def update(self, scene_elements: SceneElementsGroup, home: Home): + # 更新并画我方子弹 + for bullet in self.player_bullets: + if bullet.move(): + bullet.kill() + # 更新并画敌方子弹 + for bullet in self.enemy_bullets: + if bullet.move(): + bullet.kill() + # 更新并画我方坦克 + for tank in self.player_tanks: + tank.update() + # 更新并画敌方坦克 + for tank in self.enemy_tanks: + self.remove(tank) + remove_flag, bullet = tank.update( + scene_elements, self.player_tanks, self.enemy_tanks, home + ) + self.add(tank) + if isinstance(bullet, Bullet): + self.add(bullet) + if remove_flag: + self.remove(tank) + # 更新食物 + for food in self.foods: + if food.update(): + self.remove(food) \ No newline at end of file diff --git a/modules/sprites/home.py b/modules/sprites/home.py new file mode 100644 index 0000000..882bd59 --- /dev/null +++ b/modules/sprites/home.py @@ -0,0 +1,25 @@ +import pygame +from .scenes import SceneElement + + +class Home(SceneElement): + # 大本营 + def __init__(self, position, imagefile, walls_position): + super().__init__(position, imagefile[0]) + self.__destroyed_image = imagefile[1] + self.__destroyed = False + self.walls_position = walls_position + + + @property + def destroyed(self): + return self.__destroyed + + @destroyed.setter + def destroyed(self, destroyed): + self.__destroyed = destroyed + if destroyed: + self.image = pygame.image.load(self.__destroyed_image) + + def draw(self, screen): + screen.blit(self.image, self.rect) \ No newline at end of file diff --git a/modules/sprites/scenes.py b/modules/sprites/scenes.py new file mode 100644 index 0000000..fe9ddfd --- /dev/null +++ b/modules/sprites/scenes.py @@ -0,0 +1,66 @@ +import pygame + + +class SceneElement(pygame.sprite.Sprite): + def __init__(self, position, imagefile, blitimage=False): + pygame.sprite.Sprite.__init__(self) + self.image = pygame.image.load(imagefile) + if blitimage: + self.image = pygame.Surface((24, 24)) + for i in range(2): + for j in range(2): + self.image.blit(pygame.image.load(imagefile), (12 * i, 12 * j)) + self.rect = self.image.get_rect() + self.rect.left, self.rect.top = position + + +class Brick(SceneElement): + # 砖墙 + pass + + +class Iron(SceneElement): + # 铁墙 + pass + + +class Ice(SceneElement): + # 冰 + def __init__(self, position, imagefile): + super().__init__(position, imagefile, True) + + +class River(SceneElement): + # 河流 + def __init__(self, position, imagefile): + super().__init__(position, imagefile, True) + + +class Tree(SceneElement): + # 树 + def __init__(self, position, imagefile): + super().__init__(position, imagefile, True) + + +class SceneFactory(object): + BRICK = 'brick' + IRON = 'iron' + RIVER_1 = 'river1' + RIVER_2 = 'river2' + ICE = 'ice' + TREE = 'tree' + SCENE_MAPS = { + BRICK: Brick, + IRON: Iron, + RIVER_1: River, + RIVER_2: River, + ICE: Ice, + TREE: Tree, + } + + def __init__(self, game_config): + self.__scene_images = game_config.SCENE_IMAGE_PATHS + + def create_element(self, position, element_type): + return SceneFactory.SCENE_MAPS[element_type](position, self.__scene_images.get(element_type)) + diff --git a/modules/sprites/tanks.py b/modules/sprites/tanks.py new file mode 100644 index 0000000..3158c0e --- /dev/null +++ b/modules/sprites/tanks.py @@ -0,0 +1,466 @@ +import pygame +import random + +from modules.TankGame import TankGame +from modules.sprites import groups + +from modules.sprites.foods import Foods +from modules.sprites.bullet import Bullet +from enum import Enum +from pygame.sprite import spritecollide + + + + +class DIRECTION(Enum): + UP = (0, -1) + DOWN = (0, 1) + LEFT = (-1, 0) + RIGHT = (1, 0) + + @classmethod + def list(cls): + return [DIRECTION.UP, DIRECTION.DOWN, DIRECTION.LEFT, DIRECTION.RIGHT] + + @classmethod + def random(cls): + return random.choice([DIRECTION.UP, DIRECTION.DOWN, DIRECTION.LEFT, DIRECTION.RIGHT]) + + +class COLLISION: + WITH_TANK = 0b00001 + WITH_HOME = 0b00010 + WITH_BORDER = 0b00100 + WITH_SCENE_ELEMENTS = 0b01000 + + +class Tank(pygame.sprite.Sprite): + def __init__(self, game_config): + super().__init__() + self.__game_config = game_config + # 坦克轮子转动效果 + self._switch_count = 0 + self._switch_time = 1 + self._switch_pointer = False + # 移动缓冲 + self._move_cache_time = 4 + self._move_cache_count = 0 + # 爆炸 + self._boom_last_time = 5 + self._boom_count = 0 + self._booming_flag = False + + self._level = 0 + self._speed = 8 + # 地图边缘宽度/屏幕大小 + self._border_len = game_config.BORDER_LEN + self._screen_size = [game_config.WIDTH, game_config.HEIGHT] + self._init_resources() + + self.bullet_count = 0 + self._is_bullet_cooling = False + self._bullet_config = { + 0: { + 'speed': 8, + 'enhanced': False + }, + 1: { + 'speed': 10, + 'enhanced': False + }, + 2: { + 'speed': 10, + 'enhanced': True + }, + 'count': 1, + 'infinity': False + } + + @property + def infinity_bullet(self): + return self._bullet_config['infinity'] + + @property + def bullet_limit(self): + return self._bullet_config['count'] + + @property + def _game_config(self): + return self.__game_config + + def shoot(self): + if self._booming_flag: + return False + if not self._is_bullet_cooling: + if not self.infinity_bullet: + if self.bullet_count >= self.bullet_limit: + return False + else: + self.bullet_count += 1 + + self._is_bullet_cooling = True + position = (self.rect.centerx + self._direction.value[0], self.rect.centery + self._direction.value[1]) + bullet = Bullet(direction=self._direction, position=position, tank=self, config=self._game_config) + configkey = self._level + if configkey >= 2: + configkey = 2 + bullet.speed = self._bullet_config[configkey]['speed'] + bullet.enhanced = self._bullet_config[configkey]['enhanced'] + return bullet + + return False + + @property + def image(self): + if self._booming_flag: + return self._boom_image + return self._tank_direction_image.subsurface((48 * int(self._switch_pointer), 0), (48, 48)) + + def decrease_level(self): + if self._booming_flag: + return False + self._level -= 1 + self._tank_image = pygame.image.load(self._level_images[self._level]).convert_alpha() + self._update_direction(self._direction) + # self.image = self._tank_direction_image.subsurface((48 * int(self._switch_pointer), 0), (48, 48)) + if self._level < 0: + self._booming_flag = True + return True if self._level < 0 else False + + '''设置坦克方向''' + def _update_direction(self, direction): + self._direction = direction + if self._direction == DIRECTION.UP: + self._tank_direction_image = self._tank_image.subsurface((0, 0), (96, 48)) + elif self._direction == DIRECTION.DOWN: + self._tank_direction_image = self._tank_image.subsurface((0, 48), (96, 48)) + elif self._direction == DIRECTION.LEFT: + self._tank_direction_image = self._tank_image.subsurface((0, 96), (96, 48)) + elif self._direction == DIRECTION.RIGHT: + self._tank_direction_image = self._tank_image.subsurface((0, 144), (96, 48)) + + def _init_resources(self): + config = self._game_config + self._bullet_images = config.BULLET_IMAGE_PATHS + self._boom_image = pygame.image.load(config.OTHER_IMAGE_PATHS.get('boom_static')) + pass + + def roll(self): + # 为了坦克轮动特效切换图片 + self._switch_count += 1 + if self._switch_count > self._switch_time: + self._switch_count = 0 + self._switch_pointer = not self._switch_pointer + + def move(self, direction, scene_elems, player_tanks_group, enemy_tanks_group, home): + # 爆炸时无法移动 + if self._booming_flag: + return + # 方向不一致先改变方向 + if self._direction != direction: + self._update_direction(direction) + self._switch_count = self._switch_time + self._move_cache_count = self._move_cache_time + # 移动(使用缓冲) + self._move_cache_count += 1 + if self._move_cache_count < self._move_cache_time: + return + self._move_cache_count = 0 + + new_position = (self._direction.value[0] * self._speed, self._direction.value[1] * self._speed) + old_rect = self.rect + self.rect = self.rect.move(new_position) + # --碰到场景元素 + collisons = 0 + cannot_passthrough = [scene_elems.brick_group, scene_elems.iron_group, scene_elems.river_group] + for i in cannot_passthrough: + if spritecollide(self, i, False, None): + self.rect = old_rect + collisons |= COLLISION.WITH_SCENE_ELEMENTS + + + + # --碰到其他玩家坦克/碰到敌方坦克 + if spritecollide(self, player_tanks_group, False, None) or spritecollide(self, enemy_tanks_group, False, None): + collisons |= COLLISION.WITH_TANK + self.rect = old_rect + + # --碰到玩家大本营 + if pygame.sprite.collide_rect(self, home): + collisons |= COLLISION.WITH_HOME + self.rect = old_rect + + # --碰到边界 + if self.rect.left < self._border_len: + self.rect.left = self._border_len + collisons |= COLLISION.WITH_BORDER + elif self.rect.right > self._screen_size[0] - self._border_len: + collisons |= COLLISION.WITH_BORDER + self.rect.right = self._screen_size[0] - self._border_len + elif self.rect.top < self._border_len: + collisons |= COLLISION.WITH_BORDER + self.rect.top = self._border_len + elif self.rect.bottom > self._screen_size[1] - self._border_len: + collisons |= COLLISION.WITH_BORDER + self.rect.bottom = self._screen_size[1] - self._border_len + + if collisons == 0 and spritecollide(self, scene_elems.ice_group, False, None): + self.rect = self.rect.move(new_position) + + return collisons + + +class PlayerTank(Tank): + def __init__(self, name, position, game_config, **kwargs): + super().__init__(game_config=game_config) + self._level_images = self._game_config.PLAYER_TANK_IMAGE_PATHS.get(name) + + # 玩家1/玩家2 + self.name = name + # 初始坦克方向 + self.__init_direction = DIRECTION.UP + # 初始位置 + self.__init_position = position + # 保护罩 + self.__protected = False + self.__protected_mask_flash_time = 25 + self.__protected_mask_flash_count = 0 + self.__protected_mask_pointer = False + # 坦克生命数量 + self.health = 3 + # 重置 + self.__reborn() + + def _init_resources(self): + super()._init_resources() + # 保护罩 + self.__protected_mask = pygame.image.load(self._game_config.OTHER_IMAGE_PATHS.get('protect')) + + def update(self): + # 坦克子弹冷却更新 + if self._is_bullet_cooling: + self._bullet_cooling_count += 1 + if self._bullet_cooling_count >= self._bullet_cooling_time: + self._bullet_cooling_count = 0 + self._is_bullet_cooling = False + # 无敌状态更新 + if self.protected: + self.__protected_count += 1 + if self.__protected_count > self.__protected_time: + self.protected = False + self.__protected_count = 0 + # 爆炸状态更新 + if self._booming_flag: + self._boom_count += 1 + if self._boom_count > self._boom_last_time: + self._boom_count = 0 + self._booming_flag = False + self.__reborn() + + def improve_level(self): + # 提高坦克等级 + if self._booming_flag: + return False + self._level = min(self._level + 1, len(self._level_images) - 1) + self._tank_image = pygame.image.load(self._level_images[self._level]).convert_alpha() + self._update_direction(self._direction) + return True + + def decrease_level(self): + # 降低坦克等级 + res = super().decrease_level() + if self._level < 0: + self.health -= 1 + + return res + + def add_health(self): + # 增加生命值 + self.health += 1 + + @property + def protected(self): + return self.__protected + + @protected.setter + def protected(self, protected): + self.__protected = protected + + def draw(self, screen): + # 画我方坦克 + screen.blit(self.image, self.rect) + if self.protected: + self.__protected_mask_flash_count += 1 + if self.__protected_mask_flash_count > self.__protected_mask_flash_time: + self.__protected_mask_pointer = not self.__protected_mask_pointer + self.__protected_mask_flash_count = 0 + screen.blit(self.__protected_mask.subsurface((48 * self.__protected_mask_pointer, 0), (48, 48)), self.rect) + + def __reborn(self): + # 重置坦克, 重生的时候用 + + # 移动缓冲, 用于避免坦克连续运行不方便调整位置 + self._move_cache_time = 4 + self._move_cache_count = 0 + # 是否无敌状态 + self.protected = False + self.__protected_time = 1500 + self.__protected_count = 0 + # 坦克移动速度 + self._speed = 8 + # 子弹冷却时间 + self._bullet_cooling_time = 30 + self._bullet_cooling_count = 0 + self._is_bullet_cooling = False + # 坦克等级 + self._level = 0 + + # 坦克图片 + self._tank_image = pygame.image.load(self._level_images[self._level]).convert_alpha() + self._update_direction(self.__init_direction) + self.rect = self.image.get_rect() + self.rect.left, self.rect.top = self.__init_position + + +'''敌方坦克类''' + + +class EnemyTank(Tank): + def __init__(self, position, game_config, **kwargs): + super().__init__(game_config=game_config) + enemy_level_images = self._game_config.ENEMY_TANK_IMAGE_PATHS + self.__tank_type = random.choices(['0', '1', '2'], weights=[10, 10, TankGame().level+10])[0]#random.choice(list(enemy_level_images.keys())) + self._level_images = enemy_level_images.get(self.__tank_type) + self._bullet_config[2]['enhanced'] = False + + self._level = int(self.__tank_type) #random.randint(0, len(self._level_images) - 2) + # 子弹冷却时间 + self._bullet_cooling_time = 120 - self._level * 10 + self._bullet_cooling_count = 0 + self._is_bullet_cooling = False + # 用于给刚生成的坦克播放出生特效 + self._is_borning = True + self._borning_left_time = 90 + # 坦克是否可以行动(玩家坦克捡到食物clock可以触发为True) + self.is_keep_still = False + self.keep_still_time = 500 + self.keep_still_count = 0 + + # 坦克移动速度,等级越高速度越低 + self._speed = 10 - self._level * 3 + + self.__food = None + # 坦克出场特效 + appear_image = pygame.image.load(self._game_config.OTHER_IMAGE_PATHS.get('appear')).convert_alpha() + self.__appear_images = [ + appear_image.subsurface((0, 0), (48, 48)), + appear_image.subsurface((48, 0), (48, 48)), + appear_image.subsurface((96, 0), (48, 48)) + ] + if random.random() <= 0.3 * self._level: + # if (random.random() >= 0.6) and (self._level == len(self._level_images) - 2): + # self._level += 1 + self.__food = Foods(food_image_paths=self._game_config.FOOD_IMAGE_PATHS, screensize=self._screen_size) + + # 坦克图片路径 + self._tank_image = pygame.image.load(self._level_images[self._level]).convert_alpha() + self._direction = DIRECTION.random() + self._update_direction(self._direction) + self.rect = self.image.get_rect() + self.rect.left, self.rect.top = position + # 坦克爆炸图 + + @property + def food(self): + return self.__food + + def clear_food(self): + self.__food = None + + @property + def image(self): + if self._is_borning: + return self.__appear_images[(90 - self._borning_left_time // 10) % 3] + return super().image + + def update(self, scene_elems, player_tanks_group, enemy_tanks_group, home): + # 死后爆炸 + remove_flag = False + bullet = None + if self._booming_flag: + + self._boom_count += 1 + if self._boom_count > self._boom_last_time: + self._boom_count = 0 + self._booming_flag = False + remove_flag = True + return remove_flag, bullet + + # 禁止行动时不更新 + if self.is_keep_still: + self.keep_still_count += 1 + if self.keep_still_count > self.keep_still_time: + self.is_keep_still = False + self.keep_still_count = 0 + return remove_flag, bullet + + # 播放出生特效 + if self._is_borning: + self._borning_left_time -= 1 + if self._borning_left_time < 0: + self._is_borning = False + # 出生后实时更新 + else: + # --坦克移动 + self.move(self._direction, scene_elems, player_tanks_group, enemy_tanks_group, home) + self.roll() + # --坦克子弹冷却更新 + if self._is_bullet_cooling: + self._bullet_cooling_count += 1 + if self._bullet_cooling_count >= self._bullet_cooling_time: + self._bullet_cooling_count = 0 + self._is_bullet_cooling = False + # --能射击就射击 + bullet = self.shoot() + return remove_flag, bullet + + def random_change_direction(self, exclude_current_direction=False): + list = DIRECTION.list() + if exclude_current_direction: + list.remove(self._direction) + + self._update_direction(random.choice(list)) + self._switch_count = self._switch_time + self._move_cache_count = self._move_cache_time + + def move(self, direction, scene_elems, player_tanks_group, enemy_tanks_group, home): + # 遇到障碍物考虑改变方向 + collisions = super().move(direction, scene_elems, player_tanks_group, enemy_tanks_group, home) + if collisions is None or collisions == 0: + return + + change_direction = False + if collisions & COLLISION.WITH_SCENE_ELEMENTS & COLLISION.WITH_BORDER: + change_direction = True + self.random_change_direction(change_direction) + + def set_still(self): + self.is_keep_still = True + + +class TankFactory(object): + ENEMY_TANK = 0 + PLAYER1_TANK = 1 + PLAYER2_TANK = 2 + + def __init__(self, config): + self.__game_config = config + + def create_tank(self, position, tank_type): + if tank_type == TankFactory.ENEMY_TANK: + return EnemyTank(position=position, game_config=self.__game_config) + elif tank_type == TankFactory.PLAYER1_TANK: + return PlayerTank(name='player1', position=position, game_config=self.__game_config) + elif tank_type == TankFactory.PLAYER2_TANK: + return PlayerTank(name='player2', position=position, game_config=self.__game_config) + diff --git a/modules/views/AbstractView.py b/modules/views/AbstractView.py new file mode 100644 index 0000000..5177d71 --- /dev/null +++ b/modules/views/AbstractView.py @@ -0,0 +1,45 @@ +import pygame,sys + +from modules.TankGame import TankGame + + +class AbstractView(object): + def __init__(self): + self._init_resources() + self._init_logo() + + @property + def config(self): + return TankGame().config + + def _init_resources(self): + pass + + def _init_logo(self): + pass + + def _init_text(self): + pass + + def _init_bottons(self): + pass + + def _draw_interface(self): + pass + + def _main_loop(self): + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + sys.exit() + self._draw_interface() + pass + + def show(self): + TankGame().init_game_window() + self._init_text() + self._init_bottons() + self._main_loop() + pass + diff --git a/modules/views/GameLevelView.py b/modules/views/GameLevelView.py new file mode 100644 index 0000000..8cc27df --- /dev/null +++ b/modules/views/GameLevelView.py @@ -0,0 +1,432 @@ +import sys +import pygame +import random +from modules.TankGame import TankGame +from modules.sprites import * +from modules.views.AbstractView import AbstractView + +from modules.sprites.tanks import DIRECTION +from pygame.sprite import spritecollide, groupcollide, collide_rect + + +class GameLevelView(AbstractView): + + def _init_resources(self): + config = self.config + self.__sounds = TankGame().sounds + self.__other_images = config.OTHER_IMAGE_PATHS + self.__home_images = config.HOME_IMAGE_PATHS + self.__background_img = pygame.image.load(self.__other_images.get('background')) + self.__font = pygame.font.Font(config.FONTPATH, config.HEIGHT // 35) + + self.__border_len = config.BORDER_LEN + self.__grid_size = config.GRID_SIZE + self.__screen_width, self.__screen_height = config.WIDTH, config.HEIGHT + self.__panel_width = config.PANEL_WIDTH + self.__tank_factory = TankFactory(self.config) + self.__scene_factory = SceneFactory(self.config) + self.__scene_elements = None + + def _init_text(self): + color_white = (255, 255, 255) + self.__fix_text_tips = { + 1: {'text': 'Operate-P1'}, + 2: {'text': 'K_w: Up'}, + 3: {'text': 'K_s: Down'}, + 4: {'text': 'K_a: Left'}, + 5: {'text': 'K_d: Right'}, + 6: {'text': 'K_SPACE: Shoot'}, + 8: {'text': 'Operate-P2:'}, + 9: {'text': 'K_UP: Up'}, + 10: {'text': 'K_DOWN: Down'}, + 11: {'text': 'K_LEFT: Left'}, + 12: {'text': 'K_RIGHT: Right'}, + 13: {'text': 'K_KP0: Shoot'}, + 15: {'text': 'State-P1:'}, + 19: {'text': 'State-P2:'}, + } + for pos, tip in self.__fix_text_tips.items(): + tip['render'] = self.__font.render(tip['text'], True, color_white) + tip['rect'] = tip['render'].get_rect() + tip['rect'].left, tip['rect'].top = self.__screen_width + 5, self.__screen_height * pos / 30 + + pass + + '''开始游戏''' + def _init_game_window(self): + TankGame().init_game_window( + (self.config.WIDTH + self.config.PANEL_WIDTH, self.config.HEIGHT) + ) + + def __play_sound(self, sound): + self.__sounds[sound].play() + + def __dispatch_player_operation(self): + key_pressed = pygame.key.get_pressed() + key_maps = { + 'dir': { + self.__tank_player1: { + pygame.K_w: DIRECTION.UP, + pygame.K_s: DIRECTION.DOWN, + pygame.K_a: DIRECTION.LEFT, + pygame.K_d: DIRECTION.RIGHT, + }, + self.__tank_player2: { + pygame.K_UP: DIRECTION.UP, + pygame.K_DOWN: DIRECTION.DOWN, + pygame.K_LEFT: DIRECTION.LEFT, + pygame.K_RIGHT: DIRECTION.RIGHT, + }, + }, + 'fire': { + self.__tank_player1: pygame.K_SPACE, + self.__tank_player2: pygame.K_KP0, + }, + + } + + # 玩家一, WSAD移动, 空格键射击 + player_tank_list = [] + if self.__tank_player1.health >= 0: + player_tank_list.append(self.__tank_player1) + if TankGame().multiplayer_mode and (self.__tank_player1.health >= 0): + player_tank_list.append(self.__tank_player2) + + for tank in player_tank_list: + for key, dir in key_maps['dir'][tank].items(): + if key_pressed[key]: + self.__entities.remove(tank) + tank.move(dir, self.__scene_elements, self.__entities.player_tanks, + self.__entities.enemy_tanks, self.__home) + tank.roll() + self.__entities.add(tank) + break + + if key_pressed[key_maps['fire'][tank]]: + bullet = tank.shoot() + if bullet: + self.__play_sound('fire') if tank._level < 2 else self.__play_sound('Gunfire') + self.__entities.add(bullet) + + def __dispatch_food_effect(self, food: Foods, player_tank: PlayerTank): + self.__play_sound('add') + + if food.type == Foods.BOOM: + for _ in self.__entities.enemy_tanks: + self.__play_sound('bang') + self.__total_enemy_num -= len(self.__entities.enemy_tanks) + self.__entities.clear_enemy_tanks() + elif food.type == Foods.CLOCK: + for enemy_tank in self.__entities.enemy_tanks: + enemy_tank.set_still() + elif food.type == Foods.GUN: + player_tank.improve_level() + elif food.type == Foods.IRON: + for x, y in self.__home.walls_position: + self.__scene_elements.add( + self.__scene_factory.create_element((x, y), SceneFactory.IRON) + ) + elif food.type == Foods.PROTECT: + player_tank.protected = True + elif food.type == Foods.STAR: + player_tank.improve_level() + player_tank.improve_level() + elif food.type == Foods.TANK: + player_tank.add_health() + + self.__entities.foods.remove(food) + + def __dispatch_collisions(self): + collision_results = { + 'group': {}, + 'sprite': {}, + 'foreach_sprite': {}, + } + for (collision, args) in self.__collisions['group'].items(): + collision_results['group'][collision] = groupcollide(*args) + + for (collision, args) in self.__collisions['sprite'].items(): + collision_results['sprite'][collision] = spritecollide(*args) + + for (collision, args) in self.__collisions['foreach_sprite'].items(): + arg_list = list(args) + sprite_list = arg_list[0] + for sprite in sprite_list: + arg_list[0] = sprite + args = tuple(arg_list) + collision_results['foreach_sprite'][sprite] = spritecollide(*args) + + for bullet in self.__entities.player_bullets: + collision_result = spritecollide(bullet, self.__scene_elements.iron_group, bullet.enhanced, None) + if collision_result: + bullet.kill() + + for player_tank in self.__entities.player_tanks: + for food in self.__entities.foods: + collision_result = collide_rect(player_tank, food) + if collision_result: + self.__dispatch_food_effect(food, player_tank) + + # --我方子弹撞敌方坦克 + for tank in self.__entities.enemy_tanks: + if collision_results['foreach_sprite'][tank]: + if tank.food: + self.__entities.add(tank.food) + tank.clear_food() + if tank.decrease_level(): + self.__play_sound('bang') + self.__total_enemy_num -= 1 + + # --敌方子弹撞我方坦克 + for tank in self.__entities.player_tanks: + if collision_results['foreach_sprite'][tank]: + if tank.protected: + self.__play_sound('blast') + else: + if tank.decrease_level(): + self.__play_sound('bang') + if tank.health < 0: + self.__entities.remove(tank) + + if collision_results['sprite']['PlayerBulletWithHome'] or collision_results['sprite']['EnemyBulletWithHome']: + self.__is_win_flag = False + self.__has_next_loop = False + self.__play_sound('bang') + self.__home.destroyed = True + + if collision_results['group']['PlayerTankWithTree']: + self.__play_sound('hit') + + def _draw_interface(self): + screen = TankGame().screen + screen.fill((0, 0, 0)) + screen.blit(self.__background_img, (0, 0)) + self.__scene_elements.draw(screen, 1) + self.__entities.draw(screen, 1) + self.__scene_elements.draw(screen, 2) + self.__home.draw(screen) + self.__entities.draw(screen, 2) + self.__draw_game_panel() + pygame.display.flip() + + def _main_loop(self): + clock = pygame.time.Clock() + # cheat for test + # self.__tank_player1.improve_level() + # self.__tank_player1.improve_level() + # self.__tank_player1.protected = True + while self.__has_next_loop: + # 用户事件捕捉 + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + sys.exit() + # --敌方坦克生成 + elif event.type == self.__generate_enemies_event: + if self.max_enemy_num > len(self.__entities.enemy_tanks): + for position in self.__enemy_spawn_point: + if len(self.__entities.enemy_tanks) == self.__total_enemy_num: + break + enemy_tank = self.__tank_factory.create_tank(position, TankFactory.ENEMY_TANK) + if spritecollide(enemy_tank, self.__entities.enemy_tanks, False, None) or\ + spritecollide(enemy_tank, self.__entities.player_tanks, False, None): + del enemy_tank + else: + self.__entities.add(enemy_tank) + # --用户按键 + self.__dispatch_player_operation() + # 碰撞检测 + self.__dispatch_collisions() + self.__entities.update(self.__scene_elements, self.__home) + self._draw_interface() + # 我方坦克都挂了 + if len(self.__entities.player_tanks) == 0: + self.__is_win_flag = False + self.__has_next_loop = False + # 敌方坦克都挂了 + if self.__total_enemy_num <= 0: + self.__is_win_flag = True + self.__has_next_loop = False + + clock.tick(60) + + def __init_collision_config(self): + self.__collisions = { + 'group': { + 'PlayerBulletWithBrick': (self.__entities.player_bullets, self.__scene_elements.brick_group, True, True), + 'EnemyBulletWithBrick': (self.__entities.enemy_bullets, self.__scene_elements.brick_group, True, True), + 'EnemyBulletWithIron': (self.__entities.enemy_bullets, self.__scene_elements.iron_group, True, False), + 'BulletWithBullet': (self.__entities.player_bullets, self.__entities.enemy_bullets, True, True), + 'PlayerTankWithTree': (self.__entities.player_tanks, self.__scene_elements.tree_group, False, False), + }, + 'sprite': { + 'EnemyBulletWithHome': (self.__home, self.__entities.enemy_bullets, True, None), + 'PlayerBulletWithHome': (self.__home, self.__entities.player_bullets, True, None), + + }, + 'foreach_sprite': { + 'PlayerTankWithEnemyBullet': (self.__entities.player_tanks, self.__entities.enemy_bullets, True, None), + 'EnemyTankWithPlayerBullet': (self.__entities.enemy_tanks, self.__entities.player_bullets, True, None), + } + } + + def __init_tanks(self): + self.__tank_player1 = self.__tank_factory.create_tank( + self.__player_spawn_point[0], TankFactory.PLAYER1_TANK + ) + self.__entities.add(self.__tank_player1) + + self.__tank_player2 = None + if TankGame().multiplayer_mode: + self.__tank_player2 = self.__tank_factory.create_tank( + self.__player_spawn_point[1], TankFactory.PLAYER2_TANK + ) + self.__entities.add(self.__tank_player2) + # 敌方坦克 + for position in self.__enemy_spawn_point: + self.__entities.add( + self.__tank_factory.create_tank(position, TankFactory.ENEMY_TANK) + ) + + def __init_user_event(self): + self.__generate_enemies_event = pygame.constants.USEREVENT + pygame.time.set_timer(self.__generate_enemies_event, 20000) + + def __init_entities(self): + self.__entities = EntityGroup() + + def show(self): + self._init_game_window() + self._init_text() + self.__load_level_file() + self.__init_entities() + self.__init_user_event() + self.__init_tanks() + self.__init_collision_config() + self.__play_sound('start') + self.__is_win_flag = False + self.__has_next_loop = True + self._main_loop() + TankGame().is_win = self.__is_win_flag + + + '''显示游戏面板''' + def __draw_game_panel(self): + color_white = (255, 255, 255) + dynamic_text_tips = { + 16: {'text': 'Health: %s' % self.__tank_player1.health}, + 17: {'text': 'Level: %s' % self.__tank_player1._level}, + 23: {'text': 'Game Level: %s' % (TankGame().level + 1)}, + 24: {'text': 'Remain Enemy: %s' % self.__total_enemy_num} + } + if self.__tank_player2: + dynamic_text_tips[20] = {'text': 'Health: %s' % self.__tank_player2.health} + dynamic_text_tips[21] = {'text': 'Level: %s' % self.__tank_player2._level} + else: + dynamic_text_tips[20] = {'text': 'Health: %s' % None} + dynamic_text_tips[21] = {'text': 'Level: %s' % None} + + for pos, tip in dynamic_text_tips.items(): + tip['render'] = self.__font.render(tip['text'], True, color_white) + tip['rect'] = tip['render'].get_rect() + tip['rect'].left, tip['rect'].top = self.__screen_width + 5, self.__screen_height * pos / 30 + + screen = TankGame().screen + for pos, tip in self.__fix_text_tips.items(): + screen.blit(tip['render'], tip['rect']) + for pos, tip in dynamic_text_tips.items(): + screen.blit(tip['render'], tip['rect']) + + + def __load_level_file(self): + self.__scene_elements = groups.SceneElementsGroup() + + elems_map = { + 'B': SceneFactory.BRICK, + 'I': SceneFactory.IRON, + 'C': SceneFactory.ICE, + 'T': SceneFactory.TREE, + 'R': SceneFactory.RIVER_1 + } + + home_walls_position = [] + home_position = () + + f = open(TankGame().level_file, errors='ignore') + num_row = -1 + for line in f.readlines(): + line = line.strip('\n') + # 注释 + if line.startswith('#') or (not line): + continue + # 敌方坦克总数量 + elif line.startswith('%TOTALENEMYNUM'): + self.__total_enemy_num = 15 + TankGame().level #int(line.split(':')[-1]) + # 场上敌方坦克最大数量 + elif line.startswith('%MAXENEMYNUM'): + self.max_enemy_num = 15 + TankGame().level #int(line.split(':')[-1]) + # 大本营位置 + elif line.startswith('%HOMEPOS'): + home_position = line.split(':')[-1] + home_position = [ + int(home_position.split(',')[0]), int(home_position.split(',')[1]) + ] + home_position = ( + self.__border_len + home_position[0] * self.__grid_size, + self.__border_len + home_position[1] * self.__grid_size + ) + # 大本营周围位置 + elif line.startswith('%HOMEAROUNDPOS'): + home_walls_position = line.split(':')[-1] + home_walls_position = [ + [ + int(pos.split(',')[0]), int(pos.split(',')[1]) + ] for pos in home_walls_position.split(' ') + ] + home_walls_position = [ + ( + self.__border_len + pos[0] * self.__grid_size, self.__border_len + pos[1] * self.__grid_size + ) for pos in home_walls_position + ] + # 我方坦克初始位置 + elif line.startswith('%PLAYERTANKPOS'): + self.__player_spawn_point = line.split(':')[-1] + self.__player_spawn_point = [ + [ + int(pos.split(',')[0]), int(pos.split(',')[1]) + ] for pos in self.__player_spawn_point.split(' ') + ] + self.__player_spawn_point = [ + ( + self.__border_len + pos[0] * self.__grid_size, self.__border_len + pos[1] * self.__grid_size + ) for pos in self.__player_spawn_point + ] + # 敌方坦克初始位置 + elif line.startswith('%ENEMYTANKPOS'): + self.__enemy_spawn_point = line.split(':')[-1] + self.__enemy_spawn_point = [ + [ + int(pos.split(',')[0]), int(pos.split(',')[1]) + ] for pos in self.__enemy_spawn_point.split(' ') + ] + self.__enemy_spawn_point = [ + ( + self.__border_len + pos[0] * self.__grid_size, self.__border_len + pos[1] * self.__grid_size + ) for pos in self.__enemy_spawn_point + ] + # 地图元素 + else: + num_row += 1 + for num_col, elem in enumerate(line.split(' ')): + position = self.__border_len + num_col * self.__grid_size, self.__border_len + num_row * self.__grid_size + + scene_element = None + if elem in elems_map: + scene_element = self.__scene_factory.create_element(position, elems_map[elem]) + elif elem == 'R': + scene_element = self.__scene_factory.create_element( + position, random.choice([SceneFactory.RIVER_1, SceneFactory.RIVER_2]) + ) + if scene_element is not None: + self.__scene_elements.add(scene_element) + self.__home = Home(position=home_position, imagefile=self.__home_images, walls_position=home_walls_position) \ No newline at end of file diff --git a/modules/views/GameOverView.py b/modules/views/GameOverView.py new file mode 100644 index 0000000..37c8b76 --- /dev/null +++ b/modules/views/GameOverView.py @@ -0,0 +1,95 @@ +import sys +import pygame + +from modules.TankGame import TankGame +from modules.views.AbstractView import AbstractView + + +class GameOverView(AbstractView): + + def _init_resources(self): + config = self.config + self.__background_img = pygame.image.load(config.OTHER_IMAGE_PATHS.get('background')) + self.__gameover_logo = pygame.image.load(config.OTHER_IMAGE_PATHS.get('gameover')) + self.__gameover_logo = pygame.transform.scale(self.__gameover_logo, (150, 75)) + self.__font = pygame.font.Font(config.FONTPATH, config.WIDTH // 12) + self.__tank_cursor = pygame.image.load( + config.PLAYER_TANK_IMAGE_PATHS.get('player1')[0] + ).convert_alpha().subsurface((0, 144), (48, 48)) + + def _init_text(self): + config = self.config + if TankGame().is_win: + self.__font_render = self.__font.render('Congratulations, You win!', True, (255, 255, 255)) + else: + self.__font_render = self.__font.render('Sorry, You fail!', True, (255, 0, 0)) + self.__font_rect = self.__font_render.get_rect() + self.__font_rect.centerx, self.__font_rect.centery = config.WIDTH / 2, config.HEIGHT / 3 + self.__restart_render_white = self.__font.render('RESTART', True, (255, 255, 255)) + self.__restart_render_red = self.__font.render('RESTART', True, (255, 0, 0)) + self.__quit_render_white = self.__font.render('QUIT', True, (255, 255, 255)) + self.__quit_render_red = self.__font.render('QUIT', True, (255, 0, 0)) + + def _init_logo(self): + config = self.config + self.__gameover_logo_rect = self.__gameover_logo.get_rect() + self.__gameover_logo_rect.midtop = config.WIDTH / 2, config.HEIGHT / 8 + + def _draw_interface(self): + screen = TankGame().screen + screen.blit(self.__background_img, (0, 0)) + + if self.__gameover_show_flag: + screen.blit(self.__gameover_logo, self.__gameover_logo_rect) + + screen.blit(self.__font_render, self.__font_rect) + + if not self.__is_quit_game: + screen.blit(self.__tank_cursor, self.__tank_rect) + screen.blit(self.__restart_render_red, self.__restart_rect) + screen.blit(self.__quit_render_white, self.__quit_rect) + else: + screen.blit(self.__tank_cursor, self.__tank_rect) + screen.blit(self.__restart_render_white, self.__restart_rect) + screen.blit(self.__quit_render_red, self.__quit_rect) + + def _init_bottons(self): + config = self.config + self.__tank_rect = self.__tank_cursor.get_rect() + self.__restart_rect = self.__restart_render_white.get_rect() + self.__restart_rect.left, self.__restart_rect.top = config.WIDTH / 2.4, config.HEIGHT / 2 + self.__quit_rect = self.__quit_render_white.get_rect() + self.__quit_rect.left, self.__quit_rect.top = config.WIDTH / 2.4, config.HEIGHT / 1.6 + + def _main_loop(self): + gameover_flash_time = 25 + gameover_flash_count = 0 + self.__gameover_show_flag = True + self.__is_quit_game = False + + clock = pygame.time.Clock() + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + sys.exit() + elif event.type == pygame.KEYDOWN: + if event.key == pygame.K_RETURN: + TankGame().quit_game_flag = self.__is_quit_game + return + elif event.key in [pygame.K_UP, pygame.K_DOWN, pygame.K_w, pygame.K_s]: + self.__is_quit_game = not self.__is_quit_game + + gameover_flash_count += 1 + if gameover_flash_count > gameover_flash_time: + self.__gameover_show_flag = not self.__gameover_show_flag + gameover_flash_count = 0 + if self.__is_quit_game: + self.__tank_rect.right, self.__tank_rect.top = self.__quit_rect.left - 10, self.__quit_rect.top + else: + self.__tank_rect.right, self.__tank_rect.top = self.__restart_rect.left - 10, self.__restart_rect.top + + self._draw_interface() + + pygame.display.update() + clock.tick(60) diff --git a/modules/views/GameStartView.py b/modules/views/GameStartView.py new file mode 100644 index 0000000..d507a71 --- /dev/null +++ b/modules/views/GameStartView.py @@ -0,0 +1,92 @@ + +import sys +import pygame + +from modules.TankGame import TankGame +from modules.views.AbstractView import AbstractView + + +class GameStartView(AbstractView): + + def _init_resources(self): + config = self.config + self.__background_img = pygame.image.load(config.OTHER_IMAGE_PATHS.get('background')) + self.__font = pygame.font.Font(config.FONTPATH, config.WIDTH // 20) + self.__logo_img = pygame.image.load(config.OTHER_IMAGE_PATHS.get('logo')) + self.__logo_img = pygame.transform.scale(self.__logo_img, (446, 70)) + self.__tank_cursor = pygame.image.load( + config.PLAYER_TANK_IMAGE_PATHS.get('player1')[0] + ).convert_alpha().subsurface((0, 144), (48, 48)) + + def _init_logo(self): + config = self.config + self.__logo_rect = self.__logo_img.get_rect() + self.__logo_rect.centerx, self.__logo_rect.centery = config.WIDTH / 2, config.HEIGHT // 4 + + def _init_text(self): + color_white = (255, 255, 255) + color_red = (255, 0, 0) + config = self.config + self.__player_render_white = self.__font.render('1 PLAYER', True, color_white) + self.__player_render_red = self.__font.render('1 PLAYER', True, color_red) + self.__players_render_white = self.__font.render('2 PLAYERS', True, color_white) + self.__players_render_red = self.__font.render('2 PLAYERS', True, color_red) + self.__game_tip = self.__font.render('press to start', True, color_white) + self.__game_tip_rect = self.__game_tip.get_rect() + self.__game_tip_rect.centerx, self.__game_tip_rect.top = config.WIDTH / 2, config.HEIGHT / 1.4 + + def _init_bottons(self): + config = self.config + self.__player_rect = self.__player_render_white.get_rect() + self.__player_rect.left, self.__player_rect.top = config.WIDTH / 2.8, config.HEIGHT / 2.5 + self.__players_rect = self.__players_render_white.get_rect() + self.__players_rect.left, self.__players_rect.top = config.WIDTH / 2.8, config.HEIGHT / 2 + self.__tank_rect = self.__tank_cursor.get_rect() + + def _draw_interface(self): + screen = TankGame().screen + screen.blit(self.__background_img, (0, 0)) + screen.blit(self.__logo_img, self.__logo_rect) + if self.__game_tip_show_flag: + screen.blit(self.__game_tip, self.__game_tip_rect) + + if not self.__is_multiplayer_mode: + screen.blit(self.__tank_cursor, self.__tank_rect) + screen.blit(self.__player_render_red, self.__player_rect) + screen.blit(self.__players_render_white, self.__players_rect) + else: + screen.blit(self.__tank_cursor, self.__tank_rect) + screen.blit(self.__player_render_white, self.__player_rect) + screen.blit(self.__players_render_red, self.__players_rect) + + def _main_loop(self): + game_tip_flash_time = 25 + game_tip_flash_count = 0 + self.__game_tip_show_flag = True + self.__is_multiplayer_mode = False + clock = pygame.time.Clock() + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + sys.exit() + elif event.type == pygame.KEYDOWN: + if event.key == pygame.K_RETURN: + TankGame().multiplayer_mode = self.__is_multiplayer_mode + return + elif event.key == pygame.K_UP or event.key == pygame.K_DOWN or event.key == pygame.K_w or event.key == pygame.K_s: + self.__is_multiplayer_mode = not self.__is_multiplayer_mode + + game_tip_flash_count += 1 + if game_tip_flash_count > game_tip_flash_time: + self.__game_tip_show_flag = not self.__game_tip_show_flag + game_tip_flash_count = 0 + + if self.__is_multiplayer_mode: + self.__tank_rect.right, self.__tank_rect.top = self.__players_rect.left - 10, self.__players_rect.top + else: + self.__tank_rect.right, self.__tank_rect.top = self.__player_rect.left - 10, self.__player_rect.top + + self._draw_interface() + pygame.display.update() + clock.tick(60) diff --git a/modules/views/SwitchLevelView.py b/modules/views/SwitchLevelView.py new file mode 100644 index 0000000..ff616cc --- /dev/null +++ b/modules/views/SwitchLevelView.py @@ -0,0 +1,80 @@ +import sys +import pygame + +from modules.TankGame import TankGame +from modules.views.AbstractView import AbstractView + + +class SwitchLevelView(AbstractView): + + def _init_resources(self): + config = self.config + self.__loadbar = pygame.image.load(config.OTHER_IMAGE_PATHS.get('loadbar')).convert_alpha() + self.__background_img = pygame.image.load(config.OTHER_IMAGE_PATHS.get('background')) + self.__logo_img = pygame.image.load(config.OTHER_IMAGE_PATHS.get('logo')) + self.__logo_img = pygame.transform.scale(self.__logo_img, (446, 70)) + self.__font = pygame.font.Font(config.FONTPATH, config.WIDTH // 20) + self.__tank_cursor_img = pygame.image.load( + config.ENEMY_TANK_IMAGE_PATHS.get('1')[0] + ).convert_alpha().subsurface((0, 144), (48, 48)) + + def _init_text(self): + config = self.config + self.__font_render = self.__font.render( + 'LEVEL%d' % (TankGame().level + 1), + True, + (255, 255, 255) # White + ) + self.__font_rect = self.__font_render.get_rect() + self.__font_rect.centerx, self.__font_rect.centery = config.WIDTH / 2, config.HEIGHT / 2 + + def _init_loadbar(self): + config = self.config + self.__loadbar_rect = self.__loadbar.get_rect() + self.__loadbar_rect.centerx, self.__loadbar_rect.centery = config.WIDTH / 2, config.HEIGHT / 1.4 + self.__tank_rect = self.__tank_cursor_img.get_rect() + self.__tank_rect.left = self.__loadbar_rect.left + self.__tank_rect.centery = self.__loadbar_rect.centery + + def _init_logo(self): + self.__logo_rect = self.__logo_img.get_rect() + self.__logo_rect.centerx, self.__logo_rect.centery = self.config.WIDTH / 2, self.config.HEIGHT // 4 + + def _draw_interface(self): + screen = TankGame().screen + screen.blit(self.__background_img, (0, 0)) + screen.blit(self.__logo_img, self.__logo_rect) + screen.blit(self.__font_render, self.__font_rect) + screen.blit(self.__loadbar, self.__loadbar_rect) + screen.blit(self.__tank_cursor_img, self.__tank_rect) + pygame.draw.rect( + screen, + (192, 192, 192), # Gray + ( + self.__loadbar_rect.left + 8, + self.__loadbar_rect.top + 8, + self.__tank_rect.left - self.__loadbar_rect.left - 8, + self.__tank_rect.bottom - self.__loadbar_rect.top - 16 + ) + ) + self.__tank_rect.left += 8 + + def _main_loop(self): + load_time_left = self.__loadbar_rect.right - self.__tank_rect.right + 8 + clock = pygame.time.Clock() + + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + sys.exit() + if load_time_left <= 0: + return + self._draw_interface() + load_time_left -= 8 + pygame.display.update() + clock.tick(60) + + def show(self): + self._init_loadbar() + super().show() diff --git a/modules/views/ViewManager.py b/modules/views/ViewManager.py new file mode 100644 index 0000000..da96335 --- /dev/null +++ b/modules/views/ViewManager.py @@ -0,0 +1,30 @@ +import threading + +from modules.views import * + + +class ViewManager(object): + _instance_lock = threading.Lock() + _init_flag = False + + def __init__(self): + self.__views = { + 'SwitchLevel': SwitchLevelView(), + 'GameOver': GameOverView(), + 'GameStart': GameStartView(), + 'GameLevelView': GameLevelView(), + } + + def __new__(cls, *args, **kwargs): + if not hasattr(ViewManager, "_instance"): + with ViewManager._instance_lock: + if not hasattr(ViewManager, "_instance"): + ViewManager._instance = object.__new__(cls) + return ViewManager._instance + + def show(self, view: str): + if view not in self.__views: + raise Exception('View is not found!') + else: + self.__views[view].show() + diff --git a/modules/views/__init__.py b/modules/views/__init__.py new file mode 100644 index 0000000..520f8d6 --- /dev/null +++ b/modules/views/__init__.py @@ -0,0 +1,6 @@ +from modules.views.GameOverView import GameOverView +from modules.views.GameStartView import GameStartView +from modules.views.SwitchLevelView import SwitchLevelView +from modules.views.GameLevelView import GameLevelView +from modules.views.AbstractView import AbstractView +from modules.views.ViewManager import ViewManager diff --git a/modules/views/__pycache__/AbstractView.cpython-310.pyc b/modules/views/__pycache__/AbstractView.cpython-310.pyc new file mode 100644 index 0000000..ab9dadd Binary files /dev/null and b/modules/views/__pycache__/AbstractView.cpython-310.pyc differ diff --git a/modules/views/__pycache__/AbstractView.cpython-312.pyc b/modules/views/__pycache__/AbstractView.cpython-312.pyc new file mode 100644 index 0000000..ecfca65 Binary files /dev/null and b/modules/views/__pycache__/AbstractView.cpython-312.pyc differ diff --git a/modules/views/__pycache__/AbstractView.cpython-38.pyc b/modules/views/__pycache__/AbstractView.cpython-38.pyc new file mode 100644 index 0000000..e6ae65e Binary files /dev/null and b/modules/views/__pycache__/AbstractView.cpython-38.pyc differ diff --git a/modules/views/__pycache__/GameLevelView.cpython-310.pyc b/modules/views/__pycache__/GameLevelView.cpython-310.pyc new file mode 100644 index 0000000..cd202bd Binary files /dev/null and b/modules/views/__pycache__/GameLevelView.cpython-310.pyc differ diff --git a/modules/views/__pycache__/GameLevelView.cpython-312.pyc b/modules/views/__pycache__/GameLevelView.cpython-312.pyc new file mode 100644 index 0000000..1cbdd07 Binary files /dev/null and b/modules/views/__pycache__/GameLevelView.cpython-312.pyc differ diff --git a/modules/views/__pycache__/GameLevelView.cpython-38.pyc b/modules/views/__pycache__/GameLevelView.cpython-38.pyc new file mode 100644 index 0000000..9bc9191 Binary files /dev/null and b/modules/views/__pycache__/GameLevelView.cpython-38.pyc differ diff --git a/modules/views/__pycache__/GameOverView.cpython-310.pyc b/modules/views/__pycache__/GameOverView.cpython-310.pyc new file mode 100644 index 0000000..b486b67 Binary files /dev/null and b/modules/views/__pycache__/GameOverView.cpython-310.pyc differ diff --git a/modules/views/__pycache__/GameOverView.cpython-312.pyc b/modules/views/__pycache__/GameOverView.cpython-312.pyc new file mode 100644 index 0000000..462f355 Binary files /dev/null and b/modules/views/__pycache__/GameOverView.cpython-312.pyc differ diff --git a/modules/views/__pycache__/GameOverView.cpython-38.pyc b/modules/views/__pycache__/GameOverView.cpython-38.pyc new file mode 100644 index 0000000..c6156ed Binary files /dev/null and b/modules/views/__pycache__/GameOverView.cpython-38.pyc differ diff --git a/modules/views/__pycache__/GameStartView.cpython-310.pyc b/modules/views/__pycache__/GameStartView.cpython-310.pyc new file mode 100644 index 0000000..93136c1 Binary files /dev/null and b/modules/views/__pycache__/GameStartView.cpython-310.pyc differ diff --git a/modules/views/__pycache__/GameStartView.cpython-312.pyc b/modules/views/__pycache__/GameStartView.cpython-312.pyc new file mode 100644 index 0000000..bf30524 Binary files /dev/null and b/modules/views/__pycache__/GameStartView.cpython-312.pyc differ diff --git a/modules/views/__pycache__/GameStartView.cpython-38.pyc b/modules/views/__pycache__/GameStartView.cpython-38.pyc new file mode 100644 index 0000000..bf69b07 Binary files /dev/null and b/modules/views/__pycache__/GameStartView.cpython-38.pyc differ diff --git a/modules/views/__pycache__/SwitchLevelView.cpython-310.pyc b/modules/views/__pycache__/SwitchLevelView.cpython-310.pyc new file mode 100644 index 0000000..750ae42 Binary files /dev/null and b/modules/views/__pycache__/SwitchLevelView.cpython-310.pyc differ diff --git a/modules/views/__pycache__/SwitchLevelView.cpython-312.pyc b/modules/views/__pycache__/SwitchLevelView.cpython-312.pyc new file mode 100644 index 0000000..9fc3a0f Binary files /dev/null and b/modules/views/__pycache__/SwitchLevelView.cpython-312.pyc differ diff --git a/modules/views/__pycache__/SwitchLevelView.cpython-38.pyc b/modules/views/__pycache__/SwitchLevelView.cpython-38.pyc new file mode 100644 index 0000000..ceb5338 Binary files /dev/null and b/modules/views/__pycache__/SwitchLevelView.cpython-38.pyc differ diff --git a/modules/views/__pycache__/ViewManager.cpython-310.pyc b/modules/views/__pycache__/ViewManager.cpython-310.pyc new file mode 100644 index 0000000..2f528c9 Binary files /dev/null and b/modules/views/__pycache__/ViewManager.cpython-310.pyc differ diff --git a/modules/views/__pycache__/ViewManager.cpython-312.pyc b/modules/views/__pycache__/ViewManager.cpython-312.pyc new file mode 100644 index 0000000..74b37b6 Binary files /dev/null and b/modules/views/__pycache__/ViewManager.cpython-312.pyc differ diff --git a/modules/views/__pycache__/ViewManager.cpython-38.pyc b/modules/views/__pycache__/ViewManager.cpython-38.pyc new file mode 100644 index 0000000..218e764 Binary files /dev/null and b/modules/views/__pycache__/ViewManager.cpython-38.pyc differ diff --git a/modules/views/__pycache__/__init__.cpython-310.pyc b/modules/views/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..43a9423 Binary files /dev/null and b/modules/views/__pycache__/__init__.cpython-310.pyc differ diff --git a/modules/views/__pycache__/__init__.cpython-312.pyc b/modules/views/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..a64f7c5 Binary files /dev/null and b/modules/views/__pycache__/__init__.cpython-312.pyc differ diff --git a/modules/views/__pycache__/__init__.cpython-38.pyc b/modules/views/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..28f6ffe Binary files /dev/null and b/modules/views/__pycache__/__init__.cpython-38.pyc differ