2023-09-27 18:03:37 +00:00
|
|
|
import os
|
|
|
|
import pygame
|
2023-11-23 11:44:23 +00:00
|
|
|
import numpy as np
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-10-04 02:37:28 +00:00
|
|
|
from random import choice
|
2023-09-27 18:03:37 +00:00
|
|
|
|
|
|
|
from configs.system.window_config import TILESIZE
|
|
|
|
|
|
|
|
from utils.debug import debug
|
|
|
|
from utils.resource_loader import import_csv_layout, import_folder
|
|
|
|
|
|
|
|
from interface.ui import UI
|
|
|
|
|
|
|
|
from entities.observer import Observer
|
|
|
|
from entities.player import Player
|
2023-10-04 02:37:28 +00:00
|
|
|
from entities.enemy import Enemy
|
2023-09-27 18:03:37 +00:00
|
|
|
|
|
|
|
from .terrain import Tile
|
|
|
|
from .camera import Camera
|
|
|
|
|
|
|
|
|
|
|
|
class Level:
|
|
|
|
|
2023-11-25 11:47:07 +00:00
|
|
|
def __init__(self, n_players, reset=False):
|
2023-09-27 18:03:37 +00:00
|
|
|
|
|
|
|
# General Settings
|
|
|
|
self.game_paused = False
|
2023-11-17 02:19:03 +00:00
|
|
|
self.done = False
|
2023-11-14 21:44:43 +00:00
|
|
|
|
2023-09-27 18:03:37 +00:00
|
|
|
# Get display surface
|
|
|
|
self.display_surface = pygame.display.get_surface()
|
|
|
|
|
|
|
|
# Sprite Group setup
|
|
|
|
self.visible_sprites = Camera()
|
|
|
|
self.obstacle_sprites = pygame.sprite.Group()
|
|
|
|
self.attack_sprites = pygame.sprite.Group()
|
|
|
|
self.attackable_sprites = pygame.sprite.Group()
|
|
|
|
|
|
|
|
# Sprite setup and entity generation
|
2023-11-25 11:47:07 +00:00
|
|
|
self.create_map(n_players)
|
2023-11-14 21:44:43 +00:00
|
|
|
self.get_players_enemies()
|
|
|
|
self.get_distance_direction()
|
2023-11-19 03:27:47 +00:00
|
|
|
if not reset:
|
|
|
|
for player in self.player_sprites:
|
|
|
|
player.get_max_num_states()
|
|
|
|
player.setup_agent()
|
|
|
|
else:
|
|
|
|
for player in self.player_sprites:
|
|
|
|
player.get_max_num_states()
|
2023-11-23 11:44:23 +00:00
|
|
|
self.dead_players = np.zeros(len(self.player_sprites))
|
2023-09-27 18:03:37 +00:00
|
|
|
|
|
|
|
# UI setup
|
|
|
|
self.ui = UI()
|
2023-10-04 02:37:28 +00:00
|
|
|
|
2023-11-25 11:47:07 +00:00
|
|
|
def create_map(self, n_players):
|
2023-11-14 21:44:43 +00:00
|
|
|
player_id = 0
|
2023-09-27 18:03:37 +00:00
|
|
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
asset_path = os.path.join(
|
2023-11-14 21:44:43 +00:00
|
|
|
script_dir, '..', 'assets')
|
2023-09-27 18:03:37 +00:00
|
|
|
layouts = {
|
|
|
|
'boundary': import_csv_layout(f"{asset_path}/map/FloorBlocks.csv"),
|
|
|
|
'grass': import_csv_layout(f"{asset_path}/map/Grass.csv"),
|
|
|
|
'objects': import_csv_layout(f"{asset_path}/map/Objects.csv"),
|
|
|
|
'entities': import_csv_layout(f"{asset_path}/map/Entities.csv")
|
|
|
|
}
|
|
|
|
|
|
|
|
graphics = {
|
|
|
|
'grass': import_folder(f"{asset_path}/graphics/grass"),
|
|
|
|
'objects': import_folder(f"{asset_path}/graphics/objects")
|
|
|
|
}
|
|
|
|
|
|
|
|
for style, layout in layouts.items():
|
|
|
|
for row_index, row in enumerate(layout):
|
|
|
|
for col_index, col in enumerate(row):
|
|
|
|
if col != '-1':
|
|
|
|
x = col_index * TILESIZE
|
|
|
|
y = row_index * TILESIZE
|
|
|
|
if style == 'boundary':
|
|
|
|
Tile((x, y), [self.obstacle_sprites], 'invisible')
|
|
|
|
|
|
|
|
if style == 'grass':
|
|
|
|
random_grass_image = choice(graphics['grass'])
|
|
|
|
Tile((x, y), [self.visible_sprites, self.obstacle_sprites,
|
|
|
|
self.attackable_sprites], 'grass', random_grass_image)
|
2023-11-19 03:27:47 +00:00
|
|
|
#
|
|
|
|
# if style == 'objects':
|
|
|
|
# surf = graphics['objects'][int(col)]
|
|
|
|
# Tile((x, y), [self.visible_sprites,
|
|
|
|
# self.obstacle_sprites], 'object', surf)
|
2023-11-17 02:19:03 +00:00
|
|
|
|
|
|
|
if style == 'entities':
|
|
|
|
# The numbers represent their IDs in .csv files generated from TILED.
|
|
|
|
if col == '500':
|
|
|
|
self.observer = Observer(
|
|
|
|
(x, y), [self.visible_sprites])
|
|
|
|
|
|
|
|
elif col == '400':
|
2023-11-25 11:47:07 +00:00
|
|
|
if choice([0, 1]) == 1 and player_id <= n_players:
|
|
|
|
# Player Generation
|
|
|
|
Player(
|
|
|
|
(x, y),
|
|
|
|
[self.visible_sprites],
|
|
|
|
self.obstacle_sprites,
|
|
|
|
self.visible_sprites,
|
|
|
|
self.attack_sprites,
|
|
|
|
self.attackable_sprites,
|
|
|
|
'tank',
|
|
|
|
player_id)
|
|
|
|
|
|
|
|
player_id += 1
|
2023-11-17 02:19:03 +00:00
|
|
|
|
|
|
|
elif col == '401':
|
|
|
|
# Player Generation
|
|
|
|
Player(
|
2023-11-24 23:26:47 +00:00
|
|
|
(x, y),
|
|
|
|
[self.visible_sprites],
|
|
|
|
self.obstacle_sprites,
|
|
|
|
self.visible_sprites,
|
|
|
|
self.attack_sprites,
|
|
|
|
self.attackable_sprites,
|
|
|
|
'warrior',
|
|
|
|
player_id)
|
|
|
|
|
2023-11-17 02:19:03 +00:00
|
|
|
player_id += 1
|
|
|
|
|
|
|
|
elif col == '402':
|
|
|
|
# Player Generation
|
|
|
|
Player(
|
2023-11-24 23:26:47 +00:00
|
|
|
(x, y),
|
|
|
|
[self.visible_sprites],
|
|
|
|
self.obstacle_sprites,
|
|
|
|
self.visible_sprites,
|
|
|
|
self.attack_sprites,
|
|
|
|
self.attackable_sprites,
|
|
|
|
'mage',
|
|
|
|
player_id)
|
|
|
|
|
2023-11-14 21:44:43 +00:00
|
|
|
player_id += 1
|
2023-09-27 18:03:37 +00:00
|
|
|
|
|
|
|
else:
|
2023-10-04 02:37:28 +00:00
|
|
|
# Monster Generation
|
|
|
|
if col == '390':
|
|
|
|
monster_name = 'bamboo'
|
|
|
|
elif col == '391':
|
|
|
|
monster_name = 'spirit'
|
|
|
|
elif col == '392':
|
|
|
|
monster_name = 'raccoon'
|
2023-11-24 23:26:47 +00:00
|
|
|
elif col == ' 393':
|
2023-10-04 02:37:28 +00:00
|
|
|
monster_name = 'squid'
|
|
|
|
|
2023-11-24 23:26:47 +00:00
|
|
|
Enemy(monster_name,
|
|
|
|
(x, y),
|
|
|
|
[
|
|
|
|
self.visible_sprites,
|
|
|
|
self.attackable_sprites
|
|
|
|
],
|
|
|
|
self.visible_sprites,
|
|
|
|
self.obstacle_sprites)
|
2023-10-04 02:37:28 +00:00
|
|
|
|
|
|
|
def get_players_enemies(self):
|
|
|
|
self.player_sprites = [sprite for sprite in self.visible_sprites.sprites(
|
|
|
|
) if hasattr(sprite, 'sprite_type') and sprite.sprite_type in ('player')]
|
|
|
|
|
|
|
|
self.enemy_sprites = [sprite for sprite in self.visible_sprites.sprites(
|
|
|
|
) if hasattr(sprite, 'sprite_type') and sprite.sprite_type in ('enemy')]
|
|
|
|
|
|
|
|
def get_distance_direction(self):
|
|
|
|
for player in self.player_sprites:
|
|
|
|
player.distance_direction_from_enemy = []
|
|
|
|
|
|
|
|
for enemy in self.enemy_sprites:
|
|
|
|
enemy.distance_direction_from_player = []
|
|
|
|
|
|
|
|
for player in self.player_sprites:
|
|
|
|
player_vector = pygame.math.Vector2(player.rect.center)
|
|
|
|
for enemy in self.enemy_sprites:
|
|
|
|
enemy_vector = pygame.math.Vector2(enemy.rect.center)
|
|
|
|
distance = (player_vector - enemy_vector).magnitude()
|
|
|
|
|
|
|
|
if distance > 0:
|
|
|
|
direction = (player_vector - enemy_vector).normalize()
|
|
|
|
else:
|
|
|
|
direction = pygame.math.Vector2()
|
|
|
|
|
|
|
|
enemy.distance_direction_from_player.append(
|
|
|
|
(distance, direction, player))
|
|
|
|
player.distance_direction_from_enemy.append(
|
|
|
|
(distance, -direction, enemy))
|
|
|
|
|
|
|
|
def apply_damage_to_player(self):
|
|
|
|
for enemy in self.enemy_sprites:
|
|
|
|
for distance, _, player in enemy.distance_direction_from_player:
|
|
|
|
if distance < enemy.stats.attack_radius and player._input.combat.vulnerable:
|
|
|
|
player.stats.health -= enemy.stats.attack
|
|
|
|
player._input.combat.vulnerable = False
|
|
|
|
player._input.combat.hurt_time = pygame.time.get_ticks()
|
2023-09-27 18:03:37 +00:00
|
|
|
|
|
|
|
def toggle_menu(self):
|
|
|
|
self.game_paused = not self.game_paused
|
|
|
|
|
2023-10-04 02:37:28 +00:00
|
|
|
def run(self, who='observer'):
|
2023-09-27 18:03:37 +00:00
|
|
|
# Draw the game
|
2023-10-04 02:37:28 +00:00
|
|
|
if who == 'observer':
|
|
|
|
self.visible_sprites.custom_draw(self.observer)
|
|
|
|
self.ui.display(self.observer)
|
2023-11-24 23:26:47 +00:00
|
|
|
else:
|
|
|
|
self.visible_sprites.custom_draw(self.player)
|
|
|
|
self.ui.display(self.aaa)
|
2023-11-13 12:34:22 +00:00
|
|
|
|
2023-11-23 11:44:23 +00:00
|
|
|
debug('v0.8')
|
2023-11-13 12:34:22 +00:00
|
|
|
|
2023-09-27 18:03:37 +00:00
|
|
|
if not self.game_paused:
|
|
|
|
# Update the game
|
2023-10-04 02:37:28 +00:00
|
|
|
for player in self.player_sprites:
|
|
|
|
player.attack_logic()
|
|
|
|
|
|
|
|
self.get_players_enemies()
|
|
|
|
self.get_distance_direction()
|
2023-09-27 18:03:37 +00:00
|
|
|
self.visible_sprites.update()
|
2023-11-23 15:37:02 +00:00
|
|
|
self.apply_damage_to_player()
|
2023-10-04 02:37:28 +00:00
|
|
|
|
2023-09-27 18:03:37 +00:00
|
|
|
else:
|
2023-11-13 12:34:22 +00:00
|
|
|
debug('PAUSED')
|
2023-11-23 15:37:02 +00:00
|
|
|
|
|
|
|
for player in self.player_sprites:
|
|
|
|
if player.is_dead():
|
|
|
|
self.dead_players[player.player_id] = True
|
|
|
|
|
|
|
|
self.done = True if self.dead_players.all() == 1 else False
|