diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index 18add6f93e1..793d131faaa 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -89,6 +89,8 @@ ///Max length of a keypress command before it's considered to be a forged packet/bogus command #define MAX_KEYPRESS_COMMANDLENGTH 16 +///Maximum keys that can be bound to one button +#define MAX_COMMANDS_PER_KEY 5 ///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 diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index 165dae58383..85b2768bfdc 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -23,6 +23,19 @@ GLOBAL_VAR_INIT(cmp_field, "name") /proc/cmp_records_dsc(datum/data/record/a, datum/data/record/b) return sorttext(a.fields[GLOB.cmp_field], b.fields[GLOB.cmp_field]) +// Datum cmp with vars is always slower than a specialist cmp proc, use your judgement. +/proc/cmp_datum_numeric_asc(datum/a, datum/b, variable) + return cmp_numeric_asc(a.vars[variable], b.vars[variable]) + +/proc/cmp_datum_numeric_dsc(datum/a, datum/b, variable) + return cmp_numeric_dsc(a.vars[variable], b.vars[variable]) + +/proc/cmp_datum_text_asc(datum/a, datum/b, variable) + return sorttext(b.vars[variable], a.vars[variable]) + +/proc/cmp_datum_text_dsc(datum/a, datum/b, variable) + return sorttext(a.vars[variable], b.vars[variable]) + /proc/cmp_ckey_asc(client/a, client/b) return sorttext(b.ckey, a.ckey) diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index e33cc754baf..3166e4e26fa 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -50,6 +50,20 @@ GLOB.materials_list[D.id] = D sortList(GLOB.materials_list) + // Keybindings + for(var/KB in subtypesof(/datum/keybinding)) + var/datum/keybinding/keybinding = KB + if(!initial(keybinding.key)) + continue + var/datum/keybinding/instance = new keybinding + GLOB.keybindings_by_name[initial(instance.name)] = instance + if (!GLOB.keybinding_list_by_key[initial(instance.key)]) + GLOB.keybinding_list_by_key[initial(instance.key)] = list() + GLOB.keybinding_list_by_key[initial(instance.key)] += instance.name + // Sort all the keybindings by their weight + for(var/key in GLOB.keybinding_list_by_key) + GLOB.keybinding_list_by_key[key] = sortList(GLOB.keybinding_list_by_key[key]) + GLOB.emote_list = init_emote_list() init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes) @@ -71,3 +85,4 @@ for(var/path in subtypesof(prototype)) L+= path return L + diff --git a/code/__HELPERS/sanitize_values.dm b/code/__HELPERS/sanitize_values.dm index 81cb61fe4ba..467deee6e37 100644 --- a/code/__HELPERS/sanitize_values.dm +++ b/code/__HELPERS/sanitize_values.dm @@ -11,6 +11,12 @@ return text return default +/proc/sanitize_islist(value, default) + if(islist(value) && length(value)) + return value + if(default) + return default + /proc/sanitize_inlist(value, list/List, default) if(value in List) return value diff --git a/code/_globalvars/lists/client.dm b/code/_globalvars/lists/client.dm new file mode 100644 index 00000000000..b37c2a81101 --- /dev/null +++ b/code/_globalvars/lists/client.dm @@ -0,0 +1,2 @@ +GLOBAL_LIST_EMPTY(keybinding_list_by_key) +GLOBAL_LIST_EMPTY(keybindings_by_name) diff --git a/code/controllers/subsystem/input.dm b/code/controllers/subsystem/input.dm index 70604bfba0d..cca0358f9fc 100644 --- a/code/controllers/subsystem/input.dm +++ b/code/controllers/subsystem/input.dm @@ -6,7 +6,7 @@ SUBSYSTEM_DEF(input) priority = FIRE_PRIORITY_INPUT runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY - var/list/macro_sets + var/list/macro_set var/list/movement_keys /datum/controller/subsystem/input/Initialize() @@ -22,77 +22,13 @@ SUBSYSTEM_DEF(input) // This is for when macro sets are eventualy datumized /datum/controller/subsystem/input/proc/setup_default_macro_sets() - var/list/static/default_macro_sets + macro_set = list( + "Any" = "\"KeyDown \[\[*\]\]\"", + "Any+UP" = "\"KeyUp \[\[*\]\]\"", + "Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"", + "Tab" = "\".winset \\\"input.focus=true?map.focus=true input.background-color=[COLOR_INPUT_DISABLED]:input.focus=true input.background-color=[COLOR_INPUT_ENABLED]\\\"\"", + "Escape" = "\".winset \\\"input.text=\\\"\\\"\\\"\"") - 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]\\\"\"", - "O" = "ooc", - "T" = "say", - "M" = "me", - "Back" = "\".winset \\\"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]\\\"\"", - "Ctrl+T" = "say", - "Ctrl+O" = "ooc", - ), - "old_hotkeys" = list( - "Tab" = "\".winset \\\"mainwindow.macro=old_default input.focus=true input.background-color=[COLOR_INPUT_ENABLED]\\\"\"", - "O" = "ooc", - "T" = "say", - "M" = "me", - "Back" = "\".winset \\\"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", - "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 - "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 - ) - - 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 diff --git a/code/datums/keybinding/_defines.dm b/code/datums/keybinding/_defines.dm new file mode 100644 index 00000000000..b531336ef04 --- /dev/null +++ b/code/datums/keybinding/_defines.dm @@ -0,0 +1,16 @@ +#define CATEGORY_CLIENT "CLIENT" +#define CATEGORY_ADMIN "ADMIN" +#define CATEGORY_XENO "XENO" +#define CATEGORY_CARBON "CARBON" +#define CATEGORY_HUMAN "HUMAN" +#define CATEGORY_ROBOT "ROBOT" +#define CATEGORY_MISC "MISC" + +#define WEIGHT_HIGHEST 0 +#define WEIGHT_ADMIN 10 +#define WEIGHT_CLIENT 20 +#define WEIGHT_ROBOT 30 +#define WEIGHT_MOB 40 +#define WEIGHT_LIVING 50 +#define WEIGHT_DEAD 60 +#define WEIGHT_LOWEST 999 diff --git a/code/datums/keybinding/_keybindings.dm b/code/datums/keybinding/_keybindings.dm new file mode 100644 index 00000000000..eeb5eed7853 --- /dev/null +++ b/code/datums/keybinding/_keybindings.dm @@ -0,0 +1,13 @@ +/datum/keybinding + var/key + var/name + var/full_name + var/description = "" + var/category = CATEGORY_MISC + var/weight = WEIGHT_LOWEST + +/datum/keybinding/proc/down(client/user) + return FALSE + +/datum/keybinding/proc/up(client/user) + return FALSE diff --git a/code/datums/keybinding/admin.dm b/code/datums/keybinding/admin.dm new file mode 100644 index 00000000000..07deb6a7817 --- /dev/null +++ b/code/datums/keybinding/admin.dm @@ -0,0 +1,74 @@ +/datum/keybinding/admin + category = CATEGORY_ADMIN + weight = WEIGHT_ADMIN + + +/datum/keybinding/admin/admin_say + key = "F3" + name = "admin_say" + full_name = "Admin say" + description = "Talk with other admins." + +/datum/keybinding/admin/admin_say/down(client/user) + user.get_admin_say() + return TRUE + +/datum/keybinding/admin/admin_ghost + key = "F5" + name = "admin_ghost" + full_name = "Aghost" + description = "Go ghost" + +/datum/keybinding/admin/admin_ghost/down(client/user) + user.admin_ghost() + return TRUE + +/datum/keybinding/admin/player_panel_new + key = "F6" + name = "player_panel_new" + full_name = "Player Panel New" + description = "Opens up the new player panel" + +/datum/keybinding/admin/player_panel_new/down(client/user) + user.holder.player_panel_new() + return TRUE + +/datum/keybinding/admin/toggle_buildmode_self + key = "F7" + name = "toggle_buildmode_self" + full_name = "Toggle Buildmode Self" + description = "Toggles buildmode" + +/datum/keybinding/admin/toggle_buildmode_self/down(client/user) + user.togglebuildmodeself() + return TRUE + +/datum/keybinding/admin/stealthmode + key = "Ctrl-F8" + name = "stealth_mode" + full_name = "Stealth mode" + description = "Enters stealth mode" + +/datum/keybinding/admin/stealthmode/down(client/user) + user.stealth() + return TRUE + +/datum/keybinding/admin/invisimin + key = "F8" + name = "invisimin" + full_name = "Admin invisibility" + description = "Toggles ghost-like invisibility (Don't abuse this)" + +/datum/keybinding/admin/invisimin/down(client/user) + user.invisimin() + return TRUE + +/datum/keybinding/admin/deadsay + key = "F10" + name = "dsay" + full_name = "deadsay" + description = "Allows you to send a message to dead chat" + +/datum/keybinding/admin/deadsay/down(client/user) + user.get_dead_say() + return TRUE diff --git a/code/datums/keybinding/carbon.dm b/code/datums/keybinding/carbon.dm new file mode 100644 index 00000000000..98bfff54c71 --- /dev/null +++ b/code/datums/keybinding/carbon.dm @@ -0,0 +1,66 @@ +/datum/keybinding/carbon + category = CATEGORY_CARBON + weight = WEIGHT_MOB + + +/datum/keybinding/carbon/toggle_throw_mode + key = "R" + name = "toggle_throw_mode" + full_name = "Toggle throw mode" + description = "Toggle throwing the current item or not." + category = CATEGORY_CARBON + +/datum/keybinding/carbon/toggle_throw_mode/down(client/user) + if (!iscarbon(user.mob)) + return FALSE + var/mob/living/carbon/C = user.mob + C.toggle_throw_mode() + return TRUE + + +/datum/keybinding/carbon/select_help_intent + key = "1" + name = "select_help_intent" + full_name = "Select help intent" + description = "" + category = CATEGORY_CARBON + +/datum/keybinding/carbon/select_help_intent/down(client/user) + user.mob?.a_intent_change(INTENT_HELP) + return TRUE + + +/datum/keybinding/carbon/select_disarm_intent + key = "2" + name = "select_disarm_intent" + full_name = "Select disarm intent" + description = "" + category = CATEGORY_CARBON + +/datum/keybinding/carbon/select_disarm_intent/down(client/user) + user.mob?.a_intent_change(INTENT_DISARM) + return TRUE + + +/datum/keybinding/carbon/select_grab_intent + key = "3" + name = "select_grab_intent" + full_name = "Select grab intent" + description = "" + category = CATEGORY_CARBON + +/datum/keybinding/carbon/select_grab_intent/down(client/user) + user.mob?.a_intent_change(INTENT_GRAB) + return TRUE + + +/datum/keybinding/carbon/select_harm_intent + key = "4" + name = "select_harm_intent" + full_name = "Select harm intent" + description = "" + category = CATEGORY_CARBON + +/datum/keybinding/carbon/select_harm_intent/down(client/user) + user.mob?.a_intent_change(INTENT_HARM) + return TRUE diff --git a/code/datums/keybinding/client.dm b/code/datums/keybinding/client.dm new file mode 100644 index 00000000000..5d96cead8ae --- /dev/null +++ b/code/datums/keybinding/client.dm @@ -0,0 +1,45 @@ +/datum/keybinding/client + category = CATEGORY_CLIENT + weight = WEIGHT_HIGHEST + + +/datum/keybinding/client/admin_help + key = "F1" + name = "admin_help" + full_name = "Admin Help" + description = "Ask an admin for help." + +/datum/keybinding/client/admin_help/down(client/user) + user.get_adminhelp() + return TRUE + + +/datum/keybinding/client/screenshot + key = "F2" + name = "screenshot" + full_name = "Screenshot" + description = "Take a screenshot." + +/datum/keybinding/client/screenshot/down(client/user) + winset(user, null, "command=.screenshot [!user.keys_held["shift"] ? "auto" : ""]") + return TRUE + +/datum/keybinding/client/minimal_hud + key = "F12" + name = "minimal_hud" + full_name = "Minimal HUD" + description = "Hide most HUD features" + +/datum/keybinding/client/minimal_hud/down(client/user) + user.mob.button_pressed_F12() + return TRUE + +/datum/keybinding/client/ooc + key = "O" + name = "ooc" + full_name = "OOC" + description = "" + +/datum/keybinding/client/ooc/down(client/user) + user.ooc_wrapper() + return TRUE diff --git a/code/datums/keybinding/human.dm b/code/datums/keybinding/human.dm new file mode 100644 index 00000000000..9bdeb506082 --- /dev/null +++ b/code/datums/keybinding/human.dm @@ -0,0 +1,37 @@ +/datum/keybinding/human + category = CATEGORY_HUMAN + weight = WEIGHT_MOB + + +/datum/keybinding/human/quick_equip + key = "E" + name = "quick_equip" + full_name = "Quick Equip" + description = "Quickly puts an item in the best slot available" + +/datum/keybinding/human/quick_equip/down(client/user) + var/mob/living/carbon/human/H = user.mob + H.quick_equip() + return TRUE + +/datum/keybinding/human/quick_equipbelt + key = "Shift-E" + name = "quick_equipbelt" + full_name = "Quick equip belt" + description = "Put held thing in belt or take out most recent thing from belt" + +/datum/keybinding/human/quick_equipbelt/down(client/user) + var/mob/living/carbon/human/H = user.mob + H.smart_equipbelt() + return TRUE + +/datum/keybinding/human/bag_equip + key = "Shift-B" + name = "bag_equip" + full_name = "Bag equip" + description = "Put held thing in backpack or take out most recent thing from backpack" + +/datum/keybinding/human/bag_equip/down(client/user) + var/mob/living/carbon/human/H = user.mob + H.smart_equipbag() + return TRUE diff --git a/code/datums/keybinding/living.dm b/code/datums/keybinding/living.dm new file mode 100644 index 00000000000..94082224a7f --- /dev/null +++ b/code/datums/keybinding/living.dm @@ -0,0 +1,15 @@ +/datum/keybinding/living + category = CATEGORY_HUMAN + weight = WEIGHT_MOB + + +/datum/keybinding/living/resist + key = "B" + name = "resist" + full_name = "Resist" + description = "Break free of your current state. Handcuffed? on fire? Resist!" + +/datum/keybinding/living/resist/down(client/user) + var/mob/living/L = user.mob + L.resist() + return TRUE diff --git a/code/datums/keybinding/mob.dm b/code/datums/keybinding/mob.dm new file mode 100644 index 00000000000..5a1fb904ab2 --- /dev/null +++ b/code/datums/keybinding/mob.dm @@ -0,0 +1,233 @@ +/datum/keybinding/mob + category = CATEGORY_HUMAN + weight = WEIGHT_MOB + + +/datum/keybinding/mob/face_north + key = "Ctrl-W" + name = "face_north" + full_name = "Face North" + description = "" + +/datum/keybinding/mob/face_north/down(client/user) + var/mob/M = user.mob + M.northface() + return TRUE + + +/datum/keybinding/mob/face_east + key = "Ctrl-D" + name = "face_east" + full_name = "Face East" + description = "" + +/datum/keybinding/mob/face_east/down(client/user) + var/mob/M = user.mob + M.eastface() + return TRUE + + +/datum/keybinding/mob/face_south + key = "Ctrl-S" + name = "face_south" + full_name = "Face South" + description = "" + +/datum/keybinding/mob/face_south/down(client/user) + var/mob/M = user.mob + M.southface() + return TRUE + +/datum/keybinding/mob/face_west + key = "Ctrl-A" + name = "face_west" + full_name = "Face West" + description = "" + +/datum/keybinding/mob/face_west/down(client/user) + var/mob/M = user.mob + M.westface() + return TRUE + +/datum/keybinding/mob/stop_pulling + key = "H" + name = "stop_pulling" + full_name = "Stop pulling" + description = "" + +/datum/keybinding/mob/stop_pulling/down(client/user) + var/mob/M = user.mob + if (M.pulling) + to_chat(user, "You are not pulling anything.") + else + M.stop_pulling() + return TRUE + +/datum/keybinding/mob/cycle_intent_right + key = "Home" + name = "cycle_intent_right" + full_name = "cycle intent right" + description = "" + +/datum/keybinding/mob/cycle_intent_right/down(client/user) + var/mob/M = user.mob + M.a_intent_change(INTENT_HOTKEY_RIGHT) + return TRUE + +/datum/keybinding/mob/cycle_intent_left + key = "Insert" + name = "cycle_intent_left" + full_name = "cycle intent left" + description = "" + +/datum/keybinding/mob/cycle_intent_left/down(client/user) + var/mob/M = user.mob + M.a_intent_change(INTENT_HOTKEY_LEFT) + return TRUE + +/datum/keybinding/mob/swap_hands + key = "X" + name = "swap_hands" + full_name = "Swap hands" + description = "" + +/datum/keybinding/mob/swap_hands/down(client/user) + var/mob/M = user.mob + M.swap_hand() + return TRUE + +/datum/keybinding/mob/say + key = "T" + name = "say" + full_name = "Say" + description = "" + +/datum/keybinding/mob/say/down(client/user) + var/mob/M = user.mob + M.say_wrapper() + return TRUE + +/datum/keybinding/mob/me + key = "M" + name = "me" + full_name = "Me" + description = "" + +/datum/keybinding/mob/me/down(client/user) + var/mob/M = user.mob + M.me_verb() + return TRUE + +/datum/keybinding/mob/activate_inhand + key = "Z" + name = "activate_inhand" + full_name = "Activate in-hand" + description = "Uses whatever item you have inhand" + +/datum/keybinding/mob/activate_inhand/down(client/user) + var/mob/M = user.mob + M.mode() + return TRUE + +/datum/keybinding/mob/drop_item + key = "Q" + name = "drop_item" + full_name = "Drop Item" + description = "" + +/datum/keybinding/mob/drop_item/down(client/user) + if(iscyborg(user.mob)) //cyborgs can't drop items + return FALSE + var/mob/M = user.mob + var/obj/item/I = M.get_active_held_item() + if(!I) + to_chat(user, "You have nothing to drop in your hand!") + else + user.mob.dropItemToGround(I) + return TRUE + +/datum/keybinding/mob/toggle_move_intent + key = "Alt" + name = "toggle_move_intent" + full_name = "Hold to toggle move intent" + description = "Held down to cycle to the other move intent, release to cycle back" + +/datum/keybinding/mob/toggle_move_intent/down(client/user) + var/mob/M = user.mob + M.toggle_move_intent() + return TRUE + +/datum/keybinding/mob/toggle_move_intent/up(client/user) + var/mob/M = user.mob + M.toggle_move_intent() + return TRUE + +/datum/keybinding/mob/target_head_cycle + key = "Numpad8" + name = "target_head_cycle" + full_name = "Target: Cycle head" + description = "" + +/datum/keybinding/mob/target_head_cycle/down(client/user) + user.body_toggle_head() + return TRUE + +/datum/keybinding/mob/target_r_arm + key = "Numpad4" + name = "target_r_arm" + full_name = "Target: right arm" + description = "" + +/datum/keybinding/mob/target_r_arm/down(client/user) + user.body_r_arm() + return TRUE + +/datum/keybinding/mob/target_body_chest + key = "Numpad5" + name = "target_body_chest" + full_name = "Target: Body" + description = "" + +/datum/keybinding/mob/target_body_chest/down(client/user) + user.body_chest() + return TRUE + +/datum/keybinding/mob/target_left_arm + key = "Numpad6" + name = "target_left_arm" + full_name = "Target: left arm" + description = "" + +/datum/keybinding/mob/target_left_arm/down(client/user) + user.body_l_arm() + return TRUE + +/datum/keybinding/mob/target_right_leg + key = "Numpad1" + name = "target_right_leg" + full_name = "Target: Right leg" + description = "" + +/datum/keybinding/mob/target_right_leg/down(client/user) + user.body_r_leg() + return TRUE + +/datum/keybinding/mob/target_body_groin + key = "Numpad2" + name = "target_body_groin" + full_name = "Target: Groin" + description = "" + +/datum/keybinding/mob/target_body_groin/down(client/user) + user.body_groin() + return TRUE + +/datum/keybinding/mob/target_left_leg + key = "Numpad3" + name = "target_left_leg" + full_name = "Target: left leg" + description = "" + +/datum/keybinding/mob/target_left_leg/down(client/user) + user.body_l_leg() + return TRUE diff --git a/code/datums/keybinding/robot.dm b/code/datums/keybinding/robot.dm new file mode 100644 index 00000000000..98bfd9b6b23 --- /dev/null +++ b/code/datums/keybinding/robot.dm @@ -0,0 +1,69 @@ +/datum/keybinding/robot + category = CATEGORY_ROBOT + weight = WEIGHT_ROBOT + + +/datum/keybinding/robot/moduleone + key = "1" + name = "module_one" + full_name = "Toggle module 1" + description = "Equips or unequips the first module" + +/datum/keybinding/robot/moduleone/down(client/user) + if(!iscyborg(user.mob)) + return FALSE + var/mob/living/silicon/robot/R = user.mob + R.toggle_module(1) + return TRUE + +/datum/keybinding/robot/moduletwo + key = "2" + name = "module_two" + full_name = "Toggle module 2" + description = "Equips or unequips the second module" + +/datum/keybinding/robot/moduletwo/down(client/user) + if(!iscyborg(user.mob)) + return FALSE + var/mob/living/silicon/robot/R = user.mob + R.toggle_module(2) + return TRUE + +/datum/keybinding/robot/modulethree + key = "3" + name = "module_three" + full_name = "Toggle module 3" + description = "Equips or unequips the third module" + +/datum/keybinding/robot/modulethree/down(client/user) + if(!iscyborg(user.mob)) + return FALSE + var/mob/living/silicon/robot/R = user.mob + R.toggle_module(3) + return TRUE + +/datum/keybinding/robot/intent_cycle + key = "4" + name = "cycle_intent" + full_name = "Cycle intent left" + description = "Cycles the intent left" + +/datum/keybinding/robot/intent_cycle/down(client/user) + if(!iscyborg(user.mob)) + return FALSE + var/mob/living/silicon/robot/R = user.mob + R.a_intent_change(INTENT_HOTKEY_LEFT) + return TRUE + +/datum/keybinding/robot/unequip_module + key = "Q" + name = "unequip_module" + full_name = "Unequip module" + description = "Unequips the active module" + +/datum/keybinding/robot/unequip_module/down(client/user) + if(!iscyborg(user.mob)) + return FALSE + var/mob/living/silicon/robot/R = user.mob + R.uneq_active() + return TRUE diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index a8f9a5d9c05..b1cffd47dbb 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -28,6 +28,10 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/UI_style = null var/buttons_locked = FALSE var/hotkeys = FALSE + + // Custom Keybindings + var/list/key_bindings = list() + var/tgui_fancy = TRUE var/tgui_lock = TRUE var/windowflashing = TRUE @@ -125,6 +129,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) return //we couldn't load character data so just randomize the character appearance + name random_character() //let's create a random character then - rather than a fat, bald and naked man. + key_bindings = deepCopyList(GLOB.keybinding_list_by_key) // give them default keybinds too real_name = pref_species.random_name(gender,1) if(!loaded_preferences_successfully) save_preferences() @@ -147,6 +152,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "Character Settings" dat += "Game Preferences" dat += "OOC Preferences" + dat += "Custom Keybindings" if(!path) dat += "
Please create an account to save your preferences
" @@ -512,7 +518,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"]
" dat += "
" dat += "Action Buttons: [(buttons_locked) ? "Locked In Place" : "Unlocked"]
" - dat += "Keybindings: [(hotkeys) ? "Hotkeys" : "Default"]
" + dat += "Hotkey mode: [(hotkeys) ? "Hotkeys" : "Default"]
" dat += "
" dat += "PDA Color:     Change
" dat += "PDA Style: [pda_style]
" @@ -616,9 +622,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "Be [capitalize(i)]: [(i in be_special) ? "Enabled" : "Disabled"]
" dat += "
" dat += "Midround Antagonist: [(toggles & MIDROUND_ANTAG) ? "Enabled" : "Disabled"]
" - dat += "" - if(2) //OOC Preferences dat += "" dat += "
" dat += "

OOC Settings

" @@ -687,7 +691,36 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "
" + if(3) // Custom keybindings + // Create an inverted list of keybindings -> key + var/list/user_binds = list() + for (var/key in key_bindings) + for(var/kb_name in key_bindings[key]) + user_binds[kb_name] = key + var/list/kb_categories = list() + // Group keybinds by category + for (var/name in GLOB.keybindings_by_name) + var/datum/keybinding/kb = GLOB.keybindings_by_name[name] + if (!(kb.category in kb_categories)) + kb_categories[kb.category] = list() + kb_categories[kb.category] += list(kb) + + dat += "" + + for (var/category in kb_categories) + dat += "

[category]

" + for (var/i in kb_categories[category]) + var/datum/keybinding/kb = i + var/bound_key = user_binds[kb.name] + bound_key = (bound_key) ? bound_key : "Unbound" + + dat += " [bound_key] Default: ( [kb.key] )" + dat += "
" + + dat += "

" + dat += "\[Reset to default\]" + dat += "" dat += "
" if(!IsGuestKey(user.key)) @@ -706,6 +739,28 @@ GLOBAL_LIST_EMPTY(preferences_datums) #undef APPEARANCE_CATEGORY_COLUMN #undef MAX_MUTANT_ROWS +/datum/preferences/proc/CaptureKeybinding(mob/user, datum/keybinding/kb, var/old_key) + var/HTML = {" +
Keybinding: [kb.full_name]
[kb.description]

