This commit is contained in:
Timothy Teakettle
2020-06-10 23:14:25 +01:00
parent 8ec5672b85
commit 4c55e86da7
74 changed files with 1495 additions and 386 deletions

View File

@@ -45,20 +45,8 @@
desc = "A solid wall of slightly twitching tendrils with a reflective glow."
damaged_desc = "A wall of twitching tendrils with a reflective glow."
icon_state = "blob_glow"
flags_1 = CHECK_RICOCHET_1
flags_ricochet = RICOCHET_SHINY
point_return = 8
max_integrity = 100
brute_resist = 1
explosion_block = 2
/obj/structure/blob/shield/reflective/handle_ricochet(obj/item/projectile/P)
var/turf/p_turf = get_turf(P)
var/face_direction = get_dir(src, p_turf)
var/face_angle = dir2angle(face_direction)
var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180))
if(abs(incidence_s) > 90 && abs(incidence_s) < 270)
return FALSE
var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s)
P.setAngle(new_angle_s)
visible_message("<span class='warning'>[P] reflects off [src]!</span>")
return TRUE

View File

@@ -8,7 +8,7 @@
/datum/supply_pack/security
group = "Security"
access = ACCESS_SECURITY
access_any = ACCESS_FORENSICS_LOCKERS //| ACCESS_SECURITY
crate_type = /obj/structure/closet/crate/secure/gear
can_private_buy = FALSE
@@ -228,3 +228,38 @@
access = ACCESS_ARMORY
crate_name = "sporting crate"
crate_type = /obj/structure/closet/crate/secure // Would have liked a wooden crate but access >:(
/datum/supply_pack/security/dumdum
name = ".38 DumDum Speedloader"
desc = "Contains one speedloader of .38 DumDum ammunition, good for embedding in soft targets. Requires Security or Forensics access to open."
cost = 1200
access = FALSE
small_item = TRUE
access_any = list(ACCESS_SECURITY, ACCESS_FORENSICS_LOCKERS)
contains = list(/obj/item/ammo_box/c38/dumdum)
crate_name = ".38 match crate"
/datum/supply_pack/security/match
name = ".38 Match Grade Speedloader"
desc = "Contains one speedloader of match grade .38 ammunition, perfect for showing off trickshots. Requires Security or Forensics access to open."
cost = 1200
access = FALSE
small_item = TRUE
access_any = list(ACCESS_SECURITY, ACCESS_FORENSICS_LOCKERS)
contains = list(/obj/item/ammo_box/c38/match)
crate_name = ".38 match crate"
/datum/supply_pack/security/stingpack
name = "Stingbang Grenade Pack"
desc = "Contains five \"stingbang\" grenades, perfect for stopping riots and playing morally unthinkable pranks. Requires Security access to open."
cost = 2500
contains = list(/obj/item/storage/box/stingbangs)
crate_name = "stingbang grenade pack crate"
/datum/supply_pack/security/stingpack/single
name = "Stingbang Single-Pack"
desc = "Contains one \"stingbang\" grenade, perfect for playing meanhearted pranks. Requires Security access to open."
cost = 1400
small_item = TRUE
contains = list(/obj/item/grenade/stingbang)

View File

@@ -14,7 +14,7 @@
if(!I.embedding || I.embedding == EMBED_HARMLESS)
I.embedding = EMBED_POINTY
I.AddElement(/datum/element/embed, I.embedding)
I.updateEmbedding()
I.name = "pointy [I.name]"
GLOB.embedpocalypse = TRUE
@@ -40,7 +40,7 @@
if(!I.embedding)
I.embedding = EMBED_HARMLESS
I.AddElement(/datum/element/embed, I.embedding)
I.updateEmbedding()
I.name = "sticky [I.name]"
GLOB.stickpocalypse = TRUE

View File

@@ -95,7 +95,7 @@
//We want an accurate reading of .len
listclearnulls(BP.embedded_objects)
for(var/obj/item/embeddies in BP.embedded_objects)
if(!embeddies.is_embed_harmless())
if(!embeddies.isEmbedHarmless())
temp_bleed += 0.5
if(brutedamage >= 20)

View File

@@ -54,6 +54,8 @@ In all, this is a lot like the monkey code. /N
return
switch(M.a_intent)
if(INTENT_HELP)
if(M == src && check_self_for_injuries())
return
help_shake_act(M)
if(INTENT_GRAB)
grabbedby(M)

