mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2025-09-20 19:44:00 +00:00
Compare commits
2 commits
3c20e089f9
...
cc816d0380
Author | SHA1 | Date | |
---|---|---|---|
|
cc816d0380 | ||
|
02f0f7f314 |
|
@ -66,7 +66,7 @@ num-derive = "0.3.2"
|
||||||
num-traits = "0.2.12"
|
num-traits = "0.2.12"
|
||||||
paste = "1.0.0"
|
paste = "1.0.0"
|
||||||
pretty_env_logger = "0.4.0"
|
pretty_env_logger = "0.4.0"
|
||||||
sdl2 = { version = "0.34.3", optional = true }
|
sdl2 = { version = "0.34.3", optional = true, features = ["unsafe_textures", "bundled"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_derive = "1"
|
serde_derive = "1"
|
||||||
serde_yaml = "0.8"
|
serde_yaml = "0.8"
|
||||||
|
|
|
@ -28,7 +28,6 @@ impl BMFontRenderer {
|
||||||
let font = BMFont::load_from(filesystem::open(ctx, &full_path)?)?;
|
let font = BMFont::load_from(filesystem::open(ctx, &full_path)?)?;
|
||||||
let mut pages = Vec::new();
|
let mut pages = Vec::new();
|
||||||
|
|
||||||
println!("stem: {:?}", stem);
|
|
||||||
let (zeros, _, _) = FILE_TYPES
|
let (zeros, _, _) = FILE_TYPES
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ext| (1, ext, format!("{}_0{}", stem.to_string_lossy(), ext)))
|
.map(|ext| (1, ext, format!("{}_0{}", stem.to_string_lossy(), ext)))
|
||||||
|
@ -41,7 +40,6 @@ impl BMFontRenderer {
|
||||||
|
|
||||||
for i in 0..font.pages {
|
for i in 0..font.pages {
|
||||||
let page_path = format!("{}_{:02$}", stem.to_string_lossy(), i, zeros);
|
let page_path = format!("{}_{:02$}", stem.to_string_lossy(), i, zeros);
|
||||||
println!("x: {}", &page_path);
|
|
||||||
|
|
||||||
pages.push(page_path);
|
pages.push(page_path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -234,6 +234,22 @@ impl Direction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Point<T: Num + PartialOrd + Copy = isize> {
|
||||||
|
pub x: T,
|
||||||
|
pub y: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Num + PartialOrd + Copy> Point<T> {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new(x: T, y: T) -> Point<T> {
|
||||||
|
Point {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Rect<T: Num + PartialOrd + Copy = isize> {
|
pub struct Rect<T: Num + PartialOrd + Copy = isize> {
|
||||||
pub left: T,
|
pub left: T,
|
||||||
|
@ -243,6 +259,7 @@ pub struct Rect<T: Num + PartialOrd + Copy = isize> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Num + PartialOrd + Copy> Rect<T> {
|
impl<T: Num + PartialOrd + Copy> Rect<T> {
|
||||||
|
#[inline(always)]
|
||||||
pub fn new(left: T, top: T, right: T, bottom: T) -> Rect<T> {
|
pub fn new(left: T, top: T, right: T, bottom: T) -> Rect<T> {
|
||||||
Rect {
|
Rect {
|
||||||
left,
|
left,
|
||||||
|
@ -252,6 +269,7 @@ impl<T: Num + PartialOrd + Copy> Rect<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub fn new_size(x: T, y: T, width: T, height: T) -> Rect<T> {
|
pub fn new_size(x: T, y: T, width: T, height: T) -> Rect<T> {
|
||||||
Rect {
|
Rect {
|
||||||
left: x,
|
left: x,
|
||||||
|
@ -260,6 +278,7 @@ impl<T: Num + PartialOrd + Copy> Rect<T> {
|
||||||
bottom: y.add(height),
|
bottom: y.add(height),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn width(&self) -> T {
|
pub fn width(&self) -> T {
|
||||||
if let Some(Ordering::Greater) = self.left.partial_cmp(&self.right) {
|
if let Some(Ordering::Greater) = self.left.partial_cmp(&self.right) {
|
||||||
self.left.sub(self.right)
|
self.left.sub(self.right)
|
||||||
|
@ -392,30 +411,30 @@ impl Color {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `Color` from four `u8`'s in the range `[0-255]`
|
/// Create a new `Color` from four `u8`'s in the range `[0-255]`
|
||||||
pub const fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Color {
|
pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Color {
|
||||||
Color::from((r, g, b, a))
|
Color::from((r, g, b, a))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `Color` from three u8's in the range `[0-255]`,
|
/// Create a new `Color` from three u8's in the range `[0-255]`,
|
||||||
/// with the alpha component fixed to 255 (opaque)
|
/// with the alpha component fixed to 255 (opaque)
|
||||||
pub const fn from_rgb(r: u8, g: u8, b: u8) -> Color {
|
pub fn from_rgb(r: u8, g: u8, b: u8) -> Color {
|
||||||
Color::from((r, g, b))
|
Color::from((r, g, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a tuple of four `u8`'s in the range `[0-255]` with the `Color`'s
|
/// Return a tuple of four `u8`'s in the range `[0-255]` with the `Color`'s
|
||||||
/// components.
|
/// components.
|
||||||
pub const fn to_rgba(self) -> (u8, u8, u8, u8) {
|
pub fn to_rgba(self) -> (u8, u8, u8, u8) {
|
||||||
self.into()
|
self.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a tuple of three `u8`'s in the range `[0-255]` with the `Color`'s
|
/// Return a tuple of three `u8`'s in the range `[0-255]` with the `Color`'s
|
||||||
/// components.
|
/// components.
|
||||||
pub const fn to_rgb(self) -> (u8, u8, u8) {
|
pub fn to_rgb(self) -> (u8, u8, u8) {
|
||||||
self.into()
|
self.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a packed `u32` containing `0xRRGGBBAA` into a `Color`
|
/// Convert a packed `u32` containing `0xRRGGBBAA` into a `Color`
|
||||||
pub const fn from_rgba_u32(c: u32) -> Color {
|
pub fn from_rgba_u32(c: u32) -> Color {
|
||||||
let c = c.to_be_bytes();
|
let c = c.to_be_bytes();
|
||||||
|
|
||||||
Color::from((c[0], c[1], c[2], c[3]))
|
Color::from((c[0], c[1], c[2], c[3]))
|
||||||
|
@ -423,21 +442,21 @@ impl Color {
|
||||||
|
|
||||||
/// Convert a packed `u32` containing `0x00RRGGBB` into a `Color`.
|
/// Convert a packed `u32` containing `0x00RRGGBB` into a `Color`.
|
||||||
/// This lets you do things like `Color::from_rgb_u32(0xCD09AA)` easily if you want.
|
/// This lets you do things like `Color::from_rgb_u32(0xCD09AA)` easily if you want.
|
||||||
pub const fn from_rgb_u32(c: u32) -> Color {
|
pub fn from_rgb_u32(c: u32) -> Color {
|
||||||
let c = c.to_be_bytes();
|
let c = c.to_be_bytes();
|
||||||
|
|
||||||
Color::from((c[1], c[2], c[3]))
|
Color::from((c[1], c[2], c[3]))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a `Color` into a packed `u32`, containing `0xRRGGBBAA` as bytes.
|
/// Convert a `Color` into a packed `u32`, containing `0xRRGGBBAA` as bytes.
|
||||||
pub const fn to_rgba_u32(self) -> u32 {
|
pub fn to_rgba_u32(self) -> u32 {
|
||||||
let (r, g, b, a): (u8, u8, u8, u8) = self.into();
|
let (r, g, b, a): (u8, u8, u8, u8) = self.into();
|
||||||
|
|
||||||
u32::from_be_bytes([r, g, b, a])
|
u32::from_be_bytes([r, g, b, a])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a `Color` into a packed `u32`, containing `0x00RRGGBB` as bytes.
|
/// Convert a `Color` into a packed `u32`, containing `0x00RRGGBB` as bytes.
|
||||||
pub const fn to_rgb_u32(self) -> u32 {
|
pub fn to_rgb_u32(self) -> u32 {
|
||||||
let (r, g, b, _a): (u8, u8, u8, u8) = self.into();
|
let (r, g, b, _a): (u8, u8, u8, u8) = self.into();
|
||||||
|
|
||||||
u32::from_be_bytes([0, r, g, b])
|
u32::from_be_bytes([0, r, g, b])
|
||||||
|
@ -515,4 +534,4 @@ impl From<Color> for [f32; 4] {
|
||||||
fn from(color: Color) -> Self {
|
fn from(color: Color) -> Self {
|
||||||
[color.r, color.g, color.b, color.a]
|
[color.r, color.g, color.b, color.a]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -216,7 +216,7 @@ pub struct EngineConstants {
|
||||||
pub world: WorldConsts,
|
pub world: WorldConsts,
|
||||||
pub npc: NPCConsts,
|
pub npc: NPCConsts,
|
||||||
pub weapon: WeaponConsts,
|
pub weapon: WeaponConsts,
|
||||||
pub tex_sizes: CaseInsensitiveHashMap<(usize, usize)>,
|
pub tex_sizes: CaseInsensitiveHashMap<(u16, u16)>,
|
||||||
pub textscript: TextScriptConsts,
|
pub textscript: TextScriptConsts,
|
||||||
pub title: TitleConsts,
|
pub title: TitleConsts,
|
||||||
pub font_path: String,
|
pub font_path: String,
|
||||||
|
|
|
@ -1,9 +1,38 @@
|
||||||
|
use crate::common::{Color, Rect, Point};
|
||||||
|
use crate::framework::context::Context;
|
||||||
|
use crate::framework::error::GameResult;
|
||||||
use crate::Game;
|
use crate::Game;
|
||||||
|
|
||||||
pub(crate) trait Backend {
|
pub trait Backend {
|
||||||
fn create_event_loop(&self) -> Box<dyn BackendEventLoop>;
|
fn create_event_loop(&self) -> GameResult<Box<dyn BackendEventLoop>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait BackendEventLoop {
|
pub trait BackendEventLoop {
|
||||||
fn run(&self, game: &mut Game);
|
fn run(&mut self, game: &mut Game, ctx: &mut Context);
|
||||||
}
|
|
||||||
|
fn new_renderer(&self) -> GameResult<Box<dyn BackendRenderer>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BackendRenderer {
|
||||||
|
fn clear(&mut self, color: Color);
|
||||||
|
|
||||||
|
fn present(&mut self) -> GameResult;
|
||||||
|
|
||||||
|
fn create_texture(&mut self, width: u16, height: u16, data: &[u8]) -> GameResult<Box<dyn BackendTexture>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BackendTexture {
|
||||||
|
fn dimensions(&self) -> (u16, u16);
|
||||||
|
fn add(&mut self, command: SpriteBatchCommand);
|
||||||
|
fn clear(&mut self);
|
||||||
|
fn draw(&mut self) -> GameResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_backend() -> GameResult<Box<dyn Backend>> {
|
||||||
|
crate::framework::backend_sdl2::SDL2Backend::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SpriteBatchCommand {
|
||||||
|
DrawRect(Rect<f32>, Rect<f32>),
|
||||||
|
DrawRectTinted(Rect<f32>, Rect<f32>, Color),
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,420 @@
|
||||||
|
use core::mem;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use sdl2::{EventPump, keyboard, pixels, Sdl};
|
||||||
|
use sdl2::event::{Event, WindowEvent};
|
||||||
|
use sdl2::keyboard::Scancode;
|
||||||
|
use sdl2::pixels::PixelFormatEnum;
|
||||||
|
use sdl2::render::{BlendMode, Texture, TextureCreator, WindowCanvas};
|
||||||
|
use sdl2::video::WindowContext;
|
||||||
|
|
||||||
|
use crate::common::Color;
|
||||||
|
use crate::framework::backend::{Backend, BackendEventLoop, BackendRenderer, BackendTexture, SpriteBatchCommand};
|
||||||
|
use crate::framework::context::Context;
|
||||||
|
use crate::framework::error::{GameError, GameResult};
|
||||||
|
use crate::framework::keyboard::ScanCode;
|
||||||
|
use crate::Game;
|
||||||
|
|
||||||
|
pub struct SDL2Backend {
|
||||||
|
context: Sdl,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SDL2Backend {
|
||||||
|
pub fn new() -> GameResult<Box<dyn Backend>> {
|
||||||
|
let context = sdl2::init().map_err(|e| GameError::WindowError(e))?;
|
||||||
|
|
||||||
|
let backend = SDL2Backend {
|
||||||
|
context,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Box::new(backend))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Backend for SDL2Backend {
|
||||||
|
fn create_event_loop(&self) -> GameResult<Box<dyn BackendEventLoop>> {
|
||||||
|
SDL2EventLoop::new(&self.context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SDL2EventLoop {
|
||||||
|
event_pump: EventPump,
|
||||||
|
refs: Rc<RefCell<SDL2Context>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SDL2Context {
|
||||||
|
canvas: WindowCanvas,
|
||||||
|
texture_creator: TextureCreator<WindowContext>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SDL2EventLoop {
|
||||||
|
pub fn new(sdl: &Sdl) -> GameResult<Box<dyn BackendEventLoop>> {
|
||||||
|
let event_pump = sdl.event_pump().map_err(|e| GameError::WindowError(e))?;
|
||||||
|
let video = sdl.video().map_err(|e| GameError::WindowError(e))?;
|
||||||
|
let window = video.window("Cave Story (doukutsu-rs)", 640, 480)
|
||||||
|
.position_centered()
|
||||||
|
.resizable()
|
||||||
|
.build()
|
||||||
|
.map_err(|e| GameError::WindowError(e.to_string()))?;
|
||||||
|
|
||||||
|
let canvas = window.into_canvas()
|
||||||
|
.accelerated()
|
||||||
|
.present_vsync()
|
||||||
|
.build()
|
||||||
|
.map_err(|e| GameError::RenderError(e.to_string()))?;
|
||||||
|
|
||||||
|
let texture_creator = canvas.texture_creator();
|
||||||
|
|
||||||
|
let event_loop = SDL2EventLoop {
|
||||||
|
event_pump,
|
||||||
|
refs: Rc::new(RefCell::new(SDL2Context {
|
||||||
|
canvas,
|
||||||
|
texture_creator,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Box::new(event_loop))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackendEventLoop for SDL2EventLoop {
|
||||||
|
fn run(&mut self, game: &mut Game, ctx: &mut Context) {
|
||||||
|
let state = unsafe { &mut *game.state.get() };
|
||||||
|
|
||||||
|
loop {
|
||||||
|
for event in self.event_pump.poll_iter() {
|
||||||
|
match event {
|
||||||
|
Event::Quit { .. } => {
|
||||||
|
state.shutdown();
|
||||||
|
}
|
||||||
|
Event::Window { win_event, .. } => {
|
||||||
|
match win_event {
|
||||||
|
WindowEvent::Shown => {}
|
||||||
|
WindowEvent::Hidden => {}
|
||||||
|
WindowEvent::SizeChanged(width, height) => {
|
||||||
|
ctx.screen_size = (width.max(1) as f32, height.max(1) as f32);
|
||||||
|
state.handle_resize(ctx);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::KeyDown { scancode, repeat, .. } => {
|
||||||
|
if let Some(scancode) = scancode {
|
||||||
|
if let Some(drs_scan) = conv_scancode(scancode) {
|
||||||
|
game.key_down_event(drs_scan, repeat);
|
||||||
|
ctx.keyboard_context.set_key(drs_scan, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::KeyUp { scancode, .. } => {
|
||||||
|
if let Some(scancode) = scancode {
|
||||||
|
if let Some(drs_scan) = conv_scancode(scancode) {
|
||||||
|
ctx.keyboard_context.set_key(drs_scan, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
game.update(ctx).unwrap();
|
||||||
|
|
||||||
|
if state.shutdown {
|
||||||
|
log::info!("Shutting down...");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.next_scene.is_some() {
|
||||||
|
mem::swap(&mut game.scene, &mut state.next_scene);
|
||||||
|
state.next_scene = None;
|
||||||
|
|
||||||
|
game.scene.as_mut().unwrap().init(state, ctx).unwrap();
|
||||||
|
game.loops = 0;
|
||||||
|
state.frame_time = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
game.draw(ctx).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_renderer(&self) -> GameResult<Box<dyn BackendRenderer>> {
|
||||||
|
SDL2Renderer::new(self.refs.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SDL2Renderer {
|
||||||
|
refs: Rc<RefCell<SDL2Context>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SDL2Renderer {
|
||||||
|
pub fn new(refs: Rc<RefCell<SDL2Context>>) -> GameResult<Box<dyn BackendRenderer>> {
|
||||||
|
Ok(Box::new(SDL2Renderer {
|
||||||
|
refs,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_sdl(color: Color) -> pixels::Color {
|
||||||
|
let (r, g, b, a) = color.to_rgba();
|
||||||
|
pixels::Color::RGBA(r, g, b, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackendRenderer for SDL2Renderer {
|
||||||
|
fn clear(&mut self, color: Color) {
|
||||||
|
let mut refs = self.refs.borrow_mut();
|
||||||
|
|
||||||
|
refs.canvas.set_draw_color(to_sdl(color));
|
||||||
|
refs.canvas.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn present(&mut self) -> GameResult {
|
||||||
|
let mut refs = self.refs.borrow_mut();
|
||||||
|
|
||||||
|
refs.canvas.present();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_texture(&mut self, width: u16, height: u16, data: &[u8]) -> GameResult<Box<dyn BackendTexture>> {
|
||||||
|
let mut refs = self.refs.borrow_mut();
|
||||||
|
|
||||||
|
let mut texture = refs.texture_creator
|
||||||
|
.create_texture_streaming(PixelFormatEnum::RGBA32, width as u32, height as u32)
|
||||||
|
.map_err(|e| GameError::RenderError(e.to_string()))?;
|
||||||
|
|
||||||
|
texture.set_blend_mode(BlendMode::Blend);
|
||||||
|
texture.with_lock(None, |buffer: &mut [u8], pitch: usize| {
|
||||||
|
for y in 0..(height as usize) {
|
||||||
|
for x in 0..(width as usize) {
|
||||||
|
let offset = y * pitch + x * 4;
|
||||||
|
let data_offset = (y * width as usize + x) * 4;
|
||||||
|
|
||||||
|
buffer[offset] = data[data_offset];
|
||||||
|
buffer[offset + 1] = data[data_offset + 1];
|
||||||
|
buffer[offset + 2] = data[data_offset + 2];
|
||||||
|
buffer[offset + 3] = data[data_offset + 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).map_err(|e| GameError::RenderError(e.to_string()))?;
|
||||||
|
|
||||||
|
return Ok(Box::new(SDL2Texture {
|
||||||
|
refs: self.refs.clone(),
|
||||||
|
texture: Some(texture),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
commands: vec![],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SDL2Texture {
|
||||||
|
refs: Rc<RefCell<SDL2Context>>,
|
||||||
|
texture: Option<Texture>,
|
||||||
|
width: u16,
|
||||||
|
height: u16,
|
||||||
|
commands: Vec<SpriteBatchCommand>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackendTexture for SDL2Texture {
|
||||||
|
fn dimensions(&self) -> (u16, u16) {
|
||||||
|
(self.width, self.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add(&mut self, command: SpriteBatchCommand) {
|
||||||
|
self.commands.push(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear(&mut self) {
|
||||||
|
self.commands.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&mut self) -> GameResult {
|
||||||
|
match self.texture.as_mut() {
|
||||||
|
None => Ok(()),
|
||||||
|
Some(texture) => {
|
||||||
|
let mut refs = self.refs.borrow_mut();
|
||||||
|
for command in self.commands.iter() {
|
||||||
|
match command {
|
||||||
|
SpriteBatchCommand::DrawRect(src, dest) => {
|
||||||
|
texture.set_color_mod(255, 255, 255);
|
||||||
|
texture.set_alpha_mod(255);
|
||||||
|
|
||||||
|
refs.canvas.copy(texture,
|
||||||
|
Some(sdl2::rect::Rect::new(src.left as i32, src.top as i32, src.width() as u32, src.height() as u32)),
|
||||||
|
Some(sdl2::rect::Rect::new(dest.left as i32, dest.top as i32, dest.width() as u32, dest.height() as u32)))
|
||||||
|
.map_err(|e| GameError::RenderError(e.to_string()))?;
|
||||||
|
}
|
||||||
|
SpriteBatchCommand::DrawRectTinted(src, dest, color) => {
|
||||||
|
let (r, g, b, a) = color.to_rgba();
|
||||||
|
texture.set_color_mod(r, g, b);
|
||||||
|
texture.set_alpha_mod(a);
|
||||||
|
|
||||||
|
refs.canvas.copy(texture,
|
||||||
|
Some(sdl2::rect::Rect::new(src.left as i32, src.top as i32, src.width() as u32, src.height() as u32)),
|
||||||
|
Some(sdl2::rect::Rect::new(dest.left as i32, dest.top as i32, dest.width() as u32, dest.height() as u32)))
|
||||||
|
.map_err(|e| GameError::RenderError(e.to_string()))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for SDL2Texture {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let mut texture_opt = None;
|
||||||
|
mem::swap(&mut self.texture, &mut texture_opt);
|
||||||
|
|
||||||
|
if let Some(texture) = texture_opt {
|
||||||
|
unsafe { texture.destroy(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn conv_scancode(code: keyboard::Scancode) -> Option<ScanCode> {
|
||||||
|
match code {
|
||||||
|
Scancode::A => Some(ScanCode::A),
|
||||||
|
Scancode::B => Some(ScanCode::B),
|
||||||
|
Scancode::C => Some(ScanCode::C),
|
||||||
|
Scancode::D => Some(ScanCode::D),
|
||||||
|
Scancode::E => Some(ScanCode::E),
|
||||||
|
Scancode::F => Some(ScanCode::F),
|
||||||
|
Scancode::G => Some(ScanCode::G),
|
||||||
|
Scancode::H => Some(ScanCode::H),
|
||||||
|
Scancode::I => Some(ScanCode::I),
|
||||||
|
Scancode::J => Some(ScanCode::J),
|
||||||
|
Scancode::K => Some(ScanCode::K),
|
||||||
|
Scancode::L => Some(ScanCode::L),
|
||||||
|
Scancode::M => Some(ScanCode::M),
|
||||||
|
Scancode::N => Some(ScanCode::N),
|
||||||
|
Scancode::O => Some(ScanCode::O),
|
||||||
|
Scancode::P => Some(ScanCode::P),
|
||||||
|
Scancode::Q => Some(ScanCode::Q),
|
||||||
|
Scancode::R => Some(ScanCode::R),
|
||||||
|
Scancode::S => Some(ScanCode::S),
|
||||||
|
Scancode::T => Some(ScanCode::T),
|
||||||
|
Scancode::U => Some(ScanCode::U),
|
||||||
|
Scancode::V => Some(ScanCode::V),
|
||||||
|
Scancode::W => Some(ScanCode::W),
|
||||||
|
Scancode::X => Some(ScanCode::X),
|
||||||
|
Scancode::Y => Some(ScanCode::Y),
|
||||||
|
Scancode::Z => Some(ScanCode::Z),
|
||||||
|
Scancode::Num1 => Some(ScanCode::Key1),
|
||||||
|
Scancode::Num2 => Some(ScanCode::Key2),
|
||||||
|
Scancode::Num3 => Some(ScanCode::Key3),
|
||||||
|
Scancode::Num4 => Some(ScanCode::Key4),
|
||||||
|
Scancode::Num5 => Some(ScanCode::Key5),
|
||||||
|
Scancode::Num6 => Some(ScanCode::Key6),
|
||||||
|
Scancode::Num7 => Some(ScanCode::Key7),
|
||||||
|
Scancode::Num8 => Some(ScanCode::Key8),
|
||||||
|
Scancode::Num9 => Some(ScanCode::Key9),
|
||||||
|
Scancode::Num0 => Some(ScanCode::Key0),
|
||||||
|
Scancode::Return => Some(ScanCode::Return),
|
||||||
|
Scancode::Escape => Some(ScanCode::Escape),
|
||||||
|
Scancode::Backspace => Some(ScanCode::Backspace),
|
||||||
|
Scancode::Tab => Some(ScanCode::Tab),
|
||||||
|
Scancode::Space => Some(ScanCode::Space),
|
||||||
|
Scancode::Minus => Some(ScanCode::Minus),
|
||||||
|
Scancode::Equals => Some(ScanCode::Equals),
|
||||||
|
Scancode::LeftBracket => Some(ScanCode::LBracket),
|
||||||
|
Scancode::RightBracket => Some(ScanCode::RBracket),
|
||||||
|
Scancode::Backslash => Some(ScanCode::Backslash),
|
||||||
|
Scancode::NonUsHash => Some(ScanCode::NonUsHash),
|
||||||
|
Scancode::Semicolon => Some(ScanCode::Semicolon),
|
||||||
|
Scancode::Apostrophe => Some(ScanCode::Apostrophe),
|
||||||
|
Scancode::Grave => Some(ScanCode::Grave),
|
||||||
|
Scancode::Comma => Some(ScanCode::Comma),
|
||||||
|
Scancode::Period => Some(ScanCode::Period),
|
||||||
|
Scancode::Slash => Some(ScanCode::Slash),
|
||||||
|
Scancode::CapsLock => Some(ScanCode::Capslock),
|
||||||
|
Scancode::F1 => Some(ScanCode::F1),
|
||||||
|
Scancode::F2 => Some(ScanCode::F2),
|
||||||
|
Scancode::F3 => Some(ScanCode::F3),
|
||||||
|
Scancode::F4 => Some(ScanCode::F4),
|
||||||
|
Scancode::F5 => Some(ScanCode::F5),
|
||||||
|
Scancode::F6 => Some(ScanCode::F6),
|
||||||
|
Scancode::F7 => Some(ScanCode::F7),
|
||||||
|
Scancode::F8 => Some(ScanCode::F8),
|
||||||
|
Scancode::F9 => Some(ScanCode::F9),
|
||||||
|
Scancode::F10 => Some(ScanCode::F10),
|
||||||
|
Scancode::F11 => Some(ScanCode::F11),
|
||||||
|
Scancode::F12 => Some(ScanCode::F12),
|
||||||
|
Scancode::PrintScreen => Some(ScanCode::Sysrq),
|
||||||
|
Scancode::ScrollLock => Some(ScanCode::Scrolllock),
|
||||||
|
Scancode::Pause => Some(ScanCode::Pause),
|
||||||
|
Scancode::Insert => Some(ScanCode::Insert),
|
||||||
|
Scancode::Home => Some(ScanCode::Home),
|
||||||
|
Scancode::PageUp => Some(ScanCode::PageUp),
|
||||||
|
Scancode::Delete => Some(ScanCode::Delete),
|
||||||
|
Scancode::End => Some(ScanCode::End),
|
||||||
|
Scancode::PageDown => Some(ScanCode::PageDown),
|
||||||
|
Scancode::Right => Some(ScanCode::Right),
|
||||||
|
Scancode::Left => Some(ScanCode::Left),
|
||||||
|
Scancode::Down => Some(ScanCode::Down),
|
||||||
|
Scancode::Up => Some(ScanCode::Up),
|
||||||
|
Scancode::NumLockClear => Some(ScanCode::Numlock),
|
||||||
|
Scancode::KpDivide => Some(ScanCode::NumpadDivide),
|
||||||
|
Scancode::KpMultiply => Some(ScanCode::NumpadMultiply),
|
||||||
|
Scancode::KpMinus => Some(ScanCode::NumpadSubtract),
|
||||||
|
Scancode::KpPlus => Some(ScanCode::NumpadAdd),
|
||||||
|
Scancode::KpEnter => Some(ScanCode::NumpadEnter),
|
||||||
|
Scancode::Kp1 => Some(ScanCode::Numpad1),
|
||||||
|
Scancode::Kp2 => Some(ScanCode::Numpad2),
|
||||||
|
Scancode::Kp3 => Some(ScanCode::Numpad3),
|
||||||
|
Scancode::Kp4 => Some(ScanCode::Numpad4),
|
||||||
|
Scancode::Kp5 => Some(ScanCode::Numpad5),
|
||||||
|
Scancode::Kp6 => Some(ScanCode::Numpad6),
|
||||||
|
Scancode::Kp7 => Some(ScanCode::Numpad7),
|
||||||
|
Scancode::Kp8 => Some(ScanCode::Numpad8),
|
||||||
|
Scancode::Kp9 => Some(ScanCode::Numpad9),
|
||||||
|
Scancode::Kp0 => Some(ScanCode::Numpad0),
|
||||||
|
Scancode::NonUsBackslash => Some(ScanCode::NonUsBackslash),
|
||||||
|
Scancode::Application => Some(ScanCode::Apps),
|
||||||
|
Scancode::Power => Some(ScanCode::Power),
|
||||||
|
Scancode::KpEquals => Some(ScanCode::NumpadEquals),
|
||||||
|
Scancode::F13 => Some(ScanCode::F13),
|
||||||
|
Scancode::F14 => Some(ScanCode::F14),
|
||||||
|
Scancode::F15 => Some(ScanCode::F15),
|
||||||
|
Scancode::F16 => Some(ScanCode::F16),
|
||||||
|
Scancode::F17 => Some(ScanCode::F17),
|
||||||
|
Scancode::F18 => Some(ScanCode::F18),
|
||||||
|
Scancode::F19 => Some(ScanCode::F19),
|
||||||
|
Scancode::F20 => Some(ScanCode::F20),
|
||||||
|
Scancode::F21 => Some(ScanCode::F21),
|
||||||
|
Scancode::F22 => Some(ScanCode::F22),
|
||||||
|
Scancode::F23 => Some(ScanCode::F23),
|
||||||
|
Scancode::F24 => Some(ScanCode::F24),
|
||||||
|
Scancode::Stop => Some(ScanCode::Stop),
|
||||||
|
Scancode::Cut => Some(ScanCode::Cut),
|
||||||
|
Scancode::Copy => Some(ScanCode::Copy),
|
||||||
|
Scancode::Paste => Some(ScanCode::Paste),
|
||||||
|
Scancode::Mute => Some(ScanCode::Mute),
|
||||||
|
Scancode::VolumeUp => Some(ScanCode::VolumeUp),
|
||||||
|
Scancode::VolumeDown => Some(ScanCode::VolumeDown),
|
||||||
|
Scancode::KpComma => Some(ScanCode::NumpadComma),
|
||||||
|
Scancode::SysReq => Some(ScanCode::Sysrq),
|
||||||
|
Scancode::Return2 => Some(ScanCode::NumpadEnter),
|
||||||
|
Scancode::LCtrl => Some(ScanCode::LControl),
|
||||||
|
Scancode::LShift => Some(ScanCode::LShift),
|
||||||
|
Scancode::LAlt => Some(ScanCode::LAlt),
|
||||||
|
Scancode::LGui => Some(ScanCode::LWin),
|
||||||
|
Scancode::RCtrl => Some(ScanCode::RControl),
|
||||||
|
Scancode::RShift => Some(ScanCode::RShift),
|
||||||
|
Scancode::RAlt => Some(ScanCode::RAlt),
|
||||||
|
Scancode::RGui => Some(ScanCode::RWin),
|
||||||
|
Scancode::AudioNext => Some(ScanCode::NextTrack),
|
||||||
|
Scancode::AudioPrev => Some(ScanCode::PrevTrack),
|
||||||
|
Scancode::AudioStop => Some(ScanCode::MediaStop),
|
||||||
|
Scancode::AudioPlay => Some(ScanCode::PlayPause),
|
||||||
|
Scancode::AudioMute => Some(ScanCode::Mute),
|
||||||
|
Scancode::MediaSelect => Some(ScanCode::MediaSelect),
|
||||||
|
Scancode::Mail => Some(ScanCode::Mail),
|
||||||
|
Scancode::Calculator => Some(ScanCode::Calculator),
|
||||||
|
Scancode::Sleep => Some(ScanCode::Sleep),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,33 @@
|
||||||
|
use crate::framework::backend::{Backend, init_backend, BackendRenderer};
|
||||||
|
use crate::framework::error::GameResult;
|
||||||
use crate::framework::filesystem::Filesystem;
|
use crate::framework::filesystem::Filesystem;
|
||||||
use crate::Game;
|
use crate::Game;
|
||||||
|
use crate::framework::keyboard::KeyboardContext;
|
||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub(crate) filesystem: Filesystem,
|
pub(crate) filesystem: Filesystem,
|
||||||
|
pub(crate) renderer: Option<Box<dyn BackendRenderer>>,
|
||||||
|
pub(crate) keyboard_context: KeyboardContext,
|
||||||
|
pub(crate) screen_size: (f32, f32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
pub fn new() -> Context {
|
pub fn new() -> Context {
|
||||||
Context {
|
Context {
|
||||||
filesystem: Filesystem::new(),
|
filesystem: Filesystem::new(),
|
||||||
|
renderer: None,
|
||||||
|
keyboard_context: KeyboardContext::new(),
|
||||||
|
screen_size: (320.0, 240.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, game: &mut Game) {
|
pub fn run(&mut self, game: &mut Game) -> GameResult {
|
||||||
loop {}
|
let backend = init_backend()?;
|
||||||
|
let mut event_loop = backend.create_event_loop()?;
|
||||||
|
self.renderer = Some(event_loop.new_renderer()?);
|
||||||
|
|
||||||
|
event_loop.run(game, self);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,9 +64,7 @@ impl fmt::Display for GameError {
|
||||||
impl Error for GameError {
|
impl Error for GameError {
|
||||||
fn cause(&self) -> Option<&dyn Error> {
|
fn cause(&self) -> Option<&dyn Error> {
|
||||||
match *self {
|
match *self {
|
||||||
GameError::WindowCreationError(ref e) => Some(&**e),
|
|
||||||
GameError::IOError(ref e) => Some(&**e),
|
GameError::IOError(ref e) => Some(&**e),
|
||||||
GameError::ShaderProgramError(ref e) => Some(e),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,6 +241,11 @@ impl Filesystem {
|
||||||
pub fn mount_vfs(&mut self, vfs: Box<dyn vfs::VFS>) {
|
pub fn mount_vfs(&mut self, vfs: Box<dyn vfs::VFS>) {
|
||||||
self.vfs.push_back(vfs);
|
self.vfs.push_back(vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn mount_user_vfs(&mut self, vfs: Box<dyn vfs::VFS>) {
|
||||||
|
self.user_vfs.push_back(vfs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Opens the given path and returns the resulting `File`
|
/// Opens the given path and returns the resulting `File`
|
||||||
|
@ -340,22 +345,6 @@ pub fn read_dir<P: AsRef<path::Path>>(
|
||||||
ctx.filesystem.read_dir(path)
|
ctx.filesystem.read_dir(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prints the contents of all data directories.
|
|
||||||
/// Useful for debugging.
|
|
||||||
pub fn print_all(ctx: &mut Context) {
|
|
||||||
ctx.filesystem.print_all()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Outputs the contents of all data directories,
|
|
||||||
/// using the "info" log level of the `log` crate.
|
|
||||||
/// Useful for debugging.
|
|
||||||
///
|
|
||||||
/// See the [`logging` example](https://github.com/ggez/ggez/blob/master/examples/eventloop.rs)
|
|
||||||
/// for how to collect log information.
|
|
||||||
pub fn log_all(ctx: &mut Context) {
|
|
||||||
ctx.filesystem.log_all()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds the given (absolute) path to the list of directories
|
/// Adds the given (absolute) path to the list of directories
|
||||||
/// it will search to look for resources.
|
/// it will search to look for resources.
|
||||||
///
|
///
|
||||||
|
@ -370,4 +359,9 @@ pub fn mount(ctx: &mut Context, path: &path::Path, readonly: bool) {
|
||||||
/// Adds a VFS to the list of resource search locations.
|
/// Adds a VFS to the list of resource search locations.
|
||||||
pub fn mount_vfs(ctx: &mut Context, vfs: Box<dyn vfs::VFS>) {
|
pub fn mount_vfs(ctx: &mut Context, vfs: Box<dyn vfs::VFS>) {
|
||||||
ctx.filesystem.mount_vfs(vfs)
|
ctx.filesystem.mount_vfs(vfs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a VFS to the list of user data search locations.
|
||||||
|
pub fn mount_user_vfs(ctx: &mut Context, vfs: Box<dyn vfs::VFS>) {
|
||||||
|
ctx.filesystem.mount_user_vfs(vfs)
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::common::Color;
|
use crate::common::Color;
|
||||||
use crate::framework::context::Context;
|
use crate::framework::context::Context;
|
||||||
use crate::framework::error::GameResult;
|
use crate::framework::error::{GameResult, GameError};
|
||||||
|
use crate::framework::backend::BackendTexture;
|
||||||
|
|
||||||
pub enum FilterMode {
|
pub enum FilterMode {
|
||||||
Nearest,
|
Nearest,
|
||||||
|
@ -13,12 +14,32 @@ impl Canvas {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(ctx: &mut Context, color: Color) {}
|
pub fn clear(ctx: &mut Context, color: Color) {
|
||||||
|
if let Some(renderer) = ctx.renderer.as_mut() {
|
||||||
|
renderer.clear(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn present(ctx: &mut Context) -> GameResult {
|
||||||
|
if let Some(renderer) = ctx.renderer.as_mut() {
|
||||||
|
renderer.present()?;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn present(ctx: &mut Context) -> GameResult<()> {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drawable_size(ctx: &mut Context) -> (f32, f32) {
|
pub fn renderer_initialized(ctx: &mut Context) -> bool {
|
||||||
(320.0, 240.0)
|
ctx.renderer.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_texture(ctx: &mut Context, width: u16, height: u16, data: &[u8]) -> GameResult<Box<dyn BackendTexture>> {
|
||||||
|
if let Some(renderer) = ctx.renderer.as_mut() {
|
||||||
|
return renderer.create_texture(width, height, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(GameError::RenderError("Rendering backend hasn't been initialized yet.".to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn screen_size(ctx: &mut Context) -> (f32, f32) {
|
||||||
|
ctx.screen_size
|
||||||
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
use crate::framework::context::Context;
|
|
||||||
use crate::framework::error::GameResult;
|
|
||||||
|
|
||||||
pub struct Image {}
|
|
||||||
|
|
||||||
impl Image {
|
|
||||||
pub fn from_rgba8(
|
|
||||||
context: &mut Context,
|
|
||||||
width: u16,
|
|
||||||
height: u16,
|
|
||||||
rgba: &[u8],
|
|
||||||
) -> GameResult<Self> {
|
|
||||||
Ok(Image {})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,12 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::bitfield;
|
||||||
use crate::framework::context::Context;
|
use crate::framework::context::Context;
|
||||||
|
|
||||||
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
pub enum ScanCode {
|
pub enum ScanCode {
|
||||||
/// The '1' key over the letters.
|
/// The '1' key over the letters.
|
||||||
|
@ -109,6 +115,8 @@ pub enum ScanCode {
|
||||||
/// The "Compose" key on Linux.
|
/// The "Compose" key on Linux.
|
||||||
Compose,
|
Compose,
|
||||||
|
|
||||||
|
NonUsHash,
|
||||||
|
NonUsBackslash,
|
||||||
Caret,
|
Caret,
|
||||||
|
|
||||||
Numlock,
|
Numlock,
|
||||||
|
@ -141,6 +149,7 @@ pub enum ScanCode {
|
||||||
Backslash,
|
Backslash,
|
||||||
Calculator,
|
Calculator,
|
||||||
Capital,
|
Capital,
|
||||||
|
Capslock,
|
||||||
Colon,
|
Colon,
|
||||||
Comma,
|
Comma,
|
||||||
Convert,
|
Convert,
|
||||||
|
@ -176,6 +185,7 @@ pub enum ScanCode {
|
||||||
RControl,
|
RControl,
|
||||||
RShift,
|
RShift,
|
||||||
RWin,
|
RWin,
|
||||||
|
Scrolllock,
|
||||||
Semicolon,
|
Semicolon,
|
||||||
Slash,
|
Slash,
|
||||||
Sleep,
|
Sleep,
|
||||||
|
@ -200,6 +210,136 @@ pub enum ScanCode {
|
||||||
Cut,
|
Cut,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_key_pressed(ctx: &mut Context, code: ScanCode) -> bool {
|
bitfield! {
|
||||||
false
|
/// Bitflags describing the state of keyboard modifiers, such as `Control` or `Shift`.
|
||||||
}
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct KeyMods(u8);
|
||||||
|
|
||||||
|
/// No modifiers; equivalent to `KeyMods::default()` and
|
||||||
|
/// [`KeyMods::empty()`](struct.KeyMods.html#method.empty).
|
||||||
|
/// Left or right Shift key.
|
||||||
|
pub shift, set_shift: 0;
|
||||||
|
/// Left or right Control key.
|
||||||
|
pub ctrl, set_ctrl: 1;
|
||||||
|
/// Left or right Alt key.
|
||||||
|
pub alt, set_alt: 2;
|
||||||
|
/// Left or right Win/Cmd/equivalent key.
|
||||||
|
pub win, set_win: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct KeyboardContext {
|
||||||
|
active_modifiers: KeyMods,
|
||||||
|
pressed_keys_set: HashSet<ScanCode>,
|
||||||
|
last_pressed: Option<ScanCode>,
|
||||||
|
current_pressed: Option<ScanCode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyboardContext {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
active_modifiers: KeyMods(0),
|
||||||
|
pressed_keys_set: HashSet::with_capacity(256),
|
||||||
|
last_pressed: None,
|
||||||
|
current_pressed: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_key(&mut self, key: ScanCode, pressed: bool) {
|
||||||
|
if pressed {
|
||||||
|
let _ = self.pressed_keys_set.insert(key);
|
||||||
|
self.last_pressed = self.current_pressed;
|
||||||
|
self.current_pressed = Some(key);
|
||||||
|
} else {
|
||||||
|
let _ = self.pressed_keys_set.remove(&key);
|
||||||
|
self.current_pressed = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_key_modifier(key, pressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Take a modifier key code and alter our state.
|
||||||
|
///
|
||||||
|
/// Double check that this edge handling is necessary;
|
||||||
|
/// winit sounds like it should do this for us,
|
||||||
|
/// see https://docs.rs/winit/0.18.0/winit/struct.KeyboardInput.html#structfield.modifiers
|
||||||
|
///
|
||||||
|
/// ...more specifically, we should refactor all this to consistant-ify events a bit and
|
||||||
|
/// make winit do more of the work.
|
||||||
|
/// But to quote Scott Pilgrim, "This is... this is... Booooooring."
|
||||||
|
fn set_key_modifier(&mut self, key: ScanCode, pressed: bool) {
|
||||||
|
if pressed {
|
||||||
|
match key {
|
||||||
|
ScanCode::LShift | ScanCode::RShift => self.active_modifiers.set_shift(true),
|
||||||
|
ScanCode::LControl | ScanCode::RControl => self.active_modifiers.set_ctrl(true),
|
||||||
|
ScanCode::LAlt | ScanCode::RAlt => self.active_modifiers.set_alt(true),
|
||||||
|
ScanCode::LWin | ScanCode::RWin => self.active_modifiers.set_win(true),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match key {
|
||||||
|
ScanCode::LShift | ScanCode::RShift => self.active_modifiers.set_shift(false),
|
||||||
|
ScanCode::LControl | ScanCode::RControl => self.active_modifiers.set_ctrl(false),
|
||||||
|
ScanCode::LAlt | ScanCode::RAlt => self.active_modifiers.set_alt(false),
|
||||||
|
ScanCode::LWin | ScanCode::RWin => self.active_modifiers.set_win(false),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_modifiers(&mut self, keymods: KeyMods) {
|
||||||
|
self.active_modifiers = keymods;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_key_pressed(&self, key: ScanCode) -> bool {
|
||||||
|
self.pressed_keys_set.contains(&key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_key_repeated(&self) -> bool {
|
||||||
|
if self.last_pressed.is_some() {
|
||||||
|
self.last_pressed == self.current_pressed
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn pressed_keys(&self) -> &HashSet<ScanCode> {
|
||||||
|
&self.pressed_keys_set
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn active_mods(&self) -> KeyMods {
|
||||||
|
self.active_modifiers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for KeyboardContext {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a key is currently pressed down.
|
||||||
|
pub fn is_key_pressed(ctx: &Context, key: ScanCode) -> bool {
|
||||||
|
ctx.keyboard_context.is_key_pressed(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the last keystroke sent by the system is repeated,
|
||||||
|
/// like when a key is held down for a period of time.
|
||||||
|
pub fn is_key_repeated(ctx: &Context) -> bool {
|
||||||
|
ctx.keyboard_context.is_key_repeated()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the set of currently pressed keys.
|
||||||
|
pub fn pressed_keys(ctx: &Context) -> &HashSet<ScanCode> {
|
||||||
|
ctx.keyboard_context.pressed_keys()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if keyboard modifier (or several) is active.
|
||||||
|
pub fn is_mod_active(ctx: &Context, keymods: KeyMods) -> bool {
|
||||||
|
(ctx.keyboard_context.active_mods().0 & keymods.0) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns currently active keyboard modifiers.
|
||||||
|
pub fn active_mods(ctx: &Context) -> KeyMods {
|
||||||
|
ctx.keyboard_context.active_mods()
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ pub mod context;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod filesystem;
|
pub mod filesystem;
|
||||||
pub mod vfs;
|
pub mod vfs;
|
||||||
pub mod image;
|
|
||||||
pub mod graphics;
|
pub mod graphics;
|
||||||
pub mod keyboard;
|
pub mod keyboard;
|
||||||
pub mod backend_null;
|
pub mod backend_null;
|
||||||
|
|
86
src/lib.rs
86
src/lib.rs
|
@ -7,21 +7,28 @@ extern crate strum;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate strum_macros;
|
extern crate strum_macros;
|
||||||
|
|
||||||
use std::env;
|
use core::mem;
|
||||||
use std::cell::UnsafeCell;
|
use std::cell::UnsafeCell;
|
||||||
|
use std::env;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use directories::ProjectDirs;
|
||||||
use log::*;
|
use log::*;
|
||||||
use pretty_env_logger::env_logger::Env;
|
use pretty_env_logger::env_logger::Env;
|
||||||
|
|
||||||
|
use crate::builtin_fs::BuiltinFS;
|
||||||
|
use crate::framework::backend::init_backend;
|
||||||
use crate::framework::context::Context;
|
use crate::framework::context::Context;
|
||||||
use crate::framework::error::{GameError, GameResult};
|
use crate::framework::error::{GameError, GameResult};
|
||||||
|
use crate::framework::filesystem::{mount_user_vfs, mount_vfs};
|
||||||
use crate::framework::graphics;
|
use crate::framework::graphics;
|
||||||
use crate::framework::keyboard::ScanCode;
|
use crate::framework::keyboard::ScanCode;
|
||||||
|
use crate::framework::vfs::PhysicalFS;
|
||||||
use crate::scene::loading_scene::LoadingScene;
|
use crate::scene::loading_scene::LoadingScene;
|
||||||
use crate::scene::Scene;
|
use crate::scene::Scene;
|
||||||
use crate::shared_game_state::{SharedGameState, TimingMode};
|
use crate::shared_game_state::{SharedGameState, TimingMode};
|
||||||
use crate::texture_set::G_MAG;
|
use crate::texture_set::{G_MAG, I_MAG};
|
||||||
use crate::ui::UI;
|
use crate::ui::UI;
|
||||||
|
|
||||||
mod bmfont;
|
mod bmfont;
|
||||||
|
@ -62,7 +69,7 @@ mod texture_set;
|
||||||
mod ui;
|
mod ui;
|
||||||
mod weapon;
|
mod weapon;
|
||||||
|
|
||||||
struct Game {
|
pub struct Game {
|
||||||
scene: Option<Box<dyn Scene>>,
|
scene: Option<Box<dyn Scene>>,
|
||||||
state: UnsafeCell<SharedGameState>,
|
state: UnsafeCell<SharedGameState>,
|
||||||
ui: UI,
|
ui: UI,
|
||||||
|
@ -147,7 +154,10 @@ impl Game {
|
||||||
n1 / n2
|
n1 / n2
|
||||||
} else { 1.0 };
|
} else { 1.0 };
|
||||||
}
|
}
|
||||||
unsafe { G_MAG = if state_ref.settings.subpixel_coords { state_ref.scale } else { 1.0 } };
|
unsafe {
|
||||||
|
G_MAG = if state_ref.settings.subpixel_coords { state_ref.scale } else { 1.0 };
|
||||||
|
I_MAG = state_ref.scale;
|
||||||
|
}
|
||||||
self.loops = 0;
|
self.loops = 0;
|
||||||
|
|
||||||
graphics::clear(ctx, [0.0, 0.0, 0.0, 1.0].into());
|
graphics::clear(ctx, [0.0, 0.0, 0.0, 1.0].into());
|
||||||
|
@ -259,18 +269,35 @@ pub fn init() -> GameResult {
|
||||||
pretty_env_logger::env_logger::from_env(Env::default().default_filter_or("info"))
|
pretty_env_logger::env_logger::from_env(Env::default().default_filter_or("info"))
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
let mut resource_dir = env::current_exe()?;
|
let resource_dir = if let Ok(data_dir) = env::var("CAVESTORY_DATA_DIR") {
|
||||||
|
PathBuf::from(data_dir)
|
||||||
// Ditch the filename (if any)
|
} else {
|
||||||
if resource_dir.file_name().is_some() {
|
let mut resource_dir = env::current_exe()?;
|
||||||
let _ = resource_dir.pop();
|
if resource_dir.file_name().is_some() {
|
||||||
}
|
let _ = resource_dir.pop();
|
||||||
resource_dir.push("data");
|
}
|
||||||
|
resource_dir.push("data");
|
||||||
|
resource_dir
|
||||||
|
};
|
||||||
|
|
||||||
info!("Resource directory: {:?}", resource_dir);
|
info!("Resource directory: {:?}", resource_dir);
|
||||||
info!("Initializing engine...");
|
info!("Initializing engine...");
|
||||||
|
|
||||||
let mut context: Context = Context::new();
|
let mut context = Context::new();
|
||||||
|
mount_vfs(&mut context, Box::new(BuiltinFS::new()));
|
||||||
|
mount_vfs(&mut context, Box::new(PhysicalFS::new(&resource_dir, true)));
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
|
let project_dirs = match ProjectDirs::from("", "", "doukutsu-rs") {
|
||||||
|
Some(dirs) => dirs,
|
||||||
|
None => {
|
||||||
|
return Err(GameError::FilesystemError(String::from(
|
||||||
|
"No valid home directory path could be retrieved.",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mount_user_vfs(&mut context, Box::new(PhysicalFS::new(project_dirs.data_local_dir(), false)));
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
{
|
{
|
||||||
|
@ -284,4 +311,39 @@ pub fn init() -> GameResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut game = Game::new(&mut context)?;
|
||||||
|
let state_ref = unsafe { &mut *game.state.get() };
|
||||||
|
#[cfg(feature = "scripting")]
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
state_ref.lua.update_refs(game.state.get(), &mut context as *mut Context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state_ref.next_scene = Some(Box::new(LoadingScene::new()));
|
||||||
|
context.run(&mut game);
|
||||||
|
|
||||||
|
/* loop {
|
||||||
|
game.update(&mut context)?;
|
||||||
|
|
||||||
|
if state_ref.shutdown {
|
||||||
|
log::info!("Shutting down...");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if state_ref.next_scene.is_some() {
|
||||||
|
mem::swap(&mut game.scene, &mut state_ref.next_scene);
|
||||||
|
state_ref.next_scene = None;
|
||||||
|
|
||||||
|
game.scene.as_mut().unwrap().init(state_ref, &mut context).unwrap();
|
||||||
|
game.loops = 0;
|
||||||
|
state_ref.frame_time = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
|
game.draw(&mut context)?;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -298,10 +298,10 @@ impl GameScene {
|
||||||
let mut frame = tick;
|
let mut frame = tick;
|
||||||
|
|
||||||
for x in (0..(state.canvas_size.0 as i32 + 16)).step_by(16) {
|
for x in (0..(state.canvas_size.0 as i32 + 16)).step_by(16) {
|
||||||
if frame > 15 { frame = 15; } else { frame += 1; }
|
if frame >= 15 { frame = 15; } else { frame += 1; }
|
||||||
|
|
||||||
if frame >= 0 {
|
if frame >= 0 {
|
||||||
rect.left = frame as u16 * 16;
|
rect.left = frame.abs() as u16 * 16;
|
||||||
rect.right = rect.left + 16;
|
rect.right = rect.left + 16;
|
||||||
|
|
||||||
for y in (0..(state.canvas_size.1 as i32 + 16)).step_by(16) {
|
for y in (0..(state.canvas_size.1 as i32 + 16)).step_by(16) {
|
||||||
|
@ -318,10 +318,10 @@ impl GameScene {
|
||||||
let mut frame = tick;
|
let mut frame = tick;
|
||||||
|
|
||||||
for y in (0..(state.canvas_size.1 as i32 + 16)).step_by(16) {
|
for y in (0..(state.canvas_size.1 as i32 + 16)).step_by(16) {
|
||||||
if frame > 15 { frame = 15; } else { frame += 1; }
|
if frame >= 15 { frame = 15; } else { frame += 1; }
|
||||||
|
|
||||||
if frame >= 0 {
|
if frame >= 0 {
|
||||||
rect.left = frame as u16 * 16;
|
rect.left = frame.abs() as u16 * 16;
|
||||||
rect.right = rect.left + 16;
|
rect.right = rect.left + 16;
|
||||||
|
|
||||||
for x in (0..(state.canvas_size.0 as i32 + 16)).step_by(16) {
|
for x in (0..(state.canvas_size.0 as i32 + 16)).step_by(16) {
|
||||||
|
@ -339,20 +339,20 @@ impl GameScene {
|
||||||
let center_y = (state.canvas_size.1 / 2.0 - 8.0) as i32;
|
let center_y = (state.canvas_size.1 / 2.0 - 8.0) as i32;
|
||||||
let mut start_frame = tick;
|
let mut start_frame = tick;
|
||||||
|
|
||||||
for x in (0..(center_x + 16)).step_by(16) {
|
for x in 0..(center_x / 16 + 2) {
|
||||||
let mut frame = start_frame;
|
let mut frame = start_frame;
|
||||||
|
|
||||||
for y in (0..(center_y + 16)).step_by(16) {
|
for y in 0..(center_y / 16 + 1) {
|
||||||
if frame > 15 { frame = 15; } else { frame += 1; }
|
if frame >= 15 { frame = 15; } else { frame += 1; }
|
||||||
|
|
||||||
if frame >= 0 {
|
if frame >= 0 {
|
||||||
rect.left = frame as u16 * 16;
|
rect.left = frame.abs() as u16 * 16;
|
||||||
rect.right = rect.left + 16;
|
rect.right = rect.left + 16;
|
||||||
|
|
||||||
batch.add_rect((center_x - x) as f32, (center_y + y) as f32, &rect);
|
batch.add_rect((center_x - x * 16) as f32, (center_y + y * 16) as f32, &rect);
|
||||||
batch.add_rect((center_x - x) as f32, (center_y - y) as f32, &rect);
|
batch.add_rect((center_x - x * 16) as f32, (center_y - y * 16) as f32, &rect);
|
||||||
batch.add_rect((center_x + x) as f32, (center_y + y) as f32, &rect);
|
batch.add_rect((center_x + x * 16) as f32, (center_y + y * 16) as f32, &rect);
|
||||||
batch.add_rect((center_x + x) as f32, (center_y - y) as f32, &rect);
|
batch.add_rect((center_x + x * 16) as f32, (center_y - y * 16) as f32, &rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1213,13 +1213,13 @@ impl Scene for GameScene {
|
||||||
//graphics::set_canvas(ctx, Some(&state.game_canvas));
|
//graphics::set_canvas(ctx, Some(&state.game_canvas));
|
||||||
self.draw_background(state, ctx)?;
|
self.draw_background(state, ctx)?;
|
||||||
self.draw_tiles(state, ctx, TileLayer::Background)?;
|
self.draw_tiles(state, ctx, TileLayer::Background)?;
|
||||||
if state.settings.shader_effects
|
/*if state.settings.shader_effects
|
||||||
&& self.stage.data.background_type != BackgroundType::Black
|
&& self.stage.data.background_type != BackgroundType::Black
|
||||||
&& self.stage.data.background_type != BackgroundType::Outside
|
&& self.stage.data.background_type != BackgroundType::Outside
|
||||||
&& self.stage.data.background_type != BackgroundType::OutsideWind
|
&& self.stage.data.background_type != BackgroundType::OutsideWind
|
||||||
&& self.stage.data.background.name() != "bkBlack" {
|
&& self.stage.data.background.name() != "bkBlack" {
|
||||||
self.draw_light_map(state, ctx)?;
|
self.draw_light_map(state, ctx)?;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
self.boss.draw(state, ctx, &self.frame)?;
|
self.boss.draw(state, ctx, &self.frame)?;
|
||||||
for npc in self.npc_list.iter_alive() {
|
for npc in self.npc_list.iter_alive() {
|
||||||
|
@ -1242,11 +1242,11 @@ impl Scene for GameScene {
|
||||||
self.draw_tiles(state, ctx, TileLayer::Foreground)?;
|
self.draw_tiles(state, ctx, TileLayer::Foreground)?;
|
||||||
self.draw_tiles(state, ctx, TileLayer::Snack)?;
|
self.draw_tiles(state, ctx, TileLayer::Snack)?;
|
||||||
self.draw_carets(state, ctx)?;
|
self.draw_carets(state, ctx)?;
|
||||||
if state.settings.shader_effects
|
/*if state.settings.shader_effects
|
||||||
&& (self.stage.data.background_type == BackgroundType::Black
|
&& (self.stage.data.background_type == BackgroundType::Black
|
||||||
|| self.stage.data.background.name() == "bkBlack") {
|
|| self.stage.data.background.name() == "bkBlack") {
|
||||||
self.draw_light_map(state, ctx)?;
|
self.draw_light_map(state, ctx)?;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/*graphics::set_canvas(ctx, None);
|
/*graphics::set_canvas(ctx, None);
|
||||||
state.game_canvas.draw(ctx, DrawParam::new()
|
state.game_canvas.draw(ctx, DrawParam::new()
|
||||||
|
|
|
@ -119,11 +119,6 @@ pub struct SharedGameState {
|
||||||
|
|
||||||
impl SharedGameState {
|
impl SharedGameState {
|
||||||
pub fn new(ctx: &mut Context) -> GameResult<SharedGameState> {
|
pub fn new(ctx: &mut Context) -> GameResult<SharedGameState> {
|
||||||
let screen_size = (320.0, 240.0);
|
|
||||||
let scale = *screen_size.1.div(230.0).floor().max(&1.0);
|
|
||||||
|
|
||||||
let canvas_size = (screen_size.0 / scale, screen_size.1 / scale);
|
|
||||||
|
|
||||||
let mut constants = EngineConstants::defaults();
|
let mut constants = EngineConstants::defaults();
|
||||||
let mut base_path = "/";
|
let mut base_path = "/";
|
||||||
let settings = Settings::load(ctx)?;
|
let settings = Settings::load(ctx)?;
|
||||||
|
@ -170,9 +165,9 @@ impl SharedGameState {
|
||||||
npc_super_pos: (0, 0),
|
npc_super_pos: (0, 0),
|
||||||
stages: Vec::with_capacity(96),
|
stages: Vec::with_capacity(96),
|
||||||
frame_time: 0.0,
|
frame_time: 0.0,
|
||||||
scale,
|
scale: 2.0,
|
||||||
screen_size,
|
screen_size: (640.0, 480.0),
|
||||||
canvas_size,
|
canvas_size: (320.0, 240.0),
|
||||||
next_scene: None,
|
next_scene: None,
|
||||||
textscript_vm: TextScriptVM::new(),
|
textscript_vm: TextScriptVM::new(),
|
||||||
season,
|
season,
|
||||||
|
@ -280,12 +275,10 @@ impl SharedGameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_resize(&mut self, ctx: &mut Context) -> GameResult {
|
pub fn handle_resize(&mut self, ctx: &mut Context) -> GameResult {
|
||||||
self.screen_size = graphics::drawable_size(ctx);
|
self.screen_size = graphics::screen_size(ctx);
|
||||||
self.scale = self.screen_size.1.div(230.0).floor().max(1.0);
|
self.scale = self.screen_size.1.div(230.0).floor().max(1.0);
|
||||||
self.canvas_size = (self.screen_size.0 / self.scale, self.screen_size.1 / self.scale);
|
self.canvas_size = (self.screen_size.0 / self.scale, self.screen_size.1 / self.scale);
|
||||||
|
|
||||||
//graphics::set_screen_coordinates(ctx, graphics::Rect::new(0.0, 0.0, self.screen_size.0, self.screen_size.1))?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,6 @@ use std::ops::Not;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use byteorder::ReadBytesExt;
|
use byteorder::ReadBytesExt;
|
||||||
|
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use num_derive::FromPrimitive;
|
use num_derive::FromPrimitive;
|
||||||
use num_traits::{clamp, FromPrimitive};
|
use num_traits::{clamp, FromPrimitive};
|
||||||
|
@ -21,6 +19,9 @@ use crate::encoding::{read_cur_shift_jis, read_cur_wtf8};
|
||||||
use crate::engine_constants::EngineConstants;
|
use crate::engine_constants::EngineConstants;
|
||||||
use crate::entity::GameEntity;
|
use crate::entity::GameEntity;
|
||||||
use crate::frame::UpdateTarget;
|
use crate::frame::UpdateTarget;
|
||||||
|
use crate::framework::context::Context;
|
||||||
|
use crate::framework::error::GameError::{InvalidValue, ParseError};
|
||||||
|
use crate::framework::error::GameResult;
|
||||||
use crate::npc::NPC;
|
use crate::npc::NPC;
|
||||||
use crate::player::{ControlMode, TargetPlayer};
|
use crate::player::{ControlMode, TargetPlayer};
|
||||||
use crate::scene::game_scene::GameScene;
|
use crate::scene::game_scene::GameScene;
|
||||||
|
@ -28,7 +29,6 @@ use crate::scene::title_scene::TitleScene;
|
||||||
use crate::shared_game_state::SharedGameState;
|
use crate::shared_game_state::SharedGameState;
|
||||||
use crate::str;
|
use crate::str;
|
||||||
use crate::weapon::WeaponType;
|
use crate::weapon::WeaponType;
|
||||||
use crate::framework::error::GameError::ParseError;
|
|
||||||
|
|
||||||
/// Engine's text script VM operation codes.
|
/// Engine's text script VM operation codes.
|
||||||
#[derive(EnumString, Debug, FromPrimitive, PartialEq)]
|
#[derive(EnumString, Debug, FromPrimitive, PartialEq)]
|
||||||
|
|
|
@ -6,19 +6,22 @@ use itertools::Itertools;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use crate::common;
|
use crate::common;
|
||||||
use crate::common::FILE_TYPES;
|
use crate::common::{FILE_TYPES, Point, Rect};
|
||||||
use crate::engine_constants::EngineConstants;
|
use crate::engine_constants::EngineConstants;
|
||||||
|
use crate::framework::backend::{BackendTexture, SpriteBatchCommand};
|
||||||
use crate::framework::context::Context;
|
use crate::framework::context::Context;
|
||||||
use crate::framework::error::{GameError, GameResult};
|
use crate::framework::error::{GameError, GameResult};
|
||||||
use crate::framework::filesystem;
|
use crate::framework::filesystem;
|
||||||
use crate::framework::image::Image;
|
use crate::framework::graphics::{create_texture, FilterMode};
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use crate::shared_game_state::Season;
|
use crate::shared_game_state::Season;
|
||||||
use crate::str;
|
use crate::str;
|
||||||
|
|
||||||
|
pub static mut I_MAG: f32 = 1.0;
|
||||||
pub static mut G_MAG: f32 = 1.0;
|
pub static mut G_MAG: f32 = 1.0;
|
||||||
|
|
||||||
pub struct SizedBatch {
|
pub struct SizedBatch {
|
||||||
|
batch: Box<dyn BackendTexture>,
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
real_width: usize,
|
real_width: usize,
|
||||||
|
@ -60,15 +63,30 @@ impl SizedBatch {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
/*self.batch.clear();*/
|
self.batch.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&mut self, x: f32, y: f32) {
|
pub fn add(&mut self, mut x: f32, mut y: f32) {
|
||||||
/*let param = DrawParam::new()
|
unsafe {
|
||||||
.dest(Point2::new(x, y))
|
x = (x * G_MAG).floor() / G_MAG;
|
||||||
.scale(Vector2::new(self.scale_x, self.scale_y));
|
y = (y * G_MAG).floor() / G_MAG;
|
||||||
|
}
|
||||||
|
let mag = unsafe { I_MAG };
|
||||||
|
|
||||||
self.batch.add(param);*/
|
self.batch.add(SpriteBatchCommand::DrawRect(
|
||||||
|
Rect {
|
||||||
|
left: 0 as f32,
|
||||||
|
top: 0 as f32,
|
||||||
|
right: self.real_width as f32,
|
||||||
|
bottom: self.real_height as f32,
|
||||||
|
},
|
||||||
|
Rect {
|
||||||
|
left: x * mag,
|
||||||
|
top: y * mag,
|
||||||
|
right: (x + self.width() as f32) * mag,
|
||||||
|
bottom: (y + self.height() as f32) * mag,
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -81,7 +99,7 @@ impl SizedBatch {
|
||||||
self.add_rect_scaled_tinted(x, y, color, self.scale_x, self.scale_y, rect)
|
self.add_rect_scaled_tinted(x, y, color, self.scale_x, self.scale_y, rect)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_rect_scaled(&mut self, mut x: f32, mut y: f32, scale_x: f32, scale_y: f32, rect: &common::Rect<u16>) {
|
pub fn add_rect_scaled(&mut self, mut x: f32, mut y: f32, mut scale_x: f32, mut scale_y: f32, rect: &common::Rect<u16>) {
|
||||||
if (rect.right - rect.left) == 0 || (rect.bottom - rect.top) == 0 {
|
if (rect.right - rect.left) == 0 || (rect.bottom - rect.top) == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -90,45 +108,61 @@ impl SizedBatch {
|
||||||
x = (x * G_MAG).floor() / G_MAG;
|
x = (x * G_MAG).floor() / G_MAG;
|
||||||
y = (y * G_MAG).floor() / G_MAG;
|
y = (y * G_MAG).floor() / G_MAG;
|
||||||
}
|
}
|
||||||
|
let mag = unsafe { I_MAG };
|
||||||
|
|
||||||
/*let param = DrawParam::new()
|
self.batch.add(SpriteBatchCommand::DrawRect(
|
||||||
.src(Rect::new(rect.left as f32 / self.width as f32,
|
Rect {
|
||||||
rect.top as f32 / self.height as f32,
|
left: rect.left as f32 / scale_x,
|
||||||
(rect.right - rect.left) as f32 / self.width as f32,
|
top: rect.top as f32 / scale_y,
|
||||||
(rect.bottom - rect.top) as f32 / self.height as f32))
|
right: rect.right as f32 / scale_x,
|
||||||
.dest(mint::Point2 { x, y })
|
bottom: rect.bottom as f32 / scale_y,
|
||||||
.scale(Vector2::new(scale_x, scale_y));
|
},
|
||||||
|
Rect {
|
||||||
self.batch.add(param);*/
|
left: x * mag,
|
||||||
|
top: y * mag,
|
||||||
|
right: (x + rect.width() as f32) * mag,
|
||||||
|
bottom: (y + rect.height() as f32) * mag,
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_rect_scaled_tinted(&mut self, x: f32, y: f32, color: (u8, u8, u8, u8), scale_x: f32, scale_y: f32, rect: &common::Rect<u16>) {
|
pub fn add_rect_scaled_tinted(&mut self, mut x: f32, mut y: f32, color: (u8, u8, u8, u8), mut scale_x: f32, mut scale_y: f32, rect: &common::Rect<u16>) {
|
||||||
if (rect.right - rect.left) == 0 || (rect.bottom - rect.top) == 0 {
|
if (rect.right - rect.left) == 0 || (rect.bottom - rect.top) == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*let param = DrawParam::new()
|
unsafe {
|
||||||
.color(color.into())
|
x = (x * G_MAG).floor() / G_MAG;
|
||||||
.src(Rect::new(rect.left as f32 / self.width as f32,
|
y = (y * G_MAG).floor() / G_MAG;
|
||||||
rect.top as f32 / self.height as f32,
|
}
|
||||||
(rect.right - rect.left) as f32 / self.width as f32,
|
let mag = unsafe { I_MAG };
|
||||||
(rect.bottom - rect.top) as f32 / self.height as f32))
|
|
||||||
.dest(mint::Point2 { x, y })
|
|
||||||
.scale(Vector2::new(scale_x, scale_y));
|
|
||||||
|
|
||||||
self.batch.add(param);*/
|
self.batch.add(SpriteBatchCommand::DrawRectTinted(
|
||||||
|
Rect {
|
||||||
|
left: rect.left as f32 / scale_x,
|
||||||
|
top: rect.top as f32 / scale_y,
|
||||||
|
right: rect.right as f32 / scale_x,
|
||||||
|
bottom: rect.bottom as f32 / scale_y,
|
||||||
|
},
|
||||||
|
Rect {
|
||||||
|
left: x * mag,
|
||||||
|
top: y * mag,
|
||||||
|
right: (x + rect.width() as f32) * mag,
|
||||||
|
bottom: (y + rect.height() as f32) * mag,
|
||||||
|
},
|
||||||
|
color.into(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn draw(&mut self, ctx: &mut Context) -> GameResult {
|
pub fn draw(&mut self, ctx: &mut Context) -> GameResult {
|
||||||
//self.draw_filtered(FilterMode::Nearest, ctx)
|
self.draw_filtered(FilterMode::Nearest, ctx)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_filtered(&mut self, filter: FilterMode, ctx: &mut Context) -> GameResult {
|
pub fn draw_filtered(&mut self, filter: FilterMode, ctx: &mut Context) -> GameResult {
|
||||||
/*self.batch.set_filter(filter);
|
///self.batch.set_filter(filter);
|
||||||
self.batch.draw(ctx, DrawParam::new())?;
|
self.batch.draw()?;
|
||||||
self.batch.clear();*/
|
self.batch.clear();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,7 +200,7 @@ impl TextureSet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_image(&self, ctx: &mut Context, path: &str) -> GameResult<Image> {
|
fn load_image(&self, ctx: &mut Context, path: &str) -> GameResult<Box<dyn BackendTexture>> {
|
||||||
let img = {
|
let img = {
|
||||||
let mut buf = [0u8; 8];
|
let mut buf = [0u8; 8];
|
||||||
let mut reader = filesystem::open(ctx, path)?;
|
let mut reader = filesystem::open(ctx, path)?;
|
||||||
|
@ -182,7 +216,7 @@ impl TextureSet {
|
||||||
};
|
};
|
||||||
let (width, height) = img.dimensions();
|
let (width, height) = img.dimensions();
|
||||||
|
|
||||||
Image::from_rgba8(ctx, width as u16, height as u16, img.as_ref())
|
create_texture(ctx, width as u16, height as u16, &img)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_texture(&self, ctx: &mut Context, constants: &EngineConstants, name: &str) -> GameResult<SizedBatch> {
|
pub fn load_texture(&self, ctx: &mut Context, constants: &EngineConstants, name: &str) -> GameResult<SizedBatch> {
|
||||||
|
@ -197,26 +231,26 @@ impl TextureSet {
|
||||||
|
|
||||||
info!("Loading texture: {}", path);
|
info!("Loading texture: {}", path);
|
||||||
|
|
||||||
let image = self.load_image(ctx, &path)?;
|
let batch = self.load_image(ctx, &path)?;
|
||||||
let size = image.dimensions();
|
let size = batch.dimensions();
|
||||||
|
|
||||||
assert_ne!(size.w as isize, 0, "size.w == 0");
|
assert_ne!(size.0 as isize, 0, "size.width == 0");
|
||||||
assert_ne!(size.h as isize, 0, "size.h == 0");
|
assert_ne!(size.1 as isize, 0, "size.height == 0");
|
||||||
|
|
||||||
let dim = (size.w as usize, size.h as usize);
|
let orig_dimensions = constants.tex_sizes.get(name).unwrap_or_else(|| &size);
|
||||||
let orig_dimensions = constants.tex_sizes.get(name).unwrap_or_else(|| &dim);
|
let scale = orig_dimensions.0 as f32 / size.0 as f32;
|
||||||
let scale_x = orig_dimensions.0 as f32 / size.w;
|
let width = (size.0 as f32 * scale) as usize;
|
||||||
let scale_y = orig_dimensions.0 as f32 / size.w;
|
let height = (size.1 as f32 * scale) as usize;
|
||||||
let width = (size.w * scale_x) as usize;
|
println!("{} {} {} {}", size.0, size.1, width, height);
|
||||||
let height = (size.h * scale_y) as usize;
|
|
||||||
|
|
||||||
Ok(SizedBatch {
|
Ok(SizedBatch {
|
||||||
|
batch,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
scale_x,
|
scale_x: scale,
|
||||||
scale_y,
|
scale_y: scale,
|
||||||
real_width: size.w as usize,
|
real_width: size.0 as usize,
|
||||||
real_height: size.h as usize,
|
real_height: size.1 as usize,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue