pre commit, make CAPS tasks from main

This commit is contained in:
shiva404
2026-02-20 02:09:43 +03:00
parent 197469350d
commit 171eff1434
10 changed files with 284 additions and 126 deletions

Binary file not shown.

Binary file not shown.

1
classes.py Normal file
View File

@@ -0,0 +1 @@
from eb_terrain_objects import Rock

View File

@@ -3,9 +3,15 @@ import json
import uuid import uuid
from dataclasses import dataclass, field from dataclasses import dataclass, field
from copy import deepcopy from copy import deepcopy
from collections import defaultdict
from heapq import heappush, heappop
import pygame import pygame
import pygame_gui import pygame_gui
from classes import Rock
def path_exists(data, path): def path_exists(data, path):
current = data current = data
for key in path: for key in path:
@@ -14,3 +20,56 @@ def path_exists(data, path):
else: else:
return False return False
return True return True
def find_way(cells, start, goal):
"""Находит путь от start=(row, col) к goal=(row, col). row=y (строка), col=x (столбец)"""
rows = len(cells)
if rows == 0:
return None
cols = len(cells[0])
def is_passable(cell):
# Проходимо, если НЕ Rock в terrain И creature_obj отсутствует
return (cell.terrain_obj is None or not isinstance(cell.terrain_obj, Rock))
# Проверка границ и проходимости старт/гол
s_row, s_col = start
g_row, g_col = goal
if not (0 <= s_row < rows and 0 <= s_col < cols and 0 <= g_row < rows and 0 <= g_col < cols):
return None
start_cell = cells[s_row][s_col]
goal_cell = cells[g_row][g_col]
if not is_passable(start_cell) or not is_passable(goal_cell):
print(f"Старт/гол непроходимы: start={is_passable(start_cell)}, goal={is_passable(goal_cell)}")
return None
# A* поиск (используем прямые row,col вместо ID для простоты)
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] # вверх, вниз, лево, право
open_set = []
# f_score = g + h (h=манхэттен)
h = abs(s_row - g_row) + abs(s_col - g_col)
heappush(open_set, (h, 0, s_row, s_col)) # f, g, row, col
came_from = {}
g_score = defaultdict(lambda: float('inf'))
g_score[(s_row, s_col)] = 0
while open_set:
_, _, row, col = heappop(open_set)
if (row, col) == (g_row, g_col):
# Восстанавливаем путь
path = []
current = (row, col)
while current in came_from:
path.append(current)
current = came_from[current]
path.append(start)
return path[::-1]
for dr, dc in directions:
nr, nc = row + dr, col + dc
if 0 <= nr < rows and 0 <= nc < cols:
if is_passable(cells[nr][nc]):
tentative_g = g_score[(row, col)] + 1
pos = (nr, nc)
if tentative_g < g_score[pos]:
came_from[pos] = (row, col)
g_score[pos] = tentative_g
f = tentative_g + abs(nr - g_row) + abs(nc - g_col)
heappush(open_set, (f, tentative_g, nr, nc))
print("Путь не найден (нет связи)")
return None

View File

