Initial commit
This commit is contained in:
1 parent
afe548e9f8
commit
e4184b7cde
7 files changed
+212
-16
No files matched your search
@@ -1,3 +1,4 @@
|
||||
.venv
|
||||
.vscode
|
||||
*.pyc
|
||||
**__pycache__/
|
||||
@@ -0,0 +1,55 @@
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum, StrEnum
|
||||
|
||||
|
||||
@dataclass
|
||||
class Name:
|
||||
value: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class BaseStat:
|
||||
value: int
|
||||
|
||||
|
||||
@dataclass
|
||||
class Health(BaseStat):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Satiety(BaseStat):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Hydration(BaseStat):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Hygiene(BaseStat):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Capacity(BaseStat):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Tags:
|
||||
values: list = field(default_factory=list)
|
||||
|
||||
|
||||
class LocationType(StrEnum):
|
||||
NO_WHERE = "no_where"
|
||||
PADDOCK = "paddock"
|
||||
STORE = "store"
|
||||
INVENTORY = "inventory"
|
||||
|
||||
|
||||
@dataclass
|
||||
class Location:
|
||||
type: "LocationType" = LocationType.NO_WHERE
|
||||
id: int | None = None
|
||||
@@ -0,0 +1,57 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Iterator, Protocol
|
||||
|
||||
type Entity = int
|
||||
type Archetype = frozenset[type]
|
||||
type ComponentMap = dict[type, Any]
|
||||
type EntityComponentStorage = dict[Entity, ComponentMap]
|
||||
|
||||
|
||||
class System(Protocol):
|
||||
@classmethod
|
||||
def process(cls, world: "World") -> None: ...
|
||||
|
||||
|
||||
@dataclass
|
||||
class World:
|
||||
registry: dict[Archetype, EntityComponentStorage] = field(default_factory=dict)
|
||||
next_entity_index: int = 0
|
||||
systems: list[type[System]] = field(default_factory=list)
|
||||
|
||||
def generate_id(self) -> int:
|
||||
self.next_entity_index += 1
|
||||
return self.next_entity_index
|
||||
|
||||
def spawn(self, *components) -> int:
|
||||
archetype = frozenset([type(c) for c in components])
|
||||
|
||||
if archetype not in self.registry:
|
||||
self.registry[archetype] = {}
|
||||
|
||||
entity = self.generate_id()
|
||||
self.registry[archetype][entity] = {type(c): c for c in components}
|
||||
return entity
|
||||
|
||||
def query(
|
||||
self, *component_types, exact=False
|
||||
) -> Iterator[tuple[Entity, ComponentMap]]:
|
||||
component_subset_signature = frozenset(component_types)
|
||||
for archetype, entity_component_storage in self.registry.items():
|
||||
if (exact and component_subset_signature == archetype) or (
|
||||
not exact and component_subset_signature.issubset(archetype)
|
||||
):
|
||||
for entity, component_storage in entity_component_storage.items():
|
||||
yield entity, component_storage
|
||||
|
||||
def get_components(self, entity: int, pop=False):
|
||||
for componentstorage in self.registry.values():
|
||||
if entity in componentstorage:
|
||||
if pop:
|
||||
return componentstorage.pop(entity, None)
|
||||
else:
|
||||
return componentstorage[entity]
|
||||
return None
|
||||
|
||||
def tick(self):
|
||||
for system in self.systems:
|
||||
system.process(self)
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
ENTITY_INDEX = 0
|
||||
|
||||
|
||||
def generate_id():
|
||||
global ENTITY_INDEX
|
||||
ENTITY_INDEX += 1
|
||||
return ENTITY_INDEX
|
||||
|
||||
|
||||
@dataclass
|
||||
class BaseEntity:
|
||||
id: int = field(default_factory=generate_id)
|
||||
@@ -0,0 +1,33 @@
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from components import (
|
||||
Health,
|
||||
Satiety,
|
||||
Location,
|
||||
LocationType,
|
||||
Name,
|
||||
Hydration,
|
||||
Hygiene,
|
||||
Tags,
|
||||
)
|
||||
from ecs import World
|
||||
|
||||
|
||||
def create_pet(world: World, name: str):
|
||||
return world.spawn(
|
||||
Name(name),
|
||||
Health(100),
|
||||
Satiety(80),
|
||||
Hydration(80),
|
||||
Hygiene(80),
|
||||
Tags(),
|
||||
)
|
||||
|
||||
|
||||
def move_pet_to(world: World, pet_id, container_entity=None) -> bool:
|
||||
component_map = world.get_components(pet_id, pop=True)
|
||||
if component_map is None:
|
||||
return False
|
||||
component_map[Location].id = container_entity
|
||||
component_map[Location].type = LocationType.PADDOCK
|
||||
return True
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
from components import Health, Hydration, Hygiene, Satiety
|
||||
from ecs import World
|
||||
|
||||
|
||||
class SatietySystem:
|
||||
@classmethod
|
||||
def process(cls, world: World):
|
||||
for _, component_map in world.query(Satiety):
|
||||
health: Health = component_map[Health]
|
||||
hunger: Satiety = component_map[Satiety]
|
||||
if hunger.value <= 0:
|
||||
health.value -= 1
|
||||
hunger.value = 0
|
||||
else:
|
||||
hunger.value -= 1
|
||||
|
||||
|
||||
class HydrationSystem:
|
||||
@classmethod
|
||||
def process(cls, world: World):
|
||||
for _, component_map in world.query(Hydration):
|
||||
health: Health = component_map[Health]
|
||||
hydration: Hydration = component_map[Hydration]
|
||||
if hydration.value <= 0:
|
||||
health.value -= 1
|
||||
hydration.value = 0
|
||||
else:
|
||||
hydration.value -= 1
|
||||
|
||||
|
||||
class HygieneSystem:
|
||||
@classmethod
|
||||
def process(cls, world: World):
|
||||
for _, component_map in world.query(Hygiene):
|
||||
health: Health = component_map[Health]
|
||||
hygiene: Hygiene = component_map[Hygiene]
|
||||
if hygiene.value <= 0:
|
||||
health.value -= 1
|
||||
hygiene.value = 0
|
||||
else:
|
||||
hygiene.value -= 1
|
||||
@@ -1,3 +1,26 @@
|
||||
from entities import BaseEntity
|
||||
from imp import World, create_pet
|
||||
from systems import SatietySystem, HydrationSystem, HygieneSystem
|
||||
|
||||
object = BaseEntity()
|
||||
|
||||
def test_systems_tick():
|
||||
world = World()
|
||||
world.systems.extend(
|
||||
(
|
||||
HydrationSystem,
|
||||
SatietySystem,
|
||||
HygieneSystem,
|
||||
)
|
||||
)
|
||||
|
||||
entity = create_pet(world, "karabas")
|
||||
|
||||
for _ in range(81):
|
||||
world.tick()
|
||||
|
||||
components = world.get_components(entity)
|
||||
|
||||
print(components)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_systems_tick()
|
||||
Reference in new issue
Block a user