View File

@@ -267,7 +267,15 @@
visible_message("<span class='danger'>[usr] [internal ? "opens" : "closes"] the valve on [src]'s [ITEM.name].</span>", \
"<span class='userdanger'>[usr] [internal ? "opens" : "closes"] the valve on your [ITEM.name].</span>", \
target = usr, target_message = "<span class='danger'>You [internal ? "opens" : "closes"] the valve on [src]'s [ITEM.name].</span>")
if(href_list["embedded_object"] && usr.canUseTopic(src, BE_CLOSE, NO_DEXTERITY))
var/obj/item/bodypart/L = locate(href_list["embedded_limb"]) in bodyparts
if(!L)
return
var/obj/item/I = locate(href_list["embedded_object"]) in L.embedded_objects
if(!I || I.loc != src) //no item, no limb, or item is not in limb or in the person anymore
return
SEND_SIGNAL(src, COMSIG_CARBON_EMBED_RIP, I, L)
return
/mob/living/carbon/fall(forced)
loc.handle_fall(src, forced)//it's loc so it doesn't call the mob's handle_fall which does nothing

View File

@@ -272,6 +272,9 @@
to_chat(M, "<span class='warning'>You can't put [p_them()] out with just your bare hands!</span>")
return
if(M == src && check_self_for_injuries())
return
if(health >= 0 && !(HAS_TRAIT(src, TRAIT_FAKEDEATH)))
var/friendly_check = FALSE
if(mob_run_block(M, 0, M.name, ATTACK_TYPE_UNARMED, 0, null, null, null))
@@ -339,6 +342,26 @@
update_mobility()
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
/// Check ourselves to see if we've got any shrapnel, return true if we do. This is a much simpler version of what humans do, we only indicate we're checking ourselves if there's actually shrapnel
/mob/living/carbon/proc/check_self_for_injuries()
if(stat == DEAD || stat == UNCONSCIOUS)
return
var/embeds = FALSE
for(var/X in bodyparts)
var/obj/item/bodypart/LB = X
for(var/obj/item/I in LB.embedded_objects)
if(!embeds)
embeds = TRUE
// this way, we only visibly try to examine ourselves if we have something embedded, otherwise we'll still hug ourselves :)
visible_message("<span class='notice'>[src] examines [p_them()]self.</span>", \
"<span class='notice'>You check yourself for shrapnel.</span>")
if(I.isEmbedHarmless())
to_chat(src, "\t <a href='?src=[REF(src)];embedded_object=[REF(I)];embedded_limb=[REF(LB)]' class='warning'>There is \a [I] stuck to your [LB.name]!</a>")
else
to_chat(src, "\t <a href='?src=[REF(src)];embedded_object=[REF(I)];embedded_limb=[REF(LB)]' class='warning'>There is \a [I] embedded in your [LB.name]!</a>")
return embeds
/mob/living/carbon/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0)
. = ..()

View File

@@ -31,14 +31,35 @@
else if(get_bodypart(BODY_ZONE_HEAD))
. += "<span class='deadsay'>It appears that [t_his] brain is missing...</span>"
var/list/missing = get_missing_limbs()
var/list/msg = list("<span class='warning'>")
var/list/missing = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)
var/list/disabled = list()
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
if(BP.disabled)
disabled += BP
missing -= BP.body_zone
for(var/obj/item/I in BP.embedded_objects)
if(I.isEmbedHarmless())
msg += "<B>[t_He] [t_has] \a [icon2html(I, user)] [I] stuck to [t_his] [BP.name]!</B>\n"
else
msg += "<B>[t_He] [t_has] \a [icon2html(I, user)] [I] embedded in [t_his] [BP.name]!</B>\n"
for(var/X in disabled)
var/obj/item/bodypart/BP = X
var/damage_text
if(!(BP.get_damage(include_stamina = FALSE) >= BP.max_damage)) //Stamina is disabling the limb
damage_text = "limp and lifeless"
else
damage_text = (BP.brute_dam >= BP.burn_dam) ? BP.heavy_brute_msg : BP.heavy_burn_msg
msg += "<B>[capitalize(t_his)] [BP.name] is [damage_text]!</B>\n"
for(var/t in missing)
if(t==BODY_ZONE_HEAD)
. += "<span class='deadsay'><B>[t_His] [parse_zone(t)] is missing!</B></span>"
msg += "<span class='deadsay'><B>[t_His] [parse_zone(t)] is missing!</B></span>\n"
continue
. += "<span class='warning'><B>[t_His] [parse_zone(t)] is missing!</B></span>"
msg += "<span class='warning'><B>[t_His] [parse_zone(t)] is missing!</B></span>\n"
var/list/msg = list()
var/temp = getBruteLoss()
if(!(user == src && src.hal_screwyhud == SCREWYHUD_HEALTHY)) //fake healthy
if(temp)

