1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2025-11-29 15:56:53 +00:00

ggez is gone

This commit is contained in:
Alula 2021-02-05 23:47:13 +01:00
parent c3887e31c7
commit 318d94d843
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA
18 changed files with 509 additions and 244 deletions

View file

@ -25,10 +25,10 @@ install:
- cargo -vV
cache:
- '%USERPROFILE%\.cache\sccache -> .appveyor.yml'
- '%USERPROFILE%\.cargo -> .appveyor.yml'
- '%USERPROFILE%\.rustup -> .appveyor.yml'
- 'target -> .appveyor.yml'
- '%USERPROFILE%\.cache\sccache -> Cargo.toml'
- '%USERPROFILE%\.cargo -> Cargo.toml'
- '%USERPROFILE%\.rustup -> Cargo.toml'
- 'target -> Cargo.toml'
#test_script:
# - cargo build --verbose --all

View file

@ -41,7 +41,7 @@ opt-level = 1
[features]
default = ["scripting", "backend-sdl"]
backend-sdl = ["sdl2", "sdl2-sys"]
backend-sdl = ["sdl2", "sdl2-sys", "imgui-sdl2"]
backend-gfx = ["winit", "imgui-gfx-renderer", "imgui-winit-support"]
scripting = ["lua-ffi"]
editor = []
@ -53,9 +53,10 @@ case_insensitive_hashmap = "1.0.0"
chrono = "0.4"
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 }
imgui = "0.6.0"
imgui-gfx-renderer = { version = "0.6.0", optional = true }
imgui-winit-support = { version = "0.6.0", default-features = false, features = ["winit-23"], optional = true }
imgui-sdl2 = { version = "0.13.0", optional = true }
image = { version = "0.22", default-features = false, features = ["png_codec", "pnm", "bmp"] }
itertools = "0.9.0"
lazy_static = "1.4.0"
@ -66,8 +67,8 @@ num-derive = "0.3.2"
num-traits = "0.2.12"
paste = "1.0.0"
pretty_env_logger = "0.4.0"
sdl2 = { version = "0.34.3", optional = true, features = ["unsafe_textures", "bundled", "static-link"] }
sdl2-sys = { version = "0.34", optional = true }
sdl2 = { version = "0.34.3", optional = true, features = ["unsafe_textures", "bundled", "gfx"] }
sdl2-sys = { version = "0.34", optional = true, features = ["bundled", "gfx"] }
serde = { version = "1", features = ["derive"] }
serde_derive = "1"
serde_yaml = "0.8"

View file

@ -78,10 +78,10 @@ Vanilla Cave Story does not work yet because some important data files have been
- [ ] Machine Gun
- [ ] Missile Launcher
- [ ] Bubbler
- [ ] Blade
- [x] Blade
- [ ] Super Missile Launcher
- [ ] Nemesis
- [ ] Spur
- [x] Nemesis
- [x] Spur
- [x] Saving and loading game state
- [ ] Support for different game editions
- [ ] Vanilla

View file

