Files
7d058fc613 Jukebox TGUI update, Earphones autoplay, sound keys refactor (#21630)
V2 of [previous music playing
PR](https://github.com/Aurorastation/Aurora.3/pull/21466). TLDR no
longer uses the connect_range component for implementation because it
turned out a bit too inflexible for overlapping music players.

Removes a NanoUI template for the [TGUI
update](https://github.com/Aurorastation/Aurora.3/pull/21046).

New changelog:
  - refactor: "Ported Jukebox's NanoUI interface to TGUI."
- refactor: "Ported Jukebox audio playing functionality to a component."
- refactor: "Sound keys refactored from singletons to datums, along with
larger breakout of sound.dm to allow for easier SFX updates in future."
  - code_imp: "Expanded track datums to include track lengths."
- code_imp: "Reorganized music file folders for more intuitive access."
  - rscadd: "Earphone status feedback text now includes track length."
  - rscadd: "Added autoplay functionality to earphones."
- bugfix: "Fixed earphones' 'Previous Song' verb not sending you to the
end of the playlist when used while the first track is selected."
- bugfix: "Fixed gain adjustment for 'Konyang-1' (-23 dB -> standard
-9.8 dB)."
- bugfix: "Fixed y-offset of audioconsole-running overlay animation to
line up with the actual screen."

---------

Co-authored-by: VMSolidus <evilexecutive@gmail.com>
2026-01-22 16:54:40 +00:00

692 lines
26 KiB
Plaintext

/mob/living/carbon/human/proc/get_unarmed_attack(var/mob/living/carbon/human/target, var/hit_zone)
if(src.default_attack && src.default_attack.is_usable(src, target, hit_zone))
if(pulling_punches)
var/datum/unarmed_attack/soft_type = src.default_attack.get_sparring_variant()
if(soft_type)
return soft_type
return src.default_attack
for(var/datum/unarmed_attack/u_attack in species.unarmed_attacks)
if(u_attack.is_usable(src, target, hit_zone))
if(pulling_punches)
var/datum/unarmed_attack/soft_variant = u_attack.get_sparring_variant()
if(soft_variant)
return soft_variant
return u_attack
return null
/mob/living/carbon/human/attack_hand(mob/living/carbon/M as mob)
var/mob/living/carbon/human/H = M
if(!M.can_use_hand())
return
..()
if ((H.invisibility == INVISIBILITY_LEVEL_TWO) && M.back && (istype(M.back, /obj/item/rig)))
to_chat(H, SPAN_DANGER("You are now visible."))
H.set_invisibility(0)
anim(get_turf(H), H,'icons/mob/mob.dmi',,"uncloak",,H.dir)
anim(get_turf(H), H, 'icons/effects/effects.dmi', "electricity",null,20,null)
for(var/mob/O in oviewers(H))
O.show_message("[H.name] appears from thin air!",1)
playsound(get_turf(H), 'sound/effects/stealthoff.ogg', 75, 1)
// Should this all be in Touch()?
if(istype(H))
if(H != src && (check_shields(0, null, H, H.zone_sel.selecting, H.name) != BULLET_ACT_HIT))
H.do_attack_animation(src)
return 0
if(H.gloves && istype(H.gloves,/obj/item/clothing/gloves))
var/obj/item/clothing/gloves/G = H.gloves
if(G.cell)
if(M.a_intent == I_HURT)//Stungloves. Any contact will stun the alien.
if(G.cell.charge >= 2500)
G.cell.use(G.cell.charge) //So it drains the cell.
visible_message(SPAN_DANGER("[src] has been touched with the stun gloves by [M]!"))
M.attack_log += "\[[time_stamp()]\] <span class='warning'>Stungloved [src.name] ([src.ckey])</span>"
src.attack_log += "\[[time_stamp()]\] <font color='orange'>Has been stungloved by [M.name] ([M.ckey])</font>"
msg_admin_attack("[key_name_admin(M)] stungloved [src.name] ([src.ckey]) (<A href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[M.x];Y=[M.y];Z=[M.z]'>JMP</a>)",ckey=key_name(M),ckey_target=key_name(src))
apply_effects(5,5,0,0,5,0,0,0,0)
apply_damage(rand(5,25), DAMAGE_BURN, M.zone_sel.selecting)
if(prob(15))
playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1)
M.visible_message(SPAN_WARNING("The power source on [M]'s stun gloves overloads in a terrific fashion!"),
SPAN_WARNING("Your jury rigged stun gloves malfunction!"),
SPAN_WARNING("You hear a loud sparking."))
if(prob(50))
M.apply_damage(rand(1,5), DAMAGE_BURN)
for(M in viewers(3, null))
M.flash_act(ignore_inherent = TRUE)
return 1
else
to_chat(M, SPAN_WARNING("Not enough charge!"))
visible_message(SPAN_DANGER("[src] has been touched with the stun gloves by [M]!"))
return
if(istype(H.gloves, /obj/item/clothing/gloves/boxing/hologlove))
H.do_attack_animation(src)
var/damage = rand(0, 9)
if(!damage)
playsound(loc, SFX_PUNCH_MISS, 25, 1, -1)
visible_message(SPAN_DANGER("[H] has attempted to punch [src]!"))
return 0
var/obj/item/organ/external/affecting = get_organ(ran_zone(H.zone_sel.selecting))
if((H.mutations & HULK) || H.is_berserk())
damage += 5
playsound(loc, SFX_PUNCH, 25, 1, -1)
visible_message(SPAN_DANGER("[H] has punched [src]!"))
apply_damage(damage, DAMAGE_PAIN, affecting)
if(damage >= 9)
visible_message(SPAN_DANGER("[H] has weakened [src]!"))
apply_effect(4, WEAKEN)
return
var/datum/martial_art/attacker_style = H.primary_martial_art
switch(M.a_intent)
if(I_HELP)
if(H != src && istype(H) && (is_asystole() || (status_flags & FAKEDEATH) || failed_last_breath) && !src.on_fire)
if (cpr)
cpr = FALSE
return
cpr = TRUE
cpr(H, TRUE)
else if(!(M == src && apply_pressure(M, M.zone_sel.selecting)))
help_shake_act(M)
return 1
if(I_GRAB)
if(M == src || anchored)
return 0
if(M.is_pacified())
to_chat(M, SPAN_NOTICE("You don't want to risk hurting [src]!"))
return 0
if(attacker_style && attacker_style.grab_act(H, src))
return 1
for(var/obj/item/grab/G in src.grabbed_by)
if(G.assailant == M)
to_chat(M, SPAN_NOTICE("You already grabbed [src]."))
return
if (!attempt_grab(M))
return
if(w_uniform)
w_uniform.add_fingerprint(M)
var/obj/item/grab/G = new /obj/item/grab(M, M, src)
if(buckled_to)
to_chat(M, SPAN_NOTICE("You cannot grab [src], [src.get_pronoun("he")] [get_pronoun("is")] buckled in!"))
if(!G) //the grab will delete itself in New if affecting is anchored
return
M.put_in_active_hand(G)
G.synch()
LAssailant = WEAKREF(M)
H.do_attack_animation(src)
playsound(loc, SFX_GRAB, 50, FALSE, -1)
if(H.gloves && istype(H.gloves,/obj/item/clothing/gloves/force/syndicate)) //only antag gloves can do this for now
G.state = GRAB_AGGRESSIVE
G.icon_state = "grabbed1"
G.hud.icon_state = "reinforce1"
G.last_action = world.time
visible_message(SPAN_WARNING("[M] gets a strong grip on [src]!"))
return 1
visible_message(SPAN_WARNING("[M] has grabbed [src] passively!"))
return 1
if(I_HURT)
if(M.is_pacified())
to_chat(M, SPAN_NOTICE("You don't want to risk hurting [src]!"))
return 0
if(attacker_style && attacker_style.harm_act(H, src))
return 1
if(!istype(H))
attack_generic(H,rand(1,3),"punched")
return
var/rand_damage = rand(1, 5)
var/block = 0
var/accurate = 0
var/hit_zone = H.zone_sel.selecting
var/obj/item/organ/external/affecting = get_organ(hit_zone)
if(!affecting || affecting.is_stump())
to_chat(M, SPAN_DANGER("They are missing that limb!"))
return 1
switch(src.a_intent)
if(I_HELP)
// We didn't see this coming, so we get the full blow
rand_damage = 5
accurate = 1
if(I_HURT, I_GRAB)
// We're in a fighting stance, there's a chance we block
if(src.canmove && src!=H && prob(20))
block = 1
if (M.grabbed_by.len)
// Someone got a good grip on them, they won't be able to do much damage
rand_damage = max(1, rand_damage - 2)
if(src.grabbed_by.len || src.buckled_to || !src.canmove || src==H)
accurate = 1 // certain circumstances make it impossible for us to evade punches
rand_damage = 5
// Process evasion and blocking
var/miss_type = 0
var/attack_message
if(!accurate)
/* ~Hubblenaut
This place is kind of convoluted and will need some explaining.
ran_zone() will pick out of 11 zones, thus the chance for hitting
our target where we want to hit them is circa 9.1%.
Now since we want to statistically hit our target organ a bit more
often than other organs, we add a base chance of 20% for hitting it.
This leaves us with the following chances:
If aiming for chest:
27.3% chance you hit your target organ
70.5% chance you hit a random other organ
2.2% chance you miss
If aiming for something else:
23.2% chance you hit your target organ
56.8% chance you hit a random other organ
15.0% chance you miss
Note: We don't use get_zone_with_miss_chance() here since the chances
were made for projectiles.
TODO: proc for melee combat miss chances depending on organ?
*/
if(prob(80))
hit_zone = ran_zone(hit_zone)
if(prob(15) && hit_zone != BP_CHEST) // Missed!
if(!src.lying)
attack_message = "[H] attempted to strike [src], but missed!"
else
attack_message = "[H] attempted to strike [src], but [src.get_pronoun("he")] rolled out of the way!"
src.set_dir(pick(GLOB.cardinals))
miss_type = 1
if(!miss_type && block)
attack_message = "[H] went for [src]'s [affecting.name] but was blocked!"
miss_type = 2
// See what attack they use
var/datum/unarmed_attack/attack = H.get_unarmed_attack(src, hit_zone)
if(!attack)
return 0
H.do_attack_animation(src)
if(!attack_message)
attack.show_attack(H, src, hit_zone, rand_damage)
else
H.visible_message(SPAN_DANGER("[attack_message]"))
playsound(loc, ((miss_type) ? (miss_type == 1 ? attack.miss_sound : 'sound/weapons/thudswoosh.ogg') : attack.attack_sound), 25, 1, -1)
H.attack_log += "\[[time_stamp()]\] <span class='warning'>[miss_type ? (miss_type == 1 ? "Missed" : "Blocked") : "[pick(attack.attack_verb)]"] [src.name] ([src.ckey])</span>"
src.attack_log += "\[[time_stamp()]\] <font color='orange'>[miss_type ? (miss_type == 1 ? "Was missed by" : "Has blocked") : "Has Been [pick(attack.attack_verb)]"] by [H.name] ([H.ckey])</font>"
msg_admin_attack("[key_name(H)] [miss_type ? (miss_type == 1 ? "has missed" : "was blocked by") : "has [pick(attack.attack_verb)]"] [key_name(src)] (<A href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[H.x];Y=[H.y];Z=[H.z]'>JMP</a>)",ckey=key_name(H),ckey_target=key_name(src))
if(miss_type)
return 0
var/real_damage = rand_damage
var/hit_dam_type = attack.damage_type
var/damage_flags = attack.damage_flags()
real_damage += attack.get_unarmed_damage(src, H)
real_damage *= damage_multiplier
rand_damage *= damage_multiplier
if((H.mutations & HULK))
real_damage *= 2 // Hulks do twice the damage
rand_damage *= 2
if(H.is_berserk())
real_damage *= 1.5 // Nightshade increases damage by 50%
rand_damage *= 1.5
var/obj/item/organ/internal/parasite/blackkois/P = H.internal_organs_by_name["blackkois"]
if(istype(P))
if(P.stage >= 5)
real_damage *= 1.5 // Final stage black k'ois mycosis increases damage by 50%
rand_damage *= 1.5
real_damage = max(1, real_damage)
if(H.gloves)
if(istype(H.gloves, /obj/item/clothing/gloves))
var/obj/item/clothing/gloves/G = H.gloves
real_damage += G.punch_force
hit_dam_type = G.punch_damtype
if(H.pulling_punches)
hit_dam_type = DAMAGE_PAIN
if(istype(H.gloves,/obj/item/clothing/gloves/force))
var/obj/item/clothing/gloves/force/X = H.gloves
real_damage *= X.amplification
// Apply additional unarmed effects.
attack.apply_effects(H, src, rand_damage, hit_zone)
// Finally, apply damage to target
apply_damage(real_damage, hit_dam_type, hit_zone, damage_flags = damage_flags, armor_pen = attack.armor_penetration)
if(M.resting && src.help_up_offer)
M.visible_message(SPAN_WARNING("[M] slaps away [src]'s hand!"))
src.help_up_offer = 0
if(I_DISARM)
if(M.is_pacified())
to_chat(M, SPAN_NOTICE("You don't want to risk hurting [src]!"))
return FALSE
var/disarm_cost
var/obj/item/organ/internal/machine/power_core/cell = M.internal_organs_by_name[BP_CELL]
var/obj/item/cell/potato
if(cell)
potato = cell.cell
if(isipc(M))
disarm_cost = potato.maxcharge / 24
if(potato.charge < disarm_cost)
to_chat(M, SPAN_DANGER("You don't have enough charge to disarm someone!"))
return FALSE
potato.use(disarm_cost)
else
if(M.max_stamina > 0)
disarm_cost = M.max_stamina / 6
if(attacker_style && attacker_style.disarm_act(H, src))
return TRUE
if(M.is_drowsy())
disarm_cost *= 1.25
if(M.stamina <= disarm_cost)
to_chat(M, SPAN_DANGER("You're too tired to disarm someone!"))
return FALSE
M.stamina = clamp(M.stamina - disarm_cost, 0, M.max_stamina) // attempting to knock something out of someone's hands, or pushing them over, is exhausting!
else if(M.max_stamina <= 0)
disarm_cost = M.max_nutrition / 6
if(M.nutrition <= disarm_cost)
to_chat(M, SPAN_DANGER("You don't have enough power to disarm someone!"))
return FALSE
M.nutrition = clamp(M.nutrition - disarm_cost, 0, M.max_nutrition)
M.attack_log += "\[[time_stamp()]\] <span class='warning'>Disarmed [src.name] ([src.ckey])</span>"
src.attack_log += "\[[time_stamp()]\] <font color='orange'>Has been disarmed by [M.name] ([M.ckey])</font>"
msg_admin_attack("[key_name(M)] disarmed [src.name] ([src.ckey]) (<A href='byond://?_src_=holder;adminplayerobservecoodjump=1;X=[M.x];Y=[M.y];Z=[M.z]'>JMP</a>)",ckey=key_name(M),ckey_target=key_name(src))
M.do_attack_animation(src)
if(w_uniform)
w_uniform.add_fingerprint(M)
var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_sel.selecting))
var/list/holding = list(get_active_hand() = 40, get_inactive_hand() = 20)
//See if they have any weapons to retaliate with
if(src.a_intent != I_HELP)
for(var/obj/item/W in holding)
if(W && prob(holding[W]))
if(istype(W, /obj/item/grab))
var/obj/item/grab/G = W
if(G.affecting && G.affecting != M)
visible_message(SPAN_WARNING("[src] repositions \the [G.affecting] to block \the [M]'s disarm attempt!"), SPAN_NOTICE("You reposition \the [G.affecting] to block \the [M]'s disarm attempt!"))
G.attack_hand(M)
return
if(istype(W,/obj/item/gun))
var/list/turfs = list()
for(var/turf/T in view())
turfs += T
if(turfs.len)
var/turf/target = pick(turfs)
visible_message(SPAN_DANGER("[src]'s [W] goes off during the struggle!"))
return W.afterattack(target,src)
else
if(M.Adjacent(src))
visible_message(SPAN_DANGER("[src] retaliates against [M]'s disarm attempt with [W]!"))
return M.attackby(W,src)
var/randn = rand(1, 100)
if(z_eye) //They're looking down in front of them.
var/turf/T = loc
var/obj/structure/railing/problem_railing
var/same_loc = FALSE
for(var/obj/structure/railing/R in T)
if(R.dir == dir)
problem_railing = R
break
for(var/obj/structure/railing/R in get_step(T, dir))
if(R.dir == REVERSE_DIR(dir))
problem_railing = R
same_loc = TRUE
break
if(problem_railing)
if(!problem_railing.turf_is_crowded(TRUE))
visible_message(SPAN_DANGER("[H] shoves [src] over the railing!"), SPAN_DANGER("[H] shoves you over the railing!"))
apply_effect(5, WEAKEN)
forceMove(same_loc ? problem_railing.loc : problem_railing.get_destination_turf(src))
return
else
to_chat(H, SPAN_NOTICE("It's too crowded, you can't push [src] off the railing!")) //No return is intentional - it'll continue with a normal shove.
else
visible_message(SPAN_DANGER("[H] pushes [src] forward!"), SPAN_DANGER("[H] pushes you forward!"))
apply_effect(5, WEAKEN)
var/turf/current_turf = get_turf(z_eye)
forceMove(GET_TURF_ABOVE(current_turf)) //We use GET_TURF_ABOVE so people can't cheese it by turning their sprite.
return
if(randn <= 25)
if(H.gloves && istype(H.gloves,/obj/item/clothing/gloves/force))
apply_effect(6, WEAKEN)
playsound(loc, 'sound/weapons/push_connect.ogg', 50, 1, -1)
visible_message(SPAN_DANGER("[M] hurls [src] to the floor!"))
step_away(src,M,15)
sleep(3)
step_away(src,M,15)
return
else
var/armor_check = 100 * get_blocked_ratio(affecting, DAMAGE_BRUTE, damage = 20)
apply_effect(3, WEAKEN, armor_check)
if(armor_check < 100)
visible_message(SPAN_DANGER("[M] has pushed [src]!"))
playsound(loc, 'sound/weapons/push_connect.ogg', 50, 1, -1)
else
visible_message(SPAN_WARNING("[M] attempted to push [src]!"))
playsound(loc, 'sound/weapons/push.ogg', 50, 1, -1)
return
if(randn <= 60)
if(H.gloves && istype(H.gloves,/obj/item/clothing/gloves/force))
playsound(loc, 'sound/weapons/push_connect.ogg', 50, 1, -1)
visible_message(SPAN_DANGER("[M] shoves, sending [src] flying!"))
step_away(src,M,15)
sleep(1)
step_away(src,M,15)
sleep(1)
step_away(src,M,15)
sleep(1)
step_away(src,M,15)
sleep(1)
apply_effect(1, WEAKEN, get_blocked_ratio(M.zone_sel.selecting, DAMAGE_BRUTE, damage = 20)*100)
return
//See about breaking grips or pulls
if(break_all_grabs(M))
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
return
//Actually disarm them, if possible
for(var/obj/item/I in holding)
if(unEquip(I))
visible_message(SPAN_DANGER("\The [M] has disarmed \the [src]!"))
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
return
else
to_chat(M, SPAN_WARNING("You cannot disarm \the [I] from \the [src], as it's attached to them!"))
//No return here is intentional, as it will then try to disarm other items, and/or play a failed disarm message
playsound(loc, SFX_PUNCH_MISS, 25, 1, -1)
visible_message(SPAN_DANGER("[M] attempted to disarm [src]!"))
return
/mob/living/carbon/human/proc/cpr(mob/living/carbon/human/H, var/starting = FALSE, var/cpr_mode)
var/obj/item/main_hand = H.get_active_hand()
var/obj/item/off_hand = H.get_inactive_hand()
if(istype(main_hand) || istype(off_hand))
cpr = FALSE
to_chat(H, SPAN_NOTICE("You cannot perform CPR with anything in your hands."))
return
if(!(cpr && H.Adjacent(src) && (is_asystole() || (status_flags & FAKEDEATH) || failed_last_breath))) //Keeps doing CPR unless cancelled, or the target recovers
cpr = FALSE
to_chat(H, SPAN_NOTICE("You stop performing [cpr_mode] on \the [src]."))
return
else if (starting)
var/list/options = list(
"Full CPR" = image('icons/mob/screen/radial.dmi', "cpro2"),
"Compressions" = image('icons/mob/screen/generic.dmi', "cpr"),
"Mouth-to-Mouth" = image('icons/mob/screen/radial.dmi', "iv_tank")
)
cpr_mode = show_radial_menu(H, src, options, require_near = TRUE, tooltips = TRUE, no_repeat_close = TRUE)
if(!cpr_mode)
cpr = FALSE
return
to_chat(H, SPAN_NOTICE("You begin performing [cpr_mode] on \the [src]."))
H.do_attack_animation(src, null, image('icons/mob/screen/generic.dmi', src, "cpr", src.layer + 1))
var/starting_pixel_y = pixel_y
animate(src, pixel_y = starting_pixel_y + 4, time = 2)
animate(src, pixel_y = starting_pixel_y, time = 2)
if(!do_after(H, 8, do_flags = DO_DEFAULT | DO_USER_UNIQUE_ACT)) //Chest compressions are fast, need to wait for the loading bar to do mouth to mouth
to_chat(H, SPAN_NOTICE("You stop performing [cpr_mode] on \the [src]."))
cpr = FALSE //If it cancelled, cancel it. Simple.
if(cpr_mode == "Full CPR")
cpr_compressions(H)
cpr_ventilation(H)
if(cpr_mode == "Compressions")
cpr_compressions(H)
if(cpr_mode == "Mouth-to-Mouth")
cpr_ventilation(H)
cpr(H, FALSE, cpr_mode) //Again.
/mob/living/carbon/human/proc/cpr_compressions(mob/living/carbon/human/H)
if(is_asystole())
if(prob(5 * rand(2, 3)))
var/obj/item/organ/external/chest = get_organ(BP_CHEST)
if(chest)
chest.fracture()
var/obj/item/organ/internal/heart/heart = internal_organs_by_name[BP_HEART]
if(heart)
heart.external_pump = list(world.time, 0.4 + 0.1 + rand(-0.1,0.1))
if(stat != DEAD && prob(10 * rand(0.5, 1)))
resuscitate()
/mob/living/carbon/human/proc/cpr_ventilation(mob/living/carbon/human/H)
if(!H.check_has_mouth())
to_chat(H, SPAN_WARNING("You don't have a mouth, you cannot do mouth-to-mouth resuscitation!"))
return
if(!check_has_mouth())
to_chat(H, SPAN_WARNING("They don't have a mouth, you cannot do mouth-to-mouth resuscitation!"))
return
if((H.head && (H.head.body_parts_covered & FACE)) || (H.wear_mask && (H.wear_mask.body_parts_covered & FACE)))
to_chat(H, SPAN_WARNING("You need to remove your mouth covering for mouth-to-mouth resuscitation!"))
return 0
if((head && (head.body_parts_covered & FACE)) || (wear_mask && (wear_mask.body_parts_covered & FACE)))
to_chat(H, SPAN_WARNING("You need to remove \the [src]'s mouth covering for mouth-to-mouth resuscitation!"))
return 0
if (!H.internal_organs_by_name[H.species.breathing_organ])
to_chat(H, SPAN_DANGER("You need lungs for mouth-to-mouth resuscitation!"))
return
if(!need_breathe())
return
var/obj/item/organ/internal/lungs/L = internal_organs_by_name[species.breathing_organ]
if(L)
var/datum/gas_mixture/breath = H.get_breath_from_environment()
var/fail = L.handle_breath(breath, 1)
if(!fail)
if(!L.is_bruised() || (L.is_bruised() && L.rescued))
losebreath = 0
to_chat(src, SPAN_NOTICE("You feel a breath of fresh air enter your lungs. It feels good."))
/mob/living/carbon/human/proc/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, inrange, params)
return
/mob/living/carbon/human/attack_generic(mob/user, damage, attack_message, environment_smash, armor_penetration, attack_flags, damage_type)
if(!damage)
return
user.attack_log += "\[[time_stamp()]\] <span class='warning'>attacked [src.name] ([src.ckey])</span>"
src.attack_log += "\[[time_stamp()]\] <font color='orange'>was attacked by [user.name] ([user.ckey])</font>"
user.do_attack_animation(src)
if(damage < 15 && (check_shields(damage, null, user, null, "\the [user]") != BULLET_ACT_HIT))
return
visible_message(SPAN_DANGER("[user] has [attack_message] [src]!"))
var/dam_zone = user.zone_sel?.selecting
var/obj/item/organ/external/affecting = dam_zone ? get_organ(dam_zone) : pick(organs)
if(affecting)
apply_damage(damage, damage_type ? damage_type : DAMAGE_BRUTE, affecting, armor_pen = armor_penetration, damage_flags = attack_flags)
updatehealth()
return affecting
//Used to attack a joint through grabbing
/mob/living/carbon/human/proc/grab_joint(var/mob/living/user, var/def_zone)
var/has_grab = 0
if(user.limb_breaking)
return 0
for(var/obj/item/grab/G in list(user.l_hand, user.r_hand))
if(G.affecting == src && G.state == GRAB_NECK)
has_grab = 1
break
if(!has_grab)
return 0
if(!def_zone) def_zone = user.zone_sel.selecting
var/target_zone = check_zone(def_zone)
if(!target_zone)
return 0
var/obj/item/organ/external/organ = get_organ(check_zone(target_zone))
if(!organ || ORGAN_IS_DISLOCATED(organ) || organ.dislocated == -1)
return 0
user.visible_message(SPAN_WARNING("[user] begins to dislocate [src]'s [organ.joint]!"))
user.limb_breaking = TRUE
if(do_after(user, 100))
organ.dislocate(1)
admin_attack_log(user, src, "dislocated [organ.joint].", "had his [organ.joint] dislocated.", "dislocated [organ.joint] of")
src.visible_message(SPAN_DANGER("[src]'s [organ.joint] [pick("gives way","caves in","crumbles","collapses")]!"))
user.limb_breaking = FALSE
return 1
user.visible_message(SPAN_WARNING("[user] fails to dislocate [src]'s [organ.joint]!"))
user.limb_breaking = FALSE
return 0
//Breaks all grips and pulls that the mob currently has.
/mob/living/carbon/human/proc/break_all_grabs(mob/living/carbon/user)
var/success = 0
if(pulling)
visible_message(SPAN_DANGER("[user] has broken [src]'s grip on [pulling]!"))
success = 1
stop_pulling()
if(istype(l_hand, /obj/item/grab))
var/obj/item/grab/lgrab = l_hand
if(lgrab.affecting)
visible_message(SPAN_DANGER("[user] has broken [src]'s grip on [lgrab.affecting]!"))
success = 1
spawn(1)
qdel(lgrab)
if(istype(r_hand, /obj/item/grab))
var/obj/item/grab/rgrab = r_hand
if(rgrab.affecting)
visible_message(SPAN_DANGER("[user] has broken [src]'s grip on [rgrab.affecting]!"))
success = 1
spawn(1)
qdel(rgrab)
return success
//Apply pressure to wounds.
/mob/living/carbon/human/proc/apply_pressure(mob/living/user, var/target_zone)
var/obj/item/organ/external/organ = get_organ(target_zone)
if(!organ || !(organ.status & ORGAN_BLEEDING) || organ.status & ORGAN_ROBOT)
return 0
if(organ.applied_pressure)
var/message = SPAN_WARNING("[ismob(organ.applied_pressure)? "Someone" : "\A [organ.applied_pressure]"] is already applying pressure to [user == src? "your [organ.name]" : "[src]'s [organ.name]"].")
to_chat(user, message)
return 0
if(user == src)
user.visible_message(SPAN_NOTICE("\The [user] starts applying pressure to [user.get_pronoun("his")] [organ.name]!"),
SPAN_NOTICE("You start applying pressure to your [organ.name]!"))
else
user.visible_message(SPAN_NOTICE("\The [user] starts applying pressure to [src]'s [organ.name]!"),
SPAN_NOTICE("You start applying pressure to [src]'s [organ.name]!"))
spawn(0)
organ.applied_pressure = user
//apply pressure as long as they stay still and keep grabbing
do_after(user, INFINITY, do_flags = (DO_DEFAULT & ~DO_SHOW_PROGRESS) | DO_USER_UNIQUE_ACT)
organ.applied_pressure = null
if(user == src)
user.visible_message(SPAN_NOTICE("\The [user] stops applying pressure to [user.get_pronoun("his")] [organ.name]!"),
SPAN_NOTICE("You stop applying pressure to your [organ.name]!"))
else
user.visible_message(SPAN_NOTICE("\The [user] stops applying pressure to [src]'s [organ.name]!"),
SPAN_NOTICE("You stop applying pressure to [src]'s [organ.name]!"))
return 1
/mob/living/carbon/human/verb/check_attacks()
set name = "Check Attacks"
set category = "IC"
set src = usr
var/dat = "<b><font size = 5>Known Attacks</font></b><br/><br/>"
for(var/datum/unarmed_attack/u_attack in species.unarmed_attacks)
dat += "<b>Primarily [u_attack.attack_name] </b><br/><br/><br/>"
if(default_attack)
dat += "Current default attack: [default_attack.attack_name] - <a href='byond://?src=[REF(src)];default_attk=reset_attk'>Reset</a><br/><br/>"
for(var/datum/unarmed_attack/u_attack in species.unarmed_attacks)
var/sparring_variant = ""
var/sparring_variant_desc = ""
if(u_attack.sparring_variant_type)
var/datum/unarmed_attack/spar_attack = u_attack.sparring_variant_type
sparring_variant = " | Sparring Variant: [capitalize_first_letters(initial(spar_attack.attack_name))]"
sparring_variant_desc = "[initial(spar_attack.desc)]<br/>"
if(u_attack == default_attack)
dat += "<b>Primarily [capitalize_first_letters(u_attack.attack_name)][sparring_variant]</b> - default - <a href='byond://?src=[REF(src)];default_attk=reset_attk'>Reset</a><br/>"
dat += "Description: [u_attack.desc]<br/>[sparring_variant_desc]"
dat += "<br/>"
else
dat += "<b>Primarily [capitalize_first_letters(u_attack.attack_name)][sparring_variant]</b> - <a href='byond://?src=[REF(src)];default_attk=[REF(u_attack)]'>Set Default</a><br/>"
dat += "Description: [u_attack.desc]<br/>[sparring_variant_desc]"
dat += "<br/>"
var/datum/browser/attack_win = new(src, "checkattack", "Known Attacks", 450, 500)
attack_win.set_content(dat)
attack_win.open()
/mob/living/carbon/human/proc/set_default_attack(var/datum/unarmed_attack/u_attack)
default_attack = u_attack