part 2
This commit is contained in:
@@ -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
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
. = ..()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"])
|
||||
|
||||
@@ -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>")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user