doukutsu-rs/src/components/tilemap.rs

235 lines
9.0 KiB
Rust

use crate::common::Rect;
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::game::frame::Frame;
use crate::game::shared_game_state::{SharedGameState, TileSize};
use crate::game::stage::{BackgroundType, Stage, StageTexturePaths};
pub struct Tilemap {
tick: u32,
prev_tick: u32,
pub no_water: bool,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum TileLayer {
Background,
Middleground,
Foreground,
Snack,
}
impl Tilemap {
pub fn new() -> Self {
Tilemap { tick: 0, prev_tick: 0, no_water: false }
}
pub fn tick(&mut self) -> GameResult {
self.tick = self.tick.wrapping_add(1);
Ok(())
}
pub fn set_prev(&mut self) -> GameResult {
self.prev_tick = self.tick;
Ok(())
}
pub fn draw(
&self,
state: &mut SharedGameState,
ctx: &mut Context,
frame: &Frame,
layer: TileLayer,
textures: &StageTexturePaths,
stage: &Stage,
) -> GameResult {
if stage.map.tile_size == TileSize::Tile8x8 && layer == TileLayer::Snack {
return Ok(());
}
let tex = match layer {
TileLayer::Snack => "Npc/NpcSym",
TileLayer::Background => &textures.tileset_bg,
TileLayer::Middleground => &textures.tileset_mg,
TileLayer::Foreground => &textures.tileset_fg,
};
let (layer_offset, layer_width, layer_height, uses_layers) = if let Some(pxpack_data) = &stage.data.pxpack_data
{
match layer {
TileLayer::Background => {
(pxpack_data.offset_bg as usize, pxpack_data.size_bg.0, pxpack_data.size_bg.1, true)
}
TileLayer::Middleground => {
(pxpack_data.offset_mg as usize, pxpack_data.size_mg.0, pxpack_data.size_mg.1, true)
}
_ => (0, pxpack_data.size_fg.0, pxpack_data.size_fg.1, true),
}
} else {
(0, stage.map.width, stage.map.height, false)
};
if !uses_layers && layer == TileLayer::Middleground {
return Ok(());
}
let tile_size = state.tile_size.as_int();
let tile_sizef = state.tile_size.as_float();
let halft = tile_size / 2;
let halftf = tile_sizef / 2.0;
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, tex)?;
let mut rect = Rect::new(0, 0, tile_size as u16, tile_size as u16);
let (mut frame_x, mut frame_y) = frame.xy_interpolated(state.frame_time);
if let Some(pxpack_data) = &stage.data.pxpack_data {
let (fx, fy) = match layer {
TileLayer::Background => pxpack_data.scroll_bg.transform_camera_pos(frame_x, frame_y),
TileLayer::Middleground => pxpack_data.scroll_mg.transform_camera_pos(frame_x, frame_y),
_ => pxpack_data.scroll_fg.transform_camera_pos(frame_x, frame_y),
};
frame_x = fx;
frame_y = fy;
}
let tile_start_x = (frame_x as i32 / tile_size).clamp(0, layer_width as i32) as usize;
let tile_start_y = (frame_y as i32 / tile_size).clamp(0, layer_height as i32) as usize;
let tile_end_x =
((frame_x as i32 + 8 + state.canvas_size.0 as i32) / tile_size + 1).clamp(0, layer_width as i32) as usize;
let tile_end_y = ((frame_y as i32 + halft + state.canvas_size.1 as i32) / tile_size + 1)
.clamp(0, layer_height as i32) as usize;
if layer == TileLayer::Snack {
rect = state.constants.world.snack_rect;
}
for y in tile_start_y..tile_end_y {
for x in tile_start_x..tile_end_x {
let tile = *stage.map.tiles.get((y * layer_width as usize) + x + layer_offset).unwrap();
match layer {
_ if uses_layers => {
if tile == 0 {
continue;
}
let tile_size = tile_size as u16;
rect.left = (tile as u16 % 16) * tile_size;
rect.top = (tile as u16 / 16) * tile_size;
rect.right = rect.left + tile_size;
rect.bottom = rect.top + tile_size;
}
TileLayer::Background => {
if stage.map.attrib[tile as usize] >= 0x20 {
continue;
}
let tile_size = tile_size as u16;
rect.left = (tile as u16 % 16) * tile_size;
rect.top = (tile as u16 / 16) * tile_size;
rect.right = rect.left + tile_size;
rect.bottom = rect.top + tile_size;
}
TileLayer::Foreground => {
let attr = stage.map.attrib[tile as usize];
if attr < 0x40 || attr >= 0x80 {
continue;
}
let tile_size = tile_size as u16;
rect.left = (tile as u16 % 16) * tile_size;
rect.top = (tile as u16 / 16) * tile_size;
rect.right = rect.left + tile_size;
rect.bottom = rect.top + tile_size;
}
TileLayer::Snack => {
if stage.map.attrib[tile as usize] != 0x43 {
continue;
}
}
_ => {}
}
batch.add_rect(
(x as f32 * tile_sizef - halftf) - frame_x,
(y as f32 * tile_sizef - halftf) - frame_y,
&rect,
);
}
}
batch.draw(ctx)?;
if !self.no_water && layer == TileLayer::Foreground && stage.data.background_type == BackgroundType::Water {
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, &textures.background)?;
let rect_top = Rect { left: 0, top: 0, right: 32, bottom: 16 };
let rect_middle = Rect { left: 0, top: 16, right: 32, bottom: 48 };
let tile_start_x = frame_x as i32 / 32;
let tile_end_x = (frame_x + 16.0 + state.canvas_size.0) as i32 / 32 + 1;
let water_y = state.water_level as f32 / 512.0;
let tile_count_y = (frame_y + 16.0 + state.canvas_size.1 - water_y) as i32 / 32 + 1;
for x in tile_start_x..tile_end_x {
batch.add_rect((x as f32 * 32.0) - frame_x, water_y - frame_y, &rect_top);
for y in 0..tile_count_y {
batch.add_rect((x as f32 * 32.0) - frame_x, (y as f32 * 32.0) + water_y - frame_y, &rect_middle);
}
}
batch.draw(ctx)?;
}
if layer == TileLayer::Foreground {
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "Caret")?;
for y in tile_start_y..tile_end_y {
for x in tile_start_x..tile_end_x {
let tile = *stage.map.tiles.get((y * layer_width as usize) + x + layer_offset).unwrap();
let attr = stage.map.attrib[tile as usize];
if ![0x80, 0x81, 0x82, 0x83, 0xA0, 0xA1, 0xA2, 0xA3].contains(&attr) {
continue;
}
let shift =
((self.tick as f64 + (self.tick - self.prev_tick) as f64 * state.frame_time) * 2.0) as u16 % 16;
let mut push_rect = state.constants.world.water_push_rect;
match attr {
0x80 | 0xA0 => {
push_rect.left = push_rect.left + shift;
push_rect.right = push_rect.right + shift;
}
0x81 | 0xA1 => {
push_rect.top = push_rect.top + shift;
push_rect.bottom = push_rect.bottom + shift;
}
0x82 | 0xA2 => {
push_rect.left = push_rect.left - shift + state.tile_size.as_int() as u16;
push_rect.right = push_rect.right - shift + state.tile_size.as_int() as u16;
}
0x83 | 0xA3 => {
push_rect.top = push_rect.top - shift + state.tile_size.as_int() as u16;
push_rect.bottom = push_rect.bottom - shift + state.tile_size.as_int() as u16;
}
_ => (),
}
batch.add_rect(
(x as f32 * tile_sizef - halftf) - frame_x,
(y as f32 * tile_sizef - halftf) - frame_y,
&push_rect,
);
}
}
batch.draw(ctx)?;
}
Ok(())
}
}