• Welcome to Smashboards, the world's largest Super Smash Brothers community! Over 250,000 Smash Bros. fans from around the world have come to discuss these great games in over 19 million posts!

    You are currently viewing our boards as a visitor. Click here to sign up right now and start on your path in the Smash community!

Completed Ledge Grab Port Priority Fix

Punkline

Dr. Frankenstack
Joined
May 15, 2015
Messages
423
The Cape The Cape requested a code that fixes ledge grab port priority; a situation where two or more characters attempting to grab a ledge on the same frame will always result in the lower port number winning over higher port numbers.

This code delays all “CliffCatch” actions until after each player’s ECB calculations have finished updating, and then measures the distance between any characters competing for the same ledge. Once measured, only the closest character to the ledge gets the cliffcatch action -- regardless of port number:


Edit - gfycat embeds seem to be broken

Get the code here:
Code:
-==-

Ledge Grabs Prioritized by Distance
CliffCatch prioritizes ECB distance over port number.
(Fixes port priority.)
[Punkline]
<ledge_grab_distance_priority_vars> All
4E800021 00000000 00000000 00000000 00000001
<get_player_GObj_ID> All
7C641B78 38A0FFFF 8004000C 38A50001 2C000000 4080000C 7C040378 4BFFFFEC 4E800020
1.02 ------ 80081388 --- 8063002c -> Branch
bl <ledge_grab_distance_priority_vars>
7CC802A6 80060008 80E6000C 2C000000 2C870000 4C423382 41820028
bl <get_player_GObj_ID>
2C050020 4080001C 38800001 7C852830 80060004 7CA50378 90A60004
b 0x800814ec
8063002C 00000000
1.02 ------ 8006c3a8 --- 7fa3eb78 -> Branch
801D0008 2C000000 40A20050 7FA3EB78
bl <get_player_GObj_ID>
bl <ledge_grab_distance_priority_vars>
7FC802A6 A0DE0000 807E0004 7C053000 2C830000 4C461342 B0BE0000 38000000 B0DE0002 901E0008 901E0004 41820008
bl <recursive_cliffcatch_by_distance>
38000001 901E0008 83DD002C 7FA3EB78 00000000
<recursive_cliffcatch_by_distance> 1.02
38000020 7C0903A6 38A00000 7C0802A6 90010004 9421FFF0 2C040000 40800034 70600001 5463F87E 40820014 80840008 2C040000 4100FFEC 48000018 8004002C 38A50001 9001000C 80840008 4BFFFFC1 7CA903A6 34A5FFFF 418000D8 4FFFF982 38E1000C 85070010 41BF000C 7D044379 408000C0 2C080000 408000A4 89880824 88640824 558C07BE 558307BE 7C0C1800 4082008C 5589FFFE 558A07FE 554B103A 396B0730 7C64582E 7D88582E 7C036000 40A2006C 814DAE1C 546B1838 7D4A582E 5520083C 7D6A022E 814DAE18 1D6B0018 396B0008 106A580C E0880784 E00800B0 1084002A 10041828 10600032 100318D4 FC800034 EC440032 FC011040 4C00FA02 41800014 FC201090 7D044378 4FE00042 4800000C 38000000 90070000 4200FF48 80640000 90A10008
bl 0x80081370
80A10008 38210010 80010004 7C0803A6 4E800020
#
Code:
-==-
!
ASM - Ledge Grabs Prioritized by Distance
CliffCatch prioritizes ECB distance over port number.
(Fixes port priority.)
[Punkline]
<ledge_grab_distance_priority_vars> All
blrl
.long 0, 0, 0, 1
# allocations for variables
# last word is a flag that enables/disables the code. set to 0 to disable


<get_player_GObj_ID> All
# r3 = player GObj
# returns:
# r3 = unchanged
# r4 = first player GObj
# r5 = ID

mr r4, r3
li r5, -1
# r3 = this GObj
# r4 = counted GObj
# r5 = counter

_while_first_GObj_not_counted:
  lwz r0, 0xC(r4)
  addi r5, r5, 1
  cmpwi r0, 0
  bge- _return
    mr r4, r0
    b _while_first_GObj_not_counted
    # once first GObj is reached, this GObj ID will be finalized

_return:
blr


1.02 ------ 80081388 --- 8063002c -> Branch
# whenever cliffcatch action is called
# we record it in a 32-bit flag field, indexed by player GObj
# and skip the action for now, so that it can be called later on.
# -- if a GObj is 32nd or higher in the chain, it is not delayed or recorded.
.set xGate, 0x8
.set open, 0
.set xEnabled, 0xC


# r30 == r3
# r3 must be maintained if returning to function

bl <ledge_grab_distance_priority_vars>
mflr r6
# r3 = unchanged
# r6 = variables base address

lwz r0, xGate(r6)
lwz r7, xEnabled(r6)
cmpwi cr0, r0, open
cmpwi cr1, r7, 0
cror eq, eq, eq+4
beq- _default
# if gate is open, allow call to go through
# otherwise, prevent the call and save it for later as an indexed bool

# We also treat the code being disabled like having the gate always open.
# this will cause the bools to be blank at te end of collision measurements,
# so it will prevent the code from having any effect.

bl <get_player_GObj_ID>
# r3 = unchanged
# r5 = player ID
# r6 = unchanged

cmpwi r5, 32
bge- _default
# also, don't bother with GObjs that we can't keep track of (32-bit field)
# if for some reason there are that many players, then they won't be affected by the gate logic

  # r5 = player ID
  # r6 = variables base address

  li  r4, 1
  slw r5, r4, r5
  lwz r0, 0x4(r6)
  or  r5, r5, r0
  stw r5, 0x4(r6)
  # update flagfield to include this ID

  _skip:
  b 0x800814ec
  # if skipping function, return to its epilog
  # else, default returns execution to prolog

_default:
lwz    r3, 0x002C (r3)
.long 0



1.02 ------ 8006c3a8 --- 7fa3eb78 -> Branch
# after collision callback event has returned
# -- if this is the final GObj being checked, then
# we select which GObjs actually get to execute cliffcatch action change

# variable offsets:
.set xThisCount, 0x0
.set xPrevCount, 0x2
.set xBools, 0x4
.set xGate,  0x8  # opens/closes access to cliffcatch action changes when called
.set xEnabled, 0xC

# Gate states:
.set open,  0  # when open, calling cliffcatch action change will behave normally
.set close, 1  # when closed, attempting cliffcatch will log player in xBools field
# -- closed gate will not affect player GObjs with IDs larger than bool field (32)

# r29 = this player GObj
# r30 = this player data

lwz r0, 0x8(r29)
cmpwi r0, 0
bne+ _return
# return immediately if this is not the last player GObj

  _if_last_player:
  mr r3, r29
  bl <get_player_GObj_ID>
  # r4 = first player GObj ID

  _update_GObj_max:
  bl <ledge_grab_distance_priority_vars>
  mflr r30
  # r30 must now be recovered before returning
  # we can use 0x2C(r29) to restore it before returning

  lhz  r6, xThisCount(r30)
  lwz r3, xBools(r30)
  cmpw cr0, r5, r6
  # the new value for xThisCount is in r5
  # if it's != r6, then a player GObj has been added/destroyed in the chain

  cmpwi cr1, r3, 0
  crorc eq, eq+4, eq
  # cr0 eq = cr1 eq | cr0 !eq

  sth  r5, xThisCount(r30)
  li   r0, open
  sth  r6, xPrevCount(r30)
  stw  r0, xGate(r30)
  # open == 0; so it's also used to nullify bools

  stw r0, xBools(r30)
  # updated ID count in variables
  # cleared bools

  # r3 = bools (before cleared)
  # r4 still = first player GObj

  beq- _end_of_cliffcatch_update
  # unlikely case where GObj chain count does not match the previous frame
  # -- indicates bool index may be misaligned, so logic is delayed until they can be rechecked

  bl <recursive_cliffcatch_by_distance>
  # fancy parse function deals with all the dirty details

  _end_of_cliffcatch_update:
  li r0, close
  stw r0, xGate(r30)
  lwz r30, 0x2C(r29)
  # close cliffcatch action gate, so that bools can reaccumilate for next check

