mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2025-01-16 15:47:01 +00:00
RNG Tweaks
This commit is contained in:
parent
b94b20bf76
commit
28a3f160c3
|
@ -22,6 +22,7 @@ pub struct Replay {
|
|||
pub controller: ReplayController,
|
||||
tick: usize,
|
||||
resume_tick: usize,
|
||||
is_active: bool,
|
||||
}
|
||||
|
||||
impl Replay {
|
||||
|
@ -34,11 +35,15 @@ impl Replay {
|
|||
controller: ReplayController::new(),
|
||||
tick: 0,
|
||||
resume_tick: 0,
|
||||
is_active: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_recording(&mut self, state: &mut SharedGameState) {
|
||||
pub fn initialize_recording(&mut self, state: &mut SharedGameState) {
|
||||
if !self.is_active {
|
||||
self.rng_seed = state.game_rng.dump_state();
|
||||
self.is_active = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stop_recording(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
|
@ -47,10 +52,13 @@ impl Replay {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn start_playback(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
pub fn initialize_playback(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
if !self.is_active {
|
||||
state.replay_state = ReplayState::Playback;
|
||||
self.read_replay(state, ctx)?;
|
||||
state.game_rng.load_state(self.rng_seed);
|
||||
self.is_active = true;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -927,11 +927,7 @@ impl NPC {
|
|||
&& self.y - 0x4000 < player.y
|
||||
&& self.y + 0x2000 > player.y
|
||||
{
|
||||
if self.x <= player.x {
|
||||
self.direction = Direction::Right;
|
||||
} else {
|
||||
self.direction = Direction::Left;
|
||||
}
|
||||
self.face_player(player);
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
|
@ -963,11 +959,7 @@ impl NPC {
|
|||
|
||||
let player = self.get_closest_player_mut(players);
|
||||
|
||||
if self.x <= player.x {
|
||||
self.direction = Direction::Right;
|
||||
} else {
|
||||
self.direction = Direction::Left;
|
||||
}
|
||||
self.face_player(player);
|
||||
}
|
||||
|
||||
self.animate(2, 2, 5);
|
||||
|
|
|
@ -46,9 +46,13 @@ impl BossNPC {
|
|||
|
||||
parts[0].cond.set_alive(true);
|
||||
|
||||
for (i, part) in parts.iter_mut().enumerate() {
|
||||
BossNPC { boss_type: 0, parts, hurt_sound: [0; 20], death_sound: [0; 20] }
|
||||
}
|
||||
|
||||
pub fn init_rng(&mut self, seed: i32) {
|
||||
for (i, part) in self.parts.iter_mut().enumerate() {
|
||||
part.rng.load_state(
|
||||
((i as u32)
|
||||
((seed.abs() as u32 + i as u32)
|
||||
.wrapping_add(3271284409)
|
||||
.rotate_left(5)
|
||||
.wrapping_mul(3815776271)
|
||||
|
@ -57,8 +61,6 @@ impl BossNPC {
|
|||
& 0xffffffff) as u32,
|
||||
);
|
||||
}
|
||||
|
||||
BossNPC { boss_type: 0, parts, hurt_sound: [0; 20], death_sound: [0; 20] }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -175,8 +175,7 @@ impl BossNPC {
|
|||
self.parts[3].life = 60;
|
||||
self.parts[3].size = 2;
|
||||
self.parts[3].target_x = 0;
|
||||
self.parts[3].display_bounds =
|
||||
Rect { left: 0x1000, top: 0x1000, right: 0x1000, bottom: 0x1000 };
|
||||
self.parts[3].display_bounds = Rect { left: 0x1000, top: 0x1000, right: 0x1000, bottom: 0x1000 };
|
||||
self.parts[3].hit_bounds =
|
||||
Rect { left: 5 * 0x200, top: 5 * 0x200, right: 5 * 0x200, bottom: 5 * 0x200 };
|
||||
self.parts[3].npc_flags.set_ignore_solidity(true);
|
||||
|
@ -200,8 +199,7 @@ impl BossNPC {
|
|||
self.parts[7].anim_num = 0;
|
||||
self.parts[7].display_bounds =
|
||||
Rect { left: 52 * 0x200, top: 24 * 0x200, right: 52 * 0x200, bottom: 24 * 0x200 };
|
||||
self.parts[7].hit_bounds =
|
||||
Rect { left: 0x1000, top: 24 * 0x200, right: 0x1000, bottom: 0x2000 };
|
||||
self.parts[7].hit_bounds = Rect { left: 0x1000, top: 24 * 0x200, right: 0x1000, bottom: 0x2000 };
|
||||
self.parts[7].npc_flags.set_ignore_solidity(true);
|
||||
|
||||
self.parts[9].cond.set_alive(true);
|
||||
|
@ -212,8 +210,7 @@ impl BossNPC {
|
|||
self.parts[9].direction = Direction::Up;
|
||||
self.parts[9].display_bounds =
|
||||
Rect { left: 36 * 0x200, top: 0x1000, right: 36 * 0x200, bottom: 24 * 0x200 };
|
||||
self.parts[9].hit_bounds =
|
||||
Rect { left: 28 * 0x200, top: 0x1000, right: 28 * 0x200, bottom: 0x2000 };
|
||||
self.parts[9].hit_bounds = Rect { left: 28 * 0x200, top: 0x1000, right: 28 * 0x200, bottom: 0x2000 };
|
||||
self.hurt_sound[9] = 52;
|
||||
self.parts[9].npc_flags.set_rear_and_top_not_hurt(true);
|
||||
self.parts[9].npc_flags.set_ignore_solidity(true);
|
||||
|
@ -260,10 +257,6 @@ impl BossNPC {
|
|||
self.parts[16].anim_num = 3;
|
||||
self.parts[16].display_bounds.left = 42 * 0x200;
|
||||
self.parts[16].display_bounds.right = 30 * 0x200;
|
||||
|
||||
for npc in &mut self.parts {
|
||||
npc.init_rng();
|
||||
}
|
||||
}
|
||||
10 | 11 => {
|
||||
if self.parts[0].action_num == 10 {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::cell::{Cell, UnsafeCell};
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
use crate::framework::error::{GameResult, GameError};
|
||||
use crate::framework::error::{GameError, GameResult};
|
||||
|
||||
use crate::npc::NPC;
|
||||
|
||||
|
@ -15,6 +15,7 @@ pub struct NPCList {
|
|||
// from theoretically performing some optimizations that might break the code.
|
||||
npcs: Box<UnsafeCell<[NPC; NPC_LIST_MAX_CAP]>>,
|
||||
max_npc: Cell<u16>,
|
||||
seed: i32,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -31,6 +32,7 @@ impl NPCList {
|
|||
parts_uninit
|
||||
})),
|
||||
max_npc: Cell::new(0),
|
||||
seed: 0,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
|
@ -42,6 +44,10 @@ impl NPCList {
|
|||
map
|
||||
}
|
||||
|
||||
pub fn set_rng_seed(&mut self, seed: i32) {
|
||||
self.seed = seed;
|
||||
}
|
||||
|
||||
/// Inserts NPC into list in first available slot after given ID.
|
||||
pub fn spawn(&self, min_id: u16, mut npc: NPC) -> GameResult {
|
||||
let npc_len = unsafe { self.npcs().len() };
|
||||
|
@ -60,7 +66,7 @@ impl NPCList {
|
|||
npc.tsc_direction = npc.direction as u16;
|
||||
}
|
||||
|
||||
npc.init_rng();
|
||||
npc.init_rng(self.seed);
|
||||
|
||||
*npc_ref = npc;
|
||||
|
||||
|
@ -89,7 +95,7 @@ impl NPCList {
|
|||
npc.tsc_direction = npc.direction as u16;
|
||||
}
|
||||
|
||||
npc.init_rng();
|
||||
npc.init_rng(self.seed);
|
||||
|
||||
unsafe {
|
||||
let npc_ref = self.npcs_mut().get_unchecked_mut(id as usize);
|
||||
|
@ -105,9 +111,7 @@ impl NPCList {
|
|||
|
||||
/// Returns a mutable reference to NPC from this list.
|
||||
pub fn get_npc<'a: 'b, 'b>(&'a self, id: usize) -> Option<&'b mut NPC> {
|
||||
unsafe {
|
||||
self.npcs_mut().get_mut(id)
|
||||
}
|
||||
unsafe { self.npcs_mut().get_mut(id) }
|
||||
}
|
||||
|
||||
/// Returns an iterator that iterates over allocated (not up to it's capacity) NPC slots.
|
||||
|
@ -156,10 +160,7 @@ pub struct NPCListMutableIterator<'a> {
|
|||
|
||||
impl<'a> NPCListMutableIterator<'a> {
|
||||
pub fn new(map: &'a NPCList) -> NPCListMutableIterator<'a> {
|
||||
NPCListMutableIterator {
|
||||
index: 0,
|
||||
map,
|
||||
}
|
||||
NPCListMutableIterator { index: 0, map }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -185,10 +186,7 @@ pub struct NPCListMutableAliveIterator<'a> {
|
|||
|
||||
impl<'a> NPCListMutableAliveIterator<'a> {
|
||||
pub fn new(map: &'a NPCList) -> NPCListMutableAliveIterator<'a> {
|
||||
NPCListMutableAliveIterator {
|
||||
index: 0,
|
||||
map,
|
||||
}
|
||||
NPCListMutableAliveIterator { index: 0, map }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,13 +16,14 @@ const MAX_FALL_SPEED: i32 = 0x5FF;
|
|||
|
||||
impl NPC {
|
||||
/// Initializes the RNG. Called when the [NPC] is being added to an [NPCList].
|
||||
pub(crate) fn init_rng(&mut self) {
|
||||
pub(crate) fn init_rng(&mut self, seed: i32) {
|
||||
self.rng = Xoroshiro32PlusPlus::new(
|
||||
(self.id as u32)
|
||||
.wrapping_sub(self.npc_type as u32)
|
||||
.rotate_right(5)
|
||||
.wrapping_sub(self.flag_num as u32)
|
||||
.rotate_right((self.event_num % 13) as u32)
|
||||
.wrapping_add(seed.abs() as u32)
|
||||
.wrapping_mul(214013)
|
||||
.rotate_right(13)
|
||||
.wrapping_add(2531011)
|
||||
|
|
|
@ -1592,12 +1592,16 @@ impl GameScene {
|
|||
|
||||
impl Scene for GameScene {
|
||||
fn init(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
let seed = (self.player1.max_life as i32)
|
||||
.wrapping_add(self.player1.x as i32)
|
||||
.wrapping_add(self.player1.y as i32)
|
||||
.wrapping_add(self.stage_id as i32)
|
||||
.rotate_right(7);
|
||||
state.game_rng = XorShift::new(seed);
|
||||
if state.mod_path.is_some() && state.replay_state == ReplayState::Recording {
|
||||
self.replay.initialize_recording(state);
|
||||
}
|
||||
|
||||
if state.mod_path.is_some() && state.replay_state == ReplayState::Playback {
|
||||
self.replay.initialize_playback(state, ctx)?;
|
||||
}
|
||||
|
||||
self.npc_list.set_rng_seed(state.game_rng.next());
|
||||
self.boss.init_rng(state.game_rng.next());
|
||||
state.textscript_vm.set_scene_script(self.stage.load_text_script(
|
||||
&state.constants.base_paths,
|
||||
&state.constants,
|
||||
|
@ -1677,14 +1681,6 @@ impl Scene for GameScene {
|
|||
self.pause_menu.init(state, ctx)?;
|
||||
self.whimsical_star.init(&self.player1);
|
||||
|
||||
if state.mod_path.is_some() && state.replay_state == ReplayState::Recording {
|
||||
self.replay.start_recording(state);
|
||||
}
|
||||
|
||||
if state.mod_path.is_some() && state.replay_state == ReplayState::Playback {
|
||||
self.replay.start_playback(state, ctx)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -360,7 +360,7 @@ impl SharedGameState {
|
|||
skip_flags: bitvec::bitvec![0; 64],
|
||||
map_flags: bitvec::bitvec![0; 64],
|
||||
fade_state: FadeState::Hidden,
|
||||
game_rng: XorShift::new(0),
|
||||
game_rng: XorShift::new(chrono::Local::now().timestamp() as i32),
|
||||
effect_rng: XorShift::new(123),
|
||||
tile_size: TileSize::Tile16x16,
|
||||
quake_counter: 0,
|
||||
|
@ -593,7 +593,7 @@ impl SharedGameState {
|
|||
self.control_flags.0 = 0;
|
||||
self.game_flags = bitvec::bitvec![0; 8000];
|
||||
self.fade_state = FadeState::Hidden;
|
||||
self.game_rng = XorShift::new(0);
|
||||
self.game_rng = XorShift::new(chrono::Local::now().timestamp() as i32);
|
||||
self.teleporter_slots.clear();
|
||||
self.quake_counter = 0;
|
||||
self.carets.clear();
|
||||
|
|
Loading…
Reference in a new issue