@ -1,8 +1,10 @@
use crate::common::{Color, Rect, Point};
use imgui::DrawData;
use crate::common::{Color, Point, Rect};
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::Game;
use crate::framework::graphics::BlendMode;
use crate::Game;
pub trait Backend {
fn create_event_loop(&self) -> GameResult<Box<dyn BackendEventLoop>>;
@ -26,12 +28,21 @@ pub trait BackendRenderer {
fn set_blend_mode(&mut self, blend: BlendMode) -> GameResult;
fn set_render_target(&mut self, texture: Option<&Box<dyn BackendTexture>>) -> GameResult;
fn imgui(&self) -> GameResult<&mut imgui::Context>;
fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult;
fn prepare_frame(&self, ui: &imgui::Ui) -> GameResult;
}
pub trait BackendTexture {
fn dimensions(&self) -> (u16, u16);
fn add(&mut self, command: SpriteBatchCommand);
fn clear(&mut self);
fn draw(&mut self) -> GameResult;
}

View file

@ -1,20 +1,26 @@
use core::mem;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use imgui::{DrawCmd, DrawData, ImString, TextureId, Ui};
use imgui::internal::RawWrapper;
use sdl2::{EventPump, keyboard, pixels, Sdl};
use sdl2::event::{Event, WindowEvent};
use sdl2::gfx::primitives::DrawRenderer;
use sdl2::keyboard::Scancode;
use sdl2::pixels::PixelFormatEnum;
use sdl2::render::{Texture, TextureCreator, WindowCanvas};
use sdl2::surface::Surface;
use sdl2::video::WindowContext;
use crate::common::Color;
use crate::framework::backend::{Backend, BackendEventLoop, BackendRenderer, BackendTexture, SpriteBatchCommand};
use crate::framework::context::Context;
use crate::framework::error::{GameError, GameResult};
use crate::framework::graphics::BlendMode;
use crate::framework::graphics::{BlendMode, imgui_context};
use crate::framework::keyboard::ScanCode;
use crate::framework::ui::init_imgui;
use crate::Game;
pub struct SDL2Backend {
@ -87,14 +93,24 @@ impl BackendEventLoop for SDL2EventLoop {
fn run(&mut self, game: &mut Game, ctx: &mut Context) {
let state = unsafe { &mut *game.state.get() };
let (imgui, imgui_sdl2) = unsafe {
let renderer: &Box<SDL2Renderer> = std::mem::transmute(ctx.renderer.as_ref().unwrap());
(&mut *renderer.imgui.as_ptr(), &mut *renderer.imgui_event.as_ptr())
};
{
let (width, height) = self.refs.borrow().canvas.window().size();
ctx.screen_size = (width.max(1) as f32, height.max(1) as f32);
imgui.io_mut().display_size = [ctx.screen_size.0, ctx.screen_size.1];
let _ = state.handle_resize(ctx);
}
loop {
for event in self.event_pump.poll_iter() {
imgui_sdl2.handle_event(imgui, &event);
match event {
Event::Quit { .. } => {
state.shutdown();
@ -105,6 +121,12 @@ impl BackendEventLoop for SDL2EventLoop {
WindowEvent::Hidden => {}
WindowEvent::SizeChanged(width, height) => {
ctx.screen_size = (width.max(1) as f32, height.max(1) as f32);
if let Some(renderer) = ctx.renderer.as_ref() {
if let Ok(imgui) = renderer.imgui() {
imgui.io_mut().display_size = [ctx.screen_size.0, ctx.screen_size.1];
}
}
state.handle_resize(ctx);
}
_ => {}
@ -145,6 +167,7 @@ impl BackendEventLoop for SDL2EventLoop {
state.frame_time = 0.0;
}
imgui_sdl2.prepare_frame(imgui.io_mut(), self.refs.borrow().canvas.window(), &self.event_pump.mouse_state());
game.draw(ctx).unwrap();
}
}
@ -156,12 +179,62 @@ impl BackendEventLoop for SDL2EventLoop {
struct SDL2Renderer {
refs: Rc<RefCell<SDL2Context>>,
imgui: Rc<RefCell<imgui::Context>>,
imgui_event: Rc<RefCell<imgui_sdl2::ImguiSdl2>>,
imgui_textures: HashMap<TextureId, SDL2Texture>,
}
impl SDL2Renderer {
#[allow(clippy::new_ret_no_self)]
pub fn new(refs: Rc<RefCell<SDL2Context>>) -> GameResult<Box<dyn BackendRenderer>> {
let mut imgui = init_imgui()?;
let mut imgui_textures = HashMap::new();
imgui.set_renderer_name(ImString::new("SDL2Renderer"));
{
let mut refs = refs.clone();
let mut fonts = imgui.fonts();
let id = fonts.tex_id;
let font_tex = fonts.build_rgba32_texture();
let mut texture = refs.borrow_mut().texture_creator
.create_texture_streaming(PixelFormatEnum::RGBA32, font_tex.width, font_tex.height)
.map_err(|e| GameError::RenderError(e.to_string()))?;
texture.set_blend_mode(sdl2::render::BlendMode::Blend);
texture.with_lock(None, |buffer: &mut [u8], pitch: usize| {
for y in 0..(font_tex.height as usize) {
for x in 0..(font_tex.width as usize) {
let offset = y * pitch + x * 4;
let data_offset = (y * font_tex.width as usize + x) * 4;
buffer[offset] = font_tex.data[data_offset];
buffer[offset + 1] = font_tex.data[data_offset + 1];
buffer[offset + 2] = font_tex.data[data_offset + 2];
buffer[offset + 3] = font_tex.data[data_offset + 3];
}
}
}).map_err(|e| GameError::RenderError(e.to_string()))?;
imgui_textures.insert(id, SDL2Texture {
refs: refs.clone(),
texture: Some(texture),
width: font_tex.width as u16,
height: font_tex.height as u16,
commands: vec![],
});
}
let imgui_sdl2 = unsafe {
let refs = &mut *refs.as_ptr();
imgui_sdl2::ImguiSdl2::new(&mut imgui, refs.canvas.window())
};
Ok(Box::new(SDL2Renderer {
refs,
imgui: Rc::new(RefCell::new(imgui)),
imgui_event: Rc::new(RefCell::new(imgui_sdl2)),
imgui_textures,
}))
}
}
@ -179,6 +252,14 @@ unsafe fn set_raw_target(renderer: *mut sdl2::sys::SDL_Renderer, raw_texture: *m
}
}
fn min3(x: f32, y: f32, z: f32) -> f32 {
if x < y && x < z { x } else if y < z { y } else { z }
}
fn max3(x: f32, y: f32, z: f32) -> f32 {
if x > y && x > z { x } else if y > z { y } else { z }
}
impl BackendRenderer for SDL2Renderer {
fn clear(&mut self, color: Color) {
let mut refs = self.refs.borrow_mut();
@ -275,8 +356,137 @@ impl BackendRenderer for SDL2Renderer {
Ok(())
}
fn imgui(&self) -> GameResult<&mut imgui::Context> {
unsafe {
Ok(&mut *self.imgui.as_ptr())
}
}
fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult {
let mut refs = self.refs.borrow_mut();
for draw_list in draw_data.draw_lists() {
for cmd in draw_list.commands() {
match cmd {
DrawCmd::Elements { count, cmd_params } => {
refs.canvas.set_clip_rect(Some(sdl2::rect::Rect::new(
cmd_params.clip_rect[0] as i32,
cmd_params.clip_rect[1] as i32,
(cmd_params.clip_rect[2] - cmd_params.clip_rect[0]) as u32,
(cmd_params.clip_rect[3] - cmd_params.clip_rect[1]) as u32,
)));
let idx_buffer = draw_list.idx_buffer();
let mut vert_x = [0i16; 6];
let mut vert_y = [0i16; 6];
let mut min = [0f32; 2];
let mut max = [0f32; 2];
let mut tex_pos = [0f32; 4];
let mut is_rect = false;
for i in (0..count).step_by(3) {
if is_rect {
is_rect = false;
continue;
}
let v1 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i] as usize];
let v2 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 1] as usize];
let v3 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 2] as usize];
vert_x[0] = (v1.pos[0] - 0.5) as i16;
vert_y[0] = (v1.pos[1] - 0.5) as i16;
vert_x[1] = (v2.pos[0] - 0.5) as i16;
vert_y[1] = (v2.pos[1] - 0.5) as i16;
vert_x[2] = (v3.pos[0] - 0.5) as i16;
vert_y[2] = (v3.pos[1] - 0.5) as i16;
#[allow(clippy::float_cmp)]
if i < count - 3 {
let v4 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 3] as usize];
let v5 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 4] as usize];
let v6 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 5] as usize];
min[0] = min3(v1.pos[0], v2.pos[0], v3.pos[0]);
min[1] = min3(v1.pos[1], v2.pos[1], v3.pos[1]);
max[0] = max3(v1.pos[0], v2.pos[0], v3.pos[0]);
max[1] = max3(v1.pos[1], v2.pos[1], v3.pos[1]);
is_rect = (v1.pos[0] == min[0] || v1.pos[0] == max[0]) &&
(v1.pos[1] == min[1] || v1.pos[1] == max[1]) &&
(v2.pos[0] == min[0] || v2.pos[0] == max[0]) &&
(v2.pos[1] == min[1] || v2.pos[1] == max[1]) &&
(v3.pos[0] == min[0] || v3.pos[0] == max[0]) &&
(v3.pos[1] == min[1] || v3.pos[1] == max[1]) &&
(v4.pos[0] == min[0] || v4.pos[0] == max[0]) &&
(v4.pos[1] == min[1] || v4.pos[1] == max[1]) &&
(v5.pos[0] == min[0] || v5.pos[0] == max[0]) &&
(v5.pos[1] == min[1] || v5.pos[1] == max[1]) &&
(v6.pos[0] == min[0] || v6.pos[0] == max[0]) &&
(v6.pos[1] == min[1] || v6.pos[1] == max[1]);
if is_rect {
tex_pos[0] = min3(v1.uv[0], v2.uv[0], v3.uv[0]);
tex_pos[1] = min3(v1.uv[1], v2.uv[1], v3.uv[1]);
tex_pos[2] = max3(v1.uv[0], v2.uv[0], v3.uv[0]);
tex_pos[3] = max3(v1.uv[1], v2.uv[1], v3.uv[1]);
}
}
if let Some(surf) = self.imgui_textures.get_mut(&cmd_params.texture_id) {
unsafe {
if is_rect {
let src = sdl2::rect::Rect::new((tex_pos[0] * surf.width as f32) as i32,
(tex_pos[1] * surf.height as f32) as i32,
((tex_pos[2] - tex_pos[0]) * surf.width as f32) as u32,
((tex_pos[3] - tex_pos[1]) * surf.height as f32) as u32);
let dest = sdl2::rect::Rect::new(min[0] as i32,
min[1] as i32,
(max[0] - min[0]) as u32,
(max[1] - min[1]) as u32);
let tex = surf.texture.as_mut().unwrap();
tex.set_color_mod(v1.col[0], v1.col[1], v1.col[2]);
tex.set_alpha_mod(v1.col[3]);
refs.canvas.copy(tex, src, dest);
} else {
sdl2::sys::gfx::primitives::filledPolygonRGBA(
refs.canvas.raw(),
vert_x.as_ptr(),
vert_y.as_ptr(),
3,
v1.col[0],
v1.col[1],
v1.col[2],
v1.col[3],
);
}
}
}
}
refs.canvas.set_clip_rect(None);
}
DrawCmd::ResetRenderState => {}
DrawCmd::RawCallback { callback, raw_cmd } => unsafe {
callback(draw_list.raw(), raw_cmd)
}
}
}
}
Ok(())
}
fn prepare_frame<'ui>(&self, ui: &Ui<'ui>) -> GameResult {
Ok(())
}
}
impl SDL2Renderer {}
struct SDL2Texture {
refs: Rc<RefCell<SDL2Context>>,
texture: Option<Texture>,

View file

@ -75,3 +75,27 @@ pub fn set_blend_mode(ctx: &mut Context, blend: BlendMode) -> GameResult {
Err(GameError::RenderError("Rendering backend hasn't been initialized yet.".to_string()))
}
pub fn imgui_context(ctx: &Context) -> GameResult<&mut imgui::Context> {
if let Some(renderer) = ctx.renderer.as_ref() {
return renderer.imgui();
}
Err(GameError::RenderError("Rendering backend hasn't been initialized yet.".to_string()))
}
pub fn render_imgui(ctx: &mut Context, draw_data: &imgui::DrawData) -> GameResult {
if let Some(renderer) = ctx.renderer.as_mut() {
return renderer.render_imgui(draw_data);
}
Err(GameError::RenderError("Rendering backend hasn't been initialized yet.".to_string()))
}
pub fn imgui_prepare_frame(ctx: &Context, ui: &imgui::Ui) -> GameResult {
if let Some(renderer) = ctx.renderer.as_ref() {
return renderer.prepare_frame(ui);
}
Err(GameError::RenderError("Rendering backend hasn't been initialized yet.".to_string()))
}

View file

@ -1,9 +1,10 @@
pub mod backend;
pub mod backend_sdl2;
pub mod backend_null;
pub mod context;
pub mod error;
pub mod filesystem;
pub mod vfs;
pub mod graphics;
pub mod keyboard;
pub mod backend_null;
pub mod ui;
pub mod vfs;

136
src/framework/ui.rs Normal file
View file

@ -0,0 +1,136 @@
use std::time::Instant;
use imgui::{FontConfig, FontSource};
use imgui::sys::*;
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::graphics::{imgui_context, render_imgui};
use crate::live_debugger::LiveDebugger;
use crate::scene::Scene;
use crate::shared_game_state::SharedGameState;
pub struct UI {
pub components: Components,
last_frame: Instant,
}
pub struct Components {
pub live_debugger: LiveDebugger,
}
pub fn init_imgui() -> GameResult<imgui::Context> {
let mut imgui = imgui::Context::create();
imgui.set_ini_filename(None);
imgui.fonts().add_font(&[
FontSource::DefaultFontData {
config: Some(FontConfig::default()),
},
]);
imgui.style_mut().window_padding = [4.0, 6.0];
imgui.style_mut().frame_padding = [8.0, 6.0];
imgui.style_mut().item_spacing = [8.0, 6.0];
imgui.style_mut().item_inner_spacing = [8.0, 6.0];
imgui.style_mut().indent_spacing = 20.0;
imgui.style_mut().scrollbar_size = 20.0;
imgui.style_mut().grab_min_size = 8.0;
imgui.style_mut().window_border_size = 0.0;
imgui.style_mut().child_border_size = 0.0;
imgui.style_mut().popup_border_size = 0.0;
imgui.style_mut().frame_border_size = 0.0;
imgui.style_mut().tab_border_size = 0.0;
imgui.style_mut().window_rounding = 0.0;
imgui.style_mut().child_rounding = 0.0;
imgui.style_mut().frame_rounding = 0.0;
imgui.style_mut().popup_rounding = 0.0;
imgui.style_mut().scrollbar_rounding = 0.0;
imgui.style_mut().grab_rounding = 0.0;
imgui.style_mut().tab_rounding = 0.0;
imgui.style_mut().window_title_align = [0.50, 0.50];
imgui.style_mut().window_rounding = 0.0;
let colors = &mut imgui.style_mut().colors;
colors[ImGuiCol_Text as usize] = [0.90, 0.90, 0.90, 1.00];
colors[ImGuiCol_TextDisabled as usize] = [0.50, 0.50, 0.50, 1.00];
colors[ImGuiCol_WindowBg as usize] = [0.05, 0.05, 0.05, 0.60];
colors[ImGuiCol_ChildBg as usize] = [0.05, 0.05, 0.05, 0.60];
colors[ImGuiCol_PopupBg as usize] = [0.00, 0.00, 0.00, 0.60];
colors[ImGuiCol_Border as usize] = [0.40, 0.40, 0.40, 1.00];
colors[ImGuiCol_BorderShadow as usize] = [1.00, 1.00, 1.00, 0.00];
colors[ImGuiCol_FrameBg as usize] = [0.00, 0.00, 0.00, 0.60];
colors[ImGuiCol_FrameBgHovered as usize] = [0.84, 0.37, 0.00, 0.20];
colors[ImGuiCol_FrameBgActive as usize] = [0.84, 0.37, 0.00, 1.00];
colors[ImGuiCol_TitleBg as usize] = [0.06, 0.06, 0.06, 1.00];
colors[ImGuiCol_TitleBgActive as usize] = [0.00, 0.00, 0.00, 1.00];
colors[ImGuiCol_TitleBgCollapsed as usize] = [0.06, 0.06, 0.06, 0.40];
colors[ImGuiCol_MenuBarBg as usize] = [0.14, 0.14, 0.14, 1.00];
colors[ImGuiCol_ScrollbarBg as usize] = [0.14, 0.14, 0.14, 0.40];
colors[ImGuiCol_ScrollbarGrab as usize] = [0.31, 0.31, 0.31, 0.30];
colors[ImGuiCol_ScrollbarGrabHovered as usize] = [1.00, 1.00, 1.00, 0.30];
colors[ImGuiCol_ScrollbarGrabActive as usize] = [1.00, 1.00, 1.00, 0.50];
colors[ImGuiCol_CheckMark as usize] = [0.90, 0.90, 0.90, 1.00];
colors[ImGuiCol_SliderGrab as usize] = [0.31, 0.31, 0.31, 1.00];
colors[ImGuiCol_SliderGrabActive as usize] = [1.00, 1.00, 1.00, 0.50];
colors[ImGuiCol_Button as usize] = [0.14, 0.14, 0.14, 1.00];
colors[ImGuiCol_ButtonHovered as usize] = [0.84, 0.37, 0.00, 0.20];
colors[ImGuiCol_ButtonActive as usize] = [0.84, 0.37, 0.00, 1.00];
colors[ImGuiCol_Header as usize] = [0.14, 0.14, 0.14, 1.00];
colors[ImGuiCol_HeaderHovered as usize] = [0.84, 0.37, 0.00, 0.20];
colors[ImGuiCol_HeaderActive as usize] = [0.84, 0.37, 0.00, 1.00];
colors[ImGuiCol_Separator as usize] = [0.50, 0.50, 0.43, 0.50];
colors[ImGuiCol_SeparatorHovered as usize] = [0.75, 0.45, 0.10, 0.78];
colors[ImGuiCol_SeparatorActive as usize] = [0.75, 0.45, 0.10, 1.00];
colors[ImGuiCol_ResizeGrip as usize] = [0.98, 0.65, 0.26, 0.25];
colors[ImGuiCol_ResizeGripHovered as usize] = [0.98, 0.65, 0.26, 0.67];
colors[ImGuiCol_ResizeGripActive as usize] = [0.98, 0.65, 0.26, 0.95];
colors[ImGuiCol_Tab as usize] = [0.17, 0.10, 0.04, 0.94];
colors[ImGuiCol_TabHovered as usize] = [0.84, 0.37, 0.00, 0.60];
colors[ImGuiCol_TabActive as usize] = [0.67, 0.30, 0.00, 0.68];
colors[ImGuiCol_TabUnfocused as usize] = [0.06, 0.05, 0.05, 0.69];
colors[ImGuiCol_TabUnfocusedActive as usize] = [0.36, 0.17, 0.03, 0.64];
colors[ImGuiCol_PlotLines as usize] = [0.39, 0.39, 0.39, 1.00];
colors[ImGuiCol_PlotLinesHovered as usize] = [0.35, 0.92, 1.00, 1.00];
colors[ImGuiCol_PlotHistogram as usize] = [0.00, 0.20, 0.90, 1.00];
colors[ImGuiCol_PlotHistogramHovered as usize] = [0.00, 0.40, 1.00, 1.00];
colors[ImGuiCol_TextSelectedBg as usize] = [0.98, 0.65, 0.26, 0.35];
colors[ImGuiCol_DragDropTarget as usize] = [0.00, 0.00, 1.00, 0.90];
colors[ImGuiCol_NavHighlight as usize] = [0.98, 0.65, 0.26, 1.00];
colors[ImGuiCol_NavWindowingHighlight as usize] = [0.00, 0.00, 0.00, 0.70];
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];
Ok(imgui)
}
impl UI {
pub fn new(ctx: &mut Context) -> GameResult<Self> {
Ok(Self {
components: Components {
live_debugger: LiveDebugger::new(),
},
last_frame: Instant::now(),
})
}
pub fn draw(&mut self, state: &mut SharedGameState, ctx: &mut Context, scene: &mut Box<dyn Scene>) -> GameResult {
let ctx2 = unsafe { &mut *(ctx as *const Context as *mut Context)};
let imgui = imgui_context(ctx)?;
let io = imgui.io_mut();
let now = Instant::now();
io.update_delta_time(now - self.last_frame);
self.last_frame = now;
let mut ui = imgui.frame();
scene.debug_overlay_draw(&mut self.components, state, ctx2, &mut ui)?;
let draw_data = ui.render();
render_imgui(ctx2, draw_data)?;
Ok(())
}
}

