diff --git a/phira/src/scene/chapter.rs b/phira/src/scene/chapter.rs index b21b8a4b..59cb1ef0 100644 --- a/phira/src/scene/chapter.rs +++ b/phira/src/scene/chapter.rs @@ -254,6 +254,7 @@ impl Scene for ChapterScene { intro: info.intro.clone(), hold_partial_cover: true, + negative_length_hold: true, note_uniform_scale: false, force_aspect_ratio: false, use_rpe_170_speed: Some(false), diff --git a/prpr/locales/en-US/chart_info.ftl b/prpr/locales/en-US/chart_info.ftl index f60b48dd..9f828d7d 100644 --- a/prpr/locales/en-US/chart_info.ftl +++ b/prpr/locales/en-US/chart_info.ftl @@ -16,6 +16,9 @@ ps = Hint: aspect-hint = Aspect ratio should be in the format "width:height" or a real number. force-aspect-ratio = Force Aspect Ratio dim = Background Brightness +hold-partial-cover = Hold tail cover +negative-length-hold = Allow negative length hold +note-uniform-scale = Note uniform scale enable-unlock = Unlock Video chart-file = Chart music-file = Music diff --git a/prpr/locales/zh-CN/chart_info.ftl b/prpr/locales/zh-CN/chart_info.ftl index cf5d72d1..7846fa0a 100644 --- a/prpr/locales/zh-CN/chart_info.ftl +++ b/prpr/locales/zh-CN/chart_info.ftl @@ -17,6 +17,9 @@ ps = 注: aspect-hint = 宽高比可以直接填小数,也可以是 w:h 的形式(英文半角冒号) force-aspect-ratio = 强制宽高比 dim = 背景昏暗 +hold-partial-cover = Hold 尾部遮罩 +negative-length-hold = 允许负长度 Hold +note-uniform-scale = 音符等比缩放 enable-unlock = 启用解锁动画 chart-file = 谱面文件 music-file = 音乐文件 diff --git a/prpr/src/bin.rs b/prpr/src/bin.rs index db268e2a..f6f597ab 100644 --- a/prpr/src/bin.rs +++ b/prpr/src/bin.rs @@ -460,14 +460,16 @@ impl BinaryData for JudgeLine { impl BinaryData for ChartSettings { fn read_binary(r: &mut BinaryReader) -> Result { + let flags = r.read::()?; Ok(Self { - pe_alpha_extension: r.read::()? == 1, + pe_alpha_extension: flags & 1 != 0, hold_partial_cover: r.read::()? == 1, + negative_length_hold: flags >> 2 & 1 != 0, }) } fn write_binary(&self, w: &mut BinaryWriter) -> Result<()> { - w.write_val(self.pe_alpha_extension as u8)?; + w.write_val((self.pe_alpha_extension as u8) | ((self.negative_length_hold as u8) << 2))?; w.write_val(self.hold_partial_cover as u8)?; Ok(()) } diff --git a/prpr/src/core/chart.rs b/prpr/src/core/chart.rs index 898db21f..b223e14a 100644 --- a/prpr/src/core/chart.rs +++ b/prpr/src/core/chart.rs @@ -18,6 +18,7 @@ pub struct ChartExtra { pub struct ChartSettings { pub pe_alpha_extension: bool, pub hold_partial_cover: bool, + pub negative_length_hold: bool, } pub type HitSoundMap = HashMap; diff --git a/prpr/src/core/note.rs b/prpr/src/core/note.rs index 06615ca6..3c64a87c 100644 --- a/prpr/src/core/note.rs +++ b/prpr/src/core/note.rs @@ -76,7 +76,7 @@ fn draw_tex(res: &Resource, texture: Texture2D, order: i8, x: f32, y: f32, color params.source = Some(source); } } - params.flip_y = true; + params.flip_y ^= true; draw_tex_pts(res, texture, order, p, color, params); } fn draw_tex_pts(res: &Resource, texture: Texture2D, order: i8, p: [Point; 4], color: Color, params: DrawTextureParams) { @@ -233,9 +233,10 @@ impl Note { } }; + let is_covered = cover_base <= -0.001; if !config.draw_below && (((res.time - FADEOUT_TIME >= self.time || self.fake && res.time >= self.time) && !matches!(self.kind, NoteKind::Hold { .. })) - || (self.time > res.time && cover_base <= -0.001)) + || (self.time > res.time && is_covered)) { return; } @@ -289,8 +290,11 @@ impl Note { let top = (end_height - line_height) as f32; let tex = &style.hold; let ratio = style.hold_ratio(); + let is_negative_length = top - bottom < 0.; + let flip_y = config.settings.negative_length_hold && (config.draw_below || !is_covered) && is_negative_length; + let body_h = if flip_y { bottom - top } else { top - bottom }; + let body_y = if flip_y { bottom - body_h } else { bottom }; // body - // TODO (end_height - height) is not always total height draw_tex( res, **(if res.res_pack.info.hold_repeat { @@ -300,7 +304,7 @@ impl Note { }), order, -scale, - bottom, + body_y, color, DrawTextureParams { source: Some({ @@ -308,12 +312,13 @@ impl Note { let hold_body = style.hold_body.as_ref().unwrap(); let width = hold_body.width(); let height = hold_body.height(); - Rect::new(0., 0., 1., (top - bottom) / scale / 2. * width / height) + Rect::new(0., 0., 1., body_h / scale / 2. * width / height) } else { style.hold_body_rect() } }), - dest_size: Some(vec2(scale * 2., top - bottom)), + dest_size: Some(vec2(scale * 2., body_h)), + flip_y, ..Default::default() }, false, @@ -322,34 +327,41 @@ impl Note { if res.time < self.time || res.res_pack.info.hold_keep_head { let r = style.hold_head_rect(); let hf = vec2(scale, r.h / r.w * scale * ratio); + let head_y = if flip_y { bottom + hf.y * 2. } else { bottom }; draw_tex( res, **tex, order, -scale, - bottom - if res.res_pack.info.hold_compact { hf.y } else { hf.y * 2. }, + head_y - if res.res_pack.info.hold_compact { hf.y } else { hf.y * 2. }, color, DrawTextureParams { source: Some(r), dest_size: Some(hf * 2.), + flip_y, ..Default::default() }, false, ); } // tail + if !flip_y && is_negative_length { // only render head + return; + } let r = style.hold_tail_rect(); let hf = vec2(scale, r.h / r.w * scale * ratio); + let tail_y = if flip_y { top - hf.y * 2. } else { top }; draw_tex( res, **tex, order, -scale, - top - if res.res_pack.info.hold_compact { hf.y } else { 0. }, + tail_y - if res.res_pack.info.hold_compact { hf.y } else { 0. }, color, DrawTextureParams { source: Some(r), dest_size: Some(hf * 2.), + flip_y, ..Default::default() }, false, diff --git a/prpr/src/info.rs b/prpr/src/info.rs index ee730b6c..6ac36763 100644 --- a/prpr/src/info.rs +++ b/prpr/src/info.rs @@ -45,6 +45,7 @@ pub struct ChartInfo { pub intro: String, pub hold_partial_cover: bool, + pub negative_length_hold: bool, pub note_uniform_scale: bool, pub force_aspect_ratio: bool, pub use_rpe_170_speed: Option, @@ -86,6 +87,7 @@ impl Default for ChartInfo { intro: String::new(), hold_partial_cover: false, + negative_length_hold: true, note_uniform_scale: false, force_aspect_ratio: false, use_rpe_170_speed: None, diff --git a/prpr/src/scene/game.rs b/prpr/src/scene/game.rs index 3a679747..03cee23d 100644 --- a/prpr/src/scene/game.rs +++ b/prpr/src/scene/game.rs @@ -230,6 +230,7 @@ impl GameScene { }?; chart.load_textures(fs).await?; chart.settings.hold_partial_cover = info.hold_partial_cover; + chart.settings.negative_length_hold = info.negative_length_hold; Ok((chart, bytes, format)) } diff --git a/prpr/src/ui/chart_info.rs b/prpr/src/ui/chart_info.rs index 8fbc516d..ce1e9c09 100644 --- a/prpr/src/ui/chart_info.rs +++ b/prpr/src/ui/chart_info.rs @@ -191,7 +191,13 @@ pub fn render_chart_info(ui: &mut Ui, edit: &mut ChartInfoEdit, width: f32) -> ( ui.dx(0.01); let r = ui.checkbox(tl!("force-aspect-ratio"), &mut info.force_aspect_ratio); - dy!(r.h + s); + dy!(r.h); + let r = ui.checkbox(tl!("hold-partial-cover"), &mut info.hold_partial_cover); + dy!(r.h); + let r = ui.checkbox(tl!("negative-length-hold"), &mut info.negative_length_hold); + dy!(r.h); + let r = ui.checkbox(tl!("note-uniform-scale"), &mut info.note_uniform_scale); + dy!(r.h); ui.dx(-0.01); ui.dx(-rt);