View File

@@ -159,7 +159,7 @@
disabled += BP
missing -= BP.body_zone
for(var/obj/item/I in BP.embedded_objects)
if(I.is_embed_harmless())
if(I.isEmbedHarmless())
msg += "<B>[t_He] [t_has] \a [icon2html(I, user)] [I] stuck to [t_his] [BP.name]!</B>\n"
else
msg += "<B>[t_He] [t_has] \a [icon2html(I, user)] [I] embedded in [t_his] [BP.name]!</B>\n"

View File

@@ -220,7 +220,7 @@
var/obj/item/I = locate(href_list["embedded_object"]) in L.embedded_objects
if(!I || I.loc != src) //no item, no limb, or item is not in limb or in the person anymore
return
SEND_SIGNAL(src, COMSIG_HUMAN_EMBED_RIP, I, L)
SEND_SIGNAL(src, COMSIG_CARBON_EMBED_RIP, I, L)
return
if(href_list["item"])

View File

@@ -620,7 +620,7 @@
to_send += "\t <span class='[no_damage ? "notice" : "warning"]'>Your [LB.name] [HAS_TRAIT(src, TRAIT_SELF_AWARE) ? "has" : "is"] [status].</span>\n"
for(var/obj/item/I in LB.embedded_objects)
if(I.is_embed_harmless())
if(I.isEmbedHarmless())
to_chat(src, "\t <a href='?src=[REF(src)];embedded_object=[REF(I)];embedded_limb=[REF(LB)]' class='warning'>There is \a [I] stuck to your [LB.name]!</a>")
else
to_chat(src, "\t <a href='?src=[REF(src)];embedded_object=[REF(I)];embedded_limb=[REF(LB)]' class='warning'>There is \a [I] embedded in your [LB.name]!</a>")

View File

@@ -1,7 +1,12 @@
/mob/living/proc/run_armor_check(def_zone = null, attack_flag = "melee", absorb_text = "Your armor absorbs the blow!", soften_text = "Your armor softens the blow!", armour_penetration, penetrated_text = "Your armor was penetrated!")
/mob/living/proc/run_armor_check(def_zone = null, attack_flag = "melee", absorb_text = null, soften_text = null, silent=FALSE, armour_penetration, penetrated_text)
var/armor = getarmor(def_zone, attack_flag)
if(armor <= 0)
return armor
if(silent)
return max(0, armor - armour_penetration)
//the if "armor" check is because this is used for everything on /living, including humans
if(armor && armour_penetration)
armor = max(0, armor - armour_penetration)
@@ -10,7 +15,7 @@
else if(armor >= 100)
if(absorb_text)
to_chat(src, "<span class='danger'>[absorb_text]</span>")
else if(armor > 0)
else
if(soften_text)
to_chat(src, "<span class='danger'>[soften_text]</span>")
return armor

View File

@@ -15,4 +15,4 @@
/obj/item/throwing_star/ninja
name = "ninja throwing star"
throwforce = 30
embedding = list("pain_mult" = 6, "embed_chance" = 100, "fall_chance" = 0)
embedding = list("pain_mult" = 6, "embed_chance" = 100, "fall_chance" = 0, "embed_chance_turf_mod" = 15)

View File

@@ -203,7 +203,7 @@
throwforce = initial(throwforce)
playsound(user, 'sound/weapons/saberoff.ogg', 5, 1)
to_chat(user, "<span class='warning'>[src] can now be concealed.</span>")
RemoveElement(/datum/element/embed, embedding)
updateEmbedding()
else
on = TRUE
force = 18
@@ -214,7 +214,7 @@
throwforce = 35
playsound(user, 'sound/weapons/saberon.ogg', 5, 1)
to_chat(user, "<span class='warning'>[src] is now active.</span>")
AddElement(/datum/element/embed, embedding)
updateEmbedding()
update_icon()
/obj/item/pen/edagger/update_icon_state()

