Repository: doukutsu-rs/doukutsu-rs (language: rust)

Issue:
Hell bugs/inaccuracies
- [x] Mesas throw their first block too early:
Freeware:

https://user-images.githubusercontent.com/11477985/164581695-cf0d0464-8a05-4900-a31d-f815e2f47ba1.mp4

d-rs:

https://user-images.githubusercontent.com/11477985/164581798-aab71c8b-bb8d-44ba-a0a0-52fc880c26af.mp4

The following video goes with the issues below. Here is a Profile.dat for testing: [Profile.zip](https://github.com/doukutsu-rs/doukutsu-rs/files/8536297/Profile.zip)

https://user-images.githubusercontent.com/11477985/164582333-c13510eb-fb00-41a1-8371-5668eaf552dc.mp4

- [ ] The missiles should not be able to hit the back Delete from a standing position on this platform (in vanilla you need to jump up slightly to hit it)
- [x] Delete countdown timers are sometimes drawn behind the Delete (this is more apparent if you blow up those two Deletes one at a time)
- [x] The camera stutters when starting the Heavy Press fight
- [x] You can look up and down while falling in the Passage? after defeating Heavy Press. In vanilla you don't have any control of the player at all during this time. The same applies to entering the Seal Chamber, but also when you initially come out of the door, the player has the interacting sprite instead of the normal one.

(This last one might be a bit of a stretch, but it does come up in Hell speedruns.)
- [ ] Starting from the given Profile.dat, in freeware: Blow up both Deletes, then walk to the right to trigger the Bute spawners. After letting a few Butes spawn, go to fight Heavy Press. Observe that the initial lightning shot attack from Heavy Press doesn't spawn. Now try the same thing in d-rs, and observe that the lightning (likely) does spawn. In fact, in d-rs you have to spawn a lot more Butes before the lightning shot despawns.
(Note, this is not the only way to despawn the lightning attack, just one simple easily-reproducible setup.)
Freeware demonstration:

https://user-images.githubusercontent.com/11477985/164582950-caaca52c-4eda-43f0-be6e-659761349584.mp4

d-rs:

https://user-images.githubusercontent.com/11477985/164583306-1b208431-a683-4c25-ae02-87da3e7478f2.mp4
bug report NPC absolute RNG
![shot-2023-06-15_13-07-03](https://github.com/doukutsu-rs/doukutsu-rs/assets/101902085/aa631998-dd45-4548-ac23-18f661a4456f)
as you see that top left pignon won't walk left no matter how much i change the rom 
i even brute forced the frame i enter cemetery in a TAS 


Discussion / hints:

That's normal. The direction in which they spawn isn't based on RNG at all. If you check the map file in [Booster's Lab](https://github.com/taedixon/boosters-lab), you can see that the "Spawn with alt direction" flag is checked (In this case, the Pignon entity will always face right) for that specific Pignon. The first image below shows the Pignon that you're reporting about and the second image shows a different Pignon (_this_ specific Pignon will always face left when spawning).
![image](https://github.com/doukutsu-rs/doukutsu-rs/assets/50709579/af4819e2-ec94-4bca-ba19-2ba3fa051e01)
![image](https://github.com/doukutsu-rs/doukutsu-rs/assets/50709579/220f9766-a60e-4492-874f-b3bd6fae7b59)


dan don't open an issue about correct base game behavior challenge

Before reporting any issue about d-rs, check it against vanilla please. 

The only RNG use in pignon code is when they start to walk,
i think i get it it has to be absolute 
cuz i didn't face this issue in cs+ TAS

Fix patch:
diff --git a/src/game/npc/ai/balrog.rs b/src/game/npc/ai/balrog.rs
index bcdeff32..9283f190 100644
--- a/src/game/npc/ai/balrog.rs
+++ b/src/game/npc/ai/balrog.rs
@@ -679,7 +679,7 @@ impl NPC {
                     self.vel_x2 -= 1;
                     self.action_counter = 0;
 
-                    let angle = f64::atan2((self.y - player.y) as f64, (self.x - player.x) as f64)
+                    let angle = f64::atan2((self.y + 0x800 - player.y) as f64, (self.x - player.x) as f64)
                         + self.rng.range(-16..16) as f64 * CDEG_RAD;
 
                     let mut npc = NPC::create(11, &state.npc_table);
@@ -739,7 +739,7 @@ impl NPC {
                     self.anim_num = 3;
                 }
 
-                self.vel_y += ((self.target_y - self.y).signum() | 1) * 0x40;
+                self.vel_y += if self.y < self.target_y { 0x40 } else { -0x40 };
                 self.vel_y = clamp(self.vel_y, -0x200, 0x200);
             }
             6 => {
@@ -1016,9 +1016,8 @@ impl NPC {
                     }
                 }
 
-                if self.action_counter > 0 {
-                    self.action_counter -= 1;
-                } else {
+                self.action_counter = self.action_counter.saturating_sub(1);
+                if self.action_counter == 0 {
                     self.action_num = 2;
                     self.action_counter3 += 1;
                 }
@@ -1247,7 +1246,7 @@ impl NPC {
                     self.x + 0x1000 * self.direction.opposite().vector_x(),
                     self.y,
                     CaretType::Exhaust,
-                    self.direction,
+                    self.direction.opposite(),
                 );
             }
 
diff --git a/src/game/npc/ai/curly.rs b/src/game/npc/ai/curly.rs
index b754d09a..f517631c 100644
--- a/src/game/npc/ai/curly.rs
+++ b/src/game/npc/ai/curly.rs
@@ -865,56 +865,46 @@ impl NPC {
         players: [&mut Player; 2],
         npc_list: &NPCList,
     ) -> GameResult {
-        match self.action_num {
-            0 => {
-                if self.action_num == 0 {
-                    let player = &players[0];
-                    self.x = player.x;
-                    self.y = player.y;
+        let player = &players[0];
 
-                    let mut npc = NPC::create(321, &state.npc_table);
-                    npc.cond.set_alive(true);
-                    npc.parent_id = self.id;
-                    let _ = npc_list.spawn(0x100, npc);
+        if self.action_num == 0 {
+            self.x = player.x;
+            self.y = player.y;
 
-                    self.action_num = 1;
-                }
-            }
-            1 => {
-                let player = &players[0];
+            let mut npc = NPC::create(321, &state.npc_table);
+            npc.cond.set_alive(true);
+            npc.parent_id = self.id;
+            let _ = npc_list.spawn(0x100, npc);
 
-                self.direction = player.direction.opposite();
-                let grounded = player.flags.hit_bottom_wall();
+            self.action_num = 1;
+        }
 
-                self.target_x = player.x;
+        let grounded = player.flags.hit_bottom_wall();
 
-                if player.up {
-                    self.target_y = player.y + if grounded { -0x1800 } else { 0x1000 };
-                    self.anim_num = if grounded { 1 } else { 2 };
-                    self.direction = if grounded { Direction::Up } else { Direction::Bottom };
-                } else if player.down && !grounded {
-                    self.target_y = player.y - 0x1000;
-                    self.anim_num = 1;
-                    self.direction = Direction::Up;
-                } else {
-                    self.target_x += if self.direction == Direction::Right { 0xE00 } else { -0xE00 };
-                    self.target_y = player.y - 0x600;
-                    self.anim_num = 0;
-                }
+        self.target_x = player.x;
 
-                self.x += (self.target_x - self.x) / 2;
-                self.y += (self.target_y - self.y) / 2;
+        if player.up {
+            self.target_y = player.y + if grounded { -0x1400 } else { 0x1000 };
+            self.anim_num = if grounded { 1 } else { 2 };
+        } else if player.down && !grounded {
+            self.target_y = player.y - 0x1000;
+            self.anim_num = 1;
+        } else {
+            self.target_x += if player.direction == Direction::Left { 0xE00 } else { -0xE00 };
+            self.target_y = player.y - 0x600;
+            self.anim_num = 0;
+        }
 
-                if (player.anim_num & 1) != 0 {
-                    self.y -= 0x200
-                };
+        self.x += (self.target_x - self.x) / 2;
+        self.y += (self.target_y - self.y) / 2;
 
-                let dir_offset = if player.direction.opposite() == Direction::Left { 0 } else { 3 };
+        if (player.anim_num & 1) != 0 {
+            self.y -= 0x200
+        };
 
-                self.anim_rect = state.constants.npc.n320_curly_carried[self.anim_num as usize + dir_offset];
-            }
-            _ => (),
-        }
+        let dir_offset = if player.direction.opposite() == Direction::Left { 0 } else { 3 };
+
+        self.anim_rect = state.constants.npc.n320_curly_carried[self.anim_num as usize + dir_offset];
 
         Ok(())
     }
@@ -929,21 +919,20 @@ impl NPC {
         if let Some(npc) = self.get_parent_ref_mut(npc_list) {
             let player = &players[0];
 
-            self.direction = npc.direction;
             self.x = npc.x;
             self.y = npc.y;
 
-            match self.direction {
-                Direction::Right => {
-                    self.x += 0x1000;
-                }
-                Direction::Left => {
-                    self.x -= 0x1000;
+            match npc.anim_num {
+                0 => {
+                    self.direction = player.direction.opposite();
+                    self.x += 0x1000 * self.direction.vector_x();
                 }
-                Direction::Up => {
+                1 => {
+                    self.direction = Direction::Up;
                     self.y -= 0x1400;
                 }
-                Direction::Bottom => {
+                2 => {
+                    self.direction = Direction::Bottom;
                     self.y += 0x1400;
                 }
                 _ => (),
diff --git a/src/game/npc/ai/doctor.rs b/src/game/npc/ai/doctor.rs
index 0eebfd40..ad265108 100644
--- a/src/game/npc/ai/doctor.rs
+++ b/src/game/npc/ai/doctor.rs
@@ -654,7 +654,7 @@ impl NPC {
                 if self.flags.hit_bottom_wall() {
                     if self.life + 20 > self.action_counter3 {
                         self.animate(10, 1, 2);
-                    } else if player.flags.hit_bottom_wall() && player.x > self.x - 0x6000 && player.x < self.x + 0x6000
+                    } else if player.flags.hit_bottom_wall() && player.x > self.x - 0x6000 && player.x < self.x + 0x6000 && self.anim_num != 6
                     {
                         self.anim_num = 6;
                         state.quake_counter = 10;
diff --git a/src/game/npc/ai/hell.rs b/src/game/npc/ai/hell.rs
index 7479f897..8e99e24f 100644
--- a/src/game/npc/ai/hell.rs
+++ b/src/game/npc/ai/hell.rs
@@ -488,47 +488,44 @@ impl NPC {
         state: &mut SharedGameState,
         players: [&mut Player; 2],
     ) -> GameResult {
-        match self.action_num {
-            0 | 1 => {
-                if self.action_num == 0 {
-                    self.action_num = 1;
+        if self.action_num == 0 || self.action_num == 1 {
+            if self.action_num == 0 {
+                self.action_num = 1;
 
-                    self.vel_x = 0x600 * self.direction.vector_x();
-                    self.vel_y = 0x600 * self.direction.vector_y();
-                }
+                self.vel_x = 0x600 * self.direction.vector_x();
+                self.vel_y = 0x600 * self.direction.vector_y();
+            }
 
-                self.action_counter += 1;
-                if self.action_counter == 16 {
-                    self.npc_flags.set_ignore_solidity(false)
-                };
+            self.action_counter += 1;
+            if self.action_counter == 16 {
+                self.npc_flags.set_ignore_solidity(false)
+            };
 
-                self.x += self.vel_x;
-                self.y += self.vel_y;
+            self.x += self.vel_x;
+            self.y += self.vel_y;
 
-                if self.flags.hit_anything() {
-                    self.action_num = 10
-                };
+            if self.flags.hit_anything() {
+                self.action_num = 10
+            };
 
-                let player = self.get_closest_player_ref(&players);
-                if self.action_counter > 20
-                    && ((self.direction == Direction::Left && self.x <= player.x + 0x4000)
-                    || (self.direction == Direction::Up && self.y <= player.y + 0x4000)
-                    || (self.direction == Direction::Right && self.x <= player.x - 0x4000)
-                    || (self.direction == Direction::Bottom && self.y <= player.y - 0x4000))
-                {
-                    self.action_num = 10
-                }
+            let player = self.get_closest_player_ref(&players);
+            if self.action_counter > 20
+                && ((self.direction == Direction::Left && self.x <= player.x + 0x4000)
+                || (self.direction == Direction::Up && self.y <= player.y + 0x4000)
+                || (self.direction == Direction::Right && self.x >= player.x - 0x4000)
+                || (self.direction == Direction::Bottom && self.y >= player.y - 0x4000))
+            {
+                self.action_num = 10
             }
-            10 => {
-                self.npc_type = 309;
-                self.anim_num = 0;
-                self.action_num = 11;
-                self.npc_flags.set_shootable(true);
-                self.npc_flags.set_ignore_solidity(false);
-                self.damage = 5;
-                self.display_bounds.top = 0x1000;
-            }
-            _ => (),
+        }
+        if self.action_num == 10 {
+            self.npc_type = 309;
+            self.anim_num = 0;
+            self.action_num = 11;
+            self.npc_flags.set_shootable(true);
+            self.npc_flags.set_ignore_solidity(false);
+            self.damage = 5;
+            self.display_bounds.top = 0x1000;
         }
 
         self.animate(3, 0, 3);
diff --git a/src/game/npc/ai/mimiga_village.rs b/src/game/npc/ai/mimiga_village.rs
index 3e6a26fe..d2f1b537 100644
--- a/src/game/npc/ai/mimiga_village.rs
+++ b/src/game/npc/ai/mimiga_village.rs
@@ -27,12 +27,15 @@ impl NPC {
                     self.action_num = 2;
                     self.action_counter = 0;
                     self.anim_num = 1;
-                }
-
-                if self.rng.range(0..150) == 1 {
-                    self.action_num = 3;
-                    self.action_counter = 50;
-                    self.anim_num = 0;
+                } else {
+                    if self.rng.range(0..150) == 1 {
+                        self.direction = self.direction.opposite();
+                    }
+                    if self.rng.range(0..150) == 1 {
+                        self.action_num = 3;
+                        self.action_counter = 50;
+                        self.anim_num = 0;
+                    }
                 }
             }
             2 => {
@@ -49,9 +52,8 @@ impl NPC {
                     self.anim_counter = 0;
                 }
 
-                if self.action_counter > 0 {
-                    self.action_counter -= 1;
-                } else {
+                self.action_counter = self.action_counter.saturating_sub(1);
+                if self.action_counter == 0 {
                     self.action_num = 0;
                 }
 
@@ -371,9 +373,8 @@ impl NPC {
                     self.anim_counter = 0;
                 }
 
-                if self.action_counter > 0 {
-                    self.action_counter -= 1;
-                } else {
+                self.action_counter = self.action_counter.saturating_sub(1);
+                if self.action_counter == 0 {
                     self.action_num = 0;
                 }
 
diff --git a/src/game/npc/ai/misc.rs b/src/game/npc/ai/misc.rs
index 97fa4a9c..b990f510 100644
--- a/src/game/npc/ai/misc.rs
+++ b/src/game/npc/ai/misc.rs
@@ -190,6 +190,7 @@ impl NPC {
 
     pub(crate) fn tick_n016_save_point(&mut self, state: &mut SharedGameState, npc_list: &NPCList) -> GameResult {
         if self.action_num == 0 {
+            self.npc_flags.set_interactable(true);
             self.action_num = 1;
             
             if self.direction == Direction::Right {
@@ -200,7 +201,7 @@ impl NPC {
                 let mut npc = NPC::create(4, &state.npc_table);
                 npc.cond.set_alive(true);
 
-                for _ in 0..3 {
+                for _ in 0..4 {
                     npc.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
                     npc.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
                     npc.vel_x = self.rng.range(-341..341) as i32;
@@ -211,7 +212,7 @@ impl NPC {
             }
         }
 
-        if self.flags.hit_bottom_wall() {
+        if self.action_num == 1 && self.flags.hit_bottom_wall() {
             self.npc_flags.set_interactable(true);
         }
 
@@ -233,11 +234,13 @@ impl NPC {
             
             //Creates smoke when spawned in a shelter
             if self.direction == Direction::Right {
+                self.vel_y = -0x200;
+
                 //Creates smoke
                 let mut npc = NPC::create(4, &state.npc_table);
                 npc.cond.set_alive(true);
 
-                for _ in 0..3 {
+                for _ in 0..4 {
                     npc.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
                     npc.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
                     npc.vel_x = self.rng.range(-341..341) as i32;
@@ -267,9 +270,8 @@ impl NPC {
                 self.anim_rect = state.constants.npc.n017_health_refill[0];
                 self.anim_num = 0;
 
-                if self.action_counter > 0 {
-                    self.action_counter -= 1;
-                } else {
+                self.action_counter = self.action_counter.saturating_sub(1);
+                if self.action_counter == 0 {
                     self.action_num = 1;
                 }
             }
@@ -284,9 +286,8 @@ impl NPC {
                     self.anim_rect = state.constants.npc.n017_health_refill[0];
                 }
 
-                if self.action_counter > 0 {
-                    self.action_counter -= 1;
-                } else {
+                self.action_counter = self.action_counter.saturating_sub(1);
+                if self.action_counter == 0 {
                     self.action_num = 1;
                 }
             }
@@ -294,9 +295,8 @@ impl NPC {
                 self.anim_num = 1;
                 self.anim_rect = state.constants.npc.n017_health_refill[1];
 
-                if self.action_counter > 0 {
-                    self.action_counter -= 1;
-                } else {
+                self.action_counter = self.action_counter.saturating_sub(1);
+                if self.action_counter == 0 {
                     self.action_num = 1;
                 }
             }
@@ -2056,7 +2056,7 @@ impl NPC {
         stage: &mut Stage,
     ) -> GameResult {
         match self.action_num {
-            0 => {
+            0 | 10 | 11 => {
                 if self.action_num == 0 {
                     match self.direction {
                         Direction::Left => {
@@ -2079,31 +2079,19 @@ impl NPC {
                     }
                 }
 
-                if self.direction == Direction::Up {
-                    self.action_num = 11;
-                    self.action_counter = 16;
-
-                    if self.action_counter > 0 {
-                        self.action_counter = self.action_counter.saturating_sub(2);
-                    } else {
+                if self.action_num != 0 || self.direction == Direction::Up {
+                    if self.action_num == 10 {
+                        self.action_num = 11;
+                        self.action_counter = 16;
+                    }
+                    
+                    self.action_counter = self.action_counter.saturating_sub(2);
+                    if self.action_counter == 0 {
                         self.action_num = 100;
                         self.npc_flags.set_invulnerable(true);
                     }
                 }
             }
-            10 | 11 => {
-                if self.action_num == 10 {
-                    self.action_num = 11;
-                    self.action_counter = 16;
-                }
-
-                if self.action_counter > 0 {
-                    self.action_counter = self.action_counter.saturating_sub(2);
-                } else {
-                    self.action_num = 100;
-                    self.npc_flags.set_invulnerable(true);
-                }
-            }
             100 => {
                 self.vel_y += 0x40;
                 if self.vel_y > 0x700 {
@@ -2154,7 +2142,7 @@ impl NPC {
 
         if self.action_num == 11 {
             self.anim_rect.top += self.action_counter;
-            self.anim_rect.bottom += self.action_counter;
+            self.anim_rect.bottom -= self.action_counter;
             self.display_bounds.top = (16u32).saturating_sub(self.action_counter as u32) * 0x200;
         }
 
diff --git a/src/game/npc/ai/misery.rs b/src/game/npc/ai/misery.rs
index bf1d2269..facc4160 100644
--- a/src/game/npc/ai/misery.rs
+++ b/src/game/npc/ai/misery.rs
@@ -318,7 +318,8 @@ impl NPC {
             27 => {
                 self.action_counter += 1;
                 if self.action_counter == 50 {
-                    self.action_num = 14;
+                    self.action_num = 0;
+                    self.anim_num = 0;
                 }
             }
             30 | 31 => {
@@ -363,6 +364,10 @@ impl NPC {
 
                     let _ = npc_list.spawn(0x100, npc);
                 }
+
+                if self.action_counter > 50 {
+                    self.action_num = 0;
+                }
             }
             50 => {
                 self.anim_num = 8;
@@ -919,7 +924,7 @@ impl NPC {
 
         match self.action_num {
             0 | 1 => {
-                if self.action_num == 9 {
+                if self.action_num == 0 {
                     self.action_num = 1;
                     self.y -= 0x1000;
                     state.sound_manager.play_sfx(29);
@@ -933,7 +938,7 @@ impl NPC {
                 self.anim_num = 9;
             }
             20 | 21 => {
-                if self.action_num == 21 {
+                if self.action_num == 20 {
                     self.action_num = 21;
                     self.action_counter = 0;
                     self.anim_num = 0;
@@ -954,10 +959,10 @@ impl NPC {
 
                 let player = self.get_closest_player_ref(&players);
 
-                self.direction = if player.x > self.x { Direction::Left } else { Direction::Right };
+                self.direction = if player.x > self.x { Direction::Right } else { Direction::Left };
             }
             30 | 31 => {
-                if self.action_num == 31 {
+                if self.action_num == 30 {
                     self.action_num = 31;
                     self.action_counter = 0;
                     self.anim_num = 2;
@@ -997,6 +1002,8 @@ impl NPC {
                     self.vel_x = 0;
                     self.vel_y = 0;
 
+                    state.sound_manager.play_sfx(103);
+
                     let player = self.get_closest_player_ref(&players);
                     self.direction = if self.x > player.x { Direction::Left } else { Direction::Right };
                     self.action_counter3 = if player.y >= 0x14000 { 289 } else { 290 };
@@ -1045,7 +1052,7 @@ impl NPC {
                 if self.action_counter > 50 {
                     self.action_num = 30;
                     self.vel_y = -0x200;
-                    self.vel_x = self.direction.vector_x() * 0x200;
+                    self.vel_x = self.direction.opposite().vector_x() * 0x200;
                 }
             }
             50 | 51 => {
@@ -1070,17 +1077,17 @@ impl NPC {
 
                     let mut npc = NPC::create(301, &state.npc_table);
                     npc.cond.set_alive(true);
-                    npc.x = self.x + 0x1400 * self.direction.vector_x();
+                    npc.x = self.x + 0x1400 * self.direction.opposite().vector_x();
                     npc.y = self.y;
                     npc.tsc_direction = match ((self.action_counter / 6) & 3, self.direction) {
-                        (0, Direction::Left) => 0x58,
-                        (1, Direction::Left) => 0x6C,
-                        (2, Direction::Left) => 0x94,
-                        (3, Direction::Left) => 0xA8,
-                        (0, _) => 0xD8,
-                        (1, _) => 0xEC,
-                        (2, _) => 0x14,
-                        (3, _) => 0x28,
+                        (0, Direction::Left) => 0xD8,
+                        (1, Direction::Left) => 0xEC,
+                        (2, Direction::Left) => 0x14,
+                        (3, Direction::Left) => 0x28,
+                        (0, _) => 0x58,
+                        (1, _) => 0x6C,
+                        (2, _) => 0x94,
+                        (3, _) => 0xA8,
                         _ => unsafe {
                             unreachable_unchecked();
                         },
diff --git a/src/game/npc/ai/sue.rs b/src/game/npc/ai/sue.rs
index 2eee2df8..451bbbc4 100644
--- a/src/game/npc/ai/sue.rs
+++ b/src/game/npc/ai/sue.rs
@@ -340,7 +340,7 @@ impl NPC {
         stage: &mut Stage,
         boss: &mut BossNPC,
     ) -> GameResult {
-        if self.action_num < 100 && (!boss.parts[0].cond.alive() || self.life < 400) {
+        if self.action_num < 100 && (!boss.parts[0].cond.alive() || self.life < 500) {
             self.action_num = 100;
         }
 
@@ -403,7 +403,7 @@ impl NPC {
 
                 let player = self.get_closest_player_ref(&players);
 
-                self.direction = if player.x > self.x { Direction::Left } else { Direction::Right };
+                self.direction = if player.x > self.x { Direction::Right } else { Direction::Left };
 
                 if self.life + 50 < self.action_counter3 {
                     self.action_counter3 = self.life;
@@ -451,7 +451,7 @@ impl NPC {
                     let half_h = stage.map.height as i32 * state.tile_size.as_int() * 0x200 / 2;
 
                     if ((self.x < half_w && self.vel_x > 0) || (self.x > half_w && self.vel_x < 0))
-                        || ((self.y < half_h && self.vel_y > 0) || (self.y > half_h && self.vel_y < 0))
+                        && ((self.y < half_h && self.vel_y > 0) || (self.y > half_h && self.vel_y < 0))
                     {
                         self.npc_flags.set_ignore_solidity(true);
                     }
@@ -486,7 +486,7 @@ impl NPC {
                     let half_h = stage.map.height as i32 * state.tile_size.as_int() * 0x200 / 2;
 
                     if ((self.x < half_w && self.vel_x > 0) || (self.x > half_w && self.vel_x < 0))
-                        || ((self.y < half_h && self.vel_y > 0) || (self.y > half_h && self.vel_y < 0))
+                        && ((self.y < half_h && self.vel_y > 0) || (self.y > half_h && self.vel_y < 0))
                     {
                         self.npc_flags.set_ignore_solidity(true);
                     }
@@ -524,7 +524,7 @@ impl NPC {
                     self.action_num = 42;
                     self.action_counter = 0;
 
-                    self.vel_x = self.direction.vector_x() * 0x200;
+                    self.vel_x = self.direction.opposite().vector_x() * 0x200;
                     self.vel_y = -0x200;
                 }
             }
@@ -561,6 +561,7 @@ impl NPC {
                     self.damage = 0;
                     self.npc_flags.set_shootable(false);
                     self.npc_flags.set_ignore_solidity(true);
+                    self.vel_y = -0x200;
                     self.shock += 50;
                     boss.parts[0].anim_num += 1;
                 }
diff --git a/src/game/npc/ai/toroko.rs b/src/game/npc/ai/toroko.rs
index 86fac6f4..74ab6f3c 100644
--- a/src/game/npc/ai/toroko.rs
+++ b/src/game/npc/ai/toroko.rs
@@ -193,10 +193,10 @@ impl NPC {
 
                 self.vel_x = 0x100 * self.direction.vector_x();
 
-                self.action_counter += 1;
                 if self.action_counter != 0 && self.flags.hit_bottom_wall() {
                     self.action_num = 2;
                 }
+                self.action_counter += 1;
             }
             2 | 3 => {
                 if self.action_num == 2 {
@@ -240,11 +240,11 @@ impl NPC {
             4 => {
                 self.vel_x = 0x100 * self.direction.vector_x();
 
-                self.action_counter += 1;
                 if self.action_counter != 0 && self.flags.hit_bottom_wall() {
                     self.action_num = 5;
                     self.npc_flags.set_interactable(true);
                 }
+                self.action_counter += 1;
             }
             5 => {
                 self.vel_x = 0;
diff --git a/src/game/npc/boss/ballos.rs b/src/game/npc/boss/ballos.rs
index 2775f753..33674556 100644
--- a/src/game/npc/boss/ballos.rs
+++ b/src/game/npc/boss/ballos.rs
@@ -176,6 +176,7 @@ impl NPC {
         match self.action_num {
             0 | 1 => {
                 if self.action_num == 0 {
+                    self.action_num = 1;
                     self.action_counter = self.rng.range(0..40) as u16;
                 }
                 if self.action_counter > 0 {
@@ -362,13 +363,13 @@ impl NPC {
                     }
 
                     if self.action_num == 231 {
-                        self.direction = if self.action_num == 220 { Direction::Left } else { Direction::Right };
+                        self.face_player(player);
                     }
 
                     self.action_num += 1;
                     self.action_counter = 0;
                     self.damage = 3;
-                    let sign = self.direction.vector_x();
+                    let sign = if self.action_num == 221 { -1 } else { 1 };
 
                     for _ in 0..8 {
                         let mut npc = NPC::create(4, &state.npc_table);
@@ -435,7 +436,7 @@ impl NPC {
                     self.action_counter = 0;
                     self.anim_num = 3;
 
-                    self.direction = if self.action_num == 220 { Direction::Left } else { Direction::Right };
+                    self.face_player(player);
                 }
             }
             242 => {
@@ -520,6 +521,8 @@ impl NPC {
                     self.target_x = self.x;
                     self.vel_x = 0;
                     self.npc_flags.set_shootable(false);
+                    // I think Pixel meant for the smoke radius to be 16 pixels (0x2000) instead of 16 units,
+                    // because as it is, this just gets divided by 0x200 units/px and becomes 0
                     npc_list.create_death_smoke(self.x, self.y, 16, 16, state, &self.rng);
                 }
                 self.vel_y += 0x20;
@@ -657,7 +660,7 @@ impl NPC {
                 }
 
                 if self.action_counter2 < 2 {
-                    self.action_counter2 += 512;
+                    self.action_counter2 += 512 - 2; // Still have to subtract 2 first :)
                 } else {
                     self.action_counter2 -= 2;
                 }
@@ -672,7 +675,7 @@ impl NPC {
                 if self.life < 900 {
                     self.action_num = 22;
                     self.npc_flags.set_shootable(false);
-                    npc_list.create_death_smoke(self.x, self.y, 16, 32, state, &self.rng);
+                    npc_list.create_death_smoke(self.x, self.y, 0x2000, 32, state, &self.rng);
                     state.sound_manager.play_sfx(71);
                 }
 
@@ -686,7 +689,7 @@ impl NPC {
                 self.anim_num = 2;
 
                 if self.action_counter2 < 2 {
-                    self.action_counter2 += 512;
+                    self.action_counter2 += 512 - 2;
                 } else {
                     self.action_counter2 -= 2;
                 }
@@ -699,7 +702,7 @@ impl NPC {
                 self.anim_num = 2;
 
                 if self.action_counter2 < 4 {
-                    self.action_counter2 += 512;
+                    self.action_counter2 += 512 - 4;
                 } else {
                     self.action_counter2 -= 4;
                 }
@@ -753,6 +756,8 @@ impl NPC {
                     self.damage = 5;
                     self.npc_flags.set_ignore_solidity(false);
                     self.npc_flags.set_shootable(false);
+                    npc_list.create_death_smoke(self.x, self.y, 0x2000, 32, state, &self.rng);
+                    state.sound_manager.play_sfx(71);
                 }
 
                 if self.flags.hit_left_wall() {
@@ -793,7 +798,7 @@ impl NPC {
                         self.anim_num = 0;
                     }
                 } else {
-                    npc_list.create_death_smoke(self.x, self.y, 16, 32, state, &self.rng);
+                    npc_list.create_death_smoke(self.x, self.y, 0x2000, 32, state, &self.rng);
                     state.sound_manager.play_sfx(71);
                     self.vanish(state);
                     return Ok(());
@@ -816,6 +821,8 @@ impl NPC {
                     npc.x = self.x - 0x1000;
                     npc.y = self.y + 0x1800;
                     let _ = npc_list.spawn(0x100, npc);
+
+                    state.sound_manager.play_sfx(26);
                 }
                 (Direction::Up, 268) => {
                     let mut npc = NPC::create(4, &state.npc_table);
@@ -829,6 +836,8 @@ impl NPC {
                     npc.x = self.x - 0x1800;
                     npc.y = self.y - 0x1000;
                     let _ = npc_list.spawn(0x100, npc);
+
+                    state.sound_manager.play_sfx(26);
                 }
                 (Direction::Right, 396) => {
                     let mut npc = NPC::create(4, &state.npc_table);
@@ -848,6 +857,8 @@ impl NPC {
                     npc.x = self.x - 0x1000;
                     npc.y = self.y - 0x1800;
                     let _ = npc_list.spawn(0x100, npc);
+
+                    state.sound_manager.play_sfx(26);
                 }
                 (Direction::Bottom, 12) => {
                     let mut npc = NPC::create(4, &state.npc_table);
@@ -861,6 +872,8 @@ impl NPC {
                     npc.x = self.x + 0x1800;
                     npc.y = self.y - 0x1000;
                     let _ = npc_list.spawn(0x100, npc);
+
+                    state.sound_manager.play_sfx(26);
                 }
                 _ => (),
             }
@@ -1017,7 +1030,7 @@ impl NPC {
                 if self.action_counter2 > 0 {
                     self.action_counter2 -= 1;
                 } else {
-                    self.action_counter2 += 0x400;
+                    self.action_counter2 += 0x400 - 1;
                 }
 
                 if boss.parts[0].action_num == 421 {
@@ -1040,7 +1053,7 @@ impl NPC {
                 if self.action_counter2 > 1 {
                     self.action_counter2 -= 2;
                 } else {
-                    self.action_counter2 += 0x400;
+                    self.action_counter2 += 0x400 - 2;
                 }
 
                 if boss.parts[0].action_num == 422 {
@@ -1108,8 +1121,6 @@ impl NPC {
                     self.vel_y = self.target_y - self.y;
                 }
             }
-        } else {
-            self.vel_y = 0;
         }
 
         self.x += self.vel_x;
@@ -1432,7 +1443,7 @@ impl BossNPC {
 
                 self.parts[3].cond.set_alive(true);
                 self.parts[3].cond.set_damage_boss(true);
-                self.parts[3].npc_flags.set_solid_hard(true); // This should be soft -- investigate bug with large soft collision boxes?
+                self.parts[3].npc_flags.set_solid_soft(true);
                 self.parts[3].npc_flags.set_invulnerable(true);
                 self.parts[3].npc_flags.set_ignore_solidity(true);
                 self.parts[3].display_bounds = Rect { left: 0x7800, top: 0x7800, right: 0x7800, bottom: 0x7800 };
diff --git a/src/game/npc/boss/core.rs b/src/game/npc/boss/core.rs
index 2e4be32a..62476ffd 100644
--- a/src/game/npc/boss/core.rs
+++ b/src/game/npc/boss/core.rs
@@ -16,7 +16,7 @@ impl NPC {
             self.cond.set_alive(false);
         }
 
-        if (self.y - 0x800) > state.water_level {
+        if self.flags.in_water() {
             self.x += self.vel_x / 2;
             self.y += self.vel_y / 2;
         } else {
diff --git a/src/game/npc/boss/monster_x.rs b/src/game/npc/boss/monster_x.rs
index 26cb5d93..2ba8ac47 100644
--- a/src/game/npc/boss/monster_x.rs
+++ b/src/game/npc/boss/monster_x.rs
@@ -38,7 +38,8 @@ impl NPC {
                 self.y += self.vel_y;
 
                 let player = self.get_closest_player_mut(players);
-                let direction = f64::atan2(-(self.y - player.y) as f64, -(self.x - player.x) as f64);
+                // Get angle between 0 and 2*PI
+                let direction = f64::atan2((self.y - player.y) as f64, (self.x - player.x) as f64) + std::f64::consts::PI;
 
                 if direction < radians {
                     if radians - direction < std::f64::consts::PI {
diff --git a/src/game/npc/boss/sisters.rs b/src/game/npc/boss/sisters.rs
index 8c0e2209..baa5e30d 100644
--- a/src/game/npc/boss/sisters.rs
+++ b/src/game/npc/boss/sisters.rs
@@ -316,6 +316,15 @@ impl BossNPC {
                     part.hit_bounds.left = 0x2000;
 
                     state.sound_manager.play_sfx(51);
+                    
+                    npc_list.create_death_smoke(
+                        part.x,
+                        part.y,
+                        part.display_bounds.right as usize,
+                        4,
+                        state,
+                        &part.rng
+                    );
                 }
             }
             220 => {
@@ -328,11 +337,11 @@ impl BossNPC {
                     npc.y = part.y;
 
                     let player = part.get_closest_player_ref(players);
-                    let angle = f64::atan2((player.y - npc.y) as f64, (player.x - npc.x) as f64)
+                    let angle = f64::atan2((part.y - player.y) as f64, (part.x - player.x) as f64)
                         + (part.rng.range(-6..6) as f64 * CDEG_RAD);
 
-                    npc.vel_x = (angle.cos() * 512.0) as i32;
-                    npc.vel_y = (angle.sin() * 512.0) as i32;
+                    npc.vel_x = (angle.cos() * -512.0) as i32;
+                    npc.vel_y = (angle.sin() * -512.0) as i32;
 
                     let _ = npc_list.spawn(0x100, npc);
 
@@ -375,14 +384,14 @@ impl BossNPC {
                     npc.cond.set_alive(true);
 
                     let player = part.get_closest_player_ref(players);
-                    let angle = f64::atan2((player.y - npc.y) as f64, (player.x - npc.x) as f64)
+                    let angle = f64::atan2((part.y - player.y) as f64, (part.x - player.x) as f64)
                         + (part.rng.range(-6..6) as f64 * CDEG_RAD);
 
                     npc.x = part.x + 0x1000 * part.direction.vector_x();
                     npc.y = part.y;
 
-                    npc.vel_x = (angle.cos() * 512.0) as i32;
-                    npc.vel_y = (angle.sin() * 512.0) as i32;
+                    npc.vel_x = (angle.cos() * -512.0) as i32;
+                    npc.vel_y = (angle.sin() * -512.0) as i32;
 
                     let _ = npc_list.spawn(0x100, npc);
 
@@ -390,6 +399,7 @@ impl BossNPC {
                 }
             }
             1000 => {
+                part.npc_flags.set_shootable(false);
                 part.anim_num = 3;
             }
             _ => (),
diff --git a/src/game/npc/boss/undead_core.rs b/src/game/npc/boss/undead_core.rs
index bdd9a19c..7f9410db 100644
--- a/src/game/npc/boss/undead_core.rs
+++ b/src/game/npc/boss/undead_core.rs
@@ -20,7 +20,7 @@ impl NPC {
         if self.action_num == 0 {
             self.action_num = 20;
             self.target_y = self.y;
-            self.vel_y = if self.rng.range(0..100) & 1 == 0 { -0x100 } else { 0x100 };
+            self.vel_y = if self.rng.range(0..100) & 1 != 0 { -0x100 } else { 0x100 };
         }
 
         if self.action_num == 20 {
@@ -525,6 +525,7 @@ impl BossNPC {
                     self.parts[10].npc_flags.set_invulnerable(true);
                     self.parts[11].npc_flags.set_shootable(true);
                     self.parts[19].action_counter = self.parts[0].life;
+                    v19 = true;
 
                     state.quake_counter = 100;
                     state.quake_rumble_counter = 100;
diff --git a/src/game/npc/utils.rs b/src/game/npc/utils.rs
index c9acf0da..bf8e9b6b 100644
--- a/src/game/npc/utils.rs
+++ b/src/game/npc/utils.rs
@@ -362,9 +362,9 @@ impl NPCList {
                 state.set_flag(npc.flag_num as usize, true);
 
                 match npc.size {
-                    1 => self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 3, state, &npc.rng),
-                    2 => self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 7, state, &npc.rng),
-                    3 => self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 12, state, &npc.rng),
+                    1 => self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 4, state, &npc.rng),
+                    2 => self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 8, state, &npc.rng),
+                    3 => self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 16, state, &npc.rng),
                     _ => {}
                 };
             }
diff --git a/src/game/player/mod.rs b/src/game/player/mod.rs
index c43a1107..a469fb74 100644
--- a/src/game/player/mod.rs
+++ b/src/game/player/mod.rs
@@ -746,7 +746,7 @@ impl Player {
         if self.flags.hit_bottom_wall() {
             if self.cond.interacted() {
                 self.skin.set_state(PlayerAnimationState::Examining);
-                self.anim_num = 0;
+                self.anim_num = 11;
                 self.anim_counter = 0;
             } else if state.control_flags.control_enabled()
                 && (self.controller.move_up() || self.strafe_up)
@@ -794,7 +794,7 @@ impl Player {
 
                 self.cond.set_fallen(false);
                 self.skin.set_state(PlayerAnimationState::LookingUp);
-                self.anim_num = 0;
+                self.anim_num = 5;
                 self.anim_counter = 0;
             } else {
                 if self.cond.fallen() {
@@ -806,19 +806,15 @@ impl Player {
                 self.anim_num = 0;
                 self.anim_counter = 0;
             }
-        } else if state.control_flags.control_enabled()
-            && (self.controller.look_up() || self.strafe_up)
-            && self.control_mode == ControlMode::Normal
+        } else if self.up
         {
             self.skin.set_state(PlayerAnimationState::FallingLookingUp);
-            self.anim_num = 0;
+            self.anim_num = 6;
             self.anim_counter = 0;
-        } else if state.control_flags.control_enabled()
-            && self.controller.look_down()
-            && self.control_mode == ControlMode::Normal
+        } else if self.down
         {
             self.skin.set_state(PlayerAnimationState::FallingLookingDown);
-            self.anim_num = 0;
+            self.anim_num = 10;
             self.anim_counter = 0;
         } else {
             if self.vel_y > 0 {
diff --git a/src/game/player/player_hit.rs b/src/game/player/player_hit.rs
index d1157461..fb79d9ca 100644
--- a/src/game/player/player_hit.rs
+++ b/src/game/player/player_hit.rs
@@ -106,7 +106,7 @@ impl Player {
         let mut flags = Flag(0);
 
         if ((self.y - self.hit_bounds.top as i32) < (npc.y + npc.hit_bounds.bottom as i32 - 0x600))
-            && ((self.y + self.hit_bounds.bottom as i32) > (npc.y - npc.hit_bounds.bottom as i32 + 0x600))
+            && ((self.y + self.hit_bounds.bottom as i32) > (npc.y - npc.hit_bounds.top as i32 + 0x600))
             && ((self.x - self.hit_bounds.right as i32) < (npc.x + npc.hit_bounds.right as i32))
             && ((self.x - self.hit_bounds.right as i32) > npc.x)
         {
@@ -118,7 +118,7 @@ impl Player {
         }
 
         if ((self.y - self.hit_bounds.top as i32) < (npc.y + npc.hit_bounds.bottom as i32 - 0x600))
-            && ((self.y + self.hit_bounds.bottom as i32) > (npc.y - npc.hit_bounds.bottom as i32 + 0x600))
+            && ((self.y + self.hit_bounds.bottom as i32) > (npc.y - npc.hit_bounds.top as i32 + 0x600))
             && ((self.x + self.hit_bounds.right as i32 - 0x200) > (npc.x - npc.hit_bounds.right as i32))
             && ((self.x + self.hit_bounds.right as i32 - 0x200) < npc.x)
         {
diff --git a/src/game/player/skin/basic.rs b/src/game/player/skin/basic.rs
index 0f8b5977..25556f40 100644
--- a/src/game/player/skin/basic.rs
+++ b/src/game/player/skin/basic.rs
@@ -142,8 +142,8 @@ impl PlayerSkin for BasicPlayerSkin {
             PlayerAnimationState::Examining => 7,
             PlayerAnimationState::Sitting => 8,
             PlayerAnimationState::Collapsed => 9,
-            PlayerAnimationState::Jumping => 1,
-            PlayerAnimationState::Falling => 2,
+            PlayerAnimationState::Jumping => 2,
+            PlayerAnimationState::Falling => 1,
             PlayerAnimationState::FallingLookingUp => 4,
             PlayerAnimationState::FallingLookingDown => 6,
             PlayerAnimationState::FallingUpsideDown => 10,
diff --git a/src/game/scripting/tsc/text_script.rs b/src/game/scripting/tsc/text_script.rs
index f7536f97..42193643 100644
--- a/src/game/scripting/tsc/text_script.rs
+++ b/src/game/scripting/tsc/text_script.rs
@@ -764,8 +764,19 @@ impl TextScriptVM {
             TSCOpCode::MYD => {
                 let new_direction = read_cur_varint(&mut cursor)? as usize;
                 if let Some(direction) = Direction::from_int(new_direction) {
-                    game_scene.player1.direction = direction;
-                    game_scene.player2.direction = direction;
+                    if direction != Direction::Bottom {
+                        game_scene.player1.direction = direction;
+                        game_scene.player2.direction = direction;
+                    }
+                } else if new_direction >= 10 {
+                    for npc in game_scene.npc_list.iter_alive() {
+                        // The vanilla game treats this as a 1-byte value lol
+                        //if npc.event_num == (new_direction & 0xFF) as u16 {
+                        if npc.event_num == new_direction as u16 {
+                            game_scene.player1.direction = if game_scene.player1.x > npc.x { Direction::Left } else { Direction::Right };
+                            game_scene.player2.direction = if game_scene.player2.x > npc.x { Direction::Left } else { Direction::Right };
+                        }
+                    }
                 }
                 game_scene.player1.cond.set_interacted(new_direction == 3);
                 game_scene.player2.cond.set_interacted(new_direction == 3);
@@ -1185,6 +1196,11 @@ impl TextScriptVM {
                 new_scene.frame.wait = game_scene.frame.wait;
                 new_scene.nikumaru = game_scene.nikumaru;
                 new_scene.replay = game_scene.replay.clone();
+                // Reset player invincibility (kind of hacky, but oh well)
+                if state.constants.textscript.reset_invicibility_on_any_script {
+                    new_scene.player1.shock_counter = 0;
+                    new_scene.player2.shock_counter = 0;
+                }
 
                 let skip = state.textscript_vm.flags.cutscene_skip();
                 state.control_flags.set_tick_world(true);
diff --git a/src/game/weapon/bubbler.rs b/src/game/weapon/bubbler.rs
index 77794392..6024d1cc 100644
--- a/src/game/weapon/bubbler.rs
+++ b/src/game/weapon/bubbler.rs
@@ -84,7 +84,7 @@ impl Weapon {
                 }
                 Direction::Left => {
                     bullet_manager.create_bullet(
-                        player.x - 0x1800,
+                        player.x - 0xC00,
                         player.y + 0x600,
                         btype,
                         player_id,
@@ -95,7 +95,7 @@ impl Weapon {
                 }
                 Direction::Right => {
                     bullet_manager.create_bullet(
-                        player.x + 0x1800,
+                        player.x + 0xC00,
                         player.y + 0x600,
                         btype,
                         player_id,
@@ -126,7 +126,7 @@ impl Weapon {
                 return;
             }
 
-            self.counter2 = 0; // recharge time counter
+            // self.counter2 : recharge time counter
             self.counter1 += 1; // autofire counter
 
             if self.counter1 > 6 {
diff --git a/src/game/weapon/bullet.rs b/src/game/weapon/bullet.rs
index 38637ced..3e9568ad 100644
--- a/src/game/weapon/bullet.rs
+++ b/src/game/weapon/bullet.rs
@@ -617,21 +617,23 @@ impl Bullet {
             if self.btype == 15 {
                 match self.direction {
                     Direction::Left | Direction::Right => {
-                        self.vel_y = (self.y - player.y).signum() * 0x100;
+                        self.vel_y = if self.y > player.y { 0x100 } else { -0x100 };
                         self.vel_x = self.rng.range(-0x200..0x200);
                     }
                     Direction::Up | Direction::Bottom => {
-                        self.vel_x = (self.x - player.x).signum() * 0x100;
+                        self.vel_x = if self.x > player.x { 0x100 } else { -0x100 };
                         self.vel_y = self.rng.range(-0x200..0x200);
                     }
                     _ => {}
                 }
 
+                // offset by 1 because we 'increment' late
                 self.counter1 = match self.counter2 {
-                    1 => 64,
-                    2 => 51,
-                    _ => 128,
+                    1 => 51,
+                    2 => 128,
+                    _ => 64,
                 };
+                self.counter2 = 0;
             } else {
                 self.counter1 = 128;
             }
@@ -649,10 +651,10 @@ impl Bullet {
             if self.btype == 15 {
                 match self.direction {
                     Direction::Left | Direction::Right => {
-                        self.vel_y = (player.y - self.y).signum() * 0x20;
+                        self.vel_y += if self.y < self.target_y { 0x20 } else { -0x20 };
                     }
                     Direction::Up | Direction::Bottom => {
-                        self.vel_x = (player.x - self.x).signum() * 0x20;
+                        self.vel_x += if self.x < self.target_x { 0x20 } else { -0x20 };
                     }
                     _ => {}
                 }
@@ -938,14 +940,10 @@ impl Bullet {
 
             match self.direction {
                 Direction::Left => {
-                    let val = self.rng.range(10..16);
-                    // what the fuck
-                    self.vel_x = (((val * -0x200).rotate_left(1) & 1) - val * 0x200) / 2;
+                    self.vel_x = (self.rng.range(10..16) * -0x200) / 2;
                 }
                 Direction::Up => {
-                    let val = self.rng.range(10..16);
-                    // what the fuck
-                    self.vel_y = (((val * -0x200).rotate_left(1) & 1) - val * 0x200) / 2;
+                    self.vel_y = (self.rng.range(10..16) * -0x200) / 2;
                 }
                 Direction::Right => {
                     self.vel_x = (self.rng.range(10..16) * 0x200) / 2;
@@ -1256,21 +1254,23 @@ impl Bullet {
             if self.btype == 30 {
                 match self.direction {
                     Direction::Left | Direction::Right => {
-                        self.vel_y = (self.y - player.y).signum() * 0x100;
+                        self.vel_y = if self.y > player.y { 0x100 } else { -0x100 };
                         self.vel_x = self.rng.range(-0x200..0x200);
                     }
                     Direction::Up | Direction::Bottom => {
-                        self.vel_x = (self.x - player.x).signum() * 0x100;
+                        self.vel_x = if self.x > player.x { 0x100 } else { -0x100 };
                         self.vel_y = self.rng.range(-0x200..0x200);
                     }
                     _ => {}
                 }
 
+                // offset by 1 because we 'increment' late
                 self.counter1 = match self.counter2 {
-                    1 => 256,
-                    2 => 170,
-                    _ => 512,
+                    1 => 170,
+                    2 => 512,
+                    _ => 256,
                 };
+                self.counter2 = 0;
             } else {
                 self.counter1 = 512;
             }
@@ -1288,10 +1288,10 @@ impl Bullet {
             if self.btype == 30 {
                 match self.direction {
                     Direction::Left | Direction::Right => {
-                        self.vel_y = (player.y - self.y).signum() * 0x40;
+                        self.vel_y += if self.y < self.target_y { 0x40 } else { -0x40 };
                     }
                     Direction::Up | Direction::Bottom => {
-                        self.vel_x = (player.x - self.x).signum() * 0x40;
+                        self.vel_x += if self.x < self.target_x { 0x40 } else { -0x40 };
                     }
                     _ => {}
                 }
diff --git a/src/game/weapon/machine_gun.rs b/src/game/weapon/machine_gun.rs
index 8a42c5ec..1109348c 100644
--- a/src/game/weapon/machine_gun.rs
+++ b/src/game/weapon/machine_gun.rs
@@ -30,7 +30,7 @@ impl Weapon {
             return;
         }
 
-        self.counter2 = 0; // recharge time counter
+        // self.counter2 : recharge time counter
         self.counter1 += 1; // autofire counter
 
         if self.counter1 > 5 {
diff --git a/src/game/weapon/missile_launcher.rs b/src/game/weapon/missile_launcher.rs
index d896498c..cf44e806 100644
--- a/src/game/weapon/missile_launcher.rs
+++ b/src/game/weapon/missile_launcher.rs
@@ -44,49 +44,42 @@ impl Weapon {
             return;
         }
 
-        fn increment(weapon: &mut Weapon, bullet: &mut Bullet) {
-            weapon.counter2 = (weapon.counter2 + 1) % 3;
-            bullet.counter2 = weapon.counter2;
-        }
-
         match player.direction {
             Direction::Left if player.up => {
                 let mut bullet =
                     Bullet::new(player.x - 0x200, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
-                increment(self, &mut bullet);
-
+                
                 bullet_manager.push_bullet(bullet.clone());
 
                 state.create_caret(player.x - 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
 
                 if self.level == WeaponLevel::Level3 {
-                    bullet.x = player.x - 0x600;
+                    bullet.x = player.x + 0x600;
                     bullet.y = player.y;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 1;
                     bullet_manager.push_bullet(bullet.clone());
 
-                    bullet.x = player.x + 0x600;
-                    increment(self, &mut bullet);
+                    bullet.x = player.x - 0x600;
+                    bullet.counter2 = 2;
                     bullet_manager.push_bullet(bullet);
                 }
             }
             Direction::Right if player.up => {
                 let mut bullet =
                     Bullet::new(player.x + 0x200, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
-                increment(self, &mut bullet);
 
                 bullet_manager.push_bullet(bullet.clone());
 
                 state.create_caret(player.x + 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
 
                 if self.level == WeaponLevel::Level3 {
-                    bullet.x = player.x - 0x600;
+                    bullet.x = player.x + 0x600;
                     bullet.y = player.y;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 1;
                     bullet_manager.push_bullet(bullet.clone());
 
-                    bullet.x = player.x + 0x600;
-                    increment(self, &mut bullet);
+                    bullet.x = player.x - 0x600;
+                    bullet.counter2 = 2;
                     bullet_manager.push_bullet(bullet);
                 }
             }
@@ -99,20 +92,19 @@ impl Weapon {
                     Direction::Bottom,
                     &state.constants,
                 );
-                increment(self, &mut bullet);
 
                 bullet_manager.push_bullet(bullet.clone());
 
                 state.create_caret(player.x - 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
 
                 if self.level == WeaponLevel::Level3 {
-                    bullet.x = player.x - 0x600;
+                    bullet.x = player.x + 0x600;
                     bullet.y = player.y;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 1;
                     bullet_manager.push_bullet(bullet.clone());
 
-                    bullet.x = player.x + 0x600;
-                    increment(self, &mut bullet);
+                    bullet.x = player.x - 0x600;
+                    bullet.counter2 = 2;
                     bullet_manager.push_bullet(bullet);
                 }
             }
@@ -125,7 +117,6 @@ impl Weapon {
                     Direction::Bottom,
                     &state.constants,
                 );
-                increment(self, &mut bullet);
 
                 bullet_manager.push_bullet(bullet.clone());
 
@@ -134,53 +125,53 @@ impl Weapon {
                 if self.level == WeaponLevel::Level3 {
                     bullet.x = player.x - 0x600;
                     bullet.y = player.y;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 1;
                     bullet_manager.push_bullet(bullet.clone());
 
                     bullet.x = player.x + 0x600;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 2;
                     bullet_manager.push_bullet(bullet);
                 }
             }
             Direction::Left => {
+                let yoffset = (self.level == WeaponLevel::Level3) as i32 * 0x200;
                 let mut bullet =
-                    Bullet::new(player.x - 0xc00, player.y, btype, player_id, Direction::Left, &state.constants);
-                increment(self, &mut bullet);
+                    Bullet::new(player.x - 0xc00, player.y + yoffset, btype, player_id, Direction::Left, &state.constants);
 
                 bullet_manager.push_bullet(bullet.clone());
 
-                state.create_caret(player.x - 0x1800, player.y, CaretType::Shoot, Direction::Left);
+                state.create_caret(player.x - 0x1800, player.y + yoffset, CaretType::Shoot, Direction::Left);
 
                 if self.level == WeaponLevel::Level3 {
                     bullet.x = player.x;
                     bullet.y = player.y - 0x1000;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 1;
                     bullet_manager.push_bullet(bullet.clone());
 
-                    bullet.x = player.x - 0x800;
+                    bullet.x = player.x + 0x800;
                     bullet.y = player.y - 0x200;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 2;
                     bullet_manager.push_bullet(bullet);
                 }
             }
             Direction::Right => {
+                let yoffset = (self.level == WeaponLevel::Level3) as i32 * 0x200;
                 let mut bullet =
-                    Bullet::new(player.x + 0xc00, player.y, btype, player_id, Direction::Right, &state.constants);
-                increment(self, &mut bullet);
+                    Bullet::new(player.x + 0xc00, player.y + yoffset, btype, player_id, Direction::Right, &state.constants);
 
                 bullet_manager.push_bullet(bullet.clone());
 
-                state.create_caret(player.x + 0x1800, player.y, CaretType::Shoot, Direction::Left);
+                state.create_caret(player.x + 0x1800, player.y + yoffset, CaretType::Shoot, Direction::Left);
 
                 if self.level == WeaponLevel::Level3 {
                     bullet.x = player.x;
                     bullet.y = player.y - 0x1000;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 1;
                     bullet_manager.push_bullet(bullet.clone());
 
-                    bullet.x = player.x + 0x800;
+                    bullet.x = player.x - 0x800;
                     bullet.y = player.y - 0x200;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 2;
                     bullet_manager.push_bullet(bullet);
                 }
             }
diff --git a/src/game/weapon/nemesis.rs b/src/game/weapon/nemesis.rs
index af24b4b7..9555eb91 100644
--- a/src/game/weapon/nemesis.rs
+++ b/src/game/weapon/nemesis.rs
@@ -28,7 +28,7 @@ impl Weapon {
 
         if !self.consume_ammo(1) {
             state.sound_manager.play_sfx(37);
-            // todo spawn "empty" text
+            // The vanilla game doesn't spawn "empty" text for some reason
             return;
         }
 
@@ -36,11 +36,11 @@ impl Weapon {
             match player.direction {
                 Direction::Left => {
                     bullet_manager.create_bullet(player.x - 0x200, player.y - 0x1800, btype, player_id, Direction::Up, &state.constants);
-                    state.create_caret(player.x - 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
+                    state.create_caret(player.x - 0x200, player.y - 0x1800, CaretType::Shoot, Direction::Left);
                 }
                 Direction::Right => {
                     bullet_manager.create_bullet(player.x + 0x200, player.y - 0x1800, btype, player_id, Direction::Up, &state.constants);
-                    state.create_caret(player.x + 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
+                    state.create_caret(player.x + 0x200, player.y - 0x1800, CaretType::Shoot, Direction::Left);
                 }
                 _ => {}
             }
@@ -48,11 +48,11 @@ impl Weapon {
             match player.direction {
                 Direction::Left => {
                     bullet_manager.create_bullet(player.x - 0x200, player.y + 0x1800, btype, player_id, Direction::Bottom, &state.constants);
-                    state.create_caret(player.x - 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
+                    state.create_caret(player.x - 0x200, player.y + 0x1800, CaretType::Shoot, Direction::Left);
                 }
                 Direction::Right => {
                     bullet_manager.create_bullet(player.x + 0x200, player.y + 0x1800, btype, player_id, Direction::Bottom, &state.constants);
-                    state.create_caret(player.x + 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
+                    state.create_caret(player.x + 0x200, player.y + 0x1800, CaretType::Shoot, Direction::Left);
                 }
                 _ => {}
             }
diff --git a/src/game/weapon/super_missile_launcher.rs b/src/game/weapon/super_missile_launcher.rs
index 78705b9c..4d6c3f8f 100644
--- a/src/game/weapon/super_missile_launcher.rs
+++ b/src/game/weapon/super_missile_launcher.rs
@@ -44,49 +44,42 @@ impl Weapon {
             return;
         }
 
-        fn increment(weapon: &mut Weapon, bullet: &mut Bullet) {
-            weapon.counter2 = (weapon.counter2 + 1) % 3;
-            bullet.counter2 = weapon.counter2;
-        }
-
         match player.direction {
             Direction::Left if player.up => {
                 let mut bullet =
                     Bullet::new(player.x - 0x200, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
-                increment(self, &mut bullet);
 
                 bullet_manager.push_bullet(bullet.clone());
 
                 state.create_caret(player.x - 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
 
                 if self.level == WeaponLevel::Level3 {
-                    bullet.x = player.x - 0x600;
+                    bullet.x = player.x + 0x600;
                     bullet.y = player.y;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 1;
                     bullet_manager.push_bullet(bullet.clone());
-
-                    bullet.x = player.x + 0x600;
-                    increment(self, &mut bullet);
+                    
+                    bullet.x = player.x - 0x600;
+                    bullet.counter2 = 2;
                     bullet_manager.push_bullet(bullet);
                 }
             }
             Direction::Right if player.up => {
                 let mut bullet =
                     Bullet::new(player.x + 0x200, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
-                increment(self, &mut bullet);
 
                 bullet_manager.push_bullet(bullet.clone());
 
                 state.create_caret(player.x + 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
 
                 if self.level == WeaponLevel::Level3 {
-                    bullet.x = player.x - 0x600;
+                    bullet.x = player.x + 0x600;
                     bullet.y = player.y;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 1;
                     bullet_manager.push_bullet(bullet.clone());
 
-                    bullet.x = player.x + 0x600;
-                    increment(self, &mut bullet);
+                    bullet.x = player.x - 0x600;
+                    bullet.counter2 = 2;
                     bullet_manager.push_bullet(bullet);
                 }
             }
@@ -99,20 +92,19 @@ impl Weapon {
                     Direction::Bottom,
                     &state.constants,
                 );
-                increment(self, &mut bullet);
 
                 bullet_manager.push_bullet(bullet.clone());
 
                 state.create_caret(player.x - 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
 
                 if self.level == WeaponLevel::Level3 {
-                    bullet.x = player.x - 0x600;
+                    bullet.x = player.x + 0x600;
                     bullet.y = player.y;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 1;
                     bullet_manager.push_bullet(bullet.clone());
 
-                    bullet.x = player.x + 0x600;
-                    increment(self, &mut bullet);
+                    bullet.x = player.x - 0x600;
+                    bullet.counter2 = 2;
                     bullet_manager.push_bullet(bullet);
                 }
             }
@@ -125,7 +117,6 @@ impl Weapon {
                     Direction::Bottom,
                     &state.constants,
                 );
-                increment(self, &mut bullet);
 
                 bullet_manager.push_bullet(bullet.clone());
 
@@ -134,53 +125,53 @@ impl Weapon {
                 if self.level == WeaponLevel::Level3 {
                     bullet.x = player.x - 0x600;
                     bullet.y = player.y;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 1;
                     bullet_manager.push_bullet(bullet.clone());
 
                     bullet.x = player.x + 0x600;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 2;
                     bullet_manager.push_bullet(bullet);
                 }
             }
             Direction::Left => {
+                let yoffset = (self.level == WeaponLevel::Level3) as i32 * 0x200;
                 let mut bullet =
-                    Bullet::new(player.x - 0xc00, player.y, btype, player_id, Direction::Left, &state.constants);
-                increment(self, &mut bullet);
+                    Bullet::new(player.x - 0xc00, player.y + yoffset, btype, player_id, Direction::Left, &state.constants);
 
                 bullet_manager.push_bullet(bullet.clone());
 
-                state.create_caret(player.x - 0x1800, player.y, CaretType::Shoot, Direction::Left);
+                state.create_caret(player.x - 0x1800, player.y + yoffset, CaretType::Shoot, Direction::Left);
 
                 if self.level == WeaponLevel::Level3 {
                     bullet.x = player.x;
                     bullet.y = player.y - 0x1000;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 1;
                     bullet_manager.push_bullet(bullet.clone());
 
-                    bullet.x = player.x - 0x800;
+                    bullet.x = player.x + 0x800;
                     bullet.y = player.y - 0x200;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 2;
                     bullet_manager.push_bullet(bullet);
                 }
             }
             Direction::Right => {
+                let yoffset = (self.level == WeaponLevel::Level3) as i32 * 0x200;
                 let mut bullet =
-                    Bullet::new(player.x + 0xc00, player.y, btype, player_id, Direction::Right, &state.constants);
-                increment(self, &mut bullet);
+                    Bullet::new(player.x + 0xc00, player.y + yoffset, btype, player_id, Direction::Right, &state.constants);
 
                 bullet_manager.push_bullet(bullet.clone());
 
-                state.create_caret(player.x + 0x1800, player.y, CaretType::Shoot, Direction::Left);
+                state.create_caret(player.x + 0x1800, player.y + yoffset, CaretType::Shoot, Direction::Left);
 
                 if self.level == WeaponLevel::Level3 {
                     bullet.x = player.x;
                     bullet.y = player.y - 0x1000;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 1;
                     bullet_manager.push_bullet(bullet.clone());
 
-                    bullet.x = player.x + 0x800;
+                    bullet.x = player.x - 0x800;
                     bullet.y = player.y - 0x200;
-                    increment(self, &mut bullet);
+                    bullet.counter2 = 2;
                     bullet_manager.push_bullet(bullet);
                 }
             }


Test patch: