You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

432 lines
18 KiB

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)