235 lines
9.0 KiB
Rust
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(())
|
|
}
|
|
}
|