mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-11 18:53:06 +00:00
SSInput and Diagonal Movement
This commit is contained in:
45
code/modules/keybindings/bindings_atom.dm
Normal file
45
code/modules/keybindings/bindings_atom.dm
Normal file
@@ -0,0 +1,45 @@
|
||||
// You might be wondering why this isn't client level. If focus is null, we don't want you to move.
|
||||
// Only way to do that is to tie the behavior into the focus's keyLoop().
|
||||
|
||||
// THE TRADITIONAL STYLE FROM /TG (modified)
|
||||
|
||||
/atom/movable/keyLoop(client/user)
|
||||
// Bail out if the user is holding the "face direction" key (Maybe?)
|
||||
// TODO - I think this breaks non-hotkeys WASD movement, so maybe adopt the /tg solution)
|
||||
if(user.mod_keys_held & CTRL_KEY)
|
||||
return
|
||||
|
||||
var/must_call_move = FALSE
|
||||
var/movement_dir = MOVEMENT_KEYS_TO_DIR(user.move_keys_held)
|
||||
if(user.next_move_dir_add)
|
||||
must_call_move = TRUE // So that next_move_dir_add gets cleared if its time.
|
||||
movement_dir |= user.next_move_dir_add
|
||||
if(user.next_move_dir_sub)
|
||||
DEBUG_INPUT("[(user.next_move_dir_sub & movement_dir) ? "Actually" : "Want to"] subtract [dirs2text(user.next_move_dir_sub)] from [dirs2text(movement_dir)]")
|
||||
must_call_move = TRUE // So that next_move_dir_sub gets cleared if its time.
|
||||
movement_dir &= ~user.next_move_dir_sub
|
||||
|
||||
// Sanity checks in case you hold left and right and up to make sure you only go up
|
||||
if((movement_dir & (NORTH|SOUTH)) == (NORTH|SOUTH))
|
||||
movement_dir &= ~(NORTH|SOUTH)
|
||||
if((movement_dir & (EAST|WEST)) == (EAST|WEST))
|
||||
movement_dir &= ~(EAST|WEST)
|
||||
|
||||
#ifdef CARDINAL_INPUT_ONLY
|
||||
if(movement_dir & user.last_move_dir_pressed)
|
||||
movement_dir = user.last_move_dir_pressed
|
||||
else if (movement_dir == NORTHEAST || movement_dir == NORTHWEST)
|
||||
DEBUG_INPUT("overriding to NORTH: movement_dir=[dirs2text(movement_dir)] last=[dirs2text(user.last_move_dir_pressed)]")
|
||||
movement_dir = NORTH
|
||||
else if (movement_dir == SOUTHEAST || movement_dir == SOUTHWEST)
|
||||
DEBUG_INPUT("overriding to SOUTH: movement_dir=[dirs2text(movement_dir)] last=[dirs2text(user.last_move_dir_pressed)]")
|
||||
movement_dir = SOUTH
|
||||
#endif
|
||||
|
||||
// Compensate for client camera spinning (client.dir) so our movement still makes sense I guess.
|
||||
if(movement_dir) // Only compensate if non-zero, as byond will auto-fill dir otherwise
|
||||
movement_dir = turn(movement_dir, -dir2angle(user.dir))
|
||||
|
||||
// Move, but only if we actually are planing to move, or we need to clear the next move vars
|
||||
if(movement_dir || must_call_move)
|
||||
user.Move(get_step(src, movement_dir), movement_dir)
|
||||
77
code/modules/keybindings/bindings_movekeys.dm
Normal file
77
code/modules/keybindings/bindings_movekeys.dm
Normal file
@@ -0,0 +1,77 @@
|
||||
// TODO - Optimize this into numerics if this ends up working out
|
||||
var/global/list/MOVE_KEY_MAPPINGS = list(
|
||||
"North" = NORTH_KEY,
|
||||
"South" = SOUTH_KEY,
|
||||
"East" = EAST_KEY,
|
||||
"West" = WEST_KEY,
|
||||
"W" = W_KEY,
|
||||
"A" = A_KEY,
|
||||
"S" = S_KEY,
|
||||
"D" = D_KEY,
|
||||
"Shift" = SHIFT_KEY,
|
||||
"Ctrl" = CTRL_KEY,
|
||||
"Alt" = ALT_KEY,
|
||||
)
|
||||
|
||||
// These verbs are called for all movemen key press and release events
|
||||
/client/verb/moveKeyDown(movekeyName as text)
|
||||
set instant = TRUE
|
||||
set hidden = TRUE
|
||||
// set name = ".movekeydown"
|
||||
set name = "KeyDown"
|
||||
|
||||
// Map text sent by skin.dmf to our numeric codes. (This can be optimized away once we update skin.dmf)
|
||||
var/movekey = MOVE_KEY_MAPPINGS[movekeyName]
|
||||
|
||||
// Validate input. Must be one (and only one) of the key codes)
|
||||
if(isnull(movekey) || (movekey & ~0xFFF) || (movekey & (movekey - 1)))
|
||||
log_debug("Client [ckey] sent an illegal movement key down: [movekeyName] ([movekey])")
|
||||
return
|
||||
|
||||
// Record that we are now holding the key!
|
||||
move_keys_held |= (movekey & 0x0FF)
|
||||
mod_keys_held |= (movekey & 0xF00)
|
||||
|
||||
// If we were NOT holding at the start of this move cycle and pressed it, remember that.
|
||||
var/movement = MOVEMENT_KEYS_TO_DIR(movekey)
|
||||
if(movement && !(next_move_dir_sub & movement) && !(mod_keys_held & CTRL_KEY)) // TODO-LESHANA - Possibly not holding Alt either
|
||||
DEBUG_INPUT("Saving [dirs2text(movement)] into next_move_dir_ADD")
|
||||
next_move_dir_add |= movement
|
||||
|
||||
#ifdef CARDINAL_INPUT_ONLY
|
||||
if(movement)
|
||||
DEBUG_INPUT("set last=[dirs2text(movement)]")
|
||||
last_move_dir_pressed = movement
|
||||
#endif
|
||||
|
||||
mob.focus?.key_down(movekey, src)
|
||||
|
||||
/client/verb/moveKeyUp(movekeyName as text)
|
||||
set instant = TRUE
|
||||
set hidden = TRUE
|
||||
// set name = ".movekeyup"
|
||||
set name = "KeyUp"
|
||||
|
||||
// Map text sent by skin.dmf to our numeric codes. (This can be optimized away once we update skin.dmf)
|
||||
var/movekey = MOVE_KEY_MAPPINGS[movekeyName]
|
||||
|
||||
// Validate input. Must be one (and only one) of the key codes)
|
||||
if(isnull(movekey) || (movekey & ~0xFFF) || (movekey & (movekey - 1)))
|
||||
log_debug("Client [ckey] sent an illegal movement key up: [movekeyName] ([movekey])")
|
||||
return
|
||||
|
||||
// Clear bit indicating we were holding the key
|
||||
move_keys_held &= ~movekey
|
||||
mod_keys_held &= ~movekey
|
||||
|
||||
// If we were holding at the start of this move cycle and now relased it, remember that.
|
||||
var/movement = MOVEMENT_KEYS_TO_DIR(movekey)
|
||||
if(movement && !(next_move_dir_add & movement))
|
||||
DEBUG_INPUT("Saving [dirs2text(movement)] into next_move_dir_SUB")
|
||||
next_move_dir_sub |= movement
|
||||
|
||||
mob.focus?.key_up(movekey, src)
|
||||
|
||||
// Called every game tick
|
||||
/client/keyLoop()
|
||||
mob.focus?.keyLoop(src)
|
||||
42
code/modules/keybindings/readme.md
Normal file
42
code/modules/keybindings/readme.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# In-code keypress handling system
|
||||
|
||||
This whole system is heavily based off of forum_account's keyboard library.
|
||||
Thanks to forum_account for saving the day, the library can be found
|
||||
[here](https://secure.byond.com/developer/Forum_account/Keyboard)!
|
||||
|
||||
.dmf macros have some very serious shortcomings. For example, they do not allow reusing parts
|
||||
of one macro in another, so giving cyborgs their own shortcuts to swap active module couldn't
|
||||
inherit the movement that all mobs should have anyways. The webclient only supports one macro,
|
||||
so having more than one was problematic. Additionally each keybind has to call an actual
|
||||
verb, which meant a lot of hidden verbs that just call one other proc. Also our existing
|
||||
macro was really bad and tied unrelated behavior into `Northeast()`, `Southeast()`, `Northwest()`,
|
||||
and `Southwest()`.
|
||||
|
||||
The basic premise of this system is to not screw with .dmf macro setup at all and handle
|
||||
pressing those keys in the code instead. We have every key call `client.keyDown()`
|
||||
or `client.keyUp()` with the pressed key as an argument. Certain keys get processed
|
||||
directly by the client because they should be doable at any time, then we call
|
||||
`keyDown()` or `keyUp()` on the client's holder and the client's mob's focus.
|
||||
By default `mob.focus` is the mob itself, but you can set it to any datum to give control of a
|
||||
client's keypresses to another object. This would be a good way to handle a menu or driving
|
||||
a mech. You can also set it to null to disregard input from a certain user.
|
||||
|
||||
Movement is handled by having each client call `client.keyLoop()` every game tick.
|
||||
As above, this calls holder and `focus.keyLoop()`. `atom/movable/keyLoop()` handles movement
|
||||
Try to keep the calculations in this proc light. It runs every tick for every client after all!
|
||||
|
||||
You can also tell which keys are being held down now. Each client a list of keys pressed called
|
||||
`keys_held`. Each entry is a key as a text string associated with the world.time when it was
|
||||
pressed.
|
||||
|
||||
No client-set keybindings at this time, but it shouldn't be too hard if someone wants.
|
||||
|
||||
Notes about certain keys:
|
||||
|
||||
* `Tab` has client-sided behavior but acts normally
|
||||
* `T`, `O`, and `M` move focus to the input when pressed. This fires the keyUp macro right away.
|
||||
* `\` needs to be escaped in the dmf so any usage is `\\`
|
||||
|
||||
You cannot `TICK_CHECK` or check `world.tick_usage` inside of procs called by key down and up
|
||||
events. They happen outside of a byond tick and have no meaning there. Key looping
|
||||
works correctly since it's part of a subsystem, not direct input.
|
||||
34
code/modules/keybindings/setup.dm
Normal file
34
code/modules/keybindings/setup.dm
Normal file
@@ -0,0 +1,34 @@
|
||||
// Set a client's focus to an object and override these procs on that object to let it handle keypresses
|
||||
|
||||
/datum/proc/key_down(key, client/user) // Called when a key is pressed down initially
|
||||
return
|
||||
/datum/proc/key_up(key, client/user) // Called when a key is released
|
||||
return
|
||||
/datum/proc/keyLoop(client/user) // Called once every server tick
|
||||
set waitfor = FALSE
|
||||
return
|
||||
|
||||
/// Set mob's focus
|
||||
/// TODO - Do we even need this concept?
|
||||
/mob/proc/set_focus(datum/new_focus)
|
||||
if(focus == new_focus)
|
||||
return
|
||||
focus = new_focus
|
||||
|
||||
/// Turns a keys bitfield into text showing all bits set
|
||||
/proc/keys2text(keys)
|
||||
if(!keys)
|
||||
return ""
|
||||
var/list/out = list()
|
||||
if(keys & NORTH_KEY) out += "NORTH"
|
||||
if(keys & SOUTH_KEY) out += "SOUTH"
|
||||
if(keys & EAST_KEY) out += "EAST"
|
||||
if(keys & WEST_KEY) out += "WEST"
|
||||
if(keys & W_KEY) out += "W"
|
||||
if(keys & A_KEY) out += "A"
|
||||
if(keys & S_KEY) out += "S"
|
||||
if(keys & D_KEY) out += "D"
|
||||
if(keys & CTRL_KEY) out += "CTRL"
|
||||
if(keys & SHIFT_KEY) out += "SHIFT"
|
||||
if(keys & ALT_KEY) out += "ALT"
|
||||
return out.Join(" ")
|
||||
Reference in New Issue
Block a user