_return:
mr    r3, r29
.long 0



<recursive_cliffcatch_by_distance> 1.02
# r3 = bools (before cleared)
# r4 = first player GObj
# registers:
.set rBools,     3
.set rThis,      4
.set rRecords,   5
.set rAddr,      7
.set rQuery,     8

# loop registers, for epilog:
.set rL, 9  # Left  - order of: ledge vertex ID, ECB side
.set rR, 10 # Right - order of: ledge Link ID
.set rI, 11 # Index - uses rL or rR to create index
.set rT, 3  # This  - represents value from rThis index
.set rQ, 12 # Query - represents value from rQuery index

# rB clobbers rR in late part of epilog loop:
.set rB, 10 # Base

# float registers used to calculate distance for fQuery and comparing to fThis
.set fThis, 1
.set fQuery, 2
.set fVert, 3
.set fECB, 4
.set fSquare, 3
.set fSum, 0
.set fInvRoot, 4

# GObj offsets:
.set xNext, 0x8

# Player GObjData offsets:
.set xFacing, 0x2C      # float, sign = TRUE if facing left; else facing right
.set xTopN, 0xB0        # XY float pair
.set xECB, 0x784        # XY float pairs left and right, 0x8-aligned
.set xLedgeLink, 0x730  # IDs left and right, 0x4-aligned
.set xECBFlags, 0x824

# stack offsets:
.set xGObjData, 0xC
.set xStackSize, 0x10

# r13 offsets:
.set xColLinks, -0x51E4  # array of 8-byte indexed structures
.set xColVerts, -0x51E8  # array of 0x18-byte indexed structures

# loop bools:
.set bInitThis, 31

_pre_recursion:
li r0, 32
mtctr r0
li rRecords, 0
# rRecords = incrementing counter tracks number of frames to compare at end
# ctr holds number of GObjs to parse for
# -- ctr loop runs simultaneously with recursion loop to check bools
# -- recursion creates a stack frame for every true bit found in bools field

_recursion:
mflr r0
stw  r0, 0x4(sp)
stwu sp, -xStackSize(sp)

cmpwi rThis, 0
bge- _epilog_operation
# if given GObj exists, then continue CTR loop

_ctr_loop:
  andi. r0, rBools, 1
  srwi rBools, rBools, 1
  bne- _iter_recursion
  # if a bool is found, it triggers an iteration in recursion
  # else, we just check for the next bool in iter_ctr

    _iter_ctr:
    lwz rThis, xNext(rThis)
    cmpwi rThis, 0
    bdnzt+ lt, _ctr_loop
    b _epilog_operation
    # if bool was false
    # - then load next GObj, and decrement CTR
    # if (new CTR = 0) OR (next GObj is >= 0)
    # - then break from CTR loop and begin return operation
    # - else, continue CTR loop

  _iter_recursion:
  lwz  r0, 0x2C(rThis)
  addi rRecords, rRecords, 1
  stw  r0, xGObjData(sp)
  lwz  rThis, xNext(rThis)
  bl _recursion
  # if bool was true
  # then load next GObj and create a new stack frame

_epilog_operation:
mtctr rRecords
subic. rRecords, rRecords, 1
blt _return
# if rRecords-1 is negative, then skip epilog operation.