View File

@@ -1,17 +1,21 @@
/obj/item/ammo_casing/proc/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread, atom/fired_from)
distro += variance
for (var/i = max(1, pellets), i > 0, i--)
var/targloc = get_turf(target)
ready_proj(target, user, quiet, zone_override, fired_from)
var/targloc = get_turf(target)
ready_proj(target, user, quiet, zone_override, fired_from)
if(pellets == 1)
if(distro) //We have to spread a pixel-precision bullet. throw_proj was called before so angles should exist by now...
if(randomspread)
spread = round((rand() - 0.5) * distro)
else //Smart spread
spread = round((i / pellets - 0.5) * distro)
spread = round(1 - 0.5) * distro
if(!throw_proj(target, targloc, user, params, spread))
return 0
if(i > 1)
newshot()
return FALSE
else
if(isnull(BB))
return FALSE
AddComponent(/datum/component/pellet_cloud, projectile_type, pellets)
SEND_SIGNAL(src, COMSIG_PELLET_CLOUD_INIT, target, user, fired_from, randomspread, spread, zone_override, params, distro)
if(click_cooldown_override)
user.changeNext_move(click_cooldown_override)
else
@@ -31,7 +35,7 @@
else
BB.def_zone = user.zone_selected
BB.suppressed = quiet
if(isgun(fired_from))
var/obj/item/gun/G = fired_from
BB.damage *= G.projectile_damage_multiplier

View File

@@ -21,3 +21,16 @@
name = "1.95x129mm incendiary bullet casing"
desc = "A 1.95x129mm bullet casing designed with a chemical-filled capsule on the tip that when bursted, reacts with the atmosphere to produce a fireball, engulfing the target in flames."
projectile_type = /obj/item/projectile/bullet/incendiary/mm195x129
/obj/item/ammo_casing/mm712x82/match
name = "7.12x82mm match bullet casing"
desc = "A 7.12x82mm bullet casing manufactured to unfailingly high standards, you could pull off some cool trickshots with this."
projectile_type = /obj/projectile/bullet/mm712x82_match
/obj/projectile/bullet/mm712x82_match
name = "7.12x82mm match bullet"
damage = 40
ricochets_max = 2
ricochet_chance = 60
ricochet_auto_aim_range = 4
ricochet_incidence_leeway = 35

View File

@@ -11,6 +11,12 @@
desc = "A .357 armor-piercing bullet casing."
projectile_type = /obj/item/projectile/bullet/a357/ap
/obj/item/ammo_casing/a357/match
name = ".357 match bullet casing"
desc = "A .357 bullet casing, manufactured to exceedingly high standards."
caliber = "357"
projectile_type = /obj/projectile/bullet/a357/match
// 7.62x38mmR (Nagant Revolver)
/obj/item/ammo_casing/n762
@@ -47,4 +53,19 @@
name = ".38 Iceblox bullet casing"
desc = "A .38 Iceblox bullet casing."
caliber = "38"
projectile_type = /obj/item/projectile/bullet/c38/iceblox
projectile_type = /obj/item/projectile/bullet/c38/iceblox
/obj/item/ammo_casing/c38/match
name = ".38 Match bullet casing"
desc = "A .38 bullet casing, manufactured to exceedingly high standards."
projectile_type = /obj/projectile/bullet/c38/match
/obj/item/ammo_casing/c38/match/bouncy
name = ".38 Rubber bullet casing"
desc = "A .38 rubber bullet casing, manufactured to exceedingly high standards."
projectile_type = /obj/projectile/bullet/c38/match/bouncy
/obj/item/ammo_casing/c38/dumdum
name = ".38 DumDum bullet casing"
desc = "A .38 DumDum bullet casing."
projectile_type = /obj/projectile/bullet/c38/dumdum

View File

@@ -7,6 +7,11 @@
max_ammo = 7
multiple_sprites = 1
/obj/item/ammo_box/a357/match
name = "speed loader (.357 Match)"
desc = "Designed to quickly reload revolvers. These rounds are manufactured within extremely tight tolerances, making them easy to show off trickshots with."
ammo_type = /obj/item/ammo_casing/a357/match
/obj/item/ammo_box/a357/ap
name = "speed loader (.357 AP)"
ammo_type = /obj/item/ammo_casing/a357/ap

View File

