1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2024-09-20 17:18:50 +00:00

Compare commits

...

5 commits

Author SHA1 Message Date
IRUZZ 747d915d4b
Merge 30f1a0dadf into ca5361cc58 2024-03-31 22:10:22 -05:00
biroder ca5361cc58 Add panic logging 2024-03-25 16:06:47 +00:00
poly000 08f086bfc4
localize difficulty name in save menu (#263) 2024-03-25 11:41:21 +00:00
poly000 1f288e2a39
replace impl Clone with #[derive(Clone)] (#262) [ci skip] 2024-03-25 11:04:13 +00:00
IruzzArcana 30f1a0dadf makes curly's story appear as a separate option from challenges 2022-12-01 12:54:12 +01:00
13 changed files with 168 additions and 240 deletions

View file

@ -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"]

View file

@ -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",

View file

@ -40,7 +40,9 @@
"title": "難易度選択", "title": "難易度選択",
"easy": "簡単", "easy": "簡単",
"normal": "普通", "normal": "普通",
"hard": "難しい" "hard": "難しい",
"difficulty_name": "難易度: {difficulty}",
"unknown": "(未知)"
}, },
"coop_menu": { "coop_menu": {
"title": "プレイヤー数を選択", "title": "プレイヤー数を選択",

View file

@ -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 {

View file

@ -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;

View file

@ -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()

View file

@ -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",

View file

@ -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

View file

@ -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 {

View file

@ -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;

View file

@ -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();
} }
_ => (), _ => (),
}, },

View file

@ -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() };

View file

@ -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 {