/* Hurry Curry! - a game about cooking Copyright 2024 metamuffin This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License only. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ use hurrycurry_protocol::{glam::IVec2, TileIndex}; use log::warn; use sdl2::rect::Rect; use std::collections::{HashMap, HashSet}; use crate::render::{ sprite::{Sprite, SpriteDraw}, Renderer, }; #[derive(Default)] pub struct Tilemap { connect_group_by_tile: Vec>, connect_members_by_group: Vec>>, tile_srcs: Vec<[Rect; 16]>, tiles: HashMap, } impl Tilemap { pub fn init(&mut self, tile_names: &[String], sprite_rects: &HashMap) { let tile_index = tile_names .iter() .enumerate() .map(|(t, i)| (i.to_string(), t)) .collect::>(); self.connect_group_by_tile = vec![None; tile_names.len()]; self.connect_members_by_group = include_str!("../assets/connect.csv") .lines() .enumerate() .map(|(gid, line)| { line.split(",") .flat_map(|tile| tile_index.get(tile).copied()) .map(|ti| { self.connect_group_by_tile[ti] = Some(gid); Some(TileIndex(ti)) }) .collect::>() }) .collect::>(); self.tile_srcs = tile_names .iter() .map(|name| { let fallback = sprite_rects .get(&format!("{name}+a")) .copied() .unwrap_or_else(|| { warn!("no sprite for tile {name:?}"); Rect::new(0, 0, 0, 0) }); [ sprite_rects.get(&format!("{name}+")), sprite_rects.get(&format!("{name}+w")), sprite_rects.get(&format!("{name}+e")), sprite_rects.get(&format!("{name}+we")), sprite_rects.get(&format!("{name}+n")), sprite_rects.get(&format!("{name}+wn")), sprite_rects.get(&format!("{name}+en")), sprite_rects.get(&format!("{name}+wen")), sprite_rects.get(&format!("{name}+s")), sprite_rects.get(&format!("{name}+ws")), sprite_rects.get(&format!("{name}+es")), sprite_rects.get(&format!("{name}+wes")), sprite_rects.get(&format!("{name}+ns")), sprite_rects.get(&format!("{name}+wns")), sprite_rects.get(&format!("{name}+ens")), sprite_rects.get(&format!("{name}+wens")), ] .map(|e| e.copied().unwrap_or(fallback)) }) .collect(); } pub fn set(&mut self, pos: IVec2, tile: Option, neighbors: [Option; 4]) { let Some(tile) = tile else { self.tiles.remove(&pos); return; }; let mut idx = 0; if let Some(gid) = self.connect_group_by_tile[tile.0] { let cgroup = &self.connect_members_by_group[gid]; idx |= 0b0100 * (cgroup.contains(&neighbors[0])) as usize; idx |= (cgroup.contains(&neighbors[1])) as usize; idx |= 0b1000 * (cgroup.contains(&neighbors[2])) as usize; idx |= 0b0010 * (cgroup.contains(&neighbors[3])) as usize; } let src = self.tile_srcs[tile.0][idx]; self.tiles .insert(pos, Sprite::new_tile(src).at(pos.as_vec2())); } pub fn draw(&self, ctx: &mut Renderer) { for &sprite in self.tiles.values() { ctx.draw_world(sprite); } } }