# HG changeset patch # User Emmanuel Gil Peyrot # Date 1565546145 -7200 # Node ID f08e8e3c61964507260bd3fab5d9a9536438304b # Parent 994f41154be87a0b1776c7679ed090a10d38595f Use bitflags for the rank, instead of an u16. diff --git a/Cargo.toml b/Cargo.toml --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ license = "GPL-3.0-or-later" nom = "5" encoding_rs = "0.8" image = { version = "0.22", default-features = false, features = ["png_codec"] } +bitflags = "1" luminance = { version = "*", path = "../luminance/luminance" } luminance-glfw = { version = "*", path = "../luminance/luminance-glfw", default-features = false } luminance-derive = { version = "*", path = "../luminance/luminance-derive" } diff --git a/examples/eclrenderer.rs b/examples/eclrenderer.rs --- a/examples/eclrenderer.rs +++ b/examples/eclrenderer.rs @@ -12,7 +12,7 @@ use luminance_glfw::event::{Action, Key, use luminance_glfw::surface::{GlfwSurface, Surface, WindowDim, WindowOpt}; use touhou::th06::anm0::Anm0; use touhou::th06::anm0_vm::{Sprite, Vertex as FakeVertex}; -use touhou::th06::ecl::Ecl; +use touhou::th06::ecl::{Ecl, Rank}; use touhou::th06::ecl_vm::EclRunner; use touhou::th06::enemy::{Enemy, Game, Position}; use touhou::util::math::{perspective, setup_camera}; @@ -124,7 +124,7 @@ fn main() { let prng = Rc::new(RefCell::new(Prng::new(0))); // Create the Game god object. - let game = Game::new(prng); + let game = Game::new(prng, Rank::Normal); let game = Rc::new(RefCell::new(game)); // And the enemy object. diff --git a/src/th06/ecl.rs b/src/th06/ecl.rs --- a/src/th06/ecl.rs +++ b/src/th06/ecl.rs @@ -6,6 +6,27 @@ use nom::{ number::complete::{le_u8, le_u16, le_u32, le_i16, le_i32, le_f32}, }; use encoding_rs::SHIFT_JIS; +use bitflags::bitflags; + +bitflags! { + /// Bit flags describing the current difficulty level. + pub struct Rank: u16 { + /// Easy mode. + const Easy = 0x100; + + /// Normal mode. + const Normal = 0x200; + + /// Hard mode. + const Hard = 0x400; + + /// Lunatic mode. + const Lunatic = 0x800; + + /// Any or all modes. + const All = 0xff00; + } +} /// A single instruction, part of a `Script`. #[derive(Debug, Clone)] @@ -13,8 +34,8 @@ pub struct CallSub { /// Time at which this instruction will be called. pub time: i32, - /// TODO - pub rank_mask: u16, + /// The difficulty level(s) this instruction will be called at. + pub rank_mask: Rank, /// TODO pub param_mask: u16, @@ -209,7 +230,7 @@ declare_sub_instructions!{ 81 => fn SetBulletLaunchOffset(x: f32, y: f32, z: f32), 82 => fn SetExtendedBulletAttributes(a: i32, b: i32, c: i32, d: i32, e: f32, f: f32, g: f32, h: f32), 83 => fn ChangeBulletsInStarBonus(), - // 84: ('i', None), + 84 => fn UNK0(UNK: i32), 85 => fn NewLaser(laser_type: i16, sprite_idx_offset: i16, angle: f32, speed: f32, start_offset: f32, end_offset: f32, max_length: f32, width: f32, start_duration: i32, duration: i32, end_duration: i32, grazing_delay: i32, grazing_extra_duration: i32, UNK1: i32), 86 => fn NewLaserTowardsPlayer(laser_type: i16, sprite_idx_offset: i16, angle: f32, speed: f32, start_offset: f32, end_offset: f32, max_length: f32, width: f32, start_duration: i32, duration: i32, end_duration: i32, grazing_delay: i32, grazing_extra_duration: i32, UNK1: i32), 87 => fn SetUpcomingLaserId(id: i32), @@ -295,6 +316,7 @@ fn parse_ecl(input: &[u8]) -> IResult<&[ let (i2, size) = le_u16(i2)?; let (i2, rank_mask) = le_u16(i2)?; + let rank_mask = Rank::from_bits(rank_mask).unwrap(); let (i2, param_mask) = le_u16(i2)?; // FIXME: this - 12 can trigger a panic, fuzz it! let data = &i2[..size as usize - 12]; diff --git a/src/th06/ecl_vm.rs b/src/th06/ecl_vm.rs --- a/src/th06/ecl_vm.rs +++ b/src/th06/ecl_vm.rs @@ -1,6 +1,6 @@ //! ECL runner. -use crate::th06::ecl::{Ecl, SubInstruction}; +use crate::th06::ecl::{Ecl, SubInstruction, Rank}; use crate::th06::enemy::Enemy; use crate::util::prng::Prng; use std::cell::RefCell; @@ -56,7 +56,7 @@ impl EclRunner { self.ip += 1; let rank = self.enemy.borrow().get_rank(); - if call.rank_mask & (0x100 << rank) == 0 { + if (call.rank_mask & rank).is_empty() { continue; } @@ -82,7 +82,7 @@ impl EclRunner { -10010 => self.variables.2[1], -10011 => self.variables.2[2], -10012 => self.variables.2[3], - -10013 => enemy.get_rank(), + -10013 => enemy.get_rank().bits() as i32, -10014 => enemy.get_difficulty(), -10015 => enemy.pos.x as i32, -10016 => enemy.pos.y as i32, @@ -114,7 +114,7 @@ impl EclRunner { -10010.0 => self.variables.2[1] as f32, -10011.0 => self.variables.2[2] as f32, -10012.0 => self.variables.2[3] as f32, - -10013.0 => enemy.get_rank() as f32, + -10013.0 => enemy.get_rank().bits() as f32, -10014.0 => enemy.get_difficulty() as f32, -10015.0 => enemy.pos.x, -10016.0 => enemy.pos.y, diff --git a/src/th06/enemy.rs b/src/th06/enemy.rs --- a/src/th06/enemy.rs +++ b/src/th06/enemy.rs @@ -2,6 +2,7 @@ use crate::th06::anm0::Anm0; use crate::th06::anm0_vm::{Sprite, AnmRunner}; +use crate::th06::ecl::Rank; use crate::th06::interpolator::{Interpolator1, Interpolator2}; use crate::util::prng::Prng; use std::cell::RefCell; @@ -60,18 +61,18 @@ pub struct Game { enemies: Vec>>, anmrunners: Vec>>, prng: Rc>, - rank: i32, + rank: Rank, difficulty: i32, } impl Game { /// Create said god struct. - pub fn new(prng: Rc>) -> Game { + pub fn new(prng: Rc>, rank: Rank) -> Game { Game { enemies: Vec::new(), anmrunners: Vec::new(), prng, - rank: 0, + rank, difficulty: 0, } } @@ -262,7 +263,7 @@ impl Enemy { self.frame += 1; } - pub(crate) fn get_rank(&self) -> i32 { + pub(crate) fn get_rank(&self) -> Rank { let game = self.game.upgrade().unwrap(); let game = game.borrow(); game.rank