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

Add <SIL/<CIL

This commit is contained in:
Alula 2021-10-16 14:59:27 +02:00
parent 8b5d56fe27
commit bdc4e7d209
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA
10 changed files with 151 additions and 28 deletions

View file

@ -1,8 +1,10 @@
use crate::common::Rect;
use crate::common::{Color, Rect};
use crate::entity::GameEntity;
use crate::frame::Frame;
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::graphics;
use crate::scripting::tsc::text_script::IllustrationState;
use crate::shared_game_state::SharedGameState;
pub struct Credits {}
@ -11,14 +13,48 @@ impl Credits {
pub fn new() -> Credits {
Credits {}
}
pub fn draw_tick(&mut self, state: &mut SharedGameState) {
match state.textscript_vm.illustration_state {
IllustrationState::FadeIn(mut x) => {
x += 40.0 * state.frame_time as f32;
state.textscript_vm.illustration_state =
if x >= 0.0 { IllustrationState::Shown } else { IllustrationState::FadeIn(x) };
}
IllustrationState::FadeOut(mut x) => {
x -= 40.0 * state.frame_time as f32;
state.textscript_vm.illustration_state =
if x <= -160.0 { IllustrationState::Hidden } else { IllustrationState::FadeOut(x) };
}
_ => (),
}
}
}
impl GameEntity<()> for Credits {
fn tick(&mut self, state: &mut SharedGameState, custom: ()) -> GameResult {
fn tick(&mut self, _state: &mut SharedGameState, _custom: ()) -> GameResult {
Ok(())
}
fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, _frame: &Frame) -> GameResult {
if state.textscript_vm.illustration_state != IllustrationState::Hidden {
let x = match state.textscript_vm.illustration_state {
IllustrationState::FadeIn(x) | IllustrationState::FadeOut(x) => x,
_ => 0.0,
};
if let Some(tex) = &state.textscript_vm.current_illustration {
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, tex)?;
batch.add(x, 0.0);
batch.draw(ctx)?;
} else {
let rect = Rect::new_size((x * state.scale) as isize, 0, (160.0 * state.scale) as _, state.screen_size.1 as _);
graphics::draw_rect(ctx, rect, Color::from_rgb(0, 0, 32))?;
}
}
if state.creditscript_vm.lines.is_empty() {
return Ok(());
}

View file

@ -266,6 +266,7 @@ pub struct EngineConstants {
pub soundtracks: HashMap<String, String>,
pub music_table: Vec<String>,
pub organya_paths: Vec<String>,
pub credit_illustration_paths: Vec<String>,
}
impl Clone for EngineConstants {
@ -291,6 +292,7 @@ impl Clone for EngineConstants {
soundtracks: self.soundtracks.clone(),
music_table: self.music_table.clone(),
organya_paths: self.organya_paths.clone(),
credit_illustration_paths: self.credit_illustration_paths.clone(),
}
}
}
@ -1287,6 +1289,27 @@ impl EngineConstants {
"Bullet" => (320, 176),
"Caret" => (320, 240),
"casts" => (320, 240),
"Credit01" => (160, 240),
"Credit01a" => (160, 240),
"Credit02" => (160, 240),
"Credit02a" => (160, 240),
"Credit03" => (160, 240),
"Credit03a" => (160, 240),
"Credit04" => (160, 240),
"Credit05" => (160, 240),
"Credit06" => (160, 240),
"Credit07" => (160, 240),
"Credit08" => (160, 240),
"Credit09" => (160, 240),
"Credit10" => (160, 240),
"Credit11" => (160, 240),
"Credit12" => (160, 240),
"Credit13" => (160, 240),
"Credit14" => (160, 240),
"Credit15" => (160, 240),
"Credit16" => (160, 240),
"Credit17" => (160, 240),
"Credit18" => (160, 240),
"Face" => (288, 240),
"Face_0" => (288, 240), // nxengine
"Face_1" => (288, 240), // nxengine
@ -1487,6 +1510,11 @@ impl EngineConstants {
"/base/Org/".to_owned(), // CS+
"/Resource/ORG/".to_owned(), // CSE2E
],
credit_illustration_paths: vec![
"".to_owned(),
"Resource/BITMAP/".to_owned(), // CSE2E
"endpic/".to_owned(), // NXEngine
],
}
}

View file