@@ -20,3 +20,7 @@
/obj/item/ammo_box/magazine/mm195x129/update_icon()
..()
icon_state = "a762-[round(ammo_count(),10)]"
/obj/item/ammo_box/magazine/mm712x82/match
name = "box magazine (Match 7.12x82mm)"
ammo_type = /obj/item/ammo_casing/mm712x82/match

View File

@@ -56,9 +56,25 @@
var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle
var/spread = 0 //amount (in degrees) of projectile spread
animate_movement = 0 //Use SLIDE_STEPS in conjunction with legacy
/// how many times we've ricochet'd so far (instance variable, not a stat)
var/ricochets = 0
var/ricochets_max = 2
var/ricochet_chance = 30
/// how many times we can ricochet max
var/ricochets_max = 0
/// 0-100, the base chance of ricocheting, before being modified by the atom we shoot and our chance decay
var/ricochet_chance = 0
/// 0-1 (or more, I guess) multiplier, the ricochet_chance is modified by multiplying this after each ricochet
var/ricochet_decay_chance = 0.7
/// 0-1 (or more, I guess) multiplier, the projectile's damage is modified by multiplying this after each ricochet
var/ricochet_decay_damage = 0.7
/// On ricochet, if nonzero, we consider all mobs within this range of our projectile at the time of ricochet to home in on like Revolver Ocelot, as governed by ricochet_auto_aim_angle
var/ricochet_auto_aim_range = 0
/// On ricochet, if ricochet_auto_aim_range is nonzero, we'll consider any mobs within this range of the normal angle of incidence to home in on, higher = more auto aim
var/ricochet_auto_aim_angle = 30
/// the angle of impact must be within this many degrees of the struck surface, set to 0 to allow any angle
var/ricochet_incidence_leeway = 40
///If the object being hit can pass ths damage on to something else, it should not do it for this bullet
var/force_hit = FALSE
//Hitscan
var/hitscan = FALSE //Whether this is hitscan. If it is, speed is basically ignored.
@@ -131,6 +147,11 @@
var/temporary_unstoppable_movement = FALSE
///If defined, on hit we create an item of this type then call hitby() on the hit target with this
var/shrapnel_type
///If TRUE, hit mobs even if they're on the floor and not our target
var/hit_stunned_targets = FALSE
/obj/item/projectile/Initialize()
. = ..()
permutated = list()
@@ -146,6 +167,7 @@
on_range()
/obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range
SEND_SIGNAL(src, COMSIG_PROJECTILE_RANGE_OUT)
qdel(src)
//to get the correct limb (if any) for the projectile hit message
@@ -165,6 +187,10 @@
/obj/item/projectile/proc/on_hit(atom/target, blocked = FALSE)
if(fired_from)
SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_ON_HIT, firer, target, Angle)
// i know that this is probably more with wands and gun mods in mind, but it's a bit silly that the projectile on_hit signal doesn't ping the projectile itself.
// maybe we care what the projectile thinks! See about combining these via args some time when it's not 5AM
SEND_SIGNAL(src, COMSIG_PROJECTILE_SELF_ON_HIT, firer, target, Angle)
var/turf/target_loca = get_turf(target)
var/hitx
@@ -219,7 +245,10 @@
var/limb_hit = L.check_limb_hit(def_zone)//to get the correct message info.
if(limb_hit)
organ_hit_text = " in \the [parse_zone(limb_hit)]"
if(suppressed)
if(suppressed==SUPPRESSED_VERY)
playsound(loc, hitsound, 5, TRUE, -1)
else if(suppressed)
playsound(loc, hitsound, 5, 1, -1)
to_chat(L, "<span class='userdanger'>You're shot by \a [src][organ_hit_text]!</span>")
else
@@ -250,7 +279,23 @@
return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume
/obj/item/projectile/proc/on_ricochet(atom/A)
return
if(!ricochet_auto_aim_angle || !ricochet_auto_aim_range)
return
var/mob/living/unlucky_sob
var/best_angle = ricochet_auto_aim_angle
if(firer && HAS_TRAIT(firer, TRAIT_NICE_SHOT))
best_angle += NICE_SHOT_RICOCHET_BONUS
for(var/mob/living/L in range(ricochet_auto_aim_range, src.loc))
if(L.stat == DEAD || !isInSight(src, L))
continue
var/our_angle = abs(closer_angle_difference(Angle, Get_Angle(src.loc, L.loc)))
if(our_angle < best_angle)
best_angle = our_angle
unlucky_sob = L
if(unlucky_sob)
setAngle(Get_Angle(src, unlucky_sob.loc))
/obj/item/projectile/proc/store_hitscan_collision(datum/point/pcache)
beam_segments[beam_index] = pcache
@@ -259,13 +304,15 @@
/obj/item/projectile/Bump(atom/A)
var/turf/T = get_turf(A)
if(trajectory && check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max)
if(trajectory && ricochets < ricochets_max && check_ricochet_flag(A) && check_ricochet(A))
var/datum/point/pcache = trajectory.copy_to()
ricochets++
if(A.handle_ricochet(src))
on_ricochet(A)
ignore_source_check = TRUE
decayedRange = max(0, decayedRange - reflect_range_decrease)
ricochet_chance *= ricochet_decay_chance
damage *= ricochet_decay_damage
range = decayedRange
if(hitscan)
store_hitscan_collision(pcache)
@@ -344,13 +391,18 @@
return T
//Returns null if nothing at all was found.
/obj/item/projectile/proc/check_ricochet()
if(prob(ricochet_chance))
/obj/projectile/proc/check_ricochet(atom/A)
var/chance = ricochet_chance * A.ricochet_chance_mod
if(firer && HAS_TRAIT(firer, TRAIT_NICE_SHOT))
chance += NICE_SHOT_RICOCHET_BONUS
if(prob(chance))
return TRUE
return FALSE
/obj/item/projectile/proc/check_ricochet_flag(atom/A)
if(A.flags_1 & CHECK_RICOCHET_1)
if((flag in list("energy", "laser")) && (A.flags_ricochet & RICOCHET_SHINY))
return TRUE
if((flag in list("bomb", "bullet")) && (A.flags_ricochet & RICOCHET_HARD))
return TRUE
return FALSE
@@ -391,6 +443,8 @@
/obj/item/projectile/proc/fire(angle, atom/direct_target)
if(fired_from)
SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_BEFORE_FIRE, src, original) //If no angle needs to resolve it from xo/yo!
if(shrapnel_type)
AddElement(/datum/element/embed, projectile_payload = shrapnel_type)
if(!log_override && firer && original)
log_combat(firer, original, "fired at", src, "from [get_area_name(src, TRUE)]")
if(direct_target)

