mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2024-10-31 19:44:20 +00:00
editor shit
This commit is contained in:
parent
e2afafdfa3
commit
ef84379b62
|
@ -6,7 +6,6 @@ authors = ["Alula"]
|
|||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
#crate-type = ["lib", "cdylib"]
|
||||
crate-type = ["lib"]
|
||||
|
||||
[[bin]]
|
||||
|
@ -57,6 +56,7 @@ case_insensitive_hashmap = "1.0.0"
|
|||
chrono = "0.4"
|
||||
cpal = { git = "https://github.com/doukutsu-rs/cpal.git", rev = "4218ff23242834d36bcdcc0c2e3883985c15b5e0" }
|
||||
directories = "3"
|
||||
downcast = "0.11"
|
||||
funty = "=1.1.0" # https://github.com/bitvecto-rs/bitvec/issues/105
|
||||
glutin = { git = "https://github.com/doukutsu-rs/glutin.git", rev = "8dd457b9adb7dbac7ade337246b6356c784272d9", optional = true, default_features = false, features = ["x11"] }
|
||||
imgui = "0.8.0"
|
||||
|
@ -76,7 +76,7 @@ serde = { version = "1", features = ["derive"] }
|
|||
serde_derive = "1"
|
||||
serde_cbor = { version = "0.11.2", optional = true }
|
||||
serde_json = "1.0"
|
||||
simple_logger = { version = "1.13" }
|
||||
simple_logger = { version = "1.16", features = ["colors", "threads"] }
|
||||
strum = "0.20"
|
||||
strum_macros = "0.20"
|
||||
tokio = { version = "1.12.0", features = ["net"], optional = true }
|
||||
|
|
|
@ -48,12 +48,13 @@ impl Background {
|
|||
BackgroundType::TiledStatic => {
|
||||
graphics::clear(ctx, stage.data.background_color);
|
||||
|
||||
let count_x = state.canvas_size.0 as usize / batch.width() + 1;
|
||||
let count_y = state.canvas_size.1 as usize / batch.height() + 1;
|
||||
let (bg_width, bg_height) = (batch.width() as i32, batch.height() as i32);
|
||||
let count_x = state.canvas_size.0 as i32 / bg_width + 1;
|
||||
let count_y = state.canvas_size.1 as i32 / bg_height + 1;
|
||||
|
||||
for y in 0..count_y {
|
||||
for x in 0..count_x {
|
||||
batch.add((x * batch.width()) as f32, (y * batch.height()) as f32);
|
||||
for y in -1..count_y {
|
||||
for x in -1..count_x {
|
||||
batch.add((x * bg_width) as f32, (y * bg_height) as f32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -69,12 +70,13 @@ impl Background {
|
|||
)
|
||||
};
|
||||
|
||||
let count_x = state.canvas_size.0 as usize / batch.width() + 2;
|
||||
let count_y = state.canvas_size.1 as usize / batch.height() + 2;
|
||||
let (bg_width, bg_height) = (batch.width() as i32, batch.height() as i32);
|
||||
let count_x = state.canvas_size.0 as i32 / bg_width + 2;
|
||||
let count_y = state.canvas_size.1 as i32 / bg_height + 2;
|
||||
|
||||
for y in 0..count_y {
|
||||
for x in 0..count_x {
|
||||
batch.add((x * batch.width()) as f32 - off_x, (y * batch.height()) as f32 - off_y);
|
||||
for y in -1..count_y {
|
||||
for x in -1..count_x {
|
||||
batch.add((x * bg_width) as f32 - off_x, (y * bg_height) as f32 - off_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
285
src/editor/mod.rs
Normal file
285
src/editor/mod.rs
Normal file
|
@ -0,0 +1,285 @@
|
|||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
use imgui::{Image, MouseButton, Window, WindowFlags};
|
||||
|
||||
use crate::common::{Color, Rect};
|
||||
use crate::components::background::Background;
|
||||
use crate::components::tilemap::{TileLayer, Tilemap};
|
||||
use crate::frame::Frame;
|
||||
use crate::stage::{Stage, StageTexturePaths};
|
||||
use crate::{graphics, Context, GameResult, SharedGameState, I_MAG};
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub enum CurrentTool {
|
||||
Move,
|
||||
Brush,
|
||||
Fill,
|
||||
Rectangle,
|
||||
}
|
||||
|
||||
pub struct EditorInstance {
|
||||
pub stage: Stage,
|
||||
pub stage_id: usize,
|
||||
pub frame: Frame,
|
||||
pub background: Background,
|
||||
pub stage_textures: Rc<RefCell<StageTexturePaths>>,
|
||||
pub tilemap: Tilemap,
|
||||
pub zoom: f32,
|
||||
pub current_tile: u8,
|
||||
pub mouse_pos: (f32, f32),
|
||||
pub want_capture_mouse: bool,
|
||||
}
|
||||
|
||||
impl EditorInstance {
|
||||
pub fn new(stage_id: usize, stage: Stage) -> EditorInstance {
|
||||
let stage_textures = {
|
||||
let mut textures = StageTexturePaths::new();
|
||||
textures.update(&stage);
|
||||
Rc::new(RefCell::new(textures))
|
||||
};
|
||||
let mut frame = Frame::new();
|
||||
frame.x = -16 * 0x200;
|
||||
frame.y = -48 * 0x200;
|
||||
|
||||
EditorInstance {
|
||||
stage,
|
||||
stage_id,
|
||||
frame,
|
||||
background: Background::new(),
|
||||
stage_textures,
|
||||
tilemap: Tilemap::new(),
|
||||
zoom: 2.0,
|
||||
current_tile: 0,
|
||||
mouse_pos: (0.0, 0.0),
|
||||
want_capture_mouse: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process(&mut self, state: &mut SharedGameState, ctx: &mut Context, ui: &mut imgui::Ui, tool: CurrentTool) {
|
||||
self.frame.prev_x = self.frame.x;
|
||||
self.frame.prev_y = self.frame.y;
|
||||
self.mouse_pos = (ui.io().mouse_pos[0], ui.io().mouse_pos[1]);
|
||||
self.want_capture_mouse = ui.io().want_capture_mouse;
|
||||
|
||||
let mut drag = false;
|
||||
|
||||
match tool {
|
||||
CurrentTool::Move => {
|
||||
if ui.io().want_capture_mouse {
|
||||
return;
|
||||
}
|
||||
|
||||
drag |= ui.is_mouse_down(MouseButton::Left) || ui.is_mouse_down(MouseButton::Right);
|
||||
}
|
||||
CurrentTool::Brush => {
|
||||
self.palette_window(state, ctx, ui);
|
||||
|
||||
if ui.io().want_capture_mouse {
|
||||
return;
|
||||
}
|
||||
|
||||
drag |= ui.is_mouse_down(MouseButton::Right);
|
||||
|
||||
if !drag && ui.is_mouse_down(MouseButton::Left) {
|
||||
let tile_size = self.stage.map.tile_size.as_int();
|
||||
let halft = tile_size / 2;
|
||||
let stage_mouse_x = (self.frame.x / 0x200) + halft + (self.mouse_pos.0 / self.zoom) as i32;
|
||||
let stage_mouse_y = (self.frame.y / 0x200) + halft + (self.mouse_pos.1 / self.zoom) as i32;
|
||||
let tile_x = stage_mouse_x / tile_size;
|
||||
let tile_y = stage_mouse_y / tile_size;
|
||||
|
||||
if tile_x >= 0
|
||||
&& tile_y >= 0
|
||||
&& tile_x < self.stage.map.width as i32
|
||||
&& tile_y < self.stage.map.height as i32
|
||||
{
|
||||
self.stage.change_tile(tile_x as usize, tile_y as usize, self.current_tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
CurrentTool::Fill => {
|
||||
self.palette_window(state, ctx, ui);
|
||||
drag |= ui.is_mouse_down(MouseButton::Right);
|
||||
}
|
||||
CurrentTool::Rectangle => {
|
||||
self.palette_window(state, ctx, ui);
|
||||
drag |= ui.is_mouse_down(MouseButton::Right);
|
||||
}
|
||||
}
|
||||
|
||||
if drag {
|
||||
self.frame.x -= (512.0 * ui.io().mouse_delta[0] as f32 / self.zoom) as i32;
|
||||
self.frame.y -= (512.0 * ui.io().mouse_delta[1] as f32 / self.zoom) as i32;
|
||||
self.frame.prev_x = self.frame.x;
|
||||
self.frame.prev_y = self.frame.y;
|
||||
}
|
||||
}
|
||||
|
||||
fn tile_cursor(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
if self.want_capture_mouse {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let tile_size = self.stage.map.tile_size.as_int();
|
||||
let halft = tile_size / 2;
|
||||
let stage_mouse_x = (self.frame.x / 0x200) + halft + (self.mouse_pos.0 / self.zoom) as i32;
|
||||
let stage_mouse_y = (self.frame.y / 0x200) + halft + (self.mouse_pos.1 / self.zoom) as i32;
|
||||
let tile_x = stage_mouse_x / tile_size;
|
||||
let tile_y = stage_mouse_y / tile_size;
|
||||
let frame_x = self.frame.x as f32 / 512.0;
|
||||
let frame_y = self.frame.y as f32 / 512.0;
|
||||
|
||||
if tile_x < 0 || tile_y < 0 || tile_x >= self.stage.map.width as i32 || tile_y >= self.stage.map.height as i32 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let name = &self.stage_textures.deref().borrow().tileset_fg;
|
||||
|
||||
if let Ok(batch) = state.texture_set.get_or_load_batch(ctx, &state.constants, name) {
|
||||
let tile_size16 = tile_size as u16;
|
||||
let rect = Rect::new_size(
|
||||
(self.current_tile as u16 % 16) * tile_size16,
|
||||
(self.current_tile as u16 / 16) * tile_size16,
|
||||
tile_size16,
|
||||
tile_size16,
|
||||
);
|
||||
|
||||
batch.add_rect_tinted(
|
||||
(tile_x * tile_size - halft) as f32 - frame_x,
|
||||
(tile_y * tile_size - halft) as f32 - frame_y,
|
||||
(255, 255, 255, 192),
|
||||
&rect,
|
||||
);
|
||||
|
||||
batch.draw(ctx)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn palette_window(&mut self, state: &mut SharedGameState, ctx: &mut Context, ui: &imgui::Ui) {
|
||||
Window::new("Palette")
|
||||
.size([260.0, 260.0], imgui::Condition::Always)
|
||||
.position(ui.io().display_size, imgui::Condition::FirstUseEver)
|
||||
.position_pivot([1.0, 1.0])
|
||||
.resizable(false)
|
||||
.build(ui, || {
|
||||
let name = &self.stage_textures.deref().borrow().tileset_fg;
|
||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, name);
|
||||
|
||||
let pos = ui.cursor_screen_pos();
|
||||
let tile_size = self.stage.map.tile_size.as_float();
|
||||
|
||||
if let Ok(batch) = batch {
|
||||
let (scale_x, scale_y) = batch.scale();
|
||||
if let Some(tex) = batch.get_texture() {
|
||||
let (width, height) = tex.dimensions();
|
||||
let (width, height) = (width as f32 / scale_x, height as f32 / scale_y);
|
||||
|
||||
if let Ok(tex_id) = graphics::imgui_texture_id(ctx, tex) {
|
||||
Image::new(tex_id, [width, height]).build(ui);
|
||||
}
|
||||
|
||||
ui.set_cursor_screen_pos(pos);
|
||||
ui.invisible_button("##tiles", [width, height]);
|
||||
}
|
||||
}
|
||||
|
||||
let draw_list = ui.get_window_draw_list();
|
||||
let cur_pos1 = [
|
||||
pos[0].floor() + tile_size * (self.current_tile % 16) as f32,
|
||||
pos[1].floor() + tile_size * (self.current_tile / 16) as f32,
|
||||
];
|
||||
let cur_pos2 = [cur_pos1[0] + tile_size, cur_pos1[1] + tile_size];
|
||||
draw_list.add_rect(cur_pos1, cur_pos2, [1.0, 0.0, 0.0, 1.0]).thickness(2.0).build();
|
||||
|
||||
if ui.is_mouse_down(MouseButton::Left) {
|
||||
let mouse_pos = ui.io().mouse_pos;
|
||||
let x = (mouse_pos[0] - pos[0]) / tile_size;
|
||||
let y = (mouse_pos[1] - pos[1]) / tile_size;
|
||||
|
||||
if x >= 0.0 && x < 16.0 && y >= 0.0 && y < 16.0 {
|
||||
self.current_tile = (y as u8 * 16 + x as u8) as u8;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, tool: CurrentTool) -> GameResult {
|
||||
let old_scale = state.scale;
|
||||
set_scale(state, self.zoom);
|
||||
|
||||
let paths = self.stage_textures.deref().borrow();
|
||||
self.background.draw(state, ctx, &self.frame, &*paths, &self.stage)?;
|
||||
|
||||
self.tilemap.draw(state, ctx, &self.frame, TileLayer::Background, &*paths, &self.stage)?;
|
||||
self.tilemap.draw(state, ctx, &self.frame, TileLayer::Middleground, &*paths, &self.stage)?;
|
||||
self.tilemap.draw(state, ctx, &self.frame, TileLayer::Foreground, &*paths, &self.stage)?;
|
||||
self.tilemap.draw(state, ctx, &self.frame, TileLayer::Snack, &*paths, &self.stage)?;
|
||||
|
||||
self.draw_black_bars(state, ctx)?;
|
||||
|
||||
match tool {
|
||||
CurrentTool::Move => (),
|
||||
CurrentTool::Brush | CurrentTool::Fill | CurrentTool::Rectangle => {
|
||||
self.tile_cursor(state, ctx)?;
|
||||
}
|
||||
}
|
||||
|
||||
set_scale(state, old_scale);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_black_bars(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
let color = Color::from_rgba(0, 0, 0, 128);
|
||||
let (x, y) = self.frame.xy_interpolated(state.frame_time);
|
||||
let (x, y) = (x * state.scale, y * state.scale);
|
||||
let canvas_w_scaled = state.canvas_size.0 as f32 * state.scale;
|
||||
let canvas_h_scaled = state.canvas_size.1 as f32 * state.scale;
|
||||
let level_width = (self.stage.map.width as f32 - 1.0) * self.stage.map.tile_size.as_float();
|
||||
let level_height = (self.stage.map.height as f32 - 1.0) * self.stage.map.tile_size.as_float();
|
||||
let left_side = -x;
|
||||
let right_side = -x + level_width * state.scale;
|
||||
let upper_side = -y;
|
||||
let lower_side = -y + level_height * state.scale;
|
||||
|
||||
if left_side > 0.0 {
|
||||
let rect = Rect::new(0, upper_side as isize, left_side as isize, lower_side as isize);
|
||||
graphics::draw_rect(ctx, rect, color)?;
|
||||
}
|
||||
|
||||
if right_side < canvas_w_scaled {
|
||||
let rect = Rect::new(
|
||||
right_side as isize,
|
||||
upper_side as isize,
|
||||
(state.canvas_size.0 * state.scale) as isize,
|
||||
lower_side as isize,
|
||||
);
|
||||
graphics::draw_rect(ctx, rect, color)?;
|
||||
}
|
||||
|
||||
if upper_side > 0.0 {
|
||||
let rect = Rect::new(0, 0, canvas_w_scaled as isize, upper_side as isize);
|
||||
graphics::draw_rect(ctx, rect, color)?;
|
||||
}
|
||||
|
||||
if lower_side < canvas_h_scaled {
|
||||
let rect = Rect::new(0, lower_side as isize, canvas_w_scaled as isize, canvas_h_scaled as isize);
|
||||
graphics::draw_rect(ctx, rect, color)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn set_scale(state: &mut SharedGameState, scale: f32) {
|
||||
state.scale = scale;
|
||||
|
||||
unsafe {
|
||||
I_MAG = state.scale;
|
||||
state.canvas_size = (state.screen_size.0 / state.scale, state.screen_size.1 / state.scale);
|
||||
}
|
||||
}
|
13
src/frame.rs
13
src/frame.rs
|
@ -23,6 +23,19 @@ pub struct Frame {
|
|||
}
|
||||
|
||||
impl Frame {
|
||||
pub fn new() -> Frame {
|
||||
Frame {
|
||||
x: 0,
|
||||
y: 0,
|
||||
prev_x: 0,
|
||||
prev_y: 0,
|
||||
update_target: UpdateTarget::Player,
|
||||
target_x: 0,
|
||||
target_y: 0,
|
||||
wait: 16,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn xy_interpolated(&self, frame_time: f64) -> (f32, f32) {
|
||||
if self.prev_x == self.x && self.prev_y == self.y {
|
||||
return (fix9_scale(self.x), fix9_scale(self.y));
|
||||
|
|
23
src/lib.rs
23
src/lib.rs
|
@ -32,15 +32,17 @@ mod builtin_fs;
|
|||
mod caret;
|
||||
mod common;
|
||||
mod components;
|
||||
#[cfg(feature = "editor")]
|
||||
mod editor;
|
||||
mod encoding;
|
||||
mod engine_constants;
|
||||
mod entity;
|
||||
mod frame;
|
||||
mod framework;
|
||||
mod input;
|
||||
mod inventory;
|
||||
#[cfg(feature = "hooks")]
|
||||
mod hooks;
|
||||
mod input;
|
||||
mod inventory;
|
||||
mod live_debugger;
|
||||
mod macros;
|
||||
mod map;
|
||||
|
@ -65,6 +67,7 @@ mod weapon;
|
|||
|
||||
pub struct LaunchOptions {
|
||||
pub server_mode: bool,
|
||||
pub editor: bool,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
|
@ -100,7 +103,8 @@ impl Game {
|
|||
if let Some(scene) = self.scene.as_mut() {
|
||||
let state_ref = unsafe { &mut *self.state.get() };
|
||||
|
||||
let speed = if state_ref.textscript_vm.mode == ScriptMode::Map && state_ref.textscript_vm.flags.cutscene_skip() {
|
||||
let speed =
|
||||
if state_ref.textscript_vm.mode == ScriptMode::Map && state_ref.textscript_vm.flags.cutscene_skip() {
|
||||
4.0 * state_ref.settings.speed
|
||||
} else {
|
||||
1.0 * state_ref.settings.speed
|
||||
|
@ -114,8 +118,7 @@ impl Game {
|
|||
if (speed - 1.0).abs() < 0.01 {
|
||||
self.next_tick += state_ref.settings.timing_mode.get_delta() as u128;
|
||||
} else {
|
||||
self.next_tick +=
|
||||
(state_ref.settings.timing_mode.get_delta() as f64 / speed) as u128;
|
||||
self.next_tick += (state_ref.settings.timing_mode.get_delta() as f64 / speed) as u128;
|
||||
}
|
||||
self.loops += 1;
|
||||
}
|
||||
|
@ -123,8 +126,8 @@ impl Game {
|
|||
if self.loops == 10 {
|
||||
log::warn!("Frame skip is way too high, a long system lag occurred?");
|
||||
self.last_tick = self.start_time.elapsed().as_nanos();
|
||||
self.next_tick = self.last_tick
|
||||
+ (state_ref.settings.timing_mode.get_delta() as f64 / speed) as u128;
|
||||
self.next_tick =
|
||||
self.last_tick + (state_ref.settings.timing_mode.get_delta() as f64 / speed) as u128;
|
||||
self.loops = 0;
|
||||
}
|
||||
|
||||
|
@ -198,7 +201,11 @@ impl Game {
|
|||
}
|
||||
|
||||
pub fn init(options: LaunchOptions) -> GameResult {
|
||||
let _ = simple_logger::init_with_level(log::Level::Info);
|
||||
let _ = simple_logger::SimpleLogger::new()
|
||||
.without_timestamps()
|
||||
.with_colors(true)
|
||||
.with_level(log::Level::Info.to_level_filter())
|
||||
.init();
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let resource_dir = if let Ok(data_dir) = env::var("CAVESTORY_DATA_DIR") {
|
||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -5,13 +5,23 @@ use std::process::exit;
|
|||
fn main() {
|
||||
let args = std::env::args();
|
||||
let mut options = doukutsu_rs::LaunchOptions {
|
||||
server_mode: false
|
||||
server_mode: false,
|
||||
editor: false,
|
||||
};
|
||||
|
||||
for arg in args {
|
||||
if arg == "--server-mode" {
|
||||
options.server_mode = true;
|
||||
}
|
||||
|
||||
if arg == "--editor" {
|
||||
options.editor = true;
|
||||
}
|
||||
}
|
||||
|
||||
if options.server_mode && options.editor {
|
||||
eprintln!("Cannot run in server mode and editor mode at the same time.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let result = doukutsu_rs::init(options);
|
||||
|
|
|
@ -17,6 +17,7 @@ use crate::stage::{PxPackScroll, PxPackStageData, StageData};
|
|||
static SUPPORTED_PXM_VERSIONS: [u8; 1] = [0x10];
|
||||
static SUPPORTED_PXE_VERSIONS: [u8; 2] = [0, 0x10];
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Map {
|
||||
pub width: u16,
|
||||
pub height: u16,
|
||||
|
|
|
@ -1,19 +1,134 @@
|
|||
use imgui::MenuItem;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use downcast::Downcast;
|
||||
use imgui::{Condition, MenuItem, TabItem, TabItemFlags, Window};
|
||||
|
||||
use crate::editor::{CurrentTool, EditorInstance};
|
||||
use crate::framework::keyboard;
|
||||
use crate::framework::keyboard::ScanCode;
|
||||
use crate::framework::ui::Components;
|
||||
use crate::scene::game_scene::GameScene;
|
||||
use crate::scene::title_scene::TitleScene;
|
||||
use crate::stage::Stage;
|
||||
use crate::{Context, GameResult, Scene, SharedGameState};
|
||||
|
||||
pub struct EditorScene {}
|
||||
struct ErrorList {
|
||||
errors: Vec<String>,
|
||||
}
|
||||
|
||||
impl ErrorList {
|
||||
fn new() -> ErrorList {
|
||||
Self { errors: Vec::new() }
|
||||
}
|
||||
|
||||
fn try_or_push_error(&mut self, func: impl FnOnce() -> GameResult<()>) {
|
||||
if let Err(err) = func() {
|
||||
self.errors.push(err.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn catch(error_list: Rc<RefCell<ErrorList>>, func: impl FnOnce() -> GameResult<()>) {
|
||||
error_list.borrow_mut().try_or_push_error(func);
|
||||
}
|
||||
|
||||
pub struct EditorScene {
|
||||
stage_list: StageListWindow,
|
||||
error_list: Rc<RefCell<ErrorList>>,
|
||||
instances: Vec<EditorInstance>,
|
||||
subscene: Option<Box<GameScene>>,
|
||||
current_tool: CurrentTool,
|
||||
selected_instance: usize,
|
||||
switch_tab: bool,
|
||||
}
|
||||
|
||||
impl EditorScene {
|
||||
pub fn new() -> Self {
|
||||
EditorScene {}
|
||||
EditorScene {
|
||||
stage_list: StageListWindow::new(),
|
||||
error_list: Rc::new(RefCell::new(ErrorList::new())),
|
||||
instances: Vec::new(),
|
||||
subscene: None,
|
||||
current_tool: CurrentTool::Move,
|
||||
selected_instance: 0,
|
||||
switch_tab: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn exit_editor(&mut self, state: &mut SharedGameState) {
|
||||
state.next_scene = Some(Box::new(TitleScene::new()));
|
||||
}
|
||||
|
||||
fn open_stage(&mut self, state: &mut SharedGameState, ctx: &mut Context, stage_id: usize) {
|
||||
catch(self.error_list.clone(), || {
|
||||
for (idx, instance) in self.instances.iter().enumerate() {
|
||||
if instance.stage_id == stage_id {
|
||||
self.selected_instance = idx;
|
||||
self.switch_tab = true;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(stage) = state.stages.get(stage_id) {
|
||||
let stage = Stage::load(&state.base_path, stage, ctx)?;
|
||||
|
||||
let new_instance = EditorInstance::new(stage_id, stage);
|
||||
self.instances.push(new_instance);
|
||||
self.selected_instance = self.instances.len() - 1;
|
||||
self.switch_tab = true;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn test_stage(&mut self, state: &mut SharedGameState, ctx: &mut Context) {
|
||||
catch(self.error_list.clone(), || {
|
||||
if let Some(instance) = self.instances.get(self.selected_instance) {
|
||||
state.reset();
|
||||
state.textscript_vm.start_script(94);
|
||||
let mut game_scene = GameScene::from_stage(state, ctx, instance.stage.clone(), instance.stage_id)?;
|
||||
game_scene.init(state, ctx)?;
|
||||
game_scene.player1.cond.set_alive(true);
|
||||
game_scene.player1.x = instance.frame.x + (state.canvas_size.0 * 256.0) as i32;
|
||||
game_scene.player1.y = instance.frame.y + (state.canvas_size.1 * 256.0) as i32;
|
||||
state.control_flags.set_control_enabled(true);
|
||||
state.control_flags.set_tick_world(true);
|
||||
state.textscript_vm.suspend = false;
|
||||
self.subscene = Some(Box::new(game_scene));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn perform_actions(&mut self, state: &mut SharedGameState, ctx: &mut Context) {
|
||||
let actions = std::mem::take(&mut self.stage_list.actions);
|
||||
for action in actions.iter() {
|
||||
match action {
|
||||
StageListAction::OpenStage(idx) => self.open_stage(state, ctx, *idx),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait ExtraWidgetsExt {
|
||||
fn tool_button(&self, label: impl AsRef<str>, active: bool) -> bool;
|
||||
}
|
||||
|
||||
impl ExtraWidgetsExt for imgui::Ui<'_> {
|
||||
fn tool_button(&self, label: impl AsRef<str>, active: bool) -> bool {
|
||||
if active {
|
||||
let color = self.style_color(imgui::StyleColor::ButtonActive);
|
||||
let _token1 = self.push_style_color(imgui::StyleColor::Button, color);
|
||||
let _token2 = self.push_style_color(imgui::StyleColor::ButtonHovered, color);
|
||||
let ret = self.button(label);
|
||||
ret
|
||||
} else {
|
||||
self.button(label)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Scene for EditorScene {
|
||||
|
@ -23,7 +138,64 @@ impl Scene for EditorScene {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn draw(&self, _state: &mut SharedGameState, _ctx: &mut Context) -> GameResult {
|
||||
fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
let subscene_ref = &mut self.subscene;
|
||||
if subscene_ref.is_some() {
|
||||
subscene_ref.as_mut().unwrap().tick(state, ctx)?;
|
||||
|
||||
if keyboard::is_key_pressed(ctx, ScanCode::Escape) {
|
||||
*subscene_ref = None;
|
||||
}
|
||||
|
||||
// hijack scene switches
|
||||
let next_scene = std::mem::take(&mut state.next_scene);
|
||||
if let Some(next_scene) = next_scene {
|
||||
*subscene_ref = if let Ok(game_scene) = next_scene.downcast() {
|
||||
let mut game_scene: Box<GameScene> = game_scene;
|
||||
game_scene.init(state, ctx)?;
|
||||
Some(game_scene)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
if subscene_ref.is_none() {
|
||||
state.sound_manager.play_song(0, &state.constants, &state.settings, ctx)?;
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_tick(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
if let Some(scene) = &mut self.subscene {
|
||||
scene.draw_tick(state)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
if let Some(scene) = &self.subscene {
|
||||
scene.draw(state, ctx)?;
|
||||
state.font.draw_text(
|
||||
"Press [ESC] to return.".chars(),
|
||||
4.0,
|
||||
4.0,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
ctx,
|
||||
)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(instance) = self.instances.get(self.selected_instance) {
|
||||
instance.draw(state, ctx, self.current_tool)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -31,12 +203,25 @@ impl Scene for EditorScene {
|
|||
&mut self,
|
||||
_game_ui: &mut Components,
|
||||
state: &mut SharedGameState,
|
||||
_ctx: &mut Context,
|
||||
ctx: &mut Context,
|
||||
ui: &mut imgui::Ui,
|
||||
) -> GameResult {
|
||||
self.perform_actions(state, ctx);
|
||||
|
||||
if let Some(_) = self.subscene {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut menu_bar_size = (0.0, 0.0);
|
||||
if let Some(menu_bar) = ui.begin_main_menu_bar() {
|
||||
let [menu_bar_w, menu_bar_h] = ui.window_size();
|
||||
menu_bar_size = (menu_bar_w, menu_bar_h);
|
||||
|
||||
if let Some(menu) = ui.begin_menu("File") {
|
||||
MenuItem::new("Open stage").shortcut("Ctrl+O").build(ui);
|
||||
if MenuItem::new("Open stage").shortcut("Ctrl+O").build(ui) {
|
||||
self.stage_list.show();
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
if MenuItem::new("Exit editor").build(ui) {
|
||||
|
@ -48,6 +233,117 @@ impl Scene for EditorScene {
|
|||
menu_bar.end();
|
||||
}
|
||||
|
||||
Window::new("Toolbar")
|
||||
.title_bar(false)
|
||||
.resizable(false)
|
||||
.position([0.0, menu_bar_size.1], Condition::Always)
|
||||
.size([menu_bar_size.0, 0.0], Condition::Always)
|
||||
.build(ui, || {
|
||||
if ui.tool_button("Move", self.current_tool == CurrentTool::Move) {
|
||||
self.current_tool = CurrentTool::Move;
|
||||
}
|
||||
ui.same_line();
|
||||
if ui.tool_button("Brush", self.current_tool == CurrentTool::Brush) {
|
||||
self.current_tool = CurrentTool::Brush;
|
||||
}
|
||||
ui.same_line();
|
||||
if ui.tool_button("Fill", self.current_tool == CurrentTool::Fill) {
|
||||
self.current_tool = CurrentTool::Fill;
|
||||
}
|
||||
ui.same_line();
|
||||
if ui.tool_button("Rectangle", self.current_tool == CurrentTool::Rectangle) {
|
||||
self.current_tool = CurrentTool::Rectangle;
|
||||
}
|
||||
|
||||
ui.same_line();
|
||||
ui.text("|");
|
||||
|
||||
ui.same_line();
|
||||
if ui.button("Test Stage") {
|
||||
self.test_stage(state, ctx);
|
||||
}
|
||||
|
||||
if let Some(tab) = ui.tab_bar("Stages") {
|
||||
for (idx, inst) in self.instances.iter().enumerate() {
|
||||
let mut flags = TabItemFlags::NO_CLOSE_WITH_MIDDLE_MOUSE_BUTTON;
|
||||
if self.switch_tab && self.selected_instance == idx {
|
||||
self.switch_tab = false;
|
||||
flags |= TabItemFlags::SET_SELECTED;
|
||||
}
|
||||
|
||||
if let Some(item) = TabItem::new(&inst.stage.data.name).flags(flags).begin(ui) {
|
||||
if !self.switch_tab {
|
||||
self.selected_instance = idx;
|
||||
}
|
||||
item.end();
|
||||
}
|
||||
}
|
||||
|
||||
tab.end();
|
||||
}
|
||||
});
|
||||
|
||||
self.stage_list.action(state, ctx, ui);
|
||||
|
||||
if let Some(instance) = self.instances.get_mut(self.selected_instance) {
|
||||
instance.process(state, ctx, ui, self.current_tool);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct StageListWindow {
|
||||
visible: bool,
|
||||
selected_stage: i32,
|
||||
actions: Vec<StageListAction>,
|
||||
}
|
||||
|
||||
enum StageListAction {
|
||||
OpenStage(usize),
|
||||
}
|
||||
|
||||
impl StageListWindow {
|
||||
fn new() -> Self {
|
||||
StageListWindow { visible: false, selected_stage: 0, actions: Vec::new() }
|
||||
}
|
||||
|
||||
fn show(&mut self) {
|
||||
self.visible = true;
|
||||
}
|
||||
|
||||
fn action(&mut self, state: &mut SharedGameState, ctx: &mut Context, ui: &mut imgui::Ui) {
|
||||
if !self.visible {
|
||||
return;
|
||||
}
|
||||
|
||||
Window::new("Stage list")
|
||||
.resizable(false)
|
||||
.collapsible(false)
|
||||
.position_pivot([0.5, 0.5])
|
||||
.size([300.0, 352.0], Condition::FirstUseEver)
|
||||
.build(ui, || {
|
||||
let mut stages = Vec::with_capacity(state.stages.len());
|
||||
for stage in state.stages.iter() {
|
||||
stages.push(stage.name.as_str());
|
||||
}
|
||||
|
||||
ui.push_item_width(-1.0);
|
||||
ui.list_box("", &mut self.selected_stage, &stages, 14);
|
||||
|
||||
ui.disabled(self.selected_stage < 0, || {
|
||||
if ui.button("Open") {
|
||||
self.actions.push(StageListAction::OpenStage(self.selected_stage as usize));
|
||||
}
|
||||
|
||||
ui.same_line();
|
||||
if ui.button("Edit table entry") {}
|
||||
});
|
||||
|
||||
ui.same_line();
|
||||
if ui.button("Cancel") {
|
||||
self.visible = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::rc::Rc;
|
|||
use log::info;
|
||||
|
||||
use crate::caret::CaretType;
|
||||
use crate::common::{interpolate_fix9_scale, Color, Direction, Rect};
|
||||
use crate::common::{Color, Direction, interpolate_fix9_scale, Rect};
|
||||
use crate::components::background::Background;
|
||||
use crate::components::boss_life_bar::BossLifeBar;
|
||||
use crate::components::credits::Credits;
|
||||
|
@ -21,30 +21,30 @@ use crate::components::tilemap::{TileLayer, Tilemap};
|
|||
use crate::components::water_renderer::WaterRenderer;
|
||||
use crate::entity::GameEntity;
|
||||
use crate::frame::{Frame, UpdateTarget};
|
||||
use crate::framework::{filesystem, graphics};
|
||||
use crate::framework::backend::SpriteBatchCommand;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::graphics::{draw_rect, BlendMode, FilterMode};
|
||||
use crate::framework::graphics::{BlendMode, draw_rect, FilterMode};
|
||||
use crate::framework::ui::Components;
|
||||
use crate::framework::{filesystem, graphics};
|
||||
use crate::input::touch_controls::TouchControlType;
|
||||
use crate::inventory::{Inventory, TakeExperienceResult};
|
||||
use crate::map::WaterParams;
|
||||
use crate::npc::{NPC, NPCLayer};
|
||||
use crate::npc::boss::BossNPC;
|
||||
use crate::npc::list::NPCList;
|
||||
use crate::npc::{NPCLayer, NPC};
|
||||
use crate::physics::{PhysicalEntity, OFFSETS};
|
||||
use crate::physics::{OFFSETS, PhysicalEntity};
|
||||
use crate::player::{Player, TargetPlayer};
|
||||
use crate::rng::XorShift;
|
||||
use crate::scene::title_scene::TitleScene;
|
||||
use crate::scene::Scene;
|
||||
use crate::scene::title_scene::TitleScene;
|
||||
use crate::scripting::tsc::credit_script::CreditScriptVM;
|
||||
use crate::scripting::tsc::text_script::{ScriptMode, TextScriptExecutionState, TextScriptVM};
|
||||
use crate::shared_game_state::{SharedGameState, TileSize};
|
||||
use crate::stage::{BackgroundType, Stage, StageTexturePaths};
|
||||
use crate::texture_set::SpriteBatch;
|
||||
use crate::weapon::bullet::BulletManager;
|
||||
use crate::weapon::{Weapon, WeaponType};
|
||||
use crate::weapon::bullet::BulletManager;
|
||||
|
||||
pub struct GameScene {
|
||||
pub tick: u32,
|
||||
|
@ -96,6 +96,11 @@ impl GameScene {
|
|||
info!("Loading stage {} ({})", id, &state.stages[id].map);
|
||||
let stage = Stage::load(&state.base_path, &state.stages[id], ctx)?;
|
||||
info!("Loaded stage: {}", stage.data.name);
|
||||
|
||||
GameScene::from_stage(state, ctx, stage, id)
|
||||
}
|
||||
|
||||
pub fn from_stage(state: &mut SharedGameState, ctx: &mut Context, stage: Stage, id: usize) -> GameResult<Self> {
|
||||
let mut water_params = WaterParams::new();
|
||||
let mut water_renderer = WaterRenderer::new();
|
||||
|
||||
|
@ -109,23 +114,9 @@ impl GameScene {
|
|||
}
|
||||
|
||||
let stage_textures = {
|
||||
let background = stage.data.background.filename();
|
||||
let (tileset_fg, tileset_mg, tileset_bg) = if let Some(pxpack_data) = stage.data.pxpack_data.as_ref() {
|
||||
let t_fg = ["Stage/", &pxpack_data.tileset_fg].join("");
|
||||
let t_mg = ["Stage/", &pxpack_data.tileset_mg].join("");
|
||||
let t_bg = ["Stage/", &pxpack_data.tileset_bg].join("");
|
||||
|
||||
(t_fg, t_mg, t_bg)
|
||||
} else {
|
||||
let tex_tileset_name = ["Stage/", &stage.data.tileset.filename()].join("");
|
||||
|
||||
(tex_tileset_name.clone(), tex_tileset_name.clone(), tex_tileset_name)
|
||||
};
|
||||
|
||||
let npc1 = ["Npc/", &stage.data.npc1.filename()].join("");
|
||||
let npc2 = ["Npc/", &stage.data.npc2.filename()].join("");
|
||||
|
||||
Rc::new(RefCell::new(StageTexturePaths { background, tileset_fg, tileset_mg, tileset_bg, npc1, npc2 }))
|
||||
let mut textures = StageTexturePaths::new();
|
||||
textures.update(&stage);
|
||||
Rc::new(RefCell::new(textures))
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
|
@ -149,16 +140,7 @@ impl GameScene {
|
|||
tilemap: Tilemap::new(),
|
||||
text_boxes: TextBoxes::new(),
|
||||
fade: Fade::new(),
|
||||
frame: Frame {
|
||||
x: 0,
|
||||
y: 0,
|
||||
prev_x: 0,
|
||||
prev_y: 0,
|
||||
update_target: UpdateTarget::Player,
|
||||
target_x: 0,
|
||||
target_y: 0,
|
||||
wait: 16,
|
||||
},
|
||||
frame: Frame::new(),
|
||||
stage_id: id,
|
||||
npc_list: NPCList::new(),
|
||||
boss: BossNPC::new(),
|
||||
|
|
|
@ -5,9 +5,9 @@ use crate::npc::NPCTable;
|
|||
use crate::scene::no_data_scene::NoDataScene;
|
||||
use crate::scene::Scene;
|
||||
use crate::scripting::tsc::credit_script::CreditScript;
|
||||
use crate::scripting::tsc::text_script::TextScript;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
use crate::stage::StageData;
|
||||
use crate::scripting::tsc::text_script::TextScript;
|
||||
|
||||
pub struct LoadingScene {
|
||||
tick: usize,
|
||||
|
@ -15,9 +15,7 @@ pub struct LoadingScene {
|
|||
|
||||
impl LoadingScene {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tick: 0,
|
||||
}
|
||||
Self { tick: 0 }
|
||||
}
|
||||
|
||||
fn load_stuff(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
|
@ -71,8 +69,10 @@ impl Scene for LoadingScene {
|
|||
fn draw(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
match state.texture_set.get_or_load_batch(ctx, &state.constants, "Loading") {
|
||||
Ok(batch) => {
|
||||
batch.add(((state.canvas_size.0 - batch.width() as f32) / 2.0).floor(),
|
||||
((state.canvas_size.1 - batch.height() as f32) / 2.0).floor());
|
||||
batch.add(
|
||||
((state.canvas_size.0 - batch.width() as f32) / 2.0).floor(),
|
||||
((state.canvas_size.1 - batch.height() as f32) / 2.0).floor(),
|
||||
);
|
||||
batch.draw(ctx)?;
|
||||
}
|
||||
Err(err) => {
|
||||
|
|
|
@ -1,30 +1,48 @@
|
|||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
use crate::framework::ui::Components;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
|
||||
#[cfg(feature = "editor")]
|
||||
pub mod editor_scene;
|
||||
pub mod game_scene;
|
||||
pub mod loading_scene;
|
||||
pub mod no_data_scene;
|
||||
pub mod title_scene;
|
||||
pub mod editor_scene;
|
||||
|
||||
/// Implement this trait on any object that represents an interactive game screen.
|
||||
pub trait Scene {
|
||||
pub trait Scene: downcast::Any {
|
||||
/// Called when the scene is shown.
|
||||
fn init(&mut self, _state: &mut SharedGameState, _ctx: &mut Context) -> GameResult { Ok(()) }
|
||||
fn init(&mut self, _state: &mut SharedGameState, _ctx: &mut Context) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Called at game tick. Perform any game state updates there.
|
||||
fn tick(&mut self, _state: &mut SharedGameState, _ctx: &mut Context) -> GameResult { Ok(()) }
|
||||
fn tick(&mut self, _state: &mut SharedGameState, _ctx: &mut Context) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Called before draws between two ticks to update previous positions used for interpolation.
|
||||
/// DO NOT perform updates of the game state there.
|
||||
fn draw_tick(&mut self, _state: &mut SharedGameState) -> GameResult { Ok(()) }
|
||||
fn draw_tick(&mut self, _state: &mut SharedGameState) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Called during frame rendering operation.
|
||||
fn draw(&self, _state: &mut SharedGameState, _ctx: &mut Context) -> GameResult { Ok(()) }
|
||||
fn draw(&self, _state: &mut SharedGameState, _ctx: &mut Context) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Independent draw meant for debug overlay, that lets you mutate the game state.
|
||||
fn imgui_draw(&mut self, _game_ui: &mut Components, _state: &mut SharedGameState, _ctx: &mut Context, _frame: &mut imgui::Ui) -> GameResult { Ok(()) }
|
||||
fn imgui_draw(
|
||||
&mut self,
|
||||
_game_ui: &mut Components,
|
||||
_state: &mut SharedGameState,
|
||||
_ctx: &mut Context,
|
||||
_frame: &mut imgui::Ui,
|
||||
) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
downcast::impl_downcast!(dyn Scene);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::framework::context::Context;
|
||||
use crate::framework::error::{GameResult, GameError};
|
||||
|
||||
use crate::framework::error::{GameError, GameResult};
|
||||
use crate::scene::Scene;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
|
||||
|
@ -51,15 +50,29 @@ impl Scene for NoDataScene {
|
|||
{
|
||||
let die = "doukutsu-rs internal error";
|
||||
let die_width = state.font.text_width(die.chars().clone(), &state.constants);
|
||||
state.font.draw_colored_text(die.chars(), (state.canvas_size.0 - die_width) / 2.0, 10.0,
|
||||
(255, 100, 100, 255), &state.constants, &mut state.texture_set, ctx)?;
|
||||
state.font.draw_colored_text(
|
||||
die.chars(),
|
||||
(state.canvas_size.0 - die_width) / 2.0,
|
||||
10.0,
|
||||
(255, 100, 100, 255),
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
ctx,
|
||||
)?;
|
||||
}
|
||||
|
||||
{
|
||||
let ftl = "Failed to load game data.";
|
||||
let ftl_width = state.font.text_width(ftl.chars().clone(), &state.constants);
|
||||
state.font.draw_colored_text(ftl.chars(), (state.canvas_size.0 - ftl_width) / 2.0, 30.0,
|
||||
(255, 100, 100, 255), &state.constants, &mut state.texture_set, ctx)?;
|
||||
state.font.draw_colored_text(
|
||||
ftl.chars(),
|
||||
(state.canvas_size.0 - ftl_width) / 2.0,
|
||||
30.0,
|
||||
(255, 100, 100, 255),
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
ctx,
|
||||
)?;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
|
@ -70,24 +83,49 @@ impl Scene for NoDataScene {
|
|||
let ftl2_width = state.font.text_width(ftl2.chars().clone(), &state.constants);
|
||||
let ftl3_width = state.font.text_width(REL_URL.chars().clone(), &state.constants);
|
||||
|
||||
state.font.draw_colored_text(ftl.chars(), (state.canvas_size.0 - ftl_width) / 2.0, 60.0,
|
||||
(255, 255, 0, 255), &state.constants, &mut state.texture_set, ctx)?;
|
||||
state.font.draw_colored_text(
|
||||
ftl.chars(),
|
||||
(state.canvas_size.0 - ftl_width) / 2.0,
|
||||
60.0,
|
||||
(255, 255, 0, 255),
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
ctx,
|
||||
)?;
|
||||
|
||||
state.font.draw_colored_text(
|
||||
ftl2.chars(),
|
||||
(state.canvas_size.0 - ftl2_width) / 2.0,
|
||||
80.0,
|
||||
(255, 255, 0, 255),
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
ctx,
|
||||
)?;
|
||||
|
||||
state.font.draw_colored_text(ftl2.chars(), (state.canvas_size.0 - ftl2_width) / 2.0, 80.0,
|
||||
(255, 255, 0, 255), &state.constants, &mut state.texture_set, ctx)?;
|
||||
|
||||
state.font.draw_colored_text(REL_URL.chars(), (state.canvas_size.0 - ftl3_width) / 2.0, 100.0,
|
||||
(255, 255, 0, 255), &state.constants, &mut state.texture_set, ctx)?;
|
||||
state.font.draw_colored_text(
|
||||
REL_URL.chars(),
|
||||
(state.canvas_size.0 - ftl3_width) / 2.0,
|
||||
100.0,
|
||||
(255, 255, 0, 255),
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
ctx,
|
||||
)?;
|
||||
}
|
||||
|
||||
{
|
||||
let err_width = state.font.text_width(self.err.chars().clone(), &state.constants);
|
||||
state.font.draw_text(self.err.chars(), (state.canvas_size.0 - err_width) / 2.0, 140.0,
|
||||
&state.constants, &mut state.texture_set, ctx)?;
|
||||
state.font.draw_text(
|
||||
self.err.chars(),
|
||||
(state.canvas_size.0 - err_width) / 2.0,
|
||||
140.0,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
ctx,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ 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::settings_menu::SettingsMenu;
|
||||
use crate::menu::{Menu, MenuEntry, MenuSelectionResult};
|
||||
use crate::menu::settings_menu::SettingsMenu;
|
||||
use crate::scene::Scene;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
|
||||
|
|
24
src/stage.rs
24
src/stage.rs
|
@ -1,8 +1,8 @@
|
|||
use std::io::{Cursor, Read};
|
||||
use std::str::from_utf8;
|
||||
|
||||
use byteorder::LE;
|
||||
use byteorder::ReadBytesExt;
|
||||
use byteorder::LE;
|
||||
use log::info;
|
||||
|
||||
use crate::common::Color;
|
||||
|
@ -511,6 +511,7 @@ impl StageData {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Stage {
|
||||
pub map: Map,
|
||||
pub data: StageData,
|
||||
|
@ -609,4 +610,25 @@ impl StageTexturePaths {
|
|||
npc2: "Npc/Npc0".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, stage: &Stage) {
|
||||
self.background = stage.data.background.filename();
|
||||
let (tileset_fg, tileset_mg, tileset_bg) = if let Some(pxpack_data) = stage.data.pxpack_data.as_ref() {
|
||||
let t_fg = ["Stage/", &pxpack_data.tileset_fg].join("");
|
||||
let t_mg = ["Stage/", &pxpack_data.tileset_mg].join("");
|
||||
let t_bg = ["Stage/", &pxpack_data.tileset_bg].join("");
|
||||
|
||||
(t_fg, t_mg, t_bg)
|
||||
} else {
|
||||
let tex_tileset_name = ["Stage/", &stage.data.tileset.filename()].join("");
|
||||
|
||||
(tex_tileset_name.clone(), tex_tileset_name.clone(), tex_tileset_name)
|
||||
};
|
||||
self.tileset_fg = tileset_fg;
|
||||
self.tileset_mg = tileset_mg;
|
||||
self.tileset_bg = tileset_bg;
|
||||
|
||||
self.npc1 = ["Npc/", &stage.data.npc1.filename()].join("");
|
||||
self.npc2 = ["Npc/", &stage.data.npc2.filename()].join("");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,8 @@ pub trait SpriteBatch {
|
|||
fn draw(&mut self, ctx: &mut Context) -> GameResult;
|
||||
|
||||
fn draw_filtered(&mut self, _filter: FilterMode, _ctx: &mut Context) -> GameResult;
|
||||
|
||||
fn get_texture(&self) -> Option<&Box<dyn BackendTexture>>;
|
||||
}
|
||||
|
||||
pub struct DummyBatch;
|
||||
|
@ -136,6 +138,10 @@ impl SpriteBatch for DummyBatch {
|
|||
fn draw_filtered(&mut self, _filter: FilterMode, _ctx: &mut Context) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_texture(&self) -> Option<&Box<dyn BackendTexture>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SubBatch {
|
||||
|
@ -309,6 +315,10 @@ impl SpriteBatch for SubBatch {
|
|||
self.batch.clear();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_texture(&self) -> Option<&Box<dyn BackendTexture>> {
|
||||
Some(&self.batch)
|
||||
}
|
||||
}
|
||||
|
||||
impl SpriteBatch for CombinedBatch {
|
||||
|
@ -395,6 +405,10 @@ impl SpriteBatch for CombinedBatch {
|
|||
fn draw_filtered(&mut self, filter: FilterMode, ctx: &mut Context) -> GameResult {
|
||||
self.main_batch.draw_filtered(filter, ctx)
|
||||
}
|
||||
|
||||
fn get_texture(&self) -> Option<&Box<dyn BackendTexture>> {
|
||||
self.main_batch.get_texture()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextureSet {
|
||||
|
|
Loading…
Reference in a new issue