snack blocks, more particles and rng
This commit is contained in:
parent
ef9ca2e89a
commit
7f37153056
41
src/caret.rs
41
src/caret.rs
|
@ -1,6 +1,7 @@
|
|||
use crate::bitfield;
|
||||
use crate::common::{Direction, Rect};
|
||||
use crate::engine_constants::EngineConstants;
|
||||
use crate::rng::RNG;
|
||||
|
||||
bitfield! {
|
||||
pub struct Cond(u16);
|
||||
|
@ -24,7 +25,7 @@ pub enum CaretType {
|
|||
LevelUp,
|
||||
HurtParticles,
|
||||
Explosion,
|
||||
SmallParticles,
|
||||
LittleParticles,
|
||||
Unknown,
|
||||
SmallProjectileDissipation,
|
||||
Empty,
|
||||
|
@ -65,7 +66,7 @@ impl Caret {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, constants: &EngineConstants) {
|
||||
pub fn tick(&mut self, rng: &RNG, constants: &EngineConstants) {
|
||||
match self.ctype {
|
||||
CaretType::None => {}
|
||||
CaretType::Bubble => {}
|
||||
|
@ -115,7 +116,41 @@ impl Caret {
|
|||
CaretType::LevelUp => {}
|
||||
CaretType::HurtParticles => {}
|
||||
CaretType::Explosion => {}
|
||||
CaretType::SmallParticles => {}
|
||||
CaretType::LittleParticles => {
|
||||
if self.anim_num == 0 {
|
||||
match self.direct {
|
||||
Direction::Left => {
|
||||
self.vel_x = rng.range(-0x300..0x300) as isize;
|
||||
self.vel_y = rng.range(-0x100..0x100) as isize;
|
||||
}
|
||||
Direction::Up => {
|
||||
self.vel_y = rng.range(1..3) as isize * 0x100;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
self.anim_num += 1;
|
||||
|
||||
if self.direct == Direction::Left {
|
||||
self.vel_x = (self.vel_x * 4) / 5;
|
||||
self.vel_y = (self.vel_y * 4) / 5;
|
||||
}
|
||||
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
if self.anim_num == 21 {
|
||||
self.cond.set_visible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
self.anim_rect = constants.caret.little_particles_rects[self.anim_num / 2 % constants.caret.little_particles_rects.len()];
|
||||
|
||||
if self.direct == Direction::Right {
|
||||
self.x -= 4 * 0x200;
|
||||
}
|
||||
}
|
||||
CaretType::Unknown => {
|
||||
// not implemented because it was apparently broken in og game?
|
||||
self.cond.set_visible(false);
|
||||
|
|
|
@ -51,6 +51,7 @@ pub struct CaretConsts {
|
|||
pub offsets: [(isize, isize); 18],
|
||||
pub bubble_left_rects: Vec<Rect<usize>>,
|
||||
pub bubble_right_rects: Vec<Rect<usize>>,
|
||||
pub little_particles_rects: Vec<Rect<usize>>,
|
||||
pub exhaust_rects: Vec<Rect<usize>>,
|
||||
pub question_left_rect: Rect<usize>,
|
||||
pub question_right_rect: Rect<usize>,
|
||||
|
@ -62,6 +63,7 @@ impl Clone for CaretConsts {
|
|||
offsets: self.offsets,
|
||||
bubble_left_rects: self.bubble_left_rects.clone(),
|
||||
bubble_right_rects: self.bubble_right_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,
|
||||
|
@ -69,12 +71,26 @@ impl Clone for CaretConsts {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WorldConsts {
|
||||
pub snack_rect: Rect<usize>,
|
||||
}
|
||||
|
||||
impl Clone for WorldConsts {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
snack_rect: self.snack_rect,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EngineConstants {
|
||||
pub is_cs_plus: bool,
|
||||
pub my_char: MyCharConsts,
|
||||
pub booster: BoosterConsts,
|
||||
pub caret: CaretConsts,
|
||||
pub world: WorldConsts,
|
||||
pub tex_sizes: HashMap<String, (usize, usize)>,
|
||||
}
|
||||
|
||||
|
@ -85,6 +101,7 @@ impl Clone for EngineConstants {
|
|||
my_char: self.my_char,
|
||||
booster: self.booster,
|
||||
caret: self.caret.clone(),
|
||||
world: self.world.clone(),
|
||||
tex_sizes: self.tex_sizes.clone(),
|
||||
}
|
||||
}
|
||||
|
@ -194,6 +211,10 @@ impl EngineConstants {
|
|||
Rect { left: 80, top: 24, right: 88, bottom: 32 },
|
||||
Rect { left: 88, top: 24, right: 96, bottom: 32 },
|
||||
],
|
||||
little_particles_rects: vec![
|
||||
Rect { left: 56, top: 24, right: 64, bottom: 32 },
|
||||
Rect { left: 0, top: 0, right: 0, bottom: 0 },
|
||||
],
|
||||
exhaust_rects: vec![
|
||||
Rect { left: 56, top: 0, right: 64, bottom: 8 },
|
||||
Rect { left: 64, top: 0, right: 72, bottom: 8 },
|
||||
|
@ -206,6 +227,9 @@ impl EngineConstants {
|
|||
question_left_rect: Rect { left: 0, top: 80, right: 16, bottom: 96 },
|
||||
question_right_rect: Rect { left: 48, top: 64, right: 64, bottom: 80 },
|
||||
},
|
||||
world: WorldConsts {
|
||||
snack_rect: Rect { left: 256, top: 48, right: 272, bottom: 64 },
|
||||
},
|
||||
tex_sizes: hashmap! {
|
||||
str!("ArmsImage") => (256, 16),
|
||||
str!("Arms") => (320, 200),
|
||||
|
|
|
@ -37,6 +37,8 @@ use crate::texture_set::TextureSet;
|
|||
use crate::ui::UI;
|
||||
use crate::caret::{Caret, CaretType};
|
||||
use crate::common::Direction;
|
||||
use crate::rng::RNG;
|
||||
use std::time::Instant;
|
||||
|
||||
mod caret;
|
||||
mod common;
|
||||
|
@ -91,6 +93,8 @@ struct Game {
|
|||
|
||||
pub struct SharedGameState {
|
||||
pub flags: GameFlags,
|
||||
pub game_rng: RNG,
|
||||
pub effect_rng: RNG,
|
||||
pub carets: Vec<Caret>,
|
||||
pub key_state: KeyState,
|
||||
pub key_trigger: KeyState,
|
||||
|
@ -116,7 +120,7 @@ impl SharedGameState {
|
|||
|
||||
pub fn tick_carets(&mut self) {
|
||||
for caret in self.carets.iter_mut() {
|
||||
caret.tick(&self.constants);
|
||||
caret.tick(&self.effect_rng, &self.constants);
|
||||
}
|
||||
|
||||
self.carets.retain(|c| !c.is_dead());
|
||||
|
@ -155,6 +159,8 @@ impl Game {
|
|||
def_matrix: DrawParam::new().to_matrix(),
|
||||
state: SharedGameState {
|
||||
flags: GameFlags(0),
|
||||
game_rng: RNG::new(0),
|
||||
effect_rng: RNG::new(Instant::now().elapsed().as_nanos() as i32),
|
||||
carets: Vec::new(),
|
||||
key_state: KeyState(0),
|
||||
key_trigger: KeyState(0),
|
||||
|
|
|
@ -33,6 +33,9 @@ bitfield! {
|
|||
pub flag_x20000, set_flag_x20000: 17; // 0x20000
|
||||
pub flag_x40000, set_flag_x40000: 18; // 0x40000
|
||||
pub flag_x80000, set_flag_x80000: 19; // 0x80000
|
||||
|
||||
// engine specific flags
|
||||
pub head_bounced, set_head_bounced: 31;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
|
@ -151,6 +154,12 @@ impl Player {
|
|||
let physics = if self.flags.underwater() { state.constants.my_char.water_physics } else { state.constants.my_char.air_physics };
|
||||
|
||||
self.question = false;
|
||||
if self.flags.head_bounced() {
|
||||
self.flags.set_head_bounced(false);
|
||||
// todo: PlaySoundObject(3, SOUND_MODE_PLAY);
|
||||
state.create_caret(self.x, self.y - self.hit.top as isize, CaretType::LittleParticles, Direction::Left);
|
||||
state.create_caret(self.x, self.y - self.hit.top as isize, CaretType::LittleParticles, Direction::Left);
|
||||
}
|
||||
|
||||
if !state.flags.control_enabled() {
|
||||
self.booster_switch = 0;
|
||||
|
|
|
@ -55,7 +55,7 @@ impl Player {
|
|||
self.y = ((y * 0x10 + 8) * 0x200) + self.hit.top as isize;
|
||||
|
||||
if !self.cond.cond_x02() && self.vel_y < -0x200 {
|
||||
// PutLittleStar(); todo
|
||||
self.flags.set_head_bounced(true);
|
||||
}
|
||||
|
||||
if self.vel_y < 0 {
|
||||
|
@ -92,7 +92,7 @@ impl Player {
|
|||
self.y = (y * 0x10 * 0x200) - ((self.x - x * 0x10 * 0x200) / 2) + 0x800 + self.hit.top as isize;
|
||||
|
||||
if !self.cond.cond_x02() && self.vel_y < -0x200 {
|
||||
// PutLittleStar(); todo
|
||||
self.flags.set_head_bounced(true);
|
||||
}
|
||||
|
||||
if self.vel_y < 0 {
|
||||
|
@ -111,7 +111,7 @@ impl Player {
|
|||
self.y = (y * 0x10 * 0x200) - ((self.x - x * 0x10 * 0x200) / 2) - 0x800 + self.hit.top as isize;
|
||||
|
||||
if !self.cond.cond_x02() && self.vel_y < -0x200 {
|
||||
// PutLittleStar(); todo
|
||||
self.flags.set_head_bounced(true);
|
||||
}
|
||||
|
||||
if self.vel_y < 0 {
|
||||
|
@ -130,7 +130,7 @@ impl Player {
|
|||
self.y = (y * 0x10 * 0x200) + ((self.x - x * 0x10 * 0x200) / 2) - 0x800 + self.hit.top as isize;
|
||||
|
||||
if !self.cond.cond_x02() && self.vel_y < -0x200 {
|
||||
// PutLittleStar(); todo
|
||||
self.flags.set_head_bounced(true);
|
||||
}
|
||||
|
||||
if self.vel_y < 0 {
|
||||
|
@ -149,7 +149,7 @@ impl Player {
|
|||
self.y = (y * 0x10 * 0x200) + ((self.x - x * 0x10 * 0x200) / 2) + 0x800 + self.hit.top as isize;
|
||||
|
||||
if !self.cond.cond_x02() && self.vel_y < -0x200 {
|
||||
// PutLittleStar(); todo
|
||||
self.flags.set_head_bounced(true);
|
||||
}
|
||||
|
||||
if self.vel_y < 0 {
|
||||
|
|
27
src/rng.rs
27
src/rng.rs
|
@ -1,22 +1,23 @@
|
|||
/// Stateful RNG
|
||||
pub struct RNG {
|
||||
pub seed: u32,
|
||||
}
|
||||
use std::cell::Cell;
|
||||
|
||||
pub struct RNG(Cell<i32>);
|
||||
|
||||
impl RNG {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
seed: 0,
|
||||
}
|
||||
pub fn new(seed: i32) -> Self {
|
||||
Self(Cell::new(seed))
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> u32 {
|
||||
pub fn next(&self) -> i32 {
|
||||
// MSVC LCG values
|
||||
self.seed = self.seed.wrapping_mul(214013).wrapping_add(2531011);
|
||||
self.seed
|
||||
self.0.replace(self.0.get().wrapping_mul(214013).wrapping_add(2531011));
|
||||
self.0.get()
|
||||
}
|
||||
|
||||
pub fn range(&mut self, start: i32, end: i32) -> i32 {
|
||||
start + (self.next() % (end - start) as u32) as i32
|
||||
pub fn next_u32(&self) -> u32 {
|
||||
self.next() as u32
|
||||
}
|
||||
|
||||
pub fn range(&self, range: std::ops::Range<i32>) -> i32 {
|
||||
range.start.wrapping_add(self.next() % (range.end.wrapping_sub(range.start).wrapping_add(1)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ pub struct GameScene {
|
|||
tex_background_name: String,
|
||||
tex_caret_name: String,
|
||||
tex_hud_name: String,
|
||||
tex_npcsym_name: String,
|
||||
tex_tileset_name: String,
|
||||
life_bar: usize,
|
||||
life_bar_count: usize,
|
||||
|
@ -30,6 +31,7 @@ pub enum TileLayer {
|
|||
All,
|
||||
Background,
|
||||
Foreground,
|
||||
Snack,
|
||||
}
|
||||
|
||||
#[derive(Debug, EnumIter, PartialEq, Eq, Hash, Copy, Clone)]
|
||||
|
@ -47,6 +49,7 @@ impl GameScene {
|
|||
let tex_background_name = stage.data.background.filename();
|
||||
let tex_caret_name = str!("Caret");
|
||||
let tex_hud_name = str!("TextBox");
|
||||
let tex_npcsym_name = str!("Npc/NpcSym");
|
||||
let tex_tileset_name = ["Stage/", &stage.data.tileset.filename()].join("");
|
||||
|
||||
Ok(Self {
|
||||
|
@ -61,6 +64,7 @@ impl GameScene {
|
|||
tex_background_name,
|
||||
tex_caret_name,
|
||||
tex_hud_name,
|
||||
tex_npcsym_name,
|
||||
tex_tileset_name,
|
||||
life_bar: 3,
|
||||
life_bar_count: 0,
|
||||
|
@ -193,7 +197,11 @@ impl GameScene {
|
|||
}
|
||||
|
||||
fn draw_tiles(&self, state: &mut SharedGameState, ctx: &mut Context, layer: TileLayer) -> GameResult {
|
||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, &self.tex_tileset_name)?;
|
||||
let tex = match layer {
|
||||
TileLayer::Snack => &self.tex_npcsym_name,
|
||||
_ => &self.tex_tileset_name,
|
||||
};
|
||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, tex)?;
|
||||
let mut rect = Rect::<usize>::new(0, 0, 16, 16);
|
||||
|
||||
let tile_start_x = clamp(self.frame.x / 0x200 / 16, 0, self.stage.map.width as isize) as usize;
|
||||
|
@ -201,6 +209,10 @@ impl GameScene {
|
|||
let tile_end_x = clamp((self.frame.x / 0x200 + 8 + state.canvas_size.0 as isize) / 16 + 1, 0, self.stage.map.width as isize) as usize;
|
||||
let tile_end_y = clamp((self.frame.y / 0x200 + 8 + state.canvas_size.1 as isize) / 16 + 1, 0, self.stage.map.height as isize) as usize;
|
||||
|
||||
if layer == TileLayer::Snack {
|
||||
rect = state.constants.world.snack_rect;
|
||||
}
|
||||
|
||||
for y in tile_start_y..tile_end_y {
|
||||
for x in tile_start_x..tile_end_x {
|
||||
let tile = *self.stage.map.tiles
|
||||
|
@ -212,22 +224,34 @@ impl GameScene {
|
|||
if self.stage.map.attrib[tile as usize] >= 0x20 {
|
||||
continue;
|
||||
}
|
||||
|
||||
rect.left = (tile as usize % 16) * 16;
|
||||
rect.top = (tile as usize / 16) * 16;
|
||||
rect.right = rect.left + 16;
|
||||
rect.bottom = rect.top + 16;
|
||||
}
|
||||
TileLayer::Foreground => {
|
||||
let attr = self.stage.map.attrib[tile as usize];
|
||||
if attr < 0x40 || attr >= 0x80 {
|
||||
|
||||
if attr < 0x40 || attr >= 0x80 || attr == 0x43 {
|
||||
continue;
|
||||
}
|
||||
|
||||
rect.left = (tile as usize % 16) * 16;
|
||||
rect.top = (tile as usize / 16) * 16;
|
||||
rect.right = rect.left + 16;
|
||||
rect.bottom = rect.top + 16;
|
||||
}
|
||||
TileLayer::Snack => {
|
||||
if self.stage.map.attrib[tile as usize] != 0x43 {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
rect.left = (tile as usize % 16) * 16;
|
||||
rect.top = (tile as usize / 16) * 16;
|
||||
rect.right = rect.left + 16;
|
||||
rect.bottom = rect.top + 16;
|
||||
|
||||
batch.add_rect((x as f32 * 16.0 - 8.0) - (self.frame.x / 0x200) as f32, (y as f32 * 16.0 - 8.0) - (self.frame.y / 0x200) as f32, &rect);
|
||||
batch.add_rect((x as f32 * 16.0 - 8.0) - (self.frame.x / 0x200) as f32,
|
||||
(y as f32 * 16.0 - 8.0) - (self.frame.y / 0x200) as f32, &rect);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -287,6 +311,7 @@ impl Scene for GameScene {
|
|||
self.draw_tiles(state, ctx, TileLayer::Background)?;
|
||||
self.player.draw(state, ctx, &self.frame)?;
|
||||
self.draw_tiles(state, ctx, TileLayer::Foreground)?;
|
||||
self.draw_tiles(state, ctx, TileLayer::Snack)?;
|
||||
self.draw_carets(state, ctx)?;
|
||||
|
||||
self.draw_hud(state, ctx)?;
|
||||
|
|
Loading…
Reference in New Issue