View file

@ -24,12 +24,12 @@ use crate::framework::error::{GameError, GameResult};
use crate::framework::filesystem::{mount_user_vfs, mount_vfs};
use crate::framework::graphics;
use crate::framework::keyboard::ScanCode;
use crate::framework::ui::UI;
use crate::framework::vfs::PhysicalFS;
use crate::scene::loading_scene::LoadingScene;
use crate::scene::Scene;
use crate::shared_game_state::{SharedGameState, TimingMode};
use crate::texture_set::{G_MAG, I_MAG};
use crate::ui::UI;
mod bmfont;
mod bmfont_renderer;
@ -66,7 +66,6 @@ mod stage;
mod sound;
mod text_script;
mod texture_set;
mod ui;
mod weapon;
pub struct Game {

View file

@ -59,7 +59,7 @@ impl LiveDebugger {
Window::new(im_str!("Debugger"))
.resizable(false)
.collapsed(true, Condition::FirstUseEver)
.collapsed(false, Condition::FirstUseEver)
.position([5.0, 5.0], Condition::FirstUseEver)
.size([400.0, 170.0], Condition::FirstUseEver)
.build(ui, || {

View file

@ -15,6 +15,7 @@ use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::graphics;
use crate::framework::graphics::{BlendMode, FilterMode};
use crate::framework::ui::Components;
use crate::input::touch_controls::TouchControlType;
use crate::inventory::{Inventory, TakeExperienceResult};
use crate::npc::boss::BossNPC;
@ -29,7 +30,6 @@ use crate::shared_game_state::{Season, SharedGameState};
use crate::stage::{BackgroundType, Stage};
use crate::text_script::{ConfirmSelection, ScriptMode, TextScriptExecutionState, TextScriptVM};
use crate::texture_set::SizedBatch;
use crate::ui::Components;
use crate::weapon::WeaponType;
pub struct GameScene {
@ -636,7 +636,7 @@ impl GameScene {
}
graphics::set_blend_mode(ctx, BlendMode::Multiply)?;
graphics::set_render_target(ctx, None);
graphics::set_render_target(ctx, None)?;
let rect = Rect { left: 0.0, top: 0.0, right: state.screen_size.0, bottom: state.screen_size.1 };
canvas.clear();

View file

@ -2,7 +2,7 @@ use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::shared_game_state::SharedGameState;
use crate::ui::Components;
use crate::framework::ui::Components;
pub mod game_scene;
pub mod loading_scene;

View file

@ -8,7 +8,7 @@ use num_traits::clamp;
use crate::engine_constants::EngineConstants;
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::error::{GameResult, GameError};
use crate::framework::filesystem;
use crate::sound::organya::Song;
use crate::sound::pixtone::PixTonePlayback;
@ -16,6 +16,7 @@ use crate::sound::playback::{PlaybackEngine, SavedPlaybackState};
use crate::sound::wave_bank::SoundBank;
use crate::str;
use crate::framework::error::GameError::{AudioError, ResourceLoadError, InvalidValue};
use std::io::Error;
mod wave_bank;
mod organya;
@ -128,13 +129,19 @@ impl SoundManager {
.find(|path| filesystem::exists(ctx, path))
.ok_or_else(|| ResourceLoadError(format!("BGM {:?} does not exist.", song_name)))?;
let org = organya::Song::load_from(filesystem::open(ctx, path)?)?;
log::info!("Playing BGM: {}", song_name);
match filesystem::open(ctx, path).map(|f| organya::Song::load_from(f)) {
Ok(Ok(org)) => {
log::info!("Playing BGM: {} {}", song_id, song_name);
self.prev_song_id = self.current_song_id;
self.current_song_id = song_id;
self.tx.send(PlaybackMessage::SaveState)?;
self.tx.send(PlaybackMessage::PlaySong(Box::new(org)))?;
self.prev_song_id = self.current_song_id;
self.current_song_id = song_id;
self.tx.send(PlaybackMessage::SaveState)?;
self.tx.send(PlaybackMessage::PlaySong(Box::new(org)))?;
}
Ok(Err(err)) | Err(err) => {
log::warn!("Failed to load BGM {}: {}", song_id, err);
}
}
}
Ok(())
}

View file

@ -1,12 +1,18 @@
use std::io;
use byteorder::{LE, ReadBytesExt};
use crate::framework::error::{GameError, GameResult};
#[repr(u8)]
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Version {
// Can't find any files with this signature,
// But apparently these files had no Pi flag.
Beta = b'1',
Main = b'2',
// OrgMaker 2.05 Extended Drums
Extended = b'3'
Extended = b'3',
}
#[derive(Debug, Copy, Clone)]
@ -14,19 +20,19 @@ pub struct LoopRange {
// inclusive
pub start: i32,
// exclusive
pub end: i32
pub end: i32,
}
#[derive(Debug, Copy, Clone)]
pub struct Display {
pub beats: u8,
pub steps: u8
pub steps: u8,
}
#[derive(Debug, Copy, Clone)]
pub struct Timing {
pub wait: u16,
pub loop_range: LoopRange
pub loop_range: LoopRange,
}
#[derive(Copy, Clone)]
@ -35,12 +41,12 @@ pub struct Instrument {
pub freq: u16,
pub inst: u8,
pub pipi: u8,
pub notes: u16
pub notes: u16,
}
pub struct Track {
pub inst: Instrument,
pub notes: Vec<Note>
pub notes: Vec<Note>,
}
impl Clone for Track {
@ -65,14 +71,14 @@ pub struct Note {
pub key: u8,
pub len: u8,
pub vol: u8,
pub pan: u8
pub pan: u8,
}
#[derive(Debug)]
pub struct Song {
pub version: Version,
pub time: Timing,
pub tracks: [Track; 16]
pub tracks: [Track; 16],
}
impl Clone for Song {
@ -85,85 +91,82 @@ impl Clone for Song {
}
}
use byteorder::{LE, ReadBytesExt};
use std::io;
impl Song {
pub fn empty() -> Song {
Song {
version: Version::Main,
time: Timing { wait: 8, loop_range: LoopRange { start: 0, end: 1 } },
tracks: [
Track { inst: Instrument {freq: 1000, inst: 0, pipi: 0, notes: 0}, notes: vec![] },
Track { inst: Instrument {freq: 1000, inst: 0, pipi: 0, notes: 0}, notes: vec![] },
Track { inst: Instrument {freq: 1000, inst: 0, pipi: 0, notes: 0}, notes: vec![] },
Track { inst: Instrument {freq: 1000, inst: 0, pipi: 0, notes: 0}, notes: vec![] },
Track { inst: Instrument {freq: 1000, inst: 0, pipi: 0, notes: 0}, notes: vec![] },
Track { inst: Instrument {freq: 1000, inst: 0, pipi: 0, notes: 0}, notes: vec![] },
Track { inst: Instrument {freq: 1000, inst: 0, pipi: 0, notes: 0}, notes: vec![] },
Track { inst: Instrument {freq: 1000, inst: 0, pipi: 0, notes: 0}, notes: vec![] },
Track { inst: Instrument {freq: 1000, inst: 0, pipi: 0, notes: 0}, notes: vec![] },
Track { inst: Instrument {freq: 1000, inst: 0, pipi: 0, notes: 0}, notes: vec![] },
Track { inst: Instrument {freq: 1000, inst: 0, pipi: 0, notes: 0}, notes: vec![] },
Track { inst: Instrument {freq: 1000, inst: 0, pipi: 0, notes: 0}, notes: vec![] },
Track { inst: Instrument {freq: 1000, inst: 0, pipi: 0, notes: 0}, notes: vec![] },
Track { inst: Instrument {freq: 1000, inst: 0, pipi: 0, notes: 0}, notes: vec![] },
Track { inst: Instrument {freq: 1000, inst: 0, pipi: 0, notes: 0}, notes: vec![] },
Track { inst: Instrument {freq: 1000, inst: 0, pipi: 0, notes: 0}, notes: vec![] },
]
Track { inst: Instrument { freq: 1000, inst: 0, pipi: 0, notes: 0 }, notes: vec![] },
Track { inst: Instrument { freq: 1000, inst: 0, pipi: 0, notes: 0 }, notes: vec![] },
Track { inst: Instrument { freq: 1000, inst: 0, pipi: 0, notes: 0 }, notes: vec![] },
Track { inst: Instrument { freq: 1000, inst: 0, pipi: 0, notes: 0 }, notes: vec![] },
Track { inst: Instrument { freq: 1000, inst: 0, pipi: 0, notes: 0 }, notes: vec![] },
Track { inst: Instrument { freq: 1000, inst: 0, pipi: 0, notes: 0 }, notes: vec![] },
Track { inst: Instrument { freq: 1000, inst: 0, pipi: 0, notes: 0 }, notes: vec![] },
Track { inst: Instrument { freq: 1000, inst: 0, pipi: 0, notes: 0 }, notes: vec![] },
Track { inst: Instrument { freq: 1000, inst: 0, pipi: 0, notes: 0 }, notes: vec![] },
Track { inst: Instrument { freq: 1000, inst: 0, pipi: 0, notes: 0 }, notes: vec![] },
Track { inst: Instrument { freq: 1000, inst: 0, pipi: 0, notes: 0 }, notes: vec![] },
Track { inst: Instrument { freq: 1000, inst: 0, pipi: 0, notes: 0 }, notes: vec![] },
Track { inst: Instrument { freq: 1000, inst: 0, pipi: 0, notes: 0 }, notes: vec![] },
Track { inst: Instrument { freq: 1000, inst: 0, pipi: 0, notes: 0 }, notes: vec![] },
Track { inst: Instrument { freq: 1000, inst: 0, pipi: 0, notes: 0 }, notes: vec![] },
Track { inst: Instrument { freq: 1000, inst: 0, pipi: 0, notes: 0 }, notes: vec![] },
],
}
}
pub fn load_from<R: io::Read>(mut f: R) -> io::Result<Song> {
pub fn load_from<R: io::Read>(mut f: R) -> GameResult<Song> {
let mut magic = [0; 6];
f.read_exact(&mut magic)?;
let version =
let version =
match &magic {
b"Org-01" => Version::Beta,
b"Org-02" => Version::Main,
b"Org-03" => Version::Extended,
_ => return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid magic number"))
_ => return Err(GameError::ResourceLoadError("Invalid magic number".to_string()))
};
let wait = f.read_u16::<LE>()?;
let _bpm = f.read_u8()?;
let _spb = f.read_u8()?;
let wait = f.read_u16::<LE>()?;
let _bpm = f.read_u8()?;
let _spb = f.read_u8()?;
let start = f.read_i32::<LE>()?;
let end = f.read_i32::<LE>()?;
let end = f.read_i32::<LE>()?;
use std::mem::MaybeUninit as Mu;
let mut insts: [Mu<Instrument>; 16] = unsafe {
Mu::uninit().assume_init()
};
for i in insts.iter_mut() {
let freq = f.read_u16::<LE>()?;
let inst = f.read_u8()?;
let pipi = f.read_u8()?;
let freq = f.read_u16::<LE>()?;
let inst = f.read_u8()?;
let pipi = f.read_u8()?;
let notes = f.read_u16::<LE>()?;
*i = Mu::new(Instrument {
freq,
inst,
pipi,
notes
notes,
});
}
let insts: [Instrument; 16] = unsafe {
std::mem::transmute(insts)
};
let mut tracks: [Mu<Track>; 16] = unsafe {
Mu::uninit().assume_init()
};
for (i, t) in tracks.iter_mut().enumerate() {
let count = insts[i].notes as usize;
#[repr(C)]
#[derive(Copy, Clone)]
struct UninitNote {
@ -171,55 +174,55 @@ impl Song {
key: Mu<u8>,
len: Mu<u8>,
vol: Mu<u8>,
pan: Mu<u8>
pan: Mu<u8>,
}
let mut notes: Vec<UninitNote> = unsafe {
vec![Mu::uninit().assume_init(); count]
};
for note in notes.iter_mut() {
note.pos = Mu::new(f.read_i32::<LE>()?);
}
for note in notes.iter_mut() {
note.key = Mu::new(f.read_u8()?);
}
for note in notes.iter_mut() {
note.len = Mu::new(f.read_u8()?);
}
for note in notes.iter_mut() {
note.vol = Mu::new(f.read_u8()?);
}
for note in notes.iter_mut() {
note.pan = Mu::new(f.read_u8()?);
}
*t = Mu::new(Track {
inst: insts[i],
notes: unsafe { std::mem::transmute(notes) }
notes: unsafe { std::mem::transmute(notes) },
});
}
let tracks = unsafe {
std::mem::transmute(tracks)
};
let song = Song {
version,
time: Timing {
wait,
loop_range: LoopRange {
start,
end
}
end,
},
},
tracks
tracks,
};
Ok(song)
}
}

View file

@ -1,6 +1,6 @@
use std::mem::MaybeUninit;
use crate::sound::organya::Song as Organya;
use crate::sound::organya::{Song as Organya, Version};
use crate::sound::stuff::*;
use crate::sound::wav::*;
use crate::sound::wave_bank::SoundBank;
@ -144,8 +144,12 @@ impl PlaybackEngine {
}
}
for (idx, (_track, buf)) in song.tracks[8..].iter().zip(self.track_buffers[128..].iter_mut()).enumerate() {
*buf = RenderBuffer::new(samples.samples[idx].clone());
for (idx, (track, buf)) in song.tracks[8..].iter().zip(self.track_buffers[128..].iter_mut()).enumerate() {
if self.song.version == Version::Extended {
*buf = RenderBuffer::new(samples.samples[track.inst.inst as usize].clone());
} else {
*buf = RenderBuffer::new(samples.samples[idx].clone());
}
}
self.song = song;

View file

@ -91,12 +91,12 @@ impl SizedBatch {
#[inline(always)]
pub fn add_rect(&mut self, x: f32, y: f32, rect: &common::Rect<u16>) {
self.add_rect_scaled(x, y, self.scale_x, self.scale_y, rect)
self.add_rect_scaled(x, y, 1.0, 1.0, rect)
}
#[inline(always)]
pub fn add_rect_tinted(&mut self, x: f32, y: f32, color: (u8, u8, u8, u8), rect: &common::Rect<u16>) {
self.add_rect_scaled_tinted(x, y, color, self.scale_x, self.scale_y, rect)
self.add_rect_scaled_tinted(x, y, color, 1.0, 1.0, rect)
}
pub fn add_rect_scaled(&mut self, mut x: f32, mut y: f32, mut scale_x: f32, mut scale_y: f32, rect: &common::Rect<u16>) {
@ -112,16 +112,16 @@ impl SizedBatch {
self.batch.add(SpriteBatchCommand::DrawRect(
Rect {
left: rect.left as f32,
top: rect.top as f32,
right: rect.right as f32,
bottom: rect.bottom as f32,
left: rect.left as f32 / self.scale_x,
top: rect.top as f32 / self.scale_y,
right: rect.right as f32 / self.scale_x,
bottom: rect.bottom as f32 / self.scale_y,
},
Rect {
left: x * mag * scale_x,
top: y * mag * scale_y,
right: (x + rect.width() as f32) * mag * scale_x,
bottom: (y + rect.height() as f32) * mag * scale_y,
left: x * mag,
top: y * mag,
right: (x + rect.width() as f32 * scale_x) * mag,
bottom: (y + rect.height() as f32 * scale_y) * mag,
},
));
}
@ -160,7 +160,7 @@ impl SizedBatch {
}
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()?;
self.batch.clear();
Ok(())
@ -241,7 +241,6 @@ impl TextureSet {
let scale = orig_dimensions.0 as f32 / size.0 as f32;
let width = (size.0 as f32 * scale) as usize;
let height = (size.1 as f32 * scale) as usize;
println!("{} {} {} {}", size.0, size.1, width, height);
Ok(SizedBatch {
batch,

130
src/ui.rs
View file

@ -1,130 +0,0 @@
use std::time::Instant;
use imgui::{FontConfig, FontSource};
use imgui::sys::*;
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;
pub struct UI {
pub imgui: imgui::Context,
pub components: Components,
last_frame: Instant,
}
pub struct Components {
pub live_debugger: LiveDebugger,
}
impl UI {
pub fn new(ctx: &mut Context) -> GameResult<Self> {
let mut imgui = imgui::Context::create();
imgui.set_ini_filename(None);
imgui.fonts().add_font(&[
FontSource::DefaultFontData {
config: Some(FontConfig::default()),
},
]);
imgui.style_mut().window_padding = [4.0, 6.0];
imgui.style_mut().frame_padding = [8.0, 6.0];
imgui.style_mut().item_spacing = [8.0, 6.0];
imgui.style_mut().item_inner_spacing = [8.0, 6.0];
imgui.style_mut().indent_spacing = 20.0;
imgui.style_mut().scrollbar_size = 20.0;
imgui.style_mut().grab_min_size = 5.0;
imgui.style_mut().window_border_size = 0.0;
imgui.style_mut().child_border_size = 1.0;
imgui.style_mut().popup_border_size = 1.0;
imgui.style_mut().frame_border_size = 1.0;
imgui.style_mut().tab_border_size = 0.0;
imgui.style_mut().window_rounding = 0.0;
imgui.style_mut().child_rounding = 0.0;
imgui.style_mut().frame_rounding = 0.0;
imgui.style_mut().popup_rounding = 0.0;
imgui.style_mut().scrollbar_rounding = 0.0;
imgui.style_mut().grab_rounding = 0.0;
imgui.style_mut().tab_rounding = 0.0;
imgui.style_mut().window_title_align = [0.50, 0.50];
imgui.style_mut().window_rounding = 0.0;
let colors = &mut imgui.style_mut().colors;
colors[ImGuiCol_Text as usize] = [0.90, 0.90, 0.90, 1.00];
colors[ImGuiCol_TextDisabled as usize] = [0.50, 0.50, 0.50, 1.00];
colors[ImGuiCol_WindowBg as usize] = [0.05, 0.05, 0.05, 0.60];
colors[ImGuiCol_ChildBg as usize] = [0.05, 0.05, 0.05, 0.60];
colors[ImGuiCol_PopupBg as usize] = [0.00, 0.00, 0.00, 0.60];
colors[ImGuiCol_Border as usize] = [0.40, 0.40, 0.40, 1.00];
colors[ImGuiCol_BorderShadow as usize] = [1.00, 1.00, 1.00, 0.00];
colors[ImGuiCol_FrameBg as usize] = [0.00, 0.00, 0.00, 0.60];
colors[ImGuiCol_FrameBgHovered as usize] = [0.84, 0.37, 0.00, 0.20];
colors[ImGuiCol_FrameBgActive as usize] = [0.84, 0.37, 0.00, 1.00];
colors[ImGuiCol_TitleBg as usize] = [0.06, 0.06, 0.06, 1.00];
colors[ImGuiCol_TitleBgActive as usize] = [0.00, 0.00, 0.00, 1.00];
colors[ImGuiCol_TitleBgCollapsed as usize] = [0.06, 0.06, 0.06, 0.40];
colors[ImGuiCol_MenuBarBg as usize] = [0.14, 0.14, 0.14, 1.00];
colors[ImGuiCol_ScrollbarBg as usize] = [0.14, 0.14, 0.14, 0.40];
colors[ImGuiCol_ScrollbarGrab as usize] = [0.31, 0.31, 0.31, 0.30];
colors[ImGuiCol_ScrollbarGrabHovered as usize] = [1.00, 1.00, 1.00, 0.30];
colors[ImGuiCol_ScrollbarGrabActive as usize] = [1.00, 1.00, 1.00, 0.50];
colors[ImGuiCol_CheckMark as usize] = [0.90, 0.90, 0.90, 1.00];
colors[ImGuiCol_SliderGrab as usize] = [0.31, 0.31, 0.31, 1.00];
colors[ImGuiCol_SliderGrabActive as usize] = [1.00, 1.00, 1.00, 0.50];
colors[ImGuiCol_Button as usize] = [0.14, 0.14, 0.14, 1.00];
colors[ImGuiCol_ButtonHovered as usize] = [0.84, 0.37, 0.00, 0.20];
colors[ImGuiCol_ButtonActive as usize] = [0.84, 0.37, 0.00, 1.00];
colors[ImGuiCol_Header as usize] = [0.14, 0.14, 0.14, 1.00];
colors[ImGuiCol_HeaderHovered as usize] = [0.84, 0.37, 0.00, 0.20];
colors[ImGuiCol_HeaderActive as usize] = [0.84, 0.37, 0.00, 1.00];
colors[ImGuiCol_Separator as usize] = [0.50, 0.50, 0.43, 0.50];
colors[ImGuiCol_SeparatorHovered as usize] = [0.75, 0.45, 0.10, 0.78];
colors[ImGuiCol_SeparatorActive as usize] = [0.75, 0.45, 0.10, 1.00];
colors[ImGuiCol_ResizeGrip as usize] = [0.98, 0.65, 0.26, 0.25];
colors[ImGuiCol_ResizeGripHovered as usize] = [0.98, 0.65, 0.26, 0.67];
colors[ImGuiCol_ResizeGripActive as usize] = [0.98, 0.65, 0.26, 0.95];
colors[ImGuiCol_Tab as usize] = [0.17, 0.10, 0.04, 0.94];
colors[ImGuiCol_TabHovered as usize] = [0.84, 0.37, 0.00, 0.60];
colors[ImGuiCol_TabActive as usize] = [0.67, 0.30, 0.00, 0.68];
colors[ImGuiCol_TabUnfocused as usize] = [0.06, 0.05, 0.05, 0.69];
colors[ImGuiCol_TabUnfocusedActive as usize] = [0.36, 0.17, 0.03, 0.64];
colors[ImGuiCol_PlotLines as usize] = [0.39, 0.39, 0.39, 1.00];
colors[ImGuiCol_PlotLinesHovered as usize] = [0.35, 0.92, 1.00, 1.00];
colors[ImGuiCol_PlotHistogram as usize] = [0.00, 0.20, 0.90, 1.00];
colors[ImGuiCol_PlotHistogramHovered as usize] = [0.00, 0.40, 1.00, 1.00];
colors[ImGuiCol_TextSelectedBg as usize] = [0.98, 0.65, 0.26, 0.35];
colors[ImGuiCol_DragDropTarget as usize] = [0.00, 0.00, 1.00, 0.90];
colors[ImGuiCol_NavHighlight as usize] = [0.98, 0.65, 0.26, 1.00];
colors[ImGuiCol_NavWindowingHighlight as usize] = [0.00, 0.00, 0.00, 0.70];
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];
Ok(Self {
imgui,
components: Components {
live_debugger: LiveDebugger::new(),
},
last_frame: Instant::now(),
})
}
pub fn draw(&mut self, state: &mut SharedGameState, ctx: &mut Context, scene: &mut Box<dyn Scene>) -> GameResult {
/*{
let io = self.imgui.io_mut();
let now = Instant::now();
io.update_delta_time(now - self.last_frame);
self.last_frame = now;
}
let mut ui = self.imgui.frame();
scene.debug_overlay_draw(&mut self.components, state, ctx, &mut ui)?;
ui.render();*/
Ok(())
}
}

View file

@ -443,8 +443,8 @@ impl Weapon {
WeaponLevel::None => unreachable!(),
}
const bullets: [u16; 6] = [44, 45, 46, 47, 48, 49];
if bullet_manager.count_bullets_multi(&bullets, player_id) == 0
const BULLETS: [u16; 6] = [44, 45, 46, 47, 48, 49];
if bullet_manager.count_bullets_multi(&BULLETS, player_id) == 0
&& (player.controller.trigger_shoot() || shoot) {
if !self.consume_ammo(1) {
state.sound_manager.play_sfx(37);