diff --git a/baystation12.dme b/baystation12.dme index 4e07d1a02f..1f707a8e77 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -219,6 +219,7 @@ #include "code\controllers\subsystems\goals.dm" #include "code\controllers\subsystems\graphs.dm" #include "code\controllers\subsystems\inactivity.dm" +#include "code\controllers\subsystems\input.dm" #include "code\controllers\subsystems\jobs.dm" #include "code\controllers\subsystems\kv.dm" #include "code\controllers\subsystems\lighting.dm" @@ -1967,6 +1968,17 @@ #include "code\modules\item_worth\item_worth.dm" #include "code\modules\item_worth\value_procs.dm" #include "code\modules\item_worth\worths_list.dm" +#include "code\modules\keybindings\bindings_admin.dm" +#include "code\modules\keybindings\bindings_ai.dm" +#include "code\modules\keybindings\bindings_atom.dm" +#include "code\modules\keybindings\bindings_carbon.dm" +#include "code\modules\keybindings\bindings_client.dm" +#include "code\modules\keybindings\bindings_human.dm" +#include "code\modules\keybindings\bindings_living.dm" +#include "code\modules\keybindings\bindings_mob.dm" +#include "code\modules\keybindings\bindings_robot.dm" +#include "code\modules\keybindings\focus.dm" +#include "code\modules\keybindings\setup.dm" #include "code\modules\library\lib_items.dm" #include "code\modules\library\lib_machines.dm" #include "code\modules\library\lib_readme.dm" diff --git a/code/__defines/admin.dm b/code/__defines/admin.dm index 4d8aa8573b..db7da1ec7b 100644 --- a/code/__defines/admin.dm +++ b/code/__defines/admin.dm @@ -46,4 +46,11 @@ #define TICKET_ASSIGNED 2 // An admin has assigned themself to the ticket and will respond #define LAST_CKEY(M) (M.ckey || M.last_ckey) -#define LAST_KEY(M) (M.key || M.last_ckey) \ No newline at end of file +#define LAST_KEY(M) (M.key || M.last_ckey) + +///Max length of a keypress command before it's considered to be a forged packet/bogus command +#define MAX_KEYPRESS_COMMANDLENGTH 16 +///Max amount of keypress messages per second over two seconds before client is autokicked +#define MAX_KEYPRESS_AUTOKICK 50 +///Length of held key rolling buffer +#define HELD_KEY_BUFFER_LENGTH 15 diff --git a/code/__defines/colors.dm b/code/__defines/colors.dm index 5d5cf0d51b..6f573306f4 100644 --- a/code/__defines/colors.dm +++ b/code/__defines/colors.dm @@ -181,3 +181,6 @@ #define COLOR_DARKMODE_BACKGROUND "#202020" #define COLOR_DARKMODE_DARKBACKGROUND "#171717" #define COLOR_DARKMODE_TEXT "#a4bad6" + +#define COLOR_INPUT_DISABLED "#F0F0F0" +#define COLOR_INPUT_ENABLED "#D3B5B5" diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index cfebd1d9f9..5c9fabe6c4 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -293,3 +293,12 @@ #define INIT_MACHINERY_PROCESS_ALL 0x3 //-- +//intent defines +#define INTENT_HELP "help" +#define INTENT_GRAB "grab" +#define INTENT_DISARM "disarm" +#define INTENT_HARM "harm" +//NOTE: INTENT_HOTKEY_* defines are not actual intents! +//they are here to support hotkeys +#define INTENT_HOTKEY_LEFT "left" +#define INTENT_HOTKEY_RIGHT "right" diff --git a/code/__defines/subsystem-priority.dm b/code/__defines/subsystem-priority.dm index 995b9713e7..482a2f040e 100644 --- a/code/__defines/subsystem-priority.dm +++ b/code/__defines/subsystem-priority.dm @@ -10,6 +10,7 @@ #define SS_PRIORITY_ICON_UPDATE 20 // Queued icon updates. Mostly used by APCs and tables. // Normal +#define SS_PRIORITY_INPUT 1000 #define SS_PRIORITY_TICKER 100 // Gameticker. #define SS_PRIORITY_MOB 95 // Mob Life(). #define SS_PRIORITY_MACHINERY 95 // Machinery + powernet ticks. diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index edf610841e..9a7e945ff2 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -31,6 +31,7 @@ return;\ // Subsystems shutdown in the reverse of the order they initialize in // The numbers just define the ordering, they are meaningless otherwise. +#define SS_INIT_INPUT 19 #define SS_INIT_EARLY 18 #define SS_INIT_GARBAGE 17 #define SS_INIT_CHEMISTRY 16 diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index b25711aa87..75536ea2e1 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -1118,3 +1118,6 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) M.start_pulling(t) else step(user.pulling, get_dir(user.pulling.loc, A)) + +/proc/REF(input) + return "\ref[input]" diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 454df24071..800e8035ff 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -65,6 +65,9 @@ if(modifiers["middle"]) MiddleClickOn(A) return 1 + if(modifiers["middle"] && modifiers["alt"]) + AltMiddleClickOn(A) + return 1 if(modifiers["shift"]) ShiftClickOn(A) return 0 @@ -219,7 +222,10 @@ Only used for swapping hands */ /mob/proc/MiddleClickOn(var/atom/A) - swap_hand() + pointed(A) + return + +/mob/proc/AltMiddleClickOn(var/atom/A) return // In case of use break glass @@ -302,11 +308,8 @@ /mob/proc/CtrlAltClickOn(var/atom/A) if(A.CtrlAltClick(src)) return - pointed(A) /atom/proc/CtrlAltClick(var/mob/user) - if(user.client && user.client.eye == user) - user.pointed(src) return /* diff --git a/code/_onclick/click_inf.dm b/code/_onclick/click_inf.dm index 1c48c063c0..a782eac521 100644 --- a/code/_onclick/click_inf.dm +++ b/code/_onclick/click_inf.dm @@ -1,4 +1,4 @@ -/mob/living/carbon/MiddleClickOn(atom/A) +/mob/living/carbon/AltMiddleClickOn(atom/A) if(!stat && mind && iscarbon(A) && A != src) var/datum/changeling/C = mind.changeling if(C?.chosen_sting) diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index 9868ec3d01..8eea7ccccc 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -96,8 +96,9 @@ return return -//Middle click cycles through selected modules. -/mob/living/silicon/robot/MiddleClickOn(var/atom/A) +//Alt Middle click cycles through selected modules. +//Though we have "X" marcos for it and humans now can't swap hands using middle click, I'll let it be here +/mob/living/silicon/robot/AltMiddleClickOn(var/atom/A) cycle_modules() return diff --git a/code/_onclick/hud/ability_screen_objects.dm b/code/_onclick/hud/ability_screen_objects.dm index 5d7604a6f5..a185fb4bc2 100644 --- a/code/_onclick/hud/ability_screen_objects.dm +++ b/code/_onclick/hud/ability_screen_objects.dm @@ -353,6 +353,7 @@ toggle_open(2) //forces the icons to refresh on screen /mob/Life() + set waitfor = FALSE UNLINT(..()) if(ability_master) ability_master.update_spells(0) @@ -410,4 +411,4 @@ for(var/obj/screen/ability/spell/spell in spell_objects) spell.spell.silenced = amount spell.spell.process() - spell.update_charge(1) \ No newline at end of file + spell.update_charge(1) diff --git a/code/_onclick/rig.dm b/code/_onclick/rig.dm index 583f4e59c7..4850217649 100644 --- a/code/_onclick/rig.dm +++ b/code/_onclick/rig.dm @@ -1,4 +1,4 @@ -/mob/living/MiddleClickOn(atom/A) +/mob/living/AltMiddleClickOn(atom/A) if(get_preference_value(/datum/client_preference/hardsuit_activation) == GLOB.PREF_MIDDLE_CLICK) if(HardsuitClickOn(A)) return @@ -51,4 +51,4 @@ if(ismob(A)) // No instant mob attacking - though modules have their own cooldowns setClickCooldown(DEFAULT_ATTACK_COOLDOWN) return 1 - return 0 \ No newline at end of file + return 0 diff --git a/code/controllers/subsystems/input.dm b/code/controllers/subsystems/input.dm new file mode 100644 index 0000000000..ea8e6d7bb4 --- /dev/null +++ b/code/controllers/subsystems/input.dm @@ -0,0 +1,124 @@ +SUBSYSTEM_DEF(input) + name = "Input" + wait = 1 //SS_TICKER means this runs every tick + init_order = SS_INIT_INPUT + flags = SS_TICKER + priority = SS_PRIORITY_INPUT + runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY + + var/list/macro_sets + var/list/movement_keys + var/list/alt_movement_keys + +/datum/controller/subsystem/input/Initialize() + setup_default_macro_sets() + + setup_default_movement_keys() + + initialized = TRUE + + refresh_client_macro_sets() + + return ..() + +// This is for when macro sets are eventualy datumized +/datum/controller/subsystem/input/proc/setup_default_macro_sets() + var/list/static/default_macro_sets + + if(default_macro_sets) + macro_sets = default_macro_sets + return + + default_macro_sets = list( + "default" = list( + "Tab" = "\".winset \\\"input.focus=true?map.focus=true input.background-color=[COLOR_INPUT_DISABLED]:input.focus=true input.background-color=[COLOR_INPUT_ENABLED]\\\"\"", + "Back" = "\".winset \\\"input.focus=true input.text=\\\"\\\"\\\"\"", // This makes it so backspace can remove default inputs + "Any" = "\"KeyDown \[\[*\]\]\"", + "Any+UP" = "\"KeyUp \[\[*\]\]\"", + ), + "old_default" = list( + "Tab" = "\".winset \\\"mainwindow.macro=old_hotkeys map.focus=true input.background-color=[COLOR_INPUT_DISABLED]\\\"\"", + ), + "old_hotkeys" = list( + "Tab" = "\".winset \\\"mainwindow.macro=old_default input.focus=true input.background-color=[COLOR_INPUT_ENABLED]\\\"\"", + "Back" = "\".winset \\\"input.focus=true input.text=\\\"\\\"\\\"\"", // This makes it so backspace can remove default inputs + "Any" = "\"KeyDown \[\[*\]\]\"", + "Any+UP" = "\"KeyUp \[\[*\]\]\"", + ), + ) + + // Because i'm lazy and don't want to type all these out twice + var/list/old_default = default_macro_sets["old_default"] + + var/list/static/oldmode_keys = list( + "North", "East", "South", "West", + "Northeast", "Southeast", "Northwest", "Southwest", + "Insert", "Delete", "Ctrl", "Alt", "Shift", + "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", + ) + + for(var/i in 1 to oldmode_keys.len) + var/key = oldmode_keys[i] + old_default[key] = "\"KeyDown [key]\"" + old_default["[key]+UP"] = "\"KeyUp [key]\"" + + var/list/static/oldmode_ctrl_override_keys = list( + "W" = "W", "A" = "A", "S" = "S", "D" = "D", // movement + "1" = "1", "2" = "2", "3" = "3", "4" = "4", // intent + "B" = "B", // resist, rest + "E" = "E", // quick equip + "F" = "F", // intent left + "G" = "G", // intent right + "H" = "H", // stop pulling + "Q" = "Q", // drop + "R" = "R", // throw + "X" = "X", // switch hands + "Y" = "Y", // activate item + "Z" = "Z", // activate item + "T" = "T", // say, whisper + "M" = "M", // me + "O" = "O", // ooc + "L" = "L", // looc + "C" = "C", // stop pulling + ) + + for(var/i in 1 to oldmode_ctrl_override_keys.len) + var/key = oldmode_ctrl_override_keys[i] + var/override = oldmode_ctrl_override_keys[key] + old_default["Ctrl+[key]"] = "\"KeyDown [override]\"" + old_default["Ctrl+[key]+UP"] = "\"KeyUp [override]\"" + + macro_sets = default_macro_sets + +// For initially setting up or resetting to default the movement keys +/datum/controller/subsystem/input/proc/setup_default_movement_keys() + var/static/list/default_movement_keys = list( + "W" = NORTH, "A" = WEST, "S" = SOUTH, "D" = EAST, // WASD + "North" = NORTH, "West" = WEST, "South" = SOUTH, "East" = EAST, // Arrow keys & Numpad + ) + var/static/list/azerty_movement_keys = list( + "Z" = NORTH, "Q" = WEST, "S" = SOUTH, "D" = EAST, // WASD + "North" = NORTH, "West" = WEST, "South" = SOUTH, "East" = EAST, // Arrow keys & Numpad + ) + movement_keys = default_movement_keys.Copy() + alt_movement_keys = azerty_movement_keys.Copy() + +// Badmins just wanna have fun ♪ +/datum/controller/subsystem/input/proc/refresh_client_macro_sets() + var/list/clients = GLOB.clients + for(var/i in 1 to clients.len) + var/client/user = clients[i] + user.set_macros() + +/datum/controller/subsystem/input/fire() + var/list/clients = GLOB.clients // Let's sing the list cache song + if(listclearnulls(clients)) // clear nulls before we run keyloop + log_world("Found a null in clients list!") + for(var/i in 1 to clients.len) + var/client/C = clients[i] + C.keyLoop() + +/datum/controller/subsystem/input/Recover() + macro_sets = SSinput.macro_sets + movement_keys = SSinput.movement_keys + alt_movement_keys = SSinput.alt_movement_keys diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm index e5500d99c0..3a7e8493f1 100644 --- a/code/game/verbs/ooc.dm +++ b/code/game/verbs/ooc.dm @@ -1,10 +1,13 @@ -/client/verb/ooc(message as text) +/client/verb/ooc(message = "" as text) set name = "OOC" set category = "OOC" + if(!message) + message = input(src.mob, "", "ooc \"text\"") as text|null + sanitize_and_communicate(/decl/communication_channel/ooc, src, message) -/client/verb/looc(message as text) +/client/verb/looc(message = "" as text) set name = "LOOC" set desc = "Local OOC, seen only by those in view. Remember: Just because you see someone that doesn't mean they see you." set category = "OOC" diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index aa0b8ef7bb..019ea7cdb1 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -254,6 +254,9 @@ prefs?.apply_post_login_preferences() //[/INF] + if(SSinput.initialized) + set_macros() + ////////////// //DISCONNECT// ////////////// diff --git a/code/modules/keybindings/bindings_admin.dm b/code/modules/keybindings/bindings_admin.dm new file mode 100644 index 0000000000..673bdbcf16 --- /dev/null +++ b/code/modules/keybindings/bindings_admin.dm @@ -0,0 +1,32 @@ +/datum/admins/key_down(_key, client/user) + switch(_key) + if("F5") +// if(user.keys_held["Shift"]) +// user.get_mentor_say() +// else + user.get_admin_say() + return + if("F6") + user.admin_ghost() + return + if("F7") + player_panel() + return + if("F8") + user.cmd_admin_pm_panel() + return + if("F9") + user.invisimin() + return + if("F10") + user.get_dead_say() + return + ..() + +/client/proc/get_admin_say() + var/msg = input(src, null, "asay \"text\"") as text|null + cmd_admin_say(msg) + +/client/proc/get_dead_say() + var/msg = input(src, null, "dsay \"text\"") as text + dsay(msg) diff --git a/code/modules/keybindings/bindings_ai.dm b/code/modules/keybindings/bindings_ai.dm new file mode 100644 index 0000000000..975b5ba81a --- /dev/null +++ b/code/modules/keybindings/bindings_ai.dm @@ -0,0 +1,6 @@ +/mob/living/silicon/ai/key_down(_key, client/user) + switch(_key) + if("4") + a_intent_change(INTENT_HOTKEY_LEFT) + return + return ..() diff --git a/code/modules/keybindings/bindings_atom.dm b/code/modules/keybindings/bindings_atom.dm new file mode 100644 index 0000000000..2b7fafbe20 --- /dev/null +++ b/code/modules/keybindings/bindings_atom.dm @@ -0,0 +1,19 @@ +// 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 = null + var/list/movement = SSinput.movement_keys + for(var/_key in user.keys_held) + movement_dir = movement_dir | movement[_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) diff --git a/code/modules/keybindings/bindings_carbon.dm b/code/modules/keybindings/bindings_carbon.dm new file mode 100644 index 0000000000..a2cb4e7219 --- /dev/null +++ b/code/modules/keybindings/bindings_carbon.dm @@ -0,0 +1,21 @@ + +/mob/living/carbon/key_down(_key, client/user) + user.keys_held[_key] = world.time + if(!user.keys_held["Shift"]) + switch(_key) + if("R", "Southwest") // Southwest is End + toggle_throw_mode() + return + if("1") + a_intent_change(I_HELP) + return + if("2") + a_intent_change(I_DISARM) + return + if("3") + a_intent_change(I_GRAB) + return + if("4") + a_intent_change(I_HURT) + return + return ..() diff --git a/code/modules/keybindings/bindings_client.dm b/code/modules/keybindings/bindings_client.dm new file mode 100644 index 0000000000..ea99bd1659 --- /dev/null +++ b/code/modules/keybindings/bindings_client.dm @@ -0,0 +1,112 @@ +// 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 + + client_keysend_amount += 1 + + var/cache = client_keysend_amount + + if(keysend_tripped && next_keysend_trip_reset <= world.time) + keysend_tripped = FALSE + + if(next_keysend_reset <= world.time) + client_keysend_amount = 0 + next_keysend_reset = world.time + (1 SECONDS) + + //The "tripped" system is to confirm that flooding is still happening after one spike + //not entirely sure how byond commands interact in relation to lag + //don't want to kick people if a lag spike results in a huge flood of commands being sent + if(cache >= MAX_KEYPRESS_AUTOKICK) + if(!keysend_tripped) + keysend_tripped = TRUE + next_keysend_trip_reset = world.time + (2 SECONDS) + else + log_admin("Client [ckey] was just autokicked for flooding keysends; likely abuse but potentially lagspike.") + message_admins("Client [ckey] was just autokicked for flooding keysends; likely abuse but potentially lagspike.") + qdel(src) + return + + ///Check if the key is short enough to even be a real key + if(LAZYLEN(_key) > MAX_KEYPRESS_COMMANDLENGTH) + to_chat(src, "Invalid KeyDown detected! You have been disconnected from the server automatically.") + log_admin("Client [ckey] just attempted to send an invalid keypress. Keymessage was over [MAX_KEYPRESS_COMMANDLENGTH] characters, autokicking due to likely abuse.") + message_admins("Client [ckey] just attempted to send an invalid keypress. Keymessage was over [MAX_KEYPRESS_COMMANDLENGTH] characters, autokicking due to likely abuse.") + qdel(src) + return + //offset by 1 because the buffer address is 0 indexed because the math was simpler + keys_held[current_key_address + 1] = _key + //the time a key was pressed isn't actually used anywhere (as of 2019-9-10) but this allows easier access usage/checking + + keys_held[_key] = world.time + + current_key_address = ((current_key_address + 1) % HELD_KEY_BUFFER_LENGTH) + + var/movement = SSinput.movement_keys[_key] + if(!(next_move_dir_sub & movement) && !keys_held["Ctrl"]) + 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() + return + if("F2", "O") // Screenshot. Hold shift to choose a name and location to save in + ooc() + return + if("F3", "T") + if(keys_held["Shift"]) + mob.whisper_wrapper() + else + mob.say_wrapper() + return + if("F4", "M") + mob.me_wrapper() + return + if("L") + looc() + return + if("F11") // Toggles Fullscreen or Fits Viewport + if(keys_held["Ctrl"]) + fit_viewport() + else + toggle_fullscreen() + return + if("F12") // Toggles minimal HUD + mob.button_pressed_F12() + return + + 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 + //Can't just do a remove because it would alter the length of the rolling buffer, instead search for the key then null it out if it exists + for(var/i in 1 to HELD_KEY_BUFFER_LENGTH) + if(keys_held[i] == _key) + keys_held[i] = null + break + var/movement = SSinput.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) diff --git a/code/modules/keybindings/bindings_human.dm b/code/modules/keybindings/bindings_human.dm new file mode 100644 index 0000000000..cfec33a158 --- /dev/null +++ b/code/modules/keybindings/bindings_human.dm @@ -0,0 +1,83 @@ +/mob/living/carbon/human/key_down(_key, client/user) +/* + if(_key == "H") + var/obj/item/clothing/accessory/holster/H = null + if(istype(w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/S = w_uniform + if(S.accessories.len) + H = locate() in S.accessories + if (!H) + return + if(!H.holstered) + if(!istype(get_active_hand(), /obj/item/gun)) + to_chat(usr, "You need your gun equiped to holster it.") + return + var/obj/item/gun/W = get_active_hand() + H.holster(W, usr) + else + H.unholster(usr) +*/ + if(client.keys_held["Shift"]) + switch(_key) + if("E") // Put held thing in belt or take out most recent thing from belt + quick_equip() // Implementing the storage component is going to take way too long + return + // var/obj/item/thing = get_active_hand() + // var/obj/item/equipped_belt = get_item_by_slot(slot_belt) + // if(!equipped_belt) // We also let you equip a belt like this + // if(!thing) + // to_chat(user, "You have no belt to take something out of.") + // return + // if(equip_to_slot_if_possible(thing, slot_belt)) + // update_inv_r_hand() + // update_inv_l_hand() + // return + // if(!SEND_SIGNAL(equipped_belt, COMSIG_CONTAINS_STORAGE)) // not a storage item + // if(!thing) + // equipped_belt.attack_hand(src) + // else + // to_chat(user, "You can't fit anything in.") + // return + // if(thing) // put thing in belt + // if(!SEND_SIGNAL(equipped_belt, COMSIG_TRY_STORAGE_INSERT, thing, user.mob)) + // to_chat(user, "You can't fit anything in.") + // return + // if(!equipped_belt.contents.len) // nothing to take out + // to_chat(user, "There's nothing in your belt to take out.") + // return + // var/obj/item/stored = equipped_belt.contents[equipped_belt.contents.len] + // if(!stored || stored.on_found(src)) + // return + // stored.attack_hand(src) // take out thing from belt + // return + + /* if("B") // Put held thing in backpack or take out most recent thing from backpack + var/obj/item/thing = get_active_hand() + var/obj/item/equipped_back = get_item_by_slot(slot_back) + if(!equipped_back) // We also let you equip a backpack like this + if(!thing) + to_chat(user, "You have no backpack to take something out of.") + return + if(equip_to_slot_if_possible(thing, slot_back)) + update_inv_r_hand() + update_inv_l_hand() + return + if(!SEND_SIGNAL(equipped_back, COMSIG_CONTAINS_STORAGE)) // not a storage item + if(!thing) + equipped_back.attack_hand(src) + else + to_chat(user, "You can't fit anything in.") + return + if(thing) // put thing in backpack + if(!SEND_SIGNAL(equipped_back, COMSIG_TRY_STORAGE_INSERT, thing, user.mob)) + to_chat(user, "You can't fit anything in.") + return + if(!equipped_back.contents.len) // nothing to take out + to_chat(user, "There's nothing in your backpack to take out.") + return + var/obj/item/stored = equipped_back.contents[equipped_back.contents.len] + if(!stored || stored.on_found(src)) + return + stored.attack_hand(src) // take out thing from backpack + return */ + return ..() diff --git a/code/modules/keybindings/bindings_living.dm b/code/modules/keybindings/bindings_living.dm new file mode 100644 index 0000000000..88c14a51a1 --- /dev/null +++ b/code/modules/keybindings/bindings_living.dm @@ -0,0 +1,10 @@ +/mob/living/key_down(_key, client/user) + switch(_key) + if("B") + if(user.keys_held["Shift"]) + lay_down() + else + resist() + return + + return ..() diff --git a/code/modules/keybindings/bindings_mob.dm b/code/modules/keybindings/bindings_mob.dm new file mode 100644 index 0000000000..5d3dde1a30 --- /dev/null +++ b/code/modules/keybindings/bindings_mob.dm @@ -0,0 +1,90 @@ +// 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", "C") + if(!pulling) + to_chat(src, "You are not pulling anything.") + 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_hand() + if(!I) + to_chat(src, "You have nothing to drop in your hand!") + else + drop_item(I) + return + if("E") + quick_equip() + return + if("Shift") + set_moving_quickly() + return + if(",") + move_up() + return + if(".") + SelfMove(DOWN) + return + if("j") + toggle_gun_mode() + 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(SSinput.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("Shift") + set_moving_slowly() + return ..() diff --git a/code/modules/keybindings/bindings_robot.dm b/code/modules/keybindings/bindings_robot.dm new file mode 100644 index 0000000000..db455f82c0 --- /dev/null +++ b/code/modules/keybindings/bindings_robot.dm @@ -0,0 +1,14 @@ +/mob/living/silicon/robot/key_down(_key, client/user) + switch(_key) + if("1", "2", "3") + cmd_toggle_module(text2num(_key)) + return + if("4") + a_intent_change(INTENT_HOTKEY_LEFT) + return + if("X") + cycle_modules() + return + if("Q") + cmd_unequip_module() // User is in QWERTY hotkey mode. + return ..() diff --git a/code/modules/keybindings/focus.dm b/code/modules/keybindings/focus.dm new file mode 100644 index 0000000000..651b50cc45 --- /dev/null +++ b/code/modules/keybindings/focus.dm @@ -0,0 +1,8 @@ +/mob + var/datum/focus //What receives our keyboard inputs. src by default + +/mob/proc/set_focus(datum/new_focus) + if(focus == new_focus) + return + focus = new_focus + reset_view(focus) //Maybe this should be done manually? You figure it out, reader diff --git a/code/modules/keybindings/readme.dm b/code/modules/keybindings/readme.dm new file mode 100644 index 0000000000..3f8b992b93 --- /dev/null +++ b/code/modules/keybindings/readme.dm @@ -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. diff --git a/code/modules/keybindings/setup.dm b/code/modules/keybindings/setup.dm new file mode 100644 index 0000000000..dcc1df09ec --- /dev/null +++ b/code/modules/keybindings/setup.dm @@ -0,0 +1,61 @@ +/client + /// A rolling buffer of any keys held currently + var/list/keys_held = list() + ///used to keep track of the current rolling buffer position + var/current_key_address = 0 + + var/client_keysend_amount = 0 + var/next_keysend_reset = 0 + var/next_keysend_trip_reset = 0 + var/keysend_tripped = FALSE + /// 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. + /// On next move, add this dir to the move that would otherwise be done + var/next_move_dir_add + /// On next move, subtract this dir from the move that would otherwise be done + var/next_move_dir_sub + +// 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 + set waitfor = FALSE + return + +// removes all the existing macros +/client/proc/erase_all_macros() + var/list/macro_sets = params2list(winget(src, null, "macros")) + var/erase_output = "" + for(var/i in 1 to macro_sets.len) + var/setname = macro_sets[i] + var/list/macro_set = params2list(winget(src, "[setname].*", "command")) // The third arg doesnt matter here as we're just removing them all + for(var/k in 1 to macro_set.len) + var/list/split_name = splittext(macro_set[k], ".") + var/macro_name = "[split_name[1]].[split_name[2]]" // [3] is "command" + erase_output = "[erase_output];[macro_name].parent=null" + winset(src, null, erase_output) + +/client/proc/set_macros() + set waitfor = FALSE + + //Reset and populate the rolling buffer + keys_held.Cut() + for(var/i in 1 to HELD_KEY_BUFFER_LENGTH) + keys_held += null + + erase_all_macros() + + var/list/macro_sets = SSinput.macro_sets + for(var/i in 1 to macro_sets.len) + var/setname = macro_sets[i] + if(setname != "default") + winclone(src, "default", setname) + var/list/macro_set = macro_sets[setname] + for(var/k in 1 to macro_set.len) + var/key = macro_set[k] + var/command = macro_set[key] + winset(src, "[setname]-\ref[key]", "parent=[setname];name=[key];command=[command]") + winset(src, null, "map.focus=true input.background-color=[COLOR_INPUT_DISABLED] mainwindow.macro=default") diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index c3433f3e94..ee686a528f 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -3,10 +3,12 @@ Add fingerprints to items when we put them in our hands. This saves us from having to call add_fingerprint() any time something is put in a human's hands programmatically. */ -/mob/living/carbon/human/verb/quick_equip() +/mob/verb/quick_equip() set name = "quick-equip" set hidden = 1 + return +/mob/living/carbon/human/quick_equip() if(ishuman(src)) var/mob/living/carbon/human/H = src var/obj/item/I = H.get_active_hand() diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 074945ad40..e7c4fe6233 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -107,6 +107,9 @@ //set macro to normal incase it was overriden (like cyborg currently does) //INF winset(src, null, "mainwindow.macro=macro hotkey_toggle.is-checked=false input.focus=true input.background-color=#d3b5b5") + if(client && SSinput.initialized) + client.set_macros() + /mob/living/carbon/Login() . = ..() if(internals && internal) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index dcef99ee55..11ea1ffbba 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -52,6 +52,7 @@ move_intent = move_intents[1] if(ispath(move_intent)) move_intent = decls_repository.get_decl(move_intent) + set_focus(src) START_PROCESSING(SSmobs, src) /mob/proc/show_message(msg, type, alt, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2) @@ -226,6 +227,7 @@ return log(2, mob_size / MOB_MEDIUM) /mob/proc/Life() + set waitfor = FALSE // if(organStructure) // organStructure.ProcessOrgans() return diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index d8518f7398..7788b954f7 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -417,7 +417,7 @@ var/list/intents = list(I_HELP,I_DISARM,I_GRAB,I_HURT) else return I_HURT //change a mob's act-intent. Input the intent as a string such as "help" or use "right"/"left -/mob/verb/a_intent_change(input as text) +/mob/proc/a_intent_change(input) set name = "a-intent" set hidden = 1 diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index d0bc16c97a..e179a8f3b6 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -180,7 +180,10 @@ return if(!mob) return // Moved here to avoid nullrefs below - return mob.SelfMove(direction) + . = mob.SelfMove(direction) + if(.) + next_move_dir_add = 0 + next_move_dir_sub = 0 // Checks whether this mob is allowed to move in space // Return 1 for movement, 0 for none, diff --git a/code/modules/mob/new_player/login.dm b/code/modules/mob/new_player/login.dm index 952ee59b5b..53d6f81196 100644 --- a/code/modules/mob/new_player/login.dm +++ b/code/modules/mob/new_player/login.dm @@ -24,6 +24,8 @@ if(client) new_player_panel() + if(SSinput.initialized) + client.set_macros() if(!SScharacter_setup.initialized) SScharacter_setup.newplayers_requiring_init += src diff --git a/code/modules/mob/typing_indicator.dm b/code/modules/mob/typing_indicator.dm index 4996d5d526..bf15cb8833 100644 --- a/code/modules/mob/typing_indicator.dm +++ b/code/modules/mob/typing_indicator.dm @@ -62,6 +62,16 @@ I IS TYPIN'!' if(message) say_verb(message) +/mob/verb/whisper_wrapper() + set name = ".Whisper" + set hidden = 1 + + create_typing_indicator() + var/message = input(src, "", "whisper (text)") as text|null + remove_typing_indicator() + if(message) + whisper(message) + /mob/verb/me_wrapper() set name = ".Me" set hidden = 1 diff --git a/interface/interface.dm b/interface/interface.dm index 10a758ab2c..737b3cc538 100644 --- a/interface/interface.dm +++ b/interface/interface.dm @@ -69,10 +69,11 @@ var/admin = {" Admin: -\tF5 = стать призраком/вернуться в тело -\tF6 = панель игроков -\tF7 = отправить ПМ -\tF8 = невидимость +\tF5 = Админ чат +\tF6 = стать призраком/вернуться в тело +\tF7 = панель игроков +\tF8 = отправить ПМ +\tF9 = невидимость "} var/hotkey_mode = {" @@ -88,8 +89,9 @@ Hotkey-Mode: (Режим хоткеев включен) \te = надеть \tr = метнуть \tt = сказать -\t5 = эмоция +\tm = эмоция \tx = поменять руку +\tc = перестать тащить \tz = активировать объект в руке (или y) \tj = переключить режим прицеливания \tf = переключить-взаимодействие-влево diff --git a/interface/skin.dmf b/interface/skin.dmf index e1813b9894..3da6c13eb7 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -1,870 +1,36 @@ -macro "borghotkeymode" - elem - name = "Tab" - command = ".winset \"mainwindow.macro=borgmacro hotkey_toggle.is-checked=false input.focus=true input.background-color=#d3b5b5\"" - elem - name = "Center+REP" - command = ".center" - elem - name = "ALT+Return" - command = "Toggle-Fullscreen" - elem - name = "Northeast" - command = ".northeast" - elem - name = "Southeast" - command = ".southeast" - elem - name = "Southwest" - command = ".southwest" - elem - name = "Northwest" - command = ".northwest" - elem - name = "ALT+West" - command = "westfaceperm" - elem - name = "CTRL+West" - command = "westface" - elem - name = "West+REP" - command = ".moveleft" - elem - name = "ALT+North" - command = "northfaceperm" - elem - name = "CTRL+North" - command = "northface" - elem - name = "North+REP" - command = ".moveup" - elem - name = "ALT+East" - command = "eastfaceperm" - elem - name = "CTRL+East" - command = "eastface" - elem - name = "East+REP" - command = ".moveright" - elem - name = "ALT+South" - command = "southfaceperm" - elem - name = "CTRL+South" - command = "southface" - elem - name = "South+REP" - command = ".movedown" - elem - name = "Insert" - command = "a-intent right" - elem - name = "Delete" - command = "delete-key-pressed" - elem - name = "1" - command = "toggle-module 1" - elem - name = "CTRL+1" - command = "toggle-module 1" - elem - name = "2" - command = "toggle-module 2" - elem - name = "CTRL+2" - command = "toggle-module 2" - elem - name = "3" - command = "toggle-module 3" - elem - name = "CTRL+3" - command = "toggle-module 3" - elem - name = "4" - command = "a-intent left" - elem - name = "CTRL+4" - command = "a-intent left" - elem - name = "5" - command = ".me" - elem - name = "A+REP" - command = ".moveleft" - elem - name = "CTRL+A+REP" - command = ".moveleft" - elem - name = "D+REP" - command = ".moveright" - elem - name = "CTRL+D+REP" - command = ".moveright" - elem - name = "F" - command = "a-intent left" - elem - name = "CTRL+F" - command = "a-intent left" - elem - name = "G" - command = "a-intent right" - elem - name = "CTRL+G" - command = "a-intent right" - elem - name = "J" - command = "toggle-gun-mode" - elem - name = "CTRL+J" - command = "toggle-gun-mode" - elem - name = "Q" - command = "unequip-module" - elem - name = "CTRL+Q" - command = "unequip-module" - elem - name = "R" - command = ".southwest" - elem - name = "CTRL+R" - command = ".southwest" - elem "s_key" - name = "S+REP" - command = ".movedown" - elem - name = "CTRL+S+REP" - command = ".movedown" - elem - name = "T" - command = ".say" - elem "w_key" - name = "W+REP" - command = ".moveup" - elem - name = "CTRL+W+REP" - command = ".moveup" - elem - name = "X" - command = ".northeast" - elem - name = "CTRL+X" - command = ".northeast" - elem - name = "Y" - command = "Activate-Held-Object" - elem - name = "CTRL+Y" - command = "Activate-Held-Object" - elem - name = "Z" - command = "Activate-Held-Object" - elem - name = "CTRL+Z" - command = "Activate-Held-Object" - elem - name = "Numpad1" - command = "body-r-leg" - elem - name = "Numpad2" - command = "body-groin" - elem - name = "Numpad3" - command = "body-l-leg" - elem - name = "Numpad4" - command = "body-r-arm" - elem - name = "Numpad5" - command = "body-chest" - elem - name = "Numpad6" - command = "body-l-arm" - elem - name = "Numpad8" - command = "body-toggle-head" - elem - name = "F1" - command = "adminhelp" - elem - name = "CTRL+SHIFT+F1+REP" - command = ".options" - elem - name = "F2" - command = "ooc" - elem - name = "F2+REP" - command = ".screenshot auto" - elem - name = "SHIFT+F2+REP" - command = ".screenshot" - elem - name = "F3" - command = ".say" - elem - name = "F4" - command = ".me" - elem - name = "F5" - command = "asay" - elem - name = "F6" - command = "Player-Panel" - elem - name = "F7" - command = "ssay" - elem - name = "F8" - command = "Invisimin" - elem - name = "F12" - command = "F12" - elem - name = "," - command = "move-upwards" - elem - name = "." - command = "move-down" - -macro "macro" - elem - name = "Tab" - command = ".winset \"mainwindow.macro=hotkeymode hotkey_toggle.is-checked=true mapwindow.map.focus=true input.background-color=#f0f0f0\"" - elem - name = "Center+REP" - command = ".center" - elem - name = "ALT+Return" - command = "Toggle-Fullscreen" - elem - name = "Northeast" - command = ".northeast" - elem - name = "Southeast" - command = ".southeast" - elem - name = "Southwest" - command = ".southwest" - elem - name = "Northwest" - command = ".northwest" - elem - name = "ALT+West" - command = "westfaceperm" - elem - name = "CTRL+West" - command = "westface" - elem - name = "West+REP" - command = ".moveleft" - elem - name = "ALT+North" - command = "northfaceperm" - elem - name = "CTRL+North" - command = "northface" - elem - name = "North+REP" - command = ".moveup" - elem - name = "ALT+East" - command = "eastfaceperm" - elem - name = "CTRL+East" - command = "eastface" - elem - name = "East+REP" - command = ".moveright" - elem - name = "ALT+South" - command = "southfaceperm" - elem - name = "CTRL+South" - command = "southface" - elem - name = "South+REP" - command = ".movedown" - elem - name = "Insert" - command = "a-intent right" - elem - name = "Delete" - command = "delete-key-pressed" - elem - name = "CTRL+1" - command = "a-intent help" - elem - name = "CTRL+2" - command = "a-intent disarm" - elem - name = "CTRL+3" - command = "a-intent grab" - elem - name = "CTRL+4" - command = "a-intent harm" - elem - name = "CTRL+A+REP" - command = ".moveleft" - elem - name = "CTRL+D+REP" - command = ".moveright" - elem - name = "CTRL+E" - command = "quick-equip" - elem - name = "CTRL+F" - command = "a-intent left" - elem - name = "CTRL+G" - command = "a-intent right" - elem - name = "CTRL+SHIFT+G" - command = ".configure graphics-hwmode on" - elem - name = "CTRL+Q" - command = ".northwest" - elem - name = "CTRL+R" - command = ".southwest" - elem - name = "CTRL+S+REP" - command = ".movedown" - elem - name = "CTRL+W+REP" - command = ".moveup" - elem - name = "CTRL+X" - command = ".northeast" - elem - name = "CTRL+Y" - command = "Activate-Held-Object" - elem - name = "CTRL+Z" - command = "Activate-Held-Object" - elem - name = "CTRL+Numpad1" - command = "body-r-leg" - elem - name = "CTRL+Numpad2" - command = "body-groin" - elem - name = "CTRL+Numpad3" - command = "body-l-leg" - elem - name = "CTRL+Numpad4" - command = "body-r-arm" - elem - name = "CTRL+Numpad5" - command = "body-chest" - elem - name = "CTRL+Numpad6" - command = "body-l-arm" - elem - name = "CTRL+Numpad8" - command = "body-toggle-head" - elem - name = "CTRL+Add" - command = "move-upwards" - elem - name = "CTRL+Subtract" - command = "move-down" - elem - name = "F1" - command = "adminhelp" - elem - name = "CTRL+SHIFT+F1+REP" - command = ".options" - elem - name = "F2" - command = "ooc" - elem - name = "F2+REP" - command = ".screenshot auto" - elem - name = "SHIFT+F2+REP" - command = ".screenshot" - elem - name = "F3" - command = ".say" - elem - name = "F4" - command = ".me" - elem - name = "F5" - command = "asay" - elem - name = "F6" - command = "Player-Panel" - elem - name = "F7" - command = "ssay" - elem - name = "F8" - command = "Invisimin" - elem - name = "F12" - command = "F12" - -macro "hotkeymode" - elem - name = "Tab" - command = ".winset \"mainwindow.macro=macro hotkey_toggle.is-checked=false input.focus=true input.background-color=#d3b5b5\"" - elem - name = "Center+REP" - command = ".center" - elem - name = "ALT+Return" - command = "Toggle-Fullscreen" - elem - name = "Northeast" - command = ".northeast" - elem - name = "Southeast" - command = ".southeast" - elem - name = "Southwest" - command = ".southwest" - elem - name = "Northwest" - command = ".northwest" - elem - name = "ALT+West" - command = "westfaceperm" - elem - name = "CTRL+West" - command = "westface" - elem - name = "West+REP" - command = ".moveleft" - elem - name = "ALT+North" - command = "northfaceperm" - elem - name = "CTRL+North" - command = "northface" - elem - name = "North+REP" - command = ".moveup" - elem - name = "ALT+East" - command = "eastfaceperm" - elem - name = "CTRL+East" - command = "eastface" - elem - name = "East+REP" - command = ".moveright" - elem - name = "ALT+South" - command = "southfaceperm" - elem - name = "CTRL+South" - command = "southface" - elem - name = "South+REP" - command = ".movedown" - elem - name = "Insert" - command = "a-intent right" - elem - name = "Delete" - command = "delete-key-pressed" - elem - name = "1" - command = "a-intent help" - elem - name = "CTRL+1" - command = "a-intent help" - elem - name = "2" - command = "a-intent disarm" - elem - name = "CTRL+2" - command = "a-intent disarm" - elem - name = "3" - command = "a-intent grab" - elem - name = "CTRL+3" - command = "a-intent grab" - elem - name = "4" - command = "a-intent harm" - elem - name = "CTRL+4" - command = "a-intent harm" - elem - name = "5" - command = ".me" - elem - name = "A+REP" - command = ".moveleft" - elem - name = "CTRL+A+REP" - command = ".moveleft" - elem - name = "D+REP" - command = ".moveright" - elem - name = "CTRL+D+REP" - command = ".moveright" - elem - name = "E" - command = "quick-equip" - elem - name = "CTRL+E" - command = "quick-equip" - elem - name = "F" - command = "a-intent left" - elem - name = "CTRL+F" - command = "a-intent left" - elem - name = "G" - command = "a-intent right" - elem - name = "CTRL+G" - command = "a-intent right" - elem - name = "H" - command = "holster" - elem - name = "CTRL+H" - command = "holster" - elem - name = "J" - command = "toggle-gun-mode" - elem - name = "CTRL+J" - command = "toggle-gun-mode" - elem - name = "Q" - command = ".northwest" - elem - name = "CTRL+Q" - command = ".northwest" - elem - name = "R" - command = ".southwest" - elem - name = "CTRL+R" - command = ".southwest" - elem "s_key" - name = "S+REP" - command = ".movedown" - elem - name = "CTRL+S+REP" - command = ".movedown" - elem - name = "T" - command = ".say" - elem "w_key" - name = "W+REP" - command = ".moveup" - elem - name = "CTRL+W+REP" - command = ".moveup" - elem - name = "X" - command = ".northeast" - elem - name = "CTRL+X" - command = ".northeast" - elem - name = "Y" - command = "Activate-Held-Object" - elem - name = "CTRL+Y" - command = "Activate-Held-Object" - elem - name = "Z" - command = "Activate-Held-Object" - elem - name = "CTRL+Z" - command = "Activate-Held-Object" - elem - name = "Numpad1" - command = "body-r-leg" - elem - name = "Numpad2" - command = "body-groin" - elem - name = "Numpad3" - command = "body-l-leg" - elem - name = "Numpad4" - command = "body-r-arm" - elem - name = "Numpad5" - command = "body-chest" - elem - name = "Numpad6" - command = "body-l-arm" - elem - name = "Numpad8" - command = "body-toggle-head" - elem - name = "F1" - command = "adminhelp" - elem - name = "CTRL+SHIFT+F1+REP" - command = ".options" - elem - name = "F2" - command = "ooc" - elem - name = "F2+REP" - command = ".screenshot auto" - elem - name = "SHIFT+F2+REP" - command = ".screenshot" - elem - name = "F3" - command = ".say" - elem - name = "F4" - command = ".me" - elem - name = "F5" - command = "asay" - elem - name = "F6" - command = "Player-Panel" - elem - name = "F7" - command = "ssay" - elem - name = "F8" - command = "Invisimin" - elem - name = "F12" - command = "F12" - elem - name = "," - command = "move-upwards" - elem - name = "." - command = "move-down" - elem - name = "SHIFT" - command = "setmovingquickly" - elem - name = "SHIFT+UP" - command = "setmovingslowly" - -macro "borgmacro" - elem - name = "Tab" - command = ".winset \"mainwindow.macro=borghotkeymode hotkey_toggle.is-checked=true mapwindow.map.focus=true input.background-color=#f0f0f0\"" - elem - name = "Center+REP" - command = ".center" - elem - name = "ALT+Return" - command = "Toggle-Fullscreen" - elem - name = "Northeast" - command = ".northeast" - elem - name = "Southeast" - command = ".southeast" - elem - name = "Southwest" - command = ".southwest" - elem - name = "Northwest" - command = ".northwest" - elem - name = "ALT+West" - command = "westfaceperm" - elem - name = "CTRL+West" - command = "westface" - elem - name = "West+REP" - command = ".moveleft" - elem - name = "ALT+North" - command = "northfaceperm" - elem - name = "CTRL+North" - command = "northface" - elem - name = "North+REP" - command = ".moveup" - elem - name = "ALT+East" - command = "eastfaceperm" - elem - name = "CTRL+East" - command = "eastface" - elem - name = "East+REP" - command = ".moveright" - elem - name = "ALT+South" - command = "southfaceperm" - elem - name = "CTRL+South" - command = "southface" - elem - name = "South+REP" - command = ".movedown" - elem - name = "Insert" - command = "a-intent right" - elem - name = "Delete" - command = "delete-key-pressed" - elem - name = "CTRL+1" - command = "toggle-module 1" - elem - name = "CTRL+2" - command = "toggle-module 2" - elem - name = "CTRL+3" - command = "toggle-module 3" - elem - name = "CTRL+4" - command = "a-intent left" - elem - name = "CTRL+A+REP" - command = ".moveleft" - elem - name = "CTRL+D+REP" - command = ".moveright" - elem - name = "CTRL+F" - command = "a-intent left" - elem - name = "CTRL+G" - command = "a-intent right" - elem - name = "CTRL+Q" - command = ".northwest" - elem - name = "CTRL+R" - command = ".southwest" - elem - name = "CTRL+S+REP" - command = ".movedown" - elem - name = "CTRL+W+REP" - command = ".moveup" - elem - name = "CTRL+X" - command = ".northeast" - elem - name = "CTRL+Y" - command = "Activate-Held-Object" - elem - name = "CTRL+Z" - command = "Activate-Held-Object" - elem - name = "CTRL+Numpad1" - command = "body-r-leg" - elem - name = "CTRL+Numpad2" - command = "body-groin" - elem - name = "CTRL+Numpad3" - command = "body-l-leg" - elem - name = "CTRL+Numpad4" - command = "body-r-arm" - elem - name = "CTRL+Numpad5" - command = "body-chest" - elem - name = "CTRL+Numpad6" - command = "body-l-arm" - elem - name = "CTRL+Numpad8" - command = "body-toggle-head" - elem - name = "CTRL+Add" - command = "move-upwards" - elem - name = "CTRL+Subtract" - command = "move-down" - elem - name = "F1" - command = "adminhelp" - elem - name = "CTRL+SHIFT+F1+REP" - command = ".options" - elem - name = "F2" - command = "ooc" - elem - name = "F2+REP" - command = ".screenshot auto" - elem - name = "SHIFT+F2+REP" - command = ".screenshot" - elem - name = "F3" - command = ".say" - elem - name = "F4" - command = ".me" - elem - name = "F5" - command = "asay" - elem - name = "F6" - command = "Player-Panel" - elem - name = "F7" - command = "ssay" - elem - name = "F8" - command = "Invisimin" - elem - name = "F12" - command = "F12" - +macro "default" menu "menu" - elem + elem name = "&File" command = "" saved-params = "is-checked" - elem - name = "&Quick screenshot\tF2" - command = ".screenshot auto" - category = "&File" - saved-params = "is-checked" - elem + elem name = "&Save screenshot as...\tShift+F2" command = ".screenshot" category = "&File" saved-params = "is-checked" - elem + elem name = "" command = "" category = "&File" saved-params = "is-checked" - elem + elem name = "&Reconnect" command = ".reconnect" category = "&File" saved-params = "is-checked" - elem + elem name = "&Check ping" command = ".ping" category = "&File" saved-params = "is-checked" - elem + elem name = "&Quit" command = ".quit" category = "&File" saved-params = "is-checked" - elem + elem name = "&Size" command = "" saved-params = "is-checked" @@ -911,7 +77,7 @@ menu "menu" can-check = true group = "size" saved-params = "is-checked" - elem + elem name = "&Scaling" command = "" saved-params = "is-checked" @@ -936,16 +102,16 @@ menu "menu" can-check = true group = "scale" saved-params = "is-checked" - elem + elem name = "&Help" command = "" saved-params = "is-checked" - elem + elem name = "&Admin help\tF1" command = "adminhelp" category = "&Help" saved-params = "is-checked" - elem + elem name = "&Hotkeys" command = "hotkeys-help" category = "&Help" @@ -973,16 +139,6 @@ window "mainwindow" anchor2 = none is-visible = false saved-params = "" - elem "hotkey_toggle" - type = BUTTON - pos = 560,420 - size = 80x20 - anchor1 = 100,100 - anchor2 = none - saved-params = "" - text = "Hotkey Toggle" - command = ".winset \"mainwindow.macro!=macro ? mainwindow.macro=macro hotkey_toggle.is-checked=false input.focus=true : mainwindow.macro=hotkeymode hotkey_toggle.is-checked=true mapwindow.map.focus=true\"" - button-type = pushbox elem "mainvsplit" type = CHILD pos = 3,0 @@ -995,7 +151,7 @@ window "mainwindow" elem "input" type = INPUT pos = 3,420 - size = 517x20 + size = 600x20 anchor1 = 0,100 anchor2 = 100,100 background-color = #d3b5b5 @@ -1004,7 +160,7 @@ window "mainwindow" saved-params = "command" elem "saybutton" type = BUTTON - pos = 520,420 + pos = 600,420 size = 40x20 anchor1 = 100,100 anchor2 = none @@ -1308,4 +464,3 @@ window "TelecommsIDE" command = "cancel" multi-line = true no-command = true -