Harbor

branch main
showing the latest snapshot on main
actions.odin 3.7 KB · Plain text
platform/actions.odin 0644 Raw
package platform

import window "window:window"

MAX_ACTIONS :: 64
MAX_BINDINGS_PER_ACTION :: 4

Input_Source :: union {
	window.Key_Code,
	window.Mouse_Button,
}

Action_Binding :: struct {
	sources: [MAX_BINDINGS_PER_ACTION]Input_Source,
	count:   int,
	name:    string,
}

Action_State :: struct {
	bindings: [MAX_ACTIONS]Action_Binding,
	count:    int, // next ID to assign (1-based, 0 = invalid)
}

// Register a named action, returns its handle. Returns 0 if full.
register_action :: proc(state: ^Action_State, name: string) -> u32 {
	if state.count >= MAX_ACTIONS {
		return 0
	}
	state.count += 1
	id := state.count
	state.bindings[id].name = name
	return u32(id)
}

// Bind a key to an action. Silently ignored if bindings are full.
bind_action_key :: proc(state: ^Action_State, action: u32, key: window.Key_Code) {
	if action == 0 || int(action) > state.count {
		return
	}
	b := &state.bindings[action]
	if b.count >= MAX_BINDINGS_PER_ACTION {
		return
	}
	b.sources[b.count] = key
	b.count += 1
}

// Bind a mouse button to an action. Silently ignored if bindings are full.
bind_action_mouse :: proc(state: ^Action_State, action: u32, btn: window.Mouse_Button) {
	if action == 0 || int(action) > state.count {
		return
	}
	b := &state.bindings[action]
	if b.count >= MAX_BINDINGS_PER_ACTION {
		return
	}
	b.sources[b.count] = btn
	b.count += 1
}

// Clear all bindings for an action.
unbind_action :: proc(state: ^Action_State, action: u32) {
	if action == 0 || int(action) > state.count {
		return
	}
	b := &state.bindings[action]
	b.count = 0
	b.sources = {}
}

// Check if any binding for the action was pressed this frame.
is_action_pressed :: proc(pstate: ^Platform_State, action: u32) -> bool {
	if action == 0 || int(action) > pstate.actions.count {
		return false
	}
	b := &pstate.actions.bindings[action]
	for i in 0 ..< b.count {
		switch src in b.sources[i] {
		case window.Key_Code:
			if is_key_pressed(pstate, src) {
				return true
			}
		case window.Mouse_Button:
			if is_mouse_button_pressed(pstate, src) {
				return true
			}
		}
	}
	return false
}

// Check if any binding for the action is held down.
is_action_down :: proc(pstate: ^Platform_State, action: u32) -> bool {
	if action == 0 || int(action) > pstate.actions.count {
		return false
	}
	b := &pstate.actions.bindings[action]
	for i in 0 ..< b.count {
		switch src in b.sources[i] {
		case window.Key_Code:
			if is_key_down(pstate, src) {
				return true
			}
		case window.Mouse_Button:
			if is_mouse_button_down(pstate, src) {
				return true
			}
		}
	}
	return false
}

// Check if any binding for the action was released this frame.
is_action_released :: proc(pstate: ^Platform_State, action: u32) -> bool {
	if action == 0 || int(action) > pstate.actions.count {
		return false
	}
	b := &pstate.actions.bindings[action]
	for i in 0 ..< b.count {
		switch src in b.sources[i] {
		case window.Key_Code:
			if is_key_released(pstate, src) {
				return true
			}
		case window.Mouse_Button:
			// Mouse buttons don't have released tracking yet,
			// check via current/previous state
			idx := int(src)
			if idx >= 0 && idx < MAX_MOUSE_BUTTONS {
				if !pstate.input.mouse_current[idx] && pstate.input.mouse_previous[idx] {
					return true
				}
			}
		}
	}
	return false
}

// Get the name of an action.
get_action_name :: proc(state: ^Action_State, action: u32) -> string {
	if action == 0 || int(action) > state.count {
		return ""
	}
	return state.bindings[action].name
}

// Get active bindings for an action (for settings UI). Returns a slice into the fixed array.
get_action_bindings :: proc(state: ^Action_State, action: u32) -> []Input_Source {
	if action == 0 || int(action) > state.count {
		return {}
	}
	b := &state.bindings[action]
	return b.sources[:b.count]
}