View File

@@ -15,6 +15,38 @@
/obj/item/projectile/bullet/c38
name = ".38 bullet"
damage = 25
ricochets_max = 2
ricochet_chance = 50
ricochet_auto_aim_angle = 10
ricochet_auto_aim_range = 3
/obj/projectile/bullet/c38/match
name = ".38 Match bullet"
ricochets_max = 4
ricochet_chance = 100
ricochet_auto_aim_angle = 40
ricochet_auto_aim_range = 5
ricochet_incidence_leeway = 50
ricochet_decay_chance = 1
ricochet_decay_damage = 1
/obj/projectile/bullet/c38/match/bouncy
name = ".38 Rubber bullet"
damage = 10
stamina = 30
armour_penetration = -30
ricochets_max = 6
ricochet_incidence_leeway = 70
ricochet_chance = 130
ricochet_decay_damage = 0.8
shrapnel_type = NONE
/obj/projectile/bullet/c38/dumdum
name = ".38 DumDum bullet"
damage = 15
armour_penetration = -30
ricochets_max = 0
shrapnel_type = /obj/item/shrapnel/bullet/c38/dumdum
/obj/item/projectile/bullet/c38/rubber
name = ".38 rubber bullet"
@@ -24,6 +56,7 @@
/obj/item/projectile/bullet/c38/trac
name = ".38 TRAC bullet"
damage = 10
ricochets_max = 0
/obj/item/projectile/bullet/c38/trac/on_hit(atom/target, blocked = FALSE)
. = ..()
@@ -39,6 +72,7 @@
/obj/item/projectile/bullet/c38/hotshot //similar to incendiary bullets, but do not leave a flaming trail
name = ".38 Hot Shot bullet"
damage = 20
ricochets_max = 0
/obj/item/projectile/bullet/c38/hotshot/on_hit(atom/target, blocked = FALSE)
. = ..()
@@ -51,6 +85,7 @@
name = ".38 Iceblox bullet"
damage = 20
var/temperature = 100
ricochets_max = 0
/obj/item/projectile/bullet/c38/iceblox/on_hit(atom/target, blocked = FALSE)
. = ..()
@@ -68,4 +103,14 @@
/obj/item/projectile/bullet/a357/ap
name = ".357 armor-piercing bullet"
damage = 45
armour_penetration = 45
armour_penetration = 45
// admin only really, for ocelot memes
/obj/projectile/bullet/a357/match
name = ".357 match bullet"
ricochets_max = 5
ricochet_chance = 140
ricochet_auto_aim_angle = 50
ricochet_auto_aim_range = 6
ricochet_incidence_leeway = 80
ricochet_decay_chance = 1

