// Bindings - Action map, rebinding, and icon lookup var io = use('cellfs') var json = use('json') // Controller icon map var controller_map = { ps3: 'playstation', ps4: 'playstation', ps5: 'playstation', xbox360: 'xbox360', xboxone: 'xboxone', standard: 'standard', gamecube: 'gamecube', switchpro: 'switchpro', joyconleft: 'joyconleft', joyconright: 'joyconright', joyconpair: 'joyconpair' } function make(defaults, display_names) { defaults = defaults || {} display_names = display_names || {} var bindings = { action_map: {}, _defaults: {}, display_names: display_names, is_rebinding: false, rebind_target: null } // Copy defaults arrfor(array(defaults), function(key){ var val = defaults[key] bindings.action_map[key] = is_array(val) ? array(val) : [val] bindings._defaults[key] = array(bindings.action_map[key]) }) // Get actions that match a control bindings.get_actions = function(control) { return filter(array(this.action_map), action => find(this.action_map[action], control) != null) } // Get bindings for a specific device kind bindings.get_bindings_for_device = function(action, device_kind) { var all = this.action_map[action] || [] return filter(all, control => { if (device_kind == 'keyboard') { return !starts_with(control, 'gamepad_') && !starts_with(control, 'swipe_') && !starts_with(control, 'touch') } if (device_kind == 'gamepad') { return starts_with(control, 'gamepad_') } if (device_kind == 'touch') { return starts_with(control, 'swipe_') || starts_with(control, 'touch') } return true }) } // Get primary binding for display bindings.get_primary_binding = function(action, device_kind) { var device_bindings = this.get_bindings_for_device(action, device_kind) if (length(device_bindings)) return device_bindings[0] var all = this.action_map[action] || [] return length(all) > 0 ? all[0] : null } // Get icon for action bindings.get_icon_for_action = function(action, device_kind, gamepad_type) { var binding = this.get_primary_binding(action, device_kind) if (!binding) return null if (device_kind == 'keyboard') { if (starts_with(binding, 'mouse_button_')) { var button = replace(binding, 'mouse_button_', '') return 'ui/mouse/mouse_' + button + '.png' } else { var key_map = { 'escape': 'escape', 'return': 'return', 'space': 'space', 'up': 'arrow_up', 'down': 'arrow_down', 'left': 'arrow_left', 'right': 'arrow_right' } var key = key_map[binding] || binding return 'ui/keyboard/keyboard_' + key + '.png' } } if (device_kind == 'gamepad' && gamepad_type) { var prefix = controller_map[gamepad_type] || 'playstation' var is_ps = find(['ps3', 'ps4', 'ps5'], gamepad_type) != null var gamepad_map = { 'gamepad_a': is_ps ? 'playstation_button_cross' : 'xbox_button_a', 'gamepad_b': is_ps ? 'playstation_button_circle' : 'xbox_button_b', 'gamepad_x': is_ps ? 'playstation_button_square' : 'xbox_button_x', 'gamepad_y': is_ps ? 'playstation_button_triangle' : 'xbox_button_y', 'gamepad_dpup': prefix + '_dpad_up', 'gamepad_dpdown': prefix + '_dpad_down', 'gamepad_dpleft': prefix + '_dpad_left', 'gamepad_dpright': prefix + '_dpad_right', 'gamepad_l1': prefix + '_trigger_l1', 'gamepad_r1': prefix + '_trigger_r1', 'gamepad_l2': prefix + '_trigger_l2', 'gamepad_r2': prefix + '_trigger_r2', 'gamepad_start': this._get_start_icon(gamepad_type) } var icon = gamepad_map[binding] if (icon) return 'ui/' + prefix + '/' + icon + '.png' } return null } bindings._get_start_icon = function(gamepad_type) { if (gamepad_type == 'ps3') return 'playstation3_button_start' if (gamepad_type == 'ps4') return 'playstation4_button_options' if (gamepad_type == 'ps5') return 'playstation5_button_options' return 'xbox_button_start' } // Start rebinding bindings.start_rebind = function(action) { if (!this.action_map[action]) return false this.is_rebinding = true this.rebind_target = action return true } // Complete rebinding with new control bindings.rebind = function(action, new_control, device_kind) { if (!this.action_map[action]) return false // Remove from other actions arrfor(array(this.action_map), act => { var idx = search(this.action_map[act], new_control) if (idx >= 0) this.action_map[act] = array(array(this.action_map[act], 0, idx), array(this.action_map[act], idx+1)) }) // Clear existing bindings for this device kind var target = this.action_map[action] for (var i = length(target) - 1; i >= 0; i--) { var existing_kind = starts_with(target[i], 'gamepad_') ? 'gamepad' : starts_with(target[i], 'swipe_') ? 'touch' : 'keyboard' if (existing_kind == device_kind) this.action_map[action] = array(array(this.action_map[action], 0, i), array(this.action_map[action], i+1)) } // Add new binding this.action_map[action].unshift(new_control) this.is_rebinding = false this.rebind_target = null return true } // Cancel rebinding bindings.cancel_rebind = function() { this.is_rebinding = false this.rebind_target = null } // Save bindings bindings.save = function(path) { path = path || 'keybindings.json' try { io.slurpwrite(path, json.encode(this.action_map)) return true } catch(e) { return false } } // Load bindings bindings.load = function(path) { path = path || 'keybindings.json' try { if (io.exists(path)) { var data = json.decode(io.slurp(path)) arrfor(array(data), key => { if (this.action_map[key]) this.action_map[key] = data[key] }) return true } } catch(e) {} return false } // Reset to defaults bindings.reset = function() { arrfor(array(this._defaults), key => { this.action_map[key] = array(this._defaults[key]) }) } return bindings } return { make: make }