From 16c92c25d8e037fe09ca1b3939b7495586ef96b5 Mon Sep 17 00:00:00 2001 From: PeachyPeach <72323920+PeachyPeachSM64@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:28:01 +0100 Subject: [PATCH] Fix wall double bonk glitch (#1113) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix wall double bonk glitch [03/02/2026 12:10] colbyrayz.z64 i might have found an insane consistent way getting this somehow {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468202050981593244/2026-02-03_03-08-24.mp4?ex=6983d1f6&is=69828076&hm=6d9e564d8b422cdb7e53097f910ff122bff97c12d661222b929085b9b6b206cf& [03/02/2026 12:12] blocky.cmd what. [03/02/2026 12:14] colbyrayz.z64 its semi consistent [03/02/2026 12:14] colbyrayz.z64 it happens everytime [03/02/2026 12:14] colbyrayz.z64 has multiple causes it seems but this is one of them [03/02/2026 19:08] peachypeachsm64 thanks (unrelated, but you need to exclude `MARIO_ANIM_PART_` when scanning for animation names in your debug display) {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468307238165676305/image.png?ex=69838b2d&is=698239ad&hm=084431682e34741dbc1a1b919a38e6cd37c9592bdeec9b1424410e6d9c122334& {Reactions} πŸ‘€ πŸ‘ [03/02/2026 19:15] altiami I'm noticing the angles are perfectly aligned with the axis. It looks LuigiGamer's and my theory about reflection being buggy and about problematic angles could be relevant [03/02/2026 19:40] wbmarioo When did double wall jumps start happening [03/02/2026 19:40] wbmarioo I’ve only just seen people talking about it since yesterday [03/02/2026 19:47] theincredibleholc Many ages ago. Perhaps Millennia... Eons, even. [03/02/2026 20:34] blocky.cmd ever since coop was made [03/02/2026 20:34] blocky.cmd i'm pretty sure [03/02/2026 20:49] theincredibleholc Makes me wonder if it's in Sm64EX [03/02/2026 20:50] theincredibleholc -# (not EX-Coop) [03/02/2026 20:51] maniscat2 makes me wonder if its in sm64 [03/02/2026 21:05] birdekek makes me wonder if its in smb [03/02/2026 21:20] altiami soup [03/02/2026 21:22] theincredibleholc {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468340870515261441/hXy2f1U.gif?ex=6983aa7f&is=698258ff&hm=a0e0625d6b27ea42ea2b4db9eb970bb93871862506752582ff16c48c316107b3& [03/02/2026 21:24] emeraldlockdown Oh, I did like a bunch of research into this bug and completely forgot to send it here. Seeing how this has evolved, I'll give a summarized copy that isn't me ranting about other things for 50 paragraphs Double bonks, if I recall correctly, have been in the game ever since romhacks were added. I don't recall them existing beforehand, but perhaps I'm wrong. A commit that lines up with this memory, and changes many things related to wallkicks, is [this commit](https://github.com/coop-deluxe/sm64coopdx/commit/3e46cc11619c0f8ee80e8876c73150521e771e11). The tree for that commit so you can look around other commits is [here](https://github.com/coop-deluxe/sm64coopdx/commits/main/?before=6092488d1c4fc741b16a0789ef9c08ec0279333f+2651). This commit changes how wall normals work, so perhaps it's worth taking a look at. While I would love to look at it, I'm not really that good at math, so I couldn't help all that much, although I'll try to skim through it to find anything interesting. {Reactions} πŸ‘€ (2) [03/02/2026 21:46] altiami something I'm noticing is that in `perform_air_quarter_step`, there's an iterator over walls that uses the wall collision data instead of the wall assigned to the mario state, but only if the fix collision bugs flag is set, which isn't by default [03/02/2026 21:46] altiami otherwise it uses the last wall, which should be the same as the wall assigned to the mario state in the new `mario_update_wall` function [03/02/2026 21:49] altiami the only way that flag can be set is through mods or with a special djui menu that's only there if the game is compiled with `DEVELOPMENT` defined [03/02/2026 21:58] altiami wait, I just realized in this video that Mario immediately turns around when hitting the wall, before the wall kick happens (shown by angle). That's not supposed to happen right? [03/02/2026 22:19] theincredibleholc Not sure. I did another video at 240 fps and used frame advance to look frame-by-frame. {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468355148718932241/2026-02-03_15-17-04.mp4?ex=6983b7cc&is=6982664c&hm=1b2ff76684239890abe996631612eb05a8bc5649875b7784bb13ce692a7f8aa6& [03/02/2026 22:21] altiami are you able to add more info to that display on the side? [03/02/2026 22:21] theincredibleholc Sure, what do you want? [03/02/2026 22:22] altiami let me make sure I'm gonna say the right thing [03/02/2026 22:22] mysterd Whoa I have no memory writing that lol. That may have been done before fixCollision stuff was a toggle, if so all of that should probably only happen if fixCollisions is enabled and vanilla behavior should be preserved otherwise. [03/02/2026 22:23] altiami I can see fixCollisionBugs in the level values struct [03/02/2026 22:23] mysterd oh yeah it is there [03/02/2026 22:23] altiami and there are some if conditions for it [03/02/2026 22:23] mysterd hmm [03/02/2026 22:23] altiami either way [03/02/2026 22:23] altiami holc, your frogger level doesn't use that value right? [03/02/2026 22:23] altiami and surely the sm64 levels don't [03/02/2026 22:23] theincredibleholc I use fixColBugs in Frogger. [03/02/2026 22:24] theincredibleholc But this recording is vanilla Sm64 with just a readout. No other mods. [03/02/2026 22:24] altiami okay, two things I cna say to add [03/02/2026 22:24] altiami first, add the flag for fix collision bugs [03/02/2026 22:24] altiami it'll be a sanity check [03/02/2026 22:25] altiami second, from the `MarioState`, add `wall`, and its `normal` values [03/02/2026 22:25] blocky.cmd you do NOT need this many frames πŸ™ 240 is insane [03/02/2026 22:25] altiami and also add, from the `MarioState`, `wallNormal`, which is a vec3f [03/02/2026 22:26] altiami the idea here is to see if there are any desyncs (redundancy desyncs, not network) [03/02/2026 22:27] altiami I think that's everything I can see being possibly relevant in that commit [03/02/2026 22:28] altiami also, that clip shows an angle that isn't on a 45 degree multiple, so my theory is out [03/02/2026 22:29] blocky.cmd i want to mention because it might be important [03/02/2026 22:29] blocky.cmd but if you get hit by an enemy (i.e a goomba) the bug gets fixed [03/02/2026 22:29] blocky.cmd and its a common fix to ask a player to punch you to fix the double bonk [03/02/2026 22:30] altiami tf [03/02/2026 22:30] altiami that feels like it'd be entirely unrelated. I can't tell if I hope that's a placebo effect or not [03/02/2026 22:31] blocky.cmd it would be an awful coincidence [03/02/2026 22:31] altiami on the one hand it'd be absurd if it was. On the other, it'd provide extra context that'd help in tracking it down [03/02/2026 22:32] altiami in my experience though it tends to just happen without any consistency [03/02/2026 22:34] theincredibleholc I think it has something to do with warping/resetting {Reactions} βœ… [03/02/2026 22:34] blocky.cmd i wouldnt be surprised if any of the knockback actions did soemthing [03/02/2026 22:35] theincredibleholc So, as @ColbyRayz! pointed it you can get it to happen consistently by doing exactly what he did. Enter BOB and reset the server, then you can double bonk in CASTLE_GROUNDS. What I noticed is that after re-entering the castle, then going back to castle grounds, I can't double bonk. [03/02/2026 22:35] altiami https://tenor.com/bvZwe.gif [03/02/2026 22:36] theincredibleholc {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468359516793082072/2026-02-03_15-36-01.mp4?ex=6983bbdd&is=69826a5d&hm=e15aeb9d18419cf29c67d5034ebf9deb56ed820ce5b99fe5dd5fa222718e2bd3& [03/02/2026 22:37] altiami and fix collision bugs is indeed 0 [03/02/2026 22:37] theincredibleholc I turned it on for some tests too, and it made no difference. [03/02/2026 22:38] theincredibleholc proof {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468360133435461806/2026-02-03_15-38-29.mp4?ex=6983bc70&is=69826af0&hm=3df29070d9707fafd82d4a620a08ac88227be7e7168b33eb9f4e4fcedd1a768a& [03/02/2026 22:40] altiami I'm especially interested in the `MarioState.wallNormal` field now, because that's used in `mario_bonk_reflection` {Reactions} πŸ‘€ [03/02/2026 22:40] altiami and that's a field that was newly added in that commit [03/02/2026 22:42] altiami so the fields of interest are `MarioState.wall` (pointer) `MarioState.wall->normal.x`, `.y`, and `.z` `MarioState.wallNormal[0]` to `[2]` [03/02/2026 22:43] altiami actually, maybe not the wall pointer yet [03/02/2026 22:43] altiami nothing to compare it to [03/02/2026 22:43] altiami unless we want to manually do a wall collision detection so we get that data lol [03/02/2026 22:43] theincredibleholc I'm not going to pretend I know enough to contribute more to this conversation, but you need me to test anything let me know! [03/02/2026 22:43] theincredibleholc https://tenor.com/view/ralph-wiggum-simpsons-eating-glue-gif-20921387 [03/02/2026 22:45] theincredibleholc I'm wondering what specifically would change when Mario enters BOB (or maybe his falling level-entry Action) that would "store" the bug. [03/02/2026 22:45] altiami if you can show those two different normals in that sidebar with this test, that'd be great, because if they're not the same, then we have a big problem [03/02/2026 22:45] theincredibleholc **m.wall.normal** and **m.wallNormal** ? [03/02/2026 22:45] blocky.cmd .y [03/02/2026 22:46] altiami yeah, and keep in mind that `m.wall.normal` uses .x, .y, and .z, while `wallNormal` is an array [03/02/2026 22:46] altiami woah wtf [03/02/2026 22:46] altiami hold on [03/02/2026 22:46] altiami okay [03/02/2026 22:46] blocky.cmd i thought arrays automatically were converted [03/02/2026 22:46] altiami are they? [03/02/2026 22:46] blocky.cmd i think so yeah [03/02/2026 22:46] altiami mmk [03/02/2026 22:47] blocky.cmd i may be high af rn tho [03/02/2026 22:47] altiami can we get much higher [03/02/2026 22:47] blocky.cmd i wish [03/02/2026 22:59] theincredibleholc {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468365251509489807/2026-02-03_15-58-34.mp4?ex=6983c134&is=69826fb4&hm=4385e7d104f6de68962aaeb866c3f7eef2e4caf7201b5bbcabe065cdf34e503c& [03/02/2026 23:00] altiami ok, and then `wallNormal` [03/02/2026 23:01] altiami interestingly though we can see that the normal does not change after hitting the wall, despite being turned around [03/02/2026 23:02] altiami so you're still technically wall kicking off of that wall [03/02/2026 23:08] theincredibleholc {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468367655273500753/image.png?ex=6983c371&is=698271f1&hm=3092c87bf5044791c8da9d6959f28f3acd6cab7fd35f25f70d3161becfda781e& [03/02/2026 23:08] theincredibleholc lol [03/02/2026 23:08] altiami that would mean only the bonk would be relevant, and not the kick, I think [03/02/2026 23:09] altiami hrm [03/02/2026 23:09] xluigigamerx wall.normal [03/02/2026 23:09] xluigigamerx .x/y/z [03/02/2026 23:09] altiami no, there should be a `wallNormal` property too [03/02/2026 23:09] altiami unless it was removed [03/02/2026 23:09] xluigigamerx There isn't, there never was [03/02/2026 23:09] theincredibleholc It autofills for me, but that might be for something else. [03/02/2026 23:09] xluigigamerx Normals are vec3fs [03/02/2026 23:10] altiami https://github.com/coop-deluxe/sm64coopdx/blob/3e46cc11619c0f8ee80e8876c73150521e771e11/include/types.h#L390 [03/02/2026 23:10] altiami and yeah it's still there [03/02/2026 23:11] xluigigamerx Well is it exposed [03/02/2026 23:11] altiami that's a good question [03/02/2026 23:11] altiami though I'm also seeing that error says "Surface" [03/02/2026 23:11] xluigigamerx It's a vec3f [03/02/2026 23:11] peachypeachsm64 yes it is m.wallNormal [03/02/2026 23:11] altiami you're trying to get the `wallNormal` of the `MarioState` right [03/02/2026 23:12] xluigigamerx Are you indexing something other than .x/y/z [03/02/2026 23:12] xluigigamerx Vec3fs are converted to .x/y/z in lua altiami [03/02/2026 23:12] altiami blocky mentioned that already [03/02/2026 23:12] theincredibleholc guys [03/02/2026 23:12] theincredibleholc I did m.wall.wallNormal [03/02/2026 23:12] theincredibleholc :> [03/02/2026 23:12] altiami OOPS [03/02/2026 23:12] xluigigamerx Wallnormal is a vec3f [03/02/2026 23:12] theincredibleholc Thanks peachy [03/02/2026 23:13] xluigigamerx GET OU- [03/02/2026 23:13] theincredibleholc https://tenor.com/view/wojak-loading-dumb-soyjak-brainlet-gif-5905960021355918533 [03/02/2026 23:13] blocky.cmd add y [03/02/2026 23:13] blocky.cmd add .y [03/02/2026 23:13] altiami yes luigi I know, I'm looking right at the source code. I don't need it repeated to me three times [03/02/2026 23:14] xluigigamerx Ok, but may I interest you in... vec3fs [03/02/2026 23:14] xluigigamerx They are very peak, you can store position and get it without confusion [03/02/2026 23:14] blocky.cmd ok im home i might try myself at this too [03/02/2026 23:14] altiami {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468369124223881297/reimuAAAAAAAAAAAAAAAAA.webp?ex=6983c4d0&is=69827350&hm=33117149457125ac19973c8b284af34edc719f27fbf9ab54cd3abd55ec5097ee& [03/02/2026 23:15] altiami anyway yes, blocky's right, since it's a vec3f it'll have the .x .y and .z properties [03/02/2026 23:15] altiami and luigi [03/02/2026 23:15] xluigigamerx Hello world! [03/02/2026 23:16] altiami hello world too bland, gotta make it with "from the earth I rise" [03/02/2026 23:16] xluigigamerx But on which axis [03/02/2026 23:16] xluigigamerx Are you a y up or a z up type of person [03/02/2026 23:16] altiami I forget, do the axes change depending on which type of space you're using in SM64, or is that only relevant to rotation order [03/02/2026 23:17] xluigigamerx Speaking of Ys and Zs, may I interest you in some vec3f action [03/02/2026 23:17] xluigigamerx You can reach infinite distance! [03/02/2026 23:17] altiami hello I am in the NaN realm please help me [03/02/2026 23:18] wall_e20 oh you too? [03/02/2026 23:18] xluigigamerx The f in vec3f stands for a float, which becomes infinite when reaching the max float depending on the compiler I think [03/02/2026 23:18] blocky.cmd ok i checked [03/02/2026 23:18] altiami actually do we have any safeguards against that for mario's position since people love their BLJs [03/02/2026 23:18] blocky.cmd both match [03/02/2026 23:18] xluigigamerx Why [03/02/2026 23:18] xluigigamerx PUs are cinema [03/02/2026 23:18] altiami at least in the original sm64, it'd crash if you got to infinity [03/02/2026 23:19] xluigigamerx Well, I did go to infinity, the level just turns black, I accidentally entered the void [03/02/2026 23:19] theincredibleholc I have to step away for a bit and grind out some work for my actual job. Ping me if I can be of assistance and I'll try to check on it. [03/02/2026 23:19] altiami ok if it doesn't crash then I guess it's fine [03/02/2026 23:19] xluigigamerx You can still exit [03/02/2026 23:19] blocky.cmd {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468370376210776310/Super_Mario_64_Coop_Deluxe_v1.4.1_2026-02-03_23-18-28.mp4?ex=6983c5fa&is=6982747a&hm=20de6bb2a795d32522389421c20d59e3260f49c3741613d00053f8d6bcc0b315& [03/02/2026 23:19] blocky.cmd {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468370462722490541/image.png?ex=6983c60f&is=6982748f&hm=15efd49675b065a6135c7f4617d35188bae96c6f16a2920a43a7163ff0efaa95& [03/02/2026 23:20] altiami what about X and Z [03/02/2026 23:20] xluigigamerx How inaccurate is Mario's position in the farther PUs, since floats lose accuracy as the get bigger [03/02/2026 23:20] theincredibleholc I already posted those [03/02/2026 23:20] theincredibleholc {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468370615865053409/image.png?ex=6983c633&is=698274b3&hm=3162419073f9706a70903260f3891556e9ddeff1f654121853d087d7196f5ec3& [03/02/2026 23:20] xluigigamerx 0 when wall, 1 when floor, -1 when ceiling bruh [03/02/2026 23:21] altiami that's just for `wall.normal`. We still need to make sure `wallNormal` matches fully [03/02/2026 23:21] xluigigamerx Well check the source code and see where each is being set [03/02/2026 23:22] altiami the suspect parts are in `mario_bonk_reflection` and `perform_air_quarter_step` [03/02/2026 23:23] xluigigamerx My theory is that mario bonk reflection just runs twice for whatever reason [03/02/2026 23:24] blocky.cmd {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468371530671984771/Super_Mario_64_Coop_Deluxe_v1.4.1_2026-02-03_23-24-02.mp4?ex=6983c70d&is=6982758d&hm=53a3e8fdd1c652e245271962fce1e101889bbe5d6736606771c3c254505f2140& [03/02/2026 23:24] xluigigamerx But it could be false, because I know that diving into the wall causes the bug to occur as well [03/02/2026 23:24] blocky.cmd okay if you want my opinion it looks like wall interactions are doubled in general [03/02/2026 23:24] blocky.cmd you can see when i dive into the wall i get like twice as much negative speed i should have [03/02/2026 23:24] xluigigamerx Yeah [03/02/2026 23:24] blocky.cmd i don't know if bonk actions hard set your velocity but i assume it does [03/02/2026 23:24] peachypeachsm64 funny thing happened I put a breakpoint on mario_bonk_reflection before entering bob, bonking into a wall breaks once, in `common_air_action_step` after doing bob + server reset, bonking breaks twice, in `common_air_action_step` AND `common_air_knockback_step` after entering a level again, it "fixes" itself and only breaks once [03/02/2026 23:24] xluigigamerx I can confirm this happens [03/02/2026 23:25] altiami okay, so they do match [03/02/2026 23:25] altiami and peachy pretty much confirmed it runs twice on top of the empirical evidence of the increased speed [03/02/2026 23:26] altiami I noticed that increased speed before but I wasn't sure what to make of it [03/02/2026 23:26] altiami so now comes the part where we answer why it runs twice [03/02/2026 23:26] xluigigamerx I think whatever function is setting the wall kb speed is the one at fault [03/02/2026 23:27] altiami it's likely up the call stack, yeah [03/02/2026 23:27] xluigigamerx Which one sets the velocity after air hit wall [03/02/2026 23:27] xluigigamerx Check instances of air hit wall steps [03/02/2026 23:28] altiami `mario_bonk_reflection`can set the forward velocity but only if the `negateSpeed` argument is set [03/02/2026 23:28] altiami ...wait [03/02/2026 23:29] altiami what? [03/02/2026 23:29] altiami {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468372741777395945/image.png?ex=6983c82e&is=698276ae&hm=deca9bc76b84ac422764fd8bdd19feaddfcd983a5002a8672d480578c43bbb00& [03/02/2026 23:29] altiami is there ever a case where mario bonks and *does* turn around? [03/02/2026 23:29] blocky.cmd @Altiami {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468372840167374848/Super_Mario_64_Coop_Deluxe_v1.4.1_2026-02-03_23-28-42_online-video-cutter.com.mp4?ex=6983c846&is=698276c6&hm=8c2212526b95bc4142864cc00a7ded77896bdb65768bb5fa0adc8a7989cbbd1a& [03/02/2026 23:29] blocky.cmd i wasn't crazy [03/02/2026 23:29] emeraldlockdown This was also apart of my investigation. Since we're getting into act air hit wall, there's a quirk with this function. I logically thought through it, and I don't think it should be an issue, but it's something to note `act_air_hit_wall` has an interesting quirk, it doesn’t actually return a value. I’ll let this code comment explain: ```c //! Missing return statement. The returned value is the result of the call // to set_character_animation (set_mario_animation in original decomp). // In practice, this value is nonzero. // This results in this action "cancelling" into itself. It is supposed to // execute on two frames, but instead it executes twice on the same frame. // This results in firsties only being possible for a single frame, instead // of two. ``` [03/02/2026 23:30] blocky.cmd i think it has to do with how mario is initialized at the beggining of a level [03/02/2026 23:30] blocky.cmd one of the knockback actions puts him back in a good state it seems [03/02/2026 23:30] peachypeachsm64 . [03/02/2026 23:31] altiami dive bonk turns mario around? [03/02/2026 23:31] altiami period [03/02/2026 23:31] blocky.cmd πŸ’… [03/02/2026 23:32] peachypeachsm64 well no actually he does turn around but there is another += 0x8000 after that so it does nothing lol [03/02/2026 23:32] altiami tf [03/02/2026 23:32] altiami mario 64 glue [03/02/2026 23:32] peachypeachsm64 {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468373577928540325/image.png?ex=6983c8f6&is=69827776&hm=507f3aec5f79c19b9ede42a8da95d2acdb7970c79ee8c21b8e86341bc816bcd3& [03/02/2026 23:32] emeraldlockdown It's so hard to analyze this code because it's literally running off of hopes and dreams [03/02/2026 23:32] altiami https://tenor.com/bukEv.gif [03/02/2026 23:32] blocky.cmd ..hopes and dreams you say? [03/02/2026 23:32] altiami okay so [03/02/2026 23:33] cooliokid956 hi {Reactions} πŸ”₯ [03/02/2026 23:33] altiami we know that mario is being turned around for the wall hit action [03/02/2026 23:33] altiami ...by what? [03/02/2026 23:33] xluigigamerx I was literally at that line lmao [03/02/2026 23:33] blocky.cmd it might just be a side effect of the walls interaction being doubled in general [03/02/2026 23:33] xluigigamerx Test it out, turn on bouncy level bounds [03/02/2026 23:33] blocky.cmd so he turns around twice [03/02/2026 23:33] altiami he's not supposed to turn around though [03/02/2026 23:33] peachypeachsm64 omg it does return a value BUT it returns the value of `set_character_animation(m, CHAR_ANIM_START_WALLKICK)` WHICH CAN BE 0 [03/02/2026 23:34] peachypeachsm64 why would you use the animation current frame as a way to cancel out an action wtf [03/02/2026 23:35] altiami and even if he bonked the same wall twice, that would mean he reflects twice off the same normal, so he'd end up being in the initial direction again [03/02/2026 23:35] xluigigamerx Firsties? [03/02/2026 23:35] emeraldlockdown Why would it sometimes be 0 though? That's sort of where my lead dried up, because this happens seemingly randomly, or due to an init issue. It just returns the animFrame, so why would that change in the first place? [03/02/2026 23:35] altiami it'd be 0 if it fails right? [03/02/2026 23:35] xluigigamerx AnimFrame starts at 0 [03/02/2026 23:36] peachypeachsm64 is that how firsties work? no way [03/02/2026 23:36] xluigigamerx No [03/02/2026 23:36] emeraldlockdown Perhaps I need to rephrase it, the issue is why would the animation change when no mods are being ran? Why would how it set change? Maybe we should try returning 1 on the function and try to recreate the issue to see if it's the problem [03/02/2026 23:37] emeraldlockdown What inputs are done to recreate the issue? It looks like you access the pause menu in the level selector [03/02/2026 23:37] altiami doesn't it change the animation when you bonk though [03/02/2026 23:37] xluigigamerx The action change resets the animation to frame 0 [03/02/2026 23:37] blocky.cmd firsties are immune to double bonks [03/02/2026 23:37] emeraldlockdown That's good to know [03/02/2026 23:38] xluigigamerx I know [03/02/2026 23:38] xluigigamerx It makes sense because firsties are firsties [03/02/2026 23:38] blocky.cmd ah yes [03/02/2026 23:38] xluigigamerx They're done on exactly the same frame as hitting the wall [03/02/2026 23:38] blocky.cmd i'm looking into why getting hit would reset the bug rn [03/02/2026 23:38] xluigigamerx Otherwise it's not a firstie [03/02/2026 23:38] blocky.cmd maybe we could trace it backwards 🀷 [03/02/2026 23:39] altiami so we know it only happens if mario reaches the knnockback action [03/02/2026 23:39] altiami so, is something in the knockback action making mario turn around when it shouldn't? [03/02/2026 23:39] emeraldlockdown If I understand it correctly, it only happens if the second if statement is ran in `act_air_hit_wall`, which, sets the action to `ACT_BACKWARD_AIR_KB` [03/02/2026 23:40] peachypeachsm64 and it happens when you enter bob and restart the server (???) [03/02/2026 23:40] altiami it has to be related to something stale [03/02/2026 23:40] altiami or maybe not being initialized correctly in the first place? [03/02/2026 23:40] emeraldlockdown It can still be something in `act_air_hit_wall`, since the double turn around happens when you hit the wall [03/02/2026 23:41] altiami peachy, what does the call stack look like for each break when it doubles up? [03/02/2026 23:42] altiami it's just occurred to me we sound like an investigation firm trying to track down a serial killer [03/02/2026 23:42] altiami not entirely incorrect; I'm sure this bug has killed many players [03/02/2026 23:42] blocky.cmd only if leaving right after the act select [03/02/2026 23:43] blocky.cmd doesn't work on levels without the act select [03/02/2026 23:43] peachypeachsm64 perform_air_step runs only once per frame even when the double bonk occurs, I can't explain the double speed (yet) [03/02/2026 23:44] altiami try looking at the call stack when the breaks happen. That should let us track down the execution flow [03/02/2026 23:44] emeraldlockdown The weird thing to me is why does it only happen in `ACT_BACKWARD_AIR_KB`, and not `ACT_SOFT_BONK`? Both of these actions call `common_air_knockback_step`, and both call `check_wall_kick`. I'm going to be very interested in what the issue ends up actually being [03/02/2026 23:45] xluigigamerx Soft bonk doesn't have the star particles [03/02/2026 23:45] xluigigamerx Which seems to be a shared trait with all affected wall actions [03/02/2026 23:45] xluigigamerx Impact particles [03/02/2026 23:47] xluigigamerx It must be whatever triggers the particles [03/02/2026 23:48] peachypeachsm64 nothing worth 1st is during long jump 2nd is during air knockback action (when double bonk happens only) {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468377650996183295/image.png?ex=6983ccc1&is=69827b41&hm=95e9bbcafdb75a76a05f52692fc0fe68081f04bf27e54cd212fc9e9587f6fb78& https://cdn.discordapp.com/attachments/1264640346957021195/1468377651419943084/image.png?ex=6983ccc1&is=69827b41&hm=33a2207d5c14b0fe4068427156f1c7f410fd9451b0a389e1cfbcd08f928807de& [03/02/2026 23:49] emeraldlockdown I remember cross checking the 2nd line with sm64ex, incase anyone is curious, I believe they are the exact same [03/02/2026 23:49] xluigigamerx What about decomp [03/02/2026 23:49] emeraldlockdown I have not, I can check right now though [03/02/2026 23:52] emeraldlockdown Yea they're the same, so it's something outside of it [03/02/2026 23:52] xluigigamerx Can you compare the airborne files entirely [03/02/2026 23:52] emeraldlockdown Sure why not [03/02/2026 23:53] altiami I just zeroed in on that part of the code myself for the knockback action [03/02/2026 23:54] peachypeachsm64 after further investigation there is no double step Mario just retains his speed, but it gets reverted (dive speed 48 becomes -48 for example) [03/02/2026 23:55] emeraldlockdown Here's the diff for the decomp vs coop mario_actions_airborne.c file. I'll scan through this as well, but you can look aswell if you'd like {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468379324104315132/diff.patch?ex=6983ce50&is=69827cd0&hm=b7403326958873d63f17458cbdb9fb8289f3203c0260dec05076c444db97f4bd& [04/02/2026 00:00] blocky.cmd something in take_damage_from_interact_object fixes the bug [04/02/2026 00:00] altiami wait [04/02/2026 00:00] altiami would that involve the knockbackTimer [04/02/2026 00:00] blocky.cmd ```lua function take_damage_from_interact_object(m) if (not m or not m.interactObj) then return 0 end local shake local damage = m.interactObj.oDamageOrCoinValue if (damage >= 4) then shake = SHAKE_LARGE_DAMAGE elseif (damage >= 2) then shake = SHAKE_MED_DAMAGE else shake = SHAKE_SMALL_DAMAGE end if ((m.flags & MARIO_CAP_ON_HEAD) == 0) then damage = damage + (damage + 1) / 2 end if (m.flags & MARIO_METAL_CAP) ~= 0 then damage = 0 end -- disable player-to-player damage if the server says so if (m.interactObj and (m.interactObj.oInteractType & INTERACT_PLAYER) ~= 0 ) then if (gServerSettings.playerInteractions ~= PLAYER_INTERACTIONS_PVP) then damage = 0 end end m.hurtCounter = m.hurtCounter + 4 * damage queue_rumble_data_mario(m, 5, 80) if (m.playerIndex == 0) then set_camera_shake_from_hit(shake) end return damage end local function mario_update(m) if m.playerIndex ~= 0 then return end if m.controller.buttonPressed & X_BUTTON ~= 0 then local test = obj_get_first_with_behavior_id(id_bhvDoorWarp) test.oDamageOrCoinValue = 1 m.interactObj = test take_damage_from_interact_object(m) end end hook_event(HOOK_MARIO_UPDATE, mario_update)``` [04/02/2026 00:00] blocky.cmd i made this and the bug was consistently fixed [04/02/2026 00:01] blocky.cmd had to translate the function since it wasn't exposed [04/02/2026 00:02] altiami there's that interact player thing again... [04/02/2026 00:02] blocky.cmd i thought so too but i couldn't find anything [04/02/2026 00:02] blocky.cmd relevant [04/02/2026 00:02] blocky.cmd i'll try to disect this function further if notyhing is found [04/02/2026 00:04] emeraldlockdown Does it work if you don't set the hurt counter? [04/02/2026 00:06] altiami that would be CRAZY [04/02/2026 00:06] blocky.cmd yes it does [04/02/2026 00:06] blocky.cmd i checked if it was the camera shake JUST IN CASE [04/02/2026 00:07] blocky.cmd (it wasn't) [04/02/2026 00:07] blocky.cmd i'm just going line by line rn [04/02/2026 00:07] emeraldlockdown The only 2 things it can be then is something with interactObj or, my favorite theory, `queue_rumble_data_mario`. [04/02/2026 00:07] altiami rumble data is a bit weird iirc [04/02/2026 00:07] blocky.cmd tried ``queue_rumble_data_mario`` on its own it didn't do anything [04/02/2026 00:07] blocky.cmd at least not when isolated [04/02/2026 00:07] peachypeachsm64 I FOUND IT {Reactions} πŸ‘€ (3) πŸ”₯ (2) mariopog beware_the_polygonal βœ… πŸ• 🍞 πŸ‡¬ πŸ‡΄ πŸ…°οΈ πŸ‡Ή eyesshaking [04/02/2026 00:07] emeraldlockdown BIG [04/02/2026 00:08] altiami tell us [04/02/2026 00:08] blocky.cmd TELL [04/02/2026 00:08] blocky.cmd 🐟 [04/02/2026 00:09] peachypeachsm64 THIS you know that every Mario has oInteractType INTERACT_PLAYER and when you enter a level, you "interact" with the inactive players (maybe for 1 frame idk) resetting the server doesn't clear the interactObj, so it's always a remote Mario, unless you interact with something else, for example, a door SO this weird ass pvp stuff is the cause of the double bonk {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468383008741654658/image.png?ex=6983d1be&is=6982803e&hm=612eddaac7e7c7855619c9ead1db86b6d15e3b09b16d59a0b871ac07e6cb128a& [04/02/2026 00:10] altiami this only strengthens what I offhandedly mentioned the other day about how we need to properly handle unloading network players [04/02/2026 00:10] blocky.cmd IN YOUR FACE [04/02/2026 00:11] blocky.cmd STUPID [04/02/2026 00:11] xluigigamerx Bro, you can activate it without prior pvp, how is it pvp related [04/02/2026 00:11] altiami {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468383477488681241/DAAAMN.mp4?ex=6983d22e&is=698280ae&hm=496c9ff4ea5e6aa145accc5e3bf543c9b39a0e8d011d5ed2b3720c4260bb3487& [04/02/2026 00:11] peachypeachsm64 read bro [04/02/2026 00:11] emeraldlockdown Thank you peachy for your incredible service to finding this bug that's plagued this game for centuries. Now I'm curious how long it's been around. What file is that and what line? {Reactions} ⬆️ (2) [04/02/2026 00:11] xluigigamerx I can't im doing something for college [04/02/2026 00:11] altiami what's really funny is [04/02/2026 00:12] altiami I saw that condition [04/02/2026 00:12] altiami and was like [04/02/2026 00:12] altiami hmmmmmmmmmmmmmmmmmmmmmmmmmm [04/02/2026 00:12] xluigigamerx Read it [04/02/2026 00:12] peachypeachsm64 `mario_action_airborne.c` `common_air_knockback_step` line 1240 [04/02/2026 00:12] emeraldlockdown Thanks [04/02/2026 00:12] xluigigamerx (Past tense) [04/02/2026 00:12] altiami yup, that's the one [04/02/2026 00:12] xluigigamerx Who added that line [04/02/2026 00:12] emeraldlockdown Yup, 3 years ago [04/02/2026 00:13] altiami but, something's still weird. Doesn't that only set mario's forward velocity when that check passes? [04/02/2026 00:13] blocky.cmd 2023 :faceholdingbacktears: [04/02/2026 00:13] emeraldlockdown Ah it may have been even earlier, let me check real quick [04/02/2026 00:13] xluigigamerx I want to know who's walls I need to live in [04/02/2026 00:14] altiami or is the problem that it's *not* passing the condition [04/02/2026 00:14] peachypeachsm64 that's the problem if the fvel is not set to -16, you continue bonking into the wall or, for the dive, get sent backwards violently [04/02/2026 00:14] altiami ah, that's where I was mistaken [04/02/2026 00:15] altiami I thought the speed has been set already [04/02/2026 00:15] altiami yeah I see, it's not here in the hit wall action [04/02/2026 00:16] altiami alright gang, let's lift the mask and see whose kneecaps need to be gently tapped [04/02/2026 00:16] altiami {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468384692775555277/image.png?ex=6983d350&is=698281d0&hm=0806f2c53e9b8c4649e2bf21833e160928f6dfbd696d7add1e0269441a644c2f& [04/02/2026 00:16] blocky.cmd FIXED? [04/02/2026 00:16] blocky.cmd # FIXED?? [04/02/2026 00:16] altiami [04/02/2026 00:17] altiami it clearly had good intentions [04/02/2026 00:17] altiami but dormant network players are scuffed [04/02/2026 00:17] blocky.cmd aw it was when too much knockback setting got nerfed :( {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468384968592855165/image.png?ex=6983d391&is=69828211&hm=64f8675f995e4fabc9145eaca84d8069e3b7f9aa628c99d7a84eb08b1d0938f2& [04/02/2026 00:17] blocky.cmd i remember that [04/02/2026 00:17] emeraldlockdown No I don't know if that one is it, since, if I understand it correctly, ` if (m->interactObj == NULL || !(m->interactObj->oInteractType & INTERACT_PLAYER)) {` caused the issue, and that existed beforehand [04/02/2026 00:18] altiami the git blame tracks it back to this commit [04/02/2026 00:18] altiami {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468385134880362551/image.png?ex=6983d3b9&is=69828239&hm=fe08108418b3326b8a5952f8002fcb5d057515f199c55a5c126813406e684788& [04/02/2026 00:18] emeraldlockdown https://github.com/coop-deluxe/sm64coopdx/commit/51940d6a82c8cddbd98a9ca3eef67554711778fb [04/02/2026 00:19] emeraldlockdown {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468385345015124245/Screenshot_2026-02-03_at_5.19.04_PM.png?ex=6983d3eb&is=6982826b&hm=c834fdd510a80f007c61b2ababf197312930b85c4b2a4b402b4e6cc72f7a4215& [04/02/2026 00:19] altiami oh wait I see what you mean [04/02/2026 00:19] altiami it was already in there in the diff [04/02/2026 00:19] emeraldlockdown Dang my memory is vividly off [04/02/2026 00:19] emeraldlockdown 2020 is crazy [04/02/2026 00:19] blocky.cmd yeah [04/02/2026 00:19] blocky.cmd i did remember it existing since the beggining [04/02/2026 00:20] theincredibleholc https://tenor.com/view/renuu-thanos-rest-peaceful-gif-20831938 [04/02/2026 00:20] theincredibleholc It's all ogre now πŸ₯Ή [04/02/2026 00:21] altiami yeah emerald you found it [04/02/2026 00:21] peachypeachsm64 well, some update it's not when entering a level it's specifically when restarting the server it somehow interacts with a random mario object (13?) (object pointer | is mario obj | playerIndex) {Attachments} https://cdn.discordapp.com/attachments/1264640346957021195/1468385977859969185/image.png?ex=6983d482&is=69828302&hm=59f49bd2f11d4a48eaa781966d24cf45affadf285983114e52a34ab6db79bd4e& [04/02/2026 00:22] theincredibleholc Huge shoutout to @ColbyRayz! for finding a way to replicate it. That was a seriously obscure find! [04/02/2026 00:22] peachypeachsm64 wait [04/02/2026 00:22] peachypeachsm64 no [04/02/2026 00:22] emeraldlockdown Starting the server, or closing the server? [04/02/2026 00:22] peachypeachsm64 it doesn't interact it uses whatever was in that object slot which happens to be a mario [04/02/2026 00:22] peachypeachsm64 stale pointer lmao [04/02/2026 00:22] altiami so I was right in a super roundabout way [04/02/2026 00:22] altiami goddamn [04/02/2026 00:23] altiami I didn't expect stale object pointer though [04/02/2026 00:23] peachypeachsm64 tldr mario was never reset properly [04/02/2026 00:23] blocky.cmd i'm close to doing a pr for a bunch of star road fixes does a romhack port repo still exist [04/02/2026 00:23] blocky.cmd coop-romhacks-blahblah [04/02/2026 00:23] altiami Mario HAS A TUMOR! [04/02/2026 00:24] altiami okay well [04/02/2026 00:24] altiami that was fun [04/02/2026 00:24] emeraldlockdown Indeed [04/02/2026 00:24] altiami roleplaying as bloodhounds [04/02/2026 00:24] peachypeachsm64 https://github.com/coop-deluxe/rom-hacks [04/02/2026 00:25] altiami https://tenor.com/view/bear-sniffing-spartan370-big-blue-house-gif-22010873 [04/02/2026 00:25] altiami this us finding the bug [04/02/2026 00:25] blocky.cmd thanks! [04/02/2026 00:25] emeraldlockdown The only other bug I can think of that has been in the community for waay too long is the doors disappearing bug, but something tells me that won't be as easy to fix. But seeing how colby somehow found a way to reproduce this bug, I'll hold out hope [04/02/2026 00:25] peachypeachsm64 we found it now, let's find a way to fix it [04/02/2026 00:26] altiami oh I thought you had an idea now [04/02/2026 00:26] altiami since you found it was a stale pointer [04/02/2026 00:26] peachypeachsm64 reset interactObj but where [04/02/2026 00:26] peachypeachsm64 and when [04/02/2026 00:26] emeraldlockdown Can't you just reset it on `network_shutdown`, or am I thinking in super simple ways [04/02/2026 00:26] altiami level load imo [04/02/2026 00:26] altiami when the mario is initialized anyway [04/02/2026 00:26] altiami at least, I hope he is [04/02/2026 00:26] emeraldlockdown Oh yea [04/02/2026 00:26] altiami he better be [04/02/2026 00:27] altiami or I'm throwing hands with miyamoto himself [04/02/2026 00:27] emeraldlockdown Wait can't you just reset it in `init_single_mario`? [04/02/2026 00:27] sharen462 thats what i was gonna suggest [04/02/2026 00:27] sharen462 that function doesnt reset ot [04/02/2026 00:27] sharen462 you could just add it [04/02/2026 00:27] altiami really, *all* marios should be initialized on level load if they aren't already [04/02/2026 00:27] emeraldlockdown ```c m->heldObj = NULL; m->heldByObj = NULL; m->riddenObj = NULL; m->usedObj = NULL; m->bubbleObj = NULL; ``` They were so close! [04/02/2026 00:27] altiami LMAO [04/02/2026 00:27] sharen462 it even resets other obj fields like ``riddenObj`` and stuff [04/02/2026 00:28] sharen462 yeah lmfao [04/02/2026 00:35] peachypeachsm64 yeah that works now all I have left to do is making a comically long commit description for a one-line change [04/02/2026 00:36] altiami just screenshot this entire discord conversation, put it in a google doc, and attach it to the commit message * Fix all double bonk related bugs Here's some explanation about the infamous wall double bonk glitch. First, why does it happen seemingly randomly? When the server resets or when entering a level, Mario is fully initialized again, but, for some reason, interactObj is not reset to NULL. Because of that, it keeps the pointer of the last object Mario interacted with, which becomes a stale object pointer (the object it points to no longer exists). Because objects are stored in a static buffer rather than being allocated dynamically on the heap, this pointer always points to valid memory (it cannot crash the game), but could potentially point to a random Mario object depending on various conditions I won't list here. Why interactObj pointing to a Mario object has anything to do with wall bonks?!? Because PVP. sm64coopdx adds PVP. And the way it adds it is kinda questionable. But that's a topic for another day. To handle player vs player interactions, all Mario objects get the unique INTERACT_PLAYER interaction type. For the sake of code reusability, all PVP interactions use regular interactions functions, with a Mario's interactObj temporarily set to another Mario. When the interaction is done, the interactObj is properly reset to NULL. Meaning in regular gameplay, Mario's interactObj is NEVER another Mario object. Unless stale pointer as mentioned above. What's important here is the interaction type INTERACT_PLAYER. Because PVP is about punching and kicking other people's face with force, it would be a shame if someone threw one of their best blows just to see their opponent slightly move backwards like nothing happened. It had to be impactful. For that, knockbacks had to be changed. In SM64, ground knockbacks have capped speed and air knockbacks set the speed to a predefined value. Not really usable for in the context of PVP. So, for the sake of code reusability again, these limits had to be temporarily ignored during PVP knockbacks. Now the question is... how do you recognize a PVP knockback? ... What about checking m->interactObj->oInteractType?!?!? Because of this stupid check that does nothing most of the time because Mario's interactObj is NEVER another Mario object, BUT suddenly treats all knockbacks as PVP attacks because of a stale pointer, the following happens: - Mario bonks into a wall, entering the backwards air knockback action - Due to the check above, Mario's speed is not set properly, keeping him moving forward instead of backwards - Mario bonks into the wall again, but he's no longer facing the wall (Firsties are not affected by the bug, because they allow Mario to wall kick before he enters the backwards knockback.) Just remove the check and for proper measure, reset interactObj during Mario init and that's it, right? ... NO. Now, why does it happen AGAIN? There is still an obscure bug that probably nobody ever encountered before, but exists. During a 10-frame window after exiting a PVP knockback, the next knockbacks are treated as PVP knockbacks. To prevent players from being knockbacked again and again, a knockbackTimer is set and starts counting down as soon as Mario exits any knockback action. But! When not zero, this timer also makes the knockback actions ignore their corresponding speed cap. Not one, but TWO broken checks, that can't even properly identify a PVP knockback, causing one of the most infamous glitch of the game. Solution? First, use a specific actionArg to flag a knockback action as a PVP attack. Second, ignore the knockback speed caps only if this flag is set. Third, because some mods implement their own PVP knockbacks in a bad way (not their fault, just tried to mimic what was already in place), set the default knockback speed during set_mario_action if the PVP attack flag is not set. With this, no more double bonk, no more janky knockbacks. That's all, folks! * I'M A CHUCKSTER --- autogen/lua_definitions/constants.lua | 2 +- docs/lua/constants.md | 2 +- src/game/interaction.c | 15 +++++++++++---- src/game/interaction.h | 2 +- src/game/mario.c | 24 ++++++++++++++++++++++++ src/game/mario_actions_airborne.c | 15 +++++---------- src/game/mario_actions_moving.c | 27 ++++++++++++++------------- src/pc/lua/smlua_constants_autogen.c | 2 +- 8 files changed, 58 insertions(+), 31 deletions(-) diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index f2d30851a..d981a171e 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -3276,7 +3276,7 @@ PVP_ATTACK_KNOCKBACK_TIMER_DEFAULT = 10 PVP_ATTACK_KNOCKBACK_TIMER_OVERRIDE = -5 --- @type integer -PVP_ATTACK_OVERRIDE_VANILLA_INVINCIBILITY = 0x0000FFFF +PVP_ATTACK_KNOCKBACK_ACTION_ARG = 0x10000 --- @type integer INT_STATUS_ATTACK_MASK = 0x000000FF diff --git a/docs/lua/constants.md b/docs/lua/constants.md index 7632878e6..664556031 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -1459,7 +1459,7 @@ - ATTACK_FROM_BELOW - PVP_ATTACK_KNOCKBACK_TIMER_DEFAULT - PVP_ATTACK_KNOCKBACK_TIMER_OVERRIDE -- PVP_ATTACK_OVERRIDE_VANILLA_INVINCIBILITY +- PVP_ATTACK_KNOCKBACK_ACTION_ARG - INT_STATUS_ATTACK_MASK - INT_STATUS_HOOT_GRABBED_BY_MARIO - INT_STATUS_MARIO_UNK1 diff --git a/src/game/interaction.c b/src/game/interaction.c index d81651099..4304a23ae 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -643,7 +643,9 @@ static u32 unused_determine_knockback_action(struct MarioState *m) { return bonkAction; } -u32 determine_knockback_action(struct MarioState *m, UNUSED s32 arg) { +u32 determine_knockback_action(struct MarioState *m, RET bool *isPlayerAttack) { + *isPlayerAttack = false; + if (!m) { return 0; } if (m->interactObj == NULL) { return sForwardKnockbackActions[0][0]; @@ -739,6 +741,7 @@ u32 determine_knockback_action(struct MarioState *m, UNUSED s32 arg) { m->knockbackTimer = hasBeenPunched ? PVP_ATTACK_KNOCKBACK_TIMER_OVERRIDE : PVP_ATTACK_KNOCKBACK_TIMER_DEFAULT; #undef IF_REVAMPED_PVP m->faceAngle[1] = m->interactObj->oFaceAngleYaw + (sign == 1.0f ? 0 : 0x8000); + *isPlayerAttack = true; } return bonkAction; @@ -870,7 +873,10 @@ u32 take_damage_and_knock_back(struct MarioState *m, struct Object *o) { } update_mario_sound_and_camera(m); - return drop_and_set_mario_action(m, determine_knockback_action(m, o->oDamageOrCoinValue), damage); + + bool isPlayerAttack = false; + u32 knockbackAction = determine_knockback_action(m, &isPlayerAttack); + return drop_and_set_mario_action(m, knockbackAction, damage | (isPlayerAttack ? PVP_ATTACK_KNOCKBACK_ACTION_ARG : 0)); } return FALSE; @@ -1724,8 +1730,9 @@ u32 interact_snufit_bullet(struct MarioState *m, UNUSED u32 interactType, struct play_character_sound(m, CHAR_SOUND_ATTACKED); update_mario_sound_and_camera(m); - return drop_and_set_mario_action(m, determine_knockback_action(m, o->oDamageOrCoinValue), - o->oDamageOrCoinValue); + bool isPlayerAttack = false; + u32 knockbackAction = determine_knockback_action(m, &isPlayerAttack); + return drop_and_set_mario_action(m, knockbackAction, o->oDamageOrCoinValue | (isPlayerAttack ? PVP_ATTACK_KNOCKBACK_ACTION_ARG : 0)); } } diff --git a/src/game/interaction.h b/src/game/interaction.h index 7dfe20a74..d06e9a795 100644 --- a/src/game/interaction.h +++ b/src/game/interaction.h @@ -101,7 +101,7 @@ enum InteractionFlag { #define PVP_ATTACK_KNOCKBACK_TIMER_DEFAULT 10 #define PVP_ATTACK_KNOCKBACK_TIMER_OVERRIDE -5 -#define PVP_ATTACK_OVERRIDE_VANILLA_INVINCIBILITY 0x0000FFFF +#define PVP_ATTACK_KNOCKBACK_ACTION_ARG 0x10000 #define INT_STATUS_ATTACK_MASK 0x000000FF diff --git a/src/game/mario.c b/src/game/mario.c index 2c1e6c56b..897bbc3fe 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -1043,6 +1043,29 @@ static u32 set_mario_action_airborne(struct MarioState *m, u32 action, u32 actio case ACT_JUMP_KICK: m->vel[1] = 20.0f; break; + + // Set forward vel to a predefined value for non-player knockbacks + case ACT_BACKWARD_AIR_KB: + case ACT_HARD_BACKWARD_AIR_KB: + if (!(actionArg & PVP_ATTACK_KNOCKBACK_ACTION_ARG)) { + mario_set_forward_vel(m, -16.0f); + } + break; + + case ACT_FORWARD_AIR_KB: + case ACT_HARD_FORWARD_AIR_KB: + if (!(actionArg & PVP_ATTACK_KNOCKBACK_ACTION_ARG)) { + mario_set_forward_vel(m, 16.0f); + } + break; + + case ACT_THROWN_BACKWARD: + case ACT_THROWN_FORWARD: + case ACT_SOFT_BONK: + if (!(actionArg & PVP_ATTACK_KNOCKBACK_ACTION_ARG)) { + mario_set_forward_vel(m, m->forwardVel); // needed to update velocities + } + break; } m->peakHeight = m->pos[1]; @@ -2221,6 +2244,7 @@ void init_single_mario(struct MarioState* m) { m->heldObj = NULL; m->heldByObj = NULL; + m->interactObj = NULL; m->riddenObj = NULL; m->usedObj = NULL; m->bubbleObj = NULL; diff --git a/src/game/mario_actions_airborne.c b/src/game/mario_actions_airborne.c index 5bf71f395..00b338447 100644 --- a/src/game/mario_actions_airborne.c +++ b/src/game/mario_actions_airborne.c @@ -57,7 +57,7 @@ depending on whether Mario's forward velocity is high enough to be considered a |descriptionEnd| */ void play_knockback_sound(struct MarioState *m) { if (!m) { return; } - if (m->actionArg == 0 && (m->forwardVel <= -28.0f || m->forwardVel >= 28.0f)) { + if ((m->actionArg & ~PVP_ATTACK_KNOCKBACK_ACTION_ARG) == 0 && (m->forwardVel <= -28.0f || m->forwardVel >= 28.0f)) { play_character_sound_if_no_flag(m, CHAR_SOUND_DOH, MARIO_MARIO_SOUND_PLAYED); } else { play_character_sound_if_no_flag(m, CHAR_SOUND_UH, MARIO_MARIO_SOUND_PLAYED); @@ -1236,13 +1236,8 @@ u32 common_air_knockback_step(struct MarioState *m, u32 landAction, u32 hardFall if (!m) { return 0; } u32 stepResult; - if (m->knockbackTimer == 0) { - if (m->interactObj == NULL || !(m->interactObj->oInteractType & INTERACT_PLAYER)) { - mario_set_forward_vel(m, speed); - } - } else if (m->knockbackTimer < 0) { - // do nothing - } else { + // Refresh knockbackTimer + if (m->knockbackTimer > 0) { m->knockbackTimer = PVP_ATTACK_KNOCKBACK_TIMER_DEFAULT; } @@ -1356,7 +1351,7 @@ s32 act_hard_forward_air_kb(struct MarioState *m) { s32 act_thrown_backward(struct MarioState *m) { if (!m) { return 0; } u32 landAction; - if (m->actionArg != 0) { + if ((m->actionArg & ~PVP_ATTACK_KNOCKBACK_ACTION_ARG) != 0) { landAction = ACT_HARD_BACKWARD_GROUND_KB; } else { landAction = ACT_BACKWARD_GROUND_KB; @@ -1375,7 +1370,7 @@ s32 act_thrown_forward(struct MarioState *m) { s16 pitch; u32 landAction; - if (m->actionArg != 0) { + if ((m->actionArg & ~PVP_ATTACK_KNOCKBACK_ACTION_ARG) != 0) { landAction = ACT_HARD_FORWARD_GROUND_KB; } else { landAction = ACT_FORWARD_GROUND_KB; diff --git a/src/game/mario_actions_moving.c b/src/game/mario_actions_moving.c index 6a21d45d5..c502e5f31 100644 --- a/src/game/mario_actions_moving.c +++ b/src/game/mario_actions_moving.c @@ -1775,12 +1775,13 @@ Handles knockback on the ground (getting hit while on the ground) with shared lo s32 common_ground_knockback_action(struct MarioState *m, s32 animation, s32 arg2, s32 arg3, s32 arg4) { if (!m) { return 0; } s32 animFrame; + s32 damage = arg4 & ~PVP_ATTACK_KNOCKBACK_ACTION_ARG; if (arg3) { play_mario_heavy_landing_sound_once(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND); } - if (arg4 > 0) { + if (damage > 0) { play_character_sound_if_no_flag(m, CHAR_SOUND_ATTACKED, MARIO_MARIO_SOUND_PLAYED); } else { #ifdef VERSION_JP @@ -1790,18 +1791,18 @@ s32 common_ground_knockback_action(struct MarioState *m, s32 animation, s32 arg2 #endif } - if (m->knockbackTimer == 0) { - if (m->interactObj == NULL || !(m->interactObj->oInteractType & INTERACT_PLAYER)) { - if (m->forwardVel > 32.0f) { - m->forwardVel = 32.0f; - } - if (m->forwardVel < -32.0f) { - m->forwardVel = -32.0f; - } + // Cap speed if it's not a PVP attack + if (!(arg4 & PVP_ATTACK_KNOCKBACK_ACTION_ARG)) { + if (m->forwardVel > 32.0f) { + m->forwardVel = 32.0f; } - } else if (m->knockbackTimer < 0) { - // do nothing - } else { + if (m->forwardVel < -32.0f) { + m->forwardVel = -32.0f; + } + } + + // Refresh knockbackTimer + if (m->knockbackTimer > 0) { m->knockbackTimer = PVP_ATTACK_KNOCKBACK_TIMER_DEFAULT; } @@ -1824,7 +1825,7 @@ s32 common_ground_knockback_action(struct MarioState *m, s32 animation, s32 arg2 if (m->health < 0x100) { set_mario_action(m, ACT_STANDING_DEATH, 0); } else { - if (arg4 > 0) { + if (damage > 0) { m->invincTimer = 30; } set_mario_action(m, ACT_IDLE, 0); diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index a55aae378..129a8f876 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -1653,7 +1653,7 @@ char gSmluaConstants[] = "" "ATTACK_FROM_BELOW=6\n" "PVP_ATTACK_KNOCKBACK_TIMER_DEFAULT=10\n" "PVP_ATTACK_KNOCKBACK_TIMER_OVERRIDE=-5\n" -"PVP_ATTACK_OVERRIDE_VANILLA_INVINCIBILITY=0x0000FFFF\n" +"PVP_ATTACK_KNOCKBACK_ACTION_ARG=0x10000\n" "INT_STATUS_ATTACK_MASK=0x000000FF\n" "INT_STATUS_HOOT_GRABBED_BY_MARIO=(1 << 0)\n" "INT_STATUS_MARIO_UNK1=(1 << 1)\n"