@@ -30,7 +30,7 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -928,6 +928,15 @@
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
}, },
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "rock_small"
},
"item_obj": {},
"creature_obj": {}
},
{ {
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
@@ -937,6 +946,15 @@
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
}, },
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "rock_small"
},
"item_obj": {},
"creature_obj": {}
},
{ {
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
@@ -977,7 +995,7 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -1013,25 +1031,7 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "grass_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "grass_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -1843,7 +1843,25 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "rock_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "rock_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -1888,7 +1906,16 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "rock_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -1924,34 +1951,7 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "grass_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "grass_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "grass_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -2763,7 +2763,7 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -2826,7 +2826,16 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "rock_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -2844,7 +2853,7 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -2853,16 +2862,7 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "grass_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -3638,7 +3638,16 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "rock_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -3674,7 +3683,7 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -3683,7 +3692,7 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -3692,16 +3701,7 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "grass_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -4549,7 +4549,7 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -6335,7 +6335,25 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "rock_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "rock_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -6380,7 +6398,16 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "rock_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -6416,7 +6443,7 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -6425,7 +6452,7 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}
@@ -6434,34 +6461,7 @@
"terrain_obj": { "terrain_obj": {
"id": "1", "id": "1",
"name": "2", "name": "2",
"sprite_name": "grass_small" "sprite_name": "rock_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "grass_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "grass_small"
},
"item_obj": {},
"creature_obj": {}
},
{
"terrain_obj": {
"id": "1",
"name": "2",
"sprite_name": "grass_small"
}, },
"item_obj": {}, "item_obj": {},
"creature_obj": {} "creature_obj": {}

View File

@@ -15,9 +15,11 @@ main_dir = os.path.dirname(os.path.abspath(__file__))
sprites_dir = os.path.join(main_dir, "res", "sprites") sprites_dir = os.path.join(main_dir, "res", "sprites")
#class Render #class Render
#class ObjectManager
#class MapManager #class MapManager
#class Event #class Event
#class EventManager #class EventManager
#class Control
@dataclass @dataclass
class Cell: class Cell:
@@ -60,9 +62,17 @@ class Map:
self.cells[line].append(final_cell) self.cells[line].append(final_cell)
def move_obj(self, type, s_x, s_y, d_x, d_y): def move_obj(self, type, start, goal):
if d_y >= len(self.cells) or d_x >= len(self.cells[s_y]) or s_y >= len(self.cells) or s_x >= len(self.cells[s_y]): """Перемещает объект типа 'terrain_obj', 'item_obj' или 'creature_obj'
из клетки start=(row, col) в goal=(row, col)"""
s_y, s_x = start
d_y, d_x = goal
# Проверка границ
if (s_y >= len(self.cells) or s_x >= len(self.cells[s_y]) or
d_y >= len(self.cells) or d_x >= len(self.cells[d_y])):
return False return False
source_cell = self.cells[s_y][s_x] source_cell = self.cells[s_y][s_x]
dest_cell = self.cells[d_y][d_x] dest_cell = self.cells[d_y][d_x]
obj = getattr(source_cell, type) obj = getattr(source_cell, type)
@@ -74,8 +84,23 @@ class Map:
setattr(dest_cell, type, obj) setattr(dest_cell, type, obj)
return True return True
def get_way(self, s_x, s_y, d_x, d_y): def get_cell_at_mouse(self, mouse_pos):
pass """Возвращает индексы клетки (row, col) по позиции мыши или None если вне карты"""
mx, my = mouse_pos
# Переводим экранные координаты в координаты карты с учетом камеры и масштаба
map_x = (mx - self.cam_x * self.scale) / self.scale / self.size
map_y = (my - self.cam_y * self.scale) / self.scale / self.size
# Получаем индексы ближайшей клетки
col = int(map_x)
row = int(map_y)
# Проверяем границы карты
if (row < 0 or row >= len(self.cells) or
col < 0 or col >= len(self.cells[row])):
return None
return (row, col)
def draw_map(self, screen, current_frame, grid = True): def draw_map(self, screen, current_frame, grid = True):
for j in range(len(self.cells)): for j in range(len(self.cells)):
@@ -97,8 +122,19 @@ class Map:
cell.creature_obj.draw(dd) cell.creature_obj.draw(dd)
if grid: if grid:
if cell.is_target:
pygame.draw.rect(screen, self.target_color, pygame.Rect(dd["x"], dd["y"], dd["w"], dd["h"]), self.bord)
else:
pygame.draw.rect(screen, self.color, pygame.Rect(dd["x"], dd["y"], dd["w"], dd["h"]), self.bord) pygame.draw.rect(screen, self.color, pygame.Rect(dd["x"], dd["y"], dd["w"], dd["h"]), self.bord)
def update_map(self, current_frame):
for j in range(len(self.cells)):
for i, cell in enumerate(self.cells[j]):
if cell.creature_obj is not None and len(cell.creature_obj.waypoints) > 1 and current_frame % 57 == 0:
del cell.creature_obj.waypoints[0]
self.move_obj("creature_obj", (j, i), (cell.creature_obj.waypoints[0]))
@dataclass @dataclass
class Engine: class Engine:
sprites: dict = field(default_factory = dict) sprites: dict = field(default_factory = dict)
@@ -186,6 +222,11 @@ class Engine:
def main_loop(self): def main_loop(self):
easy_map = Map("def_map.json", self.cached_sprites) easy_map = Map("def_map.json", self.cached_sprites)
#print(easy_map.find_way((1, 1), (5, 5)))
#print(easy_map.find_way((0, 0), (1, 1)))
#print(easy_map.find_way((0, 0), (0, 1)))
#print(easy_map.find_way((0, 0), (0, 0)))
#sp = eb_objects.Sprite(self.sprites, "elf_watching") #sp = eb_objects.Sprite(self.sprites, "elf_watching")
#gr = pygame.image.load(file_path).convert_alpha() #gr = pygame.image.load(file_path).convert_alpha()
@@ -210,6 +251,8 @@ class Engine:
global_counter = 0 global_counter = 0
global_counter_cap = 100000 global_counter_cap = 100000
active_cell = []
# profiling # profiling
process = psutil.Process(os.getpid()) process = psutil.Process(os.getpid())
@@ -262,12 +305,13 @@ class Engine:
# fill the screen with a color to wipe away anything from last frame # fill the screen with a color to wipe away anything from last frame
#self.screen.fill("chartreuse4") #self.screen.fill("chartreuse4")
self.screen.blit(background, (0, 0)) self.screen.blit(background, (0, 0))
easy_map.update_map(global_counter)
easy_map.draw_map(self.screen, current_frame + 1) easy_map.draw_map(self.screen, current_frame + 1)
manager.draw_ui(self.screen) manager.draw_ui(self.screen)
if not console_active: if not console_active:
keys = pygame.key.get_pressed() keys = pygame.key.get_pressed()
if keys[pygame.K_w]: if keys[pygame.K_w]:
easy_map.cam_y += self.camera_step easy_map.cam_y += self.camera_step
if keys[pygame.K_s]: if keys[pygame.K_s]:
@@ -276,6 +320,7 @@ class Engine:
easy_map.cam_x += self.camera_step easy_map.cam_x += self.camera_step
if keys[pygame.K_d]: if keys[pygame.K_d]:
easy_map.cam_x -= self.camera_step easy_map.cam_x -= self.camera_step
if keys[pygame.K_q]: if keys[pygame.K_q]:
easy_map.scale += self.scale_step easy_map.scale += self.scale_step
self.spr_scale += self.scale_step self.spr_scale += self.scale_step
@@ -284,6 +329,27 @@ class Engine:
easy_map.scale -= self.scale_step easy_map.scale -= self.scale_step
self.spr_scale -= self.scale_step self.spr_scale -= self.scale_step
self.scale_sprites() self.scale_sprites()
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: # Левая кнопка
mouse_pos = pygame.mouse.get_pos()
console_rect = console_window.get_abs_rect()
if not console_rect.collidepoint(mouse_pos):
cell_coords = easy_map.get_cell_at_mouse(mouse_pos)
if cell_coords:
if active_cell:
easy_map.cells[active_cell[0]][active_cell[1]].is_target = False
active_cell = cell_coords
easy_map.cells[active_cell[0]][active_cell[1]].is_target = True
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 3:
mouse_pos = pygame.mouse.get_pos()
cell_coords = easy_map.get_cell_at_mouse(mouse_pos)
console_rect = console_window.get_abs_rect()
if not console_rect.collidepoint(mouse_pos) and easy_map.cells[active_cell[0]][active_cell[1]].creature_obj is not None:
easy_map.cells[active_cell[0]][active_cell[1]].creature_obj.move(easy_map.cells, (active_cell[0], active_cell[1]), (cell_coords[0], cell_coords[1]))
if keys[pygame.K_ESCAPE]: if keys[pygame.K_ESCAPE]:
running = False running = False
@@ -298,6 +364,6 @@ class Engine:
if global_counter % 10 == 0: if global_counter % 10 == 0:
current_fps = clock.get_fps() current_fps = clock.get_fps()
#print(f"Current FPS: {current_fps:.2f}") print(f"Current FPS: {current_fps:.2f}")
pygame.quit() pygame.quit()

View File

@@ -1,4 +1,4 @@
from common import deepcopy, dataclass, field, path_exists from common import deepcopy, dataclass, field
@dataclass @dataclass
class Object: class Object:
@@ -6,7 +6,8 @@ class Object:
name: str name: str
sprite_name: str sprite_name: str
sprite_state: int = 0 sprite_state: int = 0
# current_map
# pos
# weight # weight
# effects = {} # effects = {}
@@ -30,7 +31,7 @@ class Terrain(Object):
@dataclass @dataclass
class Creature(Object): class Creature(Object):
current_action: int = 0 waypoints: list = field(default_factory = list)
quick_actions: list = field(default_factory = list) quick_actions: list = field(default_factory = list)
tasks: list = field(default_factory = list) tasks: list = field(default_factory = list)
inventory: dict = field(default_factory = dict) inventory: dict = field(default_factory = dict)
@@ -38,6 +39,10 @@ class Creature(Object):
def update(self): def update(self):
pass pass
def move(self, cells, start, goal):
from common import find_way
self.waypoints = find_way(cells, start, goal)
@dataclass @dataclass
class Item(Object): class Item(Object):

27
main.py
View File

@@ -37,8 +37,35 @@ if __name__ == "__main__":
# - deepcopy + # - deepcopy +
# - общие + # - общие +
main() main()
#
# ДОДЕЛАТЬ move для Creature - хранить pos в объекте
#
# ПРОВЕРИТЬ МЕНЯЕТСЯ ЛИ ПЕРЕДАННЫЙ В ОБЪЕКТ cells и еслт да,
# перенести всё взаимодействие с картой в объекты, карта только хранит cells
# и готовит данные для отрисовки Render'ом
#
# ИГРОВОЙ ТАКТ? или только для действий их длительность?
#
# ПОСМОТРЕТЬ ПО КОММИТАМ ЗАЧЕМ БЫЛ НУЖЕН path_exists, удалить?
#
# добавил гуй, динамическая консоль, всё работает, но: # добавил гуй, динамическая консоль, всё работает, но:
# - слоп, почистить # - слоп, почистить
# - мини-баг - если первые вводимые буквы совпадают с клавишами управления, один раз успевает проскочить до лока. некритично. # - мини-баг - если первые вводимые буквы совпадают с клавишами управления, один раз успевает проскочить до лока. некритично.
# - при вводе текста нет прокрутки к концу # - при вводе текста нет прокрутки к концу
# - плавающий баг - если повводить текст, а потом закрыть консоль, игра не закроется по эскейпу. # - плавающий баг - если повводить текст, а потом закрыть консоль, игра не закроется по эскейпу.
#
# исправить поиск пути чтобы он учитывал других существ
#
# в дальнейшем вся отрисовка переедет в класс рендер,
# карта будет только вовзращать поверхность для отрисовки или даже просто Cells
# active_cell переедет в класс Control
#
# НАЙТИ В КОДЕ ГДЕ Я ТАК НЕ СДЕЛАЛ И ИСПРАВИТЬ - НАШЕЛ ОДНУ, ПОИСКАТЬ ЕЩЕ
#if a is None:
# print("a это точно None")
#
# Альтернатива
#if a is not None:
# print("a не None")
#
#