Skip to content

Commit 5c5ad9c

Browse files
committed
'first'
1 parent e2e59b4 commit 5c5ad9c

10 files changed

+811
-0
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.venv
2+
dist/
3+
entyty.egg-info/
4+
5+
__pycache__

README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# entyty
2+
3+
**entyty** is a Python module that defines a framework for managing entities in a game or simulation environment. Entities are objects that can exist on a grid, occupy cells, and perform various actions within the system. The module provides base classes for creating and managing entities in a game or simulation. It also includes a set of subclasses that extend the framework to support grid-based entities. The module relies on the [pyglet](https://github.com/pyglet/pyglet) library for event dispatching/handling.
4+
5+
## Package Structure
6+
7+
The "entyty" package consists of the following classes:
8+
9+
- '_AbstractEntity'
10+
- '_BaseEntity'
11+
12+
- 'Entity'
13+
- 'LogicalEntity'
14+
- 'VisualEntity'
15+
16+
- '_AbstractGridEntity'
17+
18+
- 'GridEntity'
19+
20+
21+
## Entities
22+
23+
### Entity
24+
25+
- `Entity` is a subclass of `BaseEntity` representing general entities.
26+
27+
### LogicalEntity
28+
29+
- `LogicalEntity` is a subclass of `BaseEntity` representing logical entities.
30+
31+
### VisualEntity
32+
33+
- `VisualEntity` is a subclass of `BaseEntity` representing visual entities.
34+
- It includes additional properties for managing an element and a position.
35+
36+
### GridEntity
37+
38+
- `GridEntity` is a concrete subclass of `AbstractGridEntity`.
39+
- It represents grid-based entities and includes methods and properties for managing their movement and actions.
40+
- Grid entities can occupy cells, move on a grid, and perform actions.
41+
42+
## Usage
43+
44+
To use the "entyty" package, you can create subclasses of the provided base classes to define specific entity types in your game or simulation. Customize the properties and methods to suit your needs.
45+
46+
Here's a simple example of how to create an entity:
47+
48+
```python
49+
from entyty import LogicalEntity
50+
51+
class MyEntity(LogicalEntity):
52+
def __init__(self):
53+
super().__init__(name='MyEntity', **kwargs)
54+
55+
my_entity = MyEntity()
56+
```

entyty/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from ._entity import AbstractEntity as _AbstractEntity, BaseEntity as _BaseEntity, LogicalEntity, VisualEntity, AbstractGridEntity as _AbstractGridEntity, GridEntity

entyty/__log__.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import logging
2+
from functools import wraps
3+
4+
logging.addLevelName(37, "ENTITY")
5+
logger = logging.getLogger("ENTITY")
6+
file_handler = logging.FileHandler('entity.log')
7+
file_handler.setLevel(37)
8+
formatter = logging.Formatter('%(asctime)s - %(message)s', 'w')
9+
file_handler.setFormatter(formatter)
10+
logger.addHandler(file_handler)
11+
logger.setLevel(37)
12+
13+
def log(message):
14+
logger.log(37, message)
15+
16+
def log_method(func):
17+
@wraps(func)
18+
def wrapper(*args, **kwargs):
19+
log(f'Calling {func.__name__}(args={args}, kwargs={kwargs})')
20+
return func(*args, **kwargs)
21+
return wrapper

entyty/__main__.py

Whitespace-only changes.

entyty/_entity/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from ._base_entity import AbstractEntity, BaseEntity, LogicalEntity, VisualEntity
2+
from ._grid_entity import GridEntity
3+

entyty/_entity/_base_entity.py

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
from __future__ import annotations as _annotations
2+
3+
from ..__log__ import log, log_method
4+
5+
from abc import ABC as _ABC, abstractmethod as _abstractmethod
6+
7+
from pyglet.event import EventDispatcher as _EventDispatcher
8+
9+
from typing import Optional as _Optional
10+
11+
from uuid import uuid4 as _uuid4, UUID as _UUID
12+
13+
import json
14+
15+
16+
class Scene(_ABC):
17+
pass
18+
19+
20+
class AbstractElement(_ABC):
21+
pass
22+
23+
24+
class EntityMeta(type):
25+
dispatcher = _EventDispatcher()
26+
events = {
27+
'on_create': 'entity_created',
28+
'on_update': 'entity_updated',
29+
'on_delete': 'entity_deleted',
30+
'on_freeze': 'entity_frozen',
31+
}
32+
for event_name in events:
33+
dispatcher.register_event_type(event_name)
34+
35+
"""A metaclass for entity _objects."""
36+
37+
def __new__(cls, name, bases, attrs):
38+
"""Create a new entity class."""
39+
return super().__new__(cls, name, bases, attrs)
40+
41+
42+
class AbstractEntity(metaclass=EntityMeta):
43+
log('Initializing abstract entity class.')
44+
_scene = None
45+
_name = None
46+
_entity_id = None
47+
_age = 0
48+
_labels = {'static': [], 'dynamic': []}
49+
_recvs_input = False
50+
_inputs = None
51+
_is_child = False
52+
_is_parent = False
53+
_children = None
54+
_siblings = None
55+
_parent = None
56+
"""An abstract base class for entity _objects."""
57+
58+
@property
59+
def name(self):
60+
"""Return the name of the entity."""
61+
return self._name
62+
63+
@name.setter
64+
def name(self, name):
65+
"""Set the name of the entity."""
66+
if self._name is None or self.__class__.__name__ != 'BaseEntity':
67+
self._name = name
68+
else:
69+
raise AttributeError("Cannot change the name of an entity.")
70+
71+
@property
72+
def scene(self):
73+
"""Return the scene of the entity."""
74+
return self._scene
75+
76+
@scene.setter
77+
def scene(self, scene):
78+
"""Set the scene of the entity."""
79+
if hasattr(self, '_scene'):
80+
if self._scene is None and scene is not None:
81+
self._scene = scene
82+
scene.add_entity(self)
83+
elif self._scene is not None and scene is None:
84+
self._scene.remove_entity(self)
85+
self._scene = None
86+
del self._scene
87+
elif self._scene is not None:
88+
self._scene.remove_entity(self)
89+
self._scene = scene
90+
scene.add_entity(self)
91+
92+
@scene.deleter
93+
def scene(self):
94+
if hasattr(self, '_scene') and self._scene is not None:
95+
self._scene.remove_entity(self)
96+
self._scene = None
97+
delattr(self, '_scene')
98+
else:
99+
return
100+
101+
@property
102+
def entity_id(self):
103+
"""Return the ID of the entity."""
104+
if self._entity_id is None:
105+
self._entity_id = _uuid4()
106+
return self._entity_id.hex
107+
108+
@entity_id.setter
109+
def entity_id(self, entity_id):
110+
"""Set the ID of the entity."""
111+
if isinstance(entity_id, _UUID):
112+
self._entity_id = entity_id
113+
114+
@property
115+
def age(self):
116+
return self._age
117+
118+
@classmethod
119+
def _add_handler(cls, event_type, handler):
120+
"""Add an event handler to the entity."""
121+
cls.dispatcher.push_handlers(handler, event_type)
122+
123+
@classmethod
124+
def _remove_handler(cls, event_type, handler):
125+
"""Remove an event handler from the entity."""
126+
cls.dispatcher.remove_handlers(handler, event_type)
127+
128+
@classmethod
129+
def _push_handlers(cls, *args, **kwargs):
130+
"""Push event handlers to the entity."""
131+
cls.dispatcher.push_handlers(*args, **kwargs)
132+
133+
@classmethod
134+
def _pop_handlers(cls):
135+
"""Pop event handlers from the entity."""
136+
cls.dispatcher.pop_handlers()
137+
138+
@classmethod
139+
def _register_event_type(cls, event_type):
140+
"""Register an event type for the entity."""
141+
cls.dispatcher.register_event_type(event_type)
142+
143+
def _dispatch_event(self, event_name, *args):
144+
"""Dispatch an event for the entity."""
145+
if event_type := self.events.get(event_name):
146+
self.dispatcher.dispatch_event(event_type, self, *args)
147+
148+
@_abstractmethod
149+
def _validate(self):
150+
"""Validate the entity."""
151+
pass
152+
153+
def _save(self, file_path: str) -> bool:
154+
"""Save the entity's data to a file.
155+
156+
Args:
157+
file_path (str): The path to the file where the data will be saved.
158+
159+
Returns:
160+
bool: True if the save operation was successful, False otherwise.
161+
"""
162+
try:
163+
data_to_save = {
164+
"name": self.name,
165+
"entity_id": str(self.entity_id),
166+
"age": self.age,
167+
# Add more entity-specific data here as needed
168+
}
169+
170+
with open(file_path, "w") as file:
171+
json.dump(data_to_save, file, indent=4)
172+
173+
print(f"Entity data saved to {file_path}")
174+
return True
175+
except Exception as e:
176+
print(f"Failed to save entity data: {str(e)}")
177+
return False
178+
179+
def _update_entity(self):
180+
pass
181+
182+
def _adopt(self, child):
183+
"""Adopt a child entity."""
184+
if not self.children:
185+
self.children = []
186+
self.children.append(child)
187+
self.is_parent = True
188+
child.parent = self
189+
child.is_child = True
190+
191+
def _orphan(self, child):
192+
"""Orphan a child entity."""
193+
if self._children:
194+
self._children.remove(child)
195+
if not self._children:
196+
self._children = None
197+
self._is_parent = False
198+
child._parent = None
199+
child._is_child = False
200+
201+
def __eq__(self, other):
202+
"""Check if the entity is equal to another entity."""
203+
if not isinstance(other, AbstractEntity):
204+
return False
205+
return self.entity_id == other.entity_id
206+
207+
def __hash__(self):
208+
"""Return a hash of the entity."""
209+
return hash(self.entity_id)
210+
211+
212+
class BaseEntity(AbstractEntity):
213+
"""A base class for entity _objects."""
214+
log('Initializing base entity class.')
215+
def __init__(
216+
self,
217+
scene: _Optional[Scene] = None,
218+
entity_id: _Optional[_UUID] = None,
219+
name: _Optional[str] = None,
220+
*args, **kwargs
221+
):
222+
"""Create a new entity object."""
223+
super(BaseEntity, self).__init__()
224+
self.name = name
225+
self.entity_id = entity_id if entity_id is not None else _uuid4()
226+
if scene is None:
227+
del self.scene
228+
else:
229+
self.scene = scene
230+
231+
def _validate(self):
232+
pass
233+
234+
def update(self, dt):
235+
self._age += 1

0 commit comments

Comments
 (0)