Press any key to change
Press ESC to clear
+ + "} + winshow(user, "capturekeypress", TRUE) + var/datum/browser/popup = new(user, "capturekeypress", "
Keybindings
", 350, 300) + popup.set_content(HTML) + popup.open(FALSE) + onclose(user, "capturekeypress", src) + /datum/preferences/proc/SetChoices(mob/user, limit = 17, list/splitJobs = list("Chief Engineer"), widthPerColumn = 295, height = 620) if(!SSjob) return @@ -1438,9 +1493,78 @@ GLOBAL_LIST_EMPTY(preferences_datums) if("hotkeys") hotkeys = !hotkeys if(hotkeys) - winset(user, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED] mainwindow.macro=default") + winset(user, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED]") else - winset(user, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED] mainwindow.macro=old_default") + winset(user, null, "input.focus=true input.background-color=[COLOR_INPUT_DISABLED]") + + if("keybindings_capture") + var/datum/keybinding/kb = GLOB.keybindings_by_name[href_list["keybinding"]] + var/old_key = href_list["old_key"] + CaptureKeybinding(user, kb, old_key) + return + + if("keybindings_set") + var/kb_name = href_list["keybinding"] + if(!kb_name) + user << browse(null, "window=capturekeypress") + ShowChoices(user) + return + + var/clear_key = text2num(href_list["clear_key"]) + var/old_key = href_list["old_key"] + if(clear_key) + if(old_key != "Unbound") // if it was already set + key_bindings[old_key] -= kb_name + key_bindings["Unbound"] += list(kb_name) + user << browse(null, "window=capturekeypress") + save_preferences() + ShowChoices(user) + return + + var/key = href_list["key"] + var/numpad = text2num(href_list["numpad"]) + var/AltMod = text2num(href_list["alt"]) ? "Alt-" : "" + var/CtrlMod = text2num(href_list["ctrl"]) ? "Ctrl-" : "" + var/ShiftMod = text2num(href_list["shift"]) ? "Shift-" : "" + // var/key_code = text2num(href_list["key_code"]) + + var/new_key = uppertext(key) + + // This is a mapping from JS keys to Byond - ref: https://keycode.info/ + var/static/list/_kbMap = list( + "UP" = "North", + "RIGHT" = "East", + "DOWN" = "South", + "LEFT" = "West", + "INSERT" = "Insert", + "HOME" = "Northwest", + "PAGEUP" = "Northeast", + "DEL" = "Delete", + "END" = "Southwest", + "PAGEDOWN" = "Southeast", + "SPACEBAR" = "Space", + "ALT" = "Alt", + "SHIFT" = "Shift", + "CONTROL" = "Ctrl" + ) + new_key = _kbMap[new_key] ? _kbMap[new_key] : new_key + + if (numpad) + new_key = "Numpad[new_key]" + + var/full_key = "[AltMod][CtrlMod][ShiftMod][new_key]" + if(!key_bindings[old_key]) + key_bindings[old_key] = list() + key_bindings[old_key] -= kb_name + key_bindings[full_key] += list(kb_name) + key_bindings[full_key] = sortList(key_bindings[full_key]) + + user << browse(null, "window=capturekeypress") + save_preferences() + + if("keybindings_reset") + key_bindings = deepCopyList(GLOB.keybinding_list_by_key) + if("action_buttons") buttons_locked = !buttons_locked if("tgui_fancy") diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index d1650b8304d..2da689e2e47 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -5,7 +5,7 @@ // You do not need to raise this if you are adding new values that have sane defaults. // Only raise this value when changing the meaning/format/name/layout of an existing value // where you would want the updater procs below to run -#define SAVEFILE_VERSION_MAX 25 +#define SAVEFILE_VERSION_MAX 26 /* SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn @@ -126,6 +126,14 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["hair_style_name"] >> hairstyle if(S["facial_style_name"]) S["facial_style_name"] >> facial_hairstyle + + if(current_version < 26) + key_bindings = sanitize_islist(key_bindings, deepCopyList(GLOB.keybinding_list_by_key)) + key_bindings["T"] = list(1 = "say") + key_bindings["M"] = list(1 = "me") + key_bindings["O"] = list(1 = "ooc") + key_bindings["L"] = list(1 = "looc") + WRITE_FILE(S["key_bindings"], key_bindings) /datum/preferences/proc/load_path(ckey,filename="preferences.sav") if(!ckey) @@ -183,6 +191,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["pda_style"] >> pda_style S["pda_color"] >> pda_color + // Custom hotkeys + S["key_bindings"] >> key_bindings + //try to fix any outdated data if necessary if(needs_update >= 0) update_preferences(needs_update, S) //needs_update = savefile_version if we need an update (positive integer) @@ -212,7 +223,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car be_special = SANITIZE_LIST(be_special) pda_style = sanitize_inlist(pda_style, GLOB.pda_styles, initial(pda_style)) pda_color = sanitize_hexcolor(pda_color, 6, 1, initial(pda_color)) - + key_bindings = sanitize_islist(key_bindings, deepCopyList(GLOB.keybinding_list_by_key)) return TRUE /datum/preferences/proc/save_preferences() @@ -258,7 +269,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["tip_delay"], tip_delay) WRITE_FILE(S["pda_style"], pda_style) WRITE_FILE(S["pda_color"], pda_color) - + WRITE_FILE(S["key_bindings"], key_bindings) return TRUE /datum/preferences/proc/load_character(slot) diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index 1a82cf204fc..0bf2c47c070 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -1,6 +1,11 @@ GLOBAL_VAR_INIT(OOC_COLOR, null)//If this is null, use the CSS for OOC. Otherwise, use a custom colour. GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") +/client/verb/ooc_wrapper() + set hidden = TRUE + var/message = input("", "OOC") as text + ooc(message) + /client/verb/ooc(msg as text) set name = "OOC" //Gave this shit a shorter name so you only have to time out "ooc" rather than "ooc message" to use it --NeoFite set category = "OOC" diff --git a/code/modules/keybindings/bindings_admin.dm b/code/modules/keybindings/bindings_admin.dm deleted file mode 100644 index ca232adbe0e..00000000000 --- a/code/modules/keybindings/bindings_admin.dm +++ /dev/null @@ -1,24 +0,0 @@ -/datum/admins/key_down(_key, client/user) - switch(_key) - if("F3") - user.get_admin_say() - return - 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 - if("F10") - user.get_dead_say() - return - ..() diff --git a/code/modules/keybindings/bindings_carbon.dm b/code/modules/keybindings/bindings_carbon.dm deleted file mode 100644 index cea93b0da32..00000000000 --- a/code/modules/keybindings/bindings_carbon.dm +++ /dev/null @@ -1,18 +0,0 @@ -/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 ..() diff --git a/code/modules/keybindings/bindings_client.dm b/code/modules/keybindings/bindings_client.dm index 0a971dca9ca..9b54ceb2034 100644 --- a/code/modules/keybindings/bindings_client.dm +++ b/code/modules/keybindings/bindings_client.dm @@ -35,6 +35,7 @@ message_admins("Client [ckey] just attempted to send an invalid keypress. Keymessage was over [MAX_KEYPRESS_COMMANDLENGTH] characters, autokicking due to likely abuse.") QDEL_IN(src, 1) 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 @@ -47,24 +48,19 @@ // 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 - get_adminhelp() - return - if("F2") // Screenshot. Hold shift to choose a name and location to save in - winset(src, null, "command=.screenshot [!keys_held["shift"] ? "auto" : ""]") - return - if("F12") // Toggles minimal HUD - mob.button_pressed_F12() - return + var/AltMod = keys_held["Alt"] ? "Alt-" : "" + var/CtrlMod = keys_held["Ctrl"] ? "Ctrl-" : "" + var/ShiftMod = keys_held["Shift"] ? "Shift-" : "" + var/full_key = "[AltMod][CtrlMod][ShiftMod][_key]" + var/keycount = 0 + for(var/kb_name in prefs.key_bindings[full_key]) + keycount++ + var/datum/keybinding/kb = GLOB.keybindings_by_name[kb_name] + if(kb.down(src) && keycount >= MAX_COMMANDS_PER_KEY) + break - if(holder) - holder.key_down(_key, src) - if(mob.focus) - mob.focus.key_down(_key, src) + holder?.key_down(full_key, src) + mob.focus?.key_down(full_key, src) /client/verb/keyUp(_key as text) set instant = TRUE @@ -79,14 +75,16 @@ 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) + // We don't do full key for release, because for mod keys you + // can hold different keys and releasing any should be handled by the key binding specifically + for (var/kb_name in prefs.key_bindings[_key]) + var/datum/keybinding/kb = GLOB.keybindings_by_name[kb_name] + if(kb.up(src)) + break + holder?.key_up(_key, src) + mob.focus?.key_up(_key, src) // Called every game tick /client/keyLoop() - if(holder) - holder.keyLoop(src) - if(mob?.focus) - mob.focus.keyLoop(src) + holder?.keyLoop(src) + mob.focus?.keyLoop(src) diff --git a/code/modules/keybindings/bindings_human.dm b/code/modules/keybindings/bindings_human.dm deleted file mode 100644 index c404acfcb7c..00000000000 --- a/code/modules/keybindings/bindings_human.dm +++ /dev/null @@ -1,65 +0,0 @@ -/mob/living/carbon/human/key_down(_key, client/user) - if(client.keys_held["Shift"]) - switch(_key) - if("E") // Put held thing in belt or take out most recent thing from belt - if(incapacitated()) - return - var/obj/item/thing = get_active_held_item() - 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_hands() - 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 - if(incapacitated()) - return - var/obj/item/thing = get_active_held_item() - 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_hands() - 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 deleted file mode 100644 index 241bc15b608..00000000000 --- a/code/modules/keybindings/bindings_living.dm +++ /dev/null @@ -1,7 +0,0 @@ -/mob/living/key_down(_key, client/user) - switch(_key) - if("B") - resist() - return - - return ..() \ No newline at end of file diff --git a/code/modules/keybindings/bindings_mob.dm b/code/modules/keybindings/bindings_mob.dm deleted file mode 100644 index 0b7fb66fdde..00000000000 --- a/code/modules/keybindings/bindings_mob.dm +++ /dev/null @@ -1,82 +0,0 @@ -// 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, "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_held_item() - if(!I) - to_chat(src, "You have nothing to drop in your hand!") - else - dropItemToGround(I) - return - if("E") - quick_equip() - 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(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("Alt") - toggle_move_intent() - return - return ..() diff --git a/code/modules/keybindings/bindings_robot.dm b/code/modules/keybindings/bindings_robot.dm deleted file mode 100644 index ad10b2bd9aa..00000000000 --- a/code/modules/keybindings/bindings_robot.dm +++ /dev/null @@ -1,12 +0,0 @@ -/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 ..() diff --git a/code/modules/keybindings/setup.dm b/code/modules/keybindings/setup.dm index 8433c9bf5a3..78e06132311 100644 --- a/code/modules/keybindings/setup.dm +++ b/code/modules/keybindings/setup.dm @@ -22,15 +22,12 @@ // 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" + var/list/macro_set = params2list(winget(src, "default.*", "command")) // The third arg doesnt matter here as we're just removing them all + for(var/k in 1 to length(macro_set)) + 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() @@ -43,18 +40,13 @@ 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]") + var/list/macro_set = SSinput.macro_set + for(var/k in 1 to length(macro_set)) + var/key = macro_set[k] + var/command = macro_set[key] + winset(src, "default-[REF(key)]", "parent=default;name=[key];command=[command]") if(prefs.hotkeys) - winset(src, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED] mainwindow.macro=default") + winset(src, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED]") else - winset(src, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED] mainwindow.macro=old_default") + winset(src, null, "input.focus=true input.background-color=[COLOR_INPUT_DISABLED]") diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index 04daf73122f..6083f1b2407 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -270,3 +270,65 @@ qdel(slot) for(var/obj/item/I in held_items) qdel(I) + +/mob/living/carbon/human/proc/smart_equipbag() // take most recent item out of bag or place held item in bag + if(incapacitated()) + return + var/obj/item/thing = get_active_held_item() + 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(src, "You have no backpack to take something out of!") + return + if(equip_to_slot_if_possible(thing, SLOT_BACK)) + update_inv_hands() + return + if(!SEND_SIGNAL(equipped_back, COMSIG_CONTAINS_STORAGE)) // not a storage item + if(!thing) + equipped_back.attack_hand(src) + else + to_chat(src, "You can't fit anything in!") + return + if(thing) // put thing in backpack + if(!SEND_SIGNAL(equipped_back, COMSIG_TRY_STORAGE_INSERT, thing, src)) + to_chat(src, "You can't fit anything in!") + return + if(!equipped_back.contents.len) // nothing to take out + to_chat(src, "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 + +/mob/living/carbon/human/proc/smart_equipbelt() // put held thing in belt or take most recent item out of belt + if(incapacitated()) + return + var/obj/item/thing = get_active_held_item() + 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(src, "You have no belt to take something out of!") + return + if(equip_to_slot_if_possible(thing, SLOT_BELT)) + update_inv_hands() + return + if(!SEND_SIGNAL(equipped_belt, COMSIG_CONTAINS_STORAGE)) // not a storage item + if(!thing) + equipped_belt.attack_hand(src) + else + to_chat(src, "You can't fit anything in!") + return + if(thing) // put thing in belt + if(!SEND_SIGNAL(equipped_belt, COMSIG_TRY_STORAGE_INSERT, thing, src)) + to_chat(src, "You can't fit anything in!") + return + if(!equipped_belt.contents.len) // nothing to take out + to_chat(src, "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 diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 250aefefa82..9b8c1e08a4b 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -10,6 +10,11 @@ if(message) say(message) +/mob/verb/say_wrapper() + set hidden = TRUE + var/message = input("", "Say") as text + say_verb(message) + ///Whisper verb /mob/verb/whisper_verb(message as text) set name = "Whisper" @@ -22,7 +27,7 @@ ///whisper a message /mob/proc/whisper(message, datum/language/language=null) say(message, language) //only living mobs actually whisper, everything else just talks - + ///The me emote verb /mob/verb/me_verb(message as text) set name = "Me" diff --git a/tgstation.dme b/tgstation.dme index 6a93e8f232a..11218221279 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -165,6 +165,7 @@ #include "code\_globalvars\traits.dm" #include "code\_globalvars\lists\achievements.dm" #include "code\_globalvars\lists\admin.dm" +#include "code\_globalvars\lists\client.dm" #include "code\_globalvars\lists\flavor_misc.dm" #include "code\_globalvars\lists\maintenance_loot.dm" #include "code\_globalvars\lists\mapping.dm" @@ -512,6 +513,15 @@ #include "code\datums\helper_datums\getrev.dm" #include "code\datums\helper_datums\icon_snapshot.dm" #include "code\datums\helper_datums\teleport.dm" +#include "code\datums\keybinding\_defines.dm" +#include "code\datums\keybinding\_keybindings.dm" +#include "code\datums\keybinding\admin.dm" +#include "code\datums\keybinding\carbon.dm" +#include "code\datums\keybinding\client.dm" +#include "code\datums\keybinding\human.dm" +#include "code\datums\keybinding\living.dm" +#include "code\datums\keybinding\mob.dm" +#include "code\datums\keybinding\robot.dm" #include "code\datums\looping_sounds\_looping_sound.dm" #include "code\datums\looping_sounds\item_sounds.dm" #include "code\datums\looping_sounds\machinery_sounds.dm" @@ -1885,14 +1895,8 @@ #include "code\modules\jobs\job_types\virologist.dm" #include "code\modules\jobs\job_types\warden.dm" #include "code\modules\jobs\map_changes\map_changes.dm" -#include "code\modules\keybindings\bindings_admin.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\language\aphasia.dm"