forked from easonabc-public/paia-hw5
reinit
This commit is contained in:
80
TankMan/src/Bullet.py
Normal file
80
TankMan/src/Bullet.py
Normal file
@ -0,0 +1,80 @@
|
||||
from os import path
|
||||
|
||||
import pygame
|
||||
from mlgame.view.view_model import create_asset_init_data, create_image_view_data
|
||||
|
||||
from .env import WINDOW_HEIGHT, WINDOW_WIDTH, IMAGE_DIR
|
||||
|
||||
Vec = pygame.math.Vector2
|
||||
|
||||
|
||||
class Bullet(pygame.sprite.Sprite):
|
||||
def __init__(self, construction, **kwargs):
|
||||
super().__init__()
|
||||
self.id = construction["_id"]
|
||||
self.no = construction["_no"]
|
||||
self.rect = pygame.Rect((0, 0), construction["_init_size"])
|
||||
self.rect.center = construction["_init_pos"]
|
||||
self.rot = kwargs["rot"]
|
||||
self.play_rect_area = kwargs["play_rect_area"]
|
||||
self.speed = kwargs["bullet_speed"]
|
||||
self.map_width = WINDOW_WIDTH
|
||||
self.map_height = WINDOW_HEIGHT
|
||||
self.angle = 3.14 / 180 * (self.rot + 90)
|
||||
# Refactor
|
||||
if 7 > self.angle > 6:
|
||||
self.angle = 0
|
||||
self.sqrt2 = 1.414
|
||||
self.move = {"left_up": Vec(-self.speed/self.sqrt2, -self.speed/self.sqrt2),
|
||||
"right_up": Vec(self.speed/self.sqrt2, -self.speed/self.sqrt2),
|
||||
"left_down": Vec(-self.speed/self.sqrt2, self.speed/self.sqrt2),
|
||||
"right_down": Vec(self.speed/self.sqrt2, self.speed)/self.sqrt2,
|
||||
"left": Vec(-self.speed, 0), "right": Vec(self.speed, 0), "up": Vec(0, -self.speed),
|
||||
"down": Vec(0, self.speed)}
|
||||
|
||||
self.max_travel_distance = (kwargs["bullet_travel_distance"] // self.speed + 1) * self.speed
|
||||
|
||||
self.travel_distance = 0
|
||||
|
||||
def update(self):
|
||||
self.travel_distance += self.speed
|
||||
|
||||
if self.play_rect_area.top < self.rect.centery < self.play_rect_area.bottom \
|
||||
and self.play_rect_area.left < self.rect.centerx < self.play_rect_area.right:
|
||||
is_out = False
|
||||
else:
|
||||
is_out = True
|
||||
|
||||
if is_out or self.travel_distance >= self.max_travel_distance:
|
||||
self.kill()
|
||||
|
||||
if self.rot == 0 or self.rot == 360:
|
||||
self.rect.center += self.move["left"]
|
||||
elif self.rot == 315 or self.rot == -45:
|
||||
self.rect.center += self.move["left_up"]
|
||||
elif self.rot == 270 or self.rot == -90:
|
||||
self.rect.center += self.move["up"]
|
||||
elif self.rot == 225 or self.rot == -135:
|
||||
self.rect.center += self.move["right_up"]
|
||||
elif self.rot == 180 or self.rot == -180:
|
||||
self.rect.center += self.move["right"]
|
||||
elif self.rot == 135 or self.rot == -225:
|
||||
self.rect.center += self.move["right_down"]
|
||||
elif self.rot == 90 or self.rot == -270:
|
||||
self.rect.center += self.move["down"]
|
||||
elif self.rot == 45 or self.rot == -315:
|
||||
self.rect.center += self.move["left_down"]
|
||||
|
||||
def get_obj_progress_data(self):
|
||||
img_id = "team_a_bullet" if self.id == 1 else "team_b_bullet"
|
||||
return create_image_view_data(img_id, self.rect.x, self.rect.y, self.rect.width, self.rect.height,
|
||||
self.angle)
|
||||
|
||||
def get_data_from_obj_to_game(self) -> dict:
|
||||
info = {"id": f"{self.no}P_bullet",
|
||||
"x": self.rect.x,
|
||||
"y": self.rect.y,
|
||||
"speed": self.speed,
|
||||
"rot": self.rot
|
||||
}
|
||||
return info
|
116
TankMan/src/Game.py
Normal file
116
TankMan/src/Game.py
Normal file
@ -0,0 +1,116 @@
|
||||
from os import path
|
||||
|
||||
import pygame
|
||||
from mlgame.game.paia_game import PaiaGame, GameStatus
|
||||
from mlgame.view.view_model import Scene
|
||||
|
||||
from .TeamBattleMode import TeamBattleMode
|
||||
from .game_module.fuctions import get_sprites_progress_data
|
||||
|
||||
MAP_WIDTH = 1000
|
||||
MAP_HEIGHT = 600
|
||||
GAME_DIR = path.dirname(__file__)
|
||||
MAP_DIR = path.join(GAME_DIR, "..", "asset", "maps")
|
||||
SOUND_DIR = path.join(GAME_DIR, "..", "asset", "sound")
|
||||
IMAGE_DIR = path.join(GAME_DIR, "..", "asset", "image")
|
||||
|
||||
|
||||
class Game(PaiaGame):
|
||||
def __init__(self, user_num: int, green_team_num: int, blue_team_num: int, is_manual: str, frame_limit: int, sound: str):
|
||||
super().__init__(user_num)
|
||||
# init game
|
||||
self.green_team_num = green_team_num
|
||||
self.blue_team_num = blue_team_num
|
||||
self.is_paused = False
|
||||
self.is_debug = False
|
||||
self.is_sound = False
|
||||
self.is_manual = False
|
||||
if sound == "on":
|
||||
self.is_sound = True
|
||||
if is_manual:
|
||||
self.is_manual = True
|
||||
self.attachements = []
|
||||
self.frame_limit = frame_limit
|
||||
self.game_mode = self.set_game_mode()
|
||||
self.scene = Scene(width=self.game_mode.scene_width, height=self.game_mode.scene_height, color="#ffffff",
|
||||
bias_y=50)
|
||||
|
||||
def get_data_from_game_to_player(self) -> dict:
|
||||
to_players_data = self.game_mode.get_ai_data_to_player()
|
||||
return to_players_data
|
||||
|
||||
def update(self, commands: dict):
|
||||
self.handle_event(commands)
|
||||
self.game_mode.debugging(self.is_debug)
|
||||
if not self.is_paused:
|
||||
self.frame_count += 1
|
||||
self.game_mode.update(commands)
|
||||
if not self.is_running():
|
||||
return "RESET"
|
||||
|
||||
def reset(self):
|
||||
self.frame_count = 0
|
||||
self.game_mode.reset()
|
||||
# self.rank()
|
||||
|
||||
def get_scene_init_data(self) -> dict:
|
||||
"""
|
||||
Get the scene and object information for drawing on the web
|
||||
"""
|
||||
game_info = {'scene': self.scene.__dict__,
|
||||
'assets': self.game_mode.get_init_image_data()}
|
||||
|
||||
return game_info
|
||||
|
||||
def get_scene_progress_data(self) -> dict:
|
||||
"""
|
||||
Get the position of src objects for drawing on the web
|
||||
"""
|
||||
scene_progress = {'background': [],
|
||||
'object_list': [*self.game_mode.background, *self.get_obj_progress_data()],
|
||||
'toggle_with_bias': [*self.game_mode.get_toggle_with_bias_data()],
|
||||
'toggle': self.game_mode.get_toggle_progress_data(),
|
||||
'foreground': [],
|
||||
'user_info': [],
|
||||
'game_sys_info': {}}
|
||||
|
||||
return scene_progress
|
||||
|
||||
def get_obj_progress_data(self):
|
||||
obj_list = []
|
||||
for sprites in self.game_mode.obj_list:
|
||||
obj_list.extend(get_sprites_progress_data(sprites))
|
||||
obj_list.extend(self.game_mode.obj_rect_list)
|
||||
return obj_list
|
||||
|
||||
def get_game_result(self):
|
||||
"""
|
||||
Get the src result for the web
|
||||
"""
|
||||
self.rank()
|
||||
return {"frame_used": self.frame_count,
|
||||
"state": self.game_result_state,
|
||||
"attachment": self.attachements
|
||||
}
|
||||
|
||||
def is_running(self):
|
||||
return self.game_mode.status == GameStatus.GAME_ALIVE
|
||||
|
||||
def rank(self):
|
||||
self.game_result_state = self.game_mode.state
|
||||
self.attachements = self.game_mode.get_player_result()
|
||||
return self.attachements
|
||||
|
||||
def handle_event(self, commands):
|
||||
if ["DEBUG"] in commands.values():
|
||||
self.is_debug = not self.is_debug
|
||||
if ["PAUSED"] in commands.values():
|
||||
self.is_paused = not self.is_paused
|
||||
|
||||
def set_game_mode(self):
|
||||
sound_path = ""
|
||||
if self.is_sound:
|
||||
sound_path = SOUND_DIR
|
||||
play_rect_area = pygame.Rect(0, 0, MAP_WIDTH, MAP_HEIGHT)
|
||||
game_mode = TeamBattleMode(self.green_team_num, self.blue_team_num, self.is_manual, self.frame_limit, sound_path, play_rect_area)
|
||||
return game_mode
|
153
TankMan/src/GenerateMap.py
Normal file
153
TankMan/src/GenerateMap.py
Normal file
@ -0,0 +1,153 @@
|
||||
from os import path
|
||||
import random
|
||||
import math
|
||||
|
||||
|
||||
PLAYER_NUM = 1
|
||||
OIL_NUM = 2
|
||||
BULLET_NUM = 2
|
||||
MAP_DIR = path.join(path.dirname(__file__), "..", "asset", 'maps')
|
||||
MAP_VERSION = "1.9"
|
||||
TILED_VERSION = "1.9.2"
|
||||
|
||||
|
||||
class MapGenerator:
|
||||
|
||||
def __init__(self, green_team_num : int, blue_team_num : int, width : int, height : int) -> None:
|
||||
|
||||
self.green_team_num = green_team_num
|
||||
self.blue_team_num = blue_team_num
|
||||
|
||||
# height should be a factor of 600
|
||||
# default is 20, 12
|
||||
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
self.height_per_tile = math.floor(600/self.height)
|
||||
self.width_per_tile = self.height_per_tile
|
||||
|
||||
self.screen_width = self.width * self.height_per_tile
|
||||
self.screen_height = 700
|
||||
|
||||
def getTileSize(self) -> int:
|
||||
return self.height_per_tile
|
||||
|
||||
def getScreeenSize(self) -> tuple:
|
||||
return self.screen_width, self.screen_height
|
||||
|
||||
def pos2index(self, x : int, y : int) -> int:
|
||||
return y * (self.width * 2 + 1) + x * 2
|
||||
|
||||
def mirrored_pos(self, x : int, y : int) -> tuple:
|
||||
return self.width - x - 1, self.height - y - 1
|
||||
|
||||
def random_pos(self, map_arr) -> tuple:
|
||||
random_x, random_y = random.randint(1, self.width-2), random.randint(1, self.height-2)
|
||||
mir_x, mir_y = self.mirrored_pos(random_x, random_y)
|
||||
while map_arr[random_y][random_x] != 0 or map_arr[mir_y][mir_x] != 0:
|
||||
random_x, random_y = random.randint(1, self.width-2), random.randint(1, self.height-2)
|
||||
mir_x, mir_y = self.mirrored_pos(random_x, random_y)
|
||||
|
||||
return random_x, random_y
|
||||
|
||||
def generate_map_str(self) -> str:
|
||||
|
||||
# generate default map[y][x]
|
||||
map_arr = [[0 for _ in range(self.width)] for _ in range(self.height)]
|
||||
|
||||
for x in range(self.width):
|
||||
map_arr[0][x] = 3
|
||||
map_arr[self.height-1][x] = 3
|
||||
|
||||
for y in range(self.height):
|
||||
map_arr[y][0] = 3
|
||||
map_arr[y][self.width-1] = 3
|
||||
|
||||
if self.width % 2 == 1:
|
||||
for y in range(1, self.height-1):
|
||||
map_arr[y][self.width//2] = 3
|
||||
else:
|
||||
for y in range(1, self.height):
|
||||
if y < math.ceil(self.height/2):
|
||||
map_arr[y][self.width//2-1] = 3
|
||||
else:
|
||||
map_arr[y][self.width//2] = 3
|
||||
|
||||
# add player
|
||||
for _ in range(min(self.green_team_num, self.blue_team_num)):
|
||||
rand_x, rand_y = self.random_pos(map_arr)
|
||||
while rand_x < self.width // 2:
|
||||
rand_x, rand_y = self.random_pos(map_arr)
|
||||
mir_x, mir_y = self.mirrored_pos(rand_x, rand_y)
|
||||
|
||||
map_arr[rand_y][rand_x] = 1
|
||||
map_arr[mir_y][mir_x] = 2
|
||||
|
||||
# add remaining green team
|
||||
for _ in range(self.green_team_num - self.blue_team_num):
|
||||
rand_x, rand_y = self.random_pos(map_arr)
|
||||
while rand_x < self.width // 2:
|
||||
rand_x, rand_y = self.random_pos(map_arr)
|
||||
|
||||
map_arr[rand_y][rand_x] = 1
|
||||
|
||||
# add remaining blue team
|
||||
for _ in range(self.blue_team_num - self.green_team_num):
|
||||
rand_x, rand_y = self.random_pos(map_arr)
|
||||
while rand_x >= self.width // 2:
|
||||
rand_x, rand_y = self.random_pos(map_arr)
|
||||
|
||||
map_arr[rand_y][rand_x] = 2
|
||||
|
||||
# add bullet station
|
||||
for _ in range(BULLET_NUM):
|
||||
rand_x, rand_y = self.random_pos(map_arr)
|
||||
mir_x, mir_y = self.mirrored_pos(rand_x, rand_y)
|
||||
map_arr[rand_y][rand_x] = 4
|
||||
map_arr[mir_y][mir_x] = 4
|
||||
|
||||
# add oil station
|
||||
for _ in range(OIL_NUM):
|
||||
rand_x, rand_y = self.random_pos(map_arr)
|
||||
mir_x, mir_y = self.mirrored_pos(rand_x, rand_y)
|
||||
map_arr[rand_y][rand_x] = 5
|
||||
map_arr[mir_y][mir_x] = 5
|
||||
|
||||
map_str = ""
|
||||
|
||||
for row in map_arr:
|
||||
for id in row:
|
||||
map_str += str(id) + ","
|
||||
map_str += "\n"
|
||||
map_str = map_str[:-2]
|
||||
|
||||
return map_str
|
||||
|
||||
def generate_map(self):
|
||||
map_name = f"map_{self.green_team_num}_v_{self.blue_team_num}.tmx"
|
||||
map_path = path.join(MAP_DIR, map_name)
|
||||
print(f'generate map at : {map_path}', flush=True)
|
||||
|
||||
with open(map_path, "w") as file:
|
||||
# file.write("test")
|
||||
file.write(f"""\
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="{MAP_VERSION}" tiledversion="{TILED_VERSION}" orientation="orthogonal" renderorder="right-down" width="{self.width}" height="{self.height}" tilewidth="{self.width_per_tile}" tileheight="{self.height_per_tile}" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||
<tileset firstgid="1" name="TankManObj" tilewidth="{self.width_per_tile}" tileheight="{self.height_per_tile}" tilecount="5" columns="5">
|
||||
<image source="../image/TankManObj.png" width="250" height="50"/>
|
||||
</tileset>
|
||||
<layer id="1" name="layer 1" width="{self.width}" height="{self.height}">
|
||||
<data encoding="csv">
|
||||
{self.generate_map_str()}
|
||||
</data>
|
||||
</layer>
|
||||
</map>
|
||||
""")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
map_generator = MapGenerator(1, 1)
|
||||
map_generator.generate_map()
|
||||
|
||||
|
106
TankMan/src/Gun.py
Normal file
106
TankMan/src/Gun.py
Normal file
@ -0,0 +1,106 @@
|
||||
from os import path
|
||||
|
||||
import pygame
|
||||
from mlgame.view.view_model import (create_asset_init_data,
|
||||
create_image_view_data)
|
||||
|
||||
from .env import IMAGE_DIR
|
||||
|
||||
|
||||
class Gun(pygame.sprite.Sprite):
|
||||
def __init__(self, id, pos, size, **kwargs):
|
||||
super().__init__()
|
||||
self.id = id
|
||||
self.rect = pygame.Rect(pos, size)
|
||||
self.origin_size = (self.rect.width, self.rect.height)
|
||||
self.draw_pos = self.rect.topleft
|
||||
self.surface = pygame.Surface(self.origin_size)
|
||||
self.rot = 0
|
||||
self.rot_speed = 45
|
||||
|
||||
self.used_frame = 0
|
||||
self.last_turn_frame = self.used_frame
|
||||
self.act_cd = kwargs["act_cd"]
|
||||
|
||||
self.is_alive = True
|
||||
self.is_turn_left = False
|
||||
self.is_turn_right = False
|
||||
|
||||
if self.id == 1:
|
||||
self.pivot_offset = pygame.Vector2(-8, 0)
|
||||
else:
|
||||
self.pivot_offset = pygame.Vector2(8, 0)
|
||||
|
||||
def update(self, gun_pos):
|
||||
self.used_frame += 1
|
||||
self.rotate()
|
||||
|
||||
if not self.act_cd:
|
||||
self.is_turn_right = False
|
||||
self.is_turn_left = False
|
||||
elif self.used_frame - self.last_turn_frame > self.act_cd:
|
||||
self.is_turn_right = False
|
||||
self.is_turn_left = False
|
||||
|
||||
self.rect.center = gun_pos + self.pivot_offset.rotate(-self.rot)
|
||||
self.draw_pos = self.rect.topleft
|
||||
|
||||
def rotate(self):
|
||||
self.rot = self.rot % 360
|
||||
self.angle = 3.14 / 180 * self.rot
|
||||
new_sur = pygame.transform.rotate(self.surface, self.rot)
|
||||
origin_center = self.rect.center
|
||||
self.rect = new_sur.get_rect()
|
||||
self.rect.center = origin_center
|
||||
self.draw_pos = self.rect.topleft
|
||||
|
||||
def turn_left(self):
|
||||
if self.is_turn_left:
|
||||
return
|
||||
|
||||
self.rot += self.rot_speed
|
||||
self.last_turn_frame = self.used_frame
|
||||
|
||||
self.is_turn_left = True
|
||||
self.is_turn_right = False
|
||||
|
||||
def turn_right(self):
|
||||
if self.is_turn_right:
|
||||
return
|
||||
|
||||
self.rot -= self.rot_speed
|
||||
self.last_turn_frame = self.used_frame
|
||||
|
||||
self.is_turn_left = False
|
||||
self.is_turn_right = True
|
||||
|
||||
def get_rot(self):
|
||||
if self.id == 2:
|
||||
return (self.rot + 180) % 360
|
||||
return self.rot
|
||||
|
||||
def get_obj_progress_data(self) -> dict:
|
||||
if not self.is_alive:
|
||||
return {}
|
||||
image_data = create_image_view_data(
|
||||
f"{self.id}P_gun", *self.draw_pos, *self.origin_size, self.angle
|
||||
)
|
||||
return image_data
|
||||
|
||||
def get_obj_init_data(self) -> list:
|
||||
img_data = {
|
||||
"1P_gun": "https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/1P_gun.svg",
|
||||
"2P_gun": "https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/2P_gun.svg",
|
||||
}
|
||||
image_init_data = []
|
||||
for id, url in img_data.items():
|
||||
image_init_data.append(
|
||||
create_asset_init_data(
|
||||
id,
|
||||
self.origin_size[0],
|
||||
self.origin_size[1],
|
||||
path.join(IMAGE_DIR, f"{id}.png"),
|
||||
url,
|
||||
)
|
||||
)
|
||||
return image_init_data
|
325
TankMan/src/Player.py
Normal file
325
TankMan/src/Player.py
Normal file
@ -0,0 +1,325 @@
|
||||
from os import path
|
||||
import random
|
||||
import pygame.draw
|
||||
from mlgame.utils.enum import get_ai_name
|
||||
from mlgame.view.view_model import create_asset_init_data, create_image_view_data, create_rect_view_data
|
||||
from .env import TURN_LEFT_CMD, TURN_RIGHT_CMD, FORWARD_CMD, BACKWARD_CMD, \
|
||||
AIM_LEFT_CMD, AIM_RIGHT_CMD, SHOOT, SHOOT_COOLDOWN, IMAGE_DIR, ORANGE, BLUE, IS_DEBUG
|
||||
from .Gun import Gun
|
||||
|
||||
Vec = pygame.math.Vector2
|
||||
|
||||
|
||||
class Player(pygame.sprite.Sprite):
|
||||
def __init__(self, construction, **kwargs):
|
||||
super().__init__()
|
||||
"""
|
||||
初始化玩家資料
|
||||
construction可直接由TiledMap打包地圖資訊後傳入
|
||||
:param construction:
|
||||
:param kwargs:
|
||||
"""
|
||||
self.id = construction["_id"]
|
||||
self.no = construction["_no"]
|
||||
self.rect = pygame.Rect(construction["_init_pos"], construction["_init_size"])
|
||||
self.play_rect_area = kwargs["play_rect_area"]
|
||||
self.origin_xy = self.rect.topleft
|
||||
self.origin_center = self.rect.center
|
||||
self.origin_size = (self.rect.width, self.rect.height)
|
||||
self.original_rect = self.rect.copy()
|
||||
self.draw_pos = self.rect.topleft
|
||||
self.surface = pygame.Surface(self.origin_size)
|
||||
self.angle = 0
|
||||
self.score = 0
|
||||
self.used_frame = 0
|
||||
self.last_shoot_frame = 0
|
||||
self.lives = 3
|
||||
self.power = 10
|
||||
self.vel = Vec(0, 0)
|
||||
|
||||
self.speed = 8
|
||||
self.sqrt2 = 1.414
|
||||
# TODO refactor use vel
|
||||
self.move_dict = {"left_up": Vec(-self.speed/self.sqrt2, -self.speed/self.sqrt2),
|
||||
"right_up": Vec(self.speed/self.sqrt2, -self.speed/self.sqrt2),
|
||||
"left_down": Vec(-self.speed/self.sqrt2, self.speed/self.sqrt2),
|
||||
"right_down": Vec(self.speed/self.sqrt2, self.speed/self.sqrt2),
|
||||
"left": Vec(-self.speed, 0), "right": Vec(self.speed, 0), "up": Vec(0, -self.speed),
|
||||
"down": Vec(0, self.speed)}
|
||||
self.rot = 0
|
||||
self.last_shoot_frame = self.used_frame
|
||||
self.last_turn_frame = self.used_frame
|
||||
self.rot_speed = 45
|
||||
self.oil = 100
|
||||
self.is_alive = True
|
||||
self.is_shoot = False
|
||||
self.is_forward = False
|
||||
self.is_backward = False
|
||||
self.is_turn_right = False
|
||||
self.is_turn_left = False
|
||||
self.collided = False
|
||||
self.action_history = []
|
||||
self.act_cd = kwargs["act_cd"]
|
||||
self.quadrant = 0
|
||||
|
||||
self.gun = Gun(self.id, self.rect.topleft, (self.rect.width, self.rect.height), **kwargs)
|
||||
|
||||
self.calculate_quadrant()
|
||||
self.pre_rect = self.rect
|
||||
|
||||
def calculate_quadrant(self):
|
||||
mid_x = self.play_rect_area.width // 2
|
||||
mid_y = (self.play_rect_area.height - 100) // 2
|
||||
self.quadrant = (
|
||||
1 if self.rect.x >= mid_x and self.rect.y < mid_y else
|
||||
2 if self.rect.x < mid_x and self.rect.y < mid_y else
|
||||
3 if self.rect.x < mid_x and self.rect.y >= mid_y else
|
||||
4
|
||||
)
|
||||
|
||||
def update(self, command: dict):
|
||||
self.pre_rect = self.rect
|
||||
self.used_frame += 1
|
||||
if self.lives <= 0:
|
||||
self.is_alive = False
|
||||
self.gun.is_alive = False
|
||||
self.lives = 0
|
||||
|
||||
if not self.is_alive:
|
||||
self.rect.topleft = Vec(self.play_rect_area.left + self.origin_size[0]*self.no
|
||||
, self.play_rect_area.top - self.origin_size[1]*2)
|
||||
return
|
||||
|
||||
self.rotate()
|
||||
self.gun.update(self.rect.center)
|
||||
|
||||
if not self.act_cd:
|
||||
self.is_turn_right = False
|
||||
self.is_turn_left = False
|
||||
elif self.used_frame - self.last_turn_frame > self.act_cd:
|
||||
self.is_turn_right = False
|
||||
self.is_turn_left = False
|
||||
|
||||
self.act(command[get_ai_name(self.no - 1)])
|
||||
# check tank if out of playground
|
||||
self.check_if_outofplayground()
|
||||
|
||||
def check_if_outofplayground(self):
|
||||
self.rect.clamp_ip(self.play_rect_area)
|
||||
|
||||
def rotate(self):
|
||||
self.rot = self.rot % 360
|
||||
self.angle = 3.14 / 180 * self.rot
|
||||
new_sur = pygame.transform.rotate(self.surface, self.rot)
|
||||
origin_center = self.rect.center
|
||||
self.rect = new_sur.get_rect()
|
||||
self.rect.center = origin_center
|
||||
self.draw_pos = self.rect.topleft
|
||||
|
||||
def act(self, commands: list):
|
||||
if not commands or self.collided:
|
||||
return None
|
||||
|
||||
# Only one action from the three categories can be executed in one frame
|
||||
move_flag = False
|
||||
shoot_flag = False
|
||||
aim_flag = False
|
||||
|
||||
while commands:
|
||||
command = commands.pop()
|
||||
|
||||
# Shoot
|
||||
if not shoot_flag:
|
||||
if self.power and SHOOT == command:
|
||||
self.shoot()
|
||||
|
||||
shoot_flag = True
|
||||
|
||||
# Aiming
|
||||
# TODO: Maybe the oil should be consumed when aiming
|
||||
if not aim_flag:
|
||||
if command == AIM_LEFT_CMD:
|
||||
self.gun.turn_left()
|
||||
elif command == AIM_RIGHT_CMD:
|
||||
self.gun.turn_right()
|
||||
|
||||
aim_flag = True
|
||||
|
||||
if self.oil <= 0:
|
||||
self.oil = 0
|
||||
self.lives = 0
|
||||
continue
|
||||
|
||||
# Movement
|
||||
if not move_flag:
|
||||
if TURN_LEFT_CMD == command and not self.is_turn_left and TURN_RIGHT_CMD != command:
|
||||
self.oil -= 0.1
|
||||
self.turn_left()
|
||||
self.is_turn_left = True
|
||||
self.is_forward = False
|
||||
self.is_backward = False
|
||||
self.is_turn_right = False
|
||||
self.action_history.append(TURN_LEFT_CMD)
|
||||
elif TURN_RIGHT_CMD == command and not self.is_turn_right and TURN_LEFT_CMD != command:
|
||||
self.oil -= 0.1
|
||||
self.turn_right()
|
||||
self.is_turn_right = True
|
||||
self.is_forward = False
|
||||
self.is_backward = False
|
||||
self.is_turn_left = False
|
||||
self.action_history.append(TURN_RIGHT_CMD)
|
||||
elif FORWARD_CMD == command and BACKWARD_CMD != command:
|
||||
self.oil -= 0.1
|
||||
self.forward()
|
||||
self.is_forward = True
|
||||
self.is_backward = False
|
||||
self.is_turn_right = False
|
||||
self.is_turn_left = False
|
||||
self.action_history.append(FORWARD_CMD)
|
||||
elif BACKWARD_CMD == command and FORWARD_CMD != command:
|
||||
self.oil -= 0.1
|
||||
self.backward()
|
||||
self.is_backward = True
|
||||
self.is_forward = False
|
||||
self.is_turn_right = False
|
||||
self.is_turn_left = False
|
||||
self.action_history.append(BACKWARD_CMD)
|
||||
|
||||
move_flag = True
|
||||
|
||||
self.action_history = self.action_history[-1:]
|
||||
|
||||
def shoot(self):
|
||||
if self.last_shoot_frame == 0 or self.used_frame - self.last_shoot_frame > SHOOT_COOLDOWN:
|
||||
self.last_shoot_frame = self.used_frame
|
||||
self.power -= 1
|
||||
self.is_shoot = True
|
||||
|
||||
def forward(self):
|
||||
if self.id != 1:
|
||||
rot = self.rot + 180
|
||||
if rot >= 360:
|
||||
rot -= 360
|
||||
else:
|
||||
rot = self.rot
|
||||
if rot == 0:
|
||||
self.rect.center += self.move_dict["left"]
|
||||
elif rot == 315:
|
||||
self.rect.center += self.move_dict["left_up"]
|
||||
elif rot == 270:
|
||||
self.rect.center += self.move_dict["up"]
|
||||
elif rot == 225:
|
||||
self.rect.center += self.move_dict["right_up"]
|
||||
elif rot == 180:
|
||||
self.rect.center += self.move_dict["right"]
|
||||
elif rot == 135:
|
||||
self.rect.center += self.move_dict["right_down"]
|
||||
elif rot == 90:
|
||||
self.rect.center += self.move_dict["down"]
|
||||
elif rot == 45:
|
||||
self.rect.center += self.move_dict["left_down"]
|
||||
|
||||
def backward(self):
|
||||
if self.id != 1:
|
||||
rot = self.rot + 180
|
||||
if rot >= 360:
|
||||
rot -= 360
|
||||
else:
|
||||
rot = self.rot
|
||||
if rot == 0:
|
||||
self.rect.center += self.move_dict["right"]
|
||||
elif rot == 315:
|
||||
self.rect.center += self.move_dict["right_down"]
|
||||
elif rot == 270:
|
||||
self.rect.center += self.move_dict["down"]
|
||||
elif rot == 225:
|
||||
self.rect.center += self.move_dict["left_down"]
|
||||
elif rot == 180:
|
||||
self.rect.center += self.move_dict["left"]
|
||||
elif rot == 135:
|
||||
self.rect.center += self.move_dict["left_up"]
|
||||
elif rot == 90:
|
||||
self.rect.center += self.move_dict["up"]
|
||||
elif rot == 45:
|
||||
self.rect.center += self.move_dict["right_up"]
|
||||
|
||||
def turn_left(self):
|
||||
self.last_turn_frame = self.used_frame
|
||||
self.rot += self.rot_speed
|
||||
|
||||
def turn_right(self):
|
||||
self.last_turn_frame = self.used_frame
|
||||
self.rot -= self.rot_speed
|
||||
|
||||
def collide_with_walls(self):
|
||||
self.rect = self.pre_rect
|
||||
|
||||
def collide_with_bullets(self):
|
||||
self.lives -= 1
|
||||
|
||||
def get_power(self, power: int):
|
||||
self.power += power
|
||||
if self.power > 10:
|
||||
self.power = 10
|
||||
elif self.power < 0:
|
||||
self.power = 0
|
||||
|
||||
def get_oil(self, oil: int):
|
||||
self.oil += oil
|
||||
if self.oil > 100:
|
||||
self.oil = 100
|
||||
elif self.oil < 0:
|
||||
self.oil = 0
|
||||
|
||||
def get_rot(self):
|
||||
if self.id == 2:
|
||||
return (self.rot + 180) % 360
|
||||
return self.rot
|
||||
|
||||
def get_data_from_obj_to_game(self) -> dict:
|
||||
info = {"id": f"{self.no}P"
|
||||
, "x": self.rect.x
|
||||
, "y": self.rect.y
|
||||
, "speed": self.speed
|
||||
, "score": self.score
|
||||
, "power": self.power
|
||||
, "oil": self.oil
|
||||
, "lives": self.lives
|
||||
, "angle": self.get_rot()
|
||||
, "gun_angle": self.gun.get_rot()
|
||||
, "cooldown": 0
|
||||
if self.last_shoot_frame == 0
|
||||
or self.used_frame - self.last_shoot_frame > SHOOT_COOLDOWN
|
||||
else SHOOT_COOLDOWN - self.used_frame + self.last_shoot_frame,
|
||||
}
|
||||
return info
|
||||
|
||||
def get_obj_progress_data(self) -> dict:
|
||||
if not self.is_alive:
|
||||
return []
|
||||
image_data = create_image_view_data(f"{self.id}P", *self.draw_pos, *self.origin_size, self.angle)
|
||||
return image_data
|
||||
|
||||
def get_obj_init_data(self) -> list:
|
||||
img_data = {"1P": "https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/1P_body.svg",
|
||||
"2P": "https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/2P_body.svg"}
|
||||
image_init_data = []
|
||||
for id, url in img_data.items():
|
||||
image_init_data.append(create_asset_init_data(id, self.origin_size[0], self.origin_size[1],
|
||||
path.join(IMAGE_DIR, f"{id}_body.png"), url))
|
||||
return image_init_data
|
||||
|
||||
def get_info_to_game_result(self) -> dict:
|
||||
info = {"no": f"{self.no}P"
|
||||
, "score": self.score
|
||||
, "lives": self.lives
|
||||
}
|
||||
if IS_DEBUG:
|
||||
if self.rect.right > self.play_rect_area.right \
|
||||
or self.rect.left < self.play_rect_area.left \
|
||||
or self.rect.bottom > self.play_rect_area.bottom \
|
||||
or self.rect.top < self.play_rect_area.top:
|
||||
print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!【OUT】!!!!!!!!!!!!!!!!!!!!!!!!!!!")
|
||||
else:
|
||||
print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!【SAFE】!!!!!!!!!!!!!!!!!!!!!!!!!!!")
|
||||
return info
|
65
TankMan/src/Station.py
Normal file
65
TankMan/src/Station.py
Normal file
@ -0,0 +1,65 @@
|
||||
from os import path
|
||||
|
||||
import pygame
|
||||
from mlgame.view.view_model import create_asset_init_data, create_image_view_data
|
||||
|
||||
from .env import IMAGE_DIR, WINDOW_HEIGHT, WINDOW_WIDTH
|
||||
|
||||
|
||||
class Station(pygame.sprite.Sprite):
|
||||
def __init__(self, construction, **kwargs):
|
||||
super().__init__()
|
||||
self.id = construction["_id"]
|
||||
self.rect = pygame.Rect(construction["_init_pos"], construction["_init_size"])
|
||||
self.power = kwargs["capacity"]
|
||||
self.angle = 0
|
||||
if self.rect.x >= WINDOW_WIDTH // 2 and self.rect.y < (WINDOW_HEIGHT - 100) // 2:
|
||||
self.quadrant = 1
|
||||
elif self.rect.x < WINDOW_WIDTH // 2 and self.rect.y < (WINDOW_HEIGHT - 100) // 2:
|
||||
self.quadrant = 2
|
||||
elif self.rect.x < WINDOW_WIDTH // 2 and self.rect.y >= (WINDOW_HEIGHT - 100) // 2:
|
||||
self.quadrant = 3
|
||||
else:
|
||||
self.quadrant = 4
|
||||
|
||||
self.spawn_cd = kwargs["spawn_cd"]
|
||||
self.cooldown = 0
|
||||
self.is_alive = True
|
||||
|
||||
def update(self):
|
||||
if not self.is_alive:
|
||||
self.cooldown -= 1
|
||||
if self.cooldown <= 0:
|
||||
self.is_alive = True
|
||||
|
||||
def collect(self):
|
||||
self.is_alive = False
|
||||
self.cooldown = self.spawn_cd
|
||||
|
||||
def get_data_from_obj_to_game(self):
|
||||
if 5 == self.id:
|
||||
info = {"id": "oil", "x": self.rect.x, "y": self.rect.y, "power": self.power if self.is_alive else 0}
|
||||
else:
|
||||
info = {"id": "bullets", "x": self.rect.x, "y": self.rect.y, "power": self.power if self.is_alive else 0}
|
||||
return info
|
||||
|
||||
def get_obj_progress_data(self):
|
||||
if not self.is_alive:
|
||||
return []
|
||||
if 5 == self.id:
|
||||
return create_image_view_data(f"oil", self.rect.x, self.rect.y
|
||||
, self.rect.width, self.rect.height, self.angle)
|
||||
else:
|
||||
return create_image_view_data(f"bullets", self.rect.x, self.rect.y
|
||||
, self.rect.width, self.rect.height, self.angle)
|
||||
|
||||
def get_obj_init_data(self):
|
||||
bullets_id = "bullets"
|
||||
oil_id = "oil"
|
||||
bullets_url = f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/{bullets_id}.svg"
|
||||
oil_url = f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/{oil_id}.svg"
|
||||
image_init_data = [create_asset_init_data(bullets_id, self.rect.width, self.rect.height,
|
||||
path.join(IMAGE_DIR, f"{bullets_id}.png"), bullets_url)
|
||||
, create_asset_init_data(oil_id, self.rect.width, self.rect.height,
|
||||
path.join(IMAGE_DIR, f"{oil_id}.png"), oil_url)]
|
||||
return image_init_data
|
508
TankMan/src/TeamBattleMode.py
Normal file
508
TankMan/src/TeamBattleMode.py
Normal file
@ -0,0 +1,508 @@
|
||||
import random
|
||||
import time
|
||||
import pygame.event
|
||||
import pygame.event
|
||||
from src.game_module.SoundController import create_sounds_data, create_bgm_data, SoundController
|
||||
from src.game_module.TiledMap import create_construction, TiledMap
|
||||
|
||||
from mlgame.game.paia_game import GameResultState, GameStatus
|
||||
from mlgame.utils.enum import get_ai_name
|
||||
from mlgame.view.view_model import create_asset_init_data, create_text_view_data, \
|
||||
create_rect_view_data, create_line_view_data
|
||||
from mlgame.view.view_model import create_image_view_data
|
||||
from .Bullet import Bullet
|
||||
from .Gun import Gun
|
||||
from .Player import Player
|
||||
from .Station import Station
|
||||
from .Wall import Wall
|
||||
from .collide_hit_rect import *
|
||||
from .env import *
|
||||
from .game_module.fuctions import set_topleft, add_score, set_shoot
|
||||
|
||||
|
||||
class TeamBattleMode:
|
||||
def __init__(self, green_team_num: int, blue_team_num: int, is_manual: bool, frame_limit: int, sound_path: str,
|
||||
play_rect_area: pygame.Rect):
|
||||
# init game
|
||||
pygame.init()
|
||||
self.sound_path = sound_path
|
||||
self.green_team_num = green_team_num
|
||||
self.blue_team_num = blue_team_num if (6 - (green_team_num + blue_team_num)) >= 0 else (6 - green_team_num)
|
||||
self.map_name = f"map_{green_team_num}_v_{self.blue_team_num}.tmx" if not IS_DEBUG else f"test_map_{green_team_num}_v_{self.blue_team_num}.tmx"
|
||||
self.map_path = path.join(MAP_DIR, self.map_name)
|
||||
self.map = TiledMap(self.map_path)
|
||||
self.scene_width = self.map.map_width
|
||||
self.scene_height = self.map.map_height + 100
|
||||
self.width_center = self.scene_width // 2
|
||||
self.height_center = self.scene_height // 2
|
||||
self.play_rect_area = play_rect_area
|
||||
self.used_frame = 0
|
||||
self.state = GameResultState.FAIL
|
||||
self.status = GameStatus.GAME_ALIVE
|
||||
self.sound_controller = SoundController(sound_path, self.get_sound_data())
|
||||
self.sound_controller.play_music(self.get_bgm_data())
|
||||
self.frame_limit = frame_limit
|
||||
self.is_manual = is_manual
|
||||
self.obj_rect_list = []
|
||||
self.team_green_score = 0
|
||||
self.team_blue_score = 0
|
||||
|
||||
# control variables
|
||||
self.is_invincible = False
|
||||
self.is_through_wall = False
|
||||
# initialize sprites group
|
||||
self.all_sprites = pygame.sprite.Group()
|
||||
self.players_a = pygame.sprite.Group()
|
||||
self.players_b = pygame.sprite.Group()
|
||||
self.all_players = pygame.sprite.Group()
|
||||
self.guns = pygame.sprite.Group()
|
||||
self.walls = pygame.sprite.Group()
|
||||
self.bullets = pygame.sprite.Group()
|
||||
self.bullet_stations = pygame.sprite.Group()
|
||||
self.oil_stations = pygame.sprite.Group()
|
||||
# init players
|
||||
act_cd = 0
|
||||
if self.is_manual:
|
||||
act_cd = 10
|
||||
# init obj data
|
||||
self.map.add_init_obj_data(PLAYER_1_IMG_NO, Player, act_cd=act_cd, play_rect_area=self.play_rect_area)
|
||||
self.map.add_init_obj_data(PLAYER_2_IMG_NO, Player, act_cd=act_cd, play_rect_area=self.play_rect_area)
|
||||
self.map.add_init_obj_data(WALL_IMG_NO, Wall, margin=8, spacing=8)
|
||||
self.map.add_init_obj_data(BULLET_STATION_IMG_NO, Station, spawn_cd=30, margin=2, spacing=2, capacity=5,
|
||||
quadrant=1)
|
||||
self.map.add_init_obj_data(OIL_STATION_IMG_NO, Station, spawn_cd=30, margin=2, spacing=2, capacity=30,
|
||||
quadrant=1)
|
||||
# create obj
|
||||
all_obj = self.map.create_init_obj_dict()
|
||||
# init players
|
||||
self.players_a.add(all_obj[PLAYER_1_IMG_NO])
|
||||
self.players_b.add(all_obj[PLAYER_2_IMG_NO])
|
||||
no = 1
|
||||
for player in self.players_a:
|
||||
player.no = no
|
||||
no += 1
|
||||
for player in self.players_b:
|
||||
player.no = no
|
||||
no += 1
|
||||
self.all_players.add(*self.players_a, *self.players_b)
|
||||
self.guns.add(*[player.gun for player in self.all_players])
|
||||
self.all_sprites.add(*self.players_a, *self.players_b)
|
||||
self.all_sprites.add(*[player.gun for player in self.all_players])
|
||||
# init walls
|
||||
self.walls.add(all_obj[WALL_IMG_NO])
|
||||
self.all_sprites.add(*self.walls)
|
||||
# init bullet stations
|
||||
self.bullet_stations.add(all_obj[BULLET_STATION_IMG_NO])
|
||||
self.all_sprites.add(*self.bullet_stations)
|
||||
# init oil stations
|
||||
self.oil_stations.add(all_obj[OIL_STATION_IMG_NO])
|
||||
self.all_sprites.add(*self.oil_stations)
|
||||
# init pos list
|
||||
self.all_pos_list = self.map.all_pos_list
|
||||
self.empty_quadrant_pos_dict = self.map.empty_quadrant_pos_dict
|
||||
self.background = []
|
||||
for pos in self.all_pos_list:
|
||||
no = random.randrange(3)
|
||||
self.background.append(
|
||||
create_image_view_data(f"floor_{no}", pos[0], pos[1], 50, 50, 0))
|
||||
self.obj_list = [self.oil_stations, self.bullet_stations, self.bullets, self.all_players, self.guns, self.walls]
|
||||
self.background.append(create_image_view_data("border", 0, -50, self.scene_width, WINDOW_HEIGHT, 0))
|
||||
# init play get new score time
|
||||
self.team_green_maxScoreTime = time.time()
|
||||
self.team_blue_maxScoreTime = time.time()
|
||||
self.team_green_maxScore = 0
|
||||
self.team_blue_maxScore = 0
|
||||
self.change_player_pos()
|
||||
|
||||
def update(self, command: dict):
|
||||
# refactor
|
||||
self.team_green_score = sum([player.score for player in self.players_a if isinstance(player, Player)])
|
||||
self.team_blue_score = sum([player.score for player in self.players_b if isinstance(player, Player)])
|
||||
self.used_frame += 1
|
||||
self.check_collisions()
|
||||
self.walls.update()
|
||||
self.create_bullet(self.all_players)
|
||||
self.bullets.update()
|
||||
self.bullet_stations.update()
|
||||
self.oil_stations.update()
|
||||
self.all_players.update(command)
|
||||
self.get_player_end()
|
||||
if self.used_frame >= self.frame_limit:
|
||||
self.get_game_end()
|
||||
|
||||
|
||||
# check if getting new score
|
||||
if self.team_green_score > self.team_green_maxScore:
|
||||
self.team_green_maxScore = self.team_green_score
|
||||
self.team_green_maxScoreTime = time.time()
|
||||
|
||||
if self.team_blue_score > self.team_blue_maxScore:
|
||||
self.team_blue_maxScore = self.team_blue_score
|
||||
self.team_blue_maxScoreTime = time.time()
|
||||
|
||||
def reset(self):
|
||||
# reset init game
|
||||
self.__init__(self.green_team_num, self.blue_team_num, self.is_manual, self.frame_limit, self.sound_path,
|
||||
self.play_rect_area)
|
||||
# reset player pos
|
||||
self.change_player_pos()
|
||||
|
||||
def get_player_end(self):
|
||||
is_alive_team_green = False
|
||||
is_alive_team_blue = False
|
||||
for player in self.all_players:
|
||||
if isinstance(player, Player) and player.is_alive:
|
||||
if player.no > self.green_team_num and not is_alive_team_blue:
|
||||
is_alive_team_blue = True
|
||||
elif player.no <= self.green_team_num:
|
||||
is_alive_team_green = True
|
||||
|
||||
if not is_alive_team_blue:
|
||||
self.set_result(GameResultState.FINISH, "GREEN_TEAM_WIN")
|
||||
elif not is_alive_team_green:
|
||||
self.set_result(GameResultState.FINISH, "BLUE_TEAM_WIN")
|
||||
|
||||
def get_game_end(self):
|
||||
if self.team_green_score > self.team_blue_score:
|
||||
self.set_result(GameResultState.FINISH, "GREEN_TEAM_WIN")
|
||||
elif self.team_green_score < self.team_blue_score:
|
||||
self.set_result(GameResultState.FINISH, "BLUE_TEAM_WIN")
|
||||
else:
|
||||
# if both the teams have a score of 0
|
||||
if self.team_green_maxScore == 0 and self.team_blue_maxScore == 0:
|
||||
conditions = ["GREEN_TEAM_WIN","BLUE_TEAM_WIN"]
|
||||
chosen_condition = random.choice(conditions)
|
||||
self.set_result(GameResultState.FINISH, chosen_condition)
|
||||
else:
|
||||
# if both teams have scored and their scores are equal
|
||||
if self.team_green_maxScoreTime > self.team_blue_maxScoreTime:
|
||||
self.set_result(GameResultState.FINISH, "BLUE_TEAM_WIN")
|
||||
else:
|
||||
self.set_result(GameResultState.FINISH, "GREEN_TEAM_WIN")
|
||||
|
||||
def set_result(self, state: str, status: str):
|
||||
self.state = state
|
||||
self.status = status
|
||||
|
||||
def get_player_result(self) -> list:
|
||||
"""Define the end of game will return the player's info for user"""
|
||||
res = []
|
||||
|
||||
for player in self.all_players:
|
||||
if isinstance(player, Player):
|
||||
if player.no > self.green_team_num:
|
||||
team_id = "blue"
|
||||
else:
|
||||
team_id = "green"
|
||||
get_res = player.get_info_to_game_result()
|
||||
|
||||
get_res["no"] = f"{team_id}_{player.no}P"
|
||||
get_res["player"] = f"{player.no}P"
|
||||
get_res["state"] = self.state
|
||||
get_res["status"] = self.status
|
||||
get_res["used_frame"] = self.used_frame
|
||||
if team_id == "green":
|
||||
timestamp = self.team_green_maxScoreTime
|
||||
else:
|
||||
timestamp = self.team_blue_maxScoreTime
|
||||
get_res["latestScoreTime"] = "{:.3f}".format(timestamp)
|
||||
res.append(get_res)
|
||||
|
||||
for player in res:
|
||||
if player["no"].find("green")!=-1:
|
||||
player["rank"] = 1 if player["status"] == "GREEN_TEAM_WIN" else 2
|
||||
elif player["no"].find("blue")!=-1:
|
||||
player["rank"] = 1 if player["status"] == "BLUE_TEAM_WIN" else 2
|
||||
|
||||
sorted_res = sorted(res, key=lambda x: x["rank"])
|
||||
|
||||
# Result
|
||||
return sorted_res
|
||||
|
||||
|
||||
def check_collisions(self):
|
||||
if not self.is_through_wall:
|
||||
collide_with_walls(self.all_players, self.walls)
|
||||
if not self.is_invincible:
|
||||
player_score_data = collide_with_bullets(self.all_players, self.bullets, self.green_team_num)
|
||||
for player, score in player_score_data.items():
|
||||
self.add_player_score(player, score)
|
||||
# TODO refactor stations
|
||||
|
||||
supply_stations = []
|
||||
|
||||
# Check collision between player and supply stations
|
||||
supply_stations.extend(collide_with_supply_stations(self.all_players, self.bullet_stations))
|
||||
supply_stations.extend(collide_with_supply_stations(self.all_players, self.oil_stations))
|
||||
|
||||
# Check collision between bullet and supply stations
|
||||
supply_stations.extend(collide_with_supply_stations(self.bullets, self.bullet_stations))
|
||||
supply_stations.extend(collide_with_supply_stations(self.bullets, self.oil_stations))
|
||||
|
||||
# Update stations position
|
||||
self.change_obj_pos(supply_stations)
|
||||
|
||||
player_score_data = collide_with_bullets(self.walls, self.bullets)
|
||||
for player, score in player_score_data.items():
|
||||
self.add_player_score(player, score)
|
||||
|
||||
def change_player_pos(self):
|
||||
for player in self.all_players:
|
||||
quadrant = player.quadrant
|
||||
self.empty_quadrant_pos_dict[quadrant].append(player.rect.topleft)
|
||||
if quadrant == 2 or quadrant == 3:
|
||||
player.quadrant = random.choice([2, 3])
|
||||
else:
|
||||
player.quadrant = random.choice([1, 4])
|
||||
quadrant = player.quadrant
|
||||
new_pos = self.empty_quadrant_pos_dict[quadrant].pop(
|
||||
random.randrange(len(self.empty_quadrant_pos_dict[quadrant])))
|
||||
set_topleft(player, new_pos)
|
||||
set_topleft(player.gun, new_pos)
|
||||
|
||||
# TODO move method to Station
|
||||
def change_obj_pos(self, objs=None):
|
||||
if objs is None:
|
||||
return
|
||||
for obj in objs:
|
||||
quadrant = obj.quadrant
|
||||
self.empty_quadrant_pos_dict[quadrant].append(obj.rect.topleft)
|
||||
if quadrant == 2 or quadrant == 3:
|
||||
obj.quadrant = random.choice([2, 3])
|
||||
else:
|
||||
obj.quadrant = random.choice([1, 4])
|
||||
quadrant = obj.quadrant
|
||||
new_pos = self.empty_quadrant_pos_dict[quadrant].pop(
|
||||
random.randrange(len(self.empty_quadrant_pos_dict[quadrant])))
|
||||
set_topleft(obj, new_pos)
|
||||
|
||||
def create_bullet(self, sprites: pygame.sprite.Group):
|
||||
for sprite in sprites:
|
||||
if not sprite.is_shoot:
|
||||
continue
|
||||
self.sound_controller.play_sound("shoot", 0.03, -1)
|
||||
init_data = create_construction(sprite.id, sprite.no, sprite.rect.center, (BULLET_SIZE[0], BULLET_SIZE[1]))
|
||||
bullet = Bullet(init_data, rot=sprite.gun.get_rot(), margin=2, spacing=2, bullet_speed=BULLET_SPEED,
|
||||
bullet_travel_distance=BULLET_TRAVEL_DISTANCE
|
||||
, play_rect_area=self.play_rect_area)
|
||||
self.bullets.add(bullet)
|
||||
self.all_sprites.add(bullet)
|
||||
set_shoot(sprite, False)
|
||||
|
||||
def get_init_image_data(self):
|
||||
init_image_data = []
|
||||
for i in range(3):
|
||||
init_image_data.append(create_asset_init_data(f"floor_{i}", 50, 50
|
||||
, path.join(IMAGE_DIR, f"grass_{i}.png"),
|
||||
f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/grass_{i}.png"))
|
||||
for i in range(15):
|
||||
init_image_data.append(create_asset_init_data(f"hourglass_{i}", 42, 42
|
||||
, path.join(IMAGE_DIR, f"hourglass_{i}.png"),
|
||||
f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/hourglass_{i}.png"))
|
||||
for station in self.bullet_stations:
|
||||
if isinstance(station, Station):
|
||||
for data in station.get_obj_init_data():
|
||||
init_image_data.append(data)
|
||||
break
|
||||
for wall in self.walls:
|
||||
if isinstance(wall, Wall):
|
||||
for data in wall.get_obj_init_data():
|
||||
init_image_data.append(data)
|
||||
break
|
||||
img_id = ["team_a_bullet", "team_b_bullet"]
|
||||
for id in img_id:
|
||||
img_url = f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/{id}.svg"
|
||||
bullet_image_init_data = create_asset_init_data(id, BULLET_SIZE[0], BULLET_SIZE[1],
|
||||
path.join(IMAGE_DIR, f"{id}.png"), img_url)
|
||||
init_image_data.append(bullet_image_init_data)
|
||||
border_image_init_data = create_asset_init_data("border", self.scene_width, WINDOW_HEIGHT,
|
||||
path.join(IMAGE_DIR, "border.png"),
|
||||
f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/border.svg")
|
||||
init_image_data.append(border_image_init_data)
|
||||
for player in self.all_players:
|
||||
if isinstance(player, Player):
|
||||
data = player.get_obj_init_data()
|
||||
init_image_data.append(data[0])
|
||||
init_image_data.append(data[1])
|
||||
break
|
||||
for gun in self.guns:
|
||||
if isinstance(gun, Gun):
|
||||
data = gun.get_obj_init_data()
|
||||
init_image_data.append(data[0])
|
||||
init_image_data.append(data[1])
|
||||
break
|
||||
for i in range(1, 4):
|
||||
team_a_lives = "team_a_lives"
|
||||
team_a_lives_image_init_data = create_asset_init_data(f"{team_a_lives}_{i}", LIVES_SIZE[0], LIVES_SIZE[1],
|
||||
path.join(IMAGE_DIR, f"{team_a_lives}_{i}.png"),
|
||||
f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/{team_a_lives}_{i}.svg")
|
||||
init_image_data.append(team_a_lives_image_init_data)
|
||||
team_b_lives = "team_b_lives"
|
||||
team_b_lives_image_init_data = create_asset_init_data(f"{team_b_lives}_{i}", LIVES_SIZE[0], LIVES_SIZE[1],
|
||||
path.join(IMAGE_DIR, f"{team_b_lives}_{i}.png"),
|
||||
f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/{team_b_lives}_{i}.svg")
|
||||
init_image_data.append(team_b_lives_image_init_data)
|
||||
return init_image_data
|
||||
|
||||
def get_toggle_progress_data(self):
|
||||
toggle_data = []
|
||||
hourglass_index = 0
|
||||
if self.is_manual:
|
||||
hourglass_index = self.used_frame // 10 % 15
|
||||
toggle_data.append(
|
||||
create_image_view_data(image_id=f"hourglass_{hourglass_index}", x=0, y=2, width=20, height=20, angle=0))
|
||||
x = 23
|
||||
y = 8
|
||||
for frame in range((self.frame_limit - self.used_frame) // int((30 * 2))):
|
||||
toggle_data.append(create_rect_view_data("frame", x, y, 3, 10, RED))
|
||||
x += 3.5
|
||||
toggle_data.append(create_text_view_data(f"Frame: {self.frame_limit - self.used_frame}",
|
||||
self.width_center + self.width_center // 2 + 85, 8, RED,
|
||||
"24px Arial BOLD"))
|
||||
x = 24
|
||||
y = 20
|
||||
for score in range(min(self.team_green_score, self.team_blue_score)):
|
||||
toggle_data.append(create_rect_view_data(name="score", x=x, y=y, width=1, height=10, color=ORANGE))
|
||||
x += 1.5
|
||||
if x > self.width_center:
|
||||
if y == 32:
|
||||
y = 44
|
||||
else:
|
||||
y = 32
|
||||
x = 24
|
||||
for score in range(abs(self.team_green_score - self.team_blue_score)):
|
||||
if self.team_green_score > self.team_blue_score:
|
||||
toggle_data.append(create_rect_view_data("score", x, y, 1, 10, DARKGREEN))
|
||||
else:
|
||||
toggle_data.append(create_rect_view_data("score", x, y, 1, 10, BLUE))
|
||||
x += 1.5
|
||||
if x > self.width_center:
|
||||
if y == 32:
|
||||
y = 44
|
||||
else:
|
||||
y = 32
|
||||
x = 24
|
||||
# 1P
|
||||
x = WINDOW_WIDTH - 125
|
||||
y = WINDOW_HEIGHT - 40
|
||||
toggle_data.append(create_text_view_data(f"Score: {self.team_green_score}", x, y, DARKGREEN, "24px Arial BOLD"))
|
||||
# 2P
|
||||
x = 5
|
||||
y = WINDOW_HEIGHT - 40
|
||||
toggle_data.append(create_text_view_data(f"Score: {self.team_blue_score}", x, y, BLUE, "24px Arial BOLD"))
|
||||
for player in self.all_players:
|
||||
if isinstance(player, Player) and player.is_alive:
|
||||
# lives
|
||||
team_id = "team_a_lives" if player.id == 1 else "team_b_lives"
|
||||
color = DARKGREEN if player.id == 1 else BLUE
|
||||
x = player.play_rect_area.midbottom[0] + 7 + (player.no - 1) * 60 if player.id == 1 \
|
||||
else player.play_rect_area.midbottom[0] - (player.no - self.green_team_num) * 60
|
||||
y = player.play_rect_area.height + 73
|
||||
toggle_data.append(
|
||||
create_text_view_data(f"{player.no}P", x - 5, y - 25, color, "22px Arial BOLD"))
|
||||
for live in range(1, player.lives + 1):
|
||||
toggle_data.append(create_image_view_data(f"{team_id}_{live}", x, y, LIVES_SIZE[0], LIVES_SIZE[1]))
|
||||
x += 10
|
||||
y -= 10
|
||||
return toggle_data
|
||||
|
||||
def get_toggle_with_bias_data(self):
|
||||
toggle_with_bias_data = []
|
||||
color = WHITE
|
||||
for player in self.all_players:
|
||||
if isinstance(player, Player) and player.is_alive:
|
||||
# number
|
||||
if player.no > self.green_team_num:
|
||||
color = WHITE
|
||||
x = player.rect.x
|
||||
y = player.rect.y - 18
|
||||
toggle_with_bias_data.append(create_text_view_data(f"{player.no}P", x, y, color, "16px Arial BOLD"))
|
||||
team_id = "team_a"
|
||||
if player.no > self.green_team_num:
|
||||
team_id = "team_b"
|
||||
# oil
|
||||
y = player.rect.bottom
|
||||
toggle_with_bias_data.append(
|
||||
create_rect_view_data(f"{team_id}_oil", x, y, int(player.oil * 0.5), 8, ORANGE))
|
||||
# power
|
||||
y = player.rect.bottom + 10
|
||||
for power in range(player.power):
|
||||
toggle_with_bias_data.append(create_rect_view_data(f"{team_id}_power", x + 1, y, 3, 8, BLUE))
|
||||
x += 5
|
||||
|
||||
return toggle_with_bias_data
|
||||
|
||||
def get_ai_data_to_player(self):
|
||||
to_player_data = {}
|
||||
num = 0
|
||||
competitor_info = {
|
||||
1: [player.get_data_from_obj_to_game() for player in self.players_a if isinstance(player, Player)]
|
||||
, 2: [player.get_data_from_obj_to_game() for player in self.players_b if isinstance(player, Player)]
|
||||
}
|
||||
walls_info = [wall.get_data_from_obj_to_game() for wall in self.walls if isinstance(wall, Wall)]
|
||||
bullet_stations_info = [bullst_station.get_data_from_obj_to_game() for bullst_station in self.bullet_stations if
|
||||
isinstance(bullst_station, Station)]
|
||||
oil_stations_info = [oil_station.get_data_from_obj_to_game() for oil_station in self.oil_stations if
|
||||
isinstance(oil_station, Station)]
|
||||
bullets_info = [bullet.get_data_from_obj_to_game() for bullet in self.bullets if
|
||||
isinstance(bullet, Bullet)]
|
||||
for player in self.players_a:
|
||||
if isinstance(player, Player):
|
||||
to_game_data = player.get_data_from_obj_to_game()
|
||||
to_game_data["used_frame"] = self.used_frame
|
||||
to_game_data["status"] = self.status
|
||||
to_game_data["teammate_info"] = competitor_info[1]
|
||||
to_game_data["competitor_info"] = competitor_info[2]
|
||||
to_game_data["walls_info"] = walls_info
|
||||
to_game_data["bullets_info"] = bullets_info
|
||||
to_game_data["bullet_stations_info"] = bullet_stations_info
|
||||
to_game_data["oil_stations_info"] = oil_stations_info
|
||||
to_player_data[get_ai_name(num)] = to_game_data
|
||||
num += 1
|
||||
for player in self.players_b:
|
||||
if isinstance(player, Player):
|
||||
to_game_data = player.get_data_from_obj_to_game()
|
||||
to_game_data["used_frame"] = self.used_frame
|
||||
to_game_data["status"] = self.status
|
||||
to_game_data["teammate_info"] = competitor_info[2]
|
||||
to_game_data["competitor_info"] = competitor_info[1]
|
||||
to_game_data["walls_info"] = walls_info
|
||||
to_game_data["bullets_info"] = bullets_info
|
||||
to_game_data["bullet_stations_info"] = bullet_stations_info
|
||||
to_game_data["oil_stations_info"] = oil_stations_info
|
||||
to_player_data[get_ai_name(num)] = to_game_data
|
||||
num += 1
|
||||
|
||||
return to_player_data
|
||||
|
||||
def get_bgm_data(self):
|
||||
return create_bgm_data("BGM.ogg", 0.1)
|
||||
|
||||
def get_sound_data(self):
|
||||
return [create_sounds_data("shoot", "shoot.wav")
|
||||
, create_sounds_data("touch", "touch.wav")]
|
||||
|
||||
def add_player_score(self, player_no: int, score: int):
|
||||
if not player_no or not score:
|
||||
return
|
||||
for player in self.all_players:
|
||||
if isinstance(player, Player) and player_no == player.no and player.lives >= 0:
|
||||
add_score(player, score)
|
||||
|
||||
def debugging(self, is_debug: bool):
|
||||
self.obj_rect_list = []
|
||||
if not is_debug:
|
||||
return
|
||||
play_rect_area_points = [self.play_rect_area.topleft, self.play_rect_area.topright
|
||||
, self.play_rect_area.bottomright, self.play_rect_area.bottomleft
|
||||
, self.play_rect_area.topleft]
|
||||
|
||||
for sprite in self.all_sprites:
|
||||
if isinstance(sprite, pygame.sprite.Sprite):
|
||||
top_left = sprite.rect.topleft
|
||||
points = [top_left, sprite.rect.topright, sprite.rect.bottomright
|
||||
, sprite.rect.bottomleft, top_left]
|
||||
for index in range(len(points) - 1):
|
||||
self.obj_rect_list.append(create_line_view_data("rect", *points[index], *points[index + 1], RED))
|
||||
self.obj_rect_list.append(create_line_view_data("play_rect_area", *play_rect_area_points[index]
|
||||
, *play_rect_area_points[index + 1], RED))
|
41
TankMan/src/Wall.py
Normal file
41
TankMan/src/Wall.py
Normal file
@ -0,0 +1,41 @@
|
||||
from os import path
|
||||
|
||||
import pygame
|
||||
from mlgame.view.view_model import create_asset_init_data, create_image_view_data
|
||||
|
||||
from .env import IMAGE_DIR, WALL_LIVE
|
||||
|
||||
|
||||
class Wall(pygame.sprite.Sprite):
|
||||
def __init__(self, construction, **kwargs):
|
||||
super().__init__()
|
||||
self.id = construction["_id"]
|
||||
self.no = 0
|
||||
self.rect = pygame.Rect(construction["_init_pos"], construction["_init_size"])
|
||||
self.angle = 0
|
||||
self.is_alive = True
|
||||
self.lives = WALL_LIVE
|
||||
|
||||
def update(self, *args, **kwargs) -> None:
|
||||
if self.lives <= 0:
|
||||
self.kill()
|
||||
|
||||
def collide_with_bullets(self):
|
||||
if self.lives > 0:
|
||||
self.lives -= 1
|
||||
|
||||
def get_data_from_obj_to_game(self):
|
||||
info = {"id": f"wall_{self.lives}", "x": self.rect.x, "y": self.rect.y, "lives": self.lives}
|
||||
return info
|
||||
|
||||
def get_obj_progress_data(self):
|
||||
return create_image_view_data(f"wall_{self.lives}", self.rect.x, self.rect.y
|
||||
, self.rect.width, self.rect.height, self.angle)
|
||||
|
||||
def get_obj_init_data(self):
|
||||
image_init_data = []
|
||||
for i in range(1, self.lives+1):
|
||||
image_init_data.append(create_asset_init_data(f"wall_{i}", self.rect.width, self.rect.height,
|
||||
path.join(IMAGE_DIR, f"wall_{min(i,3)}.png"),
|
||||
f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/wall_{min(i,3)}.png"))
|
||||
return image_init_data
|
0
TankMan/src/__init__.py
Normal file
0
TankMan/src/__init__.py
Normal file
58
TankMan/src/collide_hit_rect.py
Normal file
58
TankMan/src/collide_hit_rect.py
Normal file
@ -0,0 +1,58 @@
|
||||
from typing import Optional
|
||||
import pygame.sprite
|
||||
|
||||
from src.Player import Player
|
||||
from src.Bullet import Bullet
|
||||
from src.Wall import Wall
|
||||
|
||||
|
||||
def collide_with_walls(group1: pygame.sprite.Group, group2: pygame.sprite.Group):
|
||||
hits = pygame.sprite.groupcollide(group1, group2, False, False, pygame.sprite.collide_rect_ratio(0.8))
|
||||
for sprite, walls in hits.items():
|
||||
sprite.collide_with_walls()
|
||||
|
||||
|
||||
def collide_with_bullets(group1: pygame.sprite.Group, group2: pygame.sprite.Group, green_team_num: Optional[int] = None):
|
||||
hits = pygame.sprite.groupcollide(group1, group2, False, False, pygame.sprite.collide_rect_ratio(0.8))
|
||||
player_score_data = {}
|
||||
for sprite, bullets in hits.items():
|
||||
for bullet in bullets:
|
||||
if bullet.no != sprite.no and sprite.lives > 0:
|
||||
bullet.kill()
|
||||
|
||||
if isinstance(sprite, Player):
|
||||
assert green_team_num is not None
|
||||
if (bullet.no - 1) // green_team_num == (sprite.no - 1) // green_team_num:
|
||||
# -20 for friendly damage
|
||||
score = -20
|
||||
else:
|
||||
score = 20
|
||||
elif isinstance(sprite, Wall):
|
||||
score = 1
|
||||
if sprite.lives == 1:
|
||||
score += 5
|
||||
else:
|
||||
continue
|
||||
|
||||
sprite.collide_with_bullets()
|
||||
|
||||
if player_score_data.get(bullet.no) is None:
|
||||
player_score_data[bullet.no] = 0
|
||||
player_score_data[bullet.no] += score
|
||||
return player_score_data
|
||||
|
||||
|
||||
def collide_with_supply_stations(sprites: pygame.sprite.Group, supply_stations: pygame.sprite.Group):
|
||||
hits = pygame.sprite.groupcollide(sprites, supply_stations, False, False, pygame.sprite.collide_rect_ratio(0.8))
|
||||
for sprite, supply_station in hits.items():
|
||||
if isinstance(sprite, Player):
|
||||
if supply_station[0].id == 5:
|
||||
sprite.get_oil(supply_station[0].power)
|
||||
else:
|
||||
sprite.get_power(supply_station[0].power)
|
||||
elif isinstance(sprite, Bullet):
|
||||
sprite.kill()
|
||||
|
||||
supply_station[0].collect()
|
||||
|
||||
return [supply_station[0] for supply_station in hits.values()]
|
85
TankMan/src/env.py
Normal file
85
TankMan/src/env.py
Normal file
@ -0,0 +1,85 @@
|
||||
from os import path
|
||||
|
||||
import pygame
|
||||
|
||||
IS_DEBUG = False
|
||||
# TODO remove width and height setting
|
||||
'''width and height'''
|
||||
WINDOW_WIDTH = 1000
|
||||
WINDOW_HEIGHT = 700
|
||||
|
||||
'''environment data'''
|
||||
FPS = 30
|
||||
SHOOT_COOLDOWN = 15
|
||||
|
||||
'''color'''
|
||||
BLACK = "#000000"
|
||||
WHITE = "#ffffff"
|
||||
RED = "#ff0000"
|
||||
YELLOW = "#ffff00"
|
||||
GREEN = "#00ff00"
|
||||
DARKGREEN = "#006400"
|
||||
GREY = "#8c8c8c"
|
||||
BLUE = "#0000ff"
|
||||
LIGHT_BLUE = "#21A1F1"
|
||||
CYAN_BLUE = "#00FFFF"
|
||||
PINK = "#FF00FF"
|
||||
DARKGREY = "#282828"
|
||||
LIGHTGREY = "#646464"
|
||||
BROWN = "#643705"
|
||||
FOREST = "#22390A"
|
||||
MAGENTA = "#FF00FF"
|
||||
MEDGRAY = "#4B4B4B"
|
||||
ORANGE = "#FFA500"
|
||||
|
||||
'''command'''
|
||||
TURN_LEFT_CMD = "TURN_LEFT"
|
||||
TURN_RIGHT_CMD = "TURN_RIGHT"
|
||||
FORWARD_CMD = "FORWARD"
|
||||
BACKWARD_CMD = "BACKWARD"
|
||||
AIM_LEFT_CMD = "AIM_LEFT"
|
||||
AIM_RIGHT_CMD = "AIM_RIGHT"
|
||||
SHOOT = "SHOOT"
|
||||
|
||||
'''data path'''
|
||||
GAME_DIR = path.dirname(__file__)
|
||||
IMAGE_DIR = path.join(GAME_DIR, "..", "asset", "image")
|
||||
SOUND_DIR = path.join(GAME_DIR, "..", "asset", "sound")
|
||||
MAP_DIR = path.join(GAME_DIR, "..", "asset", 'maps')
|
||||
|
||||
'''BG View'''
|
||||
TITLE = "TankMan!"
|
||||
BG_COLOR = DARKGREY
|
||||
TILE_X_SIZE = 50
|
||||
TILE_Y_SIZE = 50
|
||||
TILE_SIZE = 50
|
||||
TEXT_SIZE = 100
|
||||
|
||||
'''object size'''
|
||||
ALL_OBJECT_SIZE = pygame.Rect(0, 0, 50, 50)
|
||||
BULLET_SIZE = (13, 16)
|
||||
LIVES_SIZE = (30, 25)
|
||||
|
||||
"""all setting"""
|
||||
DOWN_IMG = 'down'
|
||||
RIGHT_IMG = 'right'
|
||||
UP_IMG = 'up'
|
||||
LEFT_IMG = 'left'
|
||||
|
||||
"""collide setting"""
|
||||
WITH_PLAYER = 'player'
|
||||
|
||||
"""map data numbers"""
|
||||
PLAYER_1_IMG_NO = 1
|
||||
PLAYER_2_IMG_NO = 2
|
||||
WALL_IMG_NO = 3
|
||||
BULLET_STATION_IMG_NO = 4
|
||||
OIL_STATION_IMG_NO = 5
|
||||
WALL_LIVE = 4
|
||||
BULLET_SPEED = 30
|
||||
BULLET_TRAVEL_DISTANCE = 300
|
||||
|
||||
"""music"""
|
||||
BGM = 'background_music.ogg/.wav/.mp3'
|
||||
MENU_SND = 'MenuTheme.ogg/.wav/.mp3'
|
||||
|
44
TankMan/src/game_module/SoundController.py
Normal file
44
TankMan/src/game_module/SoundController.py
Normal file
@ -0,0 +1,44 @@
|
||||
from os import path
|
||||
|
||||
import pygame.mixer
|
||||
|
||||
|
||||
def create_sounds_data(id: str, name: str):
|
||||
return {
|
||||
"_id": id
|
||||
, "_name": name
|
||||
}
|
||||
|
||||
|
||||
def create_bgm_data(name: str, volume: float):
|
||||
return {
|
||||
"_name": name
|
||||
, "_volume": volume
|
||||
}
|
||||
|
||||
|
||||
class SoundController:
|
||||
def __init__(self, sound_path: str, sounds_data_list: list):
|
||||
self._sound_path = sound_path
|
||||
if not self._sound_path:
|
||||
return
|
||||
self._sounds_obj = {}
|
||||
pygame.mixer.init()
|
||||
for sounds_data in sounds_data_list:
|
||||
sound_data = path.join(self._sound_path, sounds_data["_name"])
|
||||
self._sounds_obj[sounds_data["_id"]] = pygame.mixer.Sound(sound_data)
|
||||
|
||||
def play_music(self, bgm_data: dict) -> None:
|
||||
if not self._sound_path:
|
||||
return
|
||||
pygame.mixer.init()
|
||||
pygame.mixer.music.load(path.join(self._sound_path, bgm_data["_name"]))
|
||||
pygame.mixer.music.set_volume(bgm_data["_volume"])
|
||||
pygame.mixer.music.play(-1)
|
||||
|
||||
def play_sound(self, id: str, volume: float, maz_time: int) -> None:
|
||||
if not self._sound_path:
|
||||
return
|
||||
sound_obj = self._sounds_obj[id]
|
||||
sound_obj.set_volume(volume)
|
||||
sound_obj.play(maxtime=maz_time)
|
68
TankMan/src/game_module/TiledMap.py
Normal file
68
TankMan/src/game_module/TiledMap.py
Normal file
@ -0,0 +1,68 @@
|
||||
import pytmx
|
||||
|
||||
|
||||
def create_construction(_id: int or str, _no: int, _init_pos: tuple, _init_size: tuple):
|
||||
return {
|
||||
"_id": _id
|
||||
, "_no": _no
|
||||
, "_init_pos": _init_pos
|
||||
, "_init_size": _init_size
|
||||
}
|
||||
|
||||
|
||||
# Map 讀取地圖資料
|
||||
class TiledMap:
|
||||
def __init__(self, filepath: str):
|
||||
tm = pytmx.TiledMap(filepath)
|
||||
self.tile_width = tm.tilewidth
|
||||
self.tile_height = tm.tileheight
|
||||
self.width = tm.width
|
||||
self.height = tm.height
|
||||
self.map_width = self.tile_width * self.width
|
||||
self.map_height = self.tile_height * self.height
|
||||
self.tmx_data = tm
|
||||
self._is_record = False
|
||||
self.all_pos_list = []
|
||||
self.empty_pos_list = []
|
||||
self.empty_quadrant_pos_dict = {1: [], 2: [], 3: [], 4: []}
|
||||
self.all_obj_data_dict = {}
|
||||
# TODO refactor
|
||||
self.all_obj = {}
|
||||
|
||||
def add_init_obj_data(self, img_id: int, cls, **kwargs):
|
||||
obj_data = {img_id: {"cls": cls,
|
||||
"kwargs": kwargs
|
||||
}
|
||||
}
|
||||
self.all_obj_data_dict.update(obj_data)
|
||||
self.all_obj[img_id] = []
|
||||
|
||||
def create_init_obj_dict(self) -> dict:
|
||||
obj_no = 0
|
||||
for layer in self.tmx_data.visible_layers:
|
||||
for x, y, gid, in layer:
|
||||
if isinstance(layer, pytmx.TiledTileLayer):
|
||||
pos = (x * self.tile_width, y * self.tile_height)
|
||||
if not self._is_record:
|
||||
self.all_pos_list.append(pos)
|
||||
if not self._is_record and not gid: # 0代表空格,無圖塊
|
||||
self.empty_pos_list.append(pos)
|
||||
if pos[0] >= self.map_width // 2 and pos[1] < self.map_height // 2:
|
||||
self.empty_quadrant_pos_dict[1].append(pos)
|
||||
elif pos[0] < self.map_width // 2 and pos[1] < self.map_height // 2:
|
||||
self.empty_quadrant_pos_dict[2].append(pos)
|
||||
elif pos[0] < self.map_width // 2 and pos[1] >= self.map_height // 2:
|
||||
self.empty_quadrant_pos_dict[3].append(pos)
|
||||
else:
|
||||
self.empty_quadrant_pos_dict[4].append(pos)
|
||||
elif gid:
|
||||
img_id = layer.parent.tiledgidmap[gid]
|
||||
kwargs = self.all_obj_data_dict[img_id]["kwargs"]
|
||||
obj_no += 1
|
||||
img_info = {"_id": img_id, "_no": obj_no
|
||||
, "_init_pos": pos
|
||||
, "_init_size": (self.tile_width, self.tile_height)
|
||||
}
|
||||
self.all_obj[img_id].append(self.all_obj_data_dict[img_id]["cls"](img_info, **kwargs))
|
||||
self._is_record = True
|
||||
return self.all_obj
|
0
TankMan/src/game_module/__init__.py
Normal file
0
TankMan/src/game_module/__init__.py
Normal file
26
TankMan/src/game_module/fuctions.py
Normal file
26
TankMan/src/game_module/fuctions.py
Normal file
@ -0,0 +1,26 @@
|
||||
import pygame.sprite
|
||||
|
||||
|
||||
def get_size(sprite: pygame.sprite.Sprite):
|
||||
return sprite.rect.width, sprite.rect.height
|
||||
|
||||
|
||||
def set_topleft(sprite: pygame.sprite.Sprite, top_left: tuple):
|
||||
sprite.rect.topleft = top_left
|
||||
|
||||
|
||||
def add_score(sprite: pygame.sprite.Sprite, score: int):
|
||||
sprite.score += score
|
||||
|
||||
|
||||
def set_shoot(sprite: pygame.sprite.Sprite, is_shoot: bool):
|
||||
sprite.is_shoot = is_shoot
|
||||
|
||||
|
||||
def get_sprites_progress_data(sprites: pygame.sprite.Group):
|
||||
data_list = []
|
||||
for sprite in sprites:
|
||||
data = sprite.get_obj_progress_data()
|
||||
if data:
|
||||
data_list.append(data)
|
||||
return data_list
|
11
TankMan/src/game_module/test_case.py
Normal file
11
TankMan/src/game_module/test_case.py
Normal file
@ -0,0 +1,11 @@
|
||||
from src.template.Player import Player
|
||||
|
||||
|
||||
class TestPlayer(object):
|
||||
construction = {"_id": 0, "_no": 0, "x": 0, "y": 0, "width": 60, "height": 60}
|
||||
player = Player(construction)
|
||||
|
||||
def test_get_xy_pos(self):
|
||||
assert type(self.player.get_xy_pos()) == tuple
|
||||
assert type(self.player.get_xy_pos()[0]) == int
|
||||
assert type(self.player.get_xy_pos()[1]) == int
|
Reference in New Issue
Block a user