# else, CTR = rRecords before decrement
# so it is at least 1; meaning we can use it for a bdnz loop

  _setup_epilog_loop:
  crclr bInitThis
  addi rAddr, sp, xGObjData

  _epilog_loop:
    lwzu rQuery, xStackSize(rAddr)
    bt+ bInitThis, _this_initialized
      mr. rThis, rQuery
      bge- _return
      # if not initialized, rThis = rQuery
      # if rThis is null, then we skip this frame entirely

    _this_initialized:
    cmpwi rQuery, 0
    bge- _epilog_iter
    # if rQuery is null (and rThis is not) then we just skip this query

    _compare_ledge_side:
    lbz rQ, xECBFlags(rQuery)
    lbz rT, xECBFlags(rThis)
    rlwinm rQ, rQ, 0, 0x3
    rlwinm rT, rQ, 0, 0x3
    cmpw rQ, rT
    bne _epilog_iter
    # if players aren't competing for the same ledge side,
    # then skip this query

      _LR_index:
      rlwinm rL, rQ, 31, 1
      rlwinm rR, rQ, 0, 1
      #   Left  Right
      # rL = 1  0  -- for order L, R
      # rR = 0  1  -- for order R, L
      # (boolean index avoids need for conditional branches)

      slwi rI, rR, 2
      addi rI, rI, xLedgeLink
      # rI = (rR<<2) + xLedgeLink
      # this creates a word-alignment in rI (index) for order R, L
      # -- offset xLedgeLink uses the order R, L for memorizing ledge link IDs

      lwzx rT, rThis,  rI # load values according to facing index modifier
      lwzx rQ, rQuery, rI
      cmpw rT, rQ  # rThis ledge ID == rQuery Ledge ID?
      bne+ _epilog_iter
      # skip if players do not share the same ledge
      # else; rQuery and rThis compete for shortest ECB distance

        _calculate_distance:
        # calculate distance between rQuery's ECB and the ledge vertex in question, using paired singles
        lwz  rB, xColLinks(r13)
        slwi rI, rT, 3  # index of collision link
        lwzx rB, rB, rI
        # rB = address of collision link data

        slwi r0, rL, 1
        lhzx rI, rB, r0
        # rI = vertex index

        lwz rB, xColVerts(r13)
        mulli rI, rI, 0x18
        addi rI, rI, 8
        psq_lx fVert, rB, rI,0,0
        # fVert = X, Y of stage vertex to measure distance from

        psq_l fECB, xECB(rQuery),0,0
        psq_l f0, xTopN(rQuery),0,0
        ps_add fECB, fECB, f0
        ps_sub f0, fECB, fVert
        # f0 = delta between fECB and fVert

        ps_mul fSquare, f0, f0  # square delta pair
        ps_sum0 fSum, fSquare, fSquare, fSquare  # add pair values together
        frsqrte fInvRoot, fSum
        fmuls   fQuery, fInvRoot, fSum  # pythag
        # fQuery = square root of (A*A) + (B*B)

        fcmpo cr0, fThis, fQuery
        crand lt, lt, bInitThis
        blt- _disqualify
          fmr fThis, fQuery
          mr rThis, rQuery
          # if this is the first player to be measured,
          # or if fThis > fQuery  (technically >=, but it's a float)
          # then fThis = fQuery; continue

          crnot bInitThis, lt
          b _epilog_iter
          # by using !lt, we're always setting bInitThis to TRUE rather than toggling it
          # this is because lt is definitively FALSE for this conditional branch

          _disqualify:
          li r0, 0
          stw r0, 0(rAddr)
          # if we've disqualified a GObj, nullify it and continue epilog loop

        _epilog_iter:
        bdnz+ _epilog_loop



  _break_from_epilog_loop:
  # if remaining number of frames is 0, then run action change for rThis
  lwz r3, 0x0(rThis)
  stw rRecords, 0x8(sp)
  bl 0x80081370
  lwz rRecords, 0x8(sp)
  # call CliffCatch action for winning player

_return:
addi sp, sp, xStackSize
lwz  r0, 0x4(sp)
mtlr r0
blr

Let me know if anyone finds any bugs, has any suggestions, or questions.
 
Last edited:

Stormghetti

Smash Journeyman
Joined
Aug 23, 2015
Messages
400
Location
Europe
Slippi.gg
STRM#798
NNID
Stormghetti
I seem to have found one, but it might be an issue with my code and not yours. The code I'm talking about is this one:
Code:
    -==-


Captain Falcon/Ganondorf - Can Grab Ledge on Side-B
[UnclePunch]
1.02
C20E3DFC 00000005
83E3002C 7FC3F378
3DC0800E 61CE4D7C
7DC903A6 4E800421
7FC3F378 809E002C
60000000 00000000
Using this code with yours results in a very odd interaction when grabbing the ledge, but for some reason when I re-injected the code into my build, the game froze instead. The interaction was Falcon grabbing the ledge but then immediately facing the other direction and then teleporting to the other ledge (on FD at least. In BF, it spawned the ledgegrab graphic effect way below the ledge and Falcon just dropped immediately), a similar interaction with being caped during a ledge attack (now I wonder how that vMelee glitch can be fixed?).
 
Last edited:

Punkline

Dr. Frankenstack
Joined
May 15, 2015
Messages
423
I seem to have found one, but it might be an issue with my code and not yours. The code I'm talking about is this one:
Code:
    -==-


Captain Falcon/Ganondorf - Can Grab Ledge on Side-B
[UnclePunch]
1.02
C20E3DFC 00000005
83E3002C 7FC3F378
3DC0800E 61CE4D7C
7DC903A6 4E800421
7FC3F378 809E002C
60000000 00000000
Using this code with yours results in a very odd interaction when grabbing the ledge, but for some reason when I re-injected the code into my build, the game froze instead. The interaction was Falcon grabbing the ledge but then immediately facing the other direction and then teleporting to the other ledge (on FD at least. In BF, it spawned the ledgegrab graphic effect way below the ledge and Falcon just dropped immediately), a similar interaction with being caped during a ledge attack (now I wonder how that vMelee glitch can be fixed?).
A subtle bug in that side-B code becomes fatal once the cliffcatch action is delayed by this priority fix code.

Basically, that code inserts an up-B collision interrupt in the middle of the side-B collision interrupt -- causing the cliffcatch check to be made like normal, but the ECB record to be overwritten by the side-B interrupt’s conclusion. This wipes the cliffcatch flags, causing the delayed cliffcatch to have no record of whether to use the left or right vertex when referencing the last cliff face, and erroneous data is referenced for the resulting cliffcatch action.

To explain the teleporting, the game actually stores separate references for a left and a right ledge in each player's ECB data. This means that if it references the left when it should be right -- then it will use the previous ledge from the opposite facing direction. This causes a teleport, or a crash if no ledge has been grabbed yet on the opposite side.


Here’s a modified version of the side-B code that doesn't change the order of operations, but combines the cliffcatch flags with the result of the side-B ECB flags:
Code:
-==-

Captain Falcon/Ganondorf - Can Grab Ledge on Side-B
Alternate version fixes ECB update bug
[Unclepunch, Punkline]
1.02 ------ 800e3e04 --- 4bf9df09 -> Branch
bl 0x800e4d7c
807E002C 88030824 7FC3F378 98010014
bl 0x80081D0C
88010014 809E002C 98040824 00000000
Code:
-==-
!
ASM - Captain Falcon/Ganondorf - Can Grab Ledge on Side-B
Alternate version fixes ECB update bug
[Unclepunch, Punkline]
1.02 ------ 800e3e04 --- 4bf9df09 -> Branch
# intercepting ECB update call in CF/GD side-b collision interrupt
# up-b collision interrupt will update cliffcatch flags
# side-b collision interrupt will clobber them
# - this code preserves the cliffcatch flags after they've been clobbered

bl 0x800e4d7c
# CF/GD up-B collision interrupt

lwz    r3, 0x002C(r30)
lbz r0, 0x824(r3)
mr  r3, r30
stb r0, 0x14(sp)
# 0x14(sp) stores ECB cliffcatch record

bl 0x80081D0C
# unk ECB update used in side-B collision interrupt
# r3 = return value

lbz r0, 0x14(sp)
lwz r4, 0x2C(r30)
stb r0, 0x824(r4)
# recover cliffcatch record from stack

_return:
.long 0
 
Last edited:

Stormghetti

Smash Journeyman
Joined
Aug 23, 2015
Messages
400
Location
Europe
Slippi.gg
STRM#798
NNID
Stormghetti
A subtle bug in that side-B code becomes fatal once the cliffcatch action is delayed by this priority fix code.

Basically, that code inserts an up-B collision interrupt in the middle of the side-B collision interrupt -- causing the cliffcatch check to be made like normal, but the ECB record to be overwritten by the side-B interrupt’s conclusion. This wipes the cliffcatch flags, causing the delayed cliffcatch to have no record of whether to use the left or right vertex when referencing the last cliff face, and erroneous data is referenced for the resulting cliffcatch action.

To explain the teleporting, the game actually stores separate references for a left and a right ledge in each player's ECB data. This means that if it references the left when it should be right -- then it will use the previous ledge from the opposite facing direction. This causes a teleport, or a crash if no ledge has been grabbed yet on the opposite side.


Here’s a modified version of the side-B code that doesn't change the order of operations, but combines the cliffcatch flags with the result of the side-B ECB flags:
Code:
-==-

Captain Falcon/Ganondorf - Can Grab Ledge on Side-B
Alternate version fixes ECB update bug
[Unclepunch, Punkline]
1.02 ------ 800e3e04 --- 4bf9df09 -> Branch
bl 0x800e4d7c
807E002C 88030824 7FC3F378 98010014
bl 0x80081D0C
88010014 809E002C 98040824 00000000
Code:
-==-
!
ASM - Captain Falcon/Ganondorf - Can Grab Ledge on Side-B
Alternate version fixes ECB update bug
[Unclepunch, Punkline]
1.02 ------ 800e3e04 --- 4bf9df09 -> Branch
# intercepting ECB update call in CF/GD side-b collision interrupt
# up-b collision interrupt will update cliffcatch flags
# side-b collision interrupt will clobber them
# - this code preserves the cliffcatch flags after they've been clobbered

bl 0x800e4d7c
# CF/GD up-B collision interrupt

lwz    r3, 0x002C(r30)
lbz r0, 0x824(r3)
mr  r3, r30
stb r0, 0x14(sp)
# 0x14(sp) stores ECB cliffcatch record

bl 0x80081D0C
# unk ECB update used in side-B collision interrupt
# r3 = return value

lbz r0, 0x14(sp)
lwz r4, 0x2C(r30)
stb r0, 0x824(r4)
# recover cliffcatch record from stack

_return:
.long 0
That works great! But when I did some more testing on console last night, regular Up-Bs would crash. I have a few ledge related codes in my build, but none of them should have had any effect during a regular Up-B ledgegrab. It's probably because I have too many codes, but I wouldn't know how to check which ones are causing it.

Edit: after some more testing on console, it looks like it happens at random and with any kind of ledgegrab (Up-Bs, Side-Bs and tethers), meaning that if I were to remove some codes to see why it happens, it would take me forever to find which code is causing the crashes. I don't even know if it's only on console, probably not, but I'm too lazy to check because it's so random.
 
Last edited:

Punkline

Dr. Frankenstack
Joined
May 15, 2015
Messages
423
That works great! But when I did some more testing on console last night, regular Up-Bs would crash. I have a few ledge related codes in my build, but none of them should have had any effect during a regular Up-B ledgegrab. It's probably because I have too many codes, but I wouldn't know how to check which ones are causing it.

Edit: after some more testing on console, it looks like it happens at random and with any kind of ledgegrab (Up-Bs, Side-Bs and tethers), meaning that if I were to remove some codes to see why it happens, it would take me forever to find which code is causing the crashes. I don't even know if it's only on console, probably not, but I'm too lazy to check because it's so random.
My only hint for you is this:
If you can determine that (at the start of a match, before grabbing ANY ledges) the crash happens consistently on the left but not the right, or the right but not the left -- then it would be indicative of a problem similar to the previous code.
 

UnclePunch

Smash Ace
Joined
Nov 9, 2014
Messages
673
A subtle bug in that side-B code becomes fatal once the cliffcatch action is delayed by this priority fix code.

Basically, that code inserts an up-B collision interrupt in the middle of the side-B collision interrupt -- causing the cliffcatch check to be made like normal, but the ECB record to be overwritten by the side-B interrupt’s conclusion. This wipes the cliffcatch flags, causing the delayed cliffcatch to have no record of whether to use the left or right vertex when referencing the last cliff face, and erroneous data is referenced for the resulting cliffcatch action.

To explain the teleporting, the game actually stores separate references for a left and a right ledge in each player's ECB data. This means that if it references the left when it should be right -- then it will use the previous ledge from the opposite facing direction. This causes a teleport, or a crash if no ledge has been grabbed yet on the opposite side.


Here’s a modified version of the side-B code that doesn't change the order of operations, but combines the cliffcatch flags with the result of the side-B ECB flags:
Code:
-==-

Captain Falcon/Ganondorf - Can Grab Ledge on Side-B
Alternate version fixes ECB update bug
[Unclepunch, Punkline]
1.02 ------ 800e3e04 --- 4bf9df09 -> Branch
bl 0x800e4d7c
807E002C 88030824 7FC3F378 98010014
bl 0x80081D0C
88010014 809E002C 98040824 00000000
Code:
-==-
!
ASM - Captain Falcon/Ganondorf - Can Grab Ledge on Side-B
Alternate version fixes ECB update bug
[Unclepunch, Punkline]
1.02 ------ 800e3e04 --- 4bf9df09 -> Branch
# intercepting ECB update call in CF/GD side-b collision interrupt
# up-b collision interrupt will update cliffcatch flags
# side-b collision interrupt will clobber them
# - this code preserves the cliffcatch flags after they've been clobbered

bl 0x800e4d7c
# CF/GD up-B collision interrupt

lwz    r3, 0x002C(r30)
lbz r0, 0x824(r3)
mr  r3, r30
stb r0, 0x14(sp)
# 0x14(sp) stores ECB cliffcatch record

bl 0x80081D0C
# unk ECB update used in side-B collision interrupt
# r3 = return value

lbz r0, 0x14(sp)
lwz r4, 0x2C(r30)
stb r0, 0x824(r4)
# recover cliffcatch record from stack

_return:
.long 0
my b, i didnt know anything about collision code in 2017
 

Punkline

Dr. Frankenstack
Joined
May 15, 2015
Messages
423
my b, i didnt know anything about collision code in 2017
I would have probably written it similarly, as I didn’t know about these ECB flags until writing this code. Having them unflagged seems to be inconsequential in vanilla Melee; it’s just when you try to make something rely on the flag data when it messes up.

I've been terrible about taking notes lately, but I did manage to scribble down some info about these flags:
Code:
0x824    W    ECB flags:
0x824    (02)    grabbable ledge on left
0x824    (01)    grabbable ledge on right
0x826    (80)    touching floor
0x826    (08)    touching wall on left
0x827    (40)    touching wall on left (second flag)
0x827    (20)    touching wall on right
0x827    (01)    touching wall on right (second flag)
 

UnclePunch

Smash Ace
Joined
Nov 9, 2014
Messages
673
I would have probably written it similarly, as I didn’t know about these ECB flags until writing this code. Having them unflagged seems to be inconsequential in vanilla Melee; it’s just when you try to make something rely on the flag data when it messes up.

I've been terrible about taking notes lately, but I did manage to scribble down some info about these flags:
Code:
0x824    W    ECB flags:
0x824    (02)    grabbable ledge on left
0x824    (01)    grabbable ledge on right
0x826    (80)    touching floor
0x826    (08)    touching wall on left
0x827    (40)    touching wall on left (second flag)
0x827    (20)    touching wall on right
0x827    (01)    touching wall on right (second flag)
nice, only thing i had that you didn't was

Word @ 0x824
Bit 0x4000 = Touching Ceiling Flag
 
Last edited:
Top Bottom