TG Sync 12/15/17
s s
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
/datum/admins/key_down(_key, client/user)
|
||||
switch(_key)
|
||||
if("F5")
|
||||
user.admin_ghost()
|
||||
return
|
||||
if("F6")
|
||||
player_panel_new()
|
||||
return
|
||||
if("F7")
|
||||
user.togglebuildmodeself()
|
||||
return
|
||||
if("F8")
|
||||
if(user.keys_held["Ctrl"])
|
||||
user.stealth()
|
||||
else
|
||||
user.invisimin()
|
||||
return
|
||||
..()
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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().
|
||||
|
||||
/atom/movable/keyLoop(client/user)
|
||||
if(!user.keys_held["Ctrl"])
|
||||
var/movement_dir = NONE
|
||||
for(var/_key in user.keys_held)
|
||||
movement_dir |= GLOB.movement_keys[_key]
|
||||
if(user.next_move_dir_add)
|
||||
movement_dir |= user.next_move_dir_add
|
||||
if(user.next_move_dir_sub)
|
||||
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) && (movement_dir & SOUTH))
|
||||
movement_dir &= ~(NORTH|SOUTH)
|
||||
if((movement_dir & EAST) && (movement_dir & WEST))
|
||||
movement_dir &= ~(EAST|WEST)
|
||||
user.Move(get_step(src, movement_dir), movement_dir)
|
||||
@@ -0,0 +1,18 @@
|
||||
/mob/living/carbon/key_down(_key, client/user)
|
||||
switch(_key)
|
||||
if("R", "Southwest") // Southwest is End
|
||||
toggle_throw_mode()
|
||||
return
|
||||
if("1")
|
||||
a_intent_change("help")
|
||||
return
|
||||
if("2")
|
||||
a_intent_change("disarm")
|
||||
return
|
||||
if("3")
|
||||
a_intent_change("grab")
|
||||
return
|
||||
if("4")
|
||||
a_intent_change("harm")
|
||||
return
|
||||
return ..()
|
||||
@@ -0,0 +1,50 @@
|
||||
// Clients aren't datums so we have to define these procs indpendently.
|
||||
// These verbs are called for all key press and release events
|
||||
/client/verb/keyDown(_key as text)
|
||||
set instant = TRUE
|
||||
set hidden = TRUE
|
||||
|
||||
keys_held[_key] = world.time
|
||||
var/movement = GLOB.movement_keys[_key]
|
||||
if(!(next_move_dir_sub & movement))
|
||||
next_move_dir_add |= movement
|
||||
|
||||
// Client-level keybindings are ones anyone should be able to do at any time
|
||||
// Things like taking screenshots, hitting tab, and adminhelps.
|
||||
|
||||
switch(_key)
|
||||
if("F1")
|
||||
if(keys_held["Ctrl"] && keys_held["Shift"]) // Is this command ever used?
|
||||
winset(src, null, "command=.options")
|
||||
else
|
||||
adminhelp()
|
||||
if("F2") // Screenshot. Hold shift to choose a name and location to save in
|
||||
winset(src, null, "command=.screenshot [!keys_held["shift"] ? "auto" : ""]")
|
||||
if("F12") // Toggles minimal HUD
|
||||
mob.button_pressed_F12()
|
||||
|
||||
if(holder)
|
||||
holder.key_down(_key, src)
|
||||
if(mob.focus)
|
||||
mob.focus.key_down(_key, src)
|
||||
|
||||
/client/verb/keyUp(_key as text)
|
||||
set instant = TRUE
|
||||
set hidden = TRUE
|
||||
|
||||
keys_held -= _key
|
||||
var/movement = GLOB.movement_keys[_key]
|
||||
if(!(next_move_dir_add & movement))
|
||||
next_move_dir_sub |= movement
|
||||
|
||||
if(holder)
|
||||
holder.key_up(_key, src)
|
||||
if(mob.focus)
|
||||
mob.focus.key_up(_key, src)
|
||||
|
||||
// Called every game tick
|
||||
/client/keyLoop()
|
||||
if(holder)
|
||||
holder.keyLoop(src)
|
||||
if(mob.focus)
|
||||
mob.focus.keyLoop(src)
|
||||
@@ -0,0 +1,6 @@
|
||||
/mob/living/carbon/human/key_down(_key, client/user)
|
||||
switch(_key)
|
||||
if("E")
|
||||
quick_equip()
|
||||
return
|
||||
return ..()
|
||||
@@ -0,0 +1,7 @@
|
||||
/mob/living/key_down(_key, client/user)
|
||||
switch(_key)
|
||||
if("B")
|
||||
resist()
|
||||
return
|
||||
|
||||
return ..()
|
||||
@@ -0,0 +1,79 @@
|
||||
// Technically the client argument is unncessary here since that SHOULD be src.client but let's not assume things
|
||||
// All it takes is one badmin setting their focus to someone else's client to mess things up
|
||||
// Or we can have NPC's send actual keypresses and detect that by seeing no client
|
||||
|
||||
/mob/key_down(_key, client/user)
|
||||
switch(_key)
|
||||
if("Delete", "H")
|
||||
if(!pulling)
|
||||
to_chat(src, "<span class='notice'>You are not pulling anything.</span>")
|
||||
else
|
||||
stop_pulling()
|
||||
return
|
||||
if("Insert", "G")
|
||||
a_intent_change(INTENT_HOTKEY_RIGHT)
|
||||
return
|
||||
if("F")
|
||||
a_intent_change(INTENT_HOTKEY_LEFT)
|
||||
return
|
||||
if("X", "Northeast") // Northeast is Page-up
|
||||
swap_hand()
|
||||
return
|
||||
if("Y", "Z", "Southeast") // Southeast is Page-down
|
||||
mode() // attack_self(). No idea who came up with "mode()"
|
||||
return
|
||||
if("Q", "Northwest") // Northwest is Home
|
||||
var/obj/item/I = get_active_held_item()
|
||||
if(!I)
|
||||
to_chat(src, "<span class='warning'>You have nothing to drop in your hand!</span>")
|
||||
else
|
||||
dropItemToGround(I)
|
||||
return
|
||||
if("Alt")
|
||||
toggle_move_intent()
|
||||
return
|
||||
//Bodypart selections
|
||||
if("Numpad8")
|
||||
user.body_toggle_head()
|
||||
return
|
||||
if("Numpad4")
|
||||
user.body_r_arm()
|
||||
return
|
||||
if("Numpad5")
|
||||
user.body_chest()
|
||||
return
|
||||
if("Numpad6")
|
||||
user.body_l_arm()
|
||||
return
|
||||
if("Numpad1")
|
||||
user.body_r_leg()
|
||||
return
|
||||
if("Numpad2")
|
||||
user.body_groin()
|
||||
return
|
||||
if("Numpad3")
|
||||
user.body_l_leg()
|
||||
return
|
||||
|
||||
if(client.keys_held["Ctrl"])
|
||||
switch(GLOB.movement_keys[_key])
|
||||
if(NORTH)
|
||||
northface()
|
||||
return
|
||||
if(SOUTH)
|
||||
southface()
|
||||
return
|
||||
if(WEST)
|
||||
westface()
|
||||
return
|
||||
if(EAST)
|
||||
eastface()
|
||||
return
|
||||
return ..()
|
||||
|
||||
/mob/key_up(_key, client/user)
|
||||
switch(_key)
|
||||
if("Alt")
|
||||
toggle_move_intent()
|
||||
return
|
||||
return ..()
|
||||
@@ -0,0 +1,12 @@
|
||||
/mob/living/silicon/robot/key_down(_key, client/user)
|
||||
switch(_key)
|
||||
if("1", "2", "3")
|
||||
toggle_module(text2num(_key))
|
||||
return
|
||||
if("4")
|
||||
a_intent_change(INTENT_HOTKEY_LEFT)
|
||||
return
|
||||
if("Q")
|
||||
uneq_active()
|
||||
return
|
||||
return ..()
|
||||
@@ -0,0 +1,20 @@
|
||||
/datum
|
||||
var/list/focusers //Only initialized when needed. Contains a list of mobs focusing on this.
|
||||
|
||||
/mob
|
||||
var/datum/focus //What receives our keyboard inputs. src by default
|
||||
|
||||
/mob/proc/set_focus(datum/new_focus)
|
||||
if(focus == new_focus)
|
||||
return
|
||||
|
||||
if(new_focus)
|
||||
if(!new_focus.focusers) //Set up the new focus
|
||||
new_focus.focusers = list()
|
||||
new_focus.focusers += src
|
||||
|
||||
if(focus)
|
||||
focus.focusers -= src //Tell the old focus we're done with it
|
||||
|
||||
focus = new_focus
|
||||
reset_perspective(focus) //Maybe this should be done manually? You figure it out, reader
|
||||
@@ -0,0 +1,40 @@
|
||||
# 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](http://www.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.
|
||||
@@ -0,0 +1,62 @@
|
||||
/client
|
||||
var/list/keys_held = list() // A list of any keys held currently
|
||||
// These next two vars are to apply movement for keypresses and releases made while move delayed.
|
||||
// Because discarding that input makes the game less responsive.
|
||||
var/next_move_dir_add // On next move, add this dir to the move that would otherwise be done
|
||||
var/next_move_dir_sub // On next move, subtract this dir from the move that would otherwise be done
|
||||
|
||||
// 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 frame
|
||||
return
|
||||
|
||||
// Keys used for movement
|
||||
GLOBAL_LIST_INIT(movement_keys, list(
|
||||
"W" = NORTH, "A" = WEST, "S" = SOUTH, "D" = EAST, // WASD
|
||||
"North" = NORTH, "West" = WEST, "South" = SOUTH, "East" = EAST, // Arrow keys & Numpad
|
||||
))
|
||||
|
||||
/*
|
||||
A horrific battle against shitcode was fought here to find out some use details of winset
|
||||
Aparently you need to wrap the entire proc + args in quotes if you intend on using args
|
||||
But you don't need the quote wrappings to just call on a proc with no args
|
||||
ex. winset(src, "default-Any", "command=keyDown \[\[*\]\]") fail: command = keyDown
|
||||
ex. winset(src, "default-Any", "command=keyDown \"\[\[*\]\]\"") fail: same
|
||||
ex. winset(src, "default-T", "command=say") works fine
|
||||
ex. winset(src, "default-Any", "command=\"keyDown \[\[*\]\]\"") works fine
|
||||
Thanks for the useful errors lummox ~ninjanomnom
|
||||
*/
|
||||
GLOBAL_LIST_INIT(default_macros, list(
|
||||
"Tab" = "\".winset \\\"input.focus=true?map.focus=true input.background-color=#F0F0F0:input.focus=true input.background-color=#D3B5B5\\\"\"",
|
||||
"O" = "ooc",
|
||||
"T" = "say",
|
||||
"M" = "me",
|
||||
"Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"", // This makes it so backspace can remove default inputs
|
||||
"Any" = "\"KeyDown \[\[*\]\]\"",
|
||||
"Any+UP" = "\"KeyUp \[\[*\]\]\"",
|
||||
))
|
||||
|
||||
/client/proc/set_macros()
|
||||
set waitfor = FALSE
|
||||
|
||||
winset(src, null, "reset=true")
|
||||
winset(src, null, "mainwindow.macro=default")
|
||||
var/list/default = params2list(winget(src, "default.*", "command"))
|
||||
for(var/i in 1 to length(default))
|
||||
var/id = default[i]
|
||||
winset(src, id, "parent=none")
|
||||
|
||||
var/list/default_macros = GLOB.default_macros
|
||||
for(var/i in 1 to length(default_macros))
|
||||
var/input = default_macros[i]
|
||||
var/output = default_macros[input]
|
||||
winset(src, "default-[input]", "parent=default;name=[input];command=[output]")
|
||||
|
||||
if(prefs.hotkeys)
|
||||
winset(src, null, "mapwindow.map.focus=true input.background-color=#e0e0e0")
|
||||
else
|
||||
winset(src, null, "input.focus=true input.background-color=#d3b5b5")
|
||||
Reference in New Issue
Block a user