View File

@@ -76,20 +76,6 @@
break
M.dropItemToGround(item_to_retrieve)
if(iscarbon(M)) //Edge case housekeeping
var/mob/living/carbon/C = M
if(C.stomach_contents && (item_to_retrieve in C.stomach_contents))
C.stomach_contents -= item_to_retrieve
for(var/X in C.bodyparts)
var/obj/item/bodypart/part = X
if(item_to_retrieve in part.embedded_objects)
part.embedded_objects -= item_to_retrieve
to_chat(C, "<span class='warning'>The [item_to_retrieve] that was embedded in your [L] has mysteriously vanished. How fortunate!</span>")
if(!C.has_embedded_objects())
C.clear_alert("embeddedobject")
SEND_SIGNAL(C, COMSIG_CLEAR_MOOD_EVENT, "embedded")
break
else
if(istype(item_to_retrieve.loc, /obj/machinery/portable_atmospherics/)) //Edge cases for moved machinery
var/obj/machinery/portable_atmospherics/P = item_to_retrieve.loc

View File

@@ -152,28 +152,24 @@
disabled += zone
return disabled
//Remove all embedded objects from all limbs on the carbon mob
/mob/living/carbon/proc/remove_all_embedded_objects()
var/turf/T = get_turf(src)
///Remove a specific embedded item from the carbon mob
/mob/living/carbon/proc/remove_embedded_object(obj/item/I)
SEND_SIGNAL(src, COMSIG_CARBON_EMBED_REMOVAL, I)
///Remove all embedded objects from all limbs on the carbon mob
/mob/living/carbon/proc/remove_all_embedded_objects()
for(var/X in bodyparts)
var/obj/item/bodypart/L = X
for(var/obj/item/I in L.embedded_objects)
L.embedded_objects -= I
I.forceMove(T)
I.unembedded()
clear_alert("embeddedobject")
SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "embedded")
remove_embedded_object(I)
/mob/living/carbon/proc/has_embedded_objects(include_harmless=FALSE)
. = 0
for(var/X in bodyparts)
var/obj/item/bodypart/L = X
for(var/obj/item/I in L.embedded_objects)
if(!include_harmless && I.is_embed_harmless())
if(!include_harmless && I.isEmbedHarmless())
continue
return 1
return TRUE
//Helper for quickly creating a new limb - used by augment code in species.dm spec_attacked_by

View File

@@ -23,7 +23,7 @@
var/objects = 0
for(var/obj/item/I in L.embedded_objects)
objects++
SEND_SIGNAL(H, COMSIG_HUMAN_EMBED_REMOVAL, I, L)
H.remove_embedded_object(I)
if(objects > 0)
display_results(user, target, "<span class='notice'>You successfully remove [objects] objects from [H]'s [L.name].</span>",

View File

@@ -189,6 +189,13 @@
cost = 4
include_modes = list(/datum/game_mode/nuclear)
/datum/uplink_item/ammo/machinegun/match
name = "7.12x82mm (Match) Box Magazine"
desc = "A 50-round magazine of 7.12x82mm ammunition for use in the L6 SAW; you didn't know there was a demand for match grade \
precision bullet hose ammo, but these rounds are finely tuned and perfect for ricocheting off walls all fancy-like."
item = /obj/item/ammo_box/magazine/mm712x82/match
cost = 10
/datum/uplink_item/ammo/machinegun
cost = 6
surplus = 0

View File

@@ -22,6 +22,7 @@
/obj/item/clothing/suit/armor/vest/blueshirt = 1,
/obj/item/clothing/under/rank/security/officer/blueshirt = 1,
/obj/item/clothing/gloves/tackler = 5,
/obj/item/grenade/stingbang = 1,
/obj/item/ssword_kit = 1,
/obj/item/storage/bag/ammo = 2)
armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)