mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2025-03-24 19:09:22 +00:00
(wip) start rewriting rendering stuff
This commit is contained in:
parent
c9c746a6db
commit
3c20e089f9
45
Cargo.toml
45
Cargo.toml
|
@ -18,12 +18,12 @@ opengles_version = [3, 1]
|
|||
fullscreen = true
|
||||
orientation = "sensorLandscape"
|
||||
permission = [
|
||||
{name = "android.permission.READ_EXTERNAL_STORAGE"},
|
||||
{name = "android.permission.WRITE_EXTERNAL_STORAGE"}
|
||||
{ name = "android.permission.READ_EXTERNAL_STORAGE" },
|
||||
{ name = "android.permission.WRITE_EXTERNAL_STORAGE" }
|
||||
]
|
||||
application_metadatas = [
|
||||
{name = "android:hardwareAccelerated", value = "true"},
|
||||
{name = "android:requestLegacyExternalStorage", value = "true"}
|
||||
{ name = "android:hardwareAccelerated", value = "true" },
|
||||
{ name = "android:requestLegacyExternalStorage", value = "true" }
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
@ -40,42 +40,34 @@ opt-level = 1
|
|||
opt-level = 1
|
||||
|
||||
[features]
|
||||
default = ["scripting"]
|
||||
default = ["scripting", "backend-sdl"]
|
||||
backend-sdl = ["sdl2"]
|
||||
backend-gfx = ["winit", "imgui-gfx-renderer", "imgui-winit-support"]
|
||||
scripting = ["lua-ffi"]
|
||||
editor = []
|
||||
|
||||
[dependencies]
|
||||
#cpal = {path = "./3rdparty/cpal"}
|
||||
#gfx_device_gl = {path = "./3rdparty/gfx/src/backend/gl"}
|
||||
#ggez = {path = "./3rdparty/ggez"}
|
||||
#glutin = {path = "./3rdparty/glutin/glutin"}
|
||||
#lua-ffi = {path = "./3rdparty/luajit-rs", optional = true}
|
||||
|
||||
bitvec = "0.17.4"
|
||||
byteorder = "1.3"
|
||||
case_insensitive_hashmap = "1.0.0"
|
||||
chrono = "0.4"
|
||||
cpal = {git = "https://github.com/doukutsu-rs/cpal.git", branch = "android-support"}
|
||||
directories = "2"
|
||||
gfx = "0.18"
|
||||
gfx_core = "0.9"
|
||||
gfx_device_gl = {git = "https://github.com/doukutsu-rs/gfx.git", branch = "pre-ll"}
|
||||
ggez = {git = "https://github.com/doukutsu-rs/ggez.git", rev = "43631b0401271d4bc8fe4a5afba8aad63976dba1"}
|
||||
glutin = {git = "https://github.com/doukutsu-rs/glutin.git", branch = "master"}
|
||||
imgui = {git = "https://github.com/Gekkio/imgui-rs.git", rev = "7e2293bde67f869750ab0e649fbfbd842fb0c785"}
|
||||
imgui-gfx-renderer = {git = "https://github.com/Gekkio/imgui-rs.git", rev = "7e2293bde67f869750ab0e649fbfbd842fb0c785"}
|
||||
imgui-winit-support = {git = "https://github.com/Gekkio/imgui-rs.git", default-features = false, features = ["winit-23"], rev = "7e2293bde67f869750ab0e649fbfbd842fb0c785"}
|
||||
image = {version = "0.22", default-features = false, features = ["png_codec", "pnm", "bmp"]}
|
||||
cpal = { git = "https://github.com/doukutsu-rs/cpal.git", branch = "android-support" }
|
||||
directories = "3"
|
||||
imgui = { git = "https://github.com/Gekkio/imgui-rs.git", rev = "7e2293bde67f869750ab0e649fbfbd842fb0c785" }
|
||||
imgui-gfx-renderer = { git = "https://github.com/Gekkio/imgui-rs.git", rev = "7e2293bde67f869750ab0e649fbfbd842fb0c785", optional = true }
|
||||
imgui-winit-support = { git = "https://github.com/Gekkio/imgui-rs.git", default-features = false, features = ["winit-23"], rev = "7e2293bde67f869750ab0e649fbfbd842fb0c785", optional = true }
|
||||
image = { version = "0.22", default-features = false, features = ["png_codec", "pnm", "bmp"] }
|
||||
itertools = "0.9.0"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4"
|
||||
lua-ffi = {git = "https://github.com/doukutsu-rs/lua-ffi.git", rev = "1ef3caf772d72068297ddf75df06fd2ef8c1daab", optional = true}
|
||||
lua-ffi = { git = "https://github.com/doukutsu-rs/lua-ffi.git", rev = "1ef3caf772d72068297ddf75df06fd2ef8c1daab", optional = true }
|
||||
lru = "0.6.0"
|
||||
num-derive = "0.3.2"
|
||||
num-traits = "0.2.12"
|
||||
paste = "1.0.0"
|
||||
pretty_env_logger = "0.4.0"
|
||||
serde = {version = "1", features = ["derive"]}
|
||||
sdl2 = { version = "0.34.3", optional = true }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_derive = "1"
|
||||
serde_yaml = "0.8"
|
||||
strum = "0.18.0"
|
||||
|
@ -83,7 +75,10 @@ strum_macros = "0.18.0"
|
|||
# remove and replace when drain_filter is in stable
|
||||
vec_mut_scan = "0.3.0"
|
||||
webbrowser = "0.5.5"
|
||||
winit = {version = "0.24.0", features = ["serde"]}
|
||||
winit = { version = "0.24.0", features = ["serde"], optional = true }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = { version = "0.3", features = ["winuser"] }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
ndk = "0.2"
|
||||
|
|
|
@ -3,8 +3,9 @@ use std::io;
|
|||
|
||||
use byteorder::{LE, ReadBytesExt};
|
||||
|
||||
use ggez::GameError::ResourceLoadError;
|
||||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameError::ResourceLoadError;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::str;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -4,10 +4,13 @@ use std::path::PathBuf;
|
|||
use crate::bmfont::BMFont;
|
||||
use crate::common::{FILE_TYPES, Rect};
|
||||
use crate::engine_constants::EngineConstants;
|
||||
use ggez::{Context, filesystem, GameResult};
|
||||
use ggez::GameError::ResourceLoadError;
|
||||
|
||||
use crate::str;
|
||||
use crate::texture_set::TextureSet;
|
||||
use crate::framework::error::GameError::ResourceLoadError;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::filesystem;
|
||||
|
||||
pub struct BMFontRenderer {
|
||||
font: BMFont,
|
||||
|
|
|
@ -5,9 +5,9 @@ use std::io::ErrorKind;
|
|||
use std::io::SeekFrom;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
use ggez::GameResult;
|
||||
use ggez::GameError::FilesystemError;
|
||||
use ggez::vfs::{OpenOptions, VFile, VFS, VMetadata};
|
||||
use crate::framework::error::GameError::FilesystemError;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::vfs::{OpenOptions, VFile, VMetadata, VFS};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BuiltinFile(Cursor<&'static [u8]>);
|
||||
|
@ -59,7 +59,7 @@ impl VMetadata for BuiltinMetadata {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
enum FSNode {
|
||||
File(&'static str, &'static [u8]),
|
||||
Directory(&'static str, Vec<FSNode>),
|
||||
|
@ -232,7 +232,7 @@ impl VFS for BuiltinFS {
|
|||
}
|
||||
}
|
||||
|
||||
/*#[test]
|
||||
#[test]
|
||||
fn test_builtin_fs() {
|
||||
let fs = BuiltinFS {
|
||||
root: vec![
|
||||
|
@ -240,7 +240,7 @@ fn test_builtin_fs() {
|
|||
FSNode::Directory("memes", vec![
|
||||
FSNode::File("nothing.txt", &[]),
|
||||
FSNode::Directory("secret stuff", vec![
|
||||
FSNode::File("passwords.txt", &[b'1', b'2', b'3', b'4', b'5', b'6',]),
|
||||
FSNode::File("passwords.txt", &b"12345678"),
|
||||
]),
|
||||
]),
|
||||
FSNode::File("test2.txt", &[]),
|
||||
|
@ -252,4 +252,4 @@ fn test_builtin_fs() {
|
|||
println!("{:?}", fs.get_node(Path::new("/memes")).unwrap());
|
||||
println!("{:?}", fs.get_node(Path::new("/memes/nothing.txt")).unwrap());
|
||||
println!("{:?}", fs.get_node(Path::new("/memes/secret stuff/passwords.txt")).unwrap());
|
||||
}*/
|
||||
}
|
||||
|
|
183
src/common.rs
183
src/common.rs
|
@ -260,16 +260,6 @@ impl<T: Num + PartialOrd + Copy> Rect<T> {
|
|||
bottom: y.add(height),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from(rect: ggez::graphics::Rect) -> Rect<f32> {
|
||||
Rect {
|
||||
left: rect.x,
|
||||
top: rect.y,
|
||||
right: (rect.x + rect.w),
|
||||
bottom: (rect.y + rect.h),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn width(&self) -> T {
|
||||
if let Some(Ordering::Greater) = self.left.partial_cmp(&self.right) {
|
||||
self.left.sub(self.right)
|
||||
|
@ -287,15 +277,6 @@ impl<T: Num + PartialOrd + Copy> Rect<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Num + PartialOrd + Copy + AsPrimitive<f32>> Into<ggez::graphics::Rect> for Rect<T> {
|
||||
fn into(self) -> ggez::graphics::Rect {
|
||||
ggez::graphics::Rect::new(self.left.as_(),
|
||||
self.top.as_(),
|
||||
self.width().as_(),
|
||||
self.height().as_())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Num + PartialOrd + Copy + Serialize> Serialize for Rect<T> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
|
@ -371,3 +352,167 @@ pub fn interpolate_fix9_scale(old_val: i32, val: i32, frame_delta: f64) -> f32 {
|
|||
|
||||
(lerp_f64(old_val as f64, val as f64, frame_delta) / 512.0) as f32
|
||||
}
|
||||
|
||||
|
||||
/// A RGBA color in the `sRGB` color space represented as `f32`'s in the range `[0.0-1.0]`
|
||||
///
|
||||
/// For convenience, [`WHITE`](constant.WHITE.html) and [`BLACK`](constant.BLACK.html) are provided.
|
||||
#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct Color {
|
||||
/// Red component
|
||||
pub r: f32,
|
||||
/// Green component
|
||||
pub g: f32,
|
||||
/// Blue component
|
||||
pub b: f32,
|
||||
/// Alpha component
|
||||
pub a: f32,
|
||||
}
|
||||
|
||||
/// White
|
||||
pub const WHITE: Color = Color {
|
||||
r: 1.0,
|
||||
g: 1.0,
|
||||
b: 1.0,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
/// Black
|
||||
pub const BLACK: Color = Color {
|
||||
r: 0.0,
|
||||
g: 0.0,
|
||||
b: 0.0,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
impl Color {
|
||||
/// Create a new `Color` from four `f32`'s in the range `[0.0-1.0]`
|
||||
pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||
Color { r, g, b, a }
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
Color::from((r, g, b, a))
|
||||
}
|
||||
|
||||
/// Create a new `Color` from three u8's in the range `[0-255]`,
|
||||
/// with the alpha component fixed to 255 (opaque)
|
||||
pub const fn from_rgb(r: u8, g: u8, b: u8) -> Color {
|
||||
Color::from((r, g, b))
|
||||
}
|
||||
|
||||
/// Return a tuple of four `u8`'s in the range `[0-255]` with the `Color`'s
|
||||
/// components.
|
||||
pub const fn to_rgba(self) -> (u8, u8, u8, u8) {
|
||||
self.into()
|
||||
}
|
||||
|
||||
/// Return a tuple of three `u8`'s in the range `[0-255]` with the `Color`'s
|
||||
/// components.
|
||||
pub const fn to_rgb(self) -> (u8, u8, u8) {
|
||||
self.into()
|
||||
}
|
||||
|
||||
/// Convert a packed `u32` containing `0xRRGGBBAA` into a `Color`
|
||||
pub const fn from_rgba_u32(c: u32) -> Color {
|
||||
let c = c.to_be_bytes();
|
||||
|
||||
Color::from((c[0], c[1], c[2], c[3]))
|
||||
}
|
||||
|
||||
/// Convert a packed `u32` containing `0x00RRGGBB` into a `Color`.
|
||||
/// This lets you do things like `Color::from_rgb_u32(0xCD09AA)` easily if you want.
|
||||
pub const fn from_rgb_u32(c: u32) -> Color {
|
||||
let c = c.to_be_bytes();
|
||||
|
||||
Color::from((c[1], c[2], c[3]))
|
||||
}
|
||||
|
||||
/// Convert a `Color` into a packed `u32`, containing `0xRRGGBBAA` as bytes.
|
||||
pub const fn to_rgba_u32(self) -> u32 {
|
||||
let (r, g, b, a): (u8, u8, u8, u8) = self.into();
|
||||
|
||||
u32::from_be_bytes([r, g, b, a])
|
||||
}
|
||||
|
||||
/// Convert a `Color` into a packed `u32`, containing `0x00RRGGBB` as bytes.
|
||||
pub const fn to_rgb_u32(self) -> u32 {
|
||||
let (r, g, b, _a): (u8, u8, u8, u8) = self.into();
|
||||
|
||||
u32::from_be_bytes([0, r, g, b])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(u8, u8, u8, u8)> for Color {
|
||||
/// Convert a `(R, G, B, A)` tuple of `u8`'s in the range `[0-255]` into a `Color`
|
||||
fn from(val: (u8, u8, u8, u8)) -> Self {
|
||||
let (r, g, b, a) = val;
|
||||
let rf = (f32::from(r)) / 255.0;
|
||||
let gf = (f32::from(g)) / 255.0;
|
||||
let bf = (f32::from(b)) / 255.0;
|
||||
let af = (f32::from(a)) / 255.0;
|
||||
Color::new(rf, gf, bf, af)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(u8, u8, u8)> for Color {
|
||||
/// Convert a `(R, G, B)` tuple of `u8`'s in the range `[0-255]` into a `Color`,
|
||||
/// with a value of 255 for the alpha element (i.e., no transparency.)
|
||||
fn from(val: (u8, u8, u8)) -> Self {
|
||||
let (r, g, b) = val;
|
||||
Color::from((r, g, b, 255))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[f32; 4]> for Color {
|
||||
/// Turns an `[R, G, B, A] array of `f32`'s into a `Color` with no format changes.
|
||||
/// All inputs should be in the range `[0.0-1.0]`.
|
||||
fn from(val: [f32; 4]) -> Self {
|
||||
Color::new(val[0], val[1], val[2], val[3])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(f32, f32, f32)> for Color {
|
||||
/// Convert a `(R, G, B)` tuple of `f32`'s in the range `[0.0-1.0]` into a `Color`,
|
||||
/// with a value of 1.0 to for the alpha element (ie, no transparency.)
|
||||
fn from(val: (f32, f32, f32)) -> Self {
|
||||
let (r, g, b) = val;
|
||||
Color::new(r, g, b, 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(f32, f32, f32, f32)> for Color {
|
||||
/// Convert a `(R, G, B, A)` tuple of `f32`'s in the range `[0.0-1.0]` into a `Color`
|
||||
fn from(val: (f32, f32, f32, f32)) -> Self {
|
||||
let (r, g, b, a) = val;
|
||||
Color::new(r, g, b, a)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for (u8, u8, u8, u8) {
|
||||
/// Convert a `Color` into a `(R, G, B, A)` tuple of `u8`'s in the range of `[0-255]`.
|
||||
fn from(color: Color) -> Self {
|
||||
let r = (color.r * 255.0) as u8;
|
||||
let g = (color.g * 255.0) as u8;
|
||||
let b = (color.b * 255.0) as u8;
|
||||
let a = (color.a * 255.0) as u8;
|
||||
(r, g, b, a)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for (u8, u8, u8) {
|
||||
/// Convert a `Color` into a `(R, G, B)` tuple of `u8`'s in the range of `[0-255]`,
|
||||
/// ignoring the alpha term.
|
||||
fn from(color: Color) -> Self {
|
||||
let (r, g, b, _) = color.into();
|
||||
(r, g, b)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for [f32; 4] {
|
||||
/// Convert a `Color` into an `[R, G, B, A]` array of `f32`'s in the range of `[0.0-1.0]`.
|
||||
fn from(color: Color) -> Self {
|
||||
[color.r, color.g, color.b, color.a]
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
use ggez::{Context, GameResult};
|
||||
|
||||
use crate::common::Rect;
|
||||
use crate::entity::GameEntity;
|
||||
use crate::frame::Frame;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::npc::boss::BossNPC;
|
||||
use crate::npc::list::NPCList;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
|
@ -52,7 +52,7 @@ impl BossLifeBar {
|
|||
}
|
||||
|
||||
impl GameEntity<(&NPCList, &BossNPC)> for BossLifeBar {
|
||||
fn tick(&mut self, _state: &mut SharedGameState, (npc_list, boss): (&NPCList, &BossNPC)) -> GameResult<> {
|
||||
fn tick(&mut self, _state: &mut SharedGameState, (npc_list, boss): (&NPCList, &BossNPC)) -> GameResult<()> {
|
||||
match self.target {
|
||||
BossLifeTarget::NPC(npc_id) => {
|
||||
if let Some(npc) = npc_list.get_npc(npc_id as usize) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use ggez::{Context, GameResult};
|
||||
|
||||
use crate::common::Rect;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
#[derive(Debug, EnumIter, PartialEq, Eq, Hash, Copy, Clone)]
|
||||
pub enum Alignment {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::{Context, GameResult};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::common::Rect;
|
||||
use crate::components::draw_common::{Alignment, draw_number};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::{Context, GameResult};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::common::Rect;
|
||||
use crate::entity::GameEntity;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::{Context, GameResult};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::frame::Frame;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
|
|
9
src/framework/backend.rs
Normal file
9
src/framework/backend.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use crate::Game;
|
||||
|
||||
pub(crate) trait Backend {
|
||||
fn create_event_loop(&self) -> Box<dyn BackendEventLoop>;
|
||||
}
|
||||
|
||||
pub(crate) trait BackendEventLoop {
|
||||
fn run(&self, game: &mut Game);
|
||||
}
|
0
src/framework/backend_sdl2.rs
Normal file
0
src/framework/backend_sdl2.rs
Normal file
18
src/framework/context.rs
Normal file
18
src/framework/context.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use crate::framework::filesystem::Filesystem;
|
||||
use crate::Game;
|
||||
|
||||
pub struct Context {
|
||||
pub(crate) filesystem: Filesystem,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new() -> Context {
|
||||
Context {
|
||||
filesystem: Filesystem::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self, game: &mut Game) {
|
||||
loop {}
|
||||
}
|
||||
}
|
145
src/framework/error.rs
Normal file
145
src/framework/error.rs
Normal file
|
@ -0,0 +1,145 @@
|
|||
//! Error types and conversion functions.
|
||||
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::string::FromUtf8Error;
|
||||
use std::sync::{Arc, PoisonError};
|
||||
use std::sync::mpsc::SendError;
|
||||
|
||||
/// An enum containing all kinds of game framework errors.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GameError {
|
||||
/// An error in the filesystem layout
|
||||
FilesystemError(String),
|
||||
/// An error in the config file
|
||||
ConfigError(String),
|
||||
/// Happens when an `winit::EventsLoopProxy` attempts to
|
||||
/// wake up an `winit::EventsLoop` that no longer exists.
|
||||
EventLoopError(String),
|
||||
/// An error trying to load a resource, such as getting an invalid image file.
|
||||
ResourceLoadError(String),
|
||||
/// Unable to find a resource; the `Vec` is the paths it searched for and associated errors
|
||||
ResourceNotFound(String, Vec<(std::path::PathBuf, GameError)>),
|
||||
/// Something went wrong in the renderer
|
||||
RenderError(String),
|
||||
/// Something went wrong in the audio playback
|
||||
AudioError(String),
|
||||
/// Something went wrong trying to set or get window properties.
|
||||
WindowError(String),
|
||||
/// Something went wrong trying to read from a file
|
||||
IOError(Arc<std::io::Error>),
|
||||
/// Something went wrong trying to load/render a font
|
||||
FontError(String),
|
||||
/// Something went wrong applying video settings.
|
||||
VideoError(String),
|
||||
/// Something went wrong compiling shaders
|
||||
ShaderProgramError(String),
|
||||
/// Something went wrong with the `gilrs` gamepad-input library.
|
||||
GamepadError(String),
|
||||
/// Something went wrong with the `lyon` shape-tesselation library.
|
||||
LyonError(String),
|
||||
/// Something went wrong while parsing something.
|
||||
ParseError(String),
|
||||
/// Something went wrong while converting a value.
|
||||
InvalidValue(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for GameError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
GameError::ConfigError(ref s) => write!(f, "Config error: {}", s),
|
||||
GameError::ResourceLoadError(ref s) => write!(f, "Error loading resource: {}", s),
|
||||
GameError::ResourceNotFound(ref s, ref paths) => write!(
|
||||
f,
|
||||
"Resource not found: {}, searched in paths {:?}",
|
||||
s, paths
|
||||
),
|
||||
GameError::WindowError(ref e) => write!(f, "Window creation error: {}", e),
|
||||
_ => write!(f, "GameError {:?}", self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for GameError {
|
||||
fn cause(&self) -> Option<&dyn Error> {
|
||||
match *self {
|
||||
GameError::WindowCreationError(ref e) => Some(&**e),
|
||||
GameError::IOError(ref e) => Some(&**e),
|
||||
GameError::ShaderProgramError(ref e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenient result type consisting of a return type and a `GameError`
|
||||
pub type GameResult<T = ()> = Result<T, GameError>;
|
||||
|
||||
impl From<std::io::Error> for GameError {
|
||||
fn from(e: std::io::Error) -> GameError {
|
||||
GameError::IOError(Arc::new(e))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<image::ImageError> for GameError {
|
||||
fn from(e: image::ImageError) -> GameError {
|
||||
let errstr = format!("Image load error: {}", e);
|
||||
GameError::ResourceLoadError(errstr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::string::FromUtf8Error> for GameError {
|
||||
fn from(e: FromUtf8Error) -> Self {
|
||||
let errstr = format!("UTF-8 decoding error: {:?}", e);
|
||||
GameError::ConfigError(errstr)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
impl From<jni::errors::Error> for GameError {
|
||||
fn from(e: jni::errors::Error) -> GameError {
|
||||
GameError::WindowError(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<strum::ParseError> for GameError {
|
||||
fn from(s: strum::ParseError) -> GameError {
|
||||
let errstr = format!("Strum parse error: {}", s);
|
||||
GameError::ParseError(errstr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<cpal::DefaultStreamConfigError> for GameError {
|
||||
fn from(s: cpal::DefaultStreamConfigError) -> GameError {
|
||||
let errstr = format!("Default stream config error: {}", s);
|
||||
GameError::AudioError(errstr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<cpal::PlayStreamError> for GameError {
|
||||
fn from(s: cpal::PlayStreamError) -> GameError {
|
||||
let errstr = format!("Play stream error: {}", s);
|
||||
GameError::AudioError(errstr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<cpal::BuildStreamError> for GameError {
|
||||
fn from(s: cpal::BuildStreamError) -> GameError {
|
||||
let errstr = format!("Build stream error: {}", s);
|
||||
GameError::AudioError(errstr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<PoisonError<T>> for GameError {
|
||||
fn from(s: PoisonError<T>) -> GameError {
|
||||
let errstr = format!("Poison error: {}", s);
|
||||
GameError::EventLoopError(errstr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<SendError<T>> for GameError {
|
||||
fn from(s: SendError<T>) -> GameError {
|
||||
let errstr = format!("Send error: {}", s);
|
||||
GameError::EventLoopError(errstr)
|
||||
}
|
||||
}
|
373
src/framework/filesystem.rs
Normal file
373
src/framework/filesystem.rs
Normal file
|
@ -0,0 +1,373 @@
|
|||
use std::env;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::io::SeekFrom;
|
||||
use std::path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use directories::ProjectDirs;
|
||||
use crate::framework::vfs;
|
||||
use crate::framework::vfs::{VFS, OpenOptions};
|
||||
use crate::framework::error::{GameResult, GameError};
|
||||
use crate::framework::context::Context;
|
||||
|
||||
/// A structure that contains the filesystem state and cache.
|
||||
#[derive(Debug)]
|
||||
pub struct Filesystem {
|
||||
vfs: vfs::OverlayFS,
|
||||
user_vfs: vfs::OverlayFS,
|
||||
}
|
||||
|
||||
/// Represents a file, either in the filesystem, or in the resources zip file,
|
||||
/// or whatever.
|
||||
pub enum File {
|
||||
/// A wrapper for a VFile trait object.
|
||||
VfsFile(Box<dyn vfs::VFile>),
|
||||
}
|
||||
|
||||
impl fmt::Debug for File {
|
||||
// Make this more useful?
|
||||
// But we can't seem to get a filename out of a file,
|
||||
// soooooo.
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
File::VfsFile(ref _file) => write!(f, "VfsFile"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Read for File {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match *self {
|
||||
File::VfsFile(ref mut f) => f.read(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Write for File {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
match *self {
|
||||
File::VfsFile(ref mut f) => f.write(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match *self {
|
||||
File::VfsFile(ref mut f) => f.flush(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Seek for File {
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
match *self {
|
||||
File::VfsFile(ref mut f) => f.seek(pos),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Filesystem {
|
||||
pub fn new() -> Filesystem {
|
||||
// Set up VFS to merge resource path, root path, and zip path.
|
||||
let overlay = vfs::OverlayFS::new();
|
||||
// User data VFS.
|
||||
let mut user_overlay = vfs::OverlayFS::new();
|
||||
|
||||
Filesystem {
|
||||
vfs: overlay,
|
||||
user_vfs: user_overlay,
|
||||
}
|
||||
}
|
||||
|
||||
/// Opens the given `path` and returns the resulting `File`
|
||||
/// in read-only mode.
|
||||
pub(crate) fn open<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<File> {
|
||||
self.vfs.open(path.as_ref()).map(|f| File::VfsFile(f))
|
||||
}
|
||||
|
||||
/// Opens the given `path` from user directory and returns the resulting `File`
|
||||
/// in read-only mode.
|
||||
pub(crate) fn user_open<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<File> {
|
||||
self.user_vfs.open(path.as_ref()).map(|f| File::VfsFile(f))
|
||||
}
|
||||
|
||||
/// Opens a file in the user directory with the given
|
||||
/// [`filesystem::OpenOptions`](struct.OpenOptions.html).
|
||||
/// Note that even if you open a file read-write, it can only
|
||||
/// write to files in the "user" directory.
|
||||
pub(crate) fn open_options<P: AsRef<path::Path>>(
|
||||
&mut self,
|
||||
path: P,
|
||||
options: OpenOptions,
|
||||
) -> GameResult<File> {
|
||||
self.user_vfs
|
||||
.open_options(path.as_ref(), options)
|
||||
.map(|f| File::VfsFile(f))
|
||||
.map_err(|e| {
|
||||
GameError::ResourceLoadError(format!(
|
||||
"Tried to open {:?} but got error: {:?}",
|
||||
path.as_ref(),
|
||||
e
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new file in the user directory and opens it
|
||||
/// to be written to, truncating it if it already exists.
|
||||
pub(crate) fn user_create<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<File> {
|
||||
self.user_vfs.create(path.as_ref()).map(|f| File::VfsFile(f))
|
||||
}
|
||||
|
||||
/// Create an empty directory in the user dir
|
||||
/// with the given name. Any parents to that directory
|
||||
/// that do not exist will be created.
|
||||
pub(crate) fn user_create_dir<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<()> {
|
||||
self.user_vfs.mkdir(path.as_ref())
|
||||
}
|
||||
|
||||
/// Deletes the specified file in the user dir.
|
||||
pub(crate) fn user_delete<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<()> {
|
||||
self.user_vfs.rm(path.as_ref())
|
||||
}
|
||||
|
||||
/// Deletes the specified directory in the user dir,
|
||||
/// and all its contents!
|
||||
pub(crate) fn user_delete_dir<P: AsRef<path::Path>>(&mut self, path: P) -> GameResult<()> {
|
||||
self.user_vfs.rmrf(path.as_ref())
|
||||
}
|
||||
|
||||
/// Check whether a file or directory in the user directory exists.
|
||||
pub(crate) fn user_exists<P: AsRef<path::Path>>(&self, path: P) -> bool {
|
||||
self.user_vfs.exists(path.as_ref())
|
||||
}
|
||||
|
||||
/// Check whether a file or directory exists.
|
||||
pub(crate) fn exists<P: AsRef<path::Path>>(&self, path: P) -> bool {
|
||||
self.vfs.exists(path.as_ref())
|
||||
}
|
||||
|
||||
/// Check whether a path points at a file.
|
||||
pub(crate) fn user_is_file<P: AsRef<path::Path>>(&self, path: P) -> bool {
|
||||
self.user_vfs
|
||||
.metadata(path.as_ref())
|
||||
.map(|m| m.is_file())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Check whether a path points at a file.
|
||||
pub(crate) fn is_file<P: AsRef<path::Path>>(&self, path: P) -> bool {
|
||||
self.vfs
|
||||
.metadata(path.as_ref())
|
||||
.map(|m| m.is_file())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Check whether a path points at a directory.
|
||||
pub(crate) fn user_is_dir<P: AsRef<path::Path>>(&self, path: P) -> bool {
|
||||
self.user_vfs
|
||||
.metadata(path.as_ref())
|
||||
.map(|m| m.is_dir())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Check whether a path points at a directory.
|
||||
pub(crate) fn is_dir<P: AsRef<path::Path>>(&self, path: P) -> bool {
|
||||
self.vfs
|
||||
.metadata(path.as_ref())
|
||||
.map(|m| m.is_dir())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Returns a list of all files and directories in the user directory,
|
||||
/// in no particular order.
|
||||
///
|
||||
/// Lists the base directory if an empty path is given.
|
||||
pub(crate) fn user_read_dir<P: AsRef<path::Path>>(
|
||||
&mut self,
|
||||
path: P,
|
||||
) -> GameResult<Box<dyn Iterator<Item=path::PathBuf>>> {
|
||||
let itr = self.user_vfs.read_dir(path.as_ref())?.map(|fname| {
|
||||
fname.expect("Could not read file in read_dir()? Should never happen, I hope!")
|
||||
});
|
||||
Ok(Box::new(itr))
|
||||
}
|
||||
|
||||
/// Returns a list of all files and directories in the resource directory,
|
||||
/// in no particular order.
|
||||
///
|
||||
/// Lists the base directory if an empty path is given.
|
||||
pub(crate) fn read_dir<P: AsRef<path::Path>>(
|
||||
&mut self,
|
||||
path: P,
|
||||
) -> GameResult<Box<dyn Iterator<Item=path::PathBuf>>> {
|
||||
let itr = self.vfs.read_dir(path.as_ref())?.map(|fname| {
|
||||
fname.expect("Could not read file in read_dir()? Should never happen, I hope!")
|
||||
});
|
||||
Ok(Box::new(itr))
|
||||
}
|
||||
|
||||
fn write_to_string(&mut self) -> String {
|
||||
use std::fmt::Write;
|
||||
let mut s = String::new();
|
||||
for vfs in self.vfs.roots() {
|
||||
write!(s, "Source {:?}", vfs).expect("Could not write to string; should never happen?");
|
||||
match vfs.read_dir(path::Path::new("/")) {
|
||||
Ok(files) => {
|
||||
for itm in files {
|
||||
write!(s, " {:?}", itm)
|
||||
.expect("Could not write to string; should never happen?");
|
||||
}
|
||||
}
|
||||
Err(e) => write!(s, " Could not read source: {:?}", e)
|
||||
.expect("Could not write to string; should never happen?"),
|
||||
}
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
/// Adds the given (absolute) path to the list of directories
|
||||
/// it will search to look for resources.
|
||||
///
|
||||
/// You probably shouldn't use this in the general case, since it is
|
||||
/// harder than it looks to make it bulletproof across platforms.
|
||||
/// But it can be very nice for debugging and dev purposes, such as
|
||||
/// by pushing `$CARGO_MANIFEST_DIR/resources` to it
|
||||
pub fn mount(&mut self, path: &path::Path, readonly: bool) {
|
||||
let physfs = vfs::PhysicalFS::new(path, readonly);
|
||||
trace!("Mounting new path: {:?}", physfs);
|
||||
self.vfs.push_back(Box::new(physfs));
|
||||
}
|
||||
|
||||
pub fn mount_vfs(&mut self, vfs: Box<dyn vfs::VFS>) {
|
||||
self.vfs.push_back(vfs);
|
||||
}
|
||||
}
|
||||
|
||||
/// Opens the given path and returns the resulting `File`
|
||||
/// in read-only mode.
|
||||
pub fn open<P: AsRef<path::Path>>(ctx: &mut Context, path: P) -> GameResult<File> {
|
||||
ctx.filesystem.open(path)
|
||||
}
|
||||
|
||||
/// Opens the given path in the user directory and returns the resulting `File`
|
||||
/// in read-only mode.
|
||||
pub fn user_open<P: AsRef<path::Path>>(ctx: &mut Context, path: P) -> GameResult<File> {
|
||||
ctx.filesystem.user_open(path)
|
||||
}
|
||||
|
||||
/// Opens a file in the user directory with the given `filesystem::OpenOptions`.
|
||||
pub fn open_options<P: AsRef<path::Path>>(
|
||||
ctx: &mut Context,
|
||||
path: P,
|
||||
options: OpenOptions,
|
||||
) -> GameResult<File> {
|
||||
ctx.filesystem.open_options(path, options)
|
||||
}
|
||||
|
||||
/// Creates a new file in the user directory and opens it
|
||||
/// to be written to, truncating it if it already exists.
|
||||
pub fn user_create<P: AsRef<path::Path>>(ctx: &mut Context, path: P) -> GameResult<File> {
|
||||
ctx.filesystem.user_create(path)
|
||||
}
|
||||
|
||||
/// Create an empty directory in the user dir
|
||||
/// with the given name. Any parents to that directory
|
||||
/// that do not exist will be created.
|
||||
pub fn user_create_dir<P: AsRef<path::Path>>(ctx: &mut Context, path: P) -> GameResult {
|
||||
ctx.filesystem.user_create_dir(path.as_ref())
|
||||
}
|
||||
|
||||
/// Deletes the specified file in the user dir.
|
||||
pub fn user_delete<P: AsRef<path::Path>>(ctx: &mut Context, path: P) -> GameResult {
|
||||
ctx.filesystem.user_delete(path.as_ref())
|
||||
}
|
||||
|
||||
/// Deletes the specified directory in the user dir,
|
||||
/// and all its contents!
|
||||
pub fn user_delete_dir<P: AsRef<path::Path>>(ctx: &mut Context, path: P) -> GameResult {
|
||||
ctx.filesystem.user_delete_dir(path.as_ref())
|
||||
}
|
||||
|
||||
/// Check whether a file or directory exists.
|
||||
pub fn user_exists<P: AsRef<path::Path>>(ctx: &Context, path: P) -> bool {
|
||||
ctx.filesystem.user_exists(path.as_ref())
|
||||
}
|
||||
|
||||
/// Check whether a path points at a file.
|
||||
pub fn user_is_file<P: AsRef<path::Path>>(ctx: &Context, path: P) -> bool {
|
||||
ctx.filesystem.user_is_file(path)
|
||||
}
|
||||
|
||||
/// Check whether a path points at a directory.
|
||||
pub fn user_is_dir<P: AsRef<path::Path>>(ctx: &Context, path: P) -> bool {
|
||||
ctx.filesystem.user_is_dir(path)
|
||||
}
|
||||
|
||||
/// Returns a list of all files and directories in the user directory,
|
||||
/// in no particular order.
|
||||
///
|
||||
/// Lists the base directory if an empty path is given.
|
||||
pub fn user_read_dir<P: AsRef<path::Path>>(
|
||||
ctx: &mut Context,
|
||||
path: P,
|
||||
) -> GameResult<Box<dyn Iterator<Item=path::PathBuf>>> {
|
||||
ctx.filesystem.user_read_dir(path)
|
||||
}
|
||||
|
||||
/// Check whether a file or directory exists.
|
||||
pub fn exists<P: AsRef<path::Path>>(ctx: &Context, path: P) -> bool {
|
||||
ctx.filesystem.exists(path.as_ref())
|
||||
}
|
||||
|
||||
/// Check whether a path points at a file.
|
||||
pub fn is_file<P: AsRef<path::Path>>(ctx: &Context, path: P) -> bool {
|
||||
ctx.filesystem.is_file(path)
|
||||
}
|
||||
|
||||
/// Check whether a path points at a directory.
|
||||
pub fn is_dir<P: AsRef<path::Path>>(ctx: &Context, path: P) -> bool {
|
||||
ctx.filesystem.is_dir(path)
|
||||
}
|
||||
|
||||
/// Returns a list of all files and directories in the resource directory,
|
||||
/// in no particular order.
|
||||
///
|
||||
/// Lists the base directory if an empty path is given.
|
||||
pub fn read_dir<P: AsRef<path::Path>>(
|
||||
ctx: &mut Context,
|
||||
path: P,
|
||||
) -> GameResult<Box<dyn Iterator<Item=path::PathBuf>>> {
|
||||
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
|
||||
/// it will search to look for resources.
|
||||
///
|
||||
/// You probably shouldn't use this in the general case, since it is
|
||||
/// harder than it looks to make it bulletproof across platforms.
|
||||
/// But it can be very nice for debugging and dev purposes, such as
|
||||
/// by pushing `$CARGO_MANIFEST_DIR/resources` to it
|
||||
pub fn mount(ctx: &mut Context, path: &path::Path, readonly: bool) {
|
||||
ctx.filesystem.mount(path, readonly)
|
||||
}
|
||||
|
||||
/// Adds a VFS to the list of resource search locations.
|
||||
pub fn mount_vfs(ctx: &mut Context, vfs: Box<dyn vfs::VFS>) {
|
||||
ctx.filesystem.mount_vfs(vfs)
|
||||
}
|
24
src/framework/graphics.rs
Normal file
24
src/framework/graphics.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use crate::common::Color;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
pub enum FilterMode {
|
||||
Nearest,
|
||||
Linear,
|
||||
}
|
||||
|
||||
pub struct Canvas {}
|
||||
|
||||
impl Canvas {
|
||||
|
||||
}
|
||||
|
||||
pub fn clear(ctx: &mut Context, color: Color) {}
|
||||
|
||||
pub fn present(ctx: &mut Context) -> GameResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn drawable_size(ctx: &mut Context) -> (f32, f32) {
|
||||
(320.0, 240.0)
|
||||
}
|
15
src/framework/image.rs
Normal file
15
src/framework/image.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
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 {})
|
||||
}
|
||||
}
|
205
src/framework/keyboard.rs
Normal file
205
src/framework/keyboard.rs
Normal file
|
@ -0,0 +1,205 @@
|
|||
use crate::framework::context::Context;
|
||||
|
||||
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
|
||||
#[repr(u32)]
|
||||
pub enum ScanCode {
|
||||
/// The '1' key over the letters.
|
||||
Key1,
|
||||
/// The '2' key over the letters.
|
||||
Key2,
|
||||
/// The '3' key over the letters.
|
||||
Key3,
|
||||
/// The '4' key over the letters.
|
||||
Key4,
|
||||
/// The '5' key over the letters.
|
||||
Key5,
|
||||
/// The '6' key over the letters.
|
||||
Key6,
|
||||
/// The '7' key over the letters.
|
||||
Key7,
|
||||
/// The '8' key over the letters.
|
||||
Key8,
|
||||
/// The '9' key over the letters.
|
||||
Key9,
|
||||
/// The '0' key over the 'O' and 'P' keys.
|
||||
Key0,
|
||||
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
E,
|
||||
F,
|
||||
G,
|
||||
H,
|
||||
I,
|
||||
J,
|
||||
K,
|
||||
L,
|
||||
M,
|
||||
N,
|
||||
O,
|
||||
P,
|
||||
Q,
|
||||
R,
|
||||
S,
|
||||
T,
|
||||
U,
|
||||
V,
|
||||
W,
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
|
||||
/// The Escape key, next to F1.
|
||||
Escape,
|
||||
|
||||
F1,
|
||||
F2,
|
||||
F3,
|
||||
F4,
|
||||
F5,
|
||||
F6,
|
||||
F7,
|
||||
F8,
|
||||
F9,
|
||||
F10,
|
||||
F11,
|
||||
F12,
|
||||
F13,
|
||||
F14,
|
||||
F15,
|
||||
F16,
|
||||
F17,
|
||||
F18,
|
||||
F19,
|
||||
F20,
|
||||
F21,
|
||||
F22,
|
||||
F23,
|
||||
F24,
|
||||
|
||||
/// Print Screen/SysRq.
|
||||
Snapshot,
|
||||
/// Scroll Lock.
|
||||
Scroll,
|
||||
/// Pause/Break key, next to Scroll lock.
|
||||
Pause,
|
||||
|
||||
/// `Insert`, next to Backspace.
|
||||
Insert,
|
||||
Home,
|
||||
Delete,
|
||||
End,
|
||||
PageDown,
|
||||
PageUp,
|
||||
|
||||
Left,
|
||||
Up,
|
||||
Right,
|
||||
Down,
|
||||
|
||||
/// The Backspace key, right over Enter.
|
||||
Backspace,
|
||||
/// The Enter key.
|
||||
Return,
|
||||
/// The space bar.
|
||||
Space,
|
||||
|
||||
/// The "Compose" key on Linux.
|
||||
Compose,
|
||||
|
||||
Caret,
|
||||
|
||||
Numlock,
|
||||
Numpad0,
|
||||
Numpad1,
|
||||
Numpad2,
|
||||
Numpad3,
|
||||
Numpad4,
|
||||
Numpad5,
|
||||
Numpad6,
|
||||
Numpad7,
|
||||
Numpad8,
|
||||
Numpad9,
|
||||
NumpadAdd,
|
||||
NumpadDivide,
|
||||
NumpadDecimal,
|
||||
NumpadComma,
|
||||
NumpadEnter,
|
||||
NumpadEquals,
|
||||
NumpadMultiply,
|
||||
NumpadSubtract,
|
||||
|
||||
AbntC1,
|
||||
AbntC2,
|
||||
Apostrophe,
|
||||
Apps,
|
||||
Asterisk,
|
||||
At,
|
||||
Ax,
|
||||
Backslash,
|
||||
Calculator,
|
||||
Capital,
|
||||
Colon,
|
||||
Comma,
|
||||
Convert,
|
||||
Equals,
|
||||
Grave,
|
||||
Kana,
|
||||
Kanji,
|
||||
LAlt,
|
||||
LBracket,
|
||||
LControl,
|
||||
LShift,
|
||||
LWin,
|
||||
Mail,
|
||||
MediaSelect,
|
||||
MediaStop,
|
||||
Minus,
|
||||
Mute,
|
||||
MyComputer,
|
||||
// also called "Next"
|
||||
NavigateForward,
|
||||
// also called "Prior"
|
||||
NavigateBackward,
|
||||
NextTrack,
|
||||
NoConvert,
|
||||
OEM102,
|
||||
Period,
|
||||
PlayPause,
|
||||
Plus,
|
||||
Power,
|
||||
PrevTrack,
|
||||
RAlt,
|
||||
RBracket,
|
||||
RControl,
|
||||
RShift,
|
||||
RWin,
|
||||
Semicolon,
|
||||
Slash,
|
||||
Sleep,
|
||||
Stop,
|
||||
Sysrq,
|
||||
Tab,
|
||||
Underline,
|
||||
Unlabeled,
|
||||
VolumeDown,
|
||||
VolumeUp,
|
||||
Wake,
|
||||
WebBack,
|
||||
WebFavorites,
|
||||
WebForward,
|
||||
WebHome,
|
||||
WebRefresh,
|
||||
WebSearch,
|
||||
WebStop,
|
||||
Yen,
|
||||
Copy,
|
||||
Paste,
|
||||
Cut,
|
||||
}
|
||||
|
||||
pub fn is_key_pressed(ctx: &mut Context, code: ScanCode) -> bool {
|
||||
false
|
||||
}
|
10
src/framework/mod.rs
Normal file
10
src/framework/mod.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
pub mod backend;
|
||||
pub mod backend_sdl2;
|
||||
pub mod context;
|
||||
pub mod error;
|
||||
pub mod filesystem;
|
||||
pub mod vfs;
|
||||
pub mod image;
|
||||
pub mod graphics;
|
||||
pub mod keyboard;
|
||||
pub mod backend_null;
|
678
src/framework/vfs.rs
Normal file
678
src/framework/vfs.rs
Normal file
|
@ -0,0 +1,678 @@
|
|||
//! A virtual file system layer that lets us define multiple
|
||||
//! "file systems" with various backing stores, then merge them
|
||||
//! together.
|
||||
//!
|
||||
//! Basically a re-implementation of the C library `PhysFS`. The
|
||||
//! `vfs` crate does something similar but has a couple design
|
||||
//! decisions that make it kind of incompatible with this use case:
|
||||
//! the relevant trait for it has generic methods so we can't use it
|
||||
//! as a trait object, and its path abstraction is not the most
|
||||
//! convenient.
|
||||
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt::{self, Debug};
|
||||
use std::fs;
|
||||
use std::io::{Read, Seek, Write, BufRead};
|
||||
use std::path::{self, Path, PathBuf};
|
||||
use crate::framework::error::{GameResult, GameError};
|
||||
|
||||
fn convenient_path_to_str(path: &path::Path) -> GameResult<&str> {
|
||||
path.to_str().ok_or_else(|| {
|
||||
let errmessage = format!("Invalid path format for resource: {:?}", path);
|
||||
GameError::FilesystemError(errmessage)
|
||||
})
|
||||
}
|
||||
|
||||
/// Virtual file
|
||||
pub trait VFile: Read + Write + Seek + Debug {}
|
||||
|
||||
impl<T> VFile for T where T: Read + Write + Seek + Debug {}
|
||||
|
||||
/// Options for opening files
|
||||
///
|
||||
/// We need our own version of this structure because the one in
|
||||
/// `std` annoyingly doesn't let you read the read/write/create/etc
|
||||
/// state out of it.
|
||||
#[must_use]
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Default, Copy, Clone, PartialEq)]
|
||||
pub struct OpenOptions {
|
||||
pub read: bool,
|
||||
pub write: bool,
|
||||
pub create: bool,
|
||||
pub append: bool,
|
||||
pub truncate: bool,
|
||||
}
|
||||
|
||||
impl OpenOptions {
|
||||
/// Create a new instance
|
||||
pub fn new() -> OpenOptions {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Open for reading
|
||||
pub fn read(mut self, read: bool) -> OpenOptions {
|
||||
self.read = read;
|
||||
self
|
||||
}
|
||||
|
||||
/// Open for writing
|
||||
pub fn write(mut self, write: bool) -> OpenOptions {
|
||||
self.write = write;
|
||||
self
|
||||
}
|
||||
|
||||
/// Create the file if it does not exist yet
|
||||
pub fn create(mut self, create: bool) -> OpenOptions {
|
||||
self.create = create;
|
||||
self
|
||||
}
|
||||
|
||||
/// Append at the end of the file
|
||||
pub fn append(mut self, append: bool) -> OpenOptions {
|
||||
self.append = append;
|
||||
self
|
||||
}
|
||||
|
||||
/// Truncate the file to 0 bytes after opening
|
||||
pub fn truncate(mut self, truncate: bool) -> OpenOptions {
|
||||
self.truncate = truncate;
|
||||
self
|
||||
}
|
||||
|
||||
fn to_fs_openoptions(self) -> fs::OpenOptions {
|
||||
let mut opt = fs::OpenOptions::new();
|
||||
let _ = opt
|
||||
.read(self.read)
|
||||
.write(self.write)
|
||||
.create(self.create)
|
||||
.append(self.append)
|
||||
.truncate(self.truncate)
|
||||
.create(self.create);
|
||||
opt
|
||||
}
|
||||
}
|
||||
|
||||
/// Virtual filesystem
|
||||
pub trait VFS: Debug {
|
||||
/// Open the file at this path with the given options
|
||||
fn open_options(&self, path: &Path, open_options: OpenOptions) -> GameResult<Box<dyn VFile>>;
|
||||
/// Open the file at this path for reading
|
||||
fn open(&self, path: &Path) -> GameResult<Box<dyn VFile>> {
|
||||
self.open_options(path, OpenOptions::new().read(true))
|
||||
}
|
||||
/// Open the file at this path for writing, truncating it if it exists already
|
||||
fn create(&self, path: &Path) -> GameResult<Box<dyn VFile>> {
|
||||
self.open_options(
|
||||
path,
|
||||
OpenOptions::new().write(true).create(true).truncate(true),
|
||||
)
|
||||
}
|
||||
/// Open the file at this path for appending, creating it if necessary
|
||||
fn append(&self, path: &Path) -> GameResult<Box<dyn VFile>> {
|
||||
self.open_options(
|
||||
path,
|
||||
OpenOptions::new().write(true).create(true).append(true),
|
||||
)
|
||||
}
|
||||
/// Create a directory at the location by this path
|
||||
fn mkdir(&self, path: &Path) -> GameResult;
|
||||
|
||||
/// Remove a file or an empty directory.
|
||||
fn rm(&self, path: &Path) -> GameResult;
|
||||
|
||||
/// Remove a file or directory and all its contents
|
||||
fn rmrf(&self, path: &Path) -> GameResult;
|
||||
|
||||
/// Check if the file exists
|
||||
fn exists(&self, path: &Path) -> bool;
|
||||
|
||||
/// Get the file's metadata
|
||||
fn metadata(&self, path: &Path) -> GameResult<Box<dyn VMetadata>>;
|
||||
|
||||
/// Retrieve all file and directory entries in the given directory.
|
||||
fn read_dir(&self, path: &Path) -> GameResult<Box<dyn Iterator<Item=GameResult<PathBuf>>>>;
|
||||
|
||||
/// Retrieve the actual location of the VFS root, if available.
|
||||
fn to_path_buf(&self) -> Option<PathBuf>;
|
||||
}
|
||||
|
||||
/// VFS metadata
|
||||
pub trait VMetadata {
|
||||
/// Returns whether or not it is a directory.
|
||||
/// Note that zip files don't actually have directories, awkwardly,
|
||||
/// just files with very long names.
|
||||
fn is_dir(&self) -> bool;
|
||||
/// Returns whether or not it is a file.
|
||||
fn is_file(&self) -> bool;
|
||||
/// Returns the length of the thing. If it is a directory,
|
||||
/// the result of this is undefined/platform dependent.
|
||||
fn len(&self) -> u64;
|
||||
}
|
||||
|
||||
/// A VFS that points to a directory and uses it as the root of its
|
||||
/// file hierarchy.
|
||||
///
|
||||
/// It IS allowed to have symlinks in it! They're surprisingly
|
||||
/// difficult to get rid of.
|
||||
#[derive(Clone)]
|
||||
pub struct PhysicalFS {
|
||||
root: PathBuf,
|
||||
readonly: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Physical FS metadata
|
||||
pub struct PhysicalMetadata(fs::Metadata);
|
||||
|
||||
impl VMetadata for PhysicalMetadata {
|
||||
fn is_dir(&self) -> bool {
|
||||
self.0.is_dir()
|
||||
}
|
||||
fn is_file(&self) -> bool {
|
||||
self.0.is_file()
|
||||
}
|
||||
fn len(&self) -> u64 {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
/// This takes an absolute path and returns either a sanitized relative
|
||||
/// version of it, or None if there's something bad in it.
|
||||
///
|
||||
/// What we want is an absolute path with no `..`'s in it, so, something
|
||||
/// like "/foo" or "/foo/bar.txt". This means a path with components
|
||||
/// starting with a `RootDir`, and zero or more `Normal` components.
|
||||
///
|
||||
/// We gotta return a new path because there's apparently no real good way
|
||||
/// to turn an absolute path into a relative path with the same
|
||||
/// components (other than the first), and pushing an absolute `Path`
|
||||
/// onto a `PathBuf` just completely nukes its existing contents.
|
||||
fn sanitize_path(path: &path::Path) -> Option<PathBuf> {
|
||||
let mut c = path.components();
|
||||
match c.next() {
|
||||
Some(path::Component::RootDir) => (),
|
||||
_ => return None,
|
||||
}
|
||||
|
||||
fn is_normal_component(comp: path::Component) -> Option<&str> {
|
||||
match comp {
|
||||
path::Component::Normal(s) => s.to_str(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// This could be done more cleverly but meh
|
||||
let mut accm = PathBuf::new();
|
||||
for component in c {
|
||||
if let Some(s) = is_normal_component(component) {
|
||||
accm.push(s)
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(accm)
|
||||
}
|
||||
|
||||
impl PhysicalFS {
|
||||
/// Creates a new PhysicalFS
|
||||
pub fn new(root: &Path, readonly: bool) -> Self {
|
||||
PhysicalFS {
|
||||
root: root.into(),
|
||||
readonly,
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes a given path (&str) and returns
|
||||
/// a new PathBuf containing the canonical
|
||||
/// absolute path you get when appending it
|
||||
/// to this filesystem's root.
|
||||
fn to_absolute(&self, p: &Path) -> GameResult<PathBuf> {
|
||||
if let Some(safe_path) = sanitize_path(p) {
|
||||
let mut root_path = self.root.clone();
|
||||
root_path.push(safe_path);
|
||||
Ok(root_path)
|
||||
} else {
|
||||
let msg = format!(
|
||||
"Path {:?} is not valid: must be an absolute path with no \
|
||||
references to parent directories",
|
||||
p
|
||||
);
|
||||
Err(GameError::FilesystemError(msg))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the PhysicalFS's root directory if necessary.
|
||||
/// Idempotent.
|
||||
/// This way we can not create the directory until it's
|
||||
/// actually used, though it IS a tiny bit of a performance
|
||||
/// malus.
|
||||
fn create_root(&self) -> GameResult {
|
||||
if !self.root.exists() {
|
||||
fs::create_dir_all(&self.root).map_err(GameError::from)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PhysicalFS {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "<PhysicalFS root: {}>", self.root.display())
|
||||
}
|
||||
}
|
||||
|
||||
impl VFS for PhysicalFS {
|
||||
/// Open the file at this path with the given options
|
||||
fn open_options(&self, path: &Path, open_options: OpenOptions) -> GameResult<Box<dyn VFile>> {
|
||||
if self.readonly
|
||||
&& (open_options.write
|
||||
|| open_options.create
|
||||
|| open_options.append
|
||||
|| open_options.truncate)
|
||||
{
|
||||
let msg = format!(
|
||||
"Cannot alter file {:?} in root {:?}, filesystem read-only",
|
||||
path, self
|
||||
);
|
||||
return Err(GameError::FilesystemError(msg));
|
||||
}
|
||||
self.create_root()?;
|
||||
let p = self.to_absolute(path)?;
|
||||
open_options
|
||||
.to_fs_openoptions()
|
||||
.open(p)
|
||||
.map(|x| Box::new(x) as Box<dyn VFile>)
|
||||
.map_err(GameError::from)
|
||||
}
|
||||
|
||||
/// Create a directory at the location by this path
|
||||
fn mkdir(&self, path: &Path) -> GameResult {
|
||||
if self.readonly {
|
||||
return Err(GameError::FilesystemError(
|
||||
"Tried to make directory {} but FS is \
|
||||
read-only"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
self.create_root()?;
|
||||
let p = self.to_absolute(path)?;
|
||||
//println!("Creating {:?}", p);
|
||||
fs::DirBuilder::new()
|
||||
.recursive(true)
|
||||
.create(p)
|
||||
.map_err(GameError::from)
|
||||
}
|
||||
|
||||
/// Remove a file
|
||||
fn rm(&self, path: &Path) -> GameResult {
|
||||
if self.readonly {
|
||||
return Err(GameError::FilesystemError(
|
||||
"Tried to remove file {} but FS is read-only".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
self.create_root()?;
|
||||
let p = self.to_absolute(path)?;
|
||||
if p.is_dir() {
|
||||
fs::remove_dir(p).map_err(GameError::from)
|
||||
} else {
|
||||
fs::remove_file(p).map_err(GameError::from)
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a file or directory and all its contents
|
||||
fn rmrf(&self, path: &Path) -> GameResult {
|
||||
if self.readonly {
|
||||
return Err(GameError::FilesystemError(
|
||||
"Tried to remove file/dir {} but FS is \
|
||||
read-only"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
self.create_root()?;
|
||||
let p = self.to_absolute(path)?;
|
||||
if p.is_dir() {
|
||||
fs::remove_dir_all(p).map_err(GameError::from)
|
||||
} else {
|
||||
fs::remove_file(p).map_err(GameError::from)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the file exists
|
||||
fn exists(&self, path: &Path) -> bool {
|
||||
match self.to_absolute(path) {
|
||||
Ok(p) => p.exists(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the file's metadata
|
||||
fn metadata(&self, path: &Path) -> GameResult<Box<dyn VMetadata>> {
|
||||
self.create_root()?;
|
||||
let p = self.to_absolute(path)?;
|
||||
p.metadata()
|
||||
.map(|m| Box::new(PhysicalMetadata(m)) as Box<dyn VMetadata>)
|
||||
.map_err(GameError::from)
|
||||
}
|
||||
|
||||
/// Retrieve the path entries in this path
|
||||
fn read_dir(&self, path: &Path) -> GameResult<Box<dyn Iterator<Item=GameResult<PathBuf>>>> {
|
||||
self.create_root()?;
|
||||
let p = self.to_absolute(path)?;
|
||||
// This is inconvenient because path() returns the full absolute
|
||||
// path of the bloody file, which is NOT what we want!
|
||||
// But if we use file_name() to just get the name then it is ALSO not what we want!
|
||||
// what we WANT is the full absolute file path, *relative to the resources dir*.
|
||||
// So that we can do read_dir("/foobar/"), and for each file, open it and query
|
||||
// it and such by name.
|
||||
// So we build the paths ourself.
|
||||
let direntry_to_path = |entry: &fs::DirEntry| -> GameResult<PathBuf> {
|
||||
let fname = entry
|
||||
.file_name()
|
||||
.into_string()
|
||||
.expect("Non-unicode char in file path? Should never happen, I hope!");
|
||||
let mut pathbuf = PathBuf::from(path);
|
||||
pathbuf.push(fname);
|
||||
Ok(pathbuf)
|
||||
};
|
||||
let itr = fs::read_dir(p)?
|
||||
.map(|entry| direntry_to_path(&entry?))
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter();
|
||||
Ok(Box::new(itr))
|
||||
}
|
||||
|
||||
/// Retrieve the actual location of the VFS root, if available.
|
||||
fn to_path_buf(&self) -> Option<PathBuf> {
|
||||
Some(self.root.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure that joins several VFS's together in order.
|
||||
#[derive(Debug)]
|
||||
pub struct OverlayFS {
|
||||
roots: VecDeque<Box<dyn VFS>>,
|
||||
}
|
||||
|
||||
impl OverlayFS {
|
||||
/// Creates a new OverlayFS
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
roots: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a new VFS to the front of the list.
|
||||
/// Currently unused, I suppose, but good to
|
||||
/// have at least for tests.
|
||||
#[allow(dead_code)]
|
||||
pub fn push_front(&mut self, fs: Box<dyn VFS>) {
|
||||
self.roots.push_front(fs);
|
||||
}
|
||||
|
||||
/// Adds a new VFS to the end of the list.
|
||||
pub fn push_back(&mut self, fs: Box<dyn VFS>) {
|
||||
self.roots.push_back(fs);
|
||||
}
|
||||
|
||||
/// Returns a list of registered VFS roots.
|
||||
pub fn roots(&self) -> &VecDeque<Box<dyn VFS>> {
|
||||
&self.roots
|
||||
}
|
||||
}
|
||||
|
||||
impl VFS for OverlayFS {
|
||||
/// Open the file at this path with the given options
|
||||
fn open_options(&self, path: &Path, open_options: OpenOptions) -> GameResult<Box<dyn VFile>> {
|
||||
let mut tried: Vec<(PathBuf, GameError)> = vec![];
|
||||
|
||||
for vfs in &self.roots {
|
||||
match vfs.open_options(path, open_options) {
|
||||
Err(e) => {
|
||||
if let Some(vfs_path) = vfs.to_path_buf() {
|
||||
tried.push((vfs_path, e));
|
||||
} else {
|
||||
tried.push((PathBuf::from("<invalid path>"), e));
|
||||
}
|
||||
}
|
||||
f => return f,
|
||||
}
|
||||
}
|
||||
let errmessage = String::from(convenient_path_to_str(path)?);
|
||||
Err(GameError::ResourceNotFound(errmessage, tried))
|
||||
}
|
||||
|
||||
/// Create a directory at the location by this path
|
||||
fn mkdir(&self, path: &Path) -> GameResult {
|
||||
for vfs in &self.roots {
|
||||
match vfs.mkdir(path) {
|
||||
Err(_) => (),
|
||||
f => return f,
|
||||
}
|
||||
}
|
||||
Err(GameError::FilesystemError(format!(
|
||||
"Could not find anywhere writeable to make dir {:?}",
|
||||
path
|
||||
)))
|
||||
}
|
||||
|
||||
/// Remove a file
|
||||
fn rm(&self, path: &Path) -> GameResult {
|
||||
for vfs in &self.roots {
|
||||
match vfs.rm(path) {
|
||||
Err(_) => (),
|
||||
f => return f,
|
||||
}
|
||||
}
|
||||
Err(GameError::FilesystemError(format!(
|
||||
"Could not remove file {:?}",
|
||||
path
|
||||
)))
|
||||
}
|
||||
|
||||
/// Remove a file or directory and all its contents
|
||||
fn rmrf(&self, path: &Path) -> GameResult {
|
||||
for vfs in &self.roots {
|
||||
match vfs.rmrf(path) {
|
||||
Err(_) => (),
|
||||
f => return f,
|
||||
}
|
||||
}
|
||||
Err(GameError::FilesystemError(format!(
|
||||
"Could not remove file/dir {:?}",
|
||||
path
|
||||
)))
|
||||
}
|
||||
|
||||
/// Check if the file exists
|
||||
fn exists(&self, path: &Path) -> bool {
|
||||
for vfs in &self.roots {
|
||||
if vfs.exists(path) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Get the file's metadata
|
||||
fn metadata(&self, path: &Path) -> GameResult<Box<dyn VMetadata>> {
|
||||
for vfs in &self.roots {
|
||||
match vfs.metadata(path) {
|
||||
Err(_) => (),
|
||||
f => return f,
|
||||
}
|
||||
}
|
||||
Err(GameError::FilesystemError(format!(
|
||||
"Could not get metadata for file/dir {:?}",
|
||||
path
|
||||
)))
|
||||
}
|
||||
|
||||
/// Retrieve the path entries in this path
|
||||
fn read_dir(&self, path: &Path) -> GameResult<Box<dyn Iterator<Item=GameResult<PathBuf>>>> {
|
||||
// This is tricky 'cause we have to actually merge iterators together...
|
||||
// Doing it the simple and stupid way works though.
|
||||
let mut v = Vec::new();
|
||||
for fs in &self.roots {
|
||||
if let Ok(rddir) = fs.read_dir(path) {
|
||||
v.extend(rddir)
|
||||
}
|
||||
}
|
||||
Ok(Box::new(v.into_iter()))
|
||||
}
|
||||
|
||||
/// Retrieve the actual location of the VFS root, if available.
|
||||
fn to_path_buf(&self) -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::{self, BufRead};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn headless_test_path_filtering() {
|
||||
// Valid pahts
|
||||
let p = path::Path::new("/foo");
|
||||
assert!(sanitize_path(p).is_some());
|
||||
|
||||
let p = path::Path::new("/foo/");
|
||||
assert!(sanitize_path(p).is_some());
|
||||
|
||||
let p = path::Path::new("/foo/bar.txt");
|
||||
assert!(sanitize_path(p).is_some());
|
||||
|
||||
let p = path::Path::new("/");
|
||||
assert!(sanitize_path(p).is_some());
|
||||
|
||||
// Invalid paths
|
||||
let p = path::Path::new("../foo");
|
||||
assert!(sanitize_path(p).is_none());
|
||||
|
||||
let p = path::Path::new("foo");
|
||||
assert!(sanitize_path(p).is_none());
|
||||
|
||||
let p = path::Path::new("/foo/../../");
|
||||
assert!(sanitize_path(p).is_none());
|
||||
|
||||
let p = path::Path::new("/foo/../bop");
|
||||
assert!(sanitize_path(p).is_none());
|
||||
|
||||
let p = path::Path::new("/../bar");
|
||||
assert!(sanitize_path(p).is_none());
|
||||
|
||||
let p = path::Path::new("");
|
||||
assert!(sanitize_path(p).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn headless_test_read() {
|
||||
let cargo_path = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
let fs = PhysicalFS::new(cargo_path, true);
|
||||
let f = fs.open(Path::new("/Cargo.toml")).unwrap();
|
||||
let mut bf = io::BufReader::new(f);
|
||||
let mut s = String::new();
|
||||
let _ = bf.read_line(&mut s).unwrap();
|
||||
// Trim whitespace from string 'cause it will
|
||||
// potentially be different on Windows and Unix.
|
||||
let trimmed_string = s.trim();
|
||||
assert_eq!(trimmed_string, "[package]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn headless_test_read_overlay() {
|
||||
let cargo_path = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
let fs1 = PhysicalFS::new(cargo_path, true);
|
||||
let mut f2path = PathBuf::from(cargo_path);
|
||||
f2path.push("src");
|
||||
let fs2 = PhysicalFS::new(&f2path, true);
|
||||
let mut ofs = OverlayFS::new();
|
||||
ofs.push_back(Box::new(fs1));
|
||||
ofs.push_back(Box::new(fs2));
|
||||
|
||||
assert!(ofs.exists(Path::new("/Cargo.toml")));
|
||||
assert!(ofs.exists(Path::new("/lib.rs")));
|
||||
assert!(!ofs.exists(Path::new("/foobaz.rs")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn headless_test_physical_all() {
|
||||
let cargo_path = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
let fs = PhysicalFS::new(cargo_path, false);
|
||||
let testdir = Path::new("/testdir");
|
||||
let f1 = Path::new("/testdir/file1.txt");
|
||||
|
||||
// Delete testdir if it is still lying around
|
||||
if fs.exists(testdir) {
|
||||
fs.rmrf(testdir).unwrap();
|
||||
}
|
||||
assert!(!fs.exists(testdir));
|
||||
|
||||
// Create and delete test dir
|
||||
fs.mkdir(testdir).unwrap();
|
||||
assert!(fs.exists(testdir));
|
||||
fs.rm(testdir).unwrap();
|
||||
assert!(!fs.exists(testdir));
|
||||
|
||||
let test_string = "Foo!";
|
||||
fs.mkdir(testdir).unwrap();
|
||||
{
|
||||
let mut f = fs.append(f1).unwrap();
|
||||
let _ = f.write(test_string.as_bytes()).unwrap();
|
||||
}
|
||||
{
|
||||
let mut buf = Vec::new();
|
||||
let mut f = fs.open(f1).unwrap();
|
||||
let _ = f.read_to_end(&mut buf).unwrap();
|
||||
assert_eq!(&buf[..], test_string.as_bytes());
|
||||
}
|
||||
|
||||
{
|
||||
// Test metadata()
|
||||
let m = fs.metadata(f1).unwrap();
|
||||
assert!(m.is_file());
|
||||
assert!(!m.is_dir());
|
||||
assert_eq!(m.len(), 4);
|
||||
|
||||
let m = fs.metadata(testdir).unwrap();
|
||||
assert!(!m.is_file());
|
||||
assert!(m.is_dir());
|
||||
// Not exactly sure what the "length" of a directory is, buuuuuut...
|
||||
// It appears to vary based on the platform in fact.
|
||||
// On my desktop, it's 18.
|
||||
// On Travis's VM, it's 4096.
|
||||
// On Appveyor's VM, it's 0.
|
||||
// So, it's meaningless.
|
||||
//assert_eq!(m.len(), 18);
|
||||
}
|
||||
|
||||
{
|
||||
// Test read_dir()
|
||||
let r = fs.read_dir(testdir).unwrap();
|
||||
assert_eq!(r.count(), 1);
|
||||
let r = fs.read_dir(testdir).unwrap();
|
||||
for f in r {
|
||||
let fname = f.unwrap();
|
||||
assert!(fs.exists(&fname));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
assert!(fs.exists(f1));
|
||||
fs.rm(f1).unwrap();
|
||||
assert!(!fs.exists(f1));
|
||||
}
|
||||
|
||||
fs.rmrf(testdir).unwrap();
|
||||
assert!(!fs.exists(testdir));
|
||||
}
|
||||
|
||||
// BUGGO: TODO: Make sure all functions are tested for OverlayFS and ZipFS!!
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::input::player_controller::PlayerController;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
use ggez::{GameResult, Context};
|
||||
|
||||
pub struct CombinedMenuController {
|
||||
controllers: Vec<Box<dyn PlayerController>>,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use ggez::{Context, GameResult};
|
||||
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::input::player_controller::PlayerController;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use ggez::{Context, GameResult};
|
||||
use ggez::input::keyboard;
|
||||
use winit::event::VirtualKeyCode;
|
||||
|
||||
use crate::bitfield;
|
||||
use crate::input::player_controller::PlayerController;
|
||||
use crate::player::TargetPlayer;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::keyboard;
|
||||
use crate::framework::keyboard::ScanCode;
|
||||
|
||||
bitfield! {
|
||||
#[derive(Clone, Copy)]
|
||||
|
@ -64,8 +64,8 @@ impl PlayerController for KeyboardController {
|
|||
self.state.set_skip(keyboard::is_key_pressed(ctx, keymap.skip));
|
||||
self.state.set_prev_weapon(keyboard::is_key_pressed(ctx, keymap.prev_weapon));
|
||||
self.state.set_next_weapon(keyboard::is_key_pressed(ctx, keymap.next_weapon));
|
||||
self.state.set_enter(keyboard::is_key_pressed(ctx, VirtualKeyCode::Return));
|
||||
self.state.set_escape(keyboard::is_key_pressed(ctx, VirtualKeyCode::Escape));
|
||||
self.state.set_enter(keyboard::is_key_pressed(ctx, ScanCode::Return));
|
||||
self.state.set_escape(keyboard::is_key_pressed(ctx, ScanCode::Escape));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::{Context, GameResult};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use ggez::{Context, GameResult};
|
||||
use winit::event::TouchPhase;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::common::Rect;
|
||||
use crate::engine_constants::EngineConstants;
|
||||
|
@ -39,6 +39,7 @@ impl TouchControls {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn process_winit_event(&mut self, scale: f32, touch: winit::event::Touch) {
|
||||
match touch.phase {
|
||||
TouchPhase::Started | TouchPhase::Moved => {
|
||||
|
@ -66,7 +67,7 @@ impl TouchControls {
|
|||
self.clicks.retain(|p| p.id != touch.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
pub fn point_in(&self, bounds: Rect) -> Option<u64> {
|
||||
for point in self.points.iter() {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use ggez::{Context, GameResult};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
|
||||
use crate::bitfield;
|
||||
use crate::common::Rect;
|
||||
|
|
266
src/lib.rs
266
src/lib.rs
|
@ -7,27 +7,17 @@ extern crate strum;
|
|||
#[macro_use]
|
||||
extern crate strum_macros;
|
||||
|
||||
use std::{env, mem};
|
||||
use std::env;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::path;
|
||||
use std::time::Instant;
|
||||
|
||||
use ggez::{Context, ContextBuilder, GameError, GameResult};
|
||||
use ggez::conf::{Backend, WindowMode, WindowSetup};
|
||||
use ggez::event::{KeyCode, KeyMods};
|
||||
use ggez::filesystem::mount_vfs;
|
||||
use ggez::graphics;
|
||||
use ggez::graphics::{Canvas, DrawParam, window};
|
||||
use ggez::graphics::glutin_ext::WindowUpdateExt;
|
||||
use ggez::input::keyboard;
|
||||
use ggez::mint::ColumnMatrix4;
|
||||
use ggez::nalgebra::Vector2;
|
||||
use log::*;
|
||||
use pretty_env_logger::env_logger::Env;
|
||||
use winit::event::{ElementState, Event, KeyboardInput, WindowEvent};
|
||||
use winit::event_loop::ControlFlow;
|
||||
|
||||
use crate::builtin_fs::BuiltinFS;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::{GameError, GameResult};
|
||||
use crate::framework::graphics;
|
||||
use crate::framework::keyboard::ScanCode;
|
||||
use crate::scene::loading_scene::LoadingScene;
|
||||
use crate::scene::Scene;
|
||||
use crate::shared_game_state::{SharedGameState, TimingMode};
|
||||
|
@ -46,6 +36,7 @@ mod encoding;
|
|||
mod engine_constants;
|
||||
mod entity;
|
||||
mod frame;
|
||||
mod framework;
|
||||
mod inventory;
|
||||
mod input;
|
||||
mod live_debugger;
|
||||
|
@ -61,6 +52,7 @@ mod scene;
|
|||
#[cfg(feature = "scripting")]
|
||||
mod scripting;
|
||||
mod settings;
|
||||
#[cfg(feature = "backend-gfx")]
|
||||
mod shaders;
|
||||
mod shared_game_state;
|
||||
mod stage;
|
||||
|
@ -74,7 +66,6 @@ struct Game {
|
|||
scene: Option<Box<dyn Scene>>,
|
||||
state: UnsafeCell<SharedGameState>,
|
||||
ui: UI,
|
||||
def_matrix: ColumnMatrix4<f32>,
|
||||
start_time: Instant,
|
||||
last_tick: u128,
|
||||
next_tick: u128,
|
||||
|
@ -86,7 +77,6 @@ impl Game {
|
|||
let s = Game {
|
||||
scene: None,
|
||||
ui: UI::new(ctx)?,
|
||||
def_matrix: DrawParam::new().to_matrix(),
|
||||
state: UnsafeCell::new(SharedGameState::new(ctx)?),
|
||||
start_time: Instant::now(),
|
||||
last_tick: 0,
|
||||
|
@ -161,10 +151,9 @@ impl Game {
|
|||
self.loops = 0;
|
||||
|
||||
graphics::clear(ctx, [0.0, 0.0, 0.0, 1.0].into());
|
||||
graphics::set_transform(ctx, DrawParam::new()
|
||||
.scale(Vector2::new(state_ref.scale, state_ref.scale))
|
||||
.to_matrix());
|
||||
graphics::apply_transformations(ctx)?;
|
||||
/*graphics::set_projection(ctx, DrawParam::new()
|
||||
.scale(Vec2::new(state_ref.scale, state_ref.scale))
|
||||
.to_matrix());*/
|
||||
|
||||
if let Some(scene) = self.scene.as_mut() {
|
||||
scene.draw(state_ref, ctx)?;
|
||||
|
@ -172,8 +161,7 @@ impl Game {
|
|||
state_ref.touch_controls.draw(state_ref.canvas_size, &state_ref.constants, &mut state_ref.texture_set, ctx)?;
|
||||
}
|
||||
|
||||
graphics::set_transform(ctx, self.def_matrix);
|
||||
graphics::apply_transformations(ctx)?;
|
||||
//graphics::set_projection(ctx, self.def_matrix);
|
||||
self.ui.draw(state_ref, ctx, scene)?;
|
||||
}
|
||||
|
||||
|
@ -181,34 +169,30 @@ impl Game {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn key_down_event(&mut self, key_code: KeyCode, _key_mod: KeyMods, repeat: bool) {
|
||||
fn key_down_event(&mut self, key_code: ScanCode, repeat: bool) {
|
||||
if repeat { return; }
|
||||
|
||||
let state = unsafe { &mut *self.state.get() };
|
||||
match key_code {
|
||||
KeyCode::F5 => { state.settings.subpixel_coords = !state.settings.subpixel_coords }
|
||||
KeyCode::F6 => { state.settings.motion_interpolation = !state.settings.motion_interpolation }
|
||||
KeyCode::F7 => { state.set_speed(1.0) }
|
||||
KeyCode::F8 => {
|
||||
ScanCode::F5 => { state.settings.subpixel_coords = !state.settings.subpixel_coords }
|
||||
ScanCode::F6 => { state.settings.motion_interpolation = !state.settings.motion_interpolation }
|
||||
ScanCode::F7 => { state.set_speed(1.0) }
|
||||
ScanCode::F8 => {
|
||||
if state.settings.speed > 0.2 {
|
||||
state.set_speed(state.settings.speed - 0.1);
|
||||
}
|
||||
}
|
||||
KeyCode::F9 => {
|
||||
ScanCode::F9 => {
|
||||
if state.settings.speed < 3.0 {
|
||||
state.set_speed(state.settings.speed + 0.1);
|
||||
}
|
||||
}
|
||||
KeyCode::F10 => { state.settings.debug_outlines = !state.settings.debug_outlines }
|
||||
KeyCode::F11 => { state.settings.god_mode = !state.settings.god_mode }
|
||||
KeyCode::F12 => { state.settings.infinite_booster = !state.settings.infinite_booster }
|
||||
ScanCode::F10 => { state.settings.debug_outlines = !state.settings.debug_outlines }
|
||||
ScanCode::F11 => { state.settings.god_mode = !state.settings.god_mode }
|
||||
ScanCode::F12 => { state.settings.infinite_booster = !state.settings.infinite_booster }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_up_event(&mut self, _key_code: KeyCode, _key_mod: KeyMods) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
|
@ -271,68 +255,22 @@ pub fn android_main() {
|
|||
init().unwrap();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
static BACKENDS: [Backend; 2] = [
|
||||
Backend::OpenGLES { major: 3, minor: 0 },
|
||||
Backend::OpenGLES { major: 2, minor: 0 }
|
||||
];
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
static BACKENDS: [Backend; 4] = [
|
||||
Backend::OpenGL { major: 3, minor: 2 },
|
||||
Backend::OpenGLES { major: 3, minor: 2 },
|
||||
Backend::OpenGLES { major: 3, minor: 0 },
|
||||
Backend::OpenGLES { major: 2, minor: 0 }
|
||||
];
|
||||
|
||||
fn init_ctx<P: Into<path::PathBuf> + Clone>(event_loop: &winit::event_loop::EventLoopWindowTarget<()>, resource_dir: P) -> GameResult<Context> {
|
||||
for backend in BACKENDS.iter() {
|
||||
let ctx = ContextBuilder::new("doukutsu-rs")
|
||||
.window_setup(WindowSetup::default().title("Cave Story ~ Doukutsu Monogatari (doukutsu-rs)"))
|
||||
.window_mode(WindowMode::default()
|
||||
.resizable(true)
|
||||
.min_dimensions(320.0, 240.0)
|
||||
.dimensions(640.0, 480.0))
|
||||
.add_resource_path(resource_dir.clone())
|
||||
.backend(*backend)
|
||||
.build(event_loop);
|
||||
|
||||
match ctx {
|
||||
Ok(mut ctx) => {
|
||||
mount_vfs(&mut ctx, Box::new(BuiltinFS::new()));
|
||||
|
||||
return Ok(ctx);
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("Failed to create backend using config {:?}: {}", backend, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(GameError::EventLoopError("Failed to initialize OpenGL backend. Perhaps the driver is outdated?".to_string()))
|
||||
}
|
||||
|
||||
pub fn init() -> GameResult {
|
||||
pretty_env_logger::env_logger::from_env(Env::default().default_filter_or("info"))
|
||||
.filter(Some("gfx_device_gl::factory"), LevelFilter::Warn)
|
||||
.init();
|
||||
|
||||
let resource_dir = if let Ok(data_dir) = env::var("CAVESTORY_DATA_DIR") {
|
||||
path::PathBuf::from(data_dir)
|
||||
} else if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
|
||||
let mut path = path::PathBuf::from(manifest_dir);
|
||||
path.push("data");
|
||||
path
|
||||
} else {
|
||||
path::PathBuf::from("data")
|
||||
};
|
||||
let mut resource_dir = env::current_exe()?;
|
||||
|
||||
// Ditch the filename (if any)
|
||||
if resource_dir.file_name().is_some() {
|
||||
let _ = resource_dir.pop();
|
||||
}
|
||||
resource_dir.push("data");
|
||||
|
||||
info!("Resource directory: {:?}", resource_dir);
|
||||
info!("Initializing engine...");
|
||||
|
||||
let event_loop = winit::event_loop::EventLoop::new();
|
||||
let mut context: Option<Context>;
|
||||
let mut game: Option<Game> = None;
|
||||
let mut context: Context = Context::new();
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
|
@ -346,152 +284,4 @@ pub fn init() -> GameResult {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
context = Some(init_ctx(&event_loop, resource_dir.clone())?);
|
||||
|
||||
event_loop.run(move |event, target, flow| {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
// Windows' system clock implementation isn't monotonic when the process gets switched to another core.
|
||||
// Rust has mitigations for this, but apparently aren't very effective unless Instant is called very often.
|
||||
let _ = Instant::now();
|
||||
}
|
||||
if let Some(ctx) = &mut context {
|
||||
ctx.process_event(&event);
|
||||
|
||||
if let Some(game) = &mut game {
|
||||
game.ui.handle_events(ctx, &event);
|
||||
} else {
|
||||
let mut new_game = Game::new(ctx).unwrap();
|
||||
let state_ref = unsafe { &mut *new_game.state.get() };
|
||||
state_ref.next_scene = Some(Box::new(LoadingScene::new()));
|
||||
game = Some(new_game);
|
||||
|
||||
#[cfg(feature = "scripting")]
|
||||
{
|
||||
unsafe {
|
||||
let game_ref = game.as_mut().unwrap();
|
||||
let state_ref = game_ref.state.get();
|
||||
|
||||
(&mut *state_ref).lua.update_refs(game_ref.state.get(), ctx as *mut Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match event {
|
||||
Event::Resumed => {
|
||||
#[cfg(target_os = "android")]
|
||||
if context.is_none() {
|
||||
context = Some(init_ctx(target, resource_dir.clone()).unwrap());
|
||||
}
|
||||
let _ = target;
|
||||
|
||||
if let Some(game) = &mut game {
|
||||
game.loops = 0;
|
||||
}
|
||||
}
|
||||
Event::Suspended => {
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
context = None;
|
||||
}
|
||||
if let Some(game) = &mut game {
|
||||
game.loops = 0;
|
||||
}
|
||||
}
|
||||
Event::WindowEvent { event, .. } => {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
if let Some(game) = &mut game {
|
||||
let state_ref = unsafe { &mut *game.state.get() };
|
||||
state_ref.shutdown();
|
||||
}
|
||||
*flow = ControlFlow::Exit;
|
||||
}
|
||||
WindowEvent::Resized(size) => {
|
||||
// Minimizing a window on Windows causes this event to get called with a 0x0 size
|
||||
if size.width != 0 && size.height != 0 {
|
||||
if let (Some(ctx), Some(game)) = (&mut context, &mut game) {
|
||||
let state_ref = unsafe { &mut *game.state.get() };
|
||||
|
||||
state_ref.tmp_canvas = Canvas::with_window_size(ctx).unwrap();
|
||||
state_ref.game_canvas = Canvas::with_window_size(ctx).unwrap();
|
||||
state_ref.lightmap_canvas = Canvas::with_window_size(ctx).unwrap();
|
||||
state_ref.handle_resize(ctx).unwrap();
|
||||
graphics::window(ctx).update_gfx(&mut game.ui.main_color, &mut game.ui.main_depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowEvent::Touch(touch) => {
|
||||
if let Some(game) = &mut game {
|
||||
let state_ref = unsafe { &mut *game.state.get() };
|
||||
state_ref.touch_controls.process_winit_event(state_ref.scale, touch);
|
||||
}
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
state: el_state,
|
||||
virtual_keycode: Some(keycode),
|
||||
modifiers,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
if let (Some(ctx), Some(game)) = (&mut context, &mut game) {
|
||||
match el_state {
|
||||
ElementState::Pressed => {
|
||||
let repeat = keyboard::is_key_repeated(ctx);
|
||||
game.key_down_event(keycode, modifiers.into(), repeat);
|
||||
}
|
||||
ElementState::Released => {
|
||||
game.key_up_event(keycode, modifiers.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Event::RedrawRequested(win) => {
|
||||
if let (Some(ctx), Some(game)) = (&mut context, &mut game) {
|
||||
if win == window(ctx).window().id() {
|
||||
ctx.timer_context.tick();
|
||||
game.draw(ctx).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::MainEventsCleared => {
|
||||
if let (Some(ctx), Some(game)) = (&mut context, &mut game) {
|
||||
game.update(ctx).unwrap();
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
ctx.timer_context.tick();
|
||||
game.draw(ctx).unwrap(); // redraw request is unimplemented on shitdroid
|
||||
}
|
||||
window(ctx).window().request_redraw();
|
||||
|
||||
let state_ref = unsafe { &mut *game.state.get() };
|
||||
|
||||
if state_ref.shutdown {
|
||||
log::info!("Shutting down...");
|
||||
*flow = ControlFlow::Exit;
|
||||
return;
|
||||
}
|
||||
|
||||
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, ctx).unwrap();
|
||||
game.loops = 0;
|
||||
state_ref.frame_time = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use ggez::{Context, GameResult};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use imgui::{CollapsingHeader, Condition, im_str, ImStr, ImString, Slider, Window};
|
||||
use itertools::Itertools;
|
||||
|
||||
|
|
31
src/main.rs
31
src/main.rs
|
@ -1,5 +1,34 @@
|
|||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use std::process::exit;
|
||||
|
||||
fn main() {
|
||||
doukutsu_rs::init().unwrap();
|
||||
let result = doukutsu_rs::init();
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
unsafe {
|
||||
use winapi::_core::ptr::null_mut;
|
||||
use winapi::um::winuser::MessageBoxW;
|
||||
use winapi::um::winuser::MB_OK;
|
||||
use winapi::shared::ntdef::LPCWSTR;
|
||||
use std::ffi::OsStr;
|
||||
use std::os::windows::prelude::*;
|
||||
|
||||
if let Err(e) = result {
|
||||
let title: LPCWSTR = OsStr::new("Error!")
|
||||
.encode_wide().chain(Some(0)).collect::<Vec<u16>>().as_ptr();
|
||||
let message: LPCWSTR = OsStr::new(format!("Whoops, doukutsu-rs crashed: {}", e).as_str())
|
||||
.encode_wide().chain(Some(0)).collect::<Vec<u16>>().as_ptr();
|
||||
MessageBoxW(null_mut(),
|
||||
message,
|
||||
title,
|
||||
MB_OK);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = result {
|
||||
println!("Initialization error: {}", e);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use std::io;
|
||||
|
||||
use byteorder::{LE, ReadBytesExt};
|
||||
use ggez::GameError::ResourceLoadError;
|
||||
use ggez::GameResult;
|
||||
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::str;
|
||||
use crate::framework::error::GameError::ResourceLoadError;
|
||||
|
||||
static SUPPORTED_PXM_VERSIONS: [u8; 1] = [0x10];
|
||||
static SUPPORTED_PXE_VERSIONS: [u8; 2] = [0, 0x10];
|
||||
|
@ -28,7 +30,6 @@ impl Map {
|
|||
|
||||
let version = map_data.read_u8()?;
|
||||
|
||||
// It's something Booster's Lab supports but I haven't seen anywhere being used in practice
|
||||
if !SUPPORTED_PXM_VERSIONS.contains(&version) {
|
||||
return Err(ResourceLoadError(format!("Unsupported PXM version: {:#x}", version)));
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use ggez::{Context, GameResult};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::common::Rect;
|
||||
use crate::input::combined_menu_controller::CombinedMenuController;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
|
||||
pub struct MenuSaveInfo {
|
||||
|
||||
}
|
||||
pub struct MenuSaveInfo {}
|
||||
|
||||
pub enum MenuEntry {
|
||||
Hidden,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use num_traits::clamp;
|
||||
|
||||
use crate::caret::CaretType;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::common::Direction;
|
||||
use crate::npc::NPC;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::caret::CaretType;
|
||||
use crate::common::Direction;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use num_traits::{abs, clamp};
|
||||
|
||||
use crate::common::Direction;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use num_traits::{abs, clamp};
|
||||
|
||||
use crate::caret::CaretType;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use num_traits::clamp;
|
||||
|
||||
use crate::common::Direction;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use num_traits::abs;
|
||||
use num_traits::clamp;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use ggez::GameResult;
|
||||
|
||||
use crate::common::{CDEG_RAD, Direction};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::npc::list::NPCList;
|
||||
use crate::npc::NPC;
|
||||
use crate::player::Player;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::caret::CaretType;
|
||||
use crate::common::Direction;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::common::Direction;
|
||||
use crate::npc::NPC;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use num_traits::clamp;
|
||||
|
||||
use crate::caret::CaretType;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use ggez::GameResult;
|
||||
use num_traits::{abs, clamp};
|
||||
|
||||
use crate::common::Direction;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::npc::NPC;
|
||||
use crate::player::Player;
|
||||
use crate::rng::RNG;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use num_traits::{abs, clamp};
|
||||
|
||||
use crate::caret::CaretType;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use ggez::GameResult;
|
||||
use num_traits::clamp;
|
||||
|
||||
use crate::common::Direction;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::npc::list::NPCList;
|
||||
use crate::npc::NPC;
|
||||
use crate::rng::RNG;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use crate::npc::NPC;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
use crate::player::Player;
|
||||
use ggez::GameResult;
|
||||
use num_traits::abs;
|
||||
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::npc::NPC;
|
||||
use crate::player::Player;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
|
||||
impl NPC {
|
||||
pub(crate) fn tick_n215_sandcroc_outer_wall(&mut self, state: &mut SharedGameState, players: [&mut Player; 2]) -> GameResult {
|
||||
match self.action_num {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use num_traits::clamp;
|
||||
|
||||
use crate::common::Direction;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::common::Direction;
|
||||
use crate::npc::list::NPCList;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use num_traits::{abs, clamp};
|
||||
|
||||
use crate::caret::CaretType;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use num_traits::abs;
|
||||
|
||||
use crate::common::Direction;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use num_traits::clamp;
|
||||
|
||||
use crate::common::Direction;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use num_traits::clamp;
|
||||
|
||||
use crate::common::Direction;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::npc::NPC;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::GameResult;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::caret::CaretType;
|
||||
use crate::common::{CDEG_RAD, Direction, Rect};
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use std::mem::MaybeUninit;
|
||||
|
||||
use ggez::{Context, GameResult};
|
||||
|
||||
use crate::bullet::BulletManager;
|
||||
use crate::common::{Direction, interpolate_fix9_scale};
|
||||
use crate::entity::GameEntity;
|
||||
use crate::frame::Frame;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::npc::list::NPCList;
|
||||
use crate::npc::NPC;
|
||||
use crate::player::Player;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use ggez::GameResult;
|
||||
use num_traits::{abs, clamp};
|
||||
|
||||
use crate::caret::CaretType;
|
||||
use crate::common::{CDEG_RAD, Direction, Rect};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::npc::boss::BossNPC;
|
||||
use crate::npc::list::NPCList;
|
||||
use crate::npc::NPC;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use ggez::GameResult;
|
||||
|
||||
|
||||
use crate::bullet::BulletManager;
|
||||
use crate::caret::CaretType;
|
||||
|
@ -9,6 +9,8 @@ use crate::npc::NPC;
|
|||
use crate::player::Player;
|
||||
use crate::rng::RNG;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
impl NPC {
|
||||
pub(crate) fn tick_n048_omega_projectiles(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::cell::{Cell, UnsafeCell};
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
use ggez::{GameError, GameResult};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::{GameResult, GameError};
|
||||
|
||||
use crate::npc::NPC;
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ use std::io;
|
|||
use std::io::Cursor;
|
||||
|
||||
use byteorder::{LE, ReadBytesExt};
|
||||
use ggez::{Context, GameResult};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use num_traits::abs;
|
||||
|
||||
use crate::bitfield;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::clone::Clone;
|
||||
|
||||
use ggez::{Context, GameResult};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::clamp;
|
||||
|
||||
|
|
|
@ -4,13 +4,15 @@ use byteorder::{BE, LE, ReadBytesExt, WriteBytesExt};
|
|||
use num_traits::{clamp, FromPrimitive};
|
||||
|
||||
use crate::common::{Direction, FadeState};
|
||||
use ggez::{Context, GameResult};
|
||||
use ggez::GameError::ResourceLoadError;
|
||||
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::player::ControlMode;
|
||||
use crate::scene::game_scene::GameScene;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
use crate::str;
|
||||
use crate::weapon::{WeaponLevel, WeaponType};
|
||||
use crate::framework::error::GameError::ResourceLoadError;
|
||||
|
||||
pub struct WeaponData {
|
||||
pub weapon_id: u32,
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
use ggez::{Context, GameResult, graphics, timer};
|
||||
use ggez::graphics::{BlendMode, Color, Drawable, DrawParam, FilterMode, mint};
|
||||
use ggez::graphics::spritebatch::SpriteBatch;
|
||||
use ggez::nalgebra::{clamp, Vector2};
|
||||
use log::info;
|
||||
use num_traits::abs;
|
||||
use num_traits::{abs, clamp};
|
||||
|
||||
use crate::bullet::BulletManager;
|
||||
use crate::caret::CaretType;
|
||||
use crate::common::{Direction, FadeDirection, FadeState, fix9_scale, interpolate_fix9_scale, Rect};
|
||||
use crate::common::{Direction, FadeDirection, FadeState, fix9_scale, interpolate_fix9_scale, Rect, Color};
|
||||
use crate::components::boss_life_bar::BossLifeBar;
|
||||
use crate::components::draw_common::{Alignment, draw_number};
|
||||
use crate::components::hud::HUD;
|
||||
|
@ -30,6 +26,10 @@ use crate::text_script::{ConfirmSelection, ScriptMode, TextScriptExecutionState,
|
|||
use crate::texture_set::SizedBatch;
|
||||
use crate::ui::Components;
|
||||
use crate::weapon::WeaponType;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::graphics;
|
||||
use crate::framework::graphics::FilterMode;
|
||||
|
||||
pub struct GameScene {
|
||||
pub tick: u32,
|
||||
|
@ -502,10 +502,10 @@ impl GameScene {
|
|||
}
|
||||
|
||||
fn draw_light_map(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
graphics::set_canvas(ctx, Some(&state.lightmap_canvas));
|
||||
/*graphics::set_canvas(ctx, Some(&state.lightmap_canvas));
|
||||
graphics::set_blend_mode(ctx, BlendMode::Add)?;
|
||||
|
||||
graphics::clear(ctx, Color::from_rgb(100, 100, 110));
|
||||
graphics::clear(ctx, Color::from_rgb(100, 100, 110));*/
|
||||
{
|
||||
let scale = state.scale;
|
||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "builtin/lightmap/spot")?;
|
||||
|
@ -634,94 +634,13 @@ impl GameScene {
|
|||
batch.draw_filtered(FilterMode::Linear, ctx)?;
|
||||
}
|
||||
|
||||
graphics::set_blend_mode(ctx, BlendMode::Multiply)?;
|
||||
/*graphics::set_blend_mode(ctx, BlendMode::Multiply)?;
|
||||
graphics::set_canvas(ctx, Some(&state.game_canvas));
|
||||
state.lightmap_canvas.set_filter(FilterMode::Linear);
|
||||
state.lightmap_canvas.draw(ctx, DrawParam::new()
|
||||
.scale(Vector2::new(1.0 / state.scale, 1.0 / state.scale)))?;
|
||||
|
||||
graphics::set_blend_mode(ctx, BlendMode::Alpha)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_water(&self, tile: u8) -> bool {
|
||||
[0x02, 0x04, 0x60, 0x61, 0x62, 0x64, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0xa0, 0xa1, 0xa2, 0xa3].contains(&tile)
|
||||
}
|
||||
|
||||
fn draw_water(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
let (frame_x, frame_y) = self.frame.xy_interpolated(state.frame_time, state.scale);
|
||||
|
||||
{
|
||||
state.shaders.water_shader_params.resolution = [state.canvas_size.0, state.canvas_size.1];
|
||||
state.shaders.water_shader_params.frame_pos = [frame_x, frame_y];
|
||||
state.shaders.water_shader_params.t = self.tick as f32;
|
||||
let _lock = graphics::use_shader(ctx, &state.shaders.water_shader);
|
||||
state.shaders.water_shader.send(ctx, state.shaders.water_shader_params)?;
|
||||
|
||||
graphics::set_canvas(ctx, Some(&state.tmp_canvas));
|
||||
graphics::clear(ctx, Color::new(0.0, 0.0, 0.0, 1.0));
|
||||
state.game_canvas.draw(ctx, DrawParam::new()
|
||||
.scale(mint::Vector2 { x: 1.0 / state.scale, y: -1.0 / state.scale })
|
||||
.offset(mint::Point2 { x: 0.0, y: -1.0 }))?;
|
||||
}
|
||||
graphics::set_canvas(ctx, Some(&state.game_canvas));
|
||||
|
||||
// cheap, clones a reference underneath
|
||||
let mut tmp_batch = SpriteBatch::new(state.tmp_canvas.image().clone());
|
||||
|
||||
let tile_start_x = clamp(self.frame.x / 0x200 / 16, 0, self.stage.map.width as i32) as usize;
|
||||
let tile_start_y = clamp(self.frame.y / 0x200 / 16, 0, self.stage.map.height as i32) as usize;
|
||||
let tile_end_x = clamp((self.frame.x / 0x200 + 8 + state.canvas_size.0 as i32) / 16 + 1, 0, self.stage.map.width as i32) as usize;
|
||||
let tile_end_y = clamp((self.frame.y / 0x200 + 8 + state.canvas_size.1 as i32) / 16 + 1, 0, self.stage.map.height as i32) as usize;
|
||||
let mut rect = Rect {
|
||||
left: 0.0,
|
||||
top: 0.0,
|
||||
right: 16.0,
|
||||
bottom: 16.0,
|
||||
};
|
||||
|
||||
for y in tile_start_y..tile_end_y {
|
||||
for x in tile_start_x..tile_end_x {
|
||||
let tile = unsafe {
|
||||
self.stage.map.attrib[*self.stage.map.tiles
|
||||
.get_unchecked((y * self.stage.map.width as usize) + x) as usize]
|
||||
};
|
||||
let tile_above = unsafe {
|
||||
self.stage.map.attrib[*self.stage.map.tiles
|
||||
.get_unchecked((y.saturating_sub(1) * self.stage.map.width as usize) + x) as usize]
|
||||
};
|
||||
|
||||
if !self.is_water(tile) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rect.left = (x as f32 * 16.0 - 8.0) - frame_x;
|
||||
rect.top = (y as f32 * 16.0 - 8.0) - frame_y;
|
||||
rect.right = rect.left + 16.0;
|
||||
rect.bottom = rect.top + 16.0;
|
||||
|
||||
if tile_above == 0 {
|
||||
rect.top += 3.0;
|
||||
}
|
||||
|
||||
tmp_batch.add(DrawParam::new()
|
||||
.src(ggez::graphics::Rect::new(rect.left / state.canvas_size.0,
|
||||
rect.top / state.canvas_size.1,
|
||||
(rect.right - rect.left) / state.canvas_size.0,
|
||||
(rect.bottom - rect.top) / state.canvas_size.1))
|
||||
.scale(mint::Vector2 {
|
||||
x: 1.0 / state.scale,
|
||||
y: 1.0 / state.scale,
|
||||
})
|
||||
.dest(mint::Point2 {
|
||||
x: rect.left,
|
||||
y: rect.top,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
tmp_batch.draw(ctx, DrawParam::new())?;
|
||||
graphics::set_blend_mode(ctx, BlendMode::Alpha)?;*/
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1291,7 +1210,7 @@ impl Scene for GameScene {
|
|||
}
|
||||
|
||||
fn draw(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
graphics::set_canvas(ctx, Some(&state.game_canvas));
|
||||
//graphics::set_canvas(ctx, Some(&state.game_canvas));
|
||||
self.draw_background(state, ctx)?;
|
||||
self.draw_tiles(state, ctx, TileLayer::Background)?;
|
||||
if state.settings.shader_effects
|
||||
|
@ -1316,9 +1235,9 @@ impl Scene for GameScene {
|
|||
self.draw_bullets(state, ctx)?;
|
||||
self.player2.draw(state, ctx, &self.frame)?;
|
||||
self.player1.draw(state, ctx, &self.frame)?;
|
||||
if state.settings.shader_effects && self.water_visible {
|
||||
/*if state.settings.shader_effects && self.water_visible {
|
||||
self.draw_water(state, ctx)?;
|
||||
}
|
||||
}*/
|
||||
|
||||
self.draw_tiles(state, ctx, TileLayer::Foreground)?;
|
||||
self.draw_tiles(state, ctx, TileLayer::Snack)?;
|
||||
|
@ -1329,9 +1248,9 @@ impl Scene for GameScene {
|
|||
self.draw_light_map(state, ctx)?;
|
||||
}
|
||||
|
||||
graphics::set_canvas(ctx, None);
|
||||
/*graphics::set_canvas(ctx, None);
|
||||
state.game_canvas.draw(ctx, DrawParam::new()
|
||||
.scale(Vector2::new(1.0 / state.scale, 1.0 / state.scale)))?;
|
||||
.scale(Vector2::new(1.0 / state.scale, 1.0 / state.scale)))?;*/
|
||||
self.draw_black_bars(state, ctx)?;
|
||||
|
||||
if state.control_flags.control_enabled() {
|
||||
|
@ -1389,7 +1308,7 @@ impl Scene for GameScene {
|
|||
self.draw_debug_outlines(state, ctx)?;
|
||||
}
|
||||
|
||||
draw_number(state.canvas_size.0 - 8.0, 8.0, timer::fps(ctx) as usize, Alignment::Right, state, ctx)?;
|
||||
//draw_number(state.canvas_size.0 - 8.0, 8.0, timer::fps(ctx) as usize, Alignment::Right, state, ctx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use ggez::{Context, filesystem, GameResult};
|
||||
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::filesystem;
|
||||
use crate::npc::NPCTable;
|
||||
use crate::scene::no_data_scene::NoDataScene;
|
||||
use crate::scene::Scene;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::{Context, GameResult};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
use crate::ui::Components;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use ggez::{Context, GameError, GameResult};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::{GameResult, GameError};
|
||||
|
||||
use crate::common::Rect;
|
||||
use crate::scene::Scene;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use ggez::{Context, GameResult, graphics};
|
||||
use ggez::graphics::Color;
|
||||
|
||||
use crate::common::{Rect, VERSION_BANNER};
|
||||
use crate::common::{Rect, VERSION_BANNER, Color};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::graphics;
|
||||
use crate::input::combined_menu_controller::CombinedMenuController;
|
||||
use crate::input::touch_controls::TouchControlType;
|
||||
use crate::menu::{Menu, MenuEntry, MenuSelectionResult};
|
||||
use crate::scene::Scene;
|
||||
use crate::shared_game_state::{SharedGameState, TimingMode};
|
||||
use crate::input::combined_menu_controller::CombinedMenuController;
|
||||
use crate::input::touch_controls::TouchControlType;
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
use std::io::{Read, Seek};
|
||||
use std::ptr::null_mut;
|
||||
|
||||
use ggez::{Context, filesystem, GameError, GameResult};
|
||||
use ggez::filesystem::File;
|
||||
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::{GameResult, GameError};
|
||||
|
||||
|
||||
use lua_ffi::{c_int, LuaFunction, LuaObject, State, ThreadStatus};
|
||||
use lua_ffi::ffi::lua_pushcfunction;
|
||||
|
||||
use crate::scene::game_scene::GameScene;
|
||||
use crate::scripting::doukutsu::Doukutsu;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
use crate::framework::filesystem::File;
|
||||
use crate::framework::filesystem;
|
||||
|
||||
mod doukutsu;
|
||||
mod player;
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use ggez::{Context, GameResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use winit::event::VirtualKeyCode;
|
||||
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::keyboard::ScanCode;
|
||||
use crate::input::keyboard_player_controller::KeyboardController;
|
||||
use crate::input::player_controller::PlayerController;
|
||||
use crate::player::TargetPlayer;
|
||||
use crate::input::touch_player_controller::TouchPlayerController;
|
||||
use crate::player::TargetPlayer;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Settings {
|
||||
|
@ -66,47 +67,47 @@ impl Default for Settings {
|
|||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct PlayerKeyMap {
|
||||
pub left: VirtualKeyCode,
|
||||
pub up: VirtualKeyCode,
|
||||
pub right: VirtualKeyCode,
|
||||
pub down: VirtualKeyCode,
|
||||
pub prev_weapon: VirtualKeyCode,
|
||||
pub next_weapon: VirtualKeyCode,
|
||||
pub jump: VirtualKeyCode,
|
||||
pub shoot: VirtualKeyCode,
|
||||
pub skip: VirtualKeyCode,
|
||||
pub inventory: VirtualKeyCode,
|
||||
pub map: VirtualKeyCode,
|
||||
pub left: ScanCode,
|
||||
pub up: ScanCode,
|
||||
pub right: ScanCode,
|
||||
pub down: ScanCode,
|
||||
pub prev_weapon: ScanCode,
|
||||
pub next_weapon: ScanCode,
|
||||
pub jump: ScanCode,
|
||||
pub shoot: ScanCode,
|
||||
pub skip: ScanCode,
|
||||
pub inventory: ScanCode,
|
||||
pub map: ScanCode,
|
||||
}
|
||||
|
||||
fn p1_default_keymap() -> PlayerKeyMap {
|
||||
PlayerKeyMap {
|
||||
left: VirtualKeyCode::Left,
|
||||
up: VirtualKeyCode::Up,
|
||||
right: VirtualKeyCode::Right,
|
||||
down: VirtualKeyCode::Down,
|
||||
prev_weapon: VirtualKeyCode::A,
|
||||
next_weapon: VirtualKeyCode::S,
|
||||
jump: VirtualKeyCode::Z,
|
||||
shoot: VirtualKeyCode::X,
|
||||
skip: VirtualKeyCode::LControl,
|
||||
inventory: VirtualKeyCode::Q,
|
||||
map: VirtualKeyCode::W,
|
||||
left: ScanCode::Left,
|
||||
up: ScanCode::Up,
|
||||
right: ScanCode::Right,
|
||||
down: ScanCode::Down,
|
||||
prev_weapon: ScanCode::A,
|
||||
next_weapon: ScanCode::S,
|
||||
jump: ScanCode::Z,
|
||||
shoot: ScanCode::X,
|
||||
skip: ScanCode::LControl,
|
||||
inventory: ScanCode::Q,
|
||||
map: ScanCode::W,
|
||||
}
|
||||
}
|
||||
|
||||
fn p2_default_keymap() -> PlayerKeyMap {
|
||||
PlayerKeyMap {
|
||||
left: VirtualKeyCode::Comma,
|
||||
up: VirtualKeyCode::L,
|
||||
right: VirtualKeyCode::Slash,
|
||||
down: VirtualKeyCode::Period,
|
||||
prev_weapon: VirtualKeyCode::G,
|
||||
next_weapon: VirtualKeyCode::H,
|
||||
jump: VirtualKeyCode::B,
|
||||
shoot: VirtualKeyCode::N,
|
||||
skip: VirtualKeyCode::RControl,
|
||||
inventory: VirtualKeyCode::T,
|
||||
map: VirtualKeyCode::Y,
|
||||
left: ScanCode::Comma,
|
||||
up: ScanCode::L,
|
||||
right: ScanCode::Slash,
|
||||
down: ScanCode::Period,
|
||||
prev_weapon: ScanCode::G,
|
||||
next_weapon: ScanCode::H,
|
||||
jump: ScanCode::B,
|
||||
shoot: ScanCode::N,
|
||||
skip: ScanCode::U,
|
||||
inventory: ScanCode::T,
|
||||
map: ScanCode::Y,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use gfx::{self, *};
|
||||
use ggez::graphics::Shader;
|
||||
use ggez::{Context, GameResult};
|
||||
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
gfx_defines! {
|
||||
constant WaterShaderParams {
|
||||
|
@ -11,7 +12,7 @@ gfx_defines! {
|
|||
}
|
||||
|
||||
pub struct Shaders {
|
||||
pub water_shader: Shader<WaterShaderParams>,
|
||||
//pub water_shader: Shader<WaterShaderParams>,
|
||||
pub water_shader_params: WaterShaderParams,
|
||||
}
|
||||
|
||||
|
@ -24,14 +25,14 @@ impl Shaders {
|
|||
};
|
||||
|
||||
Ok(Shaders {
|
||||
water_shader: Shader::new(
|
||||
/*water_shader: Shader::new(
|
||||
ctx,
|
||||
"/builtin/shaders/basic_es300.vert.glsl",
|
||||
"/builtin/shaders/water_es300.frag.glsl",
|
||||
water_shader_params,
|
||||
"WaterShaderParams",
|
||||
None,
|
||||
)?,
|
||||
)?,*/
|
||||
water_shader_params,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
use std::ops::Div;
|
||||
use std::time::Instant;
|
||||
|
||||
use bitvec::vec::BitVec;
|
||||
use chrono::{Datelike, Local};
|
||||
use ggez::{Context, filesystem, GameResult, graphics};
|
||||
use ggez::filesystem::OpenOptions;
|
||||
use ggez::graphics::Canvas;
|
||||
use num_traits::clamp;
|
||||
use num_traits::real::Real;
|
||||
|
||||
use crate::bmfont_renderer::BMFontRenderer;
|
||||
use crate::caret::{Caret, CaretType};
|
||||
use crate::common::{ControlFlags, Direction, FadeState};
|
||||
use crate::engine_constants::EngineConstants;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::vfs::OpenOptions;
|
||||
use crate::input::touch_controls::TouchControls;
|
||||
use crate::npc::NPCTable;
|
||||
use crate::profile::GameProfile;
|
||||
|
@ -21,12 +21,12 @@ use crate::scene::Scene;
|
|||
#[cfg(feature = "scripting")]
|
||||
use crate::scripting::LuaScriptingState;
|
||||
use crate::settings::Settings;
|
||||
use crate::shaders::Shaders;
|
||||
use crate::sound::SoundManager;
|
||||
use crate::stage::StageData;
|
||||
use crate::str;
|
||||
use crate::text_script::{ScriptMode, TextScriptExecutionState, TextScriptVM};
|
||||
use crate::texture_set::{TextureSet};
|
||||
use crate::texture_set::TextureSet;
|
||||
use crate::framework::{filesystem, graphics};
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
pub enum TimingMode {
|
||||
|
@ -102,10 +102,6 @@ pub struct SharedGameState {
|
|||
pub stages: Vec<StageData>,
|
||||
pub frame_time: f64,
|
||||
pub scale: f32,
|
||||
pub shaders: Shaders,
|
||||
pub tmp_canvas: Canvas,
|
||||
pub game_canvas: Canvas,
|
||||
pub lightmap_canvas: Canvas,
|
||||
pub canvas_size: (f32, f32),
|
||||
pub screen_size: (f32, f32),
|
||||
pub next_scene: Option<Box<dyn Scene>>,
|
||||
|
@ -123,8 +119,8 @@ pub struct SharedGameState {
|
|||
|
||||
impl SharedGameState {
|
||||
pub fn new(ctx: &mut Context) -> GameResult<SharedGameState> {
|
||||
let screen_size = graphics::drawable_size(ctx);
|
||||
let scale = screen_size.1.div(235.0).floor().max(1.0);
|
||||
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);
|
||||
|
||||
|
@ -164,7 +160,7 @@ impl SharedGameState {
|
|||
game_flags: bitvec::bitvec![0; 8000],
|
||||
fade_state: FadeState::Hidden,
|
||||
game_rng: XorShift::new(0),
|
||||
effect_rng: XorShift::new(Instant::now().elapsed().as_nanos() as i32),
|
||||
effect_rng: XorShift::new(123),
|
||||
quake_counter: 0,
|
||||
teleporter_slots: Vec::with_capacity(8),
|
||||
carets: Vec::with_capacity(32),
|
||||
|
@ -175,10 +171,6 @@ impl SharedGameState {
|
|||
stages: Vec::with_capacity(96),
|
||||
frame_time: 0.0,
|
||||
scale,
|
||||
shaders: Shaders::new(ctx)?,
|
||||
tmp_canvas: Canvas::with_window_size(ctx)?,
|
||||
game_canvas: Canvas::with_window_size(ctx)?,
|
||||
lightmap_canvas: Canvas::with_window_size(ctx)?,
|
||||
screen_size,
|
||||
canvas_size,
|
||||
next_scene: None,
|
||||
|
@ -289,10 +281,10 @@ impl SharedGameState {
|
|||
|
||||
pub fn handle_resize(&mut self, ctx: &mut Context) -> GameResult {
|
||||
self.screen_size = graphics::drawable_size(ctx);
|
||||
self.scale = self.screen_size.1.div(240.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);
|
||||
|
||||
graphics::set_screen_coordinates(ctx, graphics::Rect::new(0.0, 0.0, self.screen_size.0, self.screen_size.1))?;
|
||||
//graphics::set_screen_coordinates(ctx, graphics::Rect::new(0.0, 0.0, self.screen_size.0, self.screen_size.1))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -4,16 +4,18 @@ use std::time::Duration;
|
|||
|
||||
use cpal::Sample;
|
||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
use ggez::{Context, filesystem, GameResult};
|
||||
use ggez::GameError::{AudioError, InvalidValue, ResourceLoadError};
|
||||
use num_traits::clamp;
|
||||
|
||||
use crate::engine_constants::EngineConstants;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::filesystem;
|
||||
use crate::sound::organya::Song;
|
||||
use crate::sound::pixtone::PixTonePlayback;
|
||||
use crate::sound::playback::{PlaybackEngine, SavedPlaybackState};
|
||||
use crate::sound::wave_bank::SoundBank;
|
||||
use crate::str;
|
||||
use crate::framework::error::GameError::{AudioError, ResourceLoadError, InvalidValue};
|
||||
|
||||
mod wave_bank;
|
||||
mod organya;
|
||||
|
|
|
@ -7,10 +7,12 @@ use log::info;
|
|||
|
||||
use crate::encoding::read_cur_shift_jis;
|
||||
use crate::engine_constants::EngineConstants;
|
||||
use ggez::{Context, filesystem, GameResult};
|
||||
use ggez::GameError::ResourceLoadError;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::filesystem;
|
||||
use crate::map::{Map, NPCData};
|
||||
use crate::text_script::TextScript;
|
||||
use crate::framework::error::GameError::ResourceLoadError;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct NpcType {
|
||||
|
|
|
@ -9,8 +9,8 @@ use std::ops::Not;
|
|||
use std::str::FromStr;
|
||||
|
||||
use byteorder::ReadBytesExt;
|
||||
use ggez::{Context, GameResult};
|
||||
use ggez::GameError::{InvalidValue, ParseError};
|
||||
|
||||
|
||||
use itertools::Itertools;
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::{clamp, FromPrimitive};
|
||||
|
@ -28,6 +28,7 @@ use crate::scene::title_scene::TitleScene;
|
|||
use crate::shared_game_state::SharedGameState;
|
||||
use crate::str;
|
||||
use crate::weapon::WeaponType;
|
||||
use crate::framework::error::GameError::ParseError;
|
||||
|
||||
/// Engine's text script VM operation codes.
|
||||
#[derive(EnumString, Debug, FromPrimitive, PartialEq)]
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
use std::collections::HashMap;
|
||||
use std::io::{BufReader, Read, Seek, SeekFrom};
|
||||
|
||||
use ggez;
|
||||
use ggez::{Context, GameError, GameResult, graphics};
|
||||
use ggez::filesystem;
|
||||
use ggez::graphics::{Drawable, DrawMode, DrawParam, FilterMode, Image, Mesh, mint, Rect};
|
||||
use ggez::graphics::spritebatch::SpriteBatch;
|
||||
use ggez::nalgebra::{Point2, Vector2};
|
||||
use image::RgbaImage;
|
||||
use itertools::Itertools;
|
||||
use log::info;
|
||||
|
@ -14,6 +8,10 @@ use log::info;
|
|||
use crate::common;
|
||||
use crate::common::FILE_TYPES;
|
||||
use crate::engine_constants::EngineConstants;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::{GameError, GameResult};
|
||||
use crate::framework::filesystem;
|
||||
use crate::framework::image::Image;
|
||||
use crate::settings::Settings;
|
||||
use crate::shared_game_state::Season;
|
||||
use crate::str;
|
||||
|
@ -21,7 +19,6 @@ use crate::str;
|
|||
pub static mut G_MAG: f32 = 1.0;
|
||||
|
||||
pub struct SizedBatch {
|
||||
pub batch: SpriteBatch,
|
||||
width: usize,
|
||||
height: usize,
|
||||
real_width: usize,
|
||||
|
@ -63,15 +60,15 @@ impl SizedBatch {
|
|||
|
||||
#[inline(always)]
|
||||
pub fn clear(&mut self) {
|
||||
self.batch.clear();
|
||||
/*self.batch.clear();*/
|
||||
}
|
||||
|
||||
pub fn add(&mut self, x: f32, y: f32) {
|
||||
let param = DrawParam::new()
|
||||
/*let param = DrawParam::new()
|
||||
.dest(Point2::new(x, y))
|
||||
.scale(Vector2::new(self.scale_x, self.scale_y));
|
||||
|
||||
self.batch.add(param);
|
||||
self.batch.add(param);*/
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -94,7 +91,7 @@ impl SizedBatch {
|
|||
y = (y * G_MAG).floor() / G_MAG;
|
||||
}
|
||||
|
||||
let param = DrawParam::new()
|
||||
/*let param = DrawParam::new()
|
||||
.src(Rect::new(rect.left as f32 / self.width as f32,
|
||||
rect.top as f32 / self.height as f32,
|
||||
(rect.right - rect.left) as f32 / self.width as f32,
|
||||
|
@ -102,7 +99,7 @@ impl SizedBatch {
|
|||
.dest(mint::Point2 { x, y })
|
||||
.scale(Vector2::new(scale_x, scale_y));
|
||||
|
||||
self.batch.add(param);
|
||||
self.batch.add(param);*/
|
||||
}
|
||||
|
||||
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>) {
|
||||
|
@ -110,7 +107,7 @@ impl SizedBatch {
|
|||
return;
|
||||
}
|
||||
|
||||
let param = DrawParam::new()
|
||||
/*let param = DrawParam::new()
|
||||
.color(color.into())
|
||||
.src(Rect::new(rect.left as f32 / self.width as f32,
|
||||
rect.top as f32 / self.height as f32,
|
||||
|
@ -119,18 +116,19 @@ impl SizedBatch {
|
|||
.dest(mint::Point2 { x, y })
|
||||
.scale(Vector2::new(scale_x, scale_y));
|
||||
|
||||
self.batch.add(param);
|
||||
self.batch.add(param);*/
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
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 {
|
||||
self.batch.set_filter(filter);
|
||||
/*self.batch.set_filter(filter);
|
||||
self.batch.draw(ctx, DrawParam::new())?;
|
||||
self.batch.clear();
|
||||
self.batch.clear();*/
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -213,7 +211,6 @@ impl TextureSet {
|
|||
let height = (size.h * scale_y) as usize;
|
||||
|
||||
Ok(SizedBatch {
|
||||
batch: SpriteBatch::new(image),
|
||||
width,
|
||||
height,
|
||||
scale_x,
|
||||
|
@ -233,14 +230,10 @@ impl TextureSet {
|
|||
}
|
||||
|
||||
pub fn draw_rect(&self, rect: common::Rect, color: [f32; 4], ctx: &mut Context) -> GameResult {
|
||||
let rect = Mesh::new_rectangle(ctx, DrawMode::fill(), rect.into(), color.into())?;
|
||||
graphics::draw(ctx, &rect, DrawParam::new())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn draw_outline_rect(&self, rect: common::Rect, width: f32, color: [f32; 4], ctx: &mut Context) -> GameResult {
|
||||
let rect = Mesh::new_rectangle(ctx, DrawMode::stroke(width), rect.into(), color.into())?;
|
||||
graphics::draw(ctx, &rect, DrawParam::new())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
69
src/ui.rs
69
src/ui.rs
|
@ -2,31 +2,16 @@ use std::time::Instant;
|
|||
|
||||
use imgui::{FontConfig, FontSource};
|
||||
use imgui::sys::*;
|
||||
use imgui_gfx_renderer::{Renderer, Shaders};
|
||||
use imgui_gfx_renderer::gfx::format::DepthStencil;
|
||||
use imgui_gfx_renderer::gfx::format::Rgba8;
|
||||
use imgui_gfx_renderer::gfx::handle::DepthStencilView;
|
||||
use imgui_gfx_renderer::gfx::handle::RenderTargetView;
|
||||
use imgui_gfx_renderer::gfx::memory::Typed;
|
||||
use imgui_winit_support::{HiDpiMode, WinitPlatform};
|
||||
|
||||
use ggez::{Context, GameResult, graphics};
|
||||
use ggez::GameError::RenderError;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::live_debugger::LiveDebugger;
|
||||
use crate::scene::Scene;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
|
||||
mod types {
|
||||
pub type Resources = gfx_device_gl::Resources;
|
||||
}
|
||||
|
||||
pub struct UI {
|
||||
pub imgui: imgui::Context,
|
||||
pub platform: WinitPlatform,
|
||||
pub renderer: Renderer<Rgba8, types::Resources>,
|
||||
pub components: Components,
|
||||
pub main_color: RenderTargetView<types::Resources, Rgba8>,
|
||||
pub main_depth: DepthStencilView<types::Resources, DepthStencil>,
|
||||
last_frame: Instant,
|
||||
}
|
||||
|
||||
|
@ -119,56 +104,18 @@ impl UI {
|
|||
colors[ImGuiCol_NavWindowingDimBg as usize] = [0.20, 0.20, 0.20, 0.20];
|
||||
colors[ImGuiCol_ModalWindowDimBg as usize] = [0.20, 0.20, 0.20, 0.35];
|
||||
|
||||
let mut platform = WinitPlatform::init(&mut imgui);
|
||||
platform.attach_window(imgui.io_mut(), graphics::window(ctx).window(), HiDpiMode::Rounded);
|
||||
|
||||
let (factory, dev, _, depth, color) = graphics::gfx_objects(ctx);
|
||||
let shaders = {
|
||||
let version = dev.get_info().shading_language;
|
||||
if version.is_embedded {
|
||||
if version.major >= 3 {
|
||||
Shaders::GlSlEs300
|
||||
} else {
|
||||
Shaders::GlSlEs100
|
||||
}
|
||||
} else if version.major >= 4 {
|
||||
Shaders::GlSl400
|
||||
} else if version.major >= 3 {
|
||||
if version.minor >= 2 {
|
||||
Shaders::GlSl150
|
||||
} else {
|
||||
Shaders::GlSl130
|
||||
}
|
||||
} else {
|
||||
Shaders::GlSl110
|
||||
}
|
||||
};
|
||||
let renderer = Renderer::init(&mut imgui, factory, shaders)
|
||||
.map_err(|e| RenderError(e.to_string()))?;
|
||||
|
||||
Ok(Self {
|
||||
imgui,
|
||||
platform,
|
||||
renderer,
|
||||
components: Components {
|
||||
live_debugger: LiveDebugger::new(),
|
||||
},
|
||||
main_color: RenderTargetView::new(color),
|
||||
main_depth: DepthStencilView::new(depth),
|
||||
last_frame: Instant::now(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn handle_events(&mut self, ctx: &mut Context, event: &winit::event::Event<()>) {
|
||||
self.platform.handle_event(self.imgui.io_mut(), graphics::window(ctx).window(), &event);
|
||||
}
|
||||
|
||||
pub fn draw(&mut self, state: &mut SharedGameState, ctx: &mut Context, scene: &mut Box<dyn Scene>) -> GameResult {
|
||||
{
|
||||
/*{
|
||||
let io = self.imgui.io_mut();
|
||||
self.platform.prepare_frame(io, graphics::window(ctx).window())
|
||||
.map_err(|e| RenderError(e.to_string()))?;
|
||||
|
||||
let now = Instant::now();
|
||||
io.update_delta_time(now - self.last_frame);
|
||||
self.last_frame = now;
|
||||
|
@ -177,15 +124,7 @@ impl UI {
|
|||
|
||||
scene.debug_overlay_draw(&mut self.components, state, ctx, &mut ui)?;
|
||||
|
||||
self.platform.prepare_render(&ui, graphics::window(ctx).window());
|
||||
let draw_data = ui.render();
|
||||
let (factory, dev, encoder, _, _) = graphics::gfx_objects(ctx);
|
||||
self.renderer
|
||||
.render(factory, encoder, &mut self.main_color, draw_data)
|
||||
.map_err(|e| RenderError(e.to_string()))?;
|
||||
|
||||
encoder.flush(dev);
|
||||
|
||||
ui.render();*/
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue