Skip to content

Instantly share code, notes, and snippets.

@rapidcow
Last active January 21, 2023 11:00
Show Gist options
  • Save rapidcow/884b9c3c33ece11e080f76e9a2db8ec1 to your computer and use it in GitHub Desktop.
Save rapidcow/884b9c3c33ece11e080f76e9a2db8ec1 to your computer and use it in GitHub Desktop.
my x360ce mapping for playing repentance co-op

tutorial

a tiny explanation for what this thing is for...

so if you and another person both own BOI then you probably won't need this. but if you own BOI and the other person don't (or you have a controller and unlike me you know how to use one O_O) then... well you're probably in the right place, because i am going to teach you how i did it! (which might not be the easiest, but it worked for me :)

in short, there are three steps:

  1. turn on Family Library Sharing for the account without BOI to play
  2. download a controller emulator (i used x360ce) and map keyboard keys to controller keys (the main reason why i am posting this gist)
  3. in BOI, start a new run without leaving the first room, then invite your friend to join with the controller emulator

the library sharing worked out for me by doing something like this:

  1. make sure that the account that owns BOI is logged in (and maybe make sure that Settings > Family > Family Library Sharing is ticked?)
  2. log in the account with no BOI, open the BOI menu, and note that the usual green "Play" button is replaced by a blue "Borrow" button. press it and then request access somehow and then log out
  3. log in the account with BOI again, then go to Family Library Sharing and tick the checkbox with the other account's name
  4. now you should be log into the account without BOI and play!

next the emulator:

  1. download x360ce from somewhere (maybe here) and run the exe file anywhere you want
  2. make sure the Issues tab shows no warning (it told me to install something so i did and then the issue went away so ig yay)
  3. now click "Controller 1" and press the "Add" button (see the top right red circle below). it should let you select the input device so select your keyboard for that
  4. now copy one of the key mappings from the next section (or write your own one) and save it as x360map.conf (it's not really a conf file but i thought it sounded cooler than .txt1)
  5. run python x360map.py and copy the output
  6. press the "Paste" button (the bottom right red circle)

for those who are unfamiliar with the buttons, this might help you (as it definitely did help me!) i followed the naming convention x360ce uses, so names that you write in x360ce.conf will have to be precise. namely:

  • left bumper (LB) and right bumper (RB) would be LeftShoulder and RightShoulder
  • left trigger (LT) and right trigger (RT) would be LeftTrigger and RightTrigger
  • for the YAXB buttons, D-Pad, and the Start and Back buttons though, the names should be straightforward

now you should be good to go to play on one computer! test it out by starting a new run and pressing the ButtonStart button (without leaving the room that is). a tiny icon should appear on the top right corner! use DPadLeft and DPadRight to cycle through characters, and then press ButtonA (shoot down) to select it.

to play on two computers, you may use Steam Remote Play. again, i did something like this:

  1. in game (the keyboard player), open Steam overlay by pressing Shift+Tab
  2. right click on the other account's name (the controller player) and press invite to remote play (i don't remember what it was called)
  3. on the other account, accept the invitation by pressing the buttons mapped to the controller only (it might ask you to confirm by pressing ButtonA (or shoot down)
  4. press ButtonStart and you are good to go! cycle between characters using DPadLeft and DPadRight (the move left and right keys), and confirm your selection by pressing ButtonA again

key mappings

a few notes on what to put in x360map.conf:

  • each line takes the form of BUTTON then KEY, where the button and key may be separated by whitespace

  • BUTTON should be recognized by x360ce; i don't know the full list, but at least ones like DPadLeft and ButtonStart are valid on my side

  • the recognized KEY values are listed in KEY_TO_CODE:

    >>> print(''.join(k for k in KEY_TO_CODE.keys() if len(k) < 2),
    ...       'and',
    ...       ', '.join(k for k in KEY_TO_CODE.keys() if len(k) > 2))
    `1234567890-=QWERTYUIOP[]\ASDFGHJKL:'ZXCVBNM,./ and Space, ArrowUp, ArrowLeft, ArrowRight, ArrowDown
    
  • the D-Pad buttons control Isaac's movement, while YAXB buttons control Isaac's tears. here's a diagram that might help:

               MOVE                        SHOOT
    ---------------------------   -----------------------
              DPadUp                      ButtonY
    DPadLeft          DPadRight   ButtonX         ButtonB
             DPadDown                     ButtonA
    

    (also Button A also seems to double as a confirm button? Button B and Button X act like "exit" and "select" too... i don't know)

  • bomb with LeftShoulder, use item with LeftTrigger, use pill/card/rune with RightShoulder, and finally drop by holding RightTrigger

  • ButtonBack opens the map, and ButtonStart pauses the game (and serves as confirming to join as a second player)

  • (any of these might not be accurate if you configured the game's control otherwise---be sure to check it!)

also be mindful of overriding F, P, R, M since they have different functions (full screen, pause game, the R Key, mute). you might also want to reserve ` or ' for launching the debug console and L for the mod config menu

aight enough said here's one config i made that would most resemble bindings on a keyboard, the only difference being Z to drop and [ for map (you can save these as x360ce.conf and run x360map.py and stuff as i mentioned above)

# move (up down left right)
DPadUp          W
DPadDown        S
DPadLeft        A
DPadRight       D

# shoot (up down left right)
ButtonY         ArrowUp
ButtonA         ArrowDown
ButtonX         ArrowLeft
ButtonB         ArrowRight

# bomb (LB)
LeftShoulder    E
# item (LT)
LeftTrigger     Space
# pill (RB)
RightShoulder   Q
# drop (RT)
RightTrigger    Z

# map
ButtonBack      [
# pause / confirm join
ButtonStart     ]

if you want to play on a single keyboard then it is probably not a good idea to override WASD. in that case...

# move (up down left right)
DPadUp          Y
DPadDown        H
DPadLeft        G
DPadRight       J

# shoot (up down left right)
ButtonY         L
ButtonA         .
ButtonX         ,
ButtonB         /

# bomb (LB)
LeftShoulder    U
# item (LT)
LeftTrigger     N
# pill (RB)
RightShoulder   T
# drop (RT)
RightTrigger    B

# map
ButtonBack      [
# pause / confirm join
ButtonStart     ]

essentially i shifted WASD/Q/E/Z (drop)/spacebar to YGHJ/T/U/B/N and shifted the arrow keys to L,./... yeah, it's weird, but it helps to test things out by yourself.

Footnotes

  1. oh, and also, cool syntax highlighting

"""generate controller mapping XML for x360ce.exe"""
# map key press to x360ce's code? (i tested these out myself)
KEY_TO_CODE = {
# first row
'`': 41, '1': 2, '2': 3, '3': 4, '4': 5, '5': 6,
'6': 7, '7': 8, '8': 9, '9': 10, '0': 11, '-': 12,
'=': 13,
# second row
'Q': 16, 'W': 17, 'E': 18, 'R': 19, 'T': 20, 'Y': 21,
'U': 22, 'I': 23, 'O': 24, 'P': 25, '[': 26, ']': 27,
'\\': 43,
# third row
'A': 30, 'S': 31, 'D': 32, 'F': 33, 'G': 34, 'H': 35,
'J': 36, 'K': 37, 'L': 38, ':': 39, "'": 40,
# fourth row
'Z': 44, 'X': 45, 'C': 46, 'V': 47, 'B': 48, 'N': 49,
'M': 50, ',': 51, '.': 52, '/': 53,
'Space': 57,
# arrow keys
'ArrowUp': 105,
'ArrowLeft': 107,
'ArrowRight': 108,
'ArrowDown': 110,
}
# inverse mapping (not used)
CODE_TO_KEY = {v: k for k, v in KEY_TO_CODE.items()}
DEFAULTS = {
"GamePadType": 1,
"LeftMotorPeriod": 60,
"RightMotorPeriod": 120,
"ButtonADeadZone": 8192,
"ButtonBDeadZone": 8192,
"ButtonBackDeadZone": 8192,
"ButtonStartDeadZone": 8192,
"ButtonXDeadZone": 8192,
"ButtonYDeadZone": 8192,
"LeftThumbButtonDeadZone": 8192,
"RightThumbButtonDeadZone": 8192,
"LeftShoulderDeadZone": 8192,
"RightShoulderDeadZone": 8192,
"DPadDownDeadZone": 8192,
"DPadLeftDeadZone": 8192,
"DPadRightDeadZone": 8192,
"DPadUpDeadZone": 8192,
}
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(
description='a program that converts space-separated mapping to '
'XML that can be parsed by x360 controller emulator')
parser.add_argument('conf_path', default='x360map.conf',
help='path to the mapping configuration file')
args = parser.parse_args()
pad_setting = DEFAULTS.copy()
with open(args.conf_path, encoding='ascii') as fp:
for line in fp:
line = line.strip()
# ignore empty lines / comments
if not line or line.startswith('#'):
continue
button, key = line.split(maxsplit=1)
code = KEY_TO_CODE[key]
pad_setting[button] = code
print('<PadSetting>')
for button, code in pad_setting.items():
print(f' <{button}>{code}</{button}>')
print('</PadSetting>')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment