Bnenne da355dbc6c Merge branch 'food'
# Conflicts:
#	main.py
#	world/render_objects.py
2025-06-03 18:46:47 -05:00

148 lines
4.8 KiB
Python

from collections import defaultdict
from abc import ABC, abstractmethod
class World:
def __init__(self, partition_size=10):
self.partition_size = partition_size
self.buffers = [defaultdict(list), defaultdict(list)]
self.current_buffer = 0
def _hash_position(self, position):
# Map world coordinates to cell coordinates
return int(position.x // self.partition_size), int(
position.y // self.partition_size
)
def render_all(self, camera, screen):
for obj_list in self.buffers[self.current_buffer].values():
for obj in obj_list:
obj.render(camera, screen)
def tick_all(self):
next_buffer = 1 - self.current_buffer
self.buffers[next_buffer].clear()
# print all objects in the current buffer
print(
f"Ticking objects in buffer {self.current_buffer}:",
self.buffers[self.current_buffer].values(),
)
for obj_list in self.buffers[self.current_buffer].values():
for obj in obj_list:
if obj.flags["death"]:
continue
if obj.flags["can_interact"]:
interactable = self.query_objects_within_radius(
obj.position.x, obj.position.y, obj.interaction_radius
)
interactable.remove(obj)
print(f"Object {obj} interacting with {len(interactable)} objects.")
new_obj = obj.tick(interactable)
else:
new_obj = obj.tick()
if new_obj is None:
continue
cell = self._hash_position(new_obj.position)
self.buffers[next_buffer][cell].append(new_obj)
self.current_buffer = next_buffer
def add_object(self, new_object):
cell = self._hash_position(new_object.position)
self.buffers[self.current_buffer][cell].append(new_object)
def query_objects_within_radius(self, x, y, radius):
result = []
cell_x, cell_y = int(x // self.partition_size), int(y // self.partition_size)
cells_to_check = []
r = int((radius // self.partition_size) + 1)
for dx in range(-r, r + 1):
for dy in range(-r, r + 1):
cells_to_check.append((cell_x + dx, cell_y + dy))
for cell in cells_to_check:
for obj in self.buffers[self.current_buffer].get(cell, []):
obj_x, obj_y = obj.position.get_position()
dx = obj_x - x
dy = obj_y - y
if dx * dx + dy * dy <= radius * radius:
result.append(obj)
return result
def query_objects_in_range(self, x1, y1, x2, y2):
result = []
cell_x1, cell_y1 = (
int(x1 // self.partition_size),
int(y1 // self.partition_size),
)
cell_x2, cell_y2 = (
int(x2 // self.partition_size),
int(y2 // self.partition_size),
)
for cell_x in range(cell_x1, cell_x2 + 1):
for cell_y in range(cell_y1, cell_y2 + 1):
for obj in self.buffers[self.current_buffer].get((cell_x, cell_y), []):
obj_x, obj_y = obj.position.get_position()
if x1 <= obj_x <= x2 and y1 <= obj_y <= y2:
result.append(obj)
return result
def query_closest_object(self, x, y):
closest_obj = None
closest_distance = float("inf")
for obj_list in self.buffers[self.current_buffer].values():
for obj in obj_list:
obj_x, obj_y = obj.position.get_position()
dx = obj_x - x
dy = obj_y - y
distance = dx * dx + dy * dy
if distance < closest_distance:
closest_distance = distance
closest_obj = obj
return closest_obj
class BaseEntity(ABC):
def __init__(self, position: "Position"):
self.position = position
self.interaction_radius = 0
self.flags = {
"death": False,
"can_interact": False,
}
self.world_callbacks = {}
self.max_visual_width = 0
@abstractmethod
def tick(self, interactable=None):
return self
@abstractmethod
def render(self, camera, screen):
pass
def flag_for_death(self):
self.flags["death"] = True
def get_objects(self):
return self.objects
class Position:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"({self.x}, {self.y})"
def __repr__(self):
return f"Position({self.x}, {self.y})"
def set_position(self, x, y):
self.x = x
self.y = y
def get_position(self):
return self.x, self.y