@ -35,18 +35,23 @@ impl Frame {
}
pub fn immediate_update(&mut self, state: &mut SharedGameState, stage: &Stage) {
let mut screen_width = state.canvas_size.0;
if state.constants.is_switch {
screen_width += 10.0; // hack for scrolling
}
let tile_size = state.tile_size.as_int();
if (stage.map.width as usize).saturating_sub(1) * (tile_size as usize) < state.canvas_size.0 as usize {
self.x = -(((state.canvas_size.0 as i32 - (stage.map.width as i32 - 1) * tile_size) * 0x200) / 2);
if (stage.map.width as usize).saturating_sub(1) * (tile_size as usize) < screen_width as usize {
self.x = -(((screen_width as i32 - (stage.map.width as i32 - 1) * tile_size) * 0x200) / 2);
} else {
self.x = self.target_x - (state.canvas_size.0 as i32 * 0x200 / 2);
self.x = self.target_x - (screen_width as i32 * 0x200 / 2);
if self.x < 0 {
self.x = 0;
}
let max_x = (((stage.map.width as i32 - 1) * tile_size) - state.canvas_size.0 as i32) * 0x200;
let max_x = (((stage.map.width as i32 - 1) * tile_size) - screen_width as i32) * 0x200;
if self.x > max_x {
self.x = max_x;
}
@ -72,18 +77,22 @@ impl Frame {
}
pub fn update(&mut self, state: &mut SharedGameState, stage: &Stage) {
let mut screen_width = state.canvas_size.0;
if state.constants.is_switch {
screen_width += 10.0;
}
let tile_size = state.tile_size.as_int();
if (stage.map.width as usize).saturating_sub(1) * (tile_size as usize) < state.canvas_size.0 as usize {
self.x = -(((state.canvas_size.0 as i32 - (stage.map.width as i32 - 1) * tile_size) * 0x200) / 2);
if (stage.map.width as usize).saturating_sub(1) * (tile_size as usize) < screen_width as usize {
self.x = -(((screen_width as i32 - (stage.map.width as i32 - 1) * tile_size) * 0x200) / 2);
} else {
self.x += (self.target_x - (state.canvas_size.0 as i32 * 0x200 / 2) - self.x) / self.wait;
self.x += (self.target_x - (screen_width as i32 * 0x200 / 2) - self.x) / self.wait;
if self.x < 0 {
self.x = 0;
}
let max_x = (((stage.map.width as i32 - 1) * tile_size) - state.canvas_size.0 as i32) * 0x200;
let max_x = (((stage.map.width as i32 - 1) * tile_size) - screen_width as i32) * 0x200;
if self.x > max_x {
self.x = max_x;
}

View file

@ -75,7 +75,7 @@ pub trait BackendTexture {
}
#[allow(unreachable_code)]
pub fn init_backend(headless: bool) -> GameResult<Box<dyn Backend>> {
pub fn init_backend(headless: bool, size_hint: (u16, u16)) -> GameResult<Box<dyn Backend>> {
if headless {
return crate::framework::backend_null::NullBackend::new();
}
@ -87,7 +87,7 @@ pub fn init_backend(headless: bool) -> GameResult<Box<dyn Backend>> {
#[cfg(feature = "backend-sdl")]
{
return crate::framework::backend_sdl2::SDL2Backend::new();
return crate::framework::backend_sdl2::SDL2Backend::new(size_hint);
}
log::warn!("No backend compiled in, using null backend instead.");

View file

@ -31,13 +31,14 @@ use crate::GAME_SUSPENDED;
pub struct SDL2Backend {
context: Sdl,
size_hint: (u16, u16),
}
impl SDL2Backend {
pub fn new() -> GameResult<Box<dyn Backend>> {
pub fn new(size_hint: (u16, u16)) -> GameResult<Box<dyn Backend>> {
let context = sdl2::init().map_err(|e| GameError::WindowError(e))?;
let backend = SDL2Backend { context };
let backend = SDL2Backend { context, size_hint };
Ok(Box::new(backend))
}
@ -45,7 +46,7 @@ impl SDL2Backend {
impl Backend for SDL2Backend {
fn create_event_loop(&self) -> GameResult<Box<dyn BackendEventLoop>> {
SDL2EventLoop::new(&self.context)
SDL2EventLoop::new(&self.context, self.size_hint)
}
}
@ -64,7 +65,7 @@ struct SDL2Context {
}
impl SDL2EventLoop {
pub fn new(sdl: &Sdl) -> GameResult<Box<dyn BackendEventLoop>> {
pub fn new(sdl: &Sdl, size_hint: (u16, u16)) -> GameResult<Box<dyn BackendEventLoop>> {
let event_pump = sdl.event_pump().map_err(|e| GameError::WindowError(e))?;
let video = sdl.video().map_err(|e| GameError::WindowError(e))?;
let gl_attr = video.gl_attr();
@ -72,7 +73,7 @@ impl SDL2EventLoop {
gl_attr.set_context_profile(GLProfile::Core);
gl_attr.set_context_version(3, 0);
let mut window = video.window("Cave Story (doukutsu-rs)", 640, 480);
let mut window = video.window("Cave Story (doukutsu-rs)", size_hint.0 as _, size_hint.1 as _);
window.position_centered();
window.resizable();

View file

@ -6,6 +6,7 @@ use crate::Game;
pub struct Context {
pub headless: bool,
pub size_hint: (u16, u16),
pub(crate) filesystem: Filesystem,
pub(crate) renderer: Option<Box<dyn BackendRenderer>>,
pub(crate) keyboard_context: KeyboardContext,
@ -18,6 +19,7 @@ impl Context {
pub fn new() -> Context {
Context {
headless: false,
size_hint: (640, 480),
filesystem: Filesystem::new(),
renderer: None,
keyboard_context: KeyboardContext::new(),
@ -28,7 +30,7 @@ impl Context {
}
pub fn run(&mut self, game: &mut Game) -> GameResult {
let backend = init_backend(self.headless)?;
let backend = init_backend(self.headless, self.size_hint)?;
let mut event_loop = backend.create_event_loop()?;
self.renderer = Some(event_loop.new_renderer()?);

View file

@ -2018,6 +2018,7 @@ impl Scene for GameScene {
};
self.inventory_dim = self.inventory_dim.clamp(0.0, 1.0);
self.credits.draw_tick(state);
Ok(())
}

View file

@ -97,6 +97,14 @@ pub enum TextScriptExecutionState {
Reset,
}
#[derive(PartialEq, Copy, Clone)]
pub enum IllustrationState {
Hidden,
Shown,
FadeIn(f32),
FadeOut(f32),
}
pub struct TextScriptVM {
pub scripts: Rc<RefCell<Scripts>>,
pub state: TextScriptExecutionState,
@ -117,6 +125,8 @@ pub struct TextScriptVM {
pub line_1: Vec<char>,
pub line_2: Vec<char>,
pub line_3: Vec<char>,
pub current_illustration: Option<String>,
pub illustration_state: IllustrationState,
prev_char: char,
}
@ -180,6 +190,8 @@ impl TextScriptVM {
line_1: Vec::with_capacity(24),
line_2: Vec::with_capacity(24),
line_3: Vec::with_capacity(24),
current_illustration: None,
illustration_state: IllustrationState::Hidden,
prev_char: '\x00',
}
}
@ -219,6 +231,8 @@ impl TextScriptVM {
pub fn reset(&mut self) {
self.state = TextScriptExecutionState::Ended;
self.flags.0 = 0;
self.current_illustration = None;
self.illustration_state = IllustrationState::Hidden;
self.clear_text_box();
}
@ -1520,10 +1534,33 @@ impl TextScriptVM {
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
TSCOpCode::SIL => {
let number = read_cur_varint(&mut cursor)? as u16;
for path in state.constants.credit_illustration_paths.iter() {
let path = format!("{}Credit{:02}", path, number);
if let Some(_) = state.texture_set.find_texture(ctx, &path) {
state.textscript_vm.current_illustration = Some(path);
break;
}
}
state.textscript_vm.illustration_state = IllustrationState::FadeIn(-160.0);
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
TSCOpCode::CIL => {
state.textscript_vm.illustration_state = if let Some(_) = state.textscript_vm.current_illustration {
IllustrationState::FadeOut(0.0)
} else {
IllustrationState::Hidden
};
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
// unimplemented opcodes
// Zero operands
TSCOpCode::CIL
| TSCOpCode::CPS
TSCOpCode::CPS
| TSCOpCode::KE2
| TSCOpCode::CSS
| TSCOpCode::MLP
@ -1536,7 +1573,7 @@ impl TextScriptVM {
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
// One operand codes
TSCOpCode::UNJ | TSCOpCode::XX1 | TSCOpCode::SIL | TSCOpCode::SSS | TSCOpCode::ACH => {
TSCOpCode::UNJ | TSCOpCode::XX1 | TSCOpCode::SSS | TSCOpCode::ACH => {
let par_a = read_cur_varint(&mut cursor)?;
log::warn!("unimplemented opcode: {:?} {}", op, par_a);

View file

@ -164,6 +164,7 @@ impl SharedGameState {
base_path = "/base/";
} else if filesystem::exists(ctx, "/base/lighting.tbl") {
info!("Cave Story+ (Switch) data files detected.");
ctx.size_hint = (854, 480);
constants.apply_csplus_patches(&sound_manager);
constants.apply_csplus_nx_patches();
base_path = "/base/";

View file

@ -354,18 +354,26 @@ impl TextureSet {
create_texture(ctx, width as u16, height as u16, &img)
}
pub fn find_texture(
&self,
ctx: &mut Context,
name: &str,
) -> Option<String> {
self
.paths
.iter()
.find_map(|s| {
FILE_TYPES.iter().map(|ext| [s, name, ext].join("")).find(|path| filesystem::exists(ctx, path))
})
}
pub fn load_texture(
&self,
ctx: &mut Context,
constants: &EngineConstants,
name: &str,
) -> GameResult<Box<dyn SpriteBatch>> {
let path = self
.paths
.iter()
.find_map(|s| {
FILE_TYPES.iter().map(|ext| [s, name, ext].join("")).find(|path| filesystem::exists(ctx, path))
})
let path = self.find_texture(ctx, name)
.ok_or_else(|| GameError::ResourceLoadError(format!("Texture {} does not exist.", name)))?;
let has_glow_layer = self
@ -376,7 +384,7 @@ impl TextureSet {
})
.is_some();
info!("Loading texture: {}", path);
info!("Loading texture: {} -> {}", name, path);
let batch = self.load_image(ctx, &path)?;
let size = batch.dimensions();