Merge branch 'master' into one_last_taser_attempt

This commit is contained in:
kevinz000
2020-01-29 16:27:58 -07:00
committed by GitHub
326 changed files with 2849 additions and 3620 deletions

View File

@@ -267,6 +267,7 @@
else
if(chambered)
sprd = round((rand() - 0.5) * DUALWIELD_PENALTY_EXTRA_MULTIPLIER * (randomized_gun_spread + randomized_bonus_spread))
before_firing(target,user)
if(!chambered.fire_casing(target, user, params, , suppressed, zone_override, sprd, src))
shoot_with_empty_chamber(user)
return
@@ -301,7 +302,7 @@
sprd = round((rand() - 0.5) * DUALWIELD_PENALTY_EXTRA_MULTIPLIER * (randomized_gun_spread + randomized_bonus_spread), 1)
else //Smart spread
sprd = round((((rand_spr/burst_size) * iteration) - (0.5 + (rand_spr * 0.25))) * (randomized_gun_spread + randomized_bonus_spread), 1)
before_firing(target,user)
if(!chambered.fire_casing(target, user, params, ,suppressed, zone_override, sprd, src))
shoot_with_empty_chamber(user)
firing = FALSE
@@ -477,6 +478,10 @@
qdel(pin)
pin = new /obj/item/firing_pin
//Happens before the actual projectile creation
/obj/item/gun/proc/before_firing(atom/target,mob/user)
return
/////////////
// ZOOMING //
/////////////

View File

@@ -96,15 +96,20 @@
"Gold Trim" = "detective_gold",
"The Peacemaker" = "detective_peacemaker"
)
var/list/safe_calibers
/obj/item/gun/ballistic/revolver/detective/Initialize()
. = ..()
safe_calibers = magazine.caliber
/obj/item/gun/ballistic/revolver/detective/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
if(magazine.caliber != initial(magazine.caliber))
if(chambered && !(chambered.caliber in safe_calibers))
if(prob(70 - (magazine.ammo_count() * 10))) //minimum probability of 10, maximum of 60
playsound(user, fire_sound, 50, 1)
to_chat(user, "<span class='userdanger'>[src] blows up in your face!</span>")
user.take_bodypart_damage(0,20)
user.dropItemToGround(src)
return 0
return FALSE
..()
/obj/item/gun/ballistic/revolver/detective/screwdriver_act(mob/living/user, obj/item/I)

View File

