mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2024-09-20 17:18:50 +00:00
Compare commits
5 commits
915eb2ccb6
...
747d915d4b
Author | SHA1 | Date | |
---|---|---|---|
747d915d4b | |||
ca5361cc58 | |||
08f086bfc4 | |||
1f288e2a39 | |||
30f1a0dadf |
|
@ -4,6 +4,7 @@ description = "A re-implementation of Cave Story (Doukutsu Monogatari) engine"
|
||||||
version = "0.101.0"
|
version = "0.101.0"
|
||||||
authors = ["Alula", "dawnDus"]
|
authors = ["Alula", "dawnDus"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
rust-version = "1.65"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["lib"]
|
crate-type = ["lib"]
|
||||||
|
|
|
@ -40,7 +40,9 @@
|
||||||
"title": "Select Difficulty",
|
"title": "Select Difficulty",
|
||||||
"easy": "Easy",
|
"easy": "Easy",
|
||||||
"normal": "Normal",
|
"normal": "Normal",
|
||||||
"hard": "Hard"
|
"hard": "Hard",
|
||||||
|
"difficulty_name": "Difficulty: {difficulty}",
|
||||||
|
"unknown": "(unknown)"
|
||||||
},
|
},
|
||||||
"coop_menu": {
|
"coop_menu": {
|
||||||
"title": "Select Number of Players",
|
"title": "Select Number of Players",
|
||||||
|
|
|
@ -40,7 +40,9 @@
|
||||||
"title": "難易度選択",
|
"title": "難易度選択",
|
||||||
"easy": "簡単",
|
"easy": "簡単",
|
||||||
"normal": "普通",
|
"normal": "普通",
|
||||||
"hard": "難しい"
|
"hard": "難しい",
|
||||||
|
"difficulty_name": "難易度: {difficulty}",
|
||||||
|
"unknown": "(未知)"
|
||||||
},
|
},
|
||||||
"coop_menu": {
|
"coop_menu": {
|
||||||
"title": "プレイヤー数を選択",
|
"title": "プレイヤー数を選択",
|
||||||
|
|
|
@ -67,7 +67,7 @@ pub struct GameConsts {
|
||||||
pub tile_offset_x: i32,
|
pub tile_offset_x: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CaretConsts {
|
pub struct CaretConsts {
|
||||||
pub offsets: [(i32, i32); 18],
|
pub offsets: [(i32, i32); 18],
|
||||||
pub bubble_left_rects: Vec<Rect<u16>>,
|
pub bubble_left_rects: Vec<Rect<u16>>,
|
||||||
|
@ -97,34 +97,6 @@ pub struct TextureSizeTable {
|
||||||
sizes: HashMap<String, (u16, u16)>,
|
sizes: HashMap<String, (u16, u16)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for CaretConsts {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
offsets: self.offsets,
|
|
||||||
bubble_left_rects: self.bubble_left_rects.clone(),
|
|
||||||
bubble_right_rects: self.bubble_right_rects.clone(),
|
|
||||||
projectile_dissipation_left_rects: self.projectile_dissipation_left_rects.clone(),
|
|
||||||
projectile_dissipation_right_rects: self.projectile_dissipation_right_rects.clone(),
|
|
||||||
projectile_dissipation_up_rects: self.projectile_dissipation_up_rects.clone(),
|
|
||||||
shoot_rects: self.shoot_rects.clone(),
|
|
||||||
zzz_rects: self.zzz_rects.clone(),
|
|
||||||
drowned_quote_left_rect: self.drowned_quote_left_rect,
|
|
||||||
drowned_quote_right_rect: self.drowned_quote_right_rect,
|
|
||||||
level_up_rects: self.level_up_rects.clone(),
|
|
||||||
level_down_rects: self.level_down_rects.clone(),
|
|
||||||
hurt_particles_rects: self.hurt_particles_rects.clone(),
|
|
||||||
explosion_rects: self.explosion_rects.clone(),
|
|
||||||
little_particles_rects: self.little_particles_rects.clone(),
|
|
||||||
exhaust_rects: self.exhaust_rects.clone(),
|
|
||||||
question_left_rect: self.question_left_rect,
|
|
||||||
question_right_rect: self.question_right_rect,
|
|
||||||
small_projectile_dissipation: self.small_projectile_dissipation.clone(),
|
|
||||||
empty_text: self.empty_text.clone(),
|
|
||||||
push_jump_key: self.push_jump_key.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct BulletData {
|
pub struct BulletData {
|
||||||
pub damage: u8,
|
pub damage: u8,
|
||||||
|
@ -175,23 +147,13 @@ pub struct BulletRects {
|
||||||
pub b042_spur_trail_l3: [Rect<u16>; 6],
|
pub b042_spur_trail_l3: [Rect<u16>; 6],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct WeaponConsts {
|
pub struct WeaponConsts {
|
||||||
pub bullet_table: Vec<BulletData>,
|
pub bullet_table: Vec<BulletData>,
|
||||||
pub bullet_rects: BulletRects,
|
pub bullet_rects: BulletRects,
|
||||||
pub level_table: [[u16; 3]; 14],
|
pub level_table: [[u16; 3]; 14],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for WeaponConsts {
|
|
||||||
fn clone(&self) -> WeaponConsts {
|
|
||||||
WeaponConsts {
|
|
||||||
bullet_table: self.bullet_table.clone(),
|
|
||||||
bullet_rects: self.bullet_rects,
|
|
||||||
level_table: self.level_table,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct WorldConsts {
|
pub struct WorldConsts {
|
||||||
pub snack_rect: Rect<u16>,
|
pub snack_rect: Rect<u16>,
|
||||||
|
@ -245,7 +207,7 @@ pub struct TextScriptConsts {
|
||||||
pub fade_ticks: i8,
|
pub fade_ticks: i8,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TitleConsts {
|
pub struct TitleConsts {
|
||||||
pub intro_text: String,
|
pub intro_text: String,
|
||||||
pub logo_rect: Rect<u16>,
|
pub logo_rect: Rect<u16>,
|
||||||
|
@ -266,42 +228,12 @@ pub struct TitleConsts {
|
||||||
pub cursor_sue: [Rect<u16>; 4],
|
pub cursor_sue: [Rect<u16>; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for TitleConsts {
|
#[derive(Debug, Clone)]
|
||||||
fn clone(&self) -> TitleConsts {
|
|
||||||
TitleConsts {
|
|
||||||
intro_text: self.intro_text.clone(),
|
|
||||||
logo_rect: self.logo_rect,
|
|
||||||
logo_splash_rect: self.logo_splash_rect,
|
|
||||||
menu_left_top: self.menu_left_top,
|
|
||||||
menu_right_top: self.menu_right_top,
|
|
||||||
menu_left_bottom: self.menu_left_bottom,
|
|
||||||
menu_right_bottom: self.menu_right_bottom,
|
|
||||||
menu_top: self.menu_top,
|
|
||||||
menu_bottom: self.menu_bottom,
|
|
||||||
menu_middle: self.menu_middle,
|
|
||||||
menu_left: self.menu_left,
|
|
||||||
menu_right: self.menu_right,
|
|
||||||
cursor_quote: self.cursor_quote,
|
|
||||||
cursor_curly: self.cursor_curly,
|
|
||||||
cursor_toroko: self.cursor_toroko,
|
|
||||||
cursor_king: self.cursor_king,
|
|
||||||
cursor_sue: self.cursor_sue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct GamepadConsts {
|
pub struct GamepadConsts {
|
||||||
pub button_rects: HashMap<Button, [Rect<u16>; 4]>,
|
pub button_rects: HashMap<Button, [Rect<u16>; 4]>,
|
||||||
pub axis_rects: HashMap<Axis, [Rect<u16>; 4]>,
|
pub axis_rects: HashMap<Axis, [Rect<u16>; 4]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for GamepadConsts {
|
|
||||||
fn clone(&self) -> GamepadConsts {
|
|
||||||
GamepadConsts { button_rects: self.button_rects.clone(), axis_rects: self.axis_rects.clone() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GamepadConsts {
|
impl GamepadConsts {
|
||||||
fn rects(base: Rect<u16>) -> [Rect<u16>; 4] {
|
fn rects(base: Rect<u16>) -> [Rect<u16>; 4] {
|
||||||
[
|
[
|
||||||
|
@ -313,7 +245,7 @@ impl GamepadConsts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EngineConstants {
|
pub struct EngineConstants {
|
||||||
pub base_paths: Vec<String>,
|
pub base_paths: Vec<String>,
|
||||||
pub is_cs_plus: bool,
|
pub is_cs_plus: bool,
|
||||||
|
@ -347,43 +279,6 @@ pub struct EngineConstants {
|
||||||
pub gamepad: GamepadConsts,
|
pub gamepad: GamepadConsts,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for EngineConstants {
|
|
||||||
fn clone(&self) -> EngineConstants {
|
|
||||||
EngineConstants {
|
|
||||||
base_paths: self.base_paths.clone(),
|
|
||||||
is_cs_plus: self.is_cs_plus,
|
|
||||||
is_switch: self.is_switch,
|
|
||||||
is_demo: self.is_demo,
|
|
||||||
supports_og_textures: self.supports_og_textures,
|
|
||||||
has_difficulty_menu: self.has_difficulty_menu,
|
|
||||||
supports_two_player: self.supports_two_player,
|
|
||||||
game: self.game,
|
|
||||||
player: self.player,
|
|
||||||
booster: self.booster,
|
|
||||||
caret: self.caret.clone(),
|
|
||||||
world: self.world,
|
|
||||||
npc: self.npc,
|
|
||||||
weapon: self.weapon.clone(),
|
|
||||||
tex_sizes: self.tex_sizes.clone(),
|
|
||||||
textscript: self.textscript,
|
|
||||||
title: self.title.clone(),
|
|
||||||
inventory_dim_color: self.inventory_dim_color,
|
|
||||||
font_path: self.font_path.clone(),
|
|
||||||
font_space_offset: self.font_space_offset,
|
|
||||||
soundtracks: self.soundtracks.clone(),
|
|
||||||
music_table: self.music_table.clone(),
|
|
||||||
organya_paths: self.organya_paths.clone(),
|
|
||||||
credit_illustration_paths: self.credit_illustration_paths.clone(),
|
|
||||||
player_skin_paths: self.player_skin_paths.clone(),
|
|
||||||
animated_face_table: self.animated_face_table.clone(),
|
|
||||||
string_table: self.string_table.clone(),
|
|
||||||
missile_flags: self.missile_flags.clone(),
|
|
||||||
locales: self.locales.clone(),
|
|
||||||
gamepad: self.gamepad.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EngineConstants {
|
impl EngineConstants {
|
||||||
pub fn defaults() -> Self {
|
pub fn defaults() -> Self {
|
||||||
EngineConstants {
|
EngineConstants {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
use std::backtrace::Backtrace;
|
||||||
use std::cell::UnsafeCell;
|
use std::cell::UnsafeCell;
|
||||||
|
use std::panic::PanicInfo;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
@ -290,14 +292,27 @@ fn init_logger() -> GameResult {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn panic_hook(info: &PanicInfo<'_>) {
|
||||||
|
let backtrace = Backtrace::force_capture();
|
||||||
|
let msg = info.payload().downcast_ref::<&str>().unwrap_or(&"");
|
||||||
|
let location = info.location();
|
||||||
|
|
||||||
|
if location.is_some() {
|
||||||
|
log::error!("Panic occurred in {} with message: '{msg}'\n {backtrace:#}", location.unwrap().to_string());
|
||||||
|
} else {
|
||||||
|
log::error!("Panic occurred with message: '{msg}'\n {backtrace:#}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init(options: LaunchOptions) -> GameResult {
|
pub fn init(options: LaunchOptions) -> GameResult {
|
||||||
let _ = init_logger();
|
let _ = init_logger();
|
||||||
|
std::panic::set_hook(Box::new(panic_hook));
|
||||||
|
|
||||||
let mut context = Box::pin(Context::new());
|
let mut context = Box::pin(Context::new());
|
||||||
|
|
||||||
let mut fs_container = FilesystemContainer::new();
|
let mut fs_container = FilesystemContainer::new();
|
||||||
fs_container.mount_fs(&mut context)?;
|
fs_container.mount_fs(&mut context)?;
|
||||||
|
|
||||||
if options.server_mode {
|
if options.server_mode {
|
||||||
log::info!("Running in server mode...");
|
log::info!("Running in server mode...");
|
||||||
context.headless = true;
|
context.headless = true;
|
||||||
|
|
|
@ -1874,16 +1874,11 @@ impl TextScriptVM {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct TextScript {
|
pub struct TextScript {
|
||||||
pub(crate) event_map: HashMap<u16, Vec<u8>>,
|
pub(crate) event_map: HashMap<u16, Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for TextScript {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self { event_map: self.event_map.clone() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TextScript {
|
impl Default for TextScript {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
TextScript::new()
|
TextScript::new()
|
||||||
|
|
|
@ -15,17 +15,11 @@ use crate::game::map::{Map, NPCData};
|
||||||
use crate::game::scripting::tsc::text_script::TextScript;
|
use crate::game::scripting::tsc::text_script::TextScript;
|
||||||
use crate::util::encoding::read_cur_shift_jis;
|
use crate::util::encoding::read_cur_shift_jis;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||||
pub struct NpcType {
|
pub struct NpcType {
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for NpcType {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self { name: self.name.clone() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NpcType {
|
impl NpcType {
|
||||||
pub fn new(name: &str) -> Self {
|
pub fn new(name: &str) -> Self {
|
||||||
Self { name: name.to_owned() }
|
Self { name: name.to_owned() }
|
||||||
|
@ -36,17 +30,11 @@ impl NpcType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||||
pub struct Tileset {
|
pub struct Tileset {
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Tileset {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self { name: self.name.clone() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Tileset {
|
impl Tileset {
|
||||||
pub fn new(name: &str) -> Self {
|
pub fn new(name: &str) -> Self {
|
||||||
Self { name: name.to_owned() }
|
Self { name: name.to_owned() }
|
||||||
|
@ -66,17 +54,11 @@ impl Tileset {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||||
pub struct Background {
|
pub struct Background {
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Background {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self { name: self.name.clone() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Background {
|
impl Background {
|
||||||
pub fn new(name: &str) -> Self {
|
pub fn new(name: &str) -> Self {
|
||||||
Self { name: name.to_owned() }
|
Self { name: name.to_owned() }
|
||||||
|
@ -195,7 +177,7 @@ pub struct PxPackStageData {
|
||||||
pub offset_bg: u32,
|
pub offset_bg: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct StageData {
|
pub struct StageData {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub name_jp: String,
|
pub name_jp: String,
|
||||||
|
@ -210,24 +192,6 @@ pub struct StageData {
|
||||||
pub npc2: NpcType,
|
pub npc2: NpcType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for StageData {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
StageData {
|
|
||||||
name: self.name.clone(),
|
|
||||||
name_jp: self.name_jp.clone(),
|
|
||||||
map: self.map.clone(),
|
|
||||||
boss_no: self.boss_no,
|
|
||||||
tileset: self.tileset.clone(),
|
|
||||||
pxpack_data: self.pxpack_data.clone(),
|
|
||||||
background: self.background.clone(),
|
|
||||||
background_type: self.background_type,
|
|
||||||
background_color: self.background_color,
|
|
||||||
npc1: self.npc1.clone(),
|
|
||||||
npc2: self.npc2.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const NXENGINE_BACKDROPS: [&str; 15] = [
|
const NXENGINE_BACKDROPS: [&str; 15] = [
|
||||||
"bk0",
|
"bk0",
|
||||||
"bkBlue",
|
"bkBlue",
|
||||||
|
|
|
@ -17,11 +17,7 @@ impl Default for Locale {
|
||||||
Locale {
|
Locale {
|
||||||
code: "en".to_owned(),
|
code: "en".to_owned(),
|
||||||
name: "English".to_owned(),
|
name: "English".to_owned(),
|
||||||
font: FontData {
|
font: FontData { path: String::new(), scale: 1.0, space_offset: 0.0 },
|
||||||
path: String::new(),
|
|
||||||
scale: 1.0,
|
|
||||||
space_offset: 0.0
|
|
||||||
},
|
|
||||||
strings: HashMap::new(),
|
strings: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +61,7 @@ impl Locale {
|
||||||
strings
|
strings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// if the key does not exists, return the origin key instead
|
||||||
pub fn t<'a: 'b, 'b>(&'a self, key: &'b str) -> &'b str {
|
pub fn t<'a: 'b, 'b>(&'a self, key: &'b str) -> &'b str {
|
||||||
if let Some(str) = self.strings.get(key) {
|
if let Some(str) = self.strings.get(key) {
|
||||||
str
|
str
|
||||||
|
|
|
@ -594,23 +594,21 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
|
||||||
|
|
||||||
graphics::draw_rect(ctx, bar_rect, Color::new(1.0, 1.0, 1.0, 1.0))?;
|
graphics::draw_rect(ctx, bar_rect, Color::new(1.0, 1.0, 1.0, 1.0))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
{
|
{
|
||||||
state
|
state.font.builder().x(self.x as f32 - 25.0).y(y).shadow(true).draw(
|
||||||
.font
|
"<",
|
||||||
.builder()
|
ctx,
|
||||||
.x(self.x as f32 - 25.0)
|
&state.constants,
|
||||||
.y(y)
|
&mut state.texture_set,
|
||||||
.shadow(true)
|
)?;
|
||||||
.draw("<", ctx, &state.constants, &mut state.texture_set)?;
|
state.font.builder().x((self.x + self.width as isize) as f32 + 15.0).y(y).shadow(true).draw(
|
||||||
state
|
">",
|
||||||
.font
|
ctx,
|
||||||
.builder()
|
&state.constants,
|
||||||
.x((self.x + self.width as isize) as f32 + 15.0)
|
&mut state.texture_set,
|
||||||
.y(y)
|
)?;
|
||||||
.shadow(true)
|
|
||||||
.draw(">", ctx, &state.constants, &mut state.texture_set)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MenuEntry::NewSave => {
|
MenuEntry::NewSave => {
|
||||||
|
@ -677,17 +675,17 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
|
||||||
);
|
);
|
||||||
batch.draw(ctx)?;
|
batch.draw(ctx)?;
|
||||||
} else {
|
} else {
|
||||||
let mut difficulty_name: String = "Difficulty: ".to_owned();
|
let difficulty = match save.difficulty {
|
||||||
|
0 => state.loc.t("menus.difficulty_menu.normal"),
|
||||||
match save.difficulty {
|
2 => state.loc.t("menus.difficulty_menu.easy"),
|
||||||
0 => difficulty_name.push_str("Normal"),
|
4 => state.loc.t("menus.difficulty_menu.hard"),
|
||||||
2 => difficulty_name.push_str("Easy"),
|
_ => state.loc.t("menus.difficulty_menu.unknown"),
|
||||||
4 => difficulty_name.push_str("Hard"),
|
};
|
||||||
_ => difficulty_name.push_str("(unknown)"),
|
let difficulty_name =
|
||||||
}
|
state.loc.tt("menus.difficulty_menu.difficulty_name", &[("difficulty", &difficulty)]);
|
||||||
|
|
||||||
state.font.builder().position(self.x as f32 + 20.0, y + 10.0).draw(
|
state.font.builder().position(self.x as f32 + 20.0, y + 10.0).draw(
|
||||||
difficulty_name.as_str(),
|
&difficulty_name,
|
||||||
ctx,
|
ctx,
|
||||||
&state.constants,
|
&state.constants,
|
||||||
&mut state.texture_set,
|
&mut state.texture_set,
|
||||||
|
@ -770,11 +768,7 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
|
||||||
state: &mut SharedGameState,
|
state: &mut SharedGameState,
|
||||||
) -> MenuSelectionResult<T> {
|
) -> MenuSelectionResult<T> {
|
||||||
// the engine does 4 times more ticks during cutscene skipping
|
// the engine does 4 times more ticks during cutscene skipping
|
||||||
let max_anim_wait = if state.textscript_vm.flags.cutscene_skip() {
|
let max_anim_wait = if state.textscript_vm.flags.cutscene_skip() { 32 } else { 8 };
|
||||||
32
|
|
||||||
} else {
|
|
||||||
8
|
|
||||||
};
|
|
||||||
|
|
||||||
self.anim_wait += 1;
|
self.anim_wait += 1;
|
||||||
if self.anim_wait > max_anim_wait {
|
if self.anim_wait > max_anim_wait {
|
||||||
|
|
|
@ -253,7 +253,11 @@ impl SaveSelectMenu {
|
||||||
self.update_sizes(state);
|
self.update_sizes(state);
|
||||||
match self.current_menu {
|
match self.current_menu {
|
||||||
CurrentMenu::SaveMenu => match self.save_menu.tick(controller, state) {
|
CurrentMenu::SaveMenu => match self.save_menu.tick(controller, state) {
|
||||||
MenuSelectionResult::Selected(SaveMenuEntry::Back, _) | MenuSelectionResult::Canceled => exit_action(),
|
MenuSelectionResult::Selected(SaveMenuEntry::Back, _) | MenuSelectionResult::Canceled => {
|
||||||
|
exit_action();
|
||||||
|
state.mod_path = None;
|
||||||
|
state.reload_graphics();
|
||||||
|
}
|
||||||
MenuSelectionResult::Selected(SaveMenuEntry::New(slot), _) => {
|
MenuSelectionResult::Selected(SaveMenuEntry::New(slot), _) => {
|
||||||
state.save_slot = slot + 1;
|
state.save_slot = slot + 1;
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ enum CurrentMenu {
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
pub enum MainMenuEntry {
|
pub enum MainMenuEntry {
|
||||||
Start,
|
Start,
|
||||||
|
Challenge(usize),
|
||||||
Challenges,
|
Challenges,
|
||||||
Options,
|
Options,
|
||||||
Editor,
|
Editor,
|
||||||
|
@ -92,6 +93,7 @@ pub struct TitleScene {
|
||||||
compact_jukebox: CompactJukebox,
|
compact_jukebox: CompactJukebox,
|
||||||
stage: Stage,
|
stage: Stage,
|
||||||
textures: StageTexturePaths,
|
textures: StageTexturePaths,
|
||||||
|
curly_story_selected : bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TitleScene {
|
impl TitleScene {
|
||||||
|
@ -134,6 +136,7 @@ impl TitleScene {
|
||||||
compact_jukebox: CompactJukebox::new(),
|
compact_jukebox: CompactJukebox::new(),
|
||||||
stage: fake_stage,
|
stage: fake_stage,
|
||||||
textures,
|
textures,
|
||||||
|
curly_story_selected: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,6 +215,26 @@ impl Scene for TitleScene {
|
||||||
self.main_menu
|
self.main_menu
|
||||||
.push_entry(MainMenuEntry::Start, MenuEntry::Active(state.loc.t("menus.main_menu.start").to_owned()));
|
.push_entry(MainMenuEntry::Start, MenuEntry::Active(state.loc.t("menus.main_menu.start").to_owned()));
|
||||||
|
|
||||||
|
if !state.mod_list.mods.is_empty() {
|
||||||
|
for (idx, mod_info) in state.mod_list.mods.iter().enumerate() {
|
||||||
|
if mod_info.id.clone() == "csmod_03" {
|
||||||
|
if !mod_info.valid {
|
||||||
|
self.main_menu
|
||||||
|
.push_entry(MainMenuEntry::Challenge(idx), MenuEntry::Disabled(mod_info.path.clone()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if mod_info.satisfies_requirement(&state.mod_requirements) {
|
||||||
|
self.main_menu
|
||||||
|
.push_entry(MainMenuEntry::Challenge(idx), MenuEntry::Active(mod_info.name.clone()));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
self.main_menu
|
||||||
|
.push_entry(MainMenuEntry::Challenge(idx), MenuEntry::Disabled("???".to_owned()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !state.mod_list.mods.is_empty() {
|
if !state.mod_list.mods.is_empty() {
|
||||||
self.main_menu.push_entry(
|
self.main_menu.push_entry(
|
||||||
MainMenuEntry::Challenges,
|
MainMenuEntry::Challenges,
|
||||||
|
@ -248,22 +271,24 @@ impl Scene for TitleScene {
|
||||||
let mut mutate_selection = true;
|
let mut mutate_selection = true;
|
||||||
|
|
||||||
for (idx, mod_info) in state.mod_list.mods.iter().enumerate() {
|
for (idx, mod_info) in state.mod_list.mods.iter().enumerate() {
|
||||||
if !mod_info.valid {
|
if mod_info.id.clone() != "csmod_03" {
|
||||||
self.challenges_menu
|
if !mod_info.valid {
|
||||||
.push_entry(ChallengesMenuEntry::Challenge(idx), MenuEntry::Disabled(mod_info.path.clone()));
|
self.challenges_menu
|
||||||
continue;
|
.push_entry(ChallengesMenuEntry::Challenge(idx), MenuEntry::Disabled(mod_info.path.clone()));
|
||||||
}
|
continue;
|
||||||
if mod_info.satisfies_requirement(&state.mod_requirements) {
|
}
|
||||||
self.challenges_menu
|
if mod_info.satisfies_requirement(&state.mod_requirements) {
|
||||||
.push_entry(ChallengesMenuEntry::Challenge(idx), MenuEntry::Active(mod_info.name.clone()));
|
self.challenges_menu
|
||||||
|
.push_entry(ChallengesMenuEntry::Challenge(idx), MenuEntry::Active(mod_info.name.clone()));
|
||||||
if mutate_selection {
|
|
||||||
selected = ChallengesMenuEntry::Challenge(idx);
|
if mutate_selection {
|
||||||
mutate_selection = false;
|
selected = ChallengesMenuEntry::Challenge(idx);
|
||||||
|
mutate_selection = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.challenges_menu
|
||||||
|
.push_entry(ChallengesMenuEntry::Challenge(idx), MenuEntry::Disabled("???".to_owned()));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.challenges_menu
|
|
||||||
.push_entry(ChallengesMenuEntry::Challenge(idx), MenuEntry::Disabled("???".to_owned()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.challenges_menu
|
self.challenges_menu
|
||||||
|
@ -336,7 +361,56 @@ impl Scene for TitleScene {
|
||||||
self.save_select_menu.set_skip_difficulty_menu(!state.constants.has_difficulty_menu);
|
self.save_select_menu.set_skip_difficulty_menu(!state.constants.has_difficulty_menu);
|
||||||
self.current_menu = CurrentMenu::SaveSelectMenu;
|
self.current_menu = CurrentMenu::SaveSelectMenu;
|
||||||
}
|
}
|
||||||
|
MenuSelectionResult::Selected(MainMenuEntry::Challenge(idx), _) => {
|
||||||
|
self.curly_story_selected = true;
|
||||||
|
if let Some(mod_info) = state.mod_list.mods.get(idx) {
|
||||||
|
state.mod_path = Some(mod_info.path.clone());
|
||||||
|
if mod_info.save_slot >= 0 {
|
||||||
|
self.save_select_menu.init(state, ctx)?;
|
||||||
|
self.save_select_menu.set_skip_difficulty_menu(true);
|
||||||
|
self.nikumaru_rec.load_counter(state, ctx)?;
|
||||||
|
state.reload_graphics();
|
||||||
|
self.current_menu = CurrentMenu::SaveSelectMenu;
|
||||||
|
} else {
|
||||||
|
let mod_name = mod_info.name.clone();
|
||||||
|
self.confirm_menu.width =
|
||||||
|
(state.font.builder().compute_width(&mod_name).max(50.0) + 32.0) as u16;
|
||||||
|
|
||||||
|
self.confirm_menu.set_entry(ConfirmMenuEntry::Title, MenuEntry::Disabled(mod_name));
|
||||||
|
|
||||||
|
if state.has_replay_data(ctx, ReplayKind::Best) {
|
||||||
|
self.confirm_menu.set_entry(
|
||||||
|
ConfirmMenuEntry::Replay(ReplayKind::Best),
|
||||||
|
MenuEntry::Active(state.loc.t("menus.challenge_menu.replay_best").to_owned()),
|
||||||
|
);
|
||||||
|
self.confirm_menu.set_entry(
|
||||||
|
ConfirmMenuEntry::DeleteReplay,
|
||||||
|
MenuEntry::Active(state.loc.t("menus.challenge_menu.delete_replay").to_owned()),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.confirm_menu
|
||||||
|
.set_entry(ConfirmMenuEntry::Replay(ReplayKind::Best), MenuEntry::Hidden);
|
||||||
|
self.confirm_menu.set_entry(ConfirmMenuEntry::DeleteReplay, MenuEntry::Hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.has_replay_data(ctx, ReplayKind::Last) {
|
||||||
|
self.confirm_menu.set_entry(
|
||||||
|
ConfirmMenuEntry::Replay(ReplayKind::Last),
|
||||||
|
MenuEntry::Active(state.loc.t("menus.challenge_menu.replay_last").to_owned()),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.confirm_menu
|
||||||
|
.set_entry(ConfirmMenuEntry::Replay(ReplayKind::Last), MenuEntry::Hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.nikumaru_rec.load_counter(state, ctx)?;
|
||||||
|
state.reload_graphics();
|
||||||
|
self.current_menu = CurrentMenu::ChallengeConfirmMenu;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
MenuSelectionResult::Selected(MainMenuEntry::Challenges, _) => {
|
MenuSelectionResult::Selected(MainMenuEntry::Challenges, _) => {
|
||||||
|
self.curly_story_selected = false;
|
||||||
self.current_menu = CurrentMenu::ChallengesMenu;
|
self.current_menu = CurrentMenu::ChallengesMenu;
|
||||||
}
|
}
|
||||||
MenuSelectionResult::Selected(MainMenuEntry::Options, _) => {
|
MenuSelectionResult::Selected(MainMenuEntry::Options, _) => {
|
||||||
|
@ -375,7 +449,7 @@ impl Scene for TitleScene {
|
||||||
}
|
}
|
||||||
CurrentMenu::SaveSelectMenu => {
|
CurrentMenu::SaveSelectMenu => {
|
||||||
let cm = &mut self.current_menu;
|
let cm = &mut self.current_menu;
|
||||||
let rm = if state.mod_path.is_none() { CurrentMenu::MainMenu } else { CurrentMenu::ChallengesMenu };
|
let rm = if state.mod_path.is_none() || self.curly_story_selected { CurrentMenu::MainMenu } else { CurrentMenu::ChallengesMenu };
|
||||||
self.save_select_menu.tick(
|
self.save_select_menu.tick(
|
||||||
&mut || {
|
&mut || {
|
||||||
*cm = rm;
|
*cm = rm;
|
||||||
|
@ -393,6 +467,7 @@ impl Scene for TitleScene {
|
||||||
self.save_select_menu.init(state, ctx)?;
|
self.save_select_menu.init(state, ctx)?;
|
||||||
self.save_select_menu.set_skip_difficulty_menu(true);
|
self.save_select_menu.set_skip_difficulty_menu(true);
|
||||||
self.nikumaru_rec.load_counter(state, ctx)?;
|
self.nikumaru_rec.load_counter(state, ctx)?;
|
||||||
|
state.reload_graphics();
|
||||||
self.current_menu = CurrentMenu::SaveSelectMenu;
|
self.current_menu = CurrentMenu::SaveSelectMenu;
|
||||||
} else {
|
} else {
|
||||||
let mod_name = mod_info.name.clone();
|
let mod_name = mod_info.name.clone();
|
||||||
|
@ -427,6 +502,7 @@ impl Scene for TitleScene {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.nikumaru_rec.load_counter(state, ctx)?;
|
self.nikumaru_rec.load_counter(state, ctx)?;
|
||||||
|
state.reload_graphics();
|
||||||
self.current_menu = CurrentMenu::ChallengeConfirmMenu;
|
self.current_menu = CurrentMenu::ChallengeConfirmMenu;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -455,7 +531,14 @@ impl Scene for TitleScene {
|
||||||
self.current_menu = CurrentMenu::ChallengesMenu;
|
self.current_menu = CurrentMenu::ChallengesMenu;
|
||||||
}
|
}
|
||||||
MenuSelectionResult::Selected(ConfirmMenuEntry::Back, _) | MenuSelectionResult::Canceled => {
|
MenuSelectionResult::Selected(ConfirmMenuEntry::Back, _) | MenuSelectionResult::Canceled => {
|
||||||
self.current_menu = CurrentMenu::ChallengesMenu;
|
if !self.curly_story_selected {
|
||||||
|
self.current_menu = CurrentMenu::ChallengesMenu;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.current_menu = CurrentMenu::MainMenu;
|
||||||
|
}
|
||||||
|
state.mod_path = None;
|
||||||
|
state.reload_graphics();
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
},
|
||||||
|
|
|
@ -50,17 +50,12 @@ pub(crate) struct OrgPlaybackEngine {
|
||||||
pub interpolation: InterpolationMode,
|
pub interpolation: InterpolationMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct SavedOrganyaPlaybackState {
|
pub struct SavedOrganyaPlaybackState {
|
||||||
song: Organya,
|
song: Organya,
|
||||||
play_pos: i32,
|
play_pos: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for SavedOrganyaPlaybackState {
|
|
||||||
fn clone(&self) -> SavedOrganyaPlaybackState {
|
|
||||||
SavedOrganyaPlaybackState { song: self.song.clone(), play_pos: self.play_pos }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OrgPlaybackEngine {
|
impl OrgPlaybackEngine {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut buffers: [MaybeUninit<RenderBuffer>; 136] = unsafe { MaybeUninit::uninit().assume_init() };
|
let mut buffers: [MaybeUninit<RenderBuffer>; 136] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||||
|
|
|
@ -35,8 +35,7 @@ pub struct Timing {
|
||||||
pub loop_range: LoopRange,
|
pub loop_range: LoopRange,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Instrument {
|
pub struct Instrument {
|
||||||
pub freq: u16,
|
pub freq: u16,
|
||||||
pub inst: u8,
|
pub inst: u8,
|
||||||
|
@ -44,20 +43,12 @@ pub struct Instrument {
|
||||||
pub notes: u16,
|
pub notes: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Track {
|
pub struct Track {
|
||||||
pub inst: Instrument,
|
pub inst: Instrument,
|
||||||
pub notes: Vec<Note>,
|
pub notes: Vec<Note>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Track {
|
|
||||||
fn clone(&self) -> Track {
|
|
||||||
Track {
|
|
||||||
inst: self.inst,
|
|
||||||
notes: self.notes.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for Track {
|
impl std::fmt::Debug for Track {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
self.inst.fmt(f)
|
self.inst.fmt(f)
|
||||||
|
@ -74,23 +65,13 @@ pub struct Note {
|
||||||
pub pan: u8,
|
pub pan: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Song {
|
pub struct Song {
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
pub time: Timing,
|
pub time: Timing,
|
||||||
pub tracks: [Track; 16],
|
pub tracks: [Track; 16],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Song {
|
|
||||||
fn clone(&self) -> Song {
|
|
||||||
Song {
|
|
||||||
version: self.version,
|
|
||||||
time: self.time,
|
|
||||||
tracks: self.tracks.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Song {
|
impl Song {
|
||||||
pub fn empty() -> Song {
|
pub fn empty() -> Song {
|
||||||
Song {
|
Song {
|
||||||
|
|
Loading…
Reference in a new issue