From 786faaaf394233b056369ee515e4dd2d7303d70a Mon Sep 17 00:00:00 2001 From: Azarak Date: Thu, 17 Sep 2020 03:18:01 +0200 Subject: [PATCH] [SEMI-MODULAR] Gunpointing! Similar to bay-aiming (#800) * Gunpoint! * Spans * linter moment * wah * weh --- code/__DEFINES/~skyrat_defines/signals.dm | 6 + code/__DEFINES/~skyrat_defines/traits.dm | 1 + code/_onclick/hud/radial.dm | 2 + code/_onclick/item_attack.dm | 1 + .../objects/items/devices/radio/headset.dm | 3 +- .../game/objects/items/devices/radio/radio.dm | 6 +- .../mob/living/carbon/human/examine.dm | 7 + code/modules/mob/living/living.dm | 17 ++ code/modules/mob/mob_movement.dm | 5 + .../gunpoint/code/datum/gunpoint/gunpoint.dm | 54 ++++ .../code/datum/gunpoint/gunpoint_datum.dm | 261 ++++++++++++++++++ .../code/datum/gunpoint/gunpoint_radial.dm | 45 +++ .../gunpoint/icons/radial_gunpoint.dmi | Bin 0 -> 3052 bytes .../modules/gunpoint/icons/targeted.dmi | Bin 0 -> 617 bytes modular_skyrat/modules/gunpoint/readme.md | 36 +++ .../modules/gunpoint/sound/targetoff.ogg | Bin 0 -> 7712 bytes .../modules/gunpoint/sound/targeton.ogg | Bin 0 -> 7512 bytes tgstation.dme | 5 + 18 files changed, 446 insertions(+), 3 deletions(-) create mode 100644 code/__DEFINES/~skyrat_defines/signals.dm create mode 100644 code/__DEFINES/~skyrat_defines/traits.dm create mode 100644 modular_skyrat/modules/gunpoint/code/datum/gunpoint/gunpoint.dm create mode 100644 modular_skyrat/modules/gunpoint/code/datum/gunpoint/gunpoint_datum.dm create mode 100644 modular_skyrat/modules/gunpoint/code/datum/gunpoint/gunpoint_radial.dm create mode 100644 modular_skyrat/modules/gunpoint/icons/radial_gunpoint.dmi create mode 100644 modular_skyrat/modules/gunpoint/icons/targeted.dmi create mode 100644 modular_skyrat/modules/gunpoint/readme.md create mode 100644 modular_skyrat/modules/gunpoint/sound/targetoff.ogg create mode 100644 modular_skyrat/modules/gunpoint/sound/targeton.ogg diff --git a/code/__DEFINES/~skyrat_defines/signals.dm b/code/__DEFINES/~skyrat_defines/signals.dm new file mode 100644 index 00000000000..5d82939b5ca --- /dev/null +++ b/code/__DEFINES/~skyrat_defines/signals.dm @@ -0,0 +1,6 @@ +// Movable signals +#define COMSIG_MOVABLE_RADIO_TALK_INTO "movable_radio_talk_into" //from radio talk_into(): (obj/item/radio/radio, message, channel, list/spans, datum/language/language, direct) +// Mob signals +#define COMSIG_MOB_ITEM_ATTACK_SELF "item_attack_self" //from base of obj/item/attack_self(): (obj/item) +// Living signals +#define COMSIG_LIVING_UPDATED_MOBILITY "living_updated_mobility" //from base of (/mob/living/proc/update_mobility): (mobility_flags) diff --git a/code/__DEFINES/~skyrat_defines/traits.dm b/code/__DEFINES/~skyrat_defines/traits.dm new file mode 100644 index 00000000000..e8581b0f667 --- /dev/null +++ b/code/__DEFINES/~skyrat_defines/traits.dm @@ -0,0 +1 @@ +#define TRAIT_NORUNNING "norunning" // You walk! diff --git a/code/_onclick/hud/radial.dm b/code/_onclick/hud/radial.dm index bcdd5c85004..bd34ec85f86 100644 --- a/code/_onclick/hud/radial.dm +++ b/code/_onclick/hud/radial.dm @@ -80,6 +80,7 @@ GLOBAL_LIST_EMPTY(radial_menus) var/hudfix_method = TRUE //TRUE to change anchor to user, FALSE to shift by py_shift var/py_shift = 0 var/entry_animation = TRUE + var/icon_path = 'icons/mob/radial.dmi' //SKYRAT EDIT ADDITION - GUNPOINT //If we swap to vis_contens inventory these will need a redo /datum/radial_menu/proc/check_screen_border(mob/user) @@ -122,6 +123,7 @@ GLOBAL_LIST_EMPTY(radial_menus) var/elements_to_add = max_elements - elements.len for(var/i in 1 to elements_to_add) //Create all elements var/obj/screen/radial/slice/new_element = new /obj/screen/radial/slice + new_element.icon = icon_path //SKYRAT EDIT ADDITION - GUNPOINT new_element.tooltips = use_tooltips new_element.parent = src elements += new_element diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 226a44f3c7b..e6f074c4e98 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -23,6 +23,7 @@ /obj/item/proc/attack_self(mob/user) if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF, user) & COMPONENT_NO_INTERACT) return + SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK_SELF, src) //SKYRAT EDIT ADDITION - GUNPOINT interact(user) /** diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index c02c9c86f3e..f09a42cdfed 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -61,7 +61,8 @@ GLOBAL_LIST_INIT(channel_tokens, list( QDEL_NULL(keyslot2) return ..() -/obj/item/radio/headset/talk_into(mob/living/M, message, channel, list/spans, datum/language/language, list/message_mods) +///obj/item/radio/headset/talk_into(mob/living/M, message, channel, list/spans, datum/language/language, list/message_mods) - ORIGINAL +/obj/item/radio/headset/talk_into(mob/living/M, message, channel, list/spans, datum/language/language, list/message_mods, direct = TRUE) //SKYRAT EDIT CHANGE - GUNPOINT if (!listening) return ITALICS | REDUCE_RANGE return ..() diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 5de46b3ae22..dcad8df672b 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -185,7 +185,8 @@ recalculateChannels() . = TRUE -/obj/item/radio/talk_into(atom/movable/M, message, channel, list/spans, datum/language/language, list/message_mods) +///obj/item/radio/talk_into(atom/movable/M, message, channel, list/spans, datum/language/language, list/message_mods) - ORIGINAL +/obj/item/radio/talk_into(atom/movable/M, message, channel, list/spans, datum/language/language, list/message_mods, direct = TRUE) //SKYRAT EDIT CHANGE - GUNPOINT if(HAS_TRAIT(M, TRAIT_SIGN_LANG)) //Forces Sign Language users to wear the translation gloves to speak over radios var/mob/living/carbon/mute = M if(istype(mute)) @@ -203,6 +204,7 @@ spans = list(M.speech_span) if(!language) language = M.get_selected_language() + SEND_SIGNAL(M, COMSIG_MOVABLE_RADIO_TALK_INTO, src, message, channel, spans, language, direct) //SKYRAT EDIT ADDITION - GUNPOINT INVOKE_ASYNC(src, .proc/talk_into_impl, M, message, channel, spans.Copy(), language, message_mods) return ITALICS | REDUCE_RANGE @@ -298,7 +300,7 @@ if (idx && (idx % 2) == (message_mods[RADIO_EXTENSION] == MODE_L_HAND)) return - talk_into(speaker, raw_message, , spans, language=message_language) + talk_into(speaker, raw_message, , spans, language=message_language, direct=FALSE) //SKYRAT EDIT CHANGE - GUNPOINT // Checks if this radio can receive on the given frequency. /obj/item/radio/proc/can_receive(freq, level) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index c37f098337f..0615d925cce 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -428,6 +428,13 @@ "\[Add comment\]"), "") else if(isobserver(user)) . += "Traits: [get_quirk_string(FALSE, CAT_QUIRK_ALL)]" + //SKYRAT EDIT ADDITION BEGIN - GUNPOINT + if(gunpointing) + . += "[t_He] [t_is] holding [gunpointing.target.name] at gunpoint with [gunpointing.aimed_gun.name]!\n" + if(length(gunpointed)) + for(var/datum/gunpoint/GP in gunpointed) + . += "[GP.source.name] [GP.source.p_are()] holding [t_him] at gunpoint with [GP.aimed_gun.name]!\n" + //SKYRAT EDIT ADDITION END //SKYRAT EDIT ADDITION BEGIN - CUSTOMIZATION for(var/genital in list("penis", "testicles", "vagina", "breasts")) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 607490e1b26..95ed02846f9 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -116,6 +116,22 @@ if(!(world.time % 5)) to_chat(src, "[L] is restraining [P], you cannot push past.") return TRUE + //SKYRAT EDIT ADDITION BEGIN - GUNPOINT + if(L.gunpointed.len) + var/is_pointing = FALSE + for(var/datum/gunpoint/gp in L.gunpointed) + if(gp.source == src) + is_pointing = TRUE + break + if(!is_pointing) + if(!(world.time % 5)) + to_chat(src, "[L] is being held at gunpoint, it's not wise to push him.") + return TRUE + if(L.gunpointing) + if(!(world.time % 5)) + to_chat(src, "[L] is holding someone at gunpoint, you cannot push past.") + return TRUE + //SKYRAT EDIT ADDITION END if(moving_diagonally)//no mob swap during diagonal moves. return TRUE @@ -1282,6 +1298,7 @@ add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/limbless, multiplicative_slowdown = limbless_slowdown) else remove_movespeed_modifier(/datum/movespeed_modifier/limbless) + SEND_SIGNAL(src, COMSIG_LIVING_UPDATED_MOBILITY, mobility_flags) //SKYRAT EDIT ADDITION - GUNPOINT ///Called when mob changes from a standing position into a prone while lacking the ability to stand up at the moment, through update_mobility() diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 7714c32b124..d75260cad46 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -449,6 +449,11 @@ if(m_intent == MOVE_INTENT_RUN) m_intent = MOVE_INTENT_WALK else + //SKYRAT EDIT ADDITION BEGIN - GUNPOINT + if (HAS_TRAIT(src,TRAIT_NORUNNING)) + to_chat(src, "You find yourself unable to run.") + return FALSE + //SKYRAT EDIT ADDITION END m_intent = MOVE_INTENT_RUN if(hud_used && hud_used.static_inventory) for(var/obj/screen/mov_intent/selector in hud_used.static_inventory) diff --git a/modular_skyrat/modules/gunpoint/code/datum/gunpoint/gunpoint.dm b/modular_skyrat/modules/gunpoint/code/datum/gunpoint/gunpoint.dm new file mode 100644 index 00000000000..6a6c0b0fe59 --- /dev/null +++ b/modular_skyrat/modules/gunpoint/code/datum/gunpoint/gunpoint.dm @@ -0,0 +1,54 @@ +/mob/living + ///Who is the person gunpointing at? If they are + var/datum/gunpoint/gunpointing + ///Who is gunpointing at our person? + var/list/gunpointed = list() + ///The effect from gunpointing, to make sure we dont create multiples + var/obj/effect/overlay/gunpoint_effect/gp_effect + +/mob/living/carbon/Move(atom/newloc, direct = 0) + . = ..() + if(gunpointing) + var/dir = get_dir(get_turf(gunpointing.source),get_turf(gunpointing.target)) + if(dir) + setDir(dir) + +/mob/living/ShiftMiddleClickOn(atom/A) + var/obj/item/gun/G = get_active_held_item() + if(istype(G, /obj/item/gun)) + DoGunpoint(A, G) + else + src.pointed(A) + return + +/mob/living/proc/DoGunpoint(atom/A, obj/item/gun/G) + if(A == src) + return + if(!isliving(A)) + if(gunpointing) //Cancelling a gunpoint by shift + MMB on a non-mob + gunpointing.Destroy() + return + var/mob/living/L = A + if(gunpointing && L == gunpointing.target) //Cancelling a gunpoint by shift + MMB on the target + gunpointing.ClickDestroy() + else + if(!CanGunpointAt(L, TRUE)) + return + if(gunpointing) //We're gunpoint and we already know we're locking onto a different, valid target + gunpointing.Destroy() + gunpointing = new(src, L, G) + +/mob/living/proc/CanGunpointAt(mob/living/L, notice = FALSE) + if(!(src.mobility_flags & MOBILITY_STAND)) + if(notice) + to_chat(src, "You need to be standing to get a good aim!") + return FALSE + if(!(L in viewers(8, src))) + if(notice) + to_chat(src, "Your target is out of your view!") + return FALSE + if(L.alpha < 70) + if(notice) + to_chat(src, "You can't quite make out your target!") + return FALSE + return TRUE diff --git a/modular_skyrat/modules/gunpoint/code/datum/gunpoint/gunpoint_datum.dm b/modular_skyrat/modules/gunpoint/code/datum/gunpoint/gunpoint_datum.dm new file mode 100644 index 00000000000..c3d3f48f5b8 --- /dev/null +++ b/modular_skyrat/modules/gunpoint/code/datum/gunpoint/gunpoint_datum.dm @@ -0,0 +1,261 @@ +/obj/effect/overlay/gunpoint_effect + icon = 'modular_skyrat/modules/gunpoint/icons/targeted.dmi' + icon_state = "locking" + layer = FLY_LAYER + plane = GAME_PLANE + appearance_flags = APPEARANCE_UI_IGNORE_ALPHA | KEEP_APART + mouse_opacity = 0 + +/datum/gunpoint + var/mob/living/source + var/mob/living/target + var/datum/radial_menu/gunpoint/gunpoint_gui + var/allow_move = FALSE + var/allow_radio = FALSE + var/allow_use = FALSE + + var/obj/item/gun/aimed_gun + + var/safeguard_time = 0 + var/next_autoshot = 0 + var/locked = FALSE + var/was_running = FALSE + + var/moved_counter = 0 + + var/static/radio_forbid = image(icon = 'modular_skyrat/modules/gunpoint/icons/radial_gunpoint.dmi', icon_state = "radial_radio_forbid") + var/static/radio_allow = image(icon = 'modular_skyrat/modules/gunpoint/icons/radial_gunpoint.dmi', icon_state = "radial_radio") + var/static/use_forbid = image(icon = 'modular_skyrat/modules/gunpoint/icons/radial_gunpoint.dmi', icon_state = "radial_use_forbid") + var/static/use_allow = image(icon = 'modular_skyrat/modules/gunpoint/icons/radial_gunpoint.dmi', icon_state = "radial_use") + var/static/move_forbid = image(icon = 'modular_skyrat/modules/gunpoint/icons/radial_gunpoint.dmi', icon_state = "radial_move_forbid") + var/static/move_allow = image(icon = 'modular_skyrat/modules/gunpoint/icons/radial_gunpoint.dmi', icon_state = "radial_move") + +/datum/gunpoint/New(user, tar, gun) + source = user + source.gunpointing = src + target = tar + target.gunpointed += src + aimed_gun = gun + + source.face_atom(target) + source.visible_message("[source.name] aims at [target.name] with the [aimed_gun.name]!") + + was_running = (source.m_intent == MOVE_INTENT_RUN) + if(was_running) + source.toggle_move_intent() + ADD_TRAIT(source, TRAIT_NORUNNING, "gunpoint") + + if(!target.gp_effect) + target.gp_effect = new + target.vis_contents += target.gp_effect + + RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/MovedReact) + RegisterSignal(source, COMSIG_MOVABLE_MOVED, .proc/SourceMoved) + + RegisterSignal(target, COMSIG_MOB_FIRED_GUN, .proc/UseReact) + + RegisterSignal(source, COMSIG_LIVING_STATUS_STUN, .proc/SourceCC) + RegisterSignal(source, COMSIG_LIVING_STATUS_KNOCKDOWN, .proc/SourceCC) + RegisterSignal(source, COMSIG_LIVING_STATUS_PARALYZE, .proc/SourceCC) + RegisterSignal(source, COMSIG_LIVING_UPDATED_MOBILITY, .proc/SourceUpdatedMobility) + + RegisterSignal(aimed_gun, COMSIG_ITEM_EQUIPPED,.proc/ClickDestroy) + RegisterSignal(aimed_gun, COMSIG_ITEM_DROPPED,.proc/ClickDestroy) + + RegisterSignal(target, COMSIG_MOVABLE_RADIO_TALK_INTO,.proc/RadioReact) + + RegisterSignal(target, COMSIG_MOB_ITEM_ATTACK, .proc/UseReact) + RegisterSignal(target, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, .proc/MeleeAttackReact) + RegisterSignal(target, COMSIG_MOB_ITEM_ATTACK_SELF, .proc/UseReact) + RegisterSignal(target, COMSIG_MOB_ITEM_AFTERATTACK, .proc/UseReact) + + RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/Destroy) + RegisterSignal(source, COMSIG_PARENT_QDELETING, .proc/Destroy) + + + addtimer(CALLBACK(src, .proc/LockOn), 7) + +/datum/gunpoint/proc/MeleeAttackReact(datum/source_datum, atom/target) + if(!CheckContinuity()) + Destroy() + return + if(!allow_use && CanReact()) + source.log_message("[source] shot [target] because they attacked/disarmed/pulled", LOG_ATTACK) + to_chat(source, "You pull the trigger instinctively to [target.name] actions!") + ShootTarget() + +/datum/gunpoint/proc/LockOn() + if(src) //if we're not present then locking on failed and this datum is deleted + if(!CheckContinuity()) + Destroy() + return + locked = TRUE + log_combat(target, source, "locked onto with aiming") + playsound(get_turf(source), 'modular_skyrat/modules/gunpoint/sound/targeton.ogg', 50,1) + to_chat(source, "You lock onto [target.name]!") + target.visible_message("[source.name] holds [target.name] at gunpoint with the [aimed_gun.name]!", "[source.name] holds you at gunpoint with the [aimed_gun.name]!") + if(target.gunpointed.len == 1)//First case + to_chat(target, "You'll get shot if you use radio, move or interact with items!") + to_chat(target, "You can however take items out, toss harmless items or drop them.") + var/list/choice_list = ConstructChoiceList() + gunpoint_gui = show_radial_menu_gunpoint(source, target , choice_list, select_proc = CALLBACK(src, .proc/GunpointGuiReact, source), radius = 42) + if(target.gp_effect.icon_state != "locked") + target.gp_effect.icon_state = "locked" + safeguard_time = world.time + 30 + +/datum/gunpoint/proc/CheckContinuity() + if(!target) + return FALSE + if(source.CanGunpointAt(target)) + source.face_atom(target) + return TRUE + return FALSE + +/datum/gunpoint/proc/CanReact() + if(locked && (world.time >= safeguard_time) && !(target.stat == DEAD || target.stat == HARD_CRIT || target.stat == SOFT_CRIT) && source.next_move <= world.time && next_autoshot <= world.time) + return TRUE + return FALSE + +/datum/gunpoint/Destroy() + UnregisterSignal(aimed_gun, list(COMSIG_ITEM_DROPPED, COMSIG_ITEM_EQUIPPED)) + UnregisterSignal(target, list(COMSIG_PARENT_QDELETING, COMSIG_MOB_ITEM_AFTERATTACK, COMSIG_MOB_ITEM_ATTACK_SELF, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_MOB_ITEM_ATTACK, COMSIG_MOVABLE_RADIO_TALK_INTO, COMSIG_MOB_FIRED_GUN, COMSIG_MOVABLE_MOVED)) + UnregisterSignal(source, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED, COMSIG_LIVING_STATUS_STUN, COMSIG_LIVING_STATUS_KNOCKDOWN, COMSIG_LIVING_STATUS_PARALYZE, COMSIG_LIVING_UPDATED_MOBILITY)) + + REMOVE_TRAIT(source, TRAIT_NORUNNING, "gunpoint") + if(was_running) + source.toggle_move_intent() + if(target.gunpointed.len == 1) //Last instance being deleted + target.vis_contents -= target.gp_effect + QDEL_NULL(target.gp_effect) + target.gunpointed -= src + source.gunpointing = null + if(locked) + QDEL_NULL(gunpoint_gui) + target.visible_message("[source.name] no longer holds [target.name] at gunpoint.", "[source.name] no longer holds you at gunpoint.") + source = null + target = null + aimed_gun = null + return ..() + +/datum/gunpoint/proc/ClickDestroy() + if(locked) + playsound(get_turf(source), 'modular_skyrat/modules/gunpoint/sound/targetoff.ogg', 50,1) + Destroy() + +/datum/gunpoint/proc/SourceCC(datum/source, amount, update, ignore) + if(amount && !ignore) + Destroy() + +/datum/gunpoint/proc/ShootTarget() + next_autoshot = world.time + 5 + log_combat(target, source, "auto-shot with aim") + aimed_gun.afterattack(target, source) + +/datum/gunpoint/proc/RadioReact(datum/datum_source, obj/item/radio/radio, message, channel, list/spans, datum/language/language, direct) + if(!allow_radio && CanReact()) + if(direct) + source.log_message("[source] shot [target] because they spoke on radio", LOG_ATTACK) + to_chat(source, "You pull the trigger instinctively as [target.name] speaks on the radio!") + ShootTarget() + +/datum/gunpoint/proc/MovedReact(datum/datum_source, atom/moved, direction, forced) + if(!CheckContinuity()) + Destroy() + return + if(!allow_move && CanReact() && !(target.pulledby && target.pulledby == source)) //Don't shoot him if we're pulling them + MovedShootProc() + +/datum/gunpoint/proc/MovedShootProc() //This exists in case of someone moving several tiles in one tick, such as dashes or diagonal movement + moved_counter += 1 + var/count = moved_counter + sleep(1) + if(src && count == moved_counter) + source.log_message("[source] shot [target] because they moved", LOG_ATTACK) + ShootTarget() + +/datum/gunpoint/proc/UseReact(datum/datum_source) + if(!CheckContinuity()) + Destroy() + return + if(!allow_use && CanReact()) + source.log_message("[source] shot [target] because they used an item", LOG_ATTACK) + to_chat(source, "You pull the trigger instinctively as [target.name] uses an item!") + ShootTarget() + +/datum/gunpoint/proc/SourceMoved(datum/datum_source) + if(!CheckContinuity()) + Destroy() + +/datum/gunpoint/proc/SourceUpdatedMobility() + if(!(source.mobility_flags & MOBILITY_STAND)) + Destroy() + +/datum/gunpoint/proc/ConstructChoiceList() + var/image/radio_image = (allow_radio ? radio_allow : radio_forbid) + var/image/use_image = (allow_use ? use_allow : use_forbid) + var/image/move_image = (allow_move ? move_allow : move_forbid) + var/list/L = list("radio" = radio_image, "use" = use_image, "move" = move_image) + return L + +/datum/gunpoint/proc/GunpointGuiReact(mob/living/user,choice) + switch(choice) + if("radio") + allow_radio = !allow_radio + if(allow_radio) + var/safe = TRUE + for(var/datum/gunpoint/gp in target.gunpointed) + if(gp.allow_radio == FALSE) + safe = FALSE + break + if(safe) + to_chat(target, "[source.name] signals you can use radio.") + else + to_chat(target, "[source.name] signals you can use radio, however other people still don't") + else + var/forbid_counts = 0 + for(var/datum/gunpoint/gp in target.gunpointed) + if(gp.allow_radio == FALSE) + forbid_counts += 1 + if(forbid_counts == 1) //Only first one warns the victim + to_chat(target, "[source.name] signals you can't use radio.") + if("use") + allow_use = !allow_use + if(allow_use) + var/safe = TRUE + for(var/datum/gunpoint/gp in target.gunpointed) + if(gp.allow_use == FALSE) + safe = FALSE + break + if(safe) + to_chat(target, "[source.name] signals you can interact with items.") + else + to_chat(target, "[source.name] signals you can interact with items, however other people still don't") + else + var/forbid_counts = 0 + for(var/datum/gunpoint/gp in target.gunpointed) + if(gp.allow_use == FALSE) + forbid_counts += 1 + if(forbid_counts == 1) //Only first one warns the victim + to_chat(target, "[source.name] signals you can't interact with items.") + if("move") + allow_move = !allow_move + if(allow_move) + var/safe = TRUE + for(var/datum/gunpoint/gp in target.gunpointed) + if(gp.allow_move == FALSE) + safe = FALSE + break + if(safe) + to_chat(target, "[source.name] signals you can move.") + else + to_chat(target, "[source.name] signals you can move, however other people still don't") + else + var/forbid_counts = 0 + for(var/datum/gunpoint/gp in target.gunpointed) + if(gp.allow_move == FALSE) + forbid_counts += 1 + if(forbid_counts == 1) //Only first one warns the victim + to_chat(target, "[source.name] signals you can't move.") + var/list/new_choices = ConstructChoiceList() + gunpoint_gui.entry_animation = FALSE + gunpoint_gui.change_choices(new_choices,FALSE) diff --git a/modular_skyrat/modules/gunpoint/code/datum/gunpoint/gunpoint_radial.dm b/modular_skyrat/modules/gunpoint/code/datum/gunpoint/gunpoint_radial.dm new file mode 100644 index 00000000000..f21ca00191c --- /dev/null +++ b/modular_skyrat/modules/gunpoint/code/datum/gunpoint/gunpoint_radial.dm @@ -0,0 +1,45 @@ +/datum/radial_menu/gunpoint + var/uniqueid + var/datum/callback/select_proc_callback + icon_path = 'modular_skyrat/modules/gunpoint/icons/radial_gunpoint.dmi' + +/datum/radial_menu/gunpoint/New() + + +/datum/radial_menu/gunpoint/element_chosen(choice_id,mob/user) + select_proc_callback.Invoke(choices_values[choice_id]) + + +/datum/radial_menu/gunpoint/proc/change_choices(list/newchoices, tooltips) + if(!newchoices.len) + return + Reset() + set_choices(newchoices,tooltips) + +/datum/radial_menu/gunpoint/Destroy() + QDEL_NULL(select_proc_callback) + GLOB.radial_menus -= uniqueid + Reset() + hide() + . = ..() + +/proc/show_radial_menu_gunpoint(mob/user, atom/anchor, list/choices, datum/callback/select_proc, uniqueid, radius, tooltips = FALSE) + if(!user || !anchor || !length(choices) || !select_proc) + return + if(!uniqueid) + uniqueid = "defmenu_[REF(user)]_[REF(anchor)]" + + if(GLOB.radial_menus[uniqueid]) + return + + var/datum/radial_menu/gunpoint/menu = new + menu.uniqueid = uniqueid + GLOB.radial_menus[uniqueid] = menu + if(radius) + menu.radius = radius + menu.select_proc_callback = select_proc + menu.anchor = anchor + menu.check_screen_border(user) //Do what's needed to make it look good near borders or on hud + menu.set_choices(choices, tooltips) + menu.show_to(user) + return menu diff --git a/modular_skyrat/modules/gunpoint/icons/radial_gunpoint.dmi b/modular_skyrat/modules/gunpoint/icons/radial_gunpoint.dmi new file mode 100644 index 0000000000000000000000000000000000000000..71f4f4edb730fe635507189f1f7f3b606aaba095 GIT binary patch literal 3052 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 DW||ap literal 0 HcmV?d00001 diff --git a/modular_skyrat/modules/gunpoint/readme.md b/modular_skyrat/modules/gunpoint/readme.md new file mode 100644 index 00000000000..1ff911ed860 --- /dev/null +++ b/modular_skyrat/modules/gunpoint/readme.md @@ -0,0 +1,36 @@ +## Title: Gunpoint + +MODULE ID: GUNPOINT + +### Description: + +Allows anyone with a gun to use SHIFT + MMB to hold someone at gunpoint, much like how baystation does it, but in a more visually clear way + +### TG Proc Changes: + + ./modular_skyrat/modules/gunpoint/code/datum/gunpoint/gunpoint.dm > /mob/living/ShiftMiddleClickOn() > CHILD PROC + ./code/_onclick/hud/radial.dm > /datum/radial_menu/proc/setup_menu() + ./code/_onclick/item_attack.dm > /obj/item/proc/attack_self() + ./code/game/objects/items/devices/radio/radio.dm > /obj/item/radio/talk_into() + ./code/game/objects/items/devices/radio/headset.dm > /obj/item/radio/headset/talk_into() + ./code/modules/mob/living/carbon/human/examine.dm > /mob/living/carbon/human/examine() + ./code/modules/mob/living/living.dm > /mob/living/proc/update_mobility() + ./code/modules/mob/living/living.dm > /mob/living/proc/MobBump() + ./code/modules/mob/mob_movement.dm > /mob/proc/toggle_move_intent() + +### Defines: + + ./code/_onclick/hud/radial.dm > /datum/radial_menu - var/icon_path + ./code/__DEFINES/~skyrat_defines/signals.dm - COMSIG_MOVABLE_RADIO_TALK_INTO, COMSIG_MOB_ITEM_ATTACK_SELF, COMSIG_LIVING_UPDATED_MOBILITY + ./code/__DEFINES/~skyrat_defines/traits.dm - TRAIT_NORUNNING + +### Master file additions + +- N/A + +### Included files that are not contained in this module: + +- N/A + +### Credits: +Azarak - original code & porting diff --git a/modular_skyrat/modules/gunpoint/sound/targetoff.ogg b/modular_skyrat/modules/gunpoint/sound/targetoff.ogg new file mode 100644 index 0000000000000000000000000000000000000000..b8657aab27ed9cd46ed14ddc62599171fdaf75a5 GIT binary patch literal 7712 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 literal 0 HcmV?d00001 diff --git a/modular_skyrat/modules/gunpoint/sound/targeton.ogg b/modular_skyrat/modules/gunpoint/sound/targeton.ogg new file mode 100644 index 0000000000000000000000000000000000000000..93a2f68f326cd25c91bd8331ce423830f89dae99 GIT binary patch literal 7512 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