From f7e7121fce7bd3f7fdabe836955e43b3d194c1c6 Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Sat, 14 Sep 2024 22:33:35 -0700 Subject: [PATCH] add a spritesheet component --- kennel/engine/components/component.py | 1 + kennel/engine/components/sprite_sheet.py | 48 ++++++++++++++++++++++++ kennel/engine/entities/cat.py | 35 +++++++++++------ kennel/kennel.py | 5 +-- kennel/kennelcats.py | 5 ++- static/src/engine/component.ts | 22 +++++++++++ static/src/engine/debounce_publisher.ts | 2 +- static/src/engine/entity.ts | 6 --- static/src/main.ts | 3 +- 9 files changed, 103 insertions(+), 24 deletions(-) create mode 100644 kennel/engine/components/sprite_sheet.py diff --git a/kennel/engine/components/component.py b/kennel/engine/components/component.py index 90506b9..7fd7757 100644 --- a/kennel/engine/components/component.py +++ b/kennel/engine/components/component.py @@ -6,6 +6,7 @@ class ComponentType(str, Enum): POSITION = "POSITION" CONTROLLABLE = "CONTROLLABLE" MARKOV = "MARKOV" + SPRITESHEET = "SPRITESHEET" class Component: diff --git a/kennel/engine/components/sprite_sheet.py b/kennel/engine/components/sprite_sheet.py new file mode 100644 index 0000000..5730085 --- /dev/null +++ b/kennel/engine/components/sprite_sheet.py @@ -0,0 +1,48 @@ +from .component import Component, ComponentType + + +class SpriteSpec: + def __init__( + self, + ms_per_frame: int, + top_x: int, + top_y: int, + end_x: int, + end_y: int, + frames: int, + ): + self.ms_per_frame = ms_per_frame + self.frames = frames + self.top_x = top_x + self.top_y = top_y + self.end_x = end_x + self.end_y = end_y + + +class SpriteSheet(Component): + def __init__( + self, + source: str, + state_to_spritespec: dict[str, SpriteSpec], + initial_state: str, + ): + super().__init__(ComponentType.SPRITESHEET) + self.source = source + self.state_to_spritespec = state_to_spritespec + + # these are only really used for client initialization + self.initial_state = initial_state + self.current_frame = 0 + self.last_update = 0 + + def __repr__(self) -> str: + return f"SpriteSheet(source={self.source}, state_to_spritespec={self.state_to_spritespec}, initial_state={self.initial_state}, current_frame={self.current_frame}, last_update={self.last_update})" + + def to_dict(self) -> dict: + return { + "source": self.source, + "current_frame": self.current_frame, + "last_update": self.last_update, + "current_state": self.initial_state, + "state_to_spritespec": self.state_to_spritespec, + } diff --git a/kennel/engine/entities/cat.py b/kennel/engine/entities/cat.py index edcde37..b5a8c6c 100644 --- a/kennel/engine/entities/cat.py +++ b/kennel/engine/entities/cat.py @@ -1,14 +1,33 @@ from kennel.engine.components.position import Position - +from kennel.engine.components.sprite_sheet import SpriteSheet, SpriteSpec +from enum import Enum from .entity import Entity, EntityType +class CatState(Enum): + IDLE = "IDLE" + FROLICKING = "FROLICKING" + EEPY = "EEPY" + ALERT = "ALERT" + CHASING_CURSOR = "CHASING_CURSOR" + CHASING_CAT = "CHASING_CAT" + SCRATCHING = "SCRATCHING" + ITCHY = "ITCHY" + + class Cat(Entity): - def __init__(self, id: str): - components = [Position(0, 0)] + def __init__(self, id: str, spritesheet_source: str): + state_to_spritespec = self.get_state_to_spritespec() + components = [ + Position(0, 0), + SpriteSheet(spritesheet_source, state_to_spritespec, CatState.ALERT), + ] super().__init__(EntityType.CAT, id, components) + def get_state_to_spritespec(self): + return {CatState.ALERT: SpriteSpec(100, 0, 0, 200, 40, 5)} + # # # IDLE, FROLICKING, EEPY, ALERT, CHASING_CURSOR, CHASING_CAT, SCRATCHING, ITCHY @@ -32,14 +51,6 @@ class Cat(Entity): # ] # # -# class CatState(Enum): -# IDLE = 0 -# FROLICKING = 1 -# EEPY = 2 -# ALERT = 3 -# CHASING_CURSOR = 4 -# CHASING_CAT = 5 -# SCRATCHING = 6 -# ITCHY = 7 + # # diff --git a/kennel/kennel.py b/kennel/kennel.py index 7123b7f..6d4f8f9 100644 --- a/kennel/kennel.py +++ b/kennel/kennel.py @@ -104,8 +104,7 @@ class KennelCatsManager: removed_cats = [cats_table[id] for id in self.last_seen.difference(cat_ids)] added_cats = [cats_table[id] for id in cat_ids.difference(self.last_seen)] - logger.info(f"removing {removed_cats}") - logger.info(f"adding {added_cats}") + logger.info(f"removing {removed_cats}, adding {added_cats}") for removed in removed_cats: self.entity_manager.remove_entity(removed) @@ -113,7 +112,7 @@ class KennelCatsManager: self.network_system.server_global_event(entity_death) for added in added_cats: - new_cat = Cat(added.id) + new_cat = Cat(added.id, added.spritesheet) self.entity_manager.add_entity(new_cat) entity_born = EntityBornEvent(new_cat) self.network_system.server_global_event(entity_born) diff --git a/kennel/kennelcats.py b/kennel/kennelcats.py index 2cb2f29..d354dad 100644 --- a/kennel/kennelcats.py +++ b/kennel/kennelcats.py @@ -43,4 +43,7 @@ class KennelCatService: def get_kennel(self) -> List[KennelCat]: response = requests.get(f"{self.hc_endpoint}/kennel") response.raise_for_status() - return [KennelCat.from_dict(cat) for cat in response.json()] + cats = [KennelCat.from_dict(cat) for cat in response.json()] + for cat in cats: + cat.spritesheet = self.hc_endpoint + cat.spritesheet + return cats diff --git a/static/src/engine/component.ts b/static/src/engine/component.ts index 9607fec..a16215b 100644 --- a/static/src/engine/component.ts +++ b/static/src/engine/component.ts @@ -2,6 +2,7 @@ export enum ComponentType { POSITION = "POSITION", RENDERABLE = "RENDERABLE", TRAILING_POSITION = "TRAILING_POSITION", + SPRITESHEET = "SPRITESHEET", } export interface Component { @@ -22,3 +23,24 @@ export interface TrailingPositionComponent extends Component { export interface RenderableComponent extends Component { name: ComponentType.RENDERABLE; } + +export interface SpriteSpec { + ms_per_frame: number; + frames: number; + top_x: number; + top_y: number; + end_x: number; + end_y: number; +} + +export interface SpriteSheetComponent extends Component { + name: ComponentType.SPRITESHEET; + source: string; + sheet: HTMLImageElement; + + current_frame: number; + last_update: number; + current_state: string; + + state_to_spritespec: Record; +} diff --git a/static/src/engine/debounce_publisher.ts b/static/src/engine/debounce_publisher.ts index 8ee4bb0..bc5e95f 100644 --- a/static/src/engine/debounce_publisher.ts +++ b/static/src/engine/debounce_publisher.ts @@ -5,7 +5,7 @@ export class DebouncePublisher { constructor( private readonly publisher: (data: T) => void | Promise, - private readonly debounce_ms = 100, + private readonly debounce_ms = 150, ) {} public start() { diff --git a/static/src/engine/entity.ts b/static/src/engine/entity.ts index 2c8d38e..87c9a67 100644 --- a/static/src/engine/entity.ts +++ b/static/src/engine/entity.ts @@ -1,7 +1,6 @@ import { Component, ComponentType, - PositionComponent, RenderableComponent, TrailingPositionComponent, } from "./component"; @@ -36,10 +35,5 @@ export const create_cat = (base: Entity) => { name: ComponentType.RENDERABLE, }; base.components[ComponentType.RENDERABLE] = renderable; - base.components[ComponentType.POSITION] = { - component: ComponentType.POSITION, - x: Math.random() * 1_000, - y: Math.random() * 1_000, - } as unknown as PositionComponent; // TODO: hack return base; }; diff --git a/static/src/main.ts b/static/src/main.ts index c11e41e..cc6f42c 100644 --- a/static/src/main.ts +++ b/static/src/main.ts @@ -10,7 +10,7 @@ import { NetworkSystem } from "./engine/network"; import { RenderSystem } from "./engine/render"; import { InputSystem } from "./engine/input"; import { TrailingPositionSystem } from "./engine/trailing_position"; -import { drainPoints, setDelay } from "laser-pen"; +import { drainPoints, setDelay, setRoundCap } from "laser-pen"; $(async () => { const client_id = await fetch("/assign", { @@ -32,6 +32,7 @@ $(async () => { const input_system = new InputSystem(publisher, gamecanvas); setDelay(500); + setRoundCap(true); const render_system = new RenderSystem(gamecanvas); const trailing_position = new TrailingPositionSystem(drainPoints);