Merge branch 'master' of github.com:doukutsu-rs/doukutsu-rs into feature/i18n
This commit is contained in:
commit
1c2dfcf922
|
@ -9,7 +9,7 @@ in [Rust](https://www.rust-lang.org/).
|
|||
|
||||
- [Get stable/beta builds from GitHub Releases](https://github.com/doukutsu-rs/doukutsu-rs/releases) (executables only,
|
||||
no data files bundled, see below for instructions)
|
||||
- [Get nightly builds from AppVeyor](https://ci.appveyor.com/project/alula/doukutsu-rs) (select platform -> Artifacts ->
|
||||
- [Get nightly builds from AppVeyor](https://ci.appveyor.com/project/alula/doukutsu-rs) (has latest fixes and improvements, select platform -> Artifacts ->
|
||||
download the .zip)
|
||||
|
||||
#### Data files
|
||||
|
|
|
@ -514,7 +514,7 @@ pub struct NPCConsts {
|
|||
pub n149_horizontal_moving_block: Rect<u16>,
|
||||
|
||||
#[serde(default = "default_n150_quote")]
|
||||
pub n150_quote: SafeNPCRect<18>,
|
||||
pub n150_quote: SafeNPCRect<20>,
|
||||
|
||||
#[serde(default = "default_n151_blue_robot_standing")]
|
||||
pub n151_blue_robot_standing: SafeNPCRect<4>,
|
||||
|
@ -1115,6 +1115,7 @@ pub struct NPCConsts {
|
|||
#[serde(default = "default_n360_credits_thank_you")]
|
||||
pub n360_credits_thank_you: Rect<u16>,
|
||||
|
||||
// pub n370_second_quote: () // Same as n150_quote
|
||||
#[serde(default = "default_b01_omega")]
|
||||
pub b01_omega: SafeNPCRect<10>,
|
||||
|
||||
|
@ -2853,7 +2854,7 @@ fn default_n149_horizontal_moving_block() -> Rect<u16> {
|
|||
Rect { left: 16, top: 0, right: 48, bottom: 32 }
|
||||
}
|
||||
|
||||
fn default_n150_quote() -> SafeNPCRect<18> {
|
||||
fn default_n150_quote() -> SafeNPCRect<20> {
|
||||
SafeNPCRect([
|
||||
Rect { left: 0, top: 0, right: 16, bottom: 16 },
|
||||
Rect { left: 48, top: 0, right: 64, bottom: 16 },
|
||||
|
@ -2864,6 +2865,7 @@ fn default_n150_quote() -> SafeNPCRect<18> {
|
|||
Rect { left: 0, top: 0, right: 16, bottom: 16 },
|
||||
Rect { left: 160, top: 0, right: 176, bottom: 16 },
|
||||
Rect { left: 112, top: 0, right: 128, bottom: 16 },
|
||||
Rect { left: 96, top: 0, right: 112, bottom: 16 },
|
||||
Rect { left: 0, top: 16, right: 16, bottom: 32 },
|
||||
Rect { left: 48, top: 16, right: 64, bottom: 32 },
|
||||
Rect { left: 144, top: 16, right: 160, bottom: 32 },
|
||||
|
@ -2873,6 +2875,7 @@ fn default_n150_quote() -> SafeNPCRect<18> {
|
|||
Rect { left: 0, top: 16, right: 16, bottom: 32 },
|
||||
Rect { left: 160, top: 16, right: 176, bottom: 32 },
|
||||
Rect { left: 112, top: 16, right: 128, bottom: 32 },
|
||||
Rect { left: 96, top: 16, right: 112, bottom: 32 },
|
||||
])
|
||||
}
|
||||
|
||||
|
|
|
@ -462,6 +462,11 @@ impl NPC {
|
|||
npc.parent_id = self.id;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc.clone());
|
||||
if players[1].cond.alive() {
|
||||
npc.tsc_direction = 4;
|
||||
let _ = npc_list.spawn(0x100, npc.clone());
|
||||
npc.tsc_direction = 0;
|
||||
}
|
||||
npc.direction = Direction::Up;
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
|
@ -1307,7 +1312,12 @@ impl NPC {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n356_balrog_rescuing(&mut self, state: &mut SharedGameState, npc_list: &NPCList) -> GameResult {
|
||||
pub(crate) fn tick_n356_balrog_rescuing(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
npc_list: &NPCList,
|
||||
) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 11 => {
|
||||
if self.action_num == 0 {
|
||||
|
@ -1319,6 +1329,11 @@ impl NPC {
|
|||
let mut npc = NPC::create(355, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.parent_id = self.id;
|
||||
if players[1].cond.alive() {
|
||||
npc.tsc_direction = 5;
|
||||
let _ = npc_list.spawn(0xAA, npc.clone());
|
||||
npc.tsc_direction = 6;
|
||||
}
|
||||
npc.direction = Direction::Bottom;
|
||||
let _ = npc_list.spawn(0xAA, npc.clone());
|
||||
npc.direction = Direction::Right;
|
||||
|
|
|
@ -2532,8 +2532,38 @@ impl NPC {
|
|||
npc_list: &NPCList,
|
||||
) -> GameResult {
|
||||
if self.action_num == 0 {
|
||||
match self.direction {
|
||||
Direction::Left => {
|
||||
match (self.tsc_direction, self.direction) {
|
||||
// Co-op
|
||||
(4, _) => {
|
||||
self.spritesheet_id = 16;
|
||||
self.anim_num = 0;
|
||||
|
||||
if let Some(npc) = self.get_parent_ref_mut(npc_list) {
|
||||
self.x = npc.x;
|
||||
self.y = npc.y + 0x1400;
|
||||
}
|
||||
}
|
||||
(5, _) => {
|
||||
self.spritesheet_id = 16;
|
||||
self.anim_num = 2;
|
||||
|
||||
if let Some(npc) = self.get_parent_ref_mut(npc_list) {
|
||||
self.x = npc.x + 0x1600;
|
||||
self.y = npc.y - 0x2200;
|
||||
}
|
||||
}
|
||||
// Curly's position changes when 2P is present
|
||||
(6, Direction::Bottom) => {
|
||||
self.spritesheet_id = 23;
|
||||
self.anim_num = 3;
|
||||
|
||||
if let Some(npc) = self.get_parent_ref_mut(npc_list) {
|
||||
self.x = npc.x + 0x400;
|
||||
self.y = npc.y - 0x2600;
|
||||
}
|
||||
}
|
||||
// Normal
|
||||
(_, Direction::Left) => {
|
||||
self.spritesheet_id = 16;
|
||||
self.anim_num = 0;
|
||||
|
||||
|
@ -2542,7 +2572,7 @@ impl NPC {
|
|||
self.y = npc.y + 0x1400;
|
||||
}
|
||||
}
|
||||
Direction::Up => {
|
||||
(_, Direction::Up) => {
|
||||
self.spritesheet_id = 23;
|
||||
self.anim_num = 1;
|
||||
|
||||
|
@ -2551,16 +2581,20 @@ impl NPC {
|
|||
self.y = npc.y + 0x1400;
|
||||
}
|
||||
}
|
||||
Direction::Right => {
|
||||
(_, Direction::Right) => {
|
||||
self.spritesheet_id = 16;
|
||||
self.anim_num = 2;
|
||||
|
||||
if let Some(npc) = self.get_parent_ref_mut(npc_list) {
|
||||
self.x = npc.x - 0xe00;
|
||||
self.y = npc.y - 0x2600;
|
||||
if state.constants.is_switch {
|
||||
self.y = npc.y - 0x2200;
|
||||
} else {
|
||||
self.y = npc.y - 0x2600;
|
||||
}
|
||||
}
|
||||
}
|
||||
Direction::Bottom => {
|
||||
(_, Direction::Bottom) => {
|
||||
self.spritesheet_id = 23;
|
||||
self.anim_num = 3;
|
||||
|
||||
|
|
|
@ -32,6 +32,19 @@ impl NPC {
|
|||
self.target_x = self.x - 0xc00;
|
||||
self.vel_y = 0;
|
||||
self.npc_flags.set_ignore_solidity(true);
|
||||
|
||||
// Co-op
|
||||
if players[1].cond.alive() {
|
||||
let mut npc = NPC::create(370, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.parent_id = self.id;
|
||||
npc.x = self.x;
|
||||
npc.y = self.y;
|
||||
npc.action_num = 200;
|
||||
npc.direction = Direction::Right;
|
||||
|
||||
let _ = npc_list.spawn(0xAA, npc);
|
||||
}
|
||||
}
|
||||
|
||||
self.vel_x += if self.x >= self.target_x { -8 } else { 8 };
|
||||
|
@ -75,6 +88,16 @@ impl NPC {
|
|||
self.anim_rect.bottom += 40;
|
||||
}
|
||||
|
||||
// Switch uses the extra space on the sprite sheet for 2P's Curly
|
||||
if state.constants.is_switch {
|
||||
if self.anim_num <= 1 {
|
||||
self.anim_rect.top += 8;
|
||||
self.display_bounds.top = 0x2000;
|
||||
} else {
|
||||
self.display_bounds.top = 0x3000;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -154,10 +154,6 @@ impl NPC {
|
|||
self.action_num = 1;
|
||||
self.anim_num = 0;
|
||||
|
||||
if self.npc_type == 370 {
|
||||
self.cond.set_alive(players[1].cond.alive());
|
||||
}
|
||||
|
||||
if self.tsc_direction > 10 {
|
||||
let player = &players[state.textscript_vm.executor_player.index()];
|
||||
self.x = player.x;
|
||||
|
@ -291,7 +287,7 @@ impl NPC {
|
|||
_ => (),
|
||||
}
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 9 };
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 10 };
|
||||
self.anim_rect = state.constants.npc.n150_quote[self.anim_num as usize + dir_offset];
|
||||
|
||||
if self.action_num == 21 {
|
||||
|
@ -305,4 +301,174 @@ impl NPC {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n370_second_quote(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
npc_list: &NPCList,
|
||||
) -> GameResult {
|
||||
if !players[1].cond.alive() {
|
||||
self.cond.set_alive(false);
|
||||
return Ok(());
|
||||
}
|
||||
match self.action_num {
|
||||
0 => {
|
||||
self.action_num = 1;
|
||||
self.anim_num = 0;
|
||||
|
||||
if self.tsc_direction > 10 {
|
||||
let player = &players[state.textscript_vm.executor_player.index() + 1 % 1];
|
||||
self.x = player.x;
|
||||
self.y = player.y;
|
||||
|
||||
self.direction =
|
||||
Direction::from_int(self.tsc_direction.saturating_sub(10) as usize).unwrap_or(Direction::Left);
|
||||
} else {
|
||||
self.direction = Direction::from_int(self.tsc_direction as usize).unwrap_or(Direction::Left);
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
self.anim_num = 1;
|
||||
}
|
||||
10 => {
|
||||
self.action_num = 11;
|
||||
self.anim_num = 2;
|
||||
|
||||
state.sound_manager.play_sfx(71);
|
||||
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.direction = Direction::Left;
|
||||
npc.x = self.x;
|
||||
npc.y = self.y;
|
||||
|
||||
for _ in 0..4 {
|
||||
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
|
||||
npc.vel_y = self.rng.range(-0x600..0) as i32;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc.clone());
|
||||
}
|
||||
}
|
||||
11 => {
|
||||
self.anim_num = 2;
|
||||
}
|
||||
20 => {
|
||||
self.action_num = 21;
|
||||
self.action_counter = 63;
|
||||
|
||||
state.sound_manager.play_sfx(29);
|
||||
}
|
||||
21 => {
|
||||
if self.action_counter > 0 {
|
||||
self.action_counter -= 1;
|
||||
} else {
|
||||
self.cond.set_alive(false);
|
||||
}
|
||||
}
|
||||
50 | 51 => {
|
||||
if self.action_num == 50 {
|
||||
self.action_num = 51;
|
||||
self.anim_num = 3;
|
||||
self.anim_counter = 0;
|
||||
}
|
||||
|
||||
self.anim_counter += 1;
|
||||
if self.anim_counter > 4 {
|
||||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
if self.anim_num > 6 {
|
||||
self.anim_num = 3;
|
||||
}
|
||||
}
|
||||
|
||||
self.x += self.direction.vector_x() * 0x200;
|
||||
}
|
||||
60 | 61 => {
|
||||
if self.action_num == 60 {
|
||||
self.action_num = 61;
|
||||
self.anim_num = 7;
|
||||
self.target_x = self.x;
|
||||
self.target_y = self.y;
|
||||
}
|
||||
|
||||
self.target_y += 0x100;
|
||||
self.x = self.target_x + self.rng.range(-1..1) as i32 * 0x200;
|
||||
self.y = self.target_y + self.rng.range(-1..1) as i32 * 0x200;
|
||||
}
|
||||
70 | 71 => {
|
||||
if self.action_num == 70 {
|
||||
self.action_num = 71;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 3;
|
||||
self.anim_counter = 0;
|
||||
}
|
||||
|
||||
self.x += (self.direction.vector_x() as i32 | 1) * 0x100;
|
||||
|
||||
self.anim_counter += 1;
|
||||
if self.anim_counter > 8 {
|
||||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
if self.anim_num > 6 {
|
||||
self.anim_num = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
80 => {
|
||||
self.anim_num = 8;
|
||||
}
|
||||
99 | 100 | 101 => {
|
||||
if self.action_num == 99 || self.action_num == 100 {
|
||||
self.action_num = 101;
|
||||
self.anim_num = 3;
|
||||
self.anim_counter = 0;
|
||||
}
|
||||
|
||||
self.vel_y += 0x40;
|
||||
if self.vel_y > 0x5ff {
|
||||
self.vel_y = 0x5ff;
|
||||
}
|
||||
|
||||
if self.flags.hit_bottom_wall() {
|
||||
self.vel_y = 0;
|
||||
self.action_num = 102;
|
||||
}
|
||||
|
||||
self.y += self.vel_y;
|
||||
}
|
||||
102 => {
|
||||
self.anim_counter += 1;
|
||||
if self.anim_counter > 8 {
|
||||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
if self.anim_num > 6 {
|
||||
self.anim_num = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
200 => {
|
||||
self.anim_num = 9;
|
||||
if let Some(parent) = self.get_parent_ref_mut(npc_list) {
|
||||
self.x = parent.x + parent.vel_x + 0xA00;
|
||||
self.y = parent.y + parent.vel_y - 0x1C00;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 10 };
|
||||
self.anim_rect = state.constants.npc.n150_quote[self.anim_num as usize + dir_offset];
|
||||
|
||||
if self.action_num == 21 {
|
||||
self.anim_rect.bottom = self.anim_rect.top + self.action_counter / 4;
|
||||
}
|
||||
|
||||
let offset = players[state.textscript_vm.executor_player.index() + 1 % 1].get_texture_offset()
|
||||
+ (state.get_skinsheet_offset() * state.tile_size.as_int() as u16 * 2);
|
||||
self.anim_rect.top += offset;
|
||||
self.anim_rect.bottom += offset;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -390,7 +390,7 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &mut BulletManager, &mu
|
|||
147 => self.tick_n147_critter_purple(state, players, npc_list),
|
||||
148 => self.tick_n148_critter_purple_projectile(state),
|
||||
149 => self.tick_n149_horizontal_moving_block(state, players, npc_list),
|
||||
150 | 370 => self.tick_n150_quote(state, players, npc_list),
|
||||
150 => self.tick_n150_quote(state, players, npc_list),
|
||||
151 => self.tick_n151_blue_robot_standing(state),
|
||||
152 => self.tick_n152_shutter_stuck(),
|
||||
153 => self.tick_n153_gaudi(state, players),
|
||||
|
@ -596,11 +596,12 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &mut BulletManager, &mu
|
|||
353 => self.tick_n353_bute_sword_flying(state, players),
|
||||
354 => self.tick_n354_invisible_deathtrap_wall(state, npc_list, stage),
|
||||
355 => self.tick_n355_quote_and_curly_on_balrog(state, npc_list),
|
||||
356 => self.tick_n356_balrog_rescuing(state, npc_list),
|
||||
356 => self.tick_n356_balrog_rescuing(state, players, npc_list),
|
||||
357 => self.tick_n357_puppy_ghost(state),
|
||||
358 => self.tick_n358_misery_credits(state),
|
||||
359 => self.tick_n359_water_droplet_generator(state, players, npc_list),
|
||||
360 => self.tick_n360_credits_thank_you(state),
|
||||
370 => self.tick_n370_second_quote(state, players, npc_list),
|
||||
_ => {
|
||||
#[cfg(feature = "hooks")]
|
||||
{
|
||||
|
|
|
@ -52,6 +52,29 @@ enum BoosterSwitch {
|
|||
Down,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct DogStack {
|
||||
pub offset_x: f32,
|
||||
pub speed: f32,
|
||||
pub prev_speed: f32,
|
||||
}
|
||||
|
||||
impl DogStack {
|
||||
const TENSION: f32 = 0.25;
|
||||
const DAMPENING: f32 = 0.1;
|
||||
const MULT: f32 = 1.2;
|
||||
|
||||
pub fn new() -> DogStack {
|
||||
DogStack { offset_x: 0.0, speed: 0.0, prev_speed: 0.0 }
|
||||
}
|
||||
|
||||
pub fn tick(&mut self) {
|
||||
self.prev_speed = self.speed;
|
||||
self.speed += -self.offset_x * DogStack::TENSION as f32 - self.speed * DogStack::DAMPENING;
|
||||
self.offset_x += self.speed;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Player {
|
||||
pub x: i32,
|
||||
|
@ -98,6 +121,8 @@ pub struct Player {
|
|||
anim_counter: u16,
|
||||
anim_rect: Rect<u16>,
|
||||
weapon_rect: Rect<u16>,
|
||||
dog_stack: Vec<DogStack>,
|
||||
pub has_dog: bool,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
|
@ -150,6 +175,8 @@ impl Player {
|
|||
anim_counter: 0,
|
||||
anim_rect: constants.player.frames_right[0],
|
||||
weapon_rect: Rect::new(0, 0, 0, 0),
|
||||
dog_stack: Vec::new(),
|
||||
has_dog: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -910,6 +937,21 @@ impl GameEntity<&NPCList> for Player {
|
|||
self.cond.set_increase_acceleration(false);
|
||||
self.tick_animation(state);
|
||||
|
||||
let dog_amount = (3000..=3005).filter(|id| state.get_flag(*id as usize)).count();
|
||||
self.dog_stack.resize(dog_amount, DogStack::new());
|
||||
|
||||
if self.has_dog && self.dog_stack.len() > 0 {
|
||||
if self.vel_x.abs() > 0x100 {
|
||||
self.dog_stack[0].speed = (self.vel_x / 2) as f32;
|
||||
}
|
||||
for i in 1..self.dog_stack.len() {
|
||||
self.dog_stack[i].speed = self.dog_stack[i - 1].prev_speed * DogStack::MULT;
|
||||
}
|
||||
for dog in &mut self.dog_stack {
|
||||
dog.tick();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -921,10 +963,8 @@ impl GameEntity<&NPCList> for Player {
|
|||
let (frame_x, frame_y) = frame.xy_interpolated(state.frame_time);
|
||||
|
||||
// hack for stacked dogs
|
||||
if state.constants.is_switch {
|
||||
let dog_amount = (3000..=3005).filter(|id| state.get_flag(*id as usize)).count();
|
||||
|
||||
if dog_amount > 0 {
|
||||
if self.has_dog {
|
||||
if self.dog_stack.len() > 0 {
|
||||
let vec_x = self.direction.vector_x() * 0x800;
|
||||
let vec_y = 0x1400;
|
||||
|
||||
|
@ -939,16 +979,16 @@ impl GameEntity<&NPCList> for Player {
|
|||
};
|
||||
let off_y = entry.display_bounds.top as i32 * 0x200;
|
||||
|
||||
for i in 1..=(dog_amount as i32) {
|
||||
for i in (1..=(self.dog_stack.len() as i32)).rev() {
|
||||
batch.add_rect(
|
||||
interpolate_fix9_scale(
|
||||
self.prev_x - off_x - vec_x * i,
|
||||
self.x - off_x - vec_x * i,
|
||||
self.prev_x - off_x - vec_x * i - self.dog_stack[i as usize - 1].offset_x as i32,
|
||||
self.x - off_x - vec_x * i - self.dog_stack[i as usize - 1].offset_x as i32,
|
||||
state.frame_time,
|
||||
) - frame_x,
|
||||
interpolate_fix9_scale(
|
||||
self.prev_y - off_y - vec_y * i,
|
||||
self.y - off_y - vec_y * i,
|
||||
self.prev_y - off_y - vec_y * i - (self.y - self.prev_y) * i,
|
||||
self.y - off_y - vec_y * i - (self.y - self.prev_y) * i,
|
||||
state.frame_time,
|
||||
) - frame_y,
|
||||
&state.constants.npc.n136_puppy_carried[frame_id],
|
||||
|
|
|
@ -1439,6 +1439,11 @@ impl GameScene {
|
|||
}
|
||||
}
|
||||
|
||||
if state.constants.is_switch {
|
||||
self.player1.has_dog = self.inventory_player1.has_item(14);
|
||||
self.player2.has_dog = self.inventory_player2.has_item(14);
|
||||
}
|
||||
|
||||
self.water_renderer.tick(state, (&[&self.player1, &self.player2], &self.npc_list))?;
|
||||
|
||||
if self.map_name_counter > 0 {
|
||||
|
|
Loading…
Reference in New Issue