Added Glitch Room Source code to Gitty
This commit is contained in:
commit
1ac9eb5644
505 files changed
+28504
No files matched your search
@@ -0,0 +1,174 @@
|
||||
extends Node
|
||||
signal changed
|
||||
## Machine-local bindings; joypads are resolved by identity, never "All Devices".
|
||||
const FILE := "user://arcade_controls.cfg"
|
||||
const STEPS := [
|
||||
["arcade_start", "Press PLAYER ONE START"],
|
||||
["arcade_menu", "Press PLAYER ONE SELECT / RETURN TO MENU"],
|
||||
["arcade_up", "Move PLAYER ONE joystick UP"],
|
||||
["arcade_down", "Move PLAYER ONE joystick DOWN"],
|
||||
["arcade_left", "Move PLAYER ONE joystick LEFT"],
|
||||
["arcade_right", "Move PLAYER ONE joystick RIGHT"],
|
||||
["arcade_action_1", "Press FIRE / LEFT MISSILE BATTERY"],
|
||||
["arcade_action_2", "Press SUPERZAPPER / CENTER MISSILE BATTERY"],
|
||||
["arcade_action_3", "Press RIGHT MISSILE BATTERY"],
|
||||
["robotron_fire_up", "Choose ROBOTRON FIRE UP — button or second joystick"],
|
||||
["robotron_fire_down", "Choose ROBOTRON FIRE DOWN — button or second joystick"],
|
||||
["robotron_fire_left", "Choose ROBOTRON FIRE LEFT — button or second joystick"],
|
||||
["robotron_fire_right", "Choose ROBOTRON FIRE RIGHT — button or second joystick"],
|
||||
]
|
||||
var bindings: Dictionary = {}
|
||||
var problem := ""
|
||||
var path := FILE
|
||||
|
||||
func _ready() -> void:
|
||||
process_mode = Node.PROCESS_MODE_ALWAYS
|
||||
load_profile()
|
||||
Input.joy_connection_changed.connect(_devices_changed)
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
handle_quit(event)
|
||||
|
||||
func handle_quit(event: InputEvent) -> bool:
|
||||
if event is InputEventKey and event.pressed and not event.echo and (event.physical_keycode == KEY_ESCAPE or event.keycode == KEY_ESCAPE):
|
||||
get_viewport().set_input_as_handled()
|
||||
get_tree().quit()
|
||||
return true
|
||||
return false
|
||||
|
||||
func devices() -> Array:
|
||||
var found: Array = []
|
||||
for id in Input.get_connected_joypads():
|
||||
found.append({"id": id, "guid": Input.get_joy_guid(id), "name": Input.get_joy_name(id)})
|
||||
return found
|
||||
|
||||
func matches(identity: Dictionary, connected: Array) -> Array:
|
||||
return connected.filter(func(d): return d.guid == identity.guid and d.name == identity.name)
|
||||
|
||||
func encode(event: InputEvent, connected: Array) -> Dictionary:
|
||||
var result := {}
|
||||
if event is InputEventKey:
|
||||
if event.physical_keycode in [KEY_ESCAPE, KEY_F1]: return {}
|
||||
result = {"kind": "key", "code": event.physical_keycode, "logical": event.keycode if event.physical_keycode == 0 else 0}
|
||||
elif event is InputEventJoypadButton:
|
||||
result = {"kind": "button", "code": event.button_index}
|
||||
elif event is InputEventJoypadMotion:
|
||||
if absf(event.axis_value) < .65: return {}
|
||||
result = {"kind": "axis", "code": event.axis, "sign": -1 if event.axis_value < 0 else 1}
|
||||
elif event is InputEventMouseButton:
|
||||
if event.button_index > MOUSE_BUTTON_MIDDLE: return {}
|
||||
return {"kind": "mouse", "code": event.button_index}
|
||||
else:
|
||||
return {}
|
||||
if event is InputEventJoypadButton or event is InputEventJoypadMotion:
|
||||
var current := connected.filter(func(d): return d.id == event.device)
|
||||
if current.is_empty(): return {}
|
||||
var identity: Dictionary = current[0].duplicate()
|
||||
var same := matches(identity, connected)
|
||||
identity["slot"] = same.find(current[0])
|
||||
identity["count"] = same.size()
|
||||
identity.erase("id")
|
||||
result["controller"] = identity
|
||||
return result
|
||||
|
||||
func resolve(binding: Dictionary, connected: Array) -> int:
|
||||
var identity: Dictionary = binding.controller
|
||||
var same := matches(identity, connected)
|
||||
# An absent twin must not silently hand its controls to the remaining twin.
|
||||
if same.size() != int(identity.count): return -1
|
||||
return int(same[int(identity.slot)].id) if int(identity.slot) < same.size() else -1
|
||||
|
||||
func decode(binding: Dictionary, connected: Array) -> InputEvent:
|
||||
var event: InputEvent
|
||||
match binding.kind:
|
||||
"key":
|
||||
event = InputEventKey.new()
|
||||
event.physical_keycode = int(binding.code)
|
||||
event.keycode = int(binding.get("logical", 0))
|
||||
"mouse":
|
||||
event = InputEventMouseButton.new()
|
||||
event.button_index = int(binding.code)
|
||||
"button":
|
||||
event = InputEventJoypadButton.new()
|
||||
event.button_index = int(binding.code)
|
||||
"axis":
|
||||
event = InputEventJoypadMotion.new()
|
||||
event.axis = int(binding.code)
|
||||
event.axis_value = float(binding.sign)
|
||||
_:
|
||||
return null
|
||||
if binding.has("controller"):
|
||||
var id := resolve(binding, connected)
|
||||
if id < 0: return null
|
||||
event.device = id
|
||||
return event
|
||||
|
||||
func valid(data: Dictionary) -> bool:
|
||||
for step in STEPS:
|
||||
if not data.get(step[0]) is Dictionary: return false
|
||||
var b: Dictionary = data[step[0]]
|
||||
if b.get("kind", "") not in ["key", "mouse", "button", "axis"]: return false
|
||||
if not b.get("code") is int or b.code < 0: return false
|
||||
if b.kind == "axis" and b.get("sign", 0) not in [-1, 1]: return false
|
||||
if b.kind in ["axis", "button"]:
|
||||
if not b.get("controller") is Dictionary: return false
|
||||
var c: Dictionary = b.controller
|
||||
if not c.get("guid") is String or not c.get("name") is String: return false
|
||||
if not c.get("slot") is int or not c.get("count") is int: return false
|
||||
if c.slot < 0 or c.count <= c.slot: return false
|
||||
return true
|
||||
|
||||
func load_profile() -> bool:
|
||||
var config := ConfigFile.new()
|
||||
if config.load(path) != OK: return false
|
||||
var data = config.get_value("controls", "bindings", {})
|
||||
if config.get_value("controls", "version", 0) != 1 or not data is Dictionary or not valid(data):
|
||||
problem = "Saved controls could not be read. Please run setup again."
|
||||
return false
|
||||
bindings = data
|
||||
apply_profile()
|
||||
return true
|
||||
|
||||
func save_profile(data: Dictionary) -> Error:
|
||||
if not valid(data): return ERR_INVALID_DATA
|
||||
var config := ConfigFile.new()
|
||||
config.set_value("controls", "version", 1)
|
||||
config.set_value("controls", "bindings", data)
|
||||
var error := config.save(path + ".tmp")
|
||||
if error != OK: return error
|
||||
error = DirAccess.rename_absolute(ProjectSettings.globalize_path(path + ".tmp"), ProjectSettings.globalize_path(path))
|
||||
if error != OK: return error
|
||||
bindings = data.duplicate(true)
|
||||
apply_profile()
|
||||
return OK
|
||||
|
||||
func apply_profile() -> void:
|
||||
if bindings.is_empty(): return
|
||||
problem = ""
|
||||
var connected := devices()
|
||||
for action in bindings:
|
||||
var event := decode(bindings[action], connected)
|
||||
replace_action(action, event)
|
||||
if action in ["arcade_up", "arcade_down", "arcade_left", "arcade_right"]:
|
||||
replace_action(action.replace("arcade_", "robotron_move_"), event)
|
||||
if event == null:
|
||||
problem = "A saved controller is missing or the controller order changed. Reconnect it or run Control Setup."
|
||||
elif bindings[action].has("controller") and int(bindings[action].controller.count) > 1 and problem.is_empty():
|
||||
problem = "Identical controllers detected: check PLAYER ONE after reconnecting USB devices. Run setup if they swap."
|
||||
changed.emit()
|
||||
|
||||
func replace_action(action: String, event: InputEvent) -> void:
|
||||
Input.action_release(action)
|
||||
InputMap.action_erase_events(action)
|
||||
InputMap.action_set_deadzone(action, .35)
|
||||
if event != null: InputMap.action_add_event(action, event)
|
||||
|
||||
func _devices_changed(_device: int, _connected: bool) -> void:
|
||||
apply_profile.call_deferred()
|
||||
|
||||
func describe(binding: Dictionary) -> String:
|
||||
var event := decode(binding, devices())
|
||||
var text := event.as_text() if event != null else "%s %s (not connected)" % [binding.kind, binding.code]
|
||||
if binding.has("controller"):
|
||||
text += " — %s #%d" % [binding.controller.name, int(binding.controller.slot) + 1]
|
||||
return text
|
||||
Reference in new issue
Block a user