Compare commits

..
4 Commits
Author SHA1 Message Date
nejdetckenobi 9114238ef8 Restructures project 2026-10-17 18:02:09 +03:00
nejdetckenobi 863f39c2d2 Bumps ECS 2026-10-17 18:01:59 +03:00
nejdetckenobi 8c414fe294 Modifies path creation and deletion 2026-10-17 17:01:58 +03:00
nejdetckenobi 00fb80382b Bumps ECS framework 2026-10-17 17:01:46 +03:00
10 changed files with 179 additions and 111 deletions

No files matched your search

Submodule libs/ecs updated: 4769b8d707...ce5eedbea2.
+6
View File
@@ -0,0 +1,6 @@
from src.game import Game
if __name__ == "__main__":
game = Game()
game.run()
+6 -1
View File
@@ -38,7 +38,12 @@ class Position(Vector2D):
@dataclass @dataclass
class Target(Vector2D): class Source(Vector2D):
pass
@dataclass
class Destination(Vector2D):
pass pass
+85
View File
@@ -0,0 +1,85 @@
from dataclasses import dataclass, field
from random import randint
from typing import cast
import pyglet
from pyglet.window import Window
from pyglet.window.mouse import LEFT
from pyglet.shapes import Circle, Line
from src.components import (
PlanetMarker,
Position,
Radius,
SpaceShipMarker,
Vector2D,
)
from src.helpers import generate_random_planet
from src.world import world
from settings import FPS, FULLSCREEN, SCREEN_WIDTH, SCREEN_HEIGHT
class Game(Window):
def __init__(self, *args, **kwargs) -> None:
super(Game, self).__init__(
caption="SpaceTrader",
width=SCREEN_WIDTH,
height=SCREEN_HEIGHT,
fullscreen=FULLSCREEN,
)
self.world = world
for _ in range(12):
generate_random_planet(self.world)
def on_draw(self):
self.clear()
for _, line in self.world.state.routes.items():
line.draw()
for _, component_storage in self.world.query(SpaceShipMarker):
component_storage[Circle].draw()
for _, component_storage in self.world.query(PlanetMarker):
component_storage[Circle].draw()
def on_mouse_release(self, x, y, button, modifiers):
if button != LEFT:
return
closest_entity, component_storage = min(
self.world.query(PlanetMarker),
key=lambda p: abs(p[1][Position] - Vector2D(x, y)),
)
pos = component_storage[Position]
if component_storage[Radius].value < abs(pos - Vector2D(x, y)):
print("DESELECT")
self.world.state.selected_planet = None
elif self.world.state.selected_planet is None:
print("SELECT")
self.world.state.selected_planet = closest_entity
elif self.world.state.selected_planet != closest_entity:
first_planet, second_planet = (
(self.world.state.selected_planet, closest_entity)
if self.world.state.selected_planet > closest_entity
else (closest_entity, self.world.state.selected_planet)
)
fpos: Position = self.world.get_components(first_planet)[Position]
spos: Position = self.world.get_components(second_planet)[Position]
route_key = (first_planet, second_planet)
if route_key in self.world.state.routes:
print("ROUTE DELETE")
self.world.state.routes.pop(route_key)
else:
print("ROUTE CREATE")
self.world.state.routes[route_key] = Line(
x=fpos.x,
y=fpos.y,
x2=spos.x,
y2=spos.y,
)
self.world.state.selected_planet = None
def on_update(self, dt):
self.world.tick(dt=dt)
def run(self):
pyglet.clock.schedule_interval(self.on_update, 1 / FPS)
pyglet.app.run()
+26 -19
View File
@@ -1,12 +1,15 @@
from dataclasses import dataclass, field
from typing import cast
from pyglet.shapes import Circle, Line from pyglet.shapes import Circle, Line
from libs.ecs.world import World from libs.ecs.world import BaseWorld
from components import ( from src.components import (
Color, Color,
PlanetMarker, PlanetMarker,
Position, Position,
Radius, Radius,
SpaceShipMarker, SpaceShipMarker,
Target, Destination,
) )
from random import choice, randint, sample from random import choice, randint, sample
from settings import ( from settings import (
@@ -22,9 +25,9 @@ from settings import (
def create_planet( def create_planet(
world: World, world: BaseWorld,
x: int, x: float,
y: int, y: float,
radius: int, radius: int,
color: tuple[int, int, int, int], color: tuple[int, int, int, int],
) -> int: ) -> int:
@@ -38,34 +41,38 @@ def create_planet(
def create_spaceship( def create_spaceship(
world: World, world: BaseWorld,
src_x: int, src_x: float,
src_y: int, src_y: float,
dest_x: int, dest_x: float,
dest_y: int, dest_y: float,
approach_radius: int, approach_radius: int,
): ):
return world.spawn( return world.spawn(
SpaceShipMarker(), SpaceShipMarker(),
Position(x=src_x, y=src_y), Position(x=src_x, y=src_y),
Target(x=dest_x, y=dest_y), Destination(x=dest_x, y=dest_y),
Radius(approach_radius), Radius(approach_radius),
Circle(x=src_x, y=src_y, radius=SPACESHIP_RADIUS, color=SPACESHIP_COLOR), Circle(
Line(x=src_x, y=src_y, x2=dest_x, y2=dest_y, color=SPACESHIP_PATH_COLOR), x=src_x,
y=src_y,
radius=SPACESHIP_RADIUS,
color=SPACESHIP_COLOR,
),
) )
def generate_random_planet(world: World) -> int: def generate_random_planet(world: BaseWorld) -> int:
radius: int = randint(MIN_PLANET_RADIUS, MAX_PLANET_RADIUS) radius: int = randint(MIN_PLANET_RADIUS, MAX_PLANET_RADIUS)
x: int = randint(radius, SCREEN_WIDTH - radius) x: float = float(randint(radius, SCREEN_WIDTH - radius))
y: int = randint(radius, SCREEN_HEIGHT - radius) y: float = float(randint(radius, SCREEN_HEIGHT - radius))
# color = (randint(0, 255), randint(0, 255), randint(0, 255), 255) # color = (randint(0, 255), randint(0, 255), randint(0, 255), 255)
color = choice(PLANET_COLORS) + (255,) color = choice(PLANET_COLORS) + (255,)
return create_planet(world, x, y, radius, color) return create_planet(world, x, y, radius, color)
def create_travelling_spaceship( def create_travelling_spaceship(
world: World, world: BaseWorld,
src_planet: int, src_planet: int,
dest_planet: int, dest_planet: int,
): ):
@@ -89,7 +96,7 @@ def create_travelling_spaceship(
) )
def generate_random_spaceship(world: World) -> int: def generate_random_spaceship(world: BaseWorld) -> int:
planets = [entity for entity, _ in world.query(PlanetMarker)] planets = [entity for entity, _ in world.query(PlanetMarker)]
_planet_sample = sample(planets, 2) _planet_sample = sample(planets, 2)
+11
View File
@@ -0,0 +1,11 @@
from dataclasses import dataclass, field
from pyglet.shapes import Line
from libs.ecs.state import BaseGameState
@dataclass
class GameState(BaseGameState):
selected_planet: int | None = None
routes: dict[tuple[int, int], "Line"] = field(default_factory=dict)
+9 -9
View File
@@ -1,13 +1,13 @@
from pyglet.shapes import Circle from pyglet.shapes import Circle
from libs.ecs.world import World from libs.ecs.world import BaseWorld
from components import ( from src.components import (
PlanetMarker, PlanetMarker,
Position, Position,
Color, Color,
Radius, Radius,
SpaceShipMarker, SpaceShipMarker,
Target, Destination,
Vector2D, Vector2D,
) )
from settings import SPACESHIP_COLOR, SPACESHIP_RADIUS, SPACESHIP_SPEED from settings import SPACESHIP_COLOR, SPACESHIP_RADIUS, SPACESHIP_SPEED
@@ -15,7 +15,7 @@ from settings import SPACESHIP_COLOR, SPACESHIP_RADIUS, SPACESHIP_SPEED
class PlanetRenderSyncSystem: class PlanetRenderSyncSystem:
@classmethod @classmethod
def process(cls, world: World, **args): def process(cls, world: BaseWorld, **args):
for _, component_storage in world.query(PlanetMarker): for _, component_storage in world.query(PlanetMarker):
pos: Position = component_storage[Position] pos: Position = component_storage[Position]
color: Color = component_storage[Color] color: Color = component_storage[Color]
@@ -28,7 +28,7 @@ class PlanetRenderSyncSystem:
class SpaceShipRenderSyncSystem: class SpaceShipRenderSyncSystem:
@classmethod @classmethod
def process(cls, world: World, **args): def process(cls, world: BaseWorld, **args):
for _, component_storage in world.query(SpaceShipMarker): for _, component_storage in world.query(SpaceShipMarker):
pos: Position = component_storage[Position] pos: Position = component_storage[Position]
circle: Circle = component_storage[Circle] circle: Circle = component_storage[Circle]
@@ -39,11 +39,11 @@ class SpaceShipRenderSyncSystem:
class SpaceShipKillSystem: class SpaceShipKillSystem:
@classmethod @classmethod
def process(cls, world: World, **args): def process(cls, world: BaseWorld, **args):
marked_for_death = [] marked_for_death = []
for entity, component_storage in world.query(SpaceShipMarker): for entity, component_storage in world.query(SpaceShipMarker):
pos: Position = component_storage[Position] pos: Position = component_storage[Position]
target: Target = component_storage[Target] target: Destination = component_storage[Destination]
approach_radius = component_storage[Radius] approach_radius = component_storage[Radius]
if abs(pos - target) < approach_radius.value: if abs(pos - target) < approach_radius.value:
marked_for_death.append(entity) marked_for_death.append(entity)
@@ -53,10 +53,10 @@ class SpaceShipKillSystem:
class SpaceShipTravelSystem: class SpaceShipTravelSystem:
@classmethod @classmethod
def process(cls, world: World, **args): def process(cls, world: BaseWorld, **args):
for _, component_storage in world.query(SpaceShipMarker): for _, component_storage in world.query(SpaceShipMarker):
pos: Position = component_storage[Position] pos: Position = component_storage[Position]
target: Target = component_storage[Target] target: Destination = component_storage[Destination]
diff: Vector2D = target - pos diff: Vector2D = target - pos
diff_mag = abs(diff) diff_mag = abs(diff)
+34
View File
@@ -0,0 +1,34 @@
from dataclasses import dataclass, field
from pyglet.shapes import Line
from libs.ecs.world import BaseWorld
from libs.ecs.state import BaseGameState
from src.systems import (
PlanetRenderSyncSystem,
SpaceShipKillSystem,
SpaceShipRenderSyncSystem,
SpaceShipTravelSystem,
)
@dataclass
class GameState(BaseGameState):
selected_planet: int | None = None
routes: dict[tuple[int, int], "Line"] = field(default_factory=dict)
@dataclass
class World(BaseWorld):
state: GameState = field(default_factory=GameState)
world = World()
world.systems.extend(
(
SpaceShipKillSystem,
PlanetRenderSyncSystem,
SpaceShipRenderSyncSystem,
SpaceShipTravelSystem,
)
)
+1 -1
View File
@@ -1,4 +1,4 @@
from helpers import normalize_name, load_items from src.helpers import normalize_name, load_items
if __name__ == "__main__": if __name__ == "__main__":
items = load_items("assets/goods.txt") items = load_items("assets/goods.txt")
-80
View File
@@ -1,80 +0,0 @@
from random import randint
import pyglet
from pyglet.window.mouse import LEFT
from pyglet.shapes import Circle, Line
from components import PlanetMarker, Position, Radius, SpaceShipMarker, Vector2D
from helpers import generate_random_planet, generate_random_spaceship
from libs.ecs.world import World
from libs.ecs.event import EventBus
from systems import (
PlanetRenderSyncSystem,
SpaceShipKillSystem,
SpaceShipRenderSyncSystem,
SpaceShipTravelSystem,
)
from settings import FPS, FULLSCREEN, SCREEN_WIDTH, SCREEN_HEIGHT
world = World()
world.systems.extend(
(
SpaceShipKillSystem,
PlanetRenderSyncSystem,
SpaceShipRenderSyncSystem,
SpaceShipTravelSystem,
)
)
window = pyglet.window.Window(
caption="SpaceTrader",
width=SCREEN_WIDTH,
height=SCREEN_HEIGHT,
fullscreen=FULLSCREEN,
)
for _ in range(12):
generate_random_planet(world)
@window.event
def on_draw():
window.clear()
for _, component_storage in world.query(SpaceShipMarker):
component_storage[Line].draw()
for _, component_storage in world.query(SpaceShipMarker):
component_storage[Circle].draw()
for _, component_storage in world.query(PlanetMarker):
component_storage[Circle].draw()
@window.event
def on_mouse_release(x, y, button, modifiers):
if button == LEFT:
closest_entity, component_storage = min(
world.query(PlanetMarker),
key=lambda p: abs(p[1][Position] - Vector2D(x, y)),
)
pos = component_storage[Position]
if component_storage[Radius].value < abs(pos - Vector2D(x, y)):
print("NO PLANET")
else:
print("PLANET")
def on_update(dt):
world.tick(dt=dt)
def periodic_random_spaceship_generation(*args, **kwargs):
for i in range(randint(0, 5)):
generate_random_spaceship(world=world)
pyglet.clock.schedule_interval(on_update, 1 / FPS)
pyglet.clock.schedule_interval(periodic_random_spaceship_generation, 0.5)
pyglet.app.run()