Initial Void Bastion 0.20 prototype
This commit is contained in:
commit
7362ca0f02
251 files changed
+29688
No files matched your search
@@ -0,0 +1,243 @@
|
||||
@tool
|
||||
extends RefCounted
|
||||
|
||||
# input_map.add_action — register a new input action in project.godot.
|
||||
# Params: {name, deadzone?: 0.5}
|
||||
static func add_action(params: Dictionary) -> Dictionary:
|
||||
var action_name: String = params.get("name", "")
|
||||
if action_name == "":
|
||||
return _err(-32602, "missing 'name'")
|
||||
var deadzone: float = float(params.get("deadzone", 0.5))
|
||||
var key := "input/" + action_name
|
||||
if ProjectSettings.has_setting(key):
|
||||
return _err(-32602, "action already exists: %s" % action_name)
|
||||
ProjectSettings.set_setting(key, {
|
||||
"deadzone": deadzone,
|
||||
"events": [],
|
||||
})
|
||||
var err := ProjectSettings.save()
|
||||
if err != OK:
|
||||
return _err(-32001, "save failed: %d" % err)
|
||||
# Keep the runtime InputMap in sync with the on-disk project settings —
|
||||
# ProjectSettings.save() only writes the file; it doesn't register actions
|
||||
# with InputMap until editor restart.
|
||||
if not InputMap.has_action(action_name):
|
||||
InputMap.add_action(action_name, deadzone)
|
||||
return _ok({"added": action_name})
|
||||
|
||||
|
||||
# input_map.add_event — attach an input event to an existing action.
|
||||
# Supported event shapes (pick one via 'type'):
|
||||
# {type: "key", keycode: "A" | "Space" | "F1" ..., physical?: true}
|
||||
# {type: "mouse_button", button_index: 1 (left) | 2 (right) | 3 (middle)}
|
||||
# {type: "joy_button", button_index: 0..}
|
||||
# Params: {action, event: <shape above>}
|
||||
static func add_event(params: Dictionary) -> Dictionary:
|
||||
var action_name: String = params.get("action", "")
|
||||
var event_spec: Dictionary = params.get("event", {})
|
||||
if action_name == "":
|
||||
return _err(-32602, "missing 'action'")
|
||||
if event_spec.is_empty():
|
||||
return _err(-32602, "missing 'event'")
|
||||
var key := "input/" + action_name
|
||||
if not ProjectSettings.has_setting(key):
|
||||
return _err(-32001, "action not found: %s" % action_name)
|
||||
|
||||
var event := _build_event(event_spec)
|
||||
if event == null:
|
||||
return _err(-32602, "invalid event spec — see tool description for supported shapes")
|
||||
|
||||
var current: Dictionary = ProjectSettings.get_setting(key)
|
||||
var events_arr: Array = current.get("events", [])
|
||||
events_arr.append(event)
|
||||
current["events"] = events_arr
|
||||
ProjectSettings.set_setting(key, current)
|
||||
var err := ProjectSettings.save()
|
||||
if err != OK:
|
||||
return _err(-32001, "save failed: %d" % err)
|
||||
# Sync InputMap so Input.is_action_pressed() etc. see the event without restart.
|
||||
if InputMap.has_action(action_name):
|
||||
InputMap.action_add_event(action_name, event)
|
||||
return _ok({"action": action_name, "event": event_spec})
|
||||
|
||||
|
||||
# input_map.list — return registered input actions with their events.
|
||||
# Params: {include_builtins?: false}
|
||||
# Defaults to user-defined only (parses the [input] section of project.godot,
|
||||
# which contains only user overrides — Godot's ~90 ui_* builtins live in-memory).
|
||||
# When include_builtins, falls back to InputMap which has the full union.
|
||||
static func list_actions(params: Dictionary) -> Dictionary:
|
||||
var include_builtins: bool = params.get("include_builtins", false)
|
||||
var items: Array = []
|
||||
|
||||
if include_builtins:
|
||||
for a in InputMap.get_actions():
|
||||
var described: Array = []
|
||||
for e in InputMap.action_get_events(a):
|
||||
described.append(_describe_event(e))
|
||||
items.append({
|
||||
"name": String(a),
|
||||
"deadzone": InputMap.action_get_deadzone(a),
|
||||
"events": described,
|
||||
})
|
||||
else:
|
||||
# Read straight from project.godot so newly-added actions show up even
|
||||
# if they haven't been registered with the runtime InputMap yet.
|
||||
var cf := ConfigFile.new()
|
||||
var err := cf.load("res://project.godot")
|
||||
if err == OK and cf.has_section("input"):
|
||||
for action_name in cf.get_section_keys("input"):
|
||||
var value: Dictionary = cf.get_value("input", action_name)
|
||||
var described: Array = []
|
||||
for e in value.get("events", []):
|
||||
described.append(_describe_event(e))
|
||||
items.append({
|
||||
"name": action_name,
|
||||
"deadzone": value.get("deadzone", 0.5),
|
||||
"events": described,
|
||||
})
|
||||
|
||||
return _ok({"actions": items})
|
||||
|
||||
|
||||
# input_map.remove_event — remove an event from an action by index.
|
||||
# Call input_map.list first to see indices; events are listed in the order they
|
||||
# were added.
|
||||
# Params: {action, event_index}
|
||||
static func remove_event(params: Dictionary) -> Dictionary:
|
||||
var action_name: String = params.get("action", "")
|
||||
if action_name == "":
|
||||
return _err(-32602, "missing 'action'")
|
||||
if not params.has("event_index"):
|
||||
return _err(-32602, "missing 'event_index'")
|
||||
var idx: int = int(params.event_index)
|
||||
var key := "input/" + action_name
|
||||
if not ProjectSettings.has_setting(key):
|
||||
return _err(-32001, "action not found: %s" % action_name)
|
||||
var current: Dictionary = ProjectSettings.get_setting(key)
|
||||
var events_arr: Array = current.get("events", [])
|
||||
if idx < 0 or idx >= events_arr.size():
|
||||
return _err(-32602, "event_index %d out of range (0..%d)" % [idx, events_arr.size() - 1])
|
||||
var removed_event = events_arr[idx]
|
||||
events_arr.remove_at(idx)
|
||||
current["events"] = events_arr
|
||||
ProjectSettings.set_setting(key, current)
|
||||
var err := ProjectSettings.save()
|
||||
if err != OK:
|
||||
return _err(-32001, "save failed: %d" % err)
|
||||
if InputMap.has_action(action_name) and removed_event != null:
|
||||
InputMap.action_erase_event(action_name, removed_event)
|
||||
return _ok({"action": action_name, "removed_index": idx})
|
||||
|
||||
|
||||
# input_map.remove_action — delete a user-registered action.
|
||||
static func remove_action(params: Dictionary) -> Dictionary:
|
||||
var action_name: String = params.get("name", "")
|
||||
if action_name == "":
|
||||
return _err(-32602, "missing 'name'")
|
||||
var key := "input/" + action_name
|
||||
if not ProjectSettings.has_setting(key):
|
||||
return _err(-32001, "action not found: %s" % action_name)
|
||||
ProjectSettings.clear(key)
|
||||
var err := ProjectSettings.save()
|
||||
if err != OK:
|
||||
return _err(-32001, "save failed: %d" % err)
|
||||
if InputMap.has_action(action_name):
|
||||
InputMap.erase_action(action_name)
|
||||
return _ok({"removed": action_name})
|
||||
|
||||
|
||||
static func _build_event(spec: Dictionary) -> InputEvent:
|
||||
# device defaults to -1 (ALL_DEVICES) matching the editor's "All Devices" default.
|
||||
# For local multiplayer bind device=0, device=1, etc. to distinguish controllers.
|
||||
var device: int = int(spec.get("device", -1))
|
||||
match spec.get("type", ""):
|
||||
"key":
|
||||
var e := InputEventKey.new()
|
||||
e.device = device
|
||||
var kc_raw = spec.get("keycode", "")
|
||||
var keycode := 0
|
||||
if kc_raw is int:
|
||||
keycode = kc_raw
|
||||
elif kc_raw is String:
|
||||
keycode = OS.find_keycode_from_string(kc_raw)
|
||||
if keycode == 0:
|
||||
return null
|
||||
if spec.get("physical", true):
|
||||
e.physical_keycode = keycode
|
||||
else:
|
||||
e.keycode = keycode
|
||||
return e
|
||||
"mouse_button":
|
||||
var e := InputEventMouseButton.new()
|
||||
e.device = device
|
||||
e.button_index = int(spec.get("button_index", 1))
|
||||
return e
|
||||
"joy_button":
|
||||
var e := InputEventJoypadButton.new()
|
||||
e.device = device
|
||||
e.button_index = int(spec.get("button_index", 0))
|
||||
return e
|
||||
"joy_motion":
|
||||
# axis accepts int 0..5 or one of:
|
||||
# "left_x" / "left_y" / "right_x" / "right_y" / "trigger_left" / "trigger_right"
|
||||
# axis_value: -1.0 (full negative) to 1.0 (full positive); sign picks the direction
|
||||
# that triggers the action.
|
||||
var e := InputEventJoypadMotion.new()
|
||||
e.device = device
|
||||
e.axis = _coerce_axis(spec.get("axis", 0))
|
||||
e.axis_value = float(spec.get("axis_value", 1.0))
|
||||
return e
|
||||
_:
|
||||
return null
|
||||
|
||||
|
||||
static func _coerce_axis(v) -> int:
|
||||
if v is int:
|
||||
return v
|
||||
if v is String:
|
||||
match v:
|
||||
"left_x":
|
||||
return JOY_AXIS_LEFT_X
|
||||
"left_y":
|
||||
return JOY_AXIS_LEFT_Y
|
||||
"right_x":
|
||||
return JOY_AXIS_RIGHT_X
|
||||
"right_y":
|
||||
return JOY_AXIS_RIGHT_Y
|
||||
"trigger_left":
|
||||
return JOY_AXIS_TRIGGER_LEFT
|
||||
"trigger_right":
|
||||
return JOY_AXIS_TRIGGER_RIGHT
|
||||
return 0
|
||||
|
||||
|
||||
static func _describe_event(e) -> Dictionary:
|
||||
if e is InputEventKey:
|
||||
var kc: int = e.physical_keycode if e.physical_keycode != 0 else e.keycode
|
||||
return {
|
||||
"type": "key",
|
||||
"keycode": OS.get_keycode_string(kc),
|
||||
"physical": e.physical_keycode != 0,
|
||||
"device": e.device,
|
||||
}
|
||||
if e is InputEventMouseButton:
|
||||
return {"type": "mouse_button", "button_index": e.button_index, "device": e.device}
|
||||
if e is InputEventJoypadButton:
|
||||
return {"type": "joy_button", "button_index": e.button_index, "device": e.device}
|
||||
if e is InputEventJoypadMotion:
|
||||
return {
|
||||
"type": "joy_motion",
|
||||
"axis": e.axis,
|
||||
"axis_value": e.axis_value,
|
||||
"device": e.device,
|
||||
}
|
||||
return {"type": "unknown", "class": (e as Object).get_class() if e else "null"}
|
||||
|
||||
|
||||
static func _ok(data) -> Dictionary:
|
||||
return {"data": data}
|
||||
|
||||
|
||||
static func _err(code: int, msg: String) -> Dictionary:
|
||||
return {"error": {"code": code, "message": msg}}
|
||||
Reference in new issue
Block a user