snack blocks, more particles and rng

This commit is contained in:
Alula 2020-08-21 07:27:26 +02:00
parent ef9ca2e89a
commit 7f37153056
No known key found for this signature in database
GPG Key ID: 3E00485503A1D8BA
7 changed files with 130 additions and 30 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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