@@ -0,0 +1,375 @@
#define DUEL_IDLE 1
#define DUEL_PREPARATION 2
#define DUEL_READY 3
#define DUEL_COUNTDOWN 4
#define DUEL_FIRING 5
//paper rock scissors
#define DUEL_SETTING_A "wide"
#define DUEL_SETTING_B "cone"
#define DUEL_SETTING_C "pinpoint"
#define DUEL_HUGBOX_NONE 0 //dismember head
#define DUEL_HUGBOX_LETHAL 1 //200 damage to chest
#define DUEL_HUGBOX_NONLETHAL 2 //stamcrit
/datum/duel
var/obj/item/gun/energy/dueling/gun_A
var/obj/item/gun/energy/dueling/gun_B
var/state = DUEL_IDLE
var/required_distance = 5
var/list/confirmations = list()
var/list/fired = list()
var/countdown_length = 10
var/countdown_step = 0
/datum/duel/proc/try_begin()
//Check if both guns are held and if so begin.
var/mob/living/A = get_duelist(gun_A)
var/mob/living/B = get_duelist(gun_B)
if(!A || !B)
message_duelists("<span class='warning'>To begin the duel, both participants need to be holding paired dueling pistols.</span>")
return
begin()
/datum/duel/proc/begin()
state = DUEL_PREPARATION
confirmations.Cut()
fired.Cut()
countdown_step = countdown_length
message_duelists("<span class='notice'>Set your gun setting and move [required_distance] steps away from your opponent.</span>")
START_PROCESSING(SSobj,src)
/datum/duel/proc/get_duelist(obj/gun)
var/mob/living/G = gun.loc
if(!istype(G) || !G.is_holding(gun))
return null
return G
/datum/duel/proc/message_duelists(message)
var/mob/living/LA = get_duelist(gun_A)
if(LA)
to_chat(LA,message)
var/mob/living/LB = get_duelist(gun_B)
if(LB)
to_chat(LB,message)
/datum/duel/proc/other_gun(obj/item/gun/energy/dueling/G)
return G == gun_A ? gun_B : gun_A
/datum/duel/proc/end()
message_duelists("<span class='notice'>Duel finished. Re-engaging safety.</span>")
STOP_PROCESSING(SSobj,src)
state = DUEL_IDLE
/datum/duel/process()
switch(state)
if(DUEL_PREPARATION)
if(check_positioning())
confirm_positioning()
else if (!get_duelist(gun_A) && !get_duelist(gun_B))
end()
if(DUEL_READY)
if(!check_positioning())
back_to_prep()
else if(confirmations.len == 2)
confirm_ready()
if(DUEL_COUNTDOWN)
if(!check_positioning())
back_to_prep()
else
countdown_step()
if(DUEL_FIRING)
if(check_fired())
end()
/datum/duel/proc/back_to_prep()
message_duelists("<span class='notice'>Positions invalid. Please move to valid positions [required_distance] steps aways from each other to continue.</span>")
state = DUEL_PREPARATION
confirmations.Cut()
countdown_step = countdown_length
/datum/duel/proc/confirm_positioning()
message_duelists("<span class='notice'>Position confirmed. Confirm readiness by pulling the trigger once.</span>")
state = DUEL_READY
/datum/duel/proc/confirm_ready()
message_duelists("<span class='notice'>Readiness confirmed. Starting countdown. Commence firing at zero mark.</span>")
state = DUEL_COUNTDOWN
/datum/duel/proc/countdown_step()
countdown_step--
if(countdown_step == 0)
state = DUEL_FIRING
message_duelists("<span class='userdanger'>Fire!</span>")
else
message_duelists("<span class='userdanger'>[countdown_step]!</span>")
/datum/duel/proc/check_fired()
if(fired.len == 2)
return TRUE
//Let's say if gun was dropped/stowed the user is finished
if(!get_duelist(gun_A))
return TRUE
if(!get_duelist(gun_B))
return TRUE
return FALSE
/datum/duel/proc/check_positioning()
var/mob/living/A = get_duelist(gun_A)
var/mob/living/B = get_duelist(gun_B)
if(!A || !B)
return FALSE
if(!isturf(A.loc) || !isturf(B.loc))
return FALSE
if(get_dist(A,B) != required_distance)
return FALSE
for(var/turf/T in getline(get_turf(A),get_turf(B)))
if(is_blocked_turf(T,TRUE))
return FALSE
return TRUE
/obj/item/gun/energy/dueling
name = "dueling pistol"
desc = "High-tech dueling pistol. Launches chaff and projectile according to preset settings."
icon_state = "dueling_pistol"
item_state = "gun"
ammo_x_offset = 2
w_class = WEIGHT_CLASS_SMALL
ammo_type = list(/obj/item/ammo_casing/energy/duel)
automatic_charge_overlays = FALSE
var/unlocked = FALSE
var/setting = DUEL_SETTING_A
var/datum/duel/duel
var/mutable_appearance/setting_overlay
var/hugbox = DUEL_HUGBOX_NONE
/obj/item/gun/energy/dueling/hugbox
hugbox = DUEL_HUGBOX_LETHAL
/obj/item/gun/energy/dueling/hugbox/stamina
hugbox = DUEL_HUGBOX_NONLETHAL
/obj/item/gun/energy/dueling/Initialize()
. = ..()
setting_overlay = mutable_appearance(icon,setting_iconstate())
add_overlay(setting_overlay)
/obj/item/gun/energy/dueling/proc/setting_iconstate()
switch(setting)
if(DUEL_SETTING_A)
return "duel_red"
if(DUEL_SETTING_B)
return "duel_green"
if(DUEL_SETTING_C)
return "duel_blue"
return "duel_red"
/obj/item/gun/energy/dueling/attack_self(mob/living/user)
. = ..()
if(duel.state == DUEL_IDLE)
duel.try_begin()
else
toggle_setting(user)
/obj/item/gun/energy/dueling/proc/toggle_setting(mob/living/user)
switch(setting)
if(DUEL_SETTING_A)
setting = DUEL_SETTING_B
if(DUEL_SETTING_B)
setting = DUEL_SETTING_C
if(DUEL_SETTING_C)
setting = DUEL_SETTING_A
to_chat(user,"<span class='notice'>You switch [src] setting to [setting] mode.</span>")
update_icon()
/obj/item/gun/energy/dueling/update_icon(force_update)
. = ..()
if(setting_overlay)
cut_overlay(setting_overlay)
setting_overlay.icon_state = setting_iconstate()
add_overlay(setting_overlay)
/obj/item/gun/energy/dueling/Destroy()
. = ..()
if(duel.gun_A == src)
duel.gun_A = null
if(duel.gun_B == src)
duel.gun_B = null
duel = null
/obj/item/gun/energy/dueling/can_trigger_gun(mob/living/user)
. = ..()
switch(duel.state)
if(DUEL_FIRING)
return . && !duel.fired[src]
if(DUEL_READY)
return .
else
to_chat(user,"<span class='warning'>[src] is locked. Wait for FIRE signal before shooting.</span>")
return FALSE
/obj/item/gun/energy/dueling/proc/is_duelist(mob/living/L)
if(!istype(L))
return FALSE
if(!L.is_holding(duel.other_gun(src)))
return FALSE
return TRUE
/obj/item/gun/energy/dueling/process_fire(atom/target, mob/living/user, message, params, zone_override, bonus_spread)
if(duel.state == DUEL_READY)
duel.confirmations[src] = TRUE
to_chat(user,"<span class='notice'>You confirm your readiness.</span>")
return
else if(!is_duelist(target)) //I kinda want to leave this out just to see someone shoot a bystander or missing.
to_chat(user,"<span class='warning'>[src] safety system prevents shooting anyone but your designated opponent.</span>")
return
else
duel.fired[src] = TRUE
. = ..()
/obj/item/gun/energy/dueling/before_firing(target,user)
var/obj/item/ammo_casing/energy/duel/D = chambered
D.setting = setting
D.hugbox = hugbox
/obj/effect/temp_visual/dueling_chaff
icon = 'icons/effects/effects.dmi'
icon_state = "shield-old"
duration = 30
var/setting
/obj/effect/temp_visual/dueling_chaff/update_icon()
. = ..()
switch(setting)
if(DUEL_SETTING_A)
color = "red"
if(DUEL_SETTING_B)
color = "green"
if(DUEL_SETTING_C)
color = "blue"
//Casing
/obj/item/ammo_casing/energy/duel
e_cost = 0
projectile_type = /obj/item/projectile/energy/duel
var/setting
var/hugbox = DUEL_HUGBOX_NONE
/obj/item/ammo_casing/energy/duel/ready_proj(atom/target, mob/living/user, quiet, zone_override)
. = ..()
var/obj/item/projectile/energy/duel/D = BB
D.setting = setting
D.hugbox = hugbox
D.update_icon()
/obj/item/ammo_casing/energy/duel/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread, atom/fired_from)
. = ..()
var/obj/effect/temp_visual/dueling_chaff/C = new(get_turf(user))
C.setting = setting
C.update_icon()
//Projectile
/obj/item/projectile/energy/duel
name = "dueling beam"
icon_state = "declone"
is_reflectable = FALSE
homing = TRUE
var/setting
var/hugbox = DUEL_HUGBOX_NONE
/obj/item/projectile/energy/duel/update_icon()
. = ..()
switch(setting)
if(DUEL_SETTING_A)
color = "red"
if(DUEL_SETTING_B)
color = "green"
if(DUEL_SETTING_C)
color = "blue"
/obj/item/projectile/energy/duel/on_hit(atom/target, blocked)
. = ..()
var/turf/T = get_turf(target)
var/obj/effect/temp_visual/dueling_chaff/C = locate() in T
if(C)
var/counter_setting
switch(setting)
if(DUEL_SETTING_A)
counter_setting = DUEL_SETTING_B
if(DUEL_SETTING_B)
counter_setting = DUEL_SETTING_C
if(DUEL_SETTING_C)
counter_setting = DUEL_SETTING_A
if(C.setting == counter_setting)
return BULLET_ACT_BLOCK
var/mob/living/L = target
if(!istype(target))
return BULLET_ACT_BLOCK
switch(hugbox)
if(DUEL_HUGBOX_NONE)
var/obj/item/bodypart/B = L.get_bodypart(BODY_ZONE_HEAD)
B.dismember()
qdel(B)
if(DUEL_HUGBOX_LETHAL)
L.adjustBruteLoss(180)
L.death() //Die, powergamers.
if(DUEL_HUGBOX_NONLETHAL)
L.adjustStaminaLoss(200, forced = TRUE) //Die, powergamers x 2
L.Knockdown(100, override_hardstun = 100) //For good measure.
//Storage case.
/obj/item/storage/lockbox/dueling
name = "dueling pistol case"
desc = "Let's solve this like gentlespacemen."
icon_state = "medalbox+l"
item_state = "syringe_kit"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
w_class = WEIGHT_CLASS_NORMAL
req_access = list(ACCESS_CAPTAIN)
icon_locked = "medalbox+l"
icon_closed = "medalbox"
icon_broken = "medalbox+b"
var/gun_type = /obj/item/gun/energy/dueling
/obj/item/storage/lockbox/dueling/ComponentInitialize()
. = ..()
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
STR.max_w_class = WEIGHT_CLASS_SMALL
STR.max_items = 2
STR.can_hold = typecacheof(/obj/item/gun/energy/dueling)
/obj/item/storage/lockbox/dueling/update_icon()
cut_overlays()
var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
if(locked)
icon_state = "medalbox+l"
else
icon_state = "medalbox"
if(open)
icon_state += "open"
if(broken)
icon_state += "+b"
/obj/item/storage/lockbox/dueling/PopulateContents()
. = ..()
var/obj/item/gun/energy/dueling/gun_A = new gun_type(src)
var/obj/item/gun/energy/dueling/gun_B = new gun_type(src)
var/datum/duel/D = new
gun_A.duel = D
gun_B.duel = D
D.gun_A = gun_A
D.gun_B = gun_B
/obj/item/storage/lockbox/dueling/hugbox
gun_type = /obj/item/gun/energy/dueling/hugbox
/obj/item/storage/lockbox/dueling/hugbox/stamina
gun_type = /obj/item/gun/energy/dueling/hugbox/stamina