Extract headless sim crate from server
Move chunk, entity, world, map, sim and load_world into a new sim library crate together with the game rules from shared (player_action, action_delta, delta_action, step_allowed, tile_collidable, tick constants). shared keeps only the wire types and re-exports the rules, so server and client compile unchanged. First step toward a local single-player build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
5610c2c343
commit
303c587aee
Executable
+126
@@ -0,0 +1,126 @@
|
||||
use std::collections::HashMap;
|
||||
use crate::chunk::{Chunk, TileDef, TileFlags, PaletteFullError};
|
||||
use crate::entity::Entity;
|
||||
|
||||
pub fn tile_to_chunk(tx: i16, ty: i16) -> (i16, i16) {
|
||||
(tx >> 5, ty >> 5)
|
||||
}
|
||||
|
||||
pub fn tile_to_local(tx: i16, ty: i16) -> (u8, u8) {
|
||||
((tx & 31) as u8, (ty & 31) as u8)
|
||||
}
|
||||
|
||||
pub fn local_to_tile(cx: i16, cy: i16, lx: u8, ly: u8) -> (i16, i16) {
|
||||
((cx << 5) | lx as i16, (cy << 5) | ly as i16)
|
||||
}
|
||||
|
||||
pub struct World {
|
||||
chunks: HashMap<(i16, i16), Chunk>,
|
||||
pub entities: HashMap<u32, Entity>,
|
||||
chunk_entities: HashMap<(i16, i16), Vec<u32>>,
|
||||
next_entity_id: u32,
|
||||
}
|
||||
|
||||
impl World {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
chunks: HashMap::new(),
|
||||
entities: HashMap::new(),
|
||||
chunk_entities: HashMap::new(),
|
||||
next_entity_id: 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chunk(&self, cx: i16, cy: i16) -> Option<&Chunk> {
|
||||
self.chunks.get(&(cx, cy))
|
||||
}
|
||||
|
||||
pub fn set_chunk(&mut self, cx: i16, cy: i16, chunk: Chunk) {
|
||||
self.chunks.insert((cx, cy), chunk);
|
||||
}
|
||||
|
||||
pub fn tile_flags(&self, wx: i16, wy: i16) -> Option<TileFlags> {
|
||||
let (cx, cy) = tile_to_chunk(wx, wy);
|
||||
let (lx, ly) = tile_to_local(wx, wy);
|
||||
self.chunks.get(&(cx, cy)).map(|c| c.tile_flags(lx, ly))
|
||||
}
|
||||
|
||||
pub fn set_tile(&mut self, wx: i16, wy: i16, def: TileDef) -> Result<(), PaletteFullError> {
|
||||
let (cx, cy) = tile_to_chunk(wx, wy);
|
||||
let (lx, ly) = tile_to_local(wx, wy);
|
||||
let chunk = self.chunks.entry((cx, cy)).or_insert_with(Chunk::new);
|
||||
chunk.set_tile(lx, ly, def)
|
||||
}
|
||||
|
||||
pub fn spawn_entity(&mut self, type_id: u16, pos: (i16, i16), hp: u16) -> u32 {
|
||||
let id = self.next_entity_id;
|
||||
self.next_entity_id += 1;
|
||||
let entity = Entity::new(id, type_id, pos, hp);
|
||||
self.add_entity(entity);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn add_entity(&mut self, entity: Entity) {
|
||||
let chunk_key = tile_to_chunk(entity.pos.0, entity.pos.1);
|
||||
let id = entity.id;
|
||||
self.entities.insert(id, entity);
|
||||
self.chunk_entities.entry(chunk_key).or_default().push(id);
|
||||
}
|
||||
|
||||
pub fn remove_entity(&mut self, id: u32) {
|
||||
if let Some(entity) = self.entities.remove(&id) {
|
||||
let chunk_key = tile_to_chunk(entity.pos.0, entity.pos.1);
|
||||
if let Some(list) = self.chunk_entities.get_mut(&chunk_key) {
|
||||
list.retain(|&eid| eid != id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_entity(&mut self, id: u32, new_pos: (i16, i16)) {
|
||||
if let Some(entity) = self.entities.get_mut(&id) {
|
||||
let old_chunk = tile_to_chunk(entity.pos.0, entity.pos.1);
|
||||
let new_chunk = tile_to_chunk(new_pos.0, new_pos.1);
|
||||
entity.pos = new_pos;
|
||||
if old_chunk != new_chunk {
|
||||
if let Some(list) = self.chunk_entities.get_mut(&old_chunk) {
|
||||
list.retain(|&eid| eid != id);
|
||||
}
|
||||
self.chunk_entities.entry(new_chunk).or_default().push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn entities_in_chunk(&self, cx: i16, cy: i16) -> &[u32] {
|
||||
self.chunk_entities.get(&(cx, cy)).map_or(&[], Vec::as_slice)
|
||||
}
|
||||
|
||||
/// Returns all entities within a 30×30 tile viewport centered on `center`.
|
||||
pub fn entities_in_viewport(&self, center: (i16, i16)) -> Vec<&Entity> {
|
||||
let half: i16 = 15;
|
||||
let min_x = center.0 - half;
|
||||
let max_x = center.0 + half - 1;
|
||||
let min_y = center.1 - half;
|
||||
let max_y = center.1 + half - 1;
|
||||
|
||||
let min_cx = tile_to_chunk(min_x, min_y).0;
|
||||
let max_cx = tile_to_chunk(max_x, max_y).0;
|
||||
let min_cy = tile_to_chunk(min_x, min_y).1;
|
||||
let max_cy = tile_to_chunk(max_x, max_y).1;
|
||||
|
||||
let mut result = Vec::new();
|
||||
for cy in min_cy..=max_cy {
|
||||
for cx in min_cx..=max_cx {
|
||||
for &eid in self.entities_in_chunk(cx, cy) {
|
||||
if let Some(e) = self.entities.get(&eid) {
|
||||
if e.pos.0 >= min_x && e.pos.0 <= max_x
|
||||
&& e.pos.1 >= min_y && e.pos.1 <= max_y
|
||||
{
|
||||
result.push(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user