|
|
|
|
@@ -17,8 +17,9 @@
|
|
|
|
|
var/last_fire = 0
|
|
|
|
|
|
|
|
|
|
/obj/item/gun/ballistic/revolver/process_fire(atom/target, mob/living/user, message, params, zone_override, bonus_spread)
|
|
|
|
|
..()
|
|
|
|
|
last_fire = world.time
|
|
|
|
|
. = ..()
|
|
|
|
|
if(.)
|
|
|
|
|
last_fire = world.time
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/obj/item/gun/ballistic/revolver/chamber_round(spin_cylinder = TRUE, replace_new_round)
|
|
|
|
|
@@ -99,7 +100,7 @@
|
|
|
|
|
var/live_ammo = get_ammo(FALSE, FALSE)
|
|
|
|
|
. += "[live_ammo ? live_ammo : "None"] of those are live rounds."
|
|
|
|
|
if (current_skin)
|
|
|
|
|
. += "It can be spun with <b>alt+click</b>"
|
|
|
|
|
. += span_notice("It can be spun with [EXAMINE_HINT("alt-click")].")
|
|
|
|
|
|
|
|
|
|
/obj/item/gun/ballistic/revolver/ignition_effect(atom/A, mob/user)
|
|
|
|
|
if(last_fire && last_fire + 15 SECONDS > world.time)
|
|
|
|
|
@@ -185,22 +186,47 @@
|
|
|
|
|
desc = "A Russian-made revolver for drinking games. Uses .357 ammo, and has a mechanism requiring you to spin the chamber before each trigger pull."
|
|
|
|
|
icon_state = "russianrevolver"
|
|
|
|
|
accepted_magazine_type = /obj/item/ammo_box/magazine/internal/cylinder/rus357
|
|
|
|
|
var/spun = FALSE
|
|
|
|
|
hidden_chambered = TRUE //Cheater.
|
|
|
|
|
gun_flags = NOT_A_REAL_GUN
|
|
|
|
|
can_hold_up = FALSE // for obvious reasons
|
|
|
|
|
doafter_self_shoot = FALSE // snowflake
|
|
|
|
|
/// If we've been spun before firing
|
|
|
|
|
var/spun = FALSE
|
|
|
|
|
/// Do after for trying to fire the gun
|
|
|
|
|
var/aim_time = 4 SECONDS
|
|
|
|
|
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/examine(mob/user)
|
|
|
|
|
. = ..()
|
|
|
|
|
. += span_notice("You can change length of your pause before pulling the trigger with [EXAMINE_HINT("alt-right-click")].")
|
|
|
|
|
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/click_alt_secondary(mob/user)
|
|
|
|
|
if(loc != user)
|
|
|
|
|
to_chat(user, span_warning("You need to be holding the gun to determine how long you are going to pause!"))
|
|
|
|
|
return CLICK_ACTION_BLOCKING
|
|
|
|
|
var/new_aim_time = tgui_input_number(user, "How long will you pause before pulling the trigger (seconds)?", "Do you feel lucky?", (aim_time / (1 SECONDS)), 10, 0)
|
|
|
|
|
if(loc != user || user.incapacitated)
|
|
|
|
|
return CLICK_ACTION_BLOCKING
|
|
|
|
|
aim_time = new_aim_time * (1 SECONDS)
|
|
|
|
|
to_chat(user, span_warning("You're going to pause [aim_time] second\s before pulling the trigger[aim_time == 0 ? "... Good luck" : ""]."))
|
|
|
|
|
return CLICK_ACTION_SUCCESS
|
|
|
|
|
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/dropped(mob/user, silent)
|
|
|
|
|
. = ..()
|
|
|
|
|
aim_time = initial(aim_time) // next person chooses their own time
|
|
|
|
|
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/do_spin()
|
|
|
|
|
. = ..()
|
|
|
|
|
if(.)
|
|
|
|
|
spun = TRUE
|
|
|
|
|
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/attackby(obj/item/A, mob/user, params)
|
|
|
|
|
..()
|
|
|
|
|
if(get_ammo() > 0)
|
|
|
|
|
spin()
|
|
|
|
|
update_appearance()
|
|
|
|
|
A.update_appearance()
|
|
|
|
|
return
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/can_shoot()
|
|
|
|
|
return TRUE // we ALWAYS want to shoot. even if we don't have a chambered round, even if our chambered round has no bullet
|
|
|
|
|
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/load_gun(obj/item/ammo, mob/living/user)
|
|
|
|
|
. = ..()
|
|
|
|
|
if(!.)
|
|
|
|
|
return
|
|
|
|
|
do_spin()
|
|
|
|
|
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/can_trigger_gun(mob/living/user, akimbo_usage)
|
|
|
|
|
if(akimbo_usage)
|
|
|
|
|
@@ -210,94 +236,140 @@
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/attack_self(mob/user)
|
|
|
|
|
if(!spun)
|
|
|
|
|
spin()
|
|
|
|
|
spun = TRUE
|
|
|
|
|
return
|
|
|
|
|
..()
|
|
|
|
|
return TRUE
|
|
|
|
|
return ..()
|
|
|
|
|
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/fire_gun(atom/target, mob/living/user, flag, params)
|
|
|
|
|
. = ..(null, user, flag, params)
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/handle_chamber(empty_chamber = TRUE, from_firing = TRUE, chamber_next_round = TRUE)
|
|
|
|
|
from_firing = FALSE // never eject casings from firing the gun
|
|
|
|
|
return ..()
|
|
|
|
|
|
|
|
|
|
var/tk_controlled = FALSE
|
|
|
|
|
if(flag)
|
|
|
|
|
if(!(target in user.contents) && ismob(target))
|
|
|
|
|
if(user.combat_mode) // Flogging action
|
|
|
|
|
return
|
|
|
|
|
else if (HAS_TRAIT_FROM_ONLY(src, TRAIT_TELEKINESIS_CONTROLLED, REF(user))) // if we're far away, you can still fire it at yourself if you have TK.
|
|
|
|
|
tk_controlled = TRUE
|
|
|
|
|
|
|
|
|
|
if(isliving(user))
|
|
|
|
|
if(!can_trigger_gun(user))
|
|
|
|
|
return
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/try_fire_gun(atom/target, mob/living/user, params)
|
|
|
|
|
if(user.combat_mode)
|
|
|
|
|
return FALSE // melee attack
|
|
|
|
|
if(target != user)
|
|
|
|
|
playsound(src, dry_fire_sound, 30, TRUE)
|
|
|
|
|
shoot_with_empty_chamber(user)
|
|
|
|
|
spun = FALSE
|
|
|
|
|
user.visible_message(
|
|
|
|
|
span_danger("[user.name] tries to fire \the [src] at the same time, but only succeeds at looking like an idiot."),
|
|
|
|
|
span_danger("[user] tries to fire \the [src] aimed at something else, but only succeeds at looking like an idiot."),
|
|
|
|
|
span_danger("\The [src]'s anti-combat mechanism prevents you from firing it at anyone but yourself!"),
|
|
|
|
|
)
|
|
|
|
|
return
|
|
|
|
|
return TRUE // no melee attack
|
|
|
|
|
if(!spun)
|
|
|
|
|
to_chat(user, span_warning("You need to spin \the [src]'s chamber first!"))
|
|
|
|
|
return TRUE // no melee attack
|
|
|
|
|
if(HAS_TRAIT(user, TRAIT_CURSED)) // I cannot live, I cannot die, trapped in myself, body my holding cell.
|
|
|
|
|
to_chat(user, span_warning("What a horrible night... To have a curse!"))
|
|
|
|
|
return TRUE // no melee attack
|
|
|
|
|
if(loc != user)
|
|
|
|
|
if(tk_firing(user))
|
|
|
|
|
to_chat(user, span_warning("Russian roulette is stressful enough without trying to focus on telekinesis!"))
|
|
|
|
|
else
|
|
|
|
|
to_chat(user, span_warning("You need to be holding the gun to fire it!"))
|
|
|
|
|
return TRUE // no melee attack
|
|
|
|
|
|
|
|
|
|
if(ishuman(user))
|
|
|
|
|
var/mob/living/carbon/human/H = user
|
|
|
|
|
if(!spun)
|
|
|
|
|
to_chat(user, span_warning("You need to spin \the [src]'s chamber first!"))
|
|
|
|
|
return
|
|
|
|
|
return ..() // try to shoot the gun
|
|
|
|
|
|
|
|
|
|
spun = FALSE
|
|
|
|
|
|
|
|
|
|
var/zone = check_zone(user.zone_selected)
|
|
|
|
|
var/obj/item/bodypart/affecting = H.get_bodypart(zone)
|
|
|
|
|
var/is_target_face = zone == BODY_ZONE_HEAD || zone == BODY_ZONE_PRECISE_EYES || zone == BODY_ZONE_PRECISE_MOUTH
|
|
|
|
|
var/loaded_rounds = get_ammo(FALSE, FALSE) // check before it is fired
|
|
|
|
|
|
|
|
|
|
if(HAS_TRAIT(user, TRAIT_CURSED)) // I cannot live, I cannot die, trapped in myself, body my holding cell.
|
|
|
|
|
to_chat(user, span_warning("What a horrible night... To have a curse!"))
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if(loaded_rounds && is_target_face)
|
|
|
|
|
add_memory_in_range(user, 7, /datum/memory/witnessed_russian_roulette, \
|
|
|
|
|
protagonist = user, \
|
|
|
|
|
antagonist = src, \
|
|
|
|
|
rounds_loaded = loaded_rounds, \
|
|
|
|
|
aimed_at = affecting.name, \
|
|
|
|
|
result = (chambered ? "lost" : "won"), \
|
|
|
|
|
// Replaces clumsy check with a do after
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/check_botched(mob/living/user, atom/target)
|
|
|
|
|
if(aim_time <= 0)
|
|
|
|
|
return FALSE
|
|
|
|
|
user.visible_message(
|
|
|
|
|
span_danger("[user] aims \the [src] at [user.p_their()] [parse_zone(user.zone_selected)]..."),
|
|
|
|
|
span_userdanger("You aim \the [src] at your [parse_zone(user.zone_selected)]..."),
|
|
|
|
|
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
|
|
|
|
|
)
|
|
|
|
|
if(prob(10) && !HAS_TRAIT(user, TRAIT_FEARLESS))
|
|
|
|
|
user.adjust_jitter(aim_time)
|
|
|
|
|
if(!do_after(user, aim_time, target))
|
|
|
|
|
if(!user.incapacitated)
|
|
|
|
|
user.visible_message(
|
|
|
|
|
span_danger("[user] loses [user.p_their()] nerve and puts \the [src] down."),
|
|
|
|
|
span_userdanger("You lose your nerve and put \the [src] down."),
|
|
|
|
|
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
|
|
|
|
|
)
|
|
|
|
|
return TRUE
|
|
|
|
|
return FALSE
|
|
|
|
|
|
|
|
|
|
if(chambered)
|
|
|
|
|
var/obj/item/ammo_casing/AC = chambered
|
|
|
|
|
if(AC.fire_casing(user, user, params, distro = 0, quiet = 0, zone_override = null, spread = 0, fired_from = src))
|
|
|
|
|
playsound(user, fire_sound, fire_sound_volume, vary_fire_sound)
|
|
|
|
|
if(is_target_face)
|
|
|
|
|
shoot_self(user, affecting)
|
|
|
|
|
else if(tk_controlled) // the consequence of you doing the telekinesis stuff
|
|
|
|
|
to_chat(user, span_userdanger("As your mind concentrates on the revolver, you realize that it's pointing towards your head a little too late!"))
|
|
|
|
|
shoot_self(user, BODY_ZONE_HEAD)
|
|
|
|
|
else
|
|
|
|
|
user.visible_message(span_danger("[user.name] cowardly fires [src] at [user.p_their()] [affecting.name]!"), span_userdanger("You cowardly fire [src] at your [affecting.name]!"), span_hear("You hear a gunshot!"))
|
|
|
|
|
chambered = null
|
|
|
|
|
user.add_mood_event("russian_roulette_lose", /datum/mood_event/russian_roulette_lose)
|
|
|
|
|
return
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/before_firing(atom/target, mob/user)
|
|
|
|
|
if(target != user)
|
|
|
|
|
CRASH("Russian revolver somehow got to before_firing with a target that isn't the user!")
|
|
|
|
|
// we will definitely have a chambered round, but not always projectile
|
|
|
|
|
if(check_zone(user.zone_selected) == BODY_ZONE_HEAD)
|
|
|
|
|
chambered.loaded_projectile?.damage = 300
|
|
|
|
|
chambered.loaded_projectile?.wound_bonus = 100
|
|
|
|
|
else
|
|
|
|
|
chambered.loaded_projectile?.damage = 80
|
|
|
|
|
chambered.loaded_projectile?.wound_bonus = 10
|
|
|
|
|
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/fire_gun(atom/target, mob/living/user, flag, params)
|
|
|
|
|
// . = false = no shot fired
|
|
|
|
|
. = ..()
|
|
|
|
|
spun = FALSE
|
|
|
|
|
var/is_target_face = check_zone(user.zone_selected) == BODY_ZONE_HEAD
|
|
|
|
|
var/aimed_at_readable = parse_zone(user.zone_selected)
|
|
|
|
|
var/loaded_rounds = get_ammo(FALSE, FALSE) // check before it is fired
|
|
|
|
|
if(loaded_rounds && is_target_face)
|
|
|
|
|
add_memory_in_range(user, 7, /datum/memory/witnessed_russian_roulette, \
|
|
|
|
|
protagonist = user, \
|
|
|
|
|
antagonist = src, \
|
|
|
|
|
rounds_loaded = loaded_rounds, \
|
|
|
|
|
aimed_at = aimed_at_readable, \
|
|
|
|
|
result = (. ? "lost" : "won"), \
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if(!.)
|
|
|
|
|
if(loaded_rounds && is_target_face)
|
|
|
|
|
user.add_mood_event("russian_roulette_win", /datum/mood_event/russian_roulette_win, loaded_rounds)
|
|
|
|
|
user.visible_message(
|
|
|
|
|
span_danger("[user][is_target_face ? "": " cowardly"] points \the [src] at [user.p_their()] [aimed_at_readable], pulls the trigger, and... nothing happens!"),
|
|
|
|
|
span_danger("You[is_target_face ? "": " cowardly"] point \the [src] at your [aimed_at_readable], pull the trigger, and... nothing happens!"),
|
|
|
|
|
span_hear("You hear a click!"),
|
|
|
|
|
vision_distance = COMBAT_MESSAGE_RANGE,
|
|
|
|
|
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
|
|
|
|
|
)
|
|
|
|
|
return TRUE // so they don't hit themselves in the forehead. because returning FALSE translates to "do melee attack" for whatever reason
|
|
|
|
|
|
|
|
|
|
user.visible_message(span_danger("*click*"))
|
|
|
|
|
playsound(src, dry_fire_sound, 30, TRUE)
|
|
|
|
|
user.visible_message(
|
|
|
|
|
span_danger("[user][is_target_face ? "": " cowardly"] aims \the [src] at [user.p_their()] [aimed_at_readable] as it goes off!"),
|
|
|
|
|
span_danger("You[is_target_face ? "": " cowardly"] aim \the [src] at your [aimed_at_readable] as it goes off![user.stat >= HARD_CRIT ? " <b>Everything suddenly goes black.</b>" : ""]"),
|
|
|
|
|
span_hear("You hear a grunt[user.stat == CONSCIOUS ? "" : ", followed by a thud"]!"),
|
|
|
|
|
vision_distance = COMBAT_MESSAGE_RANGE,
|
|
|
|
|
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
|
|
|
|
|
)
|
|
|
|
|
shoot_self(user, check_zone(user.zone_selected))
|
|
|
|
|
return .
|
|
|
|
|
|
|
|
|
|
/// Called after successfully(if you can call it that) shooting ourselves
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/proc/shoot_self(mob/living/carbon/human/user, affecting = BODY_ZONE_HEAD)
|
|
|
|
|
user.apply_damage(300, BRUTE, affecting)
|
|
|
|
|
user.visible_message(span_danger("[user.name] fires [src] at [user.p_their()] head!"), span_userdanger("You fire [src] at your head!"), span_hear("You hear a gunshot!"))
|
|
|
|
|
user.add_mood_event(
|
|
|
|
|
"russian_roulette_lose",
|
|
|
|
|
affecting == BODY_ZONE_HEAD ? /datum/mood_event/russian_roulette_lose : /datum/mood_event/russian_roulette_lose_cheater,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/soul
|
|
|
|
|
name = "cursed Russian revolver"
|
|
|
|
|
desc = "To play with this revolver requires wagering your very soul."
|
|
|
|
|
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/soul/shoot_self(mob/living/user)
|
|
|
|
|
/obj/item/gun/ballistic/revolver/russian/soul/shoot_self(mob/living/user, affecting = BODY_ZONE_HEAD)
|
|
|
|
|
. = ..()
|
|
|
|
|
var/obj/item/soulstone/anybody/revolver/stone = new /obj/item/soulstone/anybody/revolver(get_turf(src))
|
|
|
|
|
if(!stone.capture_soul(user, forced = TRUE)) //Something went wrong
|
|
|
|
|
qdel(stone)
|
|
|
|
|
if(affecting == BODY_ZONE_HEAD)
|
|
|
|
|
var/obj/item/soulstone/anybody/revolver/stone = new(user.drop_location())
|
|
|
|
|
if(!stone.capture_soul(user, forced = TRUE)) //Something went wrong
|
|
|
|
|
qdel(stone)
|
|
|
|
|
return
|
|
|
|
|
user.visible_message(
|
|
|
|
|
span_danger("[user]'s soul is captured by \the [src]!"),
|
|
|
|
|
span_userdanger("You've lost the gamble! Your soul is forfeit!"),
|
|
|
|
|
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
|
|
|
|
|
)
|
|
|
|
|
return
|
|
|
|
|
user.visible_message(span_danger("[user.name]'s soul is captured by \the [src]!"), span_userdanger("You've lost the gamble! Your soul is forfeit!"))
|
|
|
|
|
|
|
|
|
|
user.visible_message(
|
|
|
|
|
span_danger("[user] is punished for trying to cheat the game!"),
|
|
|
|
|
span_userdanger("You've lost the gamble! Not only is your soul forfeit, but it is whisked away for attempting to cheat death!"),
|
|
|
|
|
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
|
|
|
|
|
)
|
|
|
|
|
user.dust(drop_items = TRUE)
|
|
|
|
|
|
|
|
|
|
/obj/item/gun/ballistic/revolver/reverse //Fires directly at its user... unless the user is a clown, of course.
|
|
|
|
|
clumsy_check = FALSE
|
|
|
|
|
|