diff --git a/src/common.rs b/src/common.rs
index e50622b..f93fd72 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -5,6 +5,10 @@ use num_traits::real::Real;
 
 use crate::bitfield;
 
+/// Multiply cave story degrees (0-255, which corresponds to 0°-360°) with this to get
+/// respective value in radians.
+pub const CDEG_RAD: f64 = std::f64::consts::PI * 0.003950617283950617;
+
 bitfield! {
   #[derive(Clone, Copy)]
   pub struct Flag(u32);
@@ -70,6 +74,9 @@ bitfield! {
   pub increase_acceleration, set_increase_acceleration: 5; // 0x20
   pub cond_x40, set_cond_x40: 6; // 0x40
   pub alive, set_alive: 7; // 0x80
+
+  // engine specific flags
+  pub drs_destroyed, set_drs_destroyed: 15;
 }
 
 bitfield! {
diff --git a/src/engine_constants.rs b/src/engine_constants.rs
index 4699bef..9ca459e 100644
--- a/src/engine_constants.rs
+++ b/src/engine_constants.rs
@@ -164,9 +164,14 @@ pub struct NPCConsts {
     pub n021_chest_open: Rect<usize>,
     pub n022_teleporter: [Rect<usize>; 2],
     pub n023_teleporter_lights: [Rect<usize>; 8],
+    pub n024_power_critter: [Rect<usize>; 12],
+    pub n025_lift: [Rect<usize>; 2],
+    pub n026_bat_flying: [Rect<usize>; 8],
     pub n027_death_trap: Rect<usize>,
+    pub n028_flying_critter: [Rect<usize>; 12],
     pub n029_cthulhu: [Rect<usize>; 4],
     pub n030_hermit_gunsmith: [Rect<usize>; 3],
+    pub n031_bat_hanging: [Rect<usize>; 10],
     pub n032_life_capsule: [Rect<usize>; 2],
     pub n034_bed: [Rect<usize>; 2],
     pub n035_mannan: [Rect<usize>; 8],
@@ -184,6 +189,7 @@ pub struct NPCConsts {
     pub n049_skullhead: [Rect<usize>; 6],
     pub n052_sitting_blue_robot: Rect<usize>,
     pub n055_kazuma: [Rect<usize>; 12],
+    pub n058_basu: [Rect<usize>; 6],
     pub n059_eye_door: [Rect<usize>; 4],
     pub n060_toroko: [Rect<usize>; 16],
     pub n061_king: [Rect<usize>; 20],
@@ -228,12 +234,15 @@ pub struct NPCConsts {
     pub n112_quote_teleport_in: [Rect<usize>; 4],
     pub n129_fireball_snake_trail: [Rect<usize>; 18],
     pub n149_horizontal_moving_block: Rect<usize>,
+    pub n150_quote: [Rect<usize>; 18],
+    pub n154_gaudi_dead: [Rect<usize>; 6],
     pub n157_vertical_moving_block: Rect<usize>,
     pub n199_wind_particles: [Rect<usize>; 5],
     pub n211_small_spikes: [Rect<usize>; 4],
     pub n298_intro_doctor: [Rect<usize>; 8],
     pub n299_intro_balrog_misery: [Rect<usize>; 2],
     pub n300_intro_demon_crown: Rect<usize>,
+    pub n361_gaudi_dashing: [Rect<usize>; 4],
 }
 
 #[derive(Debug, Copy, Clone)]
@@ -692,7 +701,49 @@ impl EngineConstants {
                     Rect { left: 264, top: 40, right: 288, bottom: 44 },
                     Rect { left: 264, top: 44, right: 288, bottom: 48 },
                 ],
+                n024_power_critter: [
+                    Rect { left: 0, top: 0, right: 24, bottom: 24 }, // left
+                    Rect { left: 24, top: 0, right: 48, bottom: 24 },
+                    Rect { left: 48, top: 0, right: 72, bottom: 24 },
+                    Rect { left: 72, top: 0, right: 96, bottom: 24 },
+                    Rect { left: 96, top: 0, right: 120, bottom: 24 },
+                    Rect { left: 120, top: 0, right: 144, bottom: 24 },
+                    Rect { left: 0, top: 24, right: 24, bottom: 48 }, // right
+                    Rect { left: 24, top: 24, right: 48, bottom: 48 },
+                    Rect { left: 48, top: 24, right: 72, bottom: 48 },
+                    Rect { left: 72, top: 24, right: 96, bottom: 48 },
+                    Rect { left: 96, top: 24, right: 120, bottom: 48 },
+                    Rect { left: 120, top: 24, right: 144, bottom: 48 },
+                ],
+                n025_lift: [
+                    Rect { left: 256, top: 64, right: 288, bottom: 80 },
+                    Rect { left: 256, top: 80, right: 288, bottom: 96 },
+                ],
+                n026_bat_flying: [
+                    Rect { left: 32, top: 80, right: 48, bottom: 96 }, // left
+                    Rect { left: 48, top: 80, right: 64, bottom: 96 },
+                    Rect { left: 64, top: 80, right: 80, bottom: 96 },
+                    Rect { left: 80, top: 80, right: 96, bottom: 96 },
+                    Rect { left: 32, top: 96, right: 48, bottom: 112 }, // right
+                    Rect { left: 48, top: 96, right: 64, bottom: 112 },
+                    Rect { left: 64, top: 96, right: 80, bottom: 112 },
+                    Rect { left: 80, top: 96, right: 96, bottom: 112 },
+                ],
                 n027_death_trap: Rect { left: 96, top: 64, right: 128, bottom: 88 },
+                n028_flying_critter: [
+                    Rect { left: 0, top: 48, right: 16, bottom: 64 }, // left
+                    Rect { left: 16, top: 48, right: 32, bottom: 64 },
+                    Rect { left: 32, top: 48, right: 48, bottom: 64 },
+                    Rect { left: 48, top: 48, right: 64, bottom: 64 },
+                    Rect { left: 64, top: 48, right: 80, bottom: 64 },
+                    Rect { left: 80, top: 48, right: 96, bottom: 64 },
+                    Rect { left: 0, top: 64, right: 16, bottom: 80 }, // right
+                    Rect { left: 16, top: 64, right: 32, bottom: 80 },
+                    Rect { left: 32, top: 64, right: 48, bottom: 80 },
+                    Rect { left: 48, top: 64, right: 64, bottom: 80 },
+                    Rect { left: 64, top: 64, right: 80, bottom: 80 },
+                    Rect { left: 80, top: 64, right: 96, bottom: 80 },
+                ],
                 n029_cthulhu: [
                     Rect { left: 0, top: 192, right: 16, bottom: 216 }, // left
                     Rect { left: 16, top: 192, right: 32, bottom: 216 },
@@ -704,6 +755,18 @@ impl EngineConstants {
                     Rect { left: 48, top: 16, right: 64, bottom: 32 },
                     Rect { left: 0, top: 32, right: 16, bottom: 48 },
                 ],
+                n031_bat_hanging: [
+                    Rect { left: 0, top: 80, right: 16, bottom: 96 }, // left
+                    Rect { left: 16, top: 80, right: 32, bottom: 96 },
+                    Rect { left: 32, top: 80, right: 48, bottom: 96 },
+                    Rect { left: 48, top: 80, right: 64, bottom: 96 },
+                    Rect { left: 64, top: 80, right: 80, bottom: 96 },
+                    Rect { left: 0, top: 96, right: 16, bottom: 112 }, // right
+                    Rect { left: 16, top: 96, right: 32, bottom: 112 },
+                    Rect { left: 32, top: 96, right: 48, bottom: 112 },
+                    Rect { left: 48, top: 96, right: 64, bottom: 112 },
+                    Rect { left: 64, top: 96, right: 80, bottom: 112 },
+                ],
                 n032_life_capsule: [
                     Rect { left: 32, top: 96, right: 48, bottom: 112 },
                     Rect { left: 48, top: 96, right: 64, bottom: 112 },
@@ -834,6 +897,14 @@ impl EngineConstants {
                     Rect { left: 192, top: 216, right: 208, bottom: 240 },
                     Rect { left: 240, top: 216, right: 256, bottom: 240 },
                 ],
+                n058_basu: [
+                    Rect { left: 192, top: 0, right: 216, bottom: 24 }, // left
+                    Rect { left: 216, top: 0, right: 240, bottom: 24 },
+                    Rect { left: 240, top: 0, right: 264, bottom: 24 },
+                    Rect { left: 192, top: 24, right: 216, bottom: 48 }, // right
+                    Rect { left: 216, top: 24, right: 240, bottom: 48 },
+                    Rect { left: 240, top: 24, right: 264, bottom: 48 },
+                ],
                 n059_eye_door: [
                     Rect { left: 224, top: 16, right: 240, bottom: 40 },
                     Rect { left: 208, top: 80, right: 224, bottom: 104 },
@@ -1260,6 +1331,34 @@ impl EngineConstants {
                     Rect { left: 208, top: 80, right: 224, bottom: 96 },
                 ],
                 n149_horizontal_moving_block: Rect { left: 16, top: 0, right: 48, bottom: 32 },
+                n150_quote: [
+                    Rect { left: 0, top: 0, right: 16, bottom: 16 }, // left
+                    Rect { left: 48, top: 0, right: 64, bottom: 16 },
+                    Rect { left: 144, top: 0, right: 160, bottom: 16 },
+                    Rect { left: 16, top: 0, right: 32, bottom: 16 },
+                    Rect { left: 0, top: 0, right: 16, bottom: 16 },
+                    Rect { left: 32, top: 0, right: 48, bottom: 16 },
+                    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: 0, top: 16, right: 16, bottom: 32 }, //right
+                    Rect { left: 48, top: 16, right: 64, bottom: 32 },
+                    Rect { left: 144, top: 16, right: 160, bottom: 32 },
+                    Rect { left: 16, top: 16, right: 32, bottom: 32 },
+                    Rect { left: 0, top: 16, right: 16, bottom: 32 },
+                    Rect { left: 32, top: 16, right: 48, bottom: 32 },
+                    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 },
+                ],
+                n154_gaudi_dead: [
+                    Rect { left: 168, top: 24, right: 192, bottom: 48 }, // left
+                    Rect { left: 192, top: 24, right: 216, bottom: 48 },
+                    Rect { left: 216, top: 24, right: 240, bottom: 48 },
+                    Rect { left: 168, top: 0, right: 192, bottom: 24 }, // right
+                    Rect { left: 192, top: 0, right: 216, bottom: 24 },
+                    Rect { left: 216, top: 0, right: 240, bottom: 24 },
+                ],
                 n157_vertical_moving_block: Rect { left: 16, top: 0, right: 48, bottom: 32 },
                 n199_wind_particles: [
                     Rect { left: 72, top: 16, right: 74, bottom: 18 },
@@ -1289,6 +1388,12 @@ impl EngineConstants {
                     Rect { left: 48, top: 0, right: 96, bottom: 48 },
                 ],
                 n300_intro_demon_crown: Rect { left: 192, top: 80, right: 208, bottom: 96 },
+                n361_gaudi_dashing: [
+                    Rect { left: 48, top: 48, right: 72, bottom: 72 }, // left
+                    Rect { left: 72, top: 48, right: 96, bottom: 72 },
+                    Rect { left: 48, top: 72, right: 72, bottom: 96 }, // right
+                    Rect { left: 72, top: 72, right: 96, bottom: 96 },
+                ],
             },
             weapon: WeaponConsts {
                 bullet_table: vec![
diff --git a/src/lib.rs b/src/lib.rs
index 9265bc0..e1e9478 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -183,6 +183,7 @@ impl Game {
             }
             KeyCode::F10 => { state.settings.debug_outlines = !state.settings.debug_outlines }
             KeyCode::F11 => { state.settings.god_mode = !state.settings.god_mode }
+            KeyCode::F12 => { state.settings.infinite_booster = !state.settings.infinite_booster }
             _ => {}
         }
     }
diff --git a/src/map.rs b/src/map.rs
index c2a8dee..d545244 100644
--- a/src/map.rs
+++ b/src/map.rs
@@ -56,7 +56,6 @@ impl Map {
 
     pub fn get_attribute(&self, x: usize, y: usize) -> u8 {
         if x >= self.width || y >= self.height {
-            log::warn!("x: {} > {}, y: {} > {}", x, self.width, y, self.height);
             return 0;
         }
 
diff --git a/src/npc/characters.rs b/src/npc/characters.rs
index 27b5a05..6dbb2d6 100644
--- a/src/npc/characters.rs
+++ b/src/npc/characters.rs
@@ -1,4 +1,4 @@
-use num_traits::clamp;
+use num_traits::{clamp, abs};
 
 use crate::common::Direction;
 use crate::ggez::GameResult;
@@ -7,6 +7,25 @@ use crate::player::Player;
 use crate::shared_game_state::SharedGameState;
 
 impl NPC {
+    pub(crate) fn tick_n029_cthulhu(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult {
+        if self.action_num == 0 {
+            self.action_num = 1;
+            self.anim_num = 0;
+            self.anim_counter = 0;
+        }
+
+        if abs(self.x - player.x) < 48 * 0x200 && self.y - 48 * 0x200 < player.y && self.y + 16 * 0x200 > player.y {
+            self.anim_num = 1;
+        } else {
+            self.anim_num = 0;
+        }
+
+        let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
+        self.anim_rect = state.constants.npc.n029_cthulhu[self.anim_num as usize + dir_offset];
+
+        Ok(())
+    }
+
     pub(crate) fn tick_n052_sitting_blue_robot(&mut self, state: &mut SharedGameState) -> GameResult {
         if self.action_num == 0 {
             self.action_num = 1;
diff --git a/src/npc/egg_corridor.rs b/src/npc/egg_corridor.rs
index 0d8a714..47e8cfc 100644
--- a/src/npc/egg_corridor.rs
+++ b/src/npc/egg_corridor.rs
@@ -1,11 +1,12 @@
 use nalgebra::clamp;
+use num_traits::abs;
 
-use crate::common::Direction;
+use crate::caret::CaretType;
+use crate::common::{Direction, Rect, CDEG_RAD};
 use crate::ggez::GameResult;
-use crate::npc::NPC;
+use crate::npc::{NPC, NPCMap};
 use crate::player::Player;
 use crate::shared_game_state::SharedGameState;
-use crate::caret::CaretType;
 
 impl NPC {
     pub(crate) fn tick_n002_behemoth(&mut self, state: &mut SharedGameState) -> GameResult {
@@ -442,6 +443,209 @@ impl NPC {
         Ok(())
     }
 
+    pub(crate) fn tick_n025_lift(&mut self, state: &mut SharedGameState) -> GameResult {
+        match self.action_num {
+            0 | 1 => {
+                if self.action_num == 0 {
+                    self.x += 8 * 0x200;
+                    self.action_num = 1;
+                    self.anim_num = 0;
+                    self.anim_counter = 1;
+                }
+
+                self.action_counter += 1;
+                if self.action_counter > 150 {
+                    self.action_num = 2;
+                    self.action_counter = 0;
+                }
+            }
+            2 => {
+                self.action_counter += 1;
+                if self.action_counter > 64 {
+                    self.action_num = 3;
+                    self.action_counter = 0;
+                } else {
+                    self.y -= 0x200;
+                }
+            }
+            3 => {
+                self.action_counter += 1;
+                if self.action_counter > 150 {
+                    self.action_num = 4;
+                    self.action_counter = 0;
+                }
+            }
+            4 => {
+                self.action_counter += 1;
+                if self.action_counter > 64 {
+                    self.action_num = 5;
+                    self.action_counter = 0;
+                } else {
+                    self.y -= 0x200;
+                }
+            }
+            5 => {
+                self.action_counter += 1;
+                if self.action_counter > 150 {
+                    self.action_num = 6;
+                    self.action_counter = 0;
+                }
+            }
+            6 => {
+                self.action_counter += 1;
+                if self.action_counter > 64 {
+                    self.action_num = 7;
+                    self.action_counter = 0;
+                } else {
+                    self.y += 0x200;
+                }
+            }
+            7 => {
+                self.action_counter += 1;
+                if self.action_counter > 150 {
+                    self.action_num = 8;
+                    self.action_counter = 0;
+                }
+            }
+            8 => {
+                self.action_counter += 1;
+                if self.action_counter > 64 {
+                    self.action_num = 1;
+                    self.action_counter = 0;
+                } else {
+                    self.y += 0x200;
+                }
+            }
+            _ => {}
+        }
+
+        if [2, 4, 6, 8].contains(&self.action_num) {
+            self.anim_counter += 1;
+            if self.anim_counter > 1 {
+                self.anim_num += 1;
+                if self.anim_num > 1 {
+                    self.anim_num = 0;
+                }
+            }
+        }
+
+        if self.anim_counter == 1 {
+            self.anim_rect = state.constants.npc.n025_lift[self.anim_num as usize];
+        }
+
+        Ok(())
+    }
+
+    pub(crate) fn tick_n058_basu(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult {
+        match self.action_num {
+            0 => {
+                if player.x < self.x + 16 * 0x200 && player.x > self.x - 16 * 0x200 {
+                    self.target_x = self.x;
+                    self.target_y = self.y;
+                    self.action_num = 1;
+                    self.action_counter = 0;
+                    self.action_counter2 = 0;
+                    self.damage = 6;
+                    self.vel_y = -0x100;
+                    self.tsc_direction = self.direction as u16;
+                    self.npc_flags.set_shootable(true);
+
+                    self.x = player.x + self.direction.vector_x() * 16 * 16 * 0x200;
+                    self.vel_x = self.direction.vector_x() * 0x2ff;
+                } else {
+                    self.anim_rect = Rect::new(0, 0, 0, 0);
+                    self.damage = 0;
+                    self.vel_x = 0;
+                    self.vel_y = 0;
+                    self.npc_flags.set_shootable(false);
+                }
+
+                return Ok(());
+            }
+            1 => {
+                if self.x > player.x {
+                    self.direction = Direction::Left;
+                    self.vel_x -= 0x10;
+                } else {
+                    self.direction = Direction::Right;
+                    self.vel_x += 0x10;
+                }
+
+                if self.flags.hit_left_wall() {
+                    self.vel_x = 0x200;
+                }
+
+                if self.flags.hit_right_wall() {
+                    self.vel_x = -0x200;
+                }
+
+                self.vel_y += ((self.target_y - self.y).signum() | 1) * 0x08;
+
+                if self.shock > 0 {
+                    self.x += self.vel_x / 2;
+                    self.y += self.vel_y / 2;
+                } else {
+                    self.x += self.vel_x;
+                    self.y += self.vel_y;
+                }
+
+                if player.x > self.x + 400 * 0x200 || player.x < self.x - 400 * 0x200 {
+                    self.action_num = 0;
+                    self.vel_x = 0;
+                    self.x = self.target_x;
+                    self.damage = 0;
+                    self.direction = Direction::from_int_facing(self.tsc_direction as usize)
+                        .unwrap_or(Direction::Left);
+                    self.anim_rect = Rect::new(0, 0, 0, 0);
+                    return Ok(());
+                }
+            }
+            _ => {}
+        }
+
+        if self.action_counter < 150 {
+            self.action_counter += 1;
+        } else {
+            self.action_counter2 += 1;
+            if (self.action_counter2 % 8) == 0 && abs(self.x - player.x) < 160 * 0x200 {
+                let angle = ((player.y - self.y) as f64 / (player.x - self.x) as f64).atan();
+                    + (state.game_rng.range(-6..6) as u8) as f64 * CDEG_RAD;
+
+                let mut npc = NPCMap::create_npc(84, &state.npc_table);
+                npc.cond.set_alive(true);
+                npc.x = self.x;
+                npc.y = self.y;
+                npc.vel_x = (angle.cos() * 1024.0) as isize;
+                npc.vel_y = (angle.sin() * 1024.0) as isize;
+
+                state.new_npcs.push(npc);
+            }
+
+            if self.action_counter2 > 8 {
+                self.action_counter = 0;
+                self.action_counter2 = 0;
+            }
+        }
+
+        self.anim_counter += 1;
+        if self.anim_counter > 1 {
+            self.anim_counter = 0;
+            self.anim_num += 1;
+            if self.anim_num > 1 {
+                self.anim_num = 0;
+            }
+        }
+
+        if self.action_counter > 120 && self.action_counter / 2 % 2 == 1 && self.anim_num == 1 {
+            self.anim_num = 2;
+        }
+
+        let dir_offset = if self.direction == Direction::Left { 0 } else { 3 };
+        self.anim_rect = state.constants.npc.n058_basu[self.anim_num as usize + dir_offset];
+
+        Ok(())
+    }
+
     pub(crate) fn tick_n084_basu_projectile(&mut self, state: &mut SharedGameState) -> GameResult {
         self.x += self.vel_x;
         self.y += self.vel_y;
diff --git a/src/npc/grasstown.rs b/src/npc/grasstown.rs
index e69de29..aaae5ee 100644
--- a/src/npc/grasstown.rs
+++ b/src/npc/grasstown.rs
@@ -0,0 +1,349 @@
+use num_traits::abs;
+use num_traits::clamp;
+
+use crate::common::Direction;
+use crate::ggez::GameResult;
+use crate::npc::NPC;
+use crate::player::Player;
+use crate::shared_game_state::SharedGameState;
+
+impl NPC {
+    pub(crate) fn tick_n024_power_critter(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult {
+        match self.action_num {
+            0 | 1 => {
+                if self.action_num == 0 {
+                    self.y += 3 * 0x200;
+                    self.action_num = 1;
+                }
+
+                if self.action_counter >= 8
+                    && abs(self.x - player.x) < (112 * 0x200)
+                    && abs(self.y - player.y) < (80 * 0x200) {
+                    if self.x > player.x {
+                        self.direction = Direction::Left;
+                    } else {
+                        self.direction = Direction::Right;
+                    }
+
+                    self.anim_num = 1;
+                } else {
+                    if self.action_counter < 8 {
+                        self.action_counter += 1;
+                    }
+
+                    self.anim_num = 0;
+                }
+
+                if self.shock > 0 {
+                    self.action_num = 2;
+                    self.action_counter = 0;
+                    self.anim_num = 0;
+                }
+
+                if self.action_counter >= 8
+                    && abs(self.x - player.x) < 96 * 0x200
+                    && self.y - 96 * 0x200 < player.y
+                    && self.y + 48 * 0x200 > player.y {
+                    self.action_num = 2;
+                    self.action_counter = 0;
+                    self.anim_num = 0;
+                }
+            }
+            2 => {
+                self.action_counter += 1;
+                if self.action_counter > 8 {
+                    if self.x > player.x {
+                        self.direction = Direction::Left;
+                    } else {
+                        self.direction = Direction::Right;
+                    }
+
+                    self.action_num = 3;
+                    self.anim_num = 2;
+                    self.vel_x = self.direction.vector_x() * 0x100;
+                    self.vel_y = -0x5ff;
+                    state.sound_manager.play_sfx(108);
+                }
+            }
+            3 => {
+                if self.vel_y > 0x200 {
+                    self.target_y = self.y;
+                    self.action_num = 4;
+                    self.action_counter = 0;
+                    self.anim_num = 3;
+                }
+            }
+            4 => {
+                if self.x > player.x {
+                    self.direction = Direction::Left;
+                } else {
+                    self.direction = Direction::Right;
+                }
+
+                self.action_counter += 1;
+
+                if (self.flags.hit_left_wall()
+                    || self.flags.hit_right_wall()
+                    || self.flags.hit_top_wall()) || self.action_counter > 100 {
+                    self.action_num = 5;
+                    self.anim_num = 2;
+                    self.vel_x /= 2;
+                    self.damage = 12;
+                }
+
+                if self.action_counter % 4 == 1 {
+                    state.sound_manager.play_sfx(110);
+                }
+
+                self.anim_counter += 1;
+                if self.anim_counter > 0 {
+                    self.anim_counter = 0;
+                    self.anim_num += 1;
+                    if self.anim_num > 5 {
+                        self.anim_num = 3;
+                    }
+                }
+            }
+            5 => {
+                if self.flags.hit_bottom_wall() {
+                    self.vel_x = 0;
+                    self.action_num = 1;
+                    self.action_counter = 0;
+                    self.anim_num = 0;
+                    self.damage = 2;
+
+                    state.sound_manager.play_sfx(23);
+                    state.quake_counter = 30;
+                }
+            }
+            _ => {}
+        }
+
+        if self.action_num != 4 {
+            self.vel_y += 0x40;
+            if self.vel_y > 0x5ff {
+                self.vel_y = 0x5ff;
+            }
+        } else {
+            self.vel_x = clamp(self.vel_x + if self.x < player.x { 0x20 } else { -0x20 }, -0x200, 0x200);
+            self.vel_y = clamp(self.vel_x + if self.y > self.target_y { -0x10 } else { 0x10 }, -0x200, 0x200);
+        }
+
+        self.x += self.vel_x;
+        self.y += self.vel_y;
+
+        let dir_offset = if self.direction == Direction::Left { 0 } else { 6 };
+        self.anim_rect = state.constants.npc.n024_power_critter[self.anim_num as usize + dir_offset];
+
+        Ok(())
+    }
+
+    pub(crate) fn tick_n026_bat_flying(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult {
+        match self.action_num {
+            0 | 1 => {
+                if self.action_num == 0 {
+                    let angle = state.game_rng.range(0..0xff);
+                    self.vel_x = ((angle as f64 * 1.40625).cos() * 512.0) as isize;
+                    self.target_x = self.x + ((angle as f64 * 1.40625 + std::f64::consts::FRAC_2_PI).cos() * 8.0 * 512.0) as isize;
+
+                    let angle = state.game_rng.range(0..0xff);
+                    self.vel_y = ((angle as f64 * 1.40625).sin() * 512.0) as isize;
+                    self.target_y = self.y + ((angle as f64 * 1.40625 + std::f64::consts::FRAC_2_PI).sin() * 8.0 * 512.0) as isize;
+
+                    self.action_num = 1;
+                    self.action_counter2 = 120;
+                }
+
+                if self.x > player.x {
+                    self.direction = Direction::Left;
+                } else {
+                    self.direction = Direction::Right;
+                }
+
+                self.vel_x = clamp(self.vel_x + 0x10 * (self.target_x - self.x).signum(), -0x200, 0x200);
+                self.vel_y = clamp(self.vel_y + 0x10 * (self.target_y - self.y).signum(), -0x200, 0x200);
+
+                if self.action_counter2 < 120 {
+                    self.action_counter2 += 1;
+                } else if abs(self.x - player.x) < 8 * 0x200
+                    && self.y < player.y
+                    && self.y + 96 * 0x200 > player.y {
+                    self.vel_x /= 2;
+                    self.vel_y = 0;
+                    self.action_num = 3;
+                    self.npc_flags.set_ignore_solidity(false);
+                }
+            }
+            3 => {
+                self.vel_y += 0x40;
+                if self.vel_y > 0x5ff {
+                    self.vel_y = 0x5ff;
+                }
+
+                if self.flags.hit_bottom_wall() {
+                    self.vel_x *= 2;
+                    self.vel_y = 0;
+                    self.action_counter2 = 0;
+                    self.action_num = 1;
+                    self.npc_flags.set_ignore_solidity(true);
+                }
+            }
+            _ => {}
+        }
+
+        self.x += self.vel_x;
+        self.y += self.vel_y;
+
+        if self.action_num == 3 {
+            self.anim_num = 3;
+        } else {
+            self.anim_counter += 1;
+            if self.anim_counter > 1 {
+                self.anim_counter = 0;
+                self.anim_num += 1;
+                if self.anim_num > 2 {
+                    self.anim_num = 0;
+                }
+            }
+        }
+
+        let dir_offset = if self.direction == Direction::Left { 0 } else { 4 };
+        self.anim_rect = state.constants.npc.n026_bat_flying[self.anim_num as usize + dir_offset];
+
+        Ok(())
+    }
+
+    pub(crate) fn tick_n028_flying_critter(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult {
+        match self.action_num {
+            0 | 1 => {
+                if self.action_num == 0 {
+                    self.y += 3 * 0x200;
+                    self.action_num = 1;
+                }
+
+                if self.action_counter >= 8
+                    && abs(self.x - player.x) < 96 * 0x200
+                    && self.y - 128 * 0x200 < player.y
+                    && self.y + 48 * 0x200 > player.y {
+                    if self.x > player.x {
+                        self.direction = Direction::Left;
+                    } else {
+                        self.direction = Direction::Right;
+                    }
+
+                    self.anim_num = 1;
+                } else {
+                    if self.action_counter < 8 {
+                        self.action_counter += 1;
+                    }
+
+                    self.anim_num = 0;
+                }
+
+                if self.shock > 0 {
+                    self.action_num = 2;
+                    self.action_counter = 0;
+                    self.anim_num = 0;
+                }
+
+                if self.action_counter >= 8
+                    && abs(self.x - player.x) < 96 * 0x200
+                    && self.y - 96 * 0x200 < player.y
+                    && self.y + 48 * 0x200 > player.y {
+                    self.action_num = 2;
+                    self.action_counter = 0;
+                    self.anim_num = 0;
+                }
+            }
+            2 => {
+                self.action_counter += 1;
+                if self.action_counter > 8 {
+                    if self.x > player.x {
+                        self.direction = Direction::Left;
+                    } else {
+                        self.direction = Direction::Right;
+                    }
+
+                    self.action_num = 3;
+                    self.anim_num = 2;
+                    self.vel_x = self.direction.vector_x() * 0x100;
+                    self.vel_y = -0x4cc;
+                    state.sound_manager.play_sfx(30);
+                }
+            }
+            3 => {
+                if self.vel_y > 0x100 {
+                    self.target_y = self.y;
+                    self.action_num = 4;
+                    self.action_counter = 0;
+                    self.anim_num = 3;
+                }
+            }
+            4 => {
+                if self.x > player.x {
+                    self.direction = Direction::Left;
+                } else {
+                    self.direction = Direction::Right;
+                }
+
+                self.action_counter += 1;
+
+                if (self.flags.hit_left_wall()
+                    || self.flags.hit_right_wall()
+                    || self.flags.hit_top_wall()) || self.action_counter > 100 {
+                    self.action_num = 5;
+                    self.anim_num = 2;
+                    self.vel_x /= 2;
+                    self.damage = 3;
+                }
+
+                if self.action_counter % 4 == 1 {
+                    state.sound_manager.play_sfx(110);
+                }
+
+                if self.flags.hit_bottom_wall() {
+                    self.vel_y = -0x200;
+                }
+
+                self.anim_counter += 1;
+                if self.anim_counter > 0 {
+                    self.anim_counter = 0;
+                    self.anim_num += 1;
+                    if self.anim_num > 5 {
+                        self.anim_num = 3;
+                    }
+                }
+            }
+            5 => {
+                if self.flags.hit_bottom_wall() {
+                    self.vel_x = 0;
+                    self.action_num = 1;
+                    self.action_counter = 0;
+                    self.anim_num = 0;
+                    self.damage = 2;
+
+                    state.sound_manager.play_sfx(23);
+                }
+            }
+            _ => {}
+        }
+
+        if self.action_num != 4 {
+            self.vel_y += 0x40;
+            if self.vel_y > 0x5ff {
+                self.vel_y = 0x5ff;
+            }
+        } else {
+            self.vel_x = clamp(self.vel_x + if self.x < player.x { 0x20 } else { -0x20 }, -0x200, 0x200);
+            self.vel_y = clamp(self.vel_x + if self.y > self.target_y { -0x10 } else { 0x10 }, -0x200, 0x200);
+        }
+
+        self.x += self.vel_x;
+        self.y += self.vel_y;
+
+        let dir_offset = if self.direction == Direction::Left { 0 } else { 6 };
+        self.anim_rect = state.constants.npc.n028_flying_critter[self.anim_num as usize + dir_offset];
+
+        Ok(())
+    }
+}
diff --git a/src/npc/maze.rs b/src/npc/maze.rs
index 0e1365e..333a545 100644
--- a/src/npc/maze.rs
+++ b/src/npc/maze.rs
@@ -1,5 +1,95 @@
+use num_traits::{abs, clamp};
+
+use crate::common::Direction;
+use crate::ggez::GameResult;
 use crate::npc::NPC;
+use crate::player::Player;
+use crate::shared_game_state::SharedGameState;
 
 impl NPC {
+    pub(crate) fn tick_n154_gaudi_dead(&mut self, state: &mut SharedGameState) -> GameResult {
+        let dir_offset = if self.direction == Direction::Left { 0 } else { 3 };
+        self.anim_rect = state.constants.npc.n154_gaudi_dead[self.anim_num as usize + dir_offset];
 
+        Ok(())
+    }
+
+    pub(crate) fn tick_n361_gaudi_dashing(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult {
+        match self.action_num {
+            0 | 1 => {
+                if self.action_num == 0 {
+                    self.vel_x2 = 0;
+                    self.vel_y2 = 0;
+                    self.action_num = 1;
+                }
+
+                if (self.direction == Direction::Right && player.x > self.x + 272 * 0x200 && player.x < self.x + 288 * 0x200)
+                    || (self.direction == Direction::Left && player.x < self.x - 272 * 0x200 && player.x > self.x - 288 * 0x200) {
+                    self.action_num = 10;
+                } else {
+                    return Ok(());
+                }
+            }
+            10 | 11 => {
+                if self.action_num == 10 {
+                    self.npc_flags.set_shootable(true);
+                    self.action_num = 11;
+                    self.damage = 5;
+                }
+
+                if self.x > player.x {
+                    self.direction = Direction::Left;
+                } else {
+                    self.direction = Direction::Right;
+                }
+
+                self.anim_counter += 1;
+                if self.anim_counter > 1 {
+                    self.anim_counter = 0;
+                    self.anim_num += 1;
+
+                    if self.anim_num > 1 {
+                        self.anim_num = 0;
+                    }
+                }
+
+                self.vel_x2 += self.direction.vector_x() * 0x10;
+                self.vel_y2 += (player.y - self.y).signum() * 0x10;
+
+                if self.vel_x2 < 0 && self.flags.hit_left_wall() {
+                    self.vel_x2 /= -2;
+                }
+
+                if self.vel_x2 > 0 && self.flags.hit_right_wall() {
+                    self.vel_x2 /= -2;
+                }
+
+                if self.vel_y2 < 0 && self.flags.hit_top_wall() {
+                    self.vel_y2 *= -1;
+                }
+
+                if self.vel_y2 > 0 && self.flags.hit_bottom_wall() {
+                    self.vel_y2 /= -2;
+                }
+
+                self.vel_x2 = clamp(self.vel_x2, -0x5ff, 0x5ff);
+                self.vel_y2 = clamp(self.vel_y2, -0x5ff, 0x5ff);
+
+                self.x += self.vel_x2;
+                self.y += self.vel_y2;
+            }
+            _ => {}
+        }
+
+        if self.life <= 985 {
+            self.cond.set_drs_destroyed(true);
+            self.npc_type = 154;
+            self.action_num = 0;
+        }
+
+        let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
+        self.anim_rect = state.constants.npc.n361_gaudi_dashing[self.anim_num as usize + dir_offset];
+
+        Ok(())
+    }
 }
diff --git a/src/npc/misc.rs b/src/npc/misc.rs
index 671421e..39f7028 100644
--- a/src/npc/misc.rs
+++ b/src/npc/misc.rs
@@ -370,6 +370,21 @@ impl NPC {
         Ok(())
     }
 
+    pub(crate) fn tick_n023_teleporter_lights(&mut self, state: &mut SharedGameState) -> GameResult {
+        self.anim_counter += 1;
+        if self.anim_counter > 1 {
+            self.anim_counter = 0;
+            self.anim_num += 1;
+            if self.anim_num > 7 {
+                self.anim_num = 0;
+            }
+        } else if self.anim_counter == 1 {
+            self.anim_rect = state.constants.npc.n023_teleporter_lights[self.anim_num as usize];
+        }
+
+        Ok(())
+    }
+
     pub(crate) fn tick_n027_death_trap(&mut self, state: &mut SharedGameState) -> GameResult {
         if self.action_num == 0 {
             self.action_num = 1;
diff --git a/src/npc/mod.rs b/src/npc/mod.rs
index 2dbd901..a99bdea 100644
--- a/src/npc/mod.rs
+++ b/src/npc/mod.rs
@@ -73,11 +73,19 @@ pub struct NPC {
     pub npc_type: u16,
     pub x: isize,
     pub y: isize,
+    /// X velocity, affected by physics code
     pub vel_x: isize,
+    /// Y velocity, affected by physics code
     pub vel_y: isize,
+    /// X velocity, unaffected by physics code
+    pub vel_x2: isize,
+    /// Y velocity, unaffected by physics code
+    pub vel_y2: isize,
     pub target_x: isize,
     pub target_y: isize,
+    /// Previous X position, used by frame interpolator
     pub prev_x: isize,
+    /// Previous Y position, used by frame interpolator
     pub prev_y: isize,
     pub exp: u16,
     pub size: u8,
@@ -88,6 +96,9 @@ pub struct NPC {
     pub flags: Flag,
     pub npc_flags: NPCFlag,
     pub direction: Direction,
+    /// Raw direction value set by TSC because some NPCs have it set outside 0-4 range,
+    /// breaking the direction type.
+    pub tsc_direction: u16,
     pub display_bounds: Rect<usize>,
     pub hit_bounds: Rect<usize>,
     pub parent_id: u16,
@@ -101,7 +112,7 @@ pub struct NPC {
     pub anim_rect: Rect<usize>,
 }
 
-static PARTICLE_NPCS: [u16; 8] = [1, 4, 73, 86, 87, 129, 199, 355];
+static PARTICLE_NPCS: [u16; 9] = [1, 4, 73, 84, 86, 87, 129, 199, 355];
 
 impl NPC {
     pub fn get_start_index(&self) -> u16 {
@@ -120,6 +131,8 @@ impl NPC {
             y: 0,
             vel_x: 0,
             vel_y: 0,
+            vel_x2: 0,
+            vel_y2: 0,
             target_x: 0,
             target_y: 0,
             prev_x: 0,
@@ -133,6 +146,7 @@ impl NPC {
             flags: Flag(0),
             npc_flags: NPCFlag(0),
             direction: Direction::Left,
+            tsc_direction: 0,
             display_bounds: Rect { left: 0, top: 0, right: 0, bottom: 0 },
             hit_bounds: Rect { left: 0, top: 0, right: 0, bottom: 0 },
             parent_id: 0,
@@ -174,7 +188,13 @@ impl GameEntity<(&mut Player, &HashMap<u16, RefCell<NPC>>, &mut Stage)> for NPC
             20 => self.tick_n020_computer(state),
             21 => self.tick_n021_chest_open(state),
             22 => self.tick_n022_teleporter(state),
+            23 => self.tick_n023_teleporter_lights(state),
+            24 => self.tick_n024_power_critter(state, player),
+            25 => self.tick_n025_lift(state),
+            26 => self.tick_n026_bat_flying(state, player),
             27 => self.tick_n027_death_trap(state),
+            28 => self.tick_n028_flying_critter(state, player),
+            29 => self.tick_n029_cthulhu(state, player),
             30 => self.tick_n030_gunsmith(state),
             32 => self.tick_n032_life_capsule(state),
             34 => self.tick_n034_bed(state),
@@ -187,6 +207,7 @@ impl GameEntity<(&mut Player, &HashMap<u16, RefCell<NPC>>, &mut Stage)> for NPC
             46 => self.tick_n046_hv_trigger(state, player),
             52 => self.tick_n052_sitting_blue_robot(state),
             55 => self.tick_n055_kazuma(state),
+            58 => self.tick_n058_basu(state, player),
             59 => self.tick_n059_eye_door(state, player),
             60 => self.tick_n060_toroko(state, player),
             61 => self.tick_n061_king(state),
@@ -225,12 +246,15 @@ impl GameEntity<(&mut Player, &HashMap<u16, RefCell<NPC>>, &mut Stage)> for NPC
             112 => self.tick_n112_quote_teleport_in(state, player),
             129 => self.tick_n129_fireball_snake_trail(state),
             149 => self.tick_n149_horizontal_moving_block(state, player),
+            150 => self.tick_n150_quote(state, player),
+            154 => self.tick_n154_gaudi_dead(state),
             157 => self.tick_n157_vertical_moving_block(state, player),
             199 => self.tick_n199_wind_particles(state),
             211 => self.tick_n211_small_spikes(state),
             298 => self.tick_n298_intro_doctor(state),
             299 => self.tick_n299_intro_balrog_misery(state),
             300 => self.tick_n300_intro_demon_crown(state),
+            361 => self.tick_n361_gaudi_dashing(state, player),
             _ => Ok(()),
         }?;
 
@@ -389,6 +413,8 @@ impl NPCMap {
             y: data.y as isize * 16 * 0x200,
             vel_x: 0,
             vel_y: 0,
+            vel_x2: 0,
+            vel_y2: 0,
             target_x: 0,
             target_y: 0,
             prev_x: 0,
@@ -405,6 +431,7 @@ impl NPCMap {
             cond: Condition(0x00),
             flags: Flag(0),
             direction: if npc_flags.spawn_facing_right() { Direction::Right } else { Direction::Left },
+            tsc_direction: 0,
             npc_flags,
             display_bounds,
             hit_bounds,
@@ -437,6 +464,8 @@ impl NPCMap {
             y: 0,
             vel_x: 0,
             vel_y: 0,
+            vel_x2: 0,
+            vel_y2: 0,
             target_x: 0,
             target_y: 0,
             prev_x: 0,
@@ -453,6 +482,7 @@ impl NPCMap {
             cond: Condition(0x00),
             flags: Flag(0),
             direction: if npc_flags.spawn_facing_right() { Direction::Right } else { Direction::Left },
+            tsc_direction: 0,
             npc_flags,
             display_bounds,
             hit_bounds,
diff --git a/src/npc/quote.rs b/src/npc/quote.rs
index ab48450..4c0e488 100644
--- a/src/npc/quote.rs
+++ b/src/npc/quote.rs
@@ -1,8 +1,8 @@
-use crate::ggez::GameResult;
-use crate::npc::NPC;
-use crate::shared_game_state::SharedGameState;
 use crate::common::Direction;
+use crate::ggez::GameResult;
+use crate::npc::{NPC, NPCMap};
 use crate::player::Player;
+use crate::shared_game_state::SharedGameState;
 
 impl NPC {
     pub fn tick_n111_quote_teleport_out(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult {
@@ -131,4 +131,158 @@ impl NPC {
 
         Ok(())
     }
+
+    pub(crate) fn tick_n150_quote(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult {
+        match self.action_num {
+            0 => {
+                self.action_num = 1;
+                self.anim_num = 0;
+
+                if self.tsc_direction > 10 {
+                    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 = NPCMap::create_npc(4, &state.npc_table);
+
+                for _ in 0..4 {
+                    npc.cond.set_alive(true);
+                    npc.direction = Direction::Left;
+                    npc.x = self.x;
+                    npc.y = self.y;
+                    npc.vel_x = state.game_rng.range(-0x155..0x155) as isize;
+                    npc.vel_y = state.game_rng.range(-0x600..0) as isize;
+
+                    state.new_npcs.push(npc);
+                }
+            }
+            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 + state.game_rng.range(-1..1) as isize * 0x200;
+                self.y = self.target_y + state.game_rng.range(-1..1) as isize * 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 isize | 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;
+                    }
+                }
+            }
+            _ => {}
+        }
+
+        let dir_offset = if self.direction == Direction::Left { 0 } else { 9 };
+        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 as usize / 4;
+        }
+
+        if player.equip.has_mimiga_mask() {
+            self.anim_rect.top += 32;
+            self.anim_rect.bottom += 32;
+        }
+
+        Ok(())
+    }
 }
diff --git a/src/npc/santa.rs b/src/npc/santa.rs
index e69de29..0e1365e 100644
--- a/src/npc/santa.rs
+++ b/src/npc/santa.rs
@@ -0,0 +1,5 @@
+use crate::npc::NPC;
+
+impl NPC {
+
+}
diff --git a/src/player.rs b/src/player.rs
index f911dfb..d9d5794 100644
--- a/src/player.rs
+++ b/src/player.rs
@@ -158,7 +158,9 @@ impl Player {
         if self.flags.hit_bottom_wall() || self.flags.hit_right_slope() || self.flags.hit_left_slope() {
             self.booster_switch = 0;
 
-            if self.equip.has_booster_0_8() || self.equip.has_booster_2_0() {
+            if state.settings.infinite_booster {
+                self.booster_fuel = usize::MAX;
+            } else if self.equip.has_booster_0_8() || self.equip.has_booster_2_0() {
                 self.booster_fuel = state.constants.booster.fuel;
             } else {
                 self.booster_fuel = 0;
@@ -212,7 +214,7 @@ impl Player {
                         if self.vel_y > 0x100 { // 0.5fix9
                             self.vel_y /= 2;
                         }
-                    } else if self.equip.has_booster_2_0() {
+                    } else if (state.settings.infinite_booster || self.equip.has_booster_2_0()) {
                         if state.key_state.up() {
                             self.booster_switch = 2;
                             self.vel_x = 0;
@@ -254,7 +256,8 @@ impl Player {
                 }
             }
 
-            if self.equip.has_booster_2_0() && self.booster_switch != 0 && (!state.key_state.jump() || self.booster_fuel == 0) {
+            if (state.settings.infinite_booster || self.equip.has_booster_2_0()) && self.booster_switch != 0
+                && (!state.key_state.jump() || self.booster_fuel == 0) {
                 match self.booster_switch {
                     1 => { self.vel_x /= 2 }
                     2 => { self.vel_y /= 2 }
@@ -297,13 +300,13 @@ impl Player {
             self.vel_y -= 0x80;
         }
         if self.flags.force_right() {
-            self.vel_x += 0x80;
+            self.vel_x += 0x88;
         }
         if self.flags.force_down() {
             self.vel_y += 0x55;
         }
 
-        if self.equip.has_booster_2_0() && self.booster_switch != 0 {
+        if (state.settings.infinite_booster || self.equip.has_booster_2_0()) && self.booster_switch != 0 {
             match self.booster_switch {
                 1 => {
                     if self.flags.hit_left_wall() || self.flags.hit_right_wall() {
@@ -386,12 +389,11 @@ impl Player {
         if !self.splash && self.flags.in_water() {
             let vertical_splash = !self.flags.hit_bottom_wall() && self.vel_y > 0x200;
             let horizontal_splash = self.vel_x > 0x200 || self.vel_x < -0x200;
-            let should_splash = vertical_splash || horizontal_splash;
 
-            if should_splash {
+            if vertical_splash || horizontal_splash {
+                let mut droplet = NPCMap::create_npc(73, &state.npc_table);
+
                 for _ in 0..7 {
-                    let mut droplet = NPCMap::create_npc(73, &state.npc_table);
-
                     droplet.cond.set_alive(true);
                     droplet.direction = if self.flags.water_splash_facing_right() { Direction::Right } else { Direction::Left };
                     droplet.x = self.x + (state.game_rng.range(-8..8) * 0x200) as isize;
@@ -424,17 +426,7 @@ impl Player {
         }
 
         // camera
-        if self.direction == Direction::Left {
-            self.index_x -= 0x200; // 1.0fix9
-            if self.index_x < -0x8000 { // -64.0fix9
-                self.index_x = -0x8000;
-            }
-        } else { // possible bug?
-            self.index_x += 0x200; // 1.0fix9
-            if self.index_x > 0x8000 { // -64.0fix9
-                self.index_x = 0x8000;
-            }
-        }
+        self.index_x = clamp(self.index_x + self.direction.vector_x() * 0x200, -0x8000, 0x8000);
 
         if state.control_flags.control_enabled() && state.key_state.up() {
             self.index_y -= 0x200; // 1.0fix9
diff --git a/src/scene/game_scene.rs b/src/scene/game_scene.rs
index fae4fe9..c6c1571 100644
--- a/src/scene/game_scene.rs
+++ b/src/scene/game_scene.rs
@@ -783,6 +783,11 @@ impl GameScene {
         for npc_id in self.npc_map.npc_ids.iter() {
             if let Some(npc_cell) = self.npc_map.npcs.get(npc_id) {
                 let mut npc = npc_cell.borrow_mut();
+
+                if npc.cond.drs_destroyed() {
+                    dead_npcs.push(npc.id);
+                }
+
                 if !npc.cond.alive() {
                     continue;
                 }
@@ -856,9 +861,11 @@ impl GameScene {
                     }
                 }
 
-                if npc.cond.explode_die() {
+                if npc.cond.explode_die() && !npc.cond.drs_destroyed() {
                     dead_npcs.push(npc.id);
                 }
+
+                npc.cond.set_drs_destroyed(false);
             }
         }
 
@@ -1058,30 +1065,31 @@ impl GameScene {
                 continue;
             }
 
-            // top
-            state.texture_set.draw_rect(Rect::new_size((npc.x - npc.hit_bounds.right as isize - self.frame.x) / 0x200,
-                                                       (npc.y - npc.hit_bounds.top as isize - self.frame.y) / 0x200,
-                                                       (npc.hit_bounds.right + npc.hit_bounds.right) as isize / 0x200,
-                                                       1),
-                                        [0.0, if npc.flags.hit_top_wall() { 1.0 } else { 0.0 }, 1.0, 1.0], ctx)?;
-            // bottom
-            state.texture_set.draw_rect(Rect::new_size((npc.x - npc.hit_bounds.right as isize - self.frame.x) / 0x200,
-                                                       (npc.y + npc.hit_bounds.bottom as isize - self.frame.y) / 0x200 - 1,
-                                                       (npc.hit_bounds.right + npc.hit_bounds.right) as isize / 0x200,
-                                                       1),
-                                        [0.0, if npc.flags.hit_bottom_wall() { 1.0 } else { 0.0 }, 1.0, 1.0], ctx)?;
-            // left
-            state.texture_set.draw_rect(Rect::new_size((npc.x - npc.hit_bounds.right as isize - self.frame.x) / 0x200,
-                                                       (npc.y - npc.hit_bounds.top as isize - self.frame.y) / 0x200,
-                                                       1,
-                                                       (npc.hit_bounds.top + npc.hit_bounds.bottom) as isize / 0x200),
-                                        [0.0, if npc.flags.hit_left_wall() { 1.0 } else { 0.0 }, 1.0, 1.0], ctx)?;
-            // right
-            state.texture_set.draw_rect(Rect::new_size((npc.x + npc.hit_bounds.right as isize - self.frame.x) / 0x200 - 1,
-                                                       (npc.y - npc.hit_bounds.top as isize - self.frame.y) / 0x200,
-                                                       1,
-                                                       (npc.hit_bounds.top + npc.hit_bounds.bottom) as isize / 0x200),
-                                        [0.0, if npc.flags.hit_right_wall() { 1.0 } else { 0.0 }, 1.0, 1.0], ctx)?;
+            // todo faster way to draw dynamic rectangles
+            // // top
+            // state.texture_set.draw_rect(Rect::new_size((npc.x - npc.hit_bounds.right as isize - self.frame.x) / 0x200,
+            //                                            (npc.y - npc.hit_bounds.top as isize - self.frame.y) / 0x200,
+            //                                            (npc.hit_bounds.right + npc.hit_bounds.right) as isize / 0x200,
+            //                                            1),
+            //                             [0.0, if npc.flags.hit_top_wall() { 1.0 } else { 0.0 }, 1.0, 1.0], ctx)?;
+            // // bottom
+            // state.texture_set.draw_rect(Rect::new_size((npc.x - npc.hit_bounds.right as isize - self.frame.x) / 0x200,
+            //                                            (npc.y + npc.hit_bounds.bottom as isize - self.frame.y) / 0x200 - 1,
+            //                                            (npc.hit_bounds.right + npc.hit_bounds.right) as isize / 0x200,
+            //                                            1),
+            //                             [0.0, if npc.flags.hit_bottom_wall() { 1.0 } else { 0.0 }, 1.0, 1.0], ctx)?;
+            // // left
+            // state.texture_set.draw_rect(Rect::new_size((npc.x - npc.hit_bounds.right as isize - self.frame.x) / 0x200,
+            //                                            (npc.y - npc.hit_bounds.top as isize - self.frame.y) / 0x200,
+            //                                            1,
+            //                                            (npc.hit_bounds.top + npc.hit_bounds.bottom) as isize / 0x200),
+            //                             [0.0, if npc.flags.hit_left_wall() { 1.0 } else { 0.0 }, 1.0, 1.0], ctx)?;
+            // // right
+            // state.texture_set.draw_rect(Rect::new_size((npc.x + npc.hit_bounds.right as isize - self.frame.x) / 0x200 - 1,
+            //                                            (npc.y - npc.hit_bounds.top as isize - self.frame.y) / 0x200,
+            //                                            1,
+            //                                            (npc.hit_bounds.top + npc.hit_bounds.bottom) as isize / 0x200),
+            //                             [0.0, if npc.flags.hit_right_wall() { 1.0 } else { 0.0 }, 1.0, 1.0], ctx)?;
 
             {
                 let hit_rect_size = clamp(npc.hit_rect_size(), 1, 4);
@@ -1108,7 +1116,7 @@ impl GameScene {
 
                 batch.add_rect(((npc.x - self.frame.x) / 0x200) as f32 - 3.0,
                                ((npc.y - self.frame.y) / 0x200) as f32 - 3.0,
-                               &caret_rect);
+                               &caret2_rect);
 
                 batch.draw(ctx)?;
             }
diff --git a/src/shared_game_state.rs b/src/shared_game_state.rs
index a4166bd..4a9805a 100644
--- a/src/shared_game_state.rs
+++ b/src/shared_game_state.rs
@@ -50,6 +50,7 @@ impl TimingMode {
 
 pub struct Settings {
     pub god_mode: bool,
+    pub infinite_booster: bool,
     pub speed: f64,
     pub original_textures: bool,
     pub lighting_efects: bool,
@@ -143,6 +144,7 @@ impl SharedGameState {
             sound_manager: SoundManager::new(ctx)?,
             settings: Settings {
                 god_mode: false,
+                infinite_booster: false,
                 speed: 1.0,
                 original_textures: false,
                 lighting_efects: true,
diff --git a/src/text_script.rs b/src/text_script.rs
index 1b04867..526d144 100644
--- a/src/text_script.rs
+++ b/src/text_script.rs
@@ -22,12 +22,12 @@ use crate::ggez::{Context, GameResult};
 use crate::ggez::GameError::ParseError;
 use crate::npc::NPCMap;
 use crate::player::ControlMode;
+use crate::profile::GameProfile;
 use crate::scene::game_scene::GameScene;
 use crate::scene::title_scene::TitleScene;
 use crate::shared_game_state::SharedGameState;
 use crate::str;
 use crate::weapon::WeaponType;
-use crate::profile::GameProfile;
 
 /// Engine's text script VM operation codes.
 #[derive(EnumString, Debug, FromPrimitive, PartialEq)]
@@ -1121,7 +1121,8 @@ impl TextScriptVM {
                     OpCode::ANP => {
                         let event_num = read_cur_varint(&mut cursor)? as u16;
                         let action_num = read_cur_varint(&mut cursor)? as u16;
-                        let direction = read_cur_varint(&mut cursor)? as usize;
+                        let tsc_direction = read_cur_varint(&mut cursor)? as usize;
+                        let direction = Direction::from_int_facing(tsc_direction).unwrap_or(Direction::Left);
 
                         for npc_id in game_scene.npc_map.npc_ids.iter() {
                             if let Some(npc_cell) = game_scene.npc_map.npcs.get(npc_id) {
@@ -1129,15 +1130,16 @@ impl TextScriptVM {
 
                                 if npc.cond.alive() && npc.event_num == event_num {
                                     npc.action_num = action_num;
+                                    npc.tsc_direction = tsc_direction as u16;
 
-                                    if direction == 4 {
+                                    if direction == Direction::FacingPlayer {
                                         npc.direction = if game_scene.player.x < npc.x {
                                             Direction::Right
                                         } else {
                                             Direction::Left
                                         };
-                                    } else if let Some(dir) = Direction::from_int(direction) {
-                                        npc.direction = dir;
+                                    } else {
+                                        npc.direction = direction;
                                     }
                                 }
                             }
@@ -1148,8 +1150,8 @@ impl TextScriptVM {
                     OpCode::CNP | OpCode::INP => {
                         let event_num = read_cur_varint(&mut cursor)? as u16;
                         let new_type = read_cur_varint(&mut cursor)? as u16;
-                        let direction = Direction::from_int_facing(read_cur_varint(&mut cursor)? as usize)
-                            .unwrap_or(Direction::Left);
+                        let tsc_direction = read_cur_varint(&mut cursor)? as usize;
+                        let direction = Direction::from_int_facing(tsc_direction).unwrap_or(Direction::Left);
 
                         for npc_id in game_scene.npc_map.npc_ids.iter() {
                             if let Some(npc_cell) = game_scene.npc_map.npcs.get(npc_id) {
@@ -1187,6 +1189,7 @@ impl TextScriptVM {
                                     npc.anim_counter = 0;
                                     npc.vel_x = 0;
                                     npc.vel_y = 0;
+                                    npc.tsc_direction = tsc_direction as u16;
 
                                     if direction == Direction::FacingPlayer {
                                         npc.direction = if game_scene.player.x < npc.x {
@@ -1209,8 +1212,8 @@ impl TextScriptVM {
                         let event_num = read_cur_varint(&mut cursor)? as u16;
                         let x = read_cur_varint(&mut cursor)? as isize;
                         let y = read_cur_varint(&mut cursor)? as isize;
-                        let direction = Direction::from_int_facing(read_cur_varint(&mut cursor)? as usize)
-                            .unwrap_or(Direction::Left);
+                        let tsc_direction = read_cur_varint(&mut cursor)? as usize;
+                        let direction = Direction::from_int_facing(tsc_direction).unwrap_or(Direction::Left);
 
                         for npc_id in game_scene.npc_map.npc_ids.iter() {
                             if let Some(npc_cell) = game_scene.npc_map.npcs.get(npc_id) {
@@ -1219,6 +1222,7 @@ impl TextScriptVM {
                                 if npc.cond.alive() && npc.event_num == event_num {
                                     npc.x = x * 16 * 0x200;
                                     npc.y = y * 16 * 0x200;
+                                    npc.tsc_direction = tsc_direction as u16;
 
                                     if direction == Direction::FacingPlayer {
                                         npc.direction = if game_scene.player.x < npc.x {
@@ -1240,14 +1244,15 @@ impl TextScriptVM {
                     OpCode::SNP => {
                         let npc_type = read_cur_varint(&mut cursor)? as u16;
                         let x = read_cur_varint(&mut cursor)? as isize;
-                        let y = read_cur_varint(&mut cursor)?as isize ;
-                        let direction = Direction::from_int_facing(read_cur_varint(&mut cursor)? as usize)
-                            .unwrap_or(Direction::Left);
+                        let y = read_cur_varint(&mut cursor)? as isize;
+                        let tsc_direction = read_cur_varint(&mut cursor)? as usize;
+                        let direction = Direction::from_int_facing(tsc_direction).unwrap_or(Direction::Left);
 
                         let mut npc = NPCMap::create_npc(npc_type, &state.npc_table);
                         npc.cond.set_alive(true);
                         npc.x = x * 16 * 0x200;
                         npc.y = y * 16 * 0x200;
+                        npc.tsc_direction = tsc_direction as u16;
 
                         if direction == Direction::FacingPlayer {
                             npc.direction = if game_scene.player.x < npc.x {