mirror of
https://github.com/fulpstation/fulpstation.git
synced 2025-12-10 18:11:47 +00:00
Freeze! Adds holding people up with guns. (#47677)
About The Pull Request Attacking someone with a gun at point blank with grab intent will now hold them at gunpoint. This means you will automatically shoot the target if they move or attack, though they're still free to rummage around in their (or your, if you continue standing next to them) inventory, speak (including using radios), and throw things. The shooter may roam around a 3 tile box radius without breaking the hold-up, allowing them to reposition themselves, though breaking visibility will break the hold-up. In addition, if you're several tiles away and they make a break for it, they may be able to dodge the projectile. Tactical choices! For the first 3 seconds after the hold-up, any triggered reaction shot will deal normal damage. Between 3-10 seconds after, any triggered reaction shot will deal double damage. After 10 seconds, the reaction shot will deal 2.5x damage. The ramp-up is to give weapons with weaker per-shot damage like disablers, laser guns, and the .38 the ability to be useful for arrests and stick ups, without affecting combat balance too much. If you got someone you're trying to kill to hold still for 2 seconds at point blank range, you may as well just keep shooting them rather than fiddle with holding them up. If you're gonna resist, it's best to do it quickly! Lastly, if the shooter is attacked and takes damage from anything, they have a chance to flinch and fire in reaction. This is a 50% chance, unless they're hit in the arm holding the gun, in which case it's an 80% chance. Why It's Good For The Game Adds in mechanical backing for taking people hostage or being able to arrest people with the threat of violence rather than actually carrying out that violence. Trying to take hostages or hold people up right now will just get you immediately shove spammed mid-"DROP $500 OR FAILRP" unless the other person feels like playing along. This makes cooperating or risking getting shot an actual choice, increasing mechanical depth while also adding more roleplay potential. Also yes, the target cuffing themselves will not trigger the reaction shot, so holding someone up, backing up, and throwing cuffs at them is a valid move.
This commit is contained in:
@@ -131,6 +131,8 @@
|
||||
#define COMPONENT_NO_ATTACK_HAND 1 //works on all 3.
|
||||
//This signal return value bitflags can be found in __DEFINES/misc.dm
|
||||
#define COMSIG_ATOM_INTERCEPT_Z_FALL "movable_intercept_z_impact" //called for each movable in a turf contents on /turf/zImpact(): (atom/movable/A, levels)
|
||||
#define COMSIG_ATOM_START_PULL "movable_start_pull" //called on a movable (NOT living) when someone starts pulling it (atom/movable/puller, state, force)
|
||||
#define COMSIG_LIVING_START_PULL "living_start_pull" //called on /living when someone starts pulling it (atom/movable/puller, state, force)
|
||||
|
||||
/////////////////
|
||||
|
||||
@@ -303,6 +305,9 @@
|
||||
// /obj/item/pen signals
|
||||
#define COMSIG_PEN_ROTATED "pen_rotated" //called after rotation in /obj/item/pen/attack_self(): (rotation, mob/living/carbon/user)
|
||||
|
||||
// /obj/item/gun signals
|
||||
#define COMSIG_MOB_FIRED_GUN "mob_fired_gun" //called in /obj/item/gun/process_fire (user, target, params, zone_override)
|
||||
|
||||
// /obj/projectile signals (sent to the firer)
|
||||
#define COMSIG_PROJECTILE_ON_HIT "projectile_on_hit" // from base of /obj/projectile/proc/on_hit(): (atom/movable/firer, atom/target, Angle)
|
||||
#define COMSIG_PROJECTILE_BEFORE_FIRE "projectile_before_fire" // from base of /obj/projectile/proc/fire(): (obj/projectile, atom/original_target)
|
||||
|
||||
@@ -113,6 +113,9 @@
|
||||
|
||||
#define STATUS_EFFECT_BOUNTY /datum/status_effect/bounty //rewards the person who added this to the target with refreshed spells and a fair heal
|
||||
|
||||
#define STATUS_EFFECT_HELDUP /datum/status_effect/heldup // someone is currently pointing a gun at you
|
||||
|
||||
#define STATUS_EFFECT_HOLDUP /datum/status_effect/holdup // you are currently pointing a gun at someone
|
||||
/////////////
|
||||
// SLIME //
|
||||
/////////////
|
||||
|
||||
153
code/datums/components/gunpoint.dm
Normal file
153
code/datums/components/gunpoint.dm
Normal file
@@ -0,0 +1,153 @@
|
||||
#define GUNPOINT_SHOOTER_STRAY_RANGE 2
|
||||
#define GUNPOINT_DELAY_STAGE_2 25
|
||||
#define GUNPOINT_DELAY_STAGE_3 75 // cumulative with past stages, so 100 deciseconds
|
||||
#define GUNPOINT_MULT_STAGE_1 1
|
||||
#define GUNPOINT_MULT_STAGE_2 2
|
||||
#define GUNPOINT_MULT_STAGE_3 2.5
|
||||
|
||||
|
||||
/datum/component/gunpoint
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE
|
||||
|
||||
var/mob/living/target
|
||||
var/obj/item/gun/weapon
|
||||
|
||||
var/stage = 1
|
||||
var/damage_mult = GUNPOINT_MULT_STAGE_1
|
||||
|
||||
var/point_of_no_return = FALSE
|
||||
|
||||
// *extremely bad russian accent* no!
|
||||
/datum/component/gunpoint/Initialize(mob/living/targ, obj/item/gun/wep)
|
||||
if(!isliving(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
var/mob/living/shooter = parent
|
||||
target = targ
|
||||
weapon = wep
|
||||
RegisterSignal(targ, list(COMSIG_MOB_ATTACK_HAND, COMSIG_MOB_ITEM_ATTACK, COMSIG_MOVABLE_MOVED, COMSIG_MOB_FIRED_GUN), .proc/trigger_reaction)
|
||||
|
||||
RegisterSignal(weapon, list(COMSIG_ITEM_DROPPED, COMSIG_ITEM_EQUIPPED), .proc/cancel)
|
||||
|
||||
shooter.visible_message("<span class='danger'>[shooter] aims [weapon] point blank at [target]!</span>", \
|
||||
"<span class='danger'>You aim [weapon] point blank at [target]!</span>", target)
|
||||
to_chat(target, "<span class='userdanger'>[shooter] aims [weapon] point blank at you!</span>")
|
||||
|
||||
shooter.apply_status_effect(STATUS_EFFECT_HOLDUP)
|
||||
target.apply_status_effect(STATUS_EFFECT_HELDUP)
|
||||
|
||||
/*
|
||||
if(target.job == "Captain" && target.stat == CONSCIOUS && is_nuclear_operative(shooter))
|
||||
if(istype(weapon, /obj/item/gun/ballistic/rocketlauncher) && weapon.chambered)
|
||||
shooter.client.give_award(/datum/award/achievement/misc/rocket_holdup, shooter)
|
||||
*/
|
||||
|
||||
target.do_alert_animation(target)
|
||||
target.playsound_local(target.loc, 'sound/machines/chime.ogg', 50, TRUE)
|
||||
|
||||
addtimer(CALLBACK(src, .proc/update_stage, 2), GUNPOINT_DELAY_STAGE_2)
|
||||
|
||||
/datum/component/gunpoint/Destroy(force, silent)
|
||||
var/mob/living/shooter = parent
|
||||
shooter.remove_status_effect(STATUS_EFFECT_HOLDUP)
|
||||
target.remove_status_effect(STATUS_EFFECT_HELDUP)
|
||||
return ..()
|
||||
|
||||
/datum/component/gunpoint/RegisterWithParent()
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/check_deescalate)
|
||||
RegisterSignal(parent, COMSIG_MOB_APPLY_DAMGE, .proc/flinch)
|
||||
RegisterSignal(parent, COMSIG_MOB_ATTACK_HAND, .proc/check_shove)
|
||||
RegisterSignal(parent, list(COMSIG_LIVING_START_PULL, COMSIG_MOVABLE_BUMP), .proc/check_bump)
|
||||
|
||||
/datum/component/gunpoint/UnregisterFromParent()
|
||||
UnregisterSignal(parent, COMSIG_MOVABLE_MOVED)
|
||||
UnregisterSignal(parent, COMSIG_MOB_APPLY_DAMGE)
|
||||
UnregisterSignal(parent, COMSIG_MOB_ATTACK_HAND)
|
||||
UnregisterSignal(parent, list(COMSIG_LIVING_START_PULL, COMSIG_MOVABLE_BUMP))
|
||||
|
||||
/datum/component/gunpoint/proc/check_bump(atom/B, atom/A)
|
||||
var/mob/living/T = A
|
||||
if(T && T == target)
|
||||
var/mob/living/shooter = parent
|
||||
shooter.visible_message("<span class='danger'>[shooter] bumps into [target] and fumbles [shooter.p_their()] aim!</span>", \
|
||||
"<span class='danger'>You bump into [target] and fumble your aim!</span>", target)
|
||||
to_chat(target, "<span class='userdanger'>[shooter] bumps into you and fumbles [shooter.p_their()] aim!</span>")
|
||||
qdel(src)
|
||||
|
||||
/datum/component/gunpoint/proc/check_shove(mob/living/carbon/shooter, mob/shooter_again, mob/living/T)
|
||||
if(T == target && (shooter.a_intent == INTENT_DISARM || shooter.a_intent == INTENT_GRAB))
|
||||
shooter.visible_message("<span class='danger'>[shooter] bumps into [target] and fumbles [shooter.p_their()] aim!</span>", \
|
||||
"<span class='danger'>You bump into [target] and fumble your aim!</span>", target)
|
||||
to_chat(target, "<span class='userdanger'>[shooter] bumps into you and fumbles [shooter.p_their()] aim!</span>")
|
||||
qdel(src)
|
||||
|
||||
// if you're gonna try to break away from a holdup, better to do it right away
|
||||
/datum/component/gunpoint/proc/update_stage(new_stage)
|
||||
stage = new_stage
|
||||
if(stage == 2)
|
||||
to_chat(parent, "<span class='danger'>You steady [weapon] on [target].</span>")
|
||||
to_chat(target, "<span class='userdanger'>[parent] has steadied [weapon] on you!</span>")
|
||||
damage_mult = GUNPOINT_MULT_STAGE_2
|
||||
addtimer(CALLBACK(src, .proc/update_stage, 3), GUNPOINT_DELAY_STAGE_3)
|
||||
else if(stage == 3)
|
||||
to_chat(parent, "<span class='danger'>You have fully steadied [weapon] on [target].</span>")
|
||||
to_chat(target, "<span class='userdanger'>[parent] has fully steadied [weapon] on you!</span>")
|
||||
damage_mult = GUNPOINT_MULT_STAGE_3
|
||||
|
||||
/datum/component/gunpoint/proc/check_deescalate()
|
||||
if(!can_see(parent, target, GUNPOINT_SHOOTER_STRAY_RANGE - 1))
|
||||
cancel()
|
||||
|
||||
/datum/component/gunpoint/proc/trigger_reaction()
|
||||
if(point_of_no_return)
|
||||
return
|
||||
point_of_no_return = TRUE
|
||||
|
||||
var/mob/living/shooter = parent
|
||||
|
||||
if(!weapon.can_shoot() || !weapon.can_trigger_gun(shooter) || (weapon.weapon_weight == WEAPON_HEAVY && shooter.get_inactive_held_item()))
|
||||
shooter.visible_message("<span class='danger'>[shooter] fumbles [weapon]!</span>", \
|
||||
"<span class='danger'>You fumble [weapon] and fail to fire at [target]!</span>", target)
|
||||
to_chat(target, "<span class='userdanger'>[shooter] fumbles [weapon] and fails to fire at you!</span>")
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
if(weapon.chambered && weapon.chambered.BB)
|
||||
weapon.chambered.BB.damage *= damage_mult
|
||||
|
||||
if(weapon.check_botched(shooter))
|
||||
return
|
||||
|
||||
weapon.process_fire(target, shooter)
|
||||
qdel(src)
|
||||
|
||||
/datum/component/gunpoint/proc/cancel()
|
||||
var/mob/living/shooter = parent
|
||||
shooter.visible_message("<span class='danger'>[shooter] breaks [shooter.p_their()] aim on [target]!</span>", \
|
||||
"<span class='danger'>You are no longer aiming [weapon] at [target].</span>", target)
|
||||
to_chat(target, "<span class='userdanger'>[shooter] breaks [shooter.p_their()] aim on you!</span>")
|
||||
qdel(src)
|
||||
|
||||
/datum/component/gunpoint/proc/flinch(attacker, damage, damagetype, def_zone)
|
||||
var/mob/living/shooter = parent
|
||||
|
||||
var/flinch_chance = 50
|
||||
var/gun_hand = LEFT_HANDS
|
||||
|
||||
if(shooter.held_items[RIGHT_HANDS] == weapon)
|
||||
gun_hand = RIGHT_HANDS
|
||||
|
||||
if((def_zone == BODY_ZONE_L_ARM && gun_hand == LEFT_HANDS) || (def_zone == BODY_ZONE_R_ARM && gun_hand == RIGHT_HANDS))
|
||||
flinch_chance = 80
|
||||
|
||||
if(prob(flinch_chance))
|
||||
shooter.visible_message("<span class='danger'>[shooter] flinches!</span>", \
|
||||
"<span class='danger'>You flinch!</span>")
|
||||
trigger_reaction()
|
||||
|
||||
#undef GUNPOINT_SHOOTER_STRAY_RANGE
|
||||
#undef GUNPOINT_DELAY_STAGE_2
|
||||
#undef GUNPOINT_DELAY_STAGE_3
|
||||
#undef GUNPOINT_MULT_STAGE_1
|
||||
#undef GUNPOINT_MULT_STAGE_2
|
||||
#undef GUNPOINT_MULT_STAGE_3
|
||||
@@ -143,3 +143,27 @@
|
||||
. = ..()
|
||||
if(.)
|
||||
listening_in = tracker
|
||||
|
||||
// heldup is for the person being aimed at
|
||||
/datum/status_effect/heldup
|
||||
id = "heldup"
|
||||
duration = -1
|
||||
status_type = STATUS_EFFECT_MULTIPLE
|
||||
alert_type = /obj/screen/alert/status_effect/heldup
|
||||
|
||||
/obj/screen/alert/status_effect/heldup
|
||||
name = "Held Up"
|
||||
desc = "Making any sudden moves would probably be a bad idea!"
|
||||
icon_state = "aimed"
|
||||
|
||||
// holdup is for the person aiming
|
||||
/datum/status_effect/holdup
|
||||
id = "holdup"
|
||||
duration = -1
|
||||
status_type = STATUS_EFFECT_UNIQUE
|
||||
alert_type = /obj/screen/alert/status_effect/holdup
|
||||
|
||||
/obj/screen/alert/status_effect/holdup
|
||||
name = "Holding Up"
|
||||
desc = "You're currently pointing a gun at someone."
|
||||
icon_state = "aimed"
|
||||
|
||||
@@ -138,6 +138,9 @@
|
||||
AMob.grabbedby(src)
|
||||
return TRUE
|
||||
stop_pulling()
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_ATOM_START_PULL, AM, state, force)
|
||||
|
||||
if(AM.pulledby)
|
||||
log_combat(AM, AM.pulledby, "pulled from", src)
|
||||
AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once.
|
||||
|
||||
@@ -271,6 +271,9 @@
|
||||
|
||||
pulling = AM
|
||||
AM.pulledby = src
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_LIVING_START_PULL, AM, state, force)
|
||||
|
||||
if(!supress_message)
|
||||
var/sound_to_play = 'sound/weapons/thudswoosh.ogg'
|
||||
if(ishuman(src))
|
||||
|
||||
@@ -173,7 +173,6 @@
|
||||
if(pb_knockback > 0)
|
||||
var/atom/throw_target = get_edge_target_turf(pbtarget, user.dir)
|
||||
pbtarget.throw_at(throw_target, pb_knockback, 2)
|
||||
|
||||
else
|
||||
user.visible_message("<span class='danger'>[user] fires [src]!</span>", \
|
||||
"<span class='danger'>You fire [src]!</span>", \
|
||||
@@ -198,6 +197,12 @@
|
||||
return
|
||||
if(target == user && user.zone_selected != BODY_ZONE_PRECISE_MOUTH) //so we can't shoot ourselves (unless mouth selected)
|
||||
return
|
||||
if(ismob(target) && user.a_intent == INTENT_GRAB)
|
||||
if(user.GetComponent(/datum/component/gunpoint))
|
||||
to_chat(user, "<span class='warning'>You are already holding someone up!</span>")
|
||||
return
|
||||
user.AddComponent(/datum/component/gunpoint, target, src)
|
||||
return
|
||||
|
||||
if(istype(user))//Check if the user can use the gun, if the user isn't alive(turrets) assume it can.
|
||||
var/mob/living/L = user
|
||||
@@ -213,15 +218,9 @@
|
||||
shoot_with_empty_chamber(user)
|
||||
return
|
||||
|
||||
//Exclude lasertag guns from the TRAIT_CLUMSY check.
|
||||
if(clumsy_check)
|
||||
if(istype(user))
|
||||
if (HAS_TRAIT(user, TRAIT_CLUMSY) && prob(40))
|
||||
to_chat(user, "<span class='userdanger'>You shoot yourself in the foot with [src]!</span>")
|
||||
var/shot_leg = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
|
||||
process_fire(user, user, FALSE, params, shot_leg)
|
||||
user.dropItemToGround(src, TRUE)
|
||||
return
|
||||
if(check_botched(user))
|
||||
return
|
||||
|
||||
var/obj/item/bodypart/other_hand = user.has_hand_for_held_index(user.get_inactive_hand_index()) //returns non-disabled inactive hands
|
||||
if(weapon_weight == WEAPON_HEAVY && (user.get_inactive_held_item() || !other_hand))
|
||||
to_chat(user, "<span class='warning'>You need two hands to fire \the [src]!</span>")
|
||||
@@ -241,7 +240,15 @@
|
||||
|
||||
return process_fire(target, user, TRUE, params, null, bonus_spread)
|
||||
|
||||
|
||||
/obj/item/gun/proc/check_botched(mob/living/user, params)
|
||||
if(clumsy_check)
|
||||
if(istype(user))
|
||||
if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(40))
|
||||
to_chat(user, "<span class='userdanger'>You shoot yourself in the foot with [src]!</span>")
|
||||
var/shot_leg = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
|
||||
process_fire(user, user, FALSE, params, shot_leg)
|
||||
user.dropItemToGround(src, TRUE)
|
||||
return TRUE
|
||||
|
||||
/obj/item/gun/can_trigger_gun(mob/living/user)
|
||||
. = ..()
|
||||
@@ -300,6 +307,9 @@
|
||||
return TRUE
|
||||
|
||||
/obj/item/gun/proc/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
|
||||
if(user)
|
||||
SEND_SIGNAL(user, COMSIG_MOB_FIRED_GUN, user, target, params, zone_override)
|
||||
|
||||
add_fingerprint(user)
|
||||
|
||||
if(semicd)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 111 KiB |
@@ -384,6 +384,7 @@
|
||||
#include "code\datums\components\forced_gravity.dm"
|
||||
#include "code\datums\components\forensics.dm"
|
||||
#include "code\datums\components\gps.dm"
|
||||
#include "code\datums\components\gunpoint.dm"
|
||||
#include "code\datums\components\heirloom.dm"
|
||||
#include "code\datums\components\igniter.dm"
|
||||
#include "code\datums\components\infective.dm"
|
||||
|
||||
Reference in New Issue
Block a user