mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-10 01:49:19 +00:00
part 2
This commit is contained in:
@@ -179,6 +179,7 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
|
|||||||
#define EMBEDDED_JOSTLE_CHANCE 5 //Chance for embedded objects to cause pain every time they move (jostle)
|
#define EMBEDDED_JOSTLE_CHANCE 5 //Chance for embedded objects to cause pain every time they move (jostle)
|
||||||
#define EMBEDDED_JOSTLE_PAIN_MULTIPLIER 1 //Coefficient of multiplication for the damage the item does while
|
#define EMBEDDED_JOSTLE_PAIN_MULTIPLIER 1 //Coefficient of multiplication for the damage the item does while
|
||||||
#define EMBEDDED_PAIN_STAM_PCT 0.0 //This percentage of all pain will be dealt as stam damage rather than brute (0-1)
|
#define EMBEDDED_PAIN_STAM_PCT 0.0 //This percentage of all pain will be dealt as stam damage rather than brute (0-1)
|
||||||
|
#define EMBED_CHANCE_TURF_MOD -15 //You are this many percentage points less likely to embed into a turf (good for things glass shards and spears vs walls)
|
||||||
|
|
||||||
#define EMBED_HARMLESS list("pain_mult" = 0, "jostle_pain_mult" = 0, "ignore_throwspeed_threshold" = TRUE)
|
#define EMBED_HARMLESS list("pain_mult" = 0, "jostle_pain_mult" = 0, "ignore_throwspeed_threshold" = TRUE)
|
||||||
#define EMBED_HARMLESS_SUPERIOR list("pain_mult" = 0, "jostle_pain_mult" = 0, "ignore_throwspeed_threshold" = TRUE, "embed_chance" = 100, "fall_chance" = 0.1)
|
#define EMBED_HARMLESS_SUPERIOR list("pain_mult" = 0, "jostle_pain_mult" = 0, "ignore_throwspeed_threshold" = TRUE, "embed_chance" = 100, "fall_chance" = 0.1)
|
||||||
@@ -198,6 +199,11 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
|
|||||||
#define EGUN_SELFCHARGE 1
|
#define EGUN_SELFCHARGE 1
|
||||||
#define EGUN_SELFCHARGE_BORG 2
|
#define EGUN_SELFCHARGE_BORG 2
|
||||||
|
|
||||||
|
//suppression defines
|
||||||
|
#define SUPPRESSED_NONE 0
|
||||||
|
#define SUPPRESSED_QUIET 1 ///standard suppressed
|
||||||
|
#define SUPPRESSED_VERY 2 /// no message
|
||||||
|
|
||||||
///Time to spend without clicking on other things required for your shots to become accurate.
|
///Time to spend without clicking on other things required for your shots to become accurate.
|
||||||
#define GUN_AIMING_TIME (2 SECONDS)
|
#define GUN_AIMING_TIME (2 SECONDS)
|
||||||
|
|
||||||
@@ -290,6 +296,8 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
|
|||||||
#define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default.
|
#define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default.
|
||||||
#define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs.
|
#define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs.
|
||||||
|
|
||||||
|
#define NICE_SHOT_RICOCHET_BONUS 10 //if the shooter has the NICE_SHOT trait and they fire a ricocheting projectile, add this to the ricochet chance and auto aim angle
|
||||||
|
|
||||||
/// Check whether or not we can block, without "triggering" a block. Basically run checks without effects like depleting shields.
|
/// Check whether or not we can block, without "triggering" a block. Basically run checks without effects like depleting shields.
|
||||||
/// Wrapper for do_run_block(). The arguments on that means the same as for this.
|
/// Wrapper for do_run_block(). The arguments on that means the same as for this.
|
||||||
#define mob_check_block(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list)\
|
#define mob_check_block(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list)\
|
||||||
|
|||||||
@@ -236,6 +236,8 @@
|
|||||||
#define COMSIG_CARBON_SOUNDBANG "carbon_soundbang" //from base of mob/living/carbon/soundbang_act(): (list(intensity))
|
#define COMSIG_CARBON_SOUNDBANG "carbon_soundbang" //from base of mob/living/carbon/soundbang_act(): (list(intensity))
|
||||||
#define COMSIG_CARBON_IDENTITY_TRANSFERRED_TO "carbon_id_transferred_to" //from datum/dna/transfer_identity(): (datum/dna, transfer_SE)
|
#define COMSIG_CARBON_IDENTITY_TRANSFERRED_TO "carbon_id_transferred_to" //from datum/dna/transfer_identity(): (datum/dna, transfer_SE)
|
||||||
#define COMSIG_CARBON_TACKLED "carbon_tackled" //sends from tackle.dm on tackle completion
|
#define COMSIG_CARBON_TACKLED "carbon_tackled" //sends from tackle.dm on tackle completion
|
||||||
|
#define COMSIG_CARBON_EMBED_RIP "item_embed_start_rip" // defined twice, in carbon and human's topics, fired when interacting with a valid embedded_object to pull it out (mob/living/carbon/target, /obj/item, /obj/item/bodypart/L)
|
||||||
|
#define COMSIG_CARBON_EMBED_REMOVAL "item_embed_remove_safe" // called when removing a given item from a mob, from mob/living/carbon/remove_embedded_object(mob/living/carbon/target, /obj/item)
|
||||||
|
|
||||||
// /mob/living/silicon signals
|
// /mob/living/silicon signals
|
||||||
#define COMSIG_ROBOT_UPDATE_ICONS "robot_update_icons" //from base of robot/update_icons(): ()
|
#define COMSIG_ROBOT_UPDATE_ICONS "robot_update_icons" //from base of robot/update_icons(): ()
|
||||||
@@ -282,9 +284,15 @@
|
|||||||
#define COMSIG_ITEM_MOUSE_EXIT "item_mouse_exit" //from base of obj/item/MouseExited(): (location, control, params)
|
#define COMSIG_ITEM_MOUSE_EXIT "item_mouse_exit" //from base of obj/item/MouseExited(): (location, control, params)
|
||||||
#define COMSIG_ITEM_MOUSE_ENTER "item_mouse_enter" //from base of obj/item/MouseEntered(): (location, control, params)
|
#define COMSIG_ITEM_MOUSE_ENTER "item_mouse_enter" //from base of obj/item/MouseEntered(): (location, control, params)
|
||||||
#define COMSIG_ITEM_DECONSTRUCTOR_DEEPSCAN "deconstructor_deepscan" //Called by deconstructive analyzers deepscanning an item: (obj/machinery/rnd/destructive_analyzer/analyzer_machine, mob/user, list/information_list)
|
#define COMSIG_ITEM_DECONSTRUCTOR_DEEPSCAN "deconstructor_deepscan" //Called by deconstructive analyzers deepscanning an item: (obj/machinery/rnd/destructive_analyzer/analyzer_machine, mob/user, list/information_list)
|
||||||
|
#define COMSIG_ITEM_DISABLE_EMBED "item_disable_embed" ///from [/obj/item/proc/disableEmbedding]:
|
||||||
|
#define COMSIG_MINE_TRIGGERED "minegoboom" ///from [/obj/effect/mine/proc/triggermine]:
|
||||||
// Uncovered information
|
// Uncovered information
|
||||||
#define COMPONENT_DEEPSCAN_UNCOVERED_INFORMATION 1
|
#define COMPONENT_DEEPSCAN_UNCOVERED_INFORMATION 1
|
||||||
|
|
||||||
|
// /obj/item/grenade signals
|
||||||
|
#define COMSIG_GRENADE_PRIME "grenade_prime" //called in /obj/item/gun/process_fire (user, target, params, zone_override)
|
||||||
|
#define COMSIG_GRENADE_ARMED "grenade_armed" //called in /obj/item/gun/process_fire (user, target, params, zone_override)
|
||||||
|
|
||||||
// /obj/item/clothing signals
|
// /obj/item/clothing signals
|
||||||
#define COMSIG_SHOES_STEP_ACTION "shoes_step_action" //from base of obj/item/clothing/shoes/proc/step_action(): ()
|
#define COMSIG_SHOES_STEP_ACTION "shoes_step_action" //from base of obj/item/clothing/shoes/proc/step_action(): ()
|
||||||
#define COMSIG_SUIT_MADE_HELMET "suit_made_helmet" //from base of obj/item/clothing/suit/MakeHelmet(): (helmet)
|
#define COMSIG_SUIT_MADE_HELMET "suit_made_helmet" //from base of obj/item/clothing/suit/MakeHelmet(): (helmet)
|
||||||
@@ -312,15 +320,18 @@
|
|||||||
#define COMSIG_PEN_ROTATED "pen_rotated" //called after rotation in /obj/item/pen/attack_self(): (rotation, mob/living/carbon/user)
|
#define COMSIG_PEN_ROTATED "pen_rotated" //called after rotation in /obj/item/pen/attack_self(): (rotation, mob/living/carbon/user)
|
||||||
|
|
||||||
// /obj/item/projectile signals (sent to the firer)
|
// /obj/item/projectile signals (sent to the firer)
|
||||||
|
#define COMSIG_PROJECTILE_SELF_ON_HIT "projectile_self_on_hit" // from base of /obj/projectile/proc/on_hit(): (atom/movable/firer, atom/target, Angle)
|
||||||
#define COMSIG_PROJECTILE_ON_HIT "projectile_on_hit" // from base of /obj/item/projectile/proc/on_hit(): (atom/movable/firer, atom/target, Angle)
|
#define COMSIG_PROJECTILE_ON_HIT "projectile_on_hit" // from base of /obj/item/projectile/proc/on_hit(): (atom/movable/firer, atom/target, Angle)
|
||||||
#define COMSIG_PROJECTILE_BEFORE_FIRE "projectile_before_fire" // from base of /obj/item/projectile/proc/fire(): (obj/item/projectile, atom/original_target)
|
#define COMSIG_PROJECTILE_BEFORE_FIRE "projectile_before_fire" // from base of /obj/item/projectile/proc/fire(): (obj/item/projectile, atom/original_target)
|
||||||
|
#define COMSIG_PROJECTILE_RANGE_OUT "projectile_range_out" // sent to targets during the process_hit proc of projectiles
|
||||||
|
#define COMSIG_EMBED_TRY_FORCE "item_try_embed" // sent when trying to force an embed (mainly for projectiles, only used in the embed element)
|
||||||
|
|
||||||
|
#define COMSIG_PELLET_CLOUD_INIT "pellet_cloud_init" // sent to targets during the process_hit proc of projectiles
|
||||||
|
|
||||||
// /mob/living/carbon/human signals
|
// /mob/living/carbon/human signals
|
||||||
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target)
|
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target)
|
||||||
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby" //from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker)
|
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby" //from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker)
|
||||||
#define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit" //Hit by successful disarm attack (mob/living/carbon/human/attacker,zone_targeted)
|
#define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit" //Hit by successful disarm attack (mob/living/carbon/human/attacker,zone_targeted)
|
||||||
#define COMSIG_HUMAN_EMBED_RIP "item_embed_removing" // called on human when said human tries to rip out this embedded item (mob/living/carbon/human/target, /obj/item, /obj/item/bodypart/L)
|
|
||||||
#define COMSIG_HUMAN_EMBED_REMOVAL "item_embed_remove_surgery" // called on human from /datum/surgery_step/remove_object/success (mob/living/carbon/human/target, /obj/item, /obj/item/bodypart/L)
|
|
||||||
#define COMSIG_HUMAN_PREFS_COPIED_TO "human_prefs_copied_to" //from datum/preferences/copy_to(): (datum/preferences, icon_updates, roundstart_checks)
|
#define COMSIG_HUMAN_PREFS_COPIED_TO "human_prefs_copied_to" //from datum/preferences/copy_to(): (datum/preferences, icon_updates, roundstart_checks)
|
||||||
#define COMSIG_HUMAN_HARDSET_DNA "human_hardset_dna" //from mob/living/carbon/human/hardset_dna(): (ui, list/mutation_index, newreal_name, newblood_type, datum/species, newfeatures)
|
#define COMSIG_HUMAN_HARDSET_DNA "human_hardset_dna" //from mob/living/carbon/human/hardset_dna(): (ui, list/mutation_index, newreal_name, newblood_type, datum/species, newfeatures)
|
||||||
#define COMSIG_HUMAN_ON_RANDOMIZE "humman_on_randomize" //from base of proc/randomize_human()
|
#define COMSIG_HUMAN_ON_RANDOMIZE "humman_on_randomize" //from base of proc/randomize_human()
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
|
|||||||
//FLAGS BITMASK
|
//FLAGS BITMASK
|
||||||
///This flag is what recursive_hear_check() uses to determine wether to add an item to the hearer list or not.
|
///This flag is what recursive_hear_check() uses to determine wether to add an item to the hearer list or not.
|
||||||
#define HEAR_1 (1<<3)
|
#define HEAR_1 (1<<3)
|
||||||
///Projectiels will check ricochet on things impacted that have this.
|
|
||||||
#define CHECK_RICOCHET_1 (1<<4)
|
|
||||||
///Conducts electricity (metal etc.).
|
///Conducts electricity (metal etc.).
|
||||||
#define CONDUCT_1 (1<<5)
|
#define CONDUCT_1 (1<<5)
|
||||||
///For machines and structures that should not break into parts, eg, holodeck stuff.
|
///For machines and structures that should not break into parts, eg, holodeck stuff.
|
||||||
@@ -46,6 +44,11 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
|
|||||||
/// Early returns mob.face_atom()
|
/// Early returns mob.face_atom()
|
||||||
#define BLOCK_FACE_ATOM_1 (1<<17)
|
#define BLOCK_FACE_ATOM_1 (1<<17)
|
||||||
|
|
||||||
|
/// If the thing can reflect light (lasers/energy)
|
||||||
|
#define RICOCHET_SHINY (1<<0)
|
||||||
|
/// If the thing can reflect matter (bullets/bomb shrapnel)
|
||||||
|
#define RICOCHET_HARD (1<<1)
|
||||||
|
|
||||||
//turf-only flags
|
//turf-only flags
|
||||||
#define NOJAUNT_1 (1<<0)
|
#define NOJAUNT_1 (1<<0)
|
||||||
#define UNUSED_RESERVATION_TURF_1 (1<<1)
|
#define UNUSED_RESERVATION_TURF_1 (1<<1)
|
||||||
|
|||||||
@@ -177,6 +177,12 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
|
|||||||
|
|
||||||
#define isitem(A) (istype(A, /obj/item))
|
#define isitem(A) (istype(A, /obj/item))
|
||||||
|
|
||||||
|
#define isgrenade(A) (istype(A, /obj/item/grenade))
|
||||||
|
|
||||||
|
#define islandmine(A) (istype(A, /obj/effect/mine))
|
||||||
|
|
||||||
|
#define isammocasing(A) (istype(A, /obj/i
|
||||||
|
|
||||||
#define isidcard(I) (istype(I, /obj/item/card/id))
|
#define isidcard(I) (istype(I, /obj/item/card/id))
|
||||||
|
|
||||||
#define isstructure(A) (istype(A, /obj/structure))
|
#define isstructure(A) (istype(A, /obj/structure))
|
||||||
|
|||||||
@@ -157,6 +157,7 @@
|
|||||||
#define TRAIT_PASSTABLE "passtable"
|
#define TRAIT_PASSTABLE "passtable"
|
||||||
#define TRAIT_GIANT "giant"
|
#define TRAIT_GIANT "giant"
|
||||||
#define TRAIT_DWARF "dwarf"
|
#define TRAIT_DWARF "dwarf"
|
||||||
|
#define TRAIT_NICE_SHOT "nice_shot" //hnnnnnnnggggg..... you're pretty good....
|
||||||
#define TRAIT_ALCOHOL_TOLERANCE "alcohol_tolerance"
|
#define TRAIT_ALCOHOL_TOLERANCE "alcohol_tolerance"
|
||||||
#define TRAIT_AGEUSIA "ageusia"
|
#define TRAIT_AGEUSIA "ageusia"
|
||||||
#define TRAIT_HEAVY_SLEEPER "heavy_sleeper"
|
#define TRAIT_HEAVY_SLEEPER "heavy_sleeper"
|
||||||
|
|||||||
@@ -675,12 +675,6 @@ Turf and target are separate in case you want to teleport some distance from a t
|
|||||||
loc = loc.loc
|
loc = loc.loc
|
||||||
return null
|
return null
|
||||||
|
|
||||||
//For objects that should embed, but make no sense being is_sharp or is_pointed()
|
|
||||||
//e.g: rods
|
|
||||||
GLOBAL_LIST_INIT(can_embed_types, typecacheof(list(
|
|
||||||
/obj/item/stack/rods,
|
|
||||||
/obj/item/pipe)))
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Checks if that loc and dir has an item on the wall
|
Checks if that loc and dir has an item on the wall
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -123,7 +123,6 @@ GLOBAL_LIST_INIT(bitfields, list(
|
|||||||
"UNUSED_RESERVATION_TURF_1" = UNUSED_RESERVATION_TURF_1,
|
"UNUSED_RESERVATION_TURF_1" = UNUSED_RESERVATION_TURF_1,
|
||||||
"CAN_BE_DIRTY_1" = CAN_BE_DIRTY_1,
|
"CAN_BE_DIRTY_1" = CAN_BE_DIRTY_1,
|
||||||
"HEAR_1" = HEAR_1,
|
"HEAR_1" = HEAR_1,
|
||||||
"CHECK_RICOCHET_1" = CHECK_RICOCHET_1,
|
|
||||||
"CONDUCT_1" = CONDUCT_1,
|
"CONDUCT_1" = CONDUCT_1,
|
||||||
"NO_LAVA_GEN_1" = NO_LAVA_GEN_1,
|
"NO_LAVA_GEN_1" = NO_LAVA_GEN_1,
|
||||||
"NODECONSTRUCT_1" = NODECONSTRUCT_1,
|
"NODECONSTRUCT_1" = NODECONSTRUCT_1,
|
||||||
@@ -138,6 +137,10 @@ GLOBAL_LIST_INIT(bitfields, list(
|
|||||||
"BLOCK_FACE_ATOM_1" = BLOCK_FACE_ATOM_1,
|
"BLOCK_FACE_ATOM_1" = BLOCK_FACE_ATOM_1,
|
||||||
"PREVENT_CONTENTS_EXPLOSION_1" = PREVENT_CONTENTS_EXPLOSION_1
|
"PREVENT_CONTENTS_EXPLOSION_1" = PREVENT_CONTENTS_EXPLOSION_1
|
||||||
),
|
),
|
||||||
|
"flags_ricochet" = list(
|
||||||
|
"RICOCHET_SHINY" = RICOCHET_SHINY,
|
||||||
|
"RICOCHET_HARD" = RICOCHET_HARD
|
||||||
|
),
|
||||||
"clothing_flags" = list(
|
"clothing_flags" = list(
|
||||||
"LAVAPROTECT" = LAVAPROTECT,
|
"LAVAPROTECT" = LAVAPROTECT,
|
||||||
"STOPSPRESSUREDAMAGE" = STOPSPRESSUREDAMAGE,
|
"STOPSPRESSUREDAMAGE" = STOPSPRESSUREDAMAGE,
|
||||||
|
|||||||
@@ -93,6 +93,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
|
|||||||
"TRAIT_PASSTABLE" = TRAIT_PASSTABLE,
|
"TRAIT_PASSTABLE" = TRAIT_PASSTABLE,
|
||||||
"TRAIT_GIANT" = TRAIT_GIANT,
|
"TRAIT_GIANT" = TRAIT_GIANT,
|
||||||
"TRAIT_DWARF" = TRAIT_DWARF,
|
"TRAIT_DWARF" = TRAIT_DWARF,
|
||||||
|
"TRAIT_SILENT_FOOTSTEPS" = TRAIT_SILENT_FOOTSTEPS,
|
||||||
|
"TRAIT_NICE_SHOT" = TRAIT_NICE_SHOT,
|
||||||
"TRAIT_COMBAT_MODE_LOCKED" = TRAIT_COMBAT_MODE_LOCKED,
|
"TRAIT_COMBAT_MODE_LOCKED" = TRAIT_COMBAT_MODE_LOCKED,
|
||||||
"TRAIT_SPRINT_LOCKED" = TRAIT_SPRINT_LOCKED,
|
"TRAIT_SPRINT_LOCKED" = TRAIT_SPRINT_LOCKED,
|
||||||
"TRAIT_AUTO_CATCH_ITEM" = TRAIT_AUTO_CATCH_ITEM,
|
"TRAIT_AUTO_CATCH_ITEM" = TRAIT_AUTO_CATCH_ITEM,
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ If you're feeling frisky, examine yourself and click the underlined item to pull
|
|||||||
|
|
||||||
/obj/screen/alert/embeddedobject/Click()
|
/obj/screen/alert/embeddedobject/Click()
|
||||||
if(isliving(usr))
|
if(isliving(usr))
|
||||||
var/mob/living/carbon/human/M = usr
|
var/mob/living/carbon/M = usr
|
||||||
return M.help_shake_act(M)
|
return M.help_shake_act(M)
|
||||||
|
|
||||||
/obj/screen/alert/weightless
|
/obj/screen/alert/weightless
|
||||||
|
|||||||
@@ -38,8 +38,9 @@
|
|||||||
parent = raw_args[1]
|
parent = raw_args[1]
|
||||||
var/list/arguments = raw_args.Copy(2)
|
var/list/arguments = raw_args.Copy(2)
|
||||||
if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE)
|
if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE)
|
||||||
|
stack_trace("Incompatible [type] assigned to a [parent.type]! args: [json_encode(arguments)]")
|
||||||
qdel(src, TRUE, TRUE)
|
qdel(src, TRUE, TRUE)
|
||||||
CRASH("Incompatible [type] assigned to a [parent.type]! args: [json_encode(arguments)]")
|
return
|
||||||
|
|
||||||
_JoinParent(parent)
|
_JoinParent(parent)
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,17 @@
|
|||||||
and when it impacts and meets the requirements to stick into something, it instantiates an embedded component. Once the item falls out, the component is destroyed, while the
|
and when it impacts and meets the requirements to stick into something, it instantiates an embedded component. Once the item falls out, the component is destroyed, while the
|
||||||
element survives to embed another day.
|
element survives to embed another day.
|
||||||
|
|
||||||
There are 2 different things that can be embedded presently: humans, and closed turfs (see: walls)
|
There are 2 different things that can be embedded presently: carbons, and closed turfs (see: walls)
|
||||||
|
|
||||||
- Human embedding has all the classical embedding behavior, and tracks more events and signals. The main behaviors and hooks to look for are:
|
- Carbon embedding has all the classical embedding behavior, and tracks more events and signals. The main behaviors and hooks to look for are:
|
||||||
-- Every process tick, there is a chance to randomly proc pain, controlled by pain_chance. There may also be a chance for the object to fall out randomly, per fall_chance
|
-- Every process tick, there is a chance to randomly proc pain, controlled by pain_chance. There may also be a chance for the object to fall out randomly, per fall_chance
|
||||||
-- Every time the mob moves, there is a chance to proc jostling pain, controlled by jostle_chance (and only 50% as likely if the mob is walking or crawling)
|
-- Every time the mob moves, there is a chance to proc jostling pain, controlled by jostle_chance (and only 50% as likely if the mob is walking or crawling)
|
||||||
-- Various signals hooking into human topic() and the embed removal surgery in order to handle removals
|
-- Various signals hooking into carbon topic() and the embed removal surgery in order to handle removals.
|
||||||
|
|
||||||
- Turf embedding is much simpler. All we do here is draw an overlay of the item's inhand on the turf, hide the item, and create an HTML link in the turf's inspect
|
- Turf embedding is much simpler. All we do here is draw an overlay of the item's inhand on the turf, hide the item, and create an HTML link in the turf's inspect
|
||||||
that allows you to rip the item out. There's nothing dynamic about this, so far less checks.
|
that allows you to rip the item out. There's nothing dynamic about this, so far less checks.
|
||||||
|
|
||||||
|
|
||||||
In addition, there are 2 cases of embedding: embedding, and sticking
|
In addition, there are 2 cases of embedding: embedding, and sticking
|
||||||
|
|
||||||
- Embedding involves harmful and dangerous embeds, whether they cause brute damage, stamina damage, or a mix. This is the default behavior for embeddings, for when something is "pointy"
|
- Embedding involves harmful and dangerous embeds, whether they cause brute damage, stamina damage, or a mix. This is the default behavior for embeddings, for when something is "pointy"
|
||||||
@@ -23,7 +24,7 @@
|
|||||||
Stickables differ from embeds in the following ways:
|
Stickables differ from embeds in the following ways:
|
||||||
-- Text descriptors use phrasing like "X is stuck to Y" rather than "X is embedded in Y"
|
-- Text descriptors use phrasing like "X is stuck to Y" rather than "X is embedded in Y"
|
||||||
-- There is no slicing sound on impact
|
-- There is no slicing sound on impact
|
||||||
-- All damage checks and bloodloss are skipped for humans
|
-- All damage checks and bloodloss are skipped for carbons
|
||||||
-- Pointy objects create sparks when embedding into a turf
|
-- Pointy objects create sparks when embedding into a turf
|
||||||
|
|
||||||
*/
|
*/
|
||||||
@@ -31,7 +32,7 @@
|
|||||||
|
|
||||||
/datum/component/embedded
|
/datum/component/embedded
|
||||||
dupe_mode = COMPONENT_DUPE_ALLOWED
|
dupe_mode = COMPONENT_DUPE_ALLOWED
|
||||||
var/obj/item/bodypart/L
|
var/obj/item/bodypart/limb
|
||||||
var/obj/item/weapon
|
var/obj/item/weapon
|
||||||
|
|
||||||
// all of this stuff is explained in _DEFINES/combat.dm
|
// all of this stuff is explained in _DEFINES/combat.dm
|
||||||
@@ -46,6 +47,7 @@
|
|||||||
var/jostle_chance
|
var/jostle_chance
|
||||||
var/jostle_pain_mult
|
var/jostle_pain_mult
|
||||||
var/pain_stam_pct
|
var/pain_stam_pct
|
||||||
|
var/embed_chance_turf_mod
|
||||||
|
|
||||||
///if both our pain multiplier and jostle pain multiplier are 0, we're harmless and can omit most of the damage related stuff
|
///if both our pain multiplier and jostle pain multiplier are 0, we're harmless and can omit most of the damage related stuff
|
||||||
var/harmful
|
var/harmful
|
||||||
@@ -53,6 +55,7 @@
|
|||||||
|
|
||||||
/datum/component/embedded/Initialize(obj/item/I,
|
/datum/component/embedded/Initialize(obj/item/I,
|
||||||
datum/thrownthing/throwingdatum,
|
datum/thrownthing/throwingdatum,
|
||||||
|
obj/item/bodypart/part,
|
||||||
embed_chance = EMBED_CHANCE,
|
embed_chance = EMBED_CHANCE,
|
||||||
fall_chance = EMBEDDED_ITEM_FALLOUT,
|
fall_chance = EMBEDDED_ITEM_FALLOUT,
|
||||||
pain_chance = EMBEDDED_PAIN_CHANCE,
|
pain_chance = EMBEDDED_PAIN_CHANCE,
|
||||||
@@ -63,11 +66,14 @@
|
|||||||
ignore_throwspeed_threshold = FALSE,
|
ignore_throwspeed_threshold = FALSE,
|
||||||
jostle_chance = EMBEDDED_JOSTLE_CHANCE,
|
jostle_chance = EMBEDDED_JOSTLE_CHANCE,
|
||||||
jostle_pain_mult = EMBEDDED_JOSTLE_PAIN_MULTIPLIER,
|
jostle_pain_mult = EMBEDDED_JOSTLE_PAIN_MULTIPLIER,
|
||||||
pain_stam_pct = EMBEDDED_PAIN_STAM_PCT)
|
pain_stam_pct = EMBEDDED_PAIN_STAM_PCT,
|
||||||
|
embed_chance_turf_mod = EMBED_CHANCE_TURF_MOD)
|
||||||
|
|
||||||
if((!ishuman(parent) && !isclosedturf(parent)) || !isitem(I))
|
if((!iscarbon(parent) && !isclosedturf(parent)) || !isitem(I))
|
||||||
return COMPONENT_INCOMPATIBLE
|
return COMPONENT_INCOMPATIBLE
|
||||||
|
|
||||||
|
if(part)
|
||||||
|
limb = part
|
||||||
src.embed_chance = embed_chance
|
src.embed_chance = embed_chance
|
||||||
src.fall_chance = fall_chance
|
src.fall_chance = fall_chance
|
||||||
src.pain_chance = pain_chance
|
src.pain_chance = pain_chance
|
||||||
@@ -79,35 +85,37 @@
|
|||||||
src.jostle_chance = jostle_chance
|
src.jostle_chance = jostle_chance
|
||||||
src.jostle_pain_mult = jostle_pain_mult
|
src.jostle_pain_mult = jostle_pain_mult
|
||||||
src.pain_stam_pct = pain_stam_pct
|
src.pain_stam_pct = pain_stam_pct
|
||||||
|
src.embed_chance_turf_mod = embed_chance_turf_mod
|
||||||
|
|
||||||
src.weapon = I
|
src.weapon = I
|
||||||
|
|
||||||
if(src.pain_mult || src.jostle_pain_mult)
|
if(!weapon.isEmbedHarmless())
|
||||||
harmful = TRUE
|
harmful = TRUE
|
||||||
if(ishuman(parent))
|
|
||||||
initHuman()
|
if(iscarbon(parent))
|
||||||
|
initCarbon()
|
||||||
else if(isclosedturf(parent))
|
else if(isclosedturf(parent))
|
||||||
initTurf(throwingdatum)
|
initTurf(throwingdatum)
|
||||||
|
|
||||||
/datum/component/embedded/RegisterWithParent()
|
/datum/component/embedded/RegisterWithParent()
|
||||||
if(ishuman(parent))
|
if(iscarbon(parent))
|
||||||
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/jostleCheck)
|
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/jostleCheck)
|
||||||
RegisterSignal(parent, COMSIG_HUMAN_EMBED_RIP, .proc/ripOutHuman)
|
RegisterSignal(parent, COMSIG_CARBON_EMBED_RIP, .proc/ripOutCarbon)
|
||||||
RegisterSignal(parent, COMSIG_HUMAN_EMBED_REMOVAL, .proc/safeRemoveHuman)
|
RegisterSignal(parent, COMSIG_CARBON_EMBED_REMOVAL, .proc/safeRemoveCarbon)
|
||||||
else if(isclosedturf(parent))
|
else if(isclosedturf(parent))
|
||||||
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/examineTurf)
|
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/examineTurf)
|
||||||
|
RegisterSignal(parent, COMSIG_PARENT_QDELETING, .proc/itemMoved)
|
||||||
|
|
||||||
/datum/component/embedded/UnregisterFromParent()
|
/datum/component/embedded/UnregisterFromParent()
|
||||||
if(ishuman(parent))
|
UnregisterSignal(parent, list(COMSIG_MOVABLE_MOVED, COMSIG_CARBON_EMBED_RIP, COMSIG_CARBON_EMBED_REMOVAL, COMSIG_PARENT_EXAMINE))
|
||||||
UnregisterSignal(parent, list(COMSIG_MOVABLE_MOVED, COMSIG_HUMAN_EMBED_RIP, COMSIG_HUMAN_EMBED_REMOVAL))
|
|
||||||
else if(isclosedturf(parent))
|
|
||||||
UnregisterSignal(parent, COMSIG_PARENT_EXAMINE)
|
|
||||||
|
|
||||||
/datum/component/embedded/process()
|
/datum/component/embedded/process()
|
||||||
if(ishuman(parent))
|
if(iscarbon(parent))
|
||||||
processHuman()
|
processCarbon()
|
||||||
|
|
||||||
/datum/component/embedded/Destroy()
|
/datum/component/embedded/Destroy()
|
||||||
|
if(weapon)
|
||||||
|
UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING))
|
||||||
if(overlay)
|
if(overlay)
|
||||||
var/atom/A = parent
|
var/atom/A = parent
|
||||||
A.cut_overlay(overlay, TRUE)
|
A.cut_overlay(overlay, TRUE)
|
||||||
@@ -119,84 +127,107 @@
|
|||||||
/////////////HUMAN PROCS////////////////
|
/////////////HUMAN PROCS////////////////
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
|
|
||||||
/// Set up an instance of embedding for a human. This is basically an extension of Initialize() so not much to say
|
/// Set up an instance of embedding for a carbon. This is basically an extension of Initialize() so not much to say
|
||||||
/datum/component/embedded/proc/initHuman()
|
/datum/component/embedded/proc/initCarbon()
|
||||||
START_PROCESSING(SSdcs, src)
|
START_PROCESSING(SSdcs, src)
|
||||||
var/mob/living/carbon/human/victim = parent
|
var/mob/living/carbon/victim = parent
|
||||||
L = pick(victim.bodyparts)
|
if(!istype(limb))
|
||||||
L.embedded_objects |= weapon // on the inside... on the inside...
|
limb = pick(victim.bodyparts)
|
||||||
|
|
||||||
|
limb.embedded_objects |= weapon // on the inside... on the inside...
|
||||||
weapon.forceMove(victim)
|
weapon.forceMove(victim)
|
||||||
|
RegisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING), .proc/byeItemCarbon)
|
||||||
|
|
||||||
if(harmful)
|
if(harmful)
|
||||||
victim.visible_message("<span class='danger'>[weapon] embeds itself in [victim]'s [L.name]!</span>","<span class='userdanger'>[weapon] embeds itself in your [L.name]!</span>")
|
victim.visible_message("<span class='danger'>[weapon] embeds itself in [victim]'s [limb.name]!</span>",ignored_mobs=victim)
|
||||||
|
to_chat(victim, "<span class='userdanger'>[weapon] embeds itself in your [limb.name]!</span>")
|
||||||
victim.throw_alert("embeddedobject", /obj/screen/alert/embeddedobject)
|
victim.throw_alert("embeddedobject", /obj/screen/alert/embeddedobject)
|
||||||
playsound(victim,'sound/weapons/bladeslice.ogg', 40)
|
playsound(victim,'sound/weapons/bladeslice.ogg', 40)
|
||||||
weapon.add_mob_blood(victim)//it embedded itself in you, of course it's bloody!
|
weapon.add_mob_blood(victim)//it embedded itself in you, of course it's bloody!
|
||||||
var/damage = weapon.w_class * impact_pain_mult
|
var/damage = weapon.w_class * impact_pain_mult
|
||||||
L.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage)
|
limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage)
|
||||||
SEND_SIGNAL(victim, COMSIG_ADD_MOOD_EVENT, "embedded", /datum/mood_event/embedded)
|
SEND_SIGNAL(victim, COMSIG_ADD_MOOD_EVENT, "embedded", /datum/mood_event/embedded)
|
||||||
else
|
else
|
||||||
victim.visible_message("<span class='danger'>[weapon] sticks itself to [victim]'s [L.name]!</span>","<span class='userdanger'>[weapon] sticks itself to your [L.name]!</span>")
|
victim.visible_message("<span class='danger'>[weapon] sticks itself to [victim]'s [limb.name]!</span>",ignored_mobs=victim)
|
||||||
|
to_chat(victim, "<span class='userdanger'>[weapon] sticks itself to your [limb.name]!</span>")
|
||||||
|
|
||||||
|
/// Called every time a carbon with a harmful embed moves, rolling a chance for the item to cause pain. The chance is halved if the carbon is crawling or walking.
|
||||||
/// Called every time a human with a harmful embed moves, rolling a chance for the item to cause pain. The chance is halved if the human is crawling or walking.
|
|
||||||
/datum/component/embedded/proc/jostleCheck()
|
/datum/component/embedded/proc/jostleCheck()
|
||||||
var/mob/living/carbon/human/victim = parent
|
var/mob/living/carbon/victim = parent
|
||||||
|
|
||||||
var/chance = jostle_chance
|
var/chance = jostle_chance
|
||||||
if(victim.m_intent == MOVE_INTENT_WALK || victim.lying)
|
if(victim.m_intent == MOVE_INTENT_WALK || !(victim.mobility_flags & MOBILITY_STAND))
|
||||||
chance *= 0.5
|
chance *= 0.5
|
||||||
|
|
||||||
if(prob(chance))
|
if(harmful && prob(chance))
|
||||||
var/damage = weapon.w_class * jostle_pain_mult
|
var/damage = weapon.w_class * jostle_pain_mult
|
||||||
L.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage)
|
limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage)
|
||||||
to_chat(victim, "<span class='userdanger'>[weapon] embedded in your [L.name] jostles and stings!</span>")
|
to_chat(victim, "<span class='userdanger'>[weapon] embedded in your [limb.name] jostles and stings!</span>")
|
||||||
|
|
||||||
|
|
||||||
/// Called when then item randomly falls out of a human. This handles the damage and descriptors, then calls safe_remove()
|
/// Called when then item randomly falls out of a carbon. This handles the damage and descriptors, then calls safe_remove()
|
||||||
/datum/component/embedded/proc/fallOutHuman()
|
/datum/component/embedded/proc/fallOutCarbon()
|
||||||
var/mob/living/carbon/human/victim = parent
|
var/mob/living/carbon/victim = parent
|
||||||
|
|
||||||
if(harmful)
|
if(harmful)
|
||||||
var/damage = weapon.w_class * remove_pain_mult
|
var/damage = weapon.w_class * remove_pain_mult
|
||||||
L.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage)
|
limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage)
|
||||||
victim.visible_message("<span class='danger'>[weapon] falls out of [victim.name]'s [L.name]!</span>","<span class='userdanger'>[weapon] falls out of your [L.name]!</span>")
|
victim.visible_message("<span class='danger'>[weapon] falls out of [victim.name]'s [limb.name]!</span>", ignored_mobs=victim)
|
||||||
|
to_chat(victim, "<span class='userdanger'>[weapon] falls out of your [limb.name]!</span>")
|
||||||
else
|
else
|
||||||
victim.visible_message("<span class='danger'>[weapon] falls off of [victim.name]'s [L.name]!</span>","<span class='userdanger'>[weapon] falls off of your [L.name]!</span>")
|
victim.visible_message("<span class='danger'>[weapon] falls off of [victim.name]'s [limb.name]!</span>", ignored_mobs=victim)
|
||||||
|
to_chat(victim, "<span class='userdanger'>[weapon] falls off of your [limb.name]!</span>")
|
||||||
|
|
||||||
safeRemoveHuman()
|
safeRemoveCarbon()
|
||||||
|
|
||||||
|
|
||||||
/// Called when a human with an object embedded/stuck to them inspects themselves and clicks the appropriate link to begin ripping the item out. This handles the ripping attempt, descriptors, and dealing damage, then calls safe_remove()
|
/// Called when a carbon with an object embedded/stuck to them inspects themselves and clicks the appropriate link to begin ripping the item out. This handles the ripping attempt, descriptors, and dealing damage, then calls safe_remove()
|
||||||
/datum/component/embedded/proc/ripOutHuman()
|
/datum/component/embedded/proc/ripOutCarbon(datum/source, obj/item/I, obj/item/bodypart/limb)
|
||||||
var/mob/living/carbon/human/victim = parent
|
if(I != weapon || src.limb != limb)
|
||||||
|
return
|
||||||
|
|
||||||
|
var/mob/living/carbon/victim = parent
|
||||||
var/time_taken = rip_time * weapon.w_class
|
var/time_taken = rip_time * weapon.w_class
|
||||||
|
|
||||||
victim.visible_message("<span class='warning'>[victim] attempts to remove [weapon] from [victim.p_their()] [L.name].</span>","<span class='notice'>You attempt to remove [weapon] from your [L.name]... (It will take [DisplayTimeText(time_taken)].)</span>")
|
victim.visible_message("<span class='warning'>[victim] attempts to remove [weapon] from [victim.p_their()] [limb.name].</span>","<span class='notice'>You attempt to remove [weapon] from your [limb.name]... (It will take [DisplayTimeText(time_taken)].)</span>")
|
||||||
if(do_after(victim, time_taken, target = victim))
|
if(do_after(victim, time_taken, target = victim))
|
||||||
if(!weapon || !L || weapon.loc != victim || !(weapon in L.embedded_objects))
|
if(!weapon || !limb || weapon.loc != victim || !(weapon in limb.embedded_objects))
|
||||||
qdel(src)
|
qdel(src)
|
||||||
|
return
|
||||||
|
|
||||||
if(harmful)
|
if(harmful)
|
||||||
var/damage = weapon.w_class * remove_pain_mult
|
var/damage = weapon.w_class * remove_pain_mult
|
||||||
L.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage) //It hurts to rip it out, get surgery you dingus.
|
limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage) //It hurts to rip it out, get surgery you dingus.
|
||||||
victim.emote("scream")
|
victim.emote("scream")
|
||||||
victim.visible_message("<span class='notice'>[victim] successfully rips [weapon] out of [victim.p_their()] [L.name]!</span>", "<span class='notice'>You successfully remove [weapon] from your [L.name].</span>")
|
victim.visible_message("<span class='notice'>[victim] successfully rips [weapon] out of [victim.p_their()] [limb.name]!</span>", "<span class='notice'>You successfully remove [weapon] from your [limb.name].</span>")
|
||||||
else
|
else
|
||||||
victim.visible_message("<span class='notice'>[victim] successfully rips [weapon] off of [victim.p_their()] [L.name]!</span>", "<span class='notice'>You successfully remove [weapon] from your [L.name].</span>")
|
victim.visible_message("<span class='notice'>[victim] successfully rips [weapon] off of [victim.p_their()] [limb.name]!</span>", "<span class='notice'>You successfully remove [weapon] from your [limb.name].</span>")
|
||||||
|
|
||||||
safeRemoveHuman(TRUE)
|
safeRemoveCarbon(TRUE)
|
||||||
|
|
||||||
|
|
||||||
/// This proc handles the final step and actual removal of an embedded/stuck item from a human, whether or not it was actually removed safely.
|
/// This proc handles the final step and actual removal of an embedded/stuck item from a carbon, whether or not it was actually removed safely.
|
||||||
/// Pass TRUE for to_hands if we want it to go to the victim's hands when they pull it out
|
/// Pass TRUE for to_hands if we want it to go to the victim's hands when they pull it out
|
||||||
/datum/component/embedded/proc/safeRemoveHuman(to_hands)
|
/datum/component/embedded/proc/safeRemoveCarbon(to_hands)
|
||||||
var/mob/living/carbon/human/victim = parent
|
var/mob/living/carbon/victim = parent
|
||||||
L.embedded_objects -= weapon
|
limb.embedded_objects -= weapon
|
||||||
|
|
||||||
if(!victim)
|
UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING)) // have to unhook these here so they don't also register as having disappeared
|
||||||
weapon.forceMove(get_turf(weapon))
|
|
||||||
|
if(!weapon)
|
||||||
|
if(!victim.has_embedded_objects())
|
||||||
|
victim.clear_alert("embeddedobject")
|
||||||
|
SEND_SIGNAL(victim, COMSIG_CLEAR_MOOD_EVENT, "embedded")
|
||||||
qdel(src)
|
qdel(src)
|
||||||
|
return
|
||||||
|
|
||||||
|
if(weapon.unembedded()) // if it deleted itself
|
||||||
|
weapon = null
|
||||||
|
if(!victim.has_embedded_objects())
|
||||||
|
victim.clear_alert("embeddedobject")
|
||||||
|
SEND_SIGNAL(victim, COMSIG_CLEAR_MOOD_EVENT, "embedded")
|
||||||
|
qdel(src)
|
||||||
|
return
|
||||||
|
|
||||||
if(to_hands)
|
if(to_hands)
|
||||||
victim.put_in_hands(weapon)
|
victim.put_in_hands(weapon)
|
||||||
@@ -209,25 +240,46 @@
|
|||||||
qdel(src)
|
qdel(src)
|
||||||
|
|
||||||
|
|
||||||
/// Items embedded/stuck to humans both check whether they randomly fall out (if applicable), as well as if the target mob and limb still exists.
|
/// Something deleted or moved our weapon while it was embedded, how rude!
|
||||||
/// Items harmfully embedded in humans have an additional check for random pain (if applicable)
|
/datum/component/embedded/proc/byeItemCarbon()
|
||||||
/datum/component/embedded/proc/processHuman()
|
var/mob/living/carbon/victim = parent
|
||||||
var/mob/living/carbon/human/victim = parent
|
limb.embedded_objects -= weapon
|
||||||
|
UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING))
|
||||||
|
|
||||||
if(!victim || !L) // in case the victim and/or their limbs exploded (say, due to a sticky bomb)
|
if(victim)
|
||||||
|
to_chat(victim, "<span class='userdanger'>\The [weapon] that was embedded in your [limb.name] disappears!</span>")
|
||||||
|
if(!victim.has_embedded_objects())
|
||||||
|
victim.clear_alert("embeddedobject")
|
||||||
|
SEND_SIGNAL(victim, COMSIG_CLEAR_MOOD_EVENT, "embedded")
|
||||||
|
weapon = null
|
||||||
|
qdel(src)
|
||||||
|
|
||||||
|
|
||||||
|
/// Items embedded/stuck to carbons both check whether they randomly fall out (if applicable), as well as if the target mob and limb still exists.
|
||||||
|
/// Items harmfully embedded in carbons have an additional check for random pain (if applicable)
|
||||||
|
/datum/component/embedded/proc/processCarbon()
|
||||||
|
var/mob/living/carbon/victim = parent
|
||||||
|
|
||||||
|
if(!victim || !limb) // in case the victim and/or their limbs exploded (say, due to a sticky bomb)
|
||||||
weapon.forceMove(get_turf(weapon))
|
weapon.forceMove(get_turf(weapon))
|
||||||
qdel(src)
|
qdel(src)
|
||||||
|
|
||||||
if(victim.stat == DEAD)
|
if(victim.stat == DEAD)
|
||||||
return
|
return
|
||||||
|
|
||||||
if(harmful && prob(pain_chance))
|
var/damage = weapon.w_class * pain_mult
|
||||||
var/damage = weapon.w_class * pain_mult
|
var/chance = pain_chance
|
||||||
L.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage)
|
if(pain_stam_pct && victim.stam_paralyzed) //if it's a less-lethal embed, give them a break if they're already stamcritted
|
||||||
to_chat(victim, "<span class='userdanger'>[weapon] embedded in your [L.name] hurts!</span>")
|
chance *= 0.3
|
||||||
|
damage *= 0.7
|
||||||
|
|
||||||
|
if(harmful && prob(chance))
|
||||||
|
limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage)
|
||||||
|
to_chat(victim, "<span class='userdanger'>[weapon] embedded in your [limb.name] hurts!</span>")
|
||||||
|
|
||||||
if(prob(fall_chance))
|
if(prob(fall_chance))
|
||||||
fallOutHuman()
|
fallOutCarbon()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
@@ -297,6 +349,7 @@
|
|||||||
|
|
||||||
if(do_after(us, 30, target = parent))
|
if(do_after(us, 30, target = parent))
|
||||||
us.put_in_hands(weapon)
|
us.put_in_hands(weapon)
|
||||||
|
weapon.unembedded()
|
||||||
qdel(src)
|
qdel(src)
|
||||||
|
|
||||||
|
|
||||||
@@ -304,4 +357,5 @@
|
|||||||
/datum/component/embedded/proc/itemMoved()
|
/datum/component/embedded/proc/itemMoved()
|
||||||
weapon.invisibility = initial(weapon.invisibility)
|
weapon.invisibility = initial(weapon.invisibility)
|
||||||
weapon.visible_message("<span class='notice'>[weapon] falls loose from [parent].</span>")
|
weapon.visible_message("<span class='notice'>[weapon] falls loose from [parent].</span>")
|
||||||
|
weapon.unembedded()
|
||||||
qdel(src)
|
qdel(src)
|
||||||
|
|||||||
@@ -144,7 +144,7 @@
|
|||||||
var/obj/item/projectile/picked_projectiletype = pickweight(weighted_projectile_types)
|
var/obj/item/projectile/picked_projectiletype = pickweight(weighted_projectile_types)
|
||||||
|
|
||||||
var/obj/item/master = comp.parent
|
var/obj/item/master = comp.parent
|
||||||
comp.appliedComponents += master.AddComponent(/datum/component/shrapnel, picked_projectiletype)
|
comp.appliedComponents += master.AddComponent(/datum/component/mirv, picked_projectiletype)
|
||||||
return "[newName] of [initial(picked_projectiletype.name)] shrapnel"
|
return "[newName] of [initial(picked_projectiletype.name)] shrapnel"
|
||||||
|
|
||||||
/datum/fantasy_affix/strength
|
/datum/fantasy_affix/strength
|
||||||
|
|||||||
@@ -1,36 +1,37 @@
|
|||||||
|
/datum/component/mirv
|
||||||
/datum/component/shrapnel
|
|
||||||
var/projectile_type
|
var/projectile_type
|
||||||
var/radius // shoots a projectile for every turf on this radius from the hit target
|
var/radius // shoots a projectile for every turf on this radius from the hit target
|
||||||
var/override_projectile_range
|
var/override_projectile_range
|
||||||
|
|
||||||
/datum/component/shrapnel/Initialize(projectile_type, radius=1, override_projectile_range)
|
/datum/component/mirv/Initialize(projectile_type, radius=1, override_projectile_range)
|
||||||
if(!isgun(parent) && !ismachinery(parent) && !isstructure(parent))
|
if(!isgun(parent) && !ismachinery(parent) && !isstructure(parent) && !isgrenade(parent))
|
||||||
return COMPONENT_INCOMPATIBLE
|
return COMPONENT_INCOMPATIBLE
|
||||||
|
|
||||||
src.projectile_type = projectile_type
|
src.projectile_type = projectile_type
|
||||||
src.radius = radius
|
src.radius = radius
|
||||||
src.override_projectile_range = override_projectile_range
|
src.override_projectile_range = override_projectile_range
|
||||||
|
if(isgrenade(parent))
|
||||||
|
parent.AddComponent(/datum/component/pellet_cloud, projectile_type=projectile_type)
|
||||||
|
|
||||||
/datum/component/shrapnel/RegisterWithParent()
|
/datum/component/mirv/RegisterWithParent()
|
||||||
. = ..()
|
. = ..()
|
||||||
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
|
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
|
||||||
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
|
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
|
||||||
|
|
||||||
/datum/component/shrapnel/UnregisterFromParent()
|
/datum/component/mirv/UnregisterFromParent()
|
||||||
. = ..()
|
. = ..()
|
||||||
UnregisterSignal(parent, list(COMSIG_PROJECTILE_ON_HIT))
|
UnregisterSignal(parent, list(COMSIG_PROJECTILE_ON_HIT))
|
||||||
|
|
||||||
/datum/component/shrapnel/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)
|
/datum/component/mirv/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)
|
||||||
do_shrapnel(firer, target)
|
do_shrapnel(firer, target)
|
||||||
|
|
||||||
/datum/component/shrapnel/proc/do_shrapnel(mob/firer, atom/target)
|
/datum/component/mirv/proc/do_shrapnel(mob/firer, atom/target)
|
||||||
if(radius < 1)
|
if(radius < 1)
|
||||||
return
|
return
|
||||||
var/turf/target_turf = get_turf(target)
|
var/turf/target_turf = get_turf(target)
|
||||||
for(var/turf/shootat_turf in RANGE_TURFS(radius, target) - RANGE_TURFS(radius-1, target))
|
for(var/turf/shootat_turf in RANGE_TURFS(radius, target) - RANGE_TURFS(radius-1, target))
|
||||||
var/obj/item/projectile/P = new projectile_type(target_turf)
|
|
||||||
|
|
||||||
|
var/obj/item/projectile/P = new projectile_type(target_turf)
|
||||||
//Shooting Code:
|
//Shooting Code:
|
||||||
P.range = radius+1
|
P.range = radius+1
|
||||||
if(override_projectile_range)
|
if(override_projectile_range)
|
||||||
287
code/datums/components/pellet_cloud.dm
Normal file
287
code/datums/components/pellet_cloud.dm
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
/*
|
||||||
|
* This component is used when you want to create a bunch of shrapnel or projectiles (say, shrapnel from a fragmentation grenade, or buckshot from a shotgun) from a central point,
|
||||||
|
* without necessarily printing a separate message for every single impact. This component should be instantiated right when you need it (like the moment of firing), then activated
|
||||||
|
* by signal.
|
||||||
|
*
|
||||||
|
* Pellet cloud currently works on two classes of sources: directed (ammo casings), and circular (grenades, landmines).
|
||||||
|
* -Directed: This means you're shooting multiple pellets, like buckshot. If an ammo casing is defined as having multiple pellets, it will automatically create a pellet cloud
|
||||||
|
* and call COMSIG_PELLET_CLOUD_INIT (see [/obj/item/ammo_casing/proc/fire_casing]). Thus, the only projectiles fired will be the ones fired here.
|
||||||
|
* The magnitude var controls how many pellets are created.
|
||||||
|
* -Circular: This results in a big spray of shrapnel flying all around the detonation point when the grenade fires COMSIG_GRENADE_PRIME or landmine triggers COMSIG_MINE_TRIGGERED.
|
||||||
|
* The magnitude var controls how big the detonation radius is (the bigger the magnitude, the more shrapnel is created). Grenades can be covered with bodies to reduce shrapnel output.
|
||||||
|
*
|
||||||
|
* Once all of the fired projectiles either hit a target or disappear due to ranging out/whatever else, we resolve the list of all the things we hit and print aggregate messages so we get
|
||||||
|
* one "You're hit by 6 buckshot pellets" vs 6x "You're hit by the buckshot blah blah" messages.
|
||||||
|
*
|
||||||
|
* Note that this is how all guns handle shooting ammo casings with multiple pellets, in case such a thing comes up.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/datum/component/pellet_cloud
|
||||||
|
/// What's the projectile path of the shrapnel we're shooting?
|
||||||
|
var/projectile_type
|
||||||
|
|
||||||
|
/// How many shrapnel projectiles are we responsible for tracking? May be reduced for grenades if someone dives on top of it. Defined by ammo casing for casings, derived from magnitude otherwise
|
||||||
|
var/num_pellets
|
||||||
|
/// For grenades/landmines, how big is the radius of turfs we're targeting? Note this does not effect the projectiles range, only how many we generate
|
||||||
|
var/radius = 4
|
||||||
|
|
||||||
|
/// The list of pellets we're responsible for tracking, once these are all accounted for, we finalize.
|
||||||
|
var/list/pellets = list()
|
||||||
|
/// An associated list with the atom hit as the key and how many pellets they've eaten for the value, for printing aggregate messages
|
||||||
|
var/list/targets_hit = list()
|
||||||
|
/// For grenades, any /mob/living's the grenade is moved onto, see [/datum/component/pellet_cloud/proc/handle_martyrs()]
|
||||||
|
var/list/bodies
|
||||||
|
/// For grenades, tracking people who die covering a grenade for achievement purposes, see [/datum/component/pellet_cloud/proc/handle_martyrs()]
|
||||||
|
var/list/purple_hearts
|
||||||
|
|
||||||
|
/// For grenades, tracking how many pellets are removed due to martyrs and how many pellets are added due to the last person to touch it being on top of it
|
||||||
|
var/pellet_delta = 0
|
||||||
|
/// how many pellets ranged out without hitting anything
|
||||||
|
var/terminated
|
||||||
|
/// how many pellets impacted something
|
||||||
|
var/hits
|
||||||
|
/// If the parent tried deleting and we're not done yet, we send it to nullspace then delete it after
|
||||||
|
var/queued_delete = FALSE
|
||||||
|
|
||||||
|
/// for if we're an ammo casing being fired
|
||||||
|
var/mob/living/shooter
|
||||||
|
|
||||||
|
/datum/component/pellet_cloud/Initialize(projectile_type=/obj/item/shrapnel, magnitude=5)
|
||||||
|
if(!isammocasing(parent) && !isgrenade(parent) && !islandmine(parent))
|
||||||
|
return COMPONENT_INCOMPATIBLE
|
||||||
|
|
||||||
|
if(magnitude < 1)
|
||||||
|
stack_trace("Invalid magnitude [magnitude] < 1 on pellet_cloud, parent: [parent]")
|
||||||
|
magnitude = 1
|
||||||
|
|
||||||
|
src.projectile_type = projectile_type
|
||||||
|
|
||||||
|
if(isammocasing(parent))
|
||||||
|
num_pellets = magnitude
|
||||||
|
else if(isgrenade(parent) || islandmine(parent))
|
||||||
|
radius = magnitude
|
||||||
|
|
||||||
|
/datum/component/pellet_cloud/Destroy(force, silent)
|
||||||
|
purple_hearts = null
|
||||||
|
pellets = null
|
||||||
|
targets_hit = null
|
||||||
|
bodies = null
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/datum/component/pellet_cloud/RegisterWithParent()
|
||||||
|
RegisterSignal(parent, COMSIG_PARENT_PREQDELETED, .proc/nullspace_parent)
|
||||||
|
if(isammocasing(parent))
|
||||||
|
RegisterSignal(parent, COMSIG_PELLET_CLOUD_INIT, .proc/create_casing_pellets)
|
||||||
|
else if(isgrenade(parent))
|
||||||
|
RegisterSignal(parent, COMSIG_GRENADE_ARMED, .proc/grenade_armed)
|
||||||
|
RegisterSignal(parent, COMSIG_GRENADE_PRIME, .proc/create_blast_pellets)
|
||||||
|
else if(islandmine(parent))
|
||||||
|
RegisterSignal(parent, COMSIG_MINE_TRIGGERED, .proc/create_blast_pellets)
|
||||||
|
|
||||||
|
/datum/component/pellet_cloud/UnregisterFromParent()
|
||||||
|
UnregisterSignal(parent, list(COMSIG_PARENT_PREQDELETED, COMSIG_PELLET_CLOUD_INIT, COMSIG_GRENADE_PRIME, COMSIG_GRENADE_ARMED, COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_UNCROSSED, COMSIG_MINE_TRIGGERED, COMSIG_ITEM_DROPPED))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create_casing_pellets() is for directed pellet clouds for ammo casings that have multiple pellets (buckshot and scatter lasers for instance)
|
||||||
|
*
|
||||||
|
* Honestly this is mostly just a rehash of [/obj/item/ammo_casing/proc/fire_casing()] for pellet counts > 1, except this lets us tamper with the pellets and hook onto them for tracking purposes.
|
||||||
|
* The arguments really don't matter, this proc is triggered by COMSIG_PELLET_CLOUD_INIT which is only for this really, it's just a big mess of the state vars we need for doing the stuff over here.
|
||||||
|
*/
|
||||||
|
/datum/component/pellet_cloud/proc/create_casing_pellets(obj/item/ammo_casing/shell, atom/target, mob/living/user, fired_from, randomspread, spread, zone_override, params, distro)
|
||||||
|
shooter = user
|
||||||
|
var/targloc = get_turf(target)
|
||||||
|
if(!zone_override)
|
||||||
|
zone_override = shooter.zone_selected
|
||||||
|
|
||||||
|
for(var/i in 1 to num_pellets)
|
||||||
|
shell.ready_proj(target, user, SUPPRESSED_VERY, zone_override, fired_from)
|
||||||
|
if(distro)
|
||||||
|
if(randomspread)
|
||||||
|
spread = round((rand() - 0.5) * distro)
|
||||||
|
else //Smart spread
|
||||||
|
spread = round((i / num_pellets - 0.5) * distro)
|
||||||
|
|
||||||
|
RegisterSignal(shell.BB, COMSIG_PROJECTILE_SELF_ON_HIT, .proc/pellet_hit)
|
||||||
|
RegisterSignal(shell.BB, list(COMSIG_PROJECTILE_RANGE_OUT, COMSIG_PARENT_QDELETING), .proc/pellet_range)
|
||||||
|
pellets += shell.BB
|
||||||
|
if(!shell.throw_proj(target, targloc, shooter, params, spread))
|
||||||
|
return
|
||||||
|
if(i != num_pellets)
|
||||||
|
shell.newshot()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create_blast_pellets() is for when we have a central point we want to shred the surroundings of with a ring of shrapnel, namely frag grenades and landmines.
|
||||||
|
*
|
||||||
|
* Note that grenades have extra handling for someone throwing themselves/being thrown on top of it, while landmines do not (obviously, it's a landmine!). See [/datum/component/pellet_cloud/proc/handle_martyrs()]
|
||||||
|
*/
|
||||||
|
/datum/component/pellet_cloud/proc/create_blast_pellets(obj/O, mob/living/lanced_by)
|
||||||
|
var/atom/A = parent
|
||||||
|
|
||||||
|
if(isgrenade(parent)) // handle_martyrs can reduce the radius and thus the number of pellets we produce if someone dives on top of a frag grenade
|
||||||
|
handle_martyrs(lanced_by) // note that we can modify radius in this proc
|
||||||
|
|
||||||
|
if(radius < 1)
|
||||||
|
return
|
||||||
|
|
||||||
|
var/list/all_the_turfs_were_gonna_lacerate = RANGE_TURFS(radius, A) - RANGE_TURFS(radius-1, A)
|
||||||
|
num_pellets = all_the_turfs_were_gonna_lacerate.len + pellet_delta
|
||||||
|
|
||||||
|
for(var/T in all_the_turfs_were_gonna_lacerate)
|
||||||
|
var/turf/shootat_turf = T
|
||||||
|
pew(shootat_turf)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handle_martyrs() is used for grenades that shoot shrapnel to check if anyone threw themselves/were thrown on top of the grenade, thus absorbing a good chunk of the shrapnel
|
||||||
|
*
|
||||||
|
* Between the time the grenade is armed and the actual detonation, we set var/list/bodies to the list of mobs currently on the new tile, as if the grenade landed on top of them, tracking if any of them move off the tile and removing them from the "under" list
|
||||||
|
* Once the grenade detonates, handle_martyrs() is called and gets all the new mobs on the tile, and add the ones not in var/list/bodies to var/list/martyrs
|
||||||
|
* We then iterate through the martyrs and reduce the shrapnel magnitude for each mob on top of it, shredding each of them with some of the shrapnel they helped absorb. This can snuff out all of the shrapnel if there's enough bodies
|
||||||
|
*
|
||||||
|
* Note we track anyone who's alive and client'd when they get shredded in var/list/purple_hearts, for achievement checking later
|
||||||
|
*/
|
||||||
|
/datum/component/pellet_cloud/proc/handle_martyrs(mob/living/lanced_by)
|
||||||
|
var/magnitude_absorbed
|
||||||
|
var/list/martyrs = list()
|
||||||
|
|
||||||
|
var/self_harm_radius_mult = 3
|
||||||
|
|
||||||
|
if(lanced_by && prob(60))
|
||||||
|
to_chat(lanced_by, "<span class='userdanger'>Your plan to whack someone with a grenade on a stick backfires on you, literally!</span>")
|
||||||
|
self_harm_radius_mult = 1 // we'll still give the guy who got hit some extra shredding, but not 3*radius
|
||||||
|
pellet_delta += radius
|
||||||
|
for(var/i in 1 to radius)
|
||||||
|
pew(lanced_by) // thought you could be tricky and lance someone with no ill effects!!
|
||||||
|
|
||||||
|
for(var/mob/living/body in get_turf(parent))
|
||||||
|
if(body == shooter)
|
||||||
|
pellet_delta += radius * self_harm_radius_mult
|
||||||
|
for(var/i in 1 to radius * self_harm_radius_mult)
|
||||||
|
pew(body) // free shrapnel if it goes off in your hand, and it doesn't even count towards the absorbed. fun!
|
||||||
|
else if(!(body in bodies))
|
||||||
|
martyrs += body // promoted from a corpse to a hero
|
||||||
|
|
||||||
|
for(var/M in martyrs)
|
||||||
|
var/mob/living/martyr = M
|
||||||
|
if(radius > 4)
|
||||||
|
martyr.visible_message("<b><span class='danger'>[martyr] heroically covers \the [parent] with [martyr.p_their()] body, absorbing a load of the shrapnel!</span></b>", "<span class='userdanger'>You heroically cover \the [parent] with your body, absorbing a load of the shrapnel!</span>")
|
||||||
|
magnitude_absorbed += round(radius * 0.5)
|
||||||
|
else if(radius >= 2)
|
||||||
|
martyr.visible_message("<b><span class='danger'>[martyr] heroically covers \the [parent] with [martyr.p_their()] body, absorbing some of the shrapnel!</span></b>", "<span class='userdanger'>You heroically cover \the [parent] with your body, absorbing some of the shrapnel!</span>")
|
||||||
|
magnitude_absorbed += 2
|
||||||
|
else
|
||||||
|
martyr.visible_message("<b><span class='danger'>[martyr] heroically covers \the [parent] with [martyr.p_their()] body, snuffing out the shrapnel!</span></b>", "<span class='userdanger'>You heroically cover \the [parent] with your body, snuffing out the shrapnel!</span>")
|
||||||
|
magnitude_absorbed = radius
|
||||||
|
|
||||||
|
var/pellets_absorbed = (radius ** 2) - ((radius - magnitude_absorbed - 1) ** 2)
|
||||||
|
radius -= magnitude_absorbed
|
||||||
|
pellet_delta -= round(pellets_absorbed * 0.5)
|
||||||
|
|
||||||
|
if(martyr.stat != DEAD && martyr.client)
|
||||||
|
LAZYADD(purple_hearts, martyr)
|
||||||
|
RegisterSignal(martyr, COMSIG_PARENT_QDELETING, .proc/on_target_qdel, override=TRUE)
|
||||||
|
|
||||||
|
for(var/i in 1 to round(pellets_absorbed * 0.5))
|
||||||
|
pew(martyr)
|
||||||
|
|
||||||
|
if(radius < 1)
|
||||||
|
break
|
||||||
|
|
||||||
|
///One of our pellets hit something, record what it was and check if we're done (terminated == num_pellets)
|
||||||
|
/datum/component/pellet_cloud/proc/pellet_hit(obj/projectile/P, atom/movable/firer, atom/target, Angle)
|
||||||
|
pellets -= P
|
||||||
|
terminated++
|
||||||
|
hits++
|
||||||
|
targets_hit[target]++
|
||||||
|
if(targets_hit[target] == 1)
|
||||||
|
RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/on_target_qdel, override=TRUE)
|
||||||
|
UnregisterSignal(P, list(COMSIG_PARENT_QDELETING, COMSIG_PROJECTILE_RANGE_OUT, COMSIG_PROJECTILE_SELF_ON_HIT))
|
||||||
|
if(terminated == num_pellets)
|
||||||
|
finalize()
|
||||||
|
|
||||||
|
///One of our pellets disappeared due to hitting their max range (or just somehow got qdel'd), remove it from our list and check if we're done (terminated == num_pellets)
|
||||||
|
/datum/component/pellet_cloud/proc/pellet_range(obj/projectile/P)
|
||||||
|
pellets -= P
|
||||||
|
terminated++
|
||||||
|
UnregisterSignal(P, list(COMSIG_PARENT_QDELETING, COMSIG_PROJECTILE_RANGE_OUT, COMSIG_PROJECTILE_SELF_ON_HIT))
|
||||||
|
if(terminated == num_pellets)
|
||||||
|
finalize()
|
||||||
|
|
||||||
|
/// Minor convenience function for creating each shrapnel piece with circle explosions, mostly stolen from the MIRV component
|
||||||
|
/datum/component/pellet_cloud/proc/pew(atom/target, spread=0)
|
||||||
|
var/obj/projectile/P = new projectile_type(get_turf(parent))
|
||||||
|
|
||||||
|
//Shooting Code:
|
||||||
|
P.spread = spread
|
||||||
|
P.original = target
|
||||||
|
P.fired_from = parent
|
||||||
|
P.firer = parent // don't hit ourself that would be really annoying
|
||||||
|
P.permutated += parent // don't hit the target we hit already with the flak
|
||||||
|
P.suppressed = SUPPRESSED_VERY // set the projectiles to make no message so we can do our own aggregate message
|
||||||
|
P.preparePixelProjectile(target, parent)
|
||||||
|
RegisterSignal(P, COMSIG_PROJECTILE_SELF_ON_HIT, .proc/pellet_hit)
|
||||||
|
RegisterSignal(P, list(COMSIG_PROJECTILE_RANGE_OUT, COMSIG_PARENT_QDELETING), .proc/pellet_range)
|
||||||
|
pellets += P
|
||||||
|
P.fire()
|
||||||
|
|
||||||
|
///All of our pellets are accounted for, time to go target by target and tell them how many things they got hit by.
|
||||||
|
/datum/component/pellet_cloud/proc/finalize()
|
||||||
|
var/obj/projectile/P = projectile_type
|
||||||
|
var/proj_name = initial(P.name)
|
||||||
|
|
||||||
|
for(var/atom/target in targets_hit)
|
||||||
|
var/num_hits = targets_hit[target]
|
||||||
|
UnregisterSignal(target, COMSIG_PARENT_QDELETING)
|
||||||
|
if(num_hits > 1)
|
||||||
|
target.visible_message("<span class='danger'>[target] is hit by [num_hits] [proj_name]s!</span>", null, null, COMBAT_MESSAGE_RANGE, target)
|
||||||
|
to_chat(target, "<span class='userdanger'>You're hit by [num_hits] [proj_name]s!</span>")
|
||||||
|
else
|
||||||
|
target.visible_message("<span class='danger'>[target] is hit by a [proj_name]!</span>", null, null, COMBAT_MESSAGE_RANGE, target)
|
||||||
|
to_chat(target, "<span class='userdanger'>You're hit by a [proj_name]!</span>")
|
||||||
|
|
||||||
|
for(var/M in purple_hearts)
|
||||||
|
var/mob/living/martyr = M
|
||||||
|
if(martyr.stat == DEAD && martyr.client)
|
||||||
|
martyr.client.give_award(/datum/award/achievement/misc/lookoutsir, martyr)
|
||||||
|
UnregisterSignal(parent, COMSIG_PARENT_PREQDELETED)
|
||||||
|
if(queued_delete)
|
||||||
|
qdel(parent)
|
||||||
|
qdel(src)
|
||||||
|
|
||||||
|
/// Look alive, we're armed! Now we start watching to see if anyone's covering us
|
||||||
|
/datum/component/pellet_cloud/proc/grenade_armed(obj/item/nade)
|
||||||
|
if(ismob(nade.loc))
|
||||||
|
shooter = nade.loc
|
||||||
|
LAZYINITLIST(bodies)
|
||||||
|
RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/grenade_dropped)
|
||||||
|
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/grenade_moved)
|
||||||
|
RegisterSignal(parent, COMSIG_MOVABLE_UNCROSSED, .proc/grenade_uncrossed)
|
||||||
|
|
||||||
|
/// Someone dropped the grenade, so set them to the shooter in case they're on top of it when it goes off
|
||||||
|
/datum/component/pellet_cloud/proc/grenade_dropped(obj/item/nade, mob/living/slick_willy)
|
||||||
|
shooter = slick_willy
|
||||||
|
grenade_moved()
|
||||||
|
|
||||||
|
/// Our grenade has moved, reset var/list/bodies so we're "on top" of any mobs currently on the tile
|
||||||
|
/datum/component/pellet_cloud/proc/grenade_moved()
|
||||||
|
LAZYCLEARLIST(bodies)
|
||||||
|
for(var/mob/living/L in get_turf(parent))
|
||||||
|
RegisterSignal(L, COMSIG_PARENT_QDELETING, .proc/on_target_qdel, override=TRUE)
|
||||||
|
bodies += L
|
||||||
|
|
||||||
|
/// Someone who was originally "under" the grenade has moved off the tile and is now eligible for being a martyr and "covering" it
|
||||||
|
/datum/component/pellet_cloud/proc/grenade_uncrossed(datum/source, atom/movable/AM)
|
||||||
|
bodies -= AM
|
||||||
|
|
||||||
|
/// Our grenade or landmine or caseless shell or whatever tried deleting itself, so we intervene and nullspace it until we're done here
|
||||||
|
/datum/component/pellet_cloud/proc/nullspace_parent()
|
||||||
|
var/atom/movable/AM = parent
|
||||||
|
AM.moveToNullspace()
|
||||||
|
queued_delete = TRUE
|
||||||
|
return TRUE
|
||||||
|
|
||||||
|
/// Someone who was originally "under" the grenade has moved off the tile and is now eligible for being a martyr and "covering" it
|
||||||
|
/datum/component/pellet_cloud/proc/on_target_qdel(atom/target)
|
||||||
|
UnregisterSignal(target, COMSIG_PARENT_QDELETING)
|
||||||
|
targets_hit -= target
|
||||||
|
bodies -= target
|
||||||
|
purple_hearts -= target
|
||||||
@@ -206,7 +206,7 @@
|
|||||||
user.set_resting(FALSE, TRUE, FALSE)
|
user.set_resting(FALSE, TRUE, FALSE)
|
||||||
user.forceMove(get_turf(target))
|
user.forceMove(get_turf(target))
|
||||||
target.adjustStaminaLoss(65)
|
target.adjustStaminaLoss(65)
|
||||||
target.Paralyze(10)
|
target.Paralyze(10)
|
||||||
target.DefaultCombatKnockdown(20)
|
target.DefaultCombatKnockdown(20)
|
||||||
if(ishuman(target) && iscarbon(user))
|
if(ishuman(target) && iscarbon(user))
|
||||||
target.grabbedby(user)
|
target.grabbedby(user)
|
||||||
@@ -415,10 +415,10 @@
|
|||||||
for(var/i = 0, i < speed, i++)
|
for(var/i = 0, i < speed, i++)
|
||||||
var/obj/item/shard/shard = new /obj/item/shard(get_turf(user))
|
var/obj/item/shard/shard = new /obj/item/shard(get_turf(user))
|
||||||
//shard.embedding = list(embed_chance = 100, ignore_throwspeed_threshold = TRUE, impact_pain_mult=3, pain_chance=5)
|
//shard.embedding = list(embed_chance = 100, ignore_throwspeed_threshold = TRUE, impact_pain_mult=3, pain_chance=5)
|
||||||
//shard.AddElement(/datum/element/embed, shard.embedding)
|
shard.updateEmbedding()
|
||||||
user.hitby(shard, skipcatch = TRUE, hitpush = FALSE)
|
user.hitby(shard, skipcatch = TRUE, hitpush = FALSE)
|
||||||
//shard.embedding = list()
|
shard.embedding = list()
|
||||||
//shard.AddElement(/datum/element/embed, shard.embedding)
|
shard.updateEmbedding()
|
||||||
W.obj_destruction()
|
W.obj_destruction()
|
||||||
user.adjustStaminaLoss(10 * speed)
|
user.adjustStaminaLoss(10 * speed)
|
||||||
user.DefaultCombatKnockdown(40)
|
user.DefaultCombatKnockdown(40)
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
The presence of this element allows an item to embed itself in a human or turf when it is thrown into a target (whether by hand, gun, or explosive wave)
|
The presence of this element allows an item (or a projectile carrying an item) to embed itself in a human or turf when it is thrown into a target (whether by hand, gun, or explosive wave) with either
|
||||||
with either at least 4 throwspeed (EMBED_THROWSPEED_THRESHOLD) or ignore_throwspeed_threshold set to TRUE.
|
at least 4 throwspeed (EMBED_THROWSPEED_THRESHOLD) or ignore_throwspeed_threshold set to TRUE. Items meant to be used as shrapnel for projectiles should have ignore_throwspeed_threshold set to true.
|
||||||
|
|
||||||
This element is granted primarily to any /obj/item that has something in its /embedding var, which should be formatted as a list. If you wish to be able to
|
Whether we're dealing with a direct /obj/item (throwing a knife at someone) or an /obj/projectile with a shrapnel_type, how we handle things plays out the same, with one extra step separating them.
|
||||||
grant/rescind the ability for an item to embed (say, when activating and deactivating an edagger), you can do so in two ways:
|
Items simply make their COMSIG_MOVABLE_IMPACT or COMSIG_MOVABLE_IMPACT_ZONE check (against a closed turf or a carbon, respectively), while projectiles check on COMSIG_PROJECTILE_SELF_ON_HIT.
|
||||||
|
Upon a projectile hitting a valid target, it spawns whatever type of payload it has defined, then has that try to embed itself in the target on its own.
|
||||||
1. Drop the throw_speed var below EMBED_THROWSPEED_THRESHOLD (object will still be able to otherwise embed if thrown at high speed by something else like a blast)
|
|
||||||
2. Add/Remove the embed element as needed (won't be able to embed at all)
|
|
||||||
|
|
||||||
Otherwise non-embeddable or stickable items can be made embeddable/stickable through wizard events/sticky tape/admin memes.
|
Otherwise non-embeddable or stickable items can be made embeddable/stickable through wizard events/sticky tape/admin memes.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define STANDARD_WALL_HARDNESS 40
|
#define STANDARD_WALL_HARDNESS 40
|
||||||
@@ -17,6 +14,7 @@
|
|||||||
/datum/element/embed
|
/datum/element/embed
|
||||||
element_flags = ELEMENT_BESPOKE
|
element_flags = ELEMENT_BESPOKE
|
||||||
id_arg_index = 2
|
id_arg_index = 2
|
||||||
|
var/initialized = FALSE /// whether we can skip assigning all the vars (since these are bespoke elements, we don't have to reset the vars every time we attach to something, we already know what we are!)
|
||||||
|
|
||||||
// all of this stuff is explained in _DEFINES/combat.dm
|
// all of this stuff is explained in _DEFINES/combat.dm
|
||||||
var/embed_chance
|
var/embed_chance
|
||||||
@@ -30,99 +28,196 @@
|
|||||||
var/jostle_chance
|
var/jostle_chance
|
||||||
var/jostle_pain_mult
|
var/jostle_pain_mult
|
||||||
var/pain_stam_pct
|
var/pain_stam_pct
|
||||||
|
var/payload_type
|
||||||
|
var/embed_chance_turf_mod
|
||||||
|
|
||||||
/datum/element/embed/Attach(datum/target, list/embedArgs)
|
/datum/element/embed/Attach(datum/target, embed_chance, fall_chance, pain_chance, pain_mult, remove_pain_mult, impact_pain_mult, rip_time, ignore_throwspeed_threshold, jostle_chance, jostle_pain_mult, pain_stam_pct, embed_chance_turf_mod, projectile_payload=/obj/item/shard)
|
||||||
. = ..()
|
. = ..()
|
||||||
parseArgs(arglist(embedArgs))
|
|
||||||
|
|
||||||
if(!isitem(target))
|
if(!isitem(target) && !isprojectile(target))
|
||||||
return ELEMENT_INCOMPATIBLE
|
return ELEMENT_INCOMPATIBLE
|
||||||
|
|
||||||
RegisterSignal(target, COMSIG_MOVABLE_IMPACT_ZONE, .proc/checkEmbedMob)
|
if(isitem(target))
|
||||||
RegisterSignal(target, COMSIG_MOVABLE_IMPACT, .proc/checkEmbedOther)
|
RegisterSignal(target, COMSIG_MOVABLE_IMPACT_ZONE, .proc/checkEmbedMob)
|
||||||
RegisterSignal(target, COMSIG_ELEMENT_ATTACH, .proc/severancePackage)
|
RegisterSignal(target, COMSIG_MOVABLE_IMPACT, .proc/checkEmbedOther)
|
||||||
RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/examined)
|
RegisterSignal(target, COMSIG_ELEMENT_ATTACH, .proc/severancePackage)
|
||||||
|
RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/examined)
|
||||||
|
RegisterSignal(target, COMSIG_EMBED_TRY_FORCE, .proc/tryForceEmbed)
|
||||||
|
RegisterSignal(target, COMSIG_ITEM_DISABLE_EMBED, .proc/detachFromWeapon)
|
||||||
|
if(!initialized)
|
||||||
|
src.embed_chance = embed_chance
|
||||||
|
src.fall_chance = fall_chance
|
||||||
|
src.pain_chance = pain_chance
|
||||||
|
src.pain_mult = pain_mult
|
||||||
|
src.remove_pain_mult = remove_pain_mult
|
||||||
|
src.rip_time = rip_time
|
||||||
|
src.impact_pain_mult = impact_pain_mult
|
||||||
|
src.ignore_throwspeed_threshold = ignore_throwspeed_threshold
|
||||||
|
src.jostle_chance = jostle_chance
|
||||||
|
src.jostle_pain_mult = jostle_pain_mult
|
||||||
|
src.pain_stam_pct = pain_stam_pct
|
||||||
|
src.embed_chance_turf_mod = embed_chance_turf_mod
|
||||||
|
initialized = TRUE
|
||||||
|
else
|
||||||
|
payload_type = projectile_payload
|
||||||
|
RegisterSignal(target, COMSIG_PROJECTILE_SELF_ON_HIT, .proc/checkEmbedProjectile)
|
||||||
|
|
||||||
/datum/element/embed/Detach(obj/item/target)
|
|
||||||
|
/datum/element/embed/Detach(obj/target)
|
||||||
. = ..()
|
. = ..()
|
||||||
UnregisterSignal(target, list(COMSIG_MOVABLE_IMPACT_ZONE, COMSIG_ELEMENT_ATTACH, COMSIG_MOVABLE_IMPACT, COMSIG_PARENT_EXAMINE))
|
if(isitem(target))
|
||||||
|
UnregisterSignal(target, list(COMSIG_MOVABLE_IMPACT_ZONE, COMSIG_ELEMENT_ATTACH, COMSIG_MOVABLE_IMPACT, COMSIG_PARENT_EXAMINE, COMSIG_EMBED_TRY_FORCE, COMSIG_ITEM_DISABLE_EMBED))
|
||||||
|
else
|
||||||
|
UnregisterSignal(target, list(COMSIG_PROJECTILE_SELF_ON_HIT))
|
||||||
|
|
||||||
|
|
||||||
/// Checking to see if we're gonna embed into a human
|
/// Checking to see if we're gonna embed into a human
|
||||||
/datum/element/embed/proc/checkEmbedMob(obj/item/weapon, mob/living/carbon/human/victim, hit_zone, datum/thrownthing/throwingdatum)
|
/datum/element/embed/proc/checkEmbedMob(obj/item/weapon, mob/living/carbon/victim, hit_zone, datum/thrownthing/throwingdatum, forced=FALSE)
|
||||||
if(!istype(victim))
|
if(!istype(victim) || HAS_TRAIT(victim, TRAIT_PIERCEIMMUNE))
|
||||||
return
|
return
|
||||||
|
|
||||||
if((((throwingdatum ? throwingdatum.speed : weapon.throw_speed) >= EMBED_THROWSPEED_THRESHOLD) || ignore_throwspeed_threshold) && prob(embed_chance) && !HAS_TRAIT(victim, TRAIT_PIERCEIMMUNE))
|
var/actual_chance = embed_chance
|
||||||
victim.AddComponent(/datum/component/embedded,\
|
|
||||||
weapon,\
|
|
||||||
throwingdatum,\
|
|
||||||
embed_chance = embed_chance,\
|
|
||||||
fall_chance = fall_chance,\
|
|
||||||
pain_chance = pain_chance,\
|
|
||||||
pain_mult = pain_mult,\
|
|
||||||
remove_pain_mult = remove_pain_mult,\
|
|
||||||
rip_time = rip_time,\
|
|
||||||
ignore_throwspeed_threshold = ignore_throwspeed_threshold,\
|
|
||||||
jostle_chance = jostle_chance,\
|
|
||||||
jostle_pain_mult = jostle_pain_mult,\
|
|
||||||
pain_stam_pct = pain_stam_pct)
|
|
||||||
|
|
||||||
/// We need the hit_zone if we're embedding into a human, so this proc only handled if we're embedding into a turf
|
if(!weapon.isEmbedHarmless()) // all the armor in the world won't save you from a kick me sign
|
||||||
/datum/element/embed/proc/checkEmbedOther(obj/item/weapon, turf/closed/hit, datum/thrownthing/throwingdatum)
|
var/armor = max(victim.run_armor_check(hit_zone, "bullet", silent=TRUE), victim.run_armor_check(hit_zone, "bomb", silent=TRUE)) // we'll be nice and take the better of bullet and bomb armor
|
||||||
|
|
||||||
|
if(armor) // we only care about armor penetration if there's actually armor to penetrate
|
||||||
|
var/pen_mod = -armor + weapon.armour_penetration // even a little bit of armor can make a big difference for shrapnel with large negative armor pen
|
||||||
|
actual_chance += pen_mod // doing the armor pen as a separate calc just in case this ever gets expanded on
|
||||||
|
if(actual_chance <= 0)
|
||||||
|
victim.visible_message("<span class='danger'>[weapon] bounces off [victim]'s armor!</span>", "<span class='notice'>[weapon] bounces off your armor!</span>", vision_distance = COMBAT_MESSAGE_RANGE)
|
||||||
|
return
|
||||||
|
|
||||||
|
var/roll_embed = prob(actual_chance)
|
||||||
|
var/pass = forced || ((((throwingdatum ? throwingdatum.speed : weapon.throw_speed) >= EMBED_THROWSPEED_THRESHOLD) || ignore_throwspeed_threshold) && roll_embed)
|
||||||
|
if(!pass)
|
||||||
|
return
|
||||||
|
|
||||||
|
var/obj/item/bodypart/limb = victim.get_bodypart(hit_zone) || pick(victim.bodyparts)
|
||||||
|
victim.AddComponent(/datum/component/embedded,\
|
||||||
|
weapon,\
|
||||||
|
throwingdatum,\
|
||||||
|
part = limb,\
|
||||||
|
embed_chance = embed_chance,\
|
||||||
|
fall_chance = fall_chance,\
|
||||||
|
pain_chance = pain_chance,\
|
||||||
|
pain_mult = pain_mult,\
|
||||||
|
remove_pain_mult = remove_pain_mult,\
|
||||||
|
rip_time = rip_time,\
|
||||||
|
ignore_throwspeed_threshold = ignore_throwspeed_threshold,\
|
||||||
|
jostle_chance = jostle_chance,\
|
||||||
|
jostle_pain_mult = jostle_pain_mult,\
|
||||||
|
pain_stam_pct = pain_stam_pct,\
|
||||||
|
embed_chance_turf_mod = embed_chance_turf_mod)
|
||||||
|
|
||||||
|
return TRUE
|
||||||
|
|
||||||
|
/// We need the hit_zone if we're embedding into a human, so this proc only handles if we're embedding into a turf
|
||||||
|
/datum/element/embed/proc/checkEmbedOther(obj/item/weapon, turf/closed/hit, datum/thrownthing/throwingdatum, forced=FALSE)
|
||||||
if(!istype(hit))
|
if(!istype(hit))
|
||||||
return
|
return
|
||||||
|
|
||||||
var/chance = embed_chance
|
var/chance = embed_chance + embed_chance_turf_mod
|
||||||
if(iswallturf(hit))
|
if(iswallturf(hit))
|
||||||
var/turf/closed/wall/W = hit
|
var/turf/closed/wall/W = hit
|
||||||
chance += 2 * (W.hardness - STANDARD_WALL_HARDNESS)
|
chance += 2 * (W.hardness - STANDARD_WALL_HARDNESS)
|
||||||
|
|
||||||
if((((throwingdatum ? throwingdatum.speed : weapon.throw_speed) >= EMBED_THROWSPEED_THRESHOLD) || ignore_throwspeed_threshold) && prob(chance))
|
if(!forced && chance <= 0 || embed_chance_turf_mod <= -100)
|
||||||
hit.AddComponent(/datum/component/embedded,\
|
return
|
||||||
weapon,\
|
|
||||||
throwingdatum,\
|
|
||||||
embed_chance = embed_chance,\
|
|
||||||
fall_chance = fall_chance,\
|
|
||||||
pain_chance = pain_chance,\
|
|
||||||
pain_mult = pain_mult,\
|
|
||||||
remove_pain_mult = remove_pain_mult,\
|
|
||||||
rip_time = rip_time,\
|
|
||||||
ignore_throwspeed_threshold = ignore_throwspeed_threshold,\
|
|
||||||
jostle_chance = jostle_chance,\
|
|
||||||
jostle_pain_mult = jostle_pain_mult,\
|
|
||||||
pain_stam_pct = pain_stam_pct)
|
|
||||||
|
|
||||||
/datum/element/embed/proc/parseArgs(embed_chance = EMBED_CHANCE,
|
var/pass = ((((throwingdatum ? throwingdatum.speed : weapon.throw_speed) >= EMBED_THROWSPEED_THRESHOLD) || ignore_throwspeed_threshold) && prob(chance))
|
||||||
fall_chance = EMBEDDED_ITEM_FALLOUT,
|
if(!pass)
|
||||||
pain_chance = EMBEDDED_PAIN_CHANCE,
|
return
|
||||||
pain_mult = EMBEDDED_PAIN_MULTIPLIER,
|
|
||||||
remove_pain_mult = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER,
|
|
||||||
rip_time = EMBEDDED_UNSAFE_REMOVAL_TIME,
|
|
||||||
impact_pain_mult = EMBEDDED_IMPACT_PAIN_MULTIPLIER,
|
|
||||||
ignore_throwspeed_threshold = FALSE,
|
|
||||||
jostle_chance = EMBEDDED_JOSTLE_CHANCE,
|
|
||||||
jostle_pain_mult = EMBEDDED_JOSTLE_PAIN_MULTIPLIER,
|
|
||||||
pain_stam_pct = EMBEDDED_PAIN_STAM_PCT)
|
|
||||||
|
|
||||||
src.embed_chance = embed_chance
|
hit.AddComponent(/datum/component/embedded,\
|
||||||
src.fall_chance = fall_chance
|
weapon,\
|
||||||
src.pain_chance = pain_chance
|
throwingdatum,\
|
||||||
src.pain_mult = pain_mult
|
embed_chance = embed_chance,\
|
||||||
src.remove_pain_mult = remove_pain_mult
|
fall_chance = fall_chance,\
|
||||||
src.impact_pain_mult = impact_pain_mult
|
pain_chance = pain_chance,\
|
||||||
src.rip_time = rip_time
|
pain_mult = pain_mult,\
|
||||||
src.ignore_throwspeed_threshold = ignore_throwspeed_threshold
|
remove_pain_mult = remove_pain_mult,\
|
||||||
src.jostle_chance = jostle_chance
|
rip_time = rip_time,\
|
||||||
src.jostle_pain_mult = jostle_pain_mult
|
ignore_throwspeed_threshold = ignore_throwspeed_threshold,\
|
||||||
src.pain_stam_pct = pain_stam_pct
|
jostle_chance = jostle_chance,\
|
||||||
|
jostle_pain_mult = jostle_pain_mult,\
|
||||||
|
pain_stam_pct = pain_stam_pct,\
|
||||||
|
embed_chance_turf_mod = embed_chance_turf_mod)
|
||||||
|
|
||||||
|
return TRUE
|
||||||
|
|
||||||
///A different embed element has been attached, so we'll detach and let them handle things
|
///A different embed element has been attached, so we'll detach and let them handle things
|
||||||
/datum/element/embed/proc/severancePackage(obj/item/weapon, datum/element/E)
|
/datum/element/embed/proc/severancePackage(obj/item/weapon, datum/element/E)
|
||||||
if(istype(E, /datum/element/embed))
|
if(istype(E, /datum/element/embed))
|
||||||
Detach(weapon)
|
Detach(weapon)
|
||||||
|
|
||||||
|
///If we don't want to be embeddable anymore (deactivating an e-dagger for instance)
|
||||||
|
/datum/element/embed/proc/detachFromWeapon(obj/weapon)
|
||||||
|
Detach(weapon)
|
||||||
|
|
||||||
|
///Someone inspected our embeddable item
|
||||||
/datum/element/embed/proc/examined(obj/item/I, mob/user, list/examine_list)
|
/datum/element/embed/proc/examined(obj/item/I, mob/user, list/examine_list)
|
||||||
if(!pain_mult && !jostle_pain_mult)
|
if(I.isEmbedHarmless())
|
||||||
examine_list += "[I] feels sticky, and could probably get stuck to someone if thrown properly!"
|
examine_list += "[I] feels sticky, and could probably get stuck to someone if thrown properly!"
|
||||||
else
|
else
|
||||||
examine_list += "[I] has a fine point, and could probably embed in someone if thrown properly!"
|
examine_list += "[I] has a fine point, and could probably embed in someone if thrown properly!"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* checkEmbedProjectile() is what we get when a projectile with a defined shrapnel_type impacts a target.
|
||||||
|
*
|
||||||
|
* If we hit a valid target (carbon or closed turf), we create the shrapnel_type object and immediately call tryEmbed() on it, targeting what we impacted. That will lead
|
||||||
|
* it to call tryForceEmbed() on its own embed element (it's out of our hands here, our projectile is done), where it will run through all the checks it needs to.
|
||||||
|
*/
|
||||||
|
/datum/element/embed/proc/checkEmbedProjectile(obj/projectile/P, atom/movable/firer, atom/hit)
|
||||||
|
if(!iscarbon(hit) && !isclosedturf(hit))
|
||||||
|
Detach(P)
|
||||||
|
return // we don't care
|
||||||
|
|
||||||
|
var/obj/item/payload = new payload_type(get_turf(hit))
|
||||||
|
var/did_embed
|
||||||
|
if(iscarbon(hit))
|
||||||
|
var/mob/living/carbon/C = hit
|
||||||
|
var/obj/item/bodypart/limb = C.get_bodypart(C.check_limb_hit(P.def_zone))
|
||||||
|
did_embed = payload.tryEmbed(limb)
|
||||||
|
else
|
||||||
|
did_embed = payload.tryEmbed(hit)
|
||||||
|
|
||||||
|
if(!did_embed)
|
||||||
|
payload.failedEmbed()
|
||||||
|
Detach(P)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tryForceEmbed() is called here when we fire COMSIG_EMBED_TRY_FORCE from [/obj/item/proc/tryEmbed]. Mostly, this means we're a piece of shrapnel from a projectile that just impacted something, and we're trying to embed in it.
|
||||||
|
*
|
||||||
|
* The reason for this extra mucking about is avoiding having to do an extra hitby(), and annoying the target by impacting them once with the projectile, then again with the shrapnel (which likely represents said bullet), and possibly
|
||||||
|
* AGAIN if we actually embed. This way, we save on at least one message. Runs the standard embed checks on the mob/turf.
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* * I- what we're trying to embed, obviously
|
||||||
|
* * target- what we're trying to shish-kabob, either a bodypart, a carbon, or a closed turf
|
||||||
|
* * hit_zone- if our target is a carbon, try to hit them in this zone, if we don't have one, pick a random one. If our target is a bodypart, we already know where we're hitting.
|
||||||
|
* * forced- if we want this to succeed 100%
|
||||||
|
*/
|
||||||
|
/datum/element/embed/proc/tryForceEmbed(obj/item/I, atom/target, hit_zone, forced=FALSE)
|
||||||
|
var/obj/item/bodypart/limb
|
||||||
|
var/mob/living/carbon/C
|
||||||
|
var/turf/closed/T
|
||||||
|
|
||||||
|
if(!forced && !prob(embed_chance))
|
||||||
|
return
|
||||||
|
|
||||||
|
if(iscarbon(target))
|
||||||
|
C = target
|
||||||
|
if(!hit_zone)
|
||||||
|
limb = pick(C.bodyparts)
|
||||||
|
hit_zone = limb.body_zone
|
||||||
|
else if(isbodypart(target))
|
||||||
|
limb = target
|
||||||
|
C = limb.owner
|
||||||
|
else if(isclosedturf(target))
|
||||||
|
T = target
|
||||||
|
|
||||||
|
if(C)
|
||||||
|
return checkEmbedMob(I, C, hit_zone, forced=TRUE)
|
||||||
|
else if(T)
|
||||||
|
return checkEmbedOther(I, T, forced=TRUE)
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
#define EMBEDID "embed-[embed_chance]-[fall_chance]-[pain_chance]-[pain_mult]-[fall_pain_mult]-[impact_pain_mult]-[rip_pain_mult]-[rip_time]-[ignore_throwspeed_threshold]-[jostle_chance]-[jostle_pain_mult]-[pain_stam_pct]"
|
|
||||||
|
|
||||||
/proc/getEmbeddingBehavior(embed_chance = EMBED_CHANCE,
|
|
||||||
fall_chance = EMBEDDED_ITEM_FALLOUT,
|
|
||||||
pain_chance = EMBEDDED_PAIN_CHANCE,
|
|
||||||
pain_mult = EMBEDDED_PAIN_MULTIPLIER,
|
|
||||||
fall_pain_mult = EMBEDDED_FALL_PAIN_MULTIPLIER,
|
|
||||||
impact_pain_mult = EMBEDDED_IMPACT_PAIN_MULTIPLIER,
|
|
||||||
rip_pain_mult = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER,
|
|
||||||
rip_time = EMBEDDED_UNSAFE_REMOVAL_TIME,
|
|
||||||
ignore_throwspeed_threshold = FALSE,
|
|
||||||
jostle_chance = EMBEDDED_JOSTLE_CHANCE,
|
|
||||||
jostle_pain_mult = EMBEDDED_JOSTLE_PAIN_MULTIPLIER,
|
|
||||||
pain_stam_pct = EMBEDDED_PAIN_STAM_PCT)
|
|
||||||
. = locate(EMBEDID)
|
|
||||||
if (!.)
|
|
||||||
. = new /datum/embedding_behavior(embed_chance, fall_chance, pain_chance, pain_mult, fall_pain_mult, impact_pain_mult, rip_pain_mult, rip_time, ignore_throwspeed_threshold, jostle_chance, jostle_pain_mult, pain_stam_pct)
|
|
||||||
|
|
||||||
/datum/embedding_behavior
|
|
||||||
var/embed_chance
|
|
||||||
var/fall_chance
|
|
||||||
var/pain_chance
|
|
||||||
var/pain_mult //The coefficient of multiplication for the damage this item does while embedded (this*w_class)
|
|
||||||
var/fall_pain_mult //The coefficient of multiplication for the damage this item does when falling out of a limb (this*w_class)
|
|
||||||
var/impact_pain_mult //The coefficient of multiplication for the damage this item does when first embedded (this*w_class)
|
|
||||||
var/rip_pain_mult //The coefficient of multiplication for the damage removing this without surgery causes (this*w_class)
|
|
||||||
var/rip_time //A time in ticks, multiplied by the w_class.
|
|
||||||
var/ignore_throwspeed_threshold //if we don't give a damn about EMBED_THROWSPEED_THRESHOLD
|
|
||||||
var/jostle_chance //Chance to cause pain every time the victim moves (1/2 chance if they're walking or crawling)
|
|
||||||
var/jostle_pain_mult //The coefficient of multiplication for the damage when jostle damage is applied (this*w_class)
|
|
||||||
var/pain_stam_pct //Percentage of all pain damage dealt as stamina instead of brute (none by default)
|
|
||||||
|
|
||||||
/datum/embedding_behavior/New(embed_chance = EMBED_CHANCE,
|
|
||||||
fall_chance = EMBEDDED_ITEM_FALLOUT,
|
|
||||||
pain_chance = EMBEDDED_PAIN_CHANCE,
|
|
||||||
pain_mult = EMBEDDED_PAIN_MULTIPLIER,
|
|
||||||
fall_pain_mult = EMBEDDED_FALL_PAIN_MULTIPLIER,
|
|
||||||
impact_pain_mult = EMBEDDED_IMPACT_PAIN_MULTIPLIER,
|
|
||||||
rip_pain_mult = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER,
|
|
||||||
rip_time = EMBEDDED_UNSAFE_REMOVAL_TIME,
|
|
||||||
ignore_throwspeed_threshold = FALSE,
|
|
||||||
jostle_chance = EMBEDDED_JOSTLE_CHANCE,
|
|
||||||
jostle_pain_mult = EMBEDDED_JOSTLE_PAIN_MULTIPLIER,
|
|
||||||
pain_stam_pct = EMBEDDED_PAIN_STAM_PCT)
|
|
||||||
src.embed_chance = embed_chance
|
|
||||||
src.fall_chance = fall_chance
|
|
||||||
src.pain_chance = pain_chance
|
|
||||||
src.pain_mult = pain_mult
|
|
||||||
src.fall_pain_mult = fall_pain_mult
|
|
||||||
src.impact_pain_mult = impact_pain_mult
|
|
||||||
src.rip_pain_mult = rip_pain_mult
|
|
||||||
src.rip_time = rip_time
|
|
||||||
src.ignore_throwspeed_threshold = ignore_throwspeed_threshold
|
|
||||||
src.jostle_chance = jostle_chance
|
|
||||||
src.jostle_pain_mult = jostle_pain_mult
|
|
||||||
src.pain_stam_pct = pain_stam_pct
|
|
||||||
tag = EMBEDID
|
|
||||||
/datum/embedding_behavior/proc/setRating(embed_chance, fall_chance, pain_chance, pain_mult, fall_pain_mult, impact_pain_mult, rip_pain_mult, rip_time, ignore_throwspeed_threshold)
|
|
||||||
return getEmbeddingBehavior((isnull(embed_chance) ? src.embed_chance : embed_chance),\
|
|
||||||
(isnull(fall_chance) ? src.fall_chance : fall_chance),\
|
|
||||||
(isnull(pain_chance) ? src.pain_chance : pain_chance),\
|
|
||||||
(isnull(pain_mult) ? src.pain_mult : pain_mult),\
|
|
||||||
(isnull(fall_pain_mult) ? src.fall_pain_mult : fall_pain_mult),\
|
|
||||||
(isnull(impact_pain_mult) ? src.impact_pain_mult : impact_pain_mult),\
|
|
||||||
(isnull(rip_pain_mult) ? src.rip_pain_mult : rip_pain_mult),\
|
|
||||||
(isnull(rip_time) ? src.rip_time : rip_time),\
|
|
||||||
(isnull(ignore_throwspeed_threshold) ? src.ignore_throwspeed_threshold : ignore_throwspeed_threshold),\
|
|
||||||
(isnull(jostle_chance) ? src.jostle_chance : jostle_chance),\
|
|
||||||
(isnull(jostle_pain_mult) ? src.jostle_pain_mult : jostle_pain_mult),\
|
|
||||||
(isnull(pain_stam_pct) ? src.pain_stam_pct : pain_stam_pct))
|
|
||||||
#undef EMBEDID
|
|
||||||
@@ -490,11 +490,7 @@
|
|||||||
|
|
||||||
var/obj/item/bodypart/L = spikey.checkembedded()
|
var/obj/item/bodypart/L = spikey.checkembedded()
|
||||||
|
|
||||||
L.embedded_objects -= spikey
|
|
||||||
//this is where it would deal damage, if it transfers chems it removes itself so no damage
|
//this is where it would deal damage, if it transfers chems it removes itself so no damage
|
||||||
spikey.forceMove(get_turf(L))
|
spikey.forceMove(get_turf(L))
|
||||||
transfered.visible_message("<span class='notice'>[spikey] falls out of [transfered]!</span>")
|
transfered.visible_message("<span class='notice'>[spikey] falls out of [transfered]!</span>")
|
||||||
if(!transfered.has_embedded_objects())
|
|
||||||
transfered.clear_alert("embeddedobject")
|
|
||||||
SEND_SIGNAL(transfered, COMSIG_CLEAR_MOOD_EVENT, "embedded")
|
|
||||||
spikey.unembedded()
|
|
||||||
|
|||||||
@@ -6,6 +6,14 @@
|
|||||||
|
|
||||||
var/flags_1 = NONE
|
var/flags_1 = NONE
|
||||||
var/interaction_flags_atom = NONE
|
var/interaction_flags_atom = NONE
|
||||||
|
|
||||||
|
var/flags_ricochet = NONE
|
||||||
|
|
||||||
|
///When a projectile tries to ricochet off this atom, the projectile ricochet chance is multiplied by this
|
||||||
|
var/ricochet_chance_mod = 1
|
||||||
|
///When a projectile ricochets off this atom, it deals the normal damage * this modifier to this atom
|
||||||
|
var/ricochet_damage_mod = 0.33
|
||||||
|
|
||||||
var/datum/reagents/reagents = null
|
var/datum/reagents/reagents = null
|
||||||
|
|
||||||
//This atom's HUD (med/sec, etc) images. Associative list.
|
//This atom's HUD (med/sec, etc) images. Associative list.
|
||||||
@@ -142,7 +150,19 @@
|
|||||||
return ..()
|
return ..()
|
||||||
|
|
||||||
/atom/proc/handle_ricochet(obj/item/projectile/P)
|
/atom/proc/handle_ricochet(obj/item/projectile/P)
|
||||||
return
|
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))
|
||||||
|
var/a_incidence_s = abs(incidence_s)
|
||||||
|
if(a_incidence_s > 90 && a_incidence_s < 270)
|
||||||
|
return FALSE
|
||||||
|
if((P.flag in list("bullet", "bomb")) && P.ricochet_incidence_leeway)
|
||||||
|
if((a_incidence_s < 90 && a_incidence_s < 90 - P.ricochet_incidence_leeway) || (a_incidence_s > 270 && a_incidence_s -270 > P.ricochet_incidence_leeway))
|
||||||
|
return
|
||||||
|
var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s)
|
||||||
|
P.setAngle(new_angle_s)
|
||||||
|
return TRUE
|
||||||
|
|
||||||
/atom/proc/CanPass(atom/movable/mover, turf/target)
|
/atom/proc/CanPass(atom/movable/mover, turf/target)
|
||||||
return !density
|
return !density
|
||||||
|
|||||||
@@ -92,6 +92,8 @@ Class Procs:
|
|||||||
pressure_resistance = 15
|
pressure_resistance = 15
|
||||||
max_integrity = 200
|
max_integrity = 200
|
||||||
layer = BELOW_OBJ_LAYER //keeps shit coming out of the machine from ending up underneath it.
|
layer = BELOW_OBJ_LAYER //keeps shit coming out of the machine from ending up underneath it.
|
||||||
|
flags_ricochet = RICOCHET_HARD
|
||||||
|
ricochet_chance_mod = 0.3
|
||||||
|
|
||||||
anchored = TRUE
|
anchored = TRUE
|
||||||
interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT
|
interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT
|
||||||
|
|||||||
@@ -170,6 +170,7 @@
|
|||||||
to_chat(user, "[src] is now in [mode] mode.")
|
to_chat(user, "[src] is now in [mode] mode.")
|
||||||
|
|
||||||
/obj/item/grenade/barrier/prime()
|
/obj/item/grenade/barrier/prime()
|
||||||
|
. = ..()
|
||||||
new /obj/structure/barricade/security(get_turf(src.loc))
|
new /obj/structure/barricade/security(get_turf(src.loc))
|
||||||
switch(mode)
|
switch(mode)
|
||||||
if(VERTICAL)
|
if(VERTICAL)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 70)
|
armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 70)
|
||||||
CanAtmosPass = ATMOS_PASS_DENSITY
|
CanAtmosPass = ATMOS_PASS_DENSITY
|
||||||
flags_1 = PREVENT_CLICK_UNDER_1
|
flags_1 = PREVENT_CLICK_UNDER_1
|
||||||
|
ricochet_chance_mod = 0.8
|
||||||
|
|
||||||
interaction_flags_atom = INTERACT_ATOM_UI_INTERACT
|
interaction_flags_atom = INTERACT_ATOM_UI_INTERACT
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
s.set_up(3, 1, src)
|
s.set_up(3, 1, src)
|
||||||
s.start()
|
s.start()
|
||||||
mineEffect(victim)
|
mineEffect(victim)
|
||||||
|
SEND_SIGNAL(src, COMSIG_MINE_TRIGGERED)
|
||||||
triggered = 1
|
triggered = 1
|
||||||
qdel(src)
|
qdel(src)
|
||||||
|
|
||||||
@@ -50,6 +51,18 @@
|
|||||||
if(isliving(victim))
|
if(isliving(victim))
|
||||||
victim.DefaultCombatKnockdown(stun_time)
|
victim.DefaultCombatKnockdown(stun_time)
|
||||||
|
|
||||||
|
/obj/effect/mine/shrapnel
|
||||||
|
name = "shrapnel mine"
|
||||||
|
var/shrapnel_type = /obj/projectile/bullet/shrapnel
|
||||||
|
var/shrapnel_magnitude = 3
|
||||||
|
|
||||||
|
/obj/effect/mine/shrapnel/mineEffect(mob/victim)
|
||||||
|
AddComponent(/datum/component/pellet_cloud, projectile_type=shrapnel_type, magnitude=shrapnel_magnitude)
|
||||||
|
|
||||||
|
/obj/effect/mine/shrapnel/sting
|
||||||
|
name = "stinger mine"
|
||||||
|
shrapnel_type = /obj/projectile/bullet/pellet/stingball
|
||||||
|
|
||||||
/obj/effect/mine/kickmine
|
/obj/effect/mine/kickmine
|
||||||
name = "kick mine"
|
name = "kick mine"
|
||||||
|
|
||||||
|
|||||||
@@ -185,16 +185,15 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
|||||||
. = ..()
|
. = ..()
|
||||||
|
|
||||||
// this proc says it's for initializing components, but we're initializing elements too because it's you and me against the world >:)
|
// this proc says it's for initializing components, but we're initializing elements too because it's you and me against the world >:)
|
||||||
if(embedding)
|
if(!LAZYLEN(embedding))
|
||||||
AddElement(/datum/element/embed, embedding)
|
if(GLOB.embedpocalypse)
|
||||||
else if(GLOB.embedpocalypse)
|
embedding = EMBED_POINTY
|
||||||
embedding = EMBED_POINTY
|
name = "pointy [name]"
|
||||||
AddElement(/datum/element/embed, embedding)
|
else if(GLOB.stickpocalypse)
|
||||||
name = "pointy [name]"
|
embedding = EMBED_HARMLESS
|
||||||
else if(GLOB.stickpocalypse)
|
name = "sticky [name]"
|
||||||
embedding = EMBED_HARMLESS
|
|
||||||
AddElement(/datum/element/embed, embedding)
|
updateEmbedding()
|
||||||
name = "sticky [name]"
|
|
||||||
|
|
||||||
if(GLOB.rpg_loot_items)
|
if(GLOB.rpg_loot_items)
|
||||||
AddComponent(/datum/component/fantasy)
|
AddComponent(/datum/component/fantasy)
|
||||||
@@ -923,7 +922,9 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
|||||||
return
|
return
|
||||||
|
|
||||||
/obj/item/proc/unembedded()
|
/obj/item/proc/unembedded()
|
||||||
return
|
if(item_flags & DROPDEL)
|
||||||
|
QDEL_NULL(src)
|
||||||
|
return TRUE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets our slowdown and updates equipment slowdown of any mob we're equipped on.
|
* Sets our slowdown and updates equipment slowdown of any mob we're equipped on.
|
||||||
@@ -940,10 +941,132 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
|||||||
if(var_name == NAMEOF(src, slowdown))
|
if(var_name == NAMEOF(src, slowdown))
|
||||||
set_slowdown(var_value) //don't care if it's a duplicate edit as slowdown'll be set, do it anyways to force normal behavior.
|
set_slowdown(var_value) //don't care if it's a duplicate edit as slowdown'll be set, do it anyways to force normal behavior.
|
||||||
|
|
||||||
/**
|
///Does the current embedding var meet the criteria for being harmless? Namely, does it have a pain multiplier and jostle pain mult of 0? If so, return true.
|
||||||
* Does the current embedding var meet the criteria for being harmless? Namely, does it have a pain multiplier and jostle pain mult of 0? If so, return true.
|
/obj/item/proc/isEmbedHarmless()
|
||||||
*
|
|
||||||
*/
|
|
||||||
/obj/item/proc/is_embed_harmless()
|
|
||||||
if(embedding)
|
if(embedding)
|
||||||
return (!embedding["pain_mult"] && !embedding["jostle_pain_mult"])
|
return !isnull(embedding["pain_mult"]) && !isnull(embedding["jostle_pain_mult"]) && embedding["pain_mult"] == 0 && embedding["jostle_pain_mult"] == 0
|
||||||
|
|
||||||
|
///In case we want to do something special (like self delete) upon failing to embed in something, return true
|
||||||
|
/obj/item/proc/failedEmbed()
|
||||||
|
if(item_flags & DROPDEL)
|
||||||
|
QDEL_NULL(src)
|
||||||
|
return TRUE
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* tryEmbed() is for when you want to try embedding something without dealing with the damage + hit messages of calling hitby() on the item while targetting the target.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* Really, this is used mostly with projectiles with shrapnel payloads, from [/datum/element/embed/proc/checkEmbedProjectile], and called on said shrapnel. Mostly acts as an intermediate between different embed elements.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* Arguments:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* * target- Either a body part, a carbon, or a closed turf. What are we hitting?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* * forced- Do we want this to go through 100%?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/obj/item/proc/tryEmbed(atom/target, forced=FALSE, silent=FALSE)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(!isbodypart(target) && !iscarbon(target) && !isclosedturf(target))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(!forced && !LAZYLEN(embedding))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(SEND_SIGNAL(src, COMSIG_EMBED_TRY_FORCE, target, forced, silent))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return TRUE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
failedEmbed()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///For when you want to disable an item's embedding capabilities (like transforming weapons and such), this proc will detach any active embed elements from it.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/obj/item/proc/disableEmbedding()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SEND_SIGNAL(src, COMSIG_ITEM_DISABLE_EMBED)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///For when you want to add/update the embedding on an item. Uses the vars in [/obj/item/embedding], and defaults to config values for values that aren't set. Will automatically detach previous embed elements on this item.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/obj/item/proc/updateEmbedding()
|
||||||
|
if(!islist(embedding) || !LAZYLEN(embedding))
|
||||||
|
return
|
||||||
|
|
||||||
|
AddElement(/datum/element/embed,\
|
||||||
|
embed_chance = (!isnull(embedding["embed_chance"]) ? embedding["embed_chance"] : EMBED_CHANCE),\
|
||||||
|
fall_chance = (!isnull(embedding["fall_chance"]) ? embedding["fall_chance"] : EMBEDDED_ITEM_FALLOUT),\
|
||||||
|
pain_chance = (!isnull(embedding["pain_chance"]) ? embedding["pain_chance"] : EMBEDDED_PAIN_CHANCE),\
|
||||||
|
pain_mult = (!isnull(embedding["pain_mult"]) ? embedding["pain_mult"] : EMBEDDED_PAIN_MULTIPLIER),\
|
||||||
|
remove_pain_mult = (!isnull(embedding["remove_pain_mult"]) ? embedding["remove_pain_mult"] : EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER),\
|
||||||
|
rip_time = (!isnull(embedding["rip_time"]) ? embedding["rip_time"] : EMBEDDED_UNSAFE_REMOVAL_TIME),\
|
||||||
|
ignore_throwspeed_threshold = (!isnull(embedding["ignore_throwspeed_threshold"]) ? embedding["ignore_throwspeed_threshold"] : FALSE),\
|
||||||
|
impact_pain_mult = (!isnull(embedding["impact_pain_mult"]) ? embedding["impact_pain_mult"] : EMBEDDED_IMPACT_PAIN_MULTIPLIER),\
|
||||||
|
jostle_chance = (!isnull(embedding["jostle_chance"]) ? embedding["jostle_chance"] : EMBEDDED_JOSTLE_CHANCE),\
|
||||||
|
jostle_pain_mult = (!isnull(embedding["jostle_pain_mult"]) ? embedding["jostle_pain_mult"] : EMBEDDED_JOSTLE_PAIN_MULTIPLIER),\
|
||||||
|
pain_stam_pct = (!isnull(embedding["pain_stam_pct"]) ? embedding["pain_stam_pct"] : EMBEDDED_PAIN_STAM_PCT),\
|
||||||
|
embed_chance_turf_mod = (!isnull(embedding["embed_chance_turf_mod"]) ? embedding["embed_chance_turf_mod"] : EMBED_CHANCE_TURF_MOD))
|
||||||
|
return TRUE
|
||||||
@@ -8,10 +8,11 @@
|
|||||||
var/duration = 300
|
var/duration = 300
|
||||||
|
|
||||||
/obj/item/grenade/antigravity/prime()
|
/obj/item/grenade/antigravity/prime()
|
||||||
|
. = ..()
|
||||||
update_mob()
|
update_mob()
|
||||||
|
|
||||||
for(var/turf/T in view(range,src))
|
for(var/turf/T in view(range,src))
|
||||||
T.AddElement(/datum/element/forced_gravity, forced_value)
|
T.AddElement(/datum/element/forced_gravity, forced_value)
|
||||||
addtimer(CALLBACK(T, /datum/.proc/_RemoveElement, list(forced_value)), duration)
|
addtimer(CALLBACK(T, /datum/.proc/_RemoveElement, list(forced_value)), duration)
|
||||||
|
|
||||||
qdel(src)
|
resolve()
|
||||||
|
|||||||
@@ -178,6 +178,7 @@
|
|||||||
if(stage != READY)
|
if(stage != READY)
|
||||||
return FALSE
|
return FALSE
|
||||||
|
|
||||||
|
. = ..()
|
||||||
var/list/datum/reagents/reactants = list()
|
var/list/datum/reagents/reactants = list()
|
||||||
for(var/obj/item/reagent_containers/glass/G in beakers)
|
for(var/obj/item/reagent_containers/glass/G in beakers)
|
||||||
reactants += G.reagents
|
reactants += G.reagents
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
var/segment_chance = 35
|
var/segment_chance = 35
|
||||||
|
|
||||||
/obj/item/grenade/clusterbuster/prime()
|
/obj/item/grenade/clusterbuster/prime()
|
||||||
|
. = ..()
|
||||||
update_mob()
|
update_mob()
|
||||||
var/numspawned = rand(min_spawned,max_spawned)
|
var/numspawned = rand(min_spawned,max_spawned)
|
||||||
var/again = 0
|
var/again = 0
|
||||||
@@ -29,7 +30,7 @@
|
|||||||
|
|
||||||
new payload_spawner(drop_location(), payload, numspawned)//Launches payload
|
new payload_spawner(drop_location(), payload, numspawned)//Launches payload
|
||||||
playsound(src, prime_sound, 75, 1, -3)
|
playsound(src, prime_sound, 75, 1, -3)
|
||||||
qdel(src)
|
resolve()
|
||||||
|
|
||||||
//////////////////////
|
//////////////////////
|
||||||
//Clusterbang segment
|
//Clusterbang segment
|
||||||
@@ -62,7 +63,7 @@
|
|||||||
/obj/item/grenade/clusterbuster/segment/prime()
|
/obj/item/grenade/clusterbuster/segment/prime()
|
||||||
new payload_spawner(drop_location(), payload, rand(min_spawned,max_spawned))
|
new payload_spawner(drop_location(), payload, rand(min_spawned,max_spawned))
|
||||||
playsound(src, prime_sound, 75, 1, -3)
|
playsound(src, prime_sound, 75, 1, -3)
|
||||||
qdel(src)
|
resolve()
|
||||||
|
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
//The payload spawner effect
|
//The payload spawner effect
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
item_state = "emp"
|
item_state = "emp"
|
||||||
|
|
||||||
/obj/item/grenade/empgrenade/prime()
|
/obj/item/grenade/empgrenade/prime()
|
||||||
|
. = ..()
|
||||||
update_mob()
|
update_mob()
|
||||||
empulse(src, 4, 10)
|
empulse(src, 4, 10)
|
||||||
qdel(src)
|
resolve()
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
var/flashbang_range = 7 //how many tiles away the mob will be stunned.
|
var/flashbang_range = 7 //how many tiles away the mob will be stunned.
|
||||||
|
|
||||||
/obj/item/grenade/flashbang/prime()
|
/obj/item/grenade/flashbang/prime()
|
||||||
|
. = ..()
|
||||||
update_mob()
|
update_mob()
|
||||||
var/flashbang_turf = get_turf(src)
|
var/flashbang_turf = get_turf(src)
|
||||||
if(!flashbang_turf)
|
if(!flashbang_turf)
|
||||||
@@ -15,7 +16,7 @@
|
|||||||
playsound(flashbang_turf, 'sound/weapons/flashbang.ogg', 100, TRUE, 8, 0.9)
|
playsound(flashbang_turf, 'sound/weapons/flashbang.ogg', 100, TRUE, 8, 0.9)
|
||||||
new /obj/effect/dummy/lighting_obj (flashbang_turf, LIGHT_COLOR_WHITE, (flashbang_range + 2), 4, 2)
|
new /obj/effect/dummy/lighting_obj (flashbang_turf, LIGHT_COLOR_WHITE, (flashbang_range + 2), 4, 2)
|
||||||
flashbang_mobs(flashbang_turf, flashbang_range)
|
flashbang_mobs(flashbang_turf, flashbang_range)
|
||||||
qdel(src)
|
resolve()
|
||||||
|
|
||||||
/obj/item/grenade/flashbang/proc/flashbang_mobs(turf/source, range)
|
/obj/item/grenade/flashbang/proc/flashbang_mobs(turf/source, range)
|
||||||
var/list/banged = get_hearers_in_view(range, source)
|
var/list/banged = get_hearers_in_view(range, source)
|
||||||
@@ -42,3 +43,194 @@
|
|||||||
var/distance = get_dist(get_turf(M), source)
|
var/distance = get_dist(get_turf(M), source)
|
||||||
if(M.flash_act(affect_silicon = 1))
|
if(M.flash_act(affect_silicon = 1))
|
||||||
M.DefaultCombatKnockdown(max(200/max(1,distance), 60))
|
M.DefaultCombatKnockdown(max(200/max(1,distance), 60))
|
||||||
|
|
||||||
|
/obj/item/grenade/stingbang
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
name = "stingbang"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
icon_state = "timeg"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
item_state = "flashbang"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var/flashbang_range = 1 //how many tiles away the mob will be stunned.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
shrapnel_type = /obj/projectile/bullet/pellet/stingball
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
shrapnel_radius = 5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
custom_premium_price = 700 // mostly gotten through cargo, but throw in one for the sec vendor ;)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/obj/item/grenade/stingbang/mega
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
name = "mega stingbang"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
shrapnel_type = /obj/projectile/bullet/pellet/stingball/mega
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
shrapnel_radius = 12
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/obj/item/grenade/stingbang/prime()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(iscarbon(loc))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var/mob/living/carbon/C = loc
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var/obj/item/bodypart/B = C.get_holding_bodypart_of_item(src)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(B)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
C.visible_message("<b><span class='danger'>[src] goes off in [C]'s hand, blowing [C.p_their()] [B.name] to bloody shreds!</span></b>", "<span class='userdanger'>[src] goes off in your hand, blowing your [B.name] to bloody shreds!</span>")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
B.dismember()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
. = ..()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
update_mob()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var/flashbang_turf = get_turf(src)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(!flashbang_turf)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
do_sparks(rand(5, 9), FALSE, src)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
playsound(flashbang_turf, 'sound/weapons/flashbang.ogg', 50, TRUE, 8, 0.9)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
new /obj/effect/dummy/lighting_obj (flashbang_turf, LIGHT_COLOR_WHITE, (flashbang_range + 2), 2, 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for(var/mob/living/M in get_hearers_in_view(flashbang_range, flashbang_turf))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pop(get_turf(M), M)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
resolve()
|
||||||
|
|
||||||
|
/obj/item/grenade/stingbang/proc/pop(turf/T , mob/living/M)
|
||||||
|
if(M.stat == DEAD) //They're dead!
|
||||||
|
return
|
||||||
|
M.show_message("<span class='warning'>POP</span>", MSG_AUDIBLE)
|
||||||
|
var/distance = max(0,get_dist(get_turf(src),T))
|
||||||
|
|
||||||
|
//Flash
|
||||||
|
if(M.flash_act(affect_silicon = 1))
|
||||||
|
M.Paralyze(max(10/max(1,distance), 5))
|
||||||
|
M.Knockdown(max(100/max(1,distance), 60))
|
||||||
|
|
||||||
|
//Bang
|
||||||
|
if(!distance || loc == M || loc == M.loc) //Stop allahu akbarring rooms with this.
|
||||||
|
M.Paralyze(20)
|
||||||
|
M.Knockdown(200)
|
||||||
|
M.soundbang_act(1, 200, 10, 15)
|
||||||
|
if(M.apply_damages(10, 10))
|
||||||
|
to_chat(M, "<span class='userdanger'>The blast from \the [src] bruises and burns you!</span>")
|
||||||
|
|
||||||
|
// only checking if they're on top of the tile, cause being one tile over will be its own punishment
|
||||||
|
|
||||||
|
// Grenade that releases more shrapnel the more times you use it in hand between priming and detonation (sorta like the 9bang from MW3), for admin goofs
|
||||||
|
/obj/item/grenade/primer
|
||||||
|
name = "rotfrag grenade"
|
||||||
|
desc = "A grenade that generates more shrapnel the more you rotate it in your hand after pulling the pin. This one releases shrapnel shards."
|
||||||
|
icon_state = "timeg"
|
||||||
|
item_state = "flashbang"
|
||||||
|
lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
|
||||||
|
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
|
||||||
|
var/rots_per_mag = 3 /// how many times we need to "rotate" the charge in hand per extra tile of magnitude
|
||||||
|
shrapnel_type = /obj/projectile/bullet/shrapnel
|
||||||
|
var/rots = 1 /// how many times we've "rotated" the charge
|
||||||
|
|
||||||
|
/obj/item/grenade/primer/attack_self(mob/user)
|
||||||
|
. = ..()
|
||||||
|
if(active)
|
||||||
|
user.playsound_local(user, 'sound/misc/box_deploy.ogg', 50, TRUE)
|
||||||
|
rots++
|
||||||
|
user.changeNext_move(CLICK_CD_RAPID)
|
||||||
|
|
||||||
|
/obj/item/grenade/primer/prime()
|
||||||
|
shrapnel_radius = round(rots / rots_per_mag)
|
||||||
|
. = ..()
|
||||||
|
resolve()
|
||||||
|
|
||||||
|
/obj/item/grenade/primer/stingbang
|
||||||
|
name = "rotsting"
|
||||||
|
desc = "A grenade that generates more shrapnel the more you rotate it in your hand after pulling the pin. This one releases stingballs."
|
||||||
|
lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
|
||||||
|
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
|
||||||
|
rots_per_mag = 2
|
||||||
|
shrapnel_type = /obj/projectile/bullet/pellet/stingball
|
||||||
|
|||||||
@@ -51,9 +51,10 @@
|
|||||||
preprime(user, null, FALSE)
|
preprime(user, null, FALSE)
|
||||||
|
|
||||||
/obj/item/grenade/iedcasing/prime() //Blowing that can up
|
/obj/item/grenade/iedcasing/prime() //Blowing that can up
|
||||||
|
. = ..()
|
||||||
update_mob()
|
update_mob()
|
||||||
explosion(src.loc,-1,-1,2, flame_range = 4) // small explosion, plus a very large fireball.
|
explosion(src.loc,-1,-1,2, flame_range = 4) // small explosion, plus a very large fireball.
|
||||||
qdel(src)
|
resolve()
|
||||||
|
|
||||||
/obj/item/grenade/iedcasing/examine(mob/user)
|
/obj/item/grenade/iedcasing/examine(mob/user)
|
||||||
. = ..()
|
. = ..()
|
||||||
|
|||||||
@@ -18,10 +18,30 @@
|
|||||||
var/display_timer = 1
|
var/display_timer = 1
|
||||||
var/clumsy_check = GRENADE_CLUMSY_FUMBLE
|
var/clumsy_check = GRENADE_CLUMSY_FUMBLE
|
||||||
var/sticky = FALSE
|
var/sticky = FALSE
|
||||||
|
// I moved the explosion vars and behavior to base grenades because we want all grenades to call [/obj/item/grenade/proc/prime] so we can send COMSIG_GRENADE_PRIME
|
||||||
|
///how big of a devastation explosion radius on prime
|
||||||
|
var/ex_dev = 0
|
||||||
|
///how big of a heavy explosion radius on prime
|
||||||
|
var/ex_heavy = 0
|
||||||
|
///how big of a light explosion radius on prime
|
||||||
|
var/ex_light = 0
|
||||||
|
///how big of a flame explosion radius on prime
|
||||||
|
var/ex_flame = 0
|
||||||
|
|
||||||
|
// dealing with creating a [/datum/component/pellet_cloud] on prime
|
||||||
|
/// if set, will spew out projectiles of this type
|
||||||
|
var/shrapnel_type
|
||||||
|
/// the higher this number, the more projectiles are created as shrapnel
|
||||||
|
var/shrapnel_radius
|
||||||
|
var/shrapnel_initialized
|
||||||
|
|
||||||
/obj/item/grenade/suicide_act(mob/living/carbon/user)
|
/obj/item/grenade/suicide_act(mob/living/carbon/user)
|
||||||
user.visible_message("<span class='suicide'>[user] primes [src], then eats it! It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
user.visible_message("<span class='suicide'>[user] primes [src], then eats it! It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
||||||
|
if(shrapnel_type && shrapnel_radius)
|
||||||
|
shrapnel_initialized = TRUE
|
||||||
|
AddComponent(/datum/component/pellet_cloud, projectile_type=shrapnel_type, magnitude=shrapnel_radius)
|
||||||
playsound(src, 'sound/items/eatfood.ogg', 50, 1)
|
playsound(src, 'sound/items/eatfood.ogg', 50, 1)
|
||||||
|
SEND_SIGNAL(src, COMSIG_GRENADE_ARMED, det_time, delayoverride)
|
||||||
preprime(user, det_time)
|
preprime(user, det_time)
|
||||||
user.transferItemToLoc(src, user, TRUE)//>eat a grenade set to 5 seconds >rush captain
|
user.transferItemToLoc(src, user, TRUE)//>eat a grenade set to 5 seconds >rush captain
|
||||||
sleep(det_time)//so you dont die instantly
|
sleep(det_time)//so you dont die instantly
|
||||||
@@ -96,6 +116,14 @@
|
|||||||
var/turf/T = get_turf(src)
|
var/turf/T = get_turf(src)
|
||||||
log_game("Grenade detonation at [AREACOORD(T)], location [loc]")
|
log_game("Grenade detonation at [AREACOORD(T)], location [loc]")
|
||||||
|
|
||||||
|
if(shrapnel_type && shrapnel_radius && !shrapnel_initialized) // add a second check for adding the component in case whatever triggered the grenade went straight to prime (badminnery for example)
|
||||||
|
shrapnel_initialized = TRUE
|
||||||
|
AddComponent(/datum/component/pellet_cloud, projectile_type=shrapnel_type, magnitude=shrapnel_radius)
|
||||||
|
|
||||||
|
SEND_SIGNAL(src, COMSIG_GRENADE_PRIME)
|
||||||
|
if(ex_dev || ex_heavy || ex_light || ex_flame)
|
||||||
|
explosion(loc, ex_dev, ex_heavy, ex_light, flame_range = ex_flame)
|
||||||
|
|
||||||
/obj/item/grenade/proc/update_mob()
|
/obj/item/grenade/proc/update_mob()
|
||||||
if(ismob(loc))
|
if(ismob(loc))
|
||||||
var/mob/M = loc
|
var/mob/M = loc
|
||||||
@@ -137,3 +165,13 @@
|
|||||||
|
|
||||||
/obj/item/proc/grenade_prime_react(obj/item/grenade/nade)
|
/obj/item/proc/grenade_prime_react(obj/item/grenade/nade)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
/// Don't call qdel() directly on the grenade after it booms, call this instead so it can still resolve its pellet_cloud component if it has shrapnel, then the component will qdel it
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/obj/item/grenade/proc/resolve()
|
||||||
|
if(shrapnel_type)
|
||||||
|
moveToNullspace()
|
||||||
|
else
|
||||||
|
qdel(src)
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
I.throw_range = max(1, (I.throw_range - 3))
|
I.throw_range = max(1, (I.throw_range - 3))
|
||||||
if(I.embedding)
|
if(I.embedding)
|
||||||
I.embedding["embed_chance"] = 0
|
I.embedding["embed_chance"] = 0
|
||||||
I.AddElement(/datum/element/embed, I.embedding)
|
I.updateEmbedding()
|
||||||
|
|
||||||
target.add_overlay(plastic_overlay, TRUE)
|
target.add_overlay(plastic_overlay, TRUE)
|
||||||
if(!nadeassembly)
|
if(!nadeassembly)
|
||||||
@@ -158,7 +158,7 @@
|
|||||||
shout_syndicate_crap(user)
|
shout_syndicate_crap(user)
|
||||||
explosion(user,0,2,0) //Cheap explosion imitation because putting prime() here causes runtimes
|
explosion(user,0,2,0) //Cheap explosion imitation because putting prime() here causes runtimes
|
||||||
user.gib(1, 1)
|
user.gib(1, 1)
|
||||||
qdel(src)
|
resolve()
|
||||||
|
|
||||||
/obj/item/grenade/plastic/update_icon_state()
|
/obj/item/grenade/plastic/update_icon_state()
|
||||||
if(nadeassembly)
|
if(nadeassembly)
|
||||||
@@ -210,6 +210,7 @@
|
|||||||
/obj/item/grenade/plastic/c4/prime()
|
/obj/item/grenade/plastic/c4/prime()
|
||||||
if(QDELETED(src))
|
if(QDELETED(src))
|
||||||
return
|
return
|
||||||
|
. = ..()
|
||||||
var/turf/location
|
var/turf/location
|
||||||
if(target)
|
if(target)
|
||||||
if(!QDELETED(target))
|
if(!QDELETED(target))
|
||||||
@@ -221,7 +222,7 @@
|
|||||||
location = get_turf(src)
|
location = get_turf(src)
|
||||||
if(location)
|
if(location)
|
||||||
explosion(location,0,0,3)
|
explosion(location,0,0,3)
|
||||||
qdel(src)
|
resolve()
|
||||||
|
|
||||||
/obj/item/grenade/plastic/c4/attack(mob/M, mob/user, def_zone)
|
/obj/item/grenade/plastic/c4/attack(mob/M, mob/user, def_zone)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
return ..()
|
return ..()
|
||||||
|
|
||||||
/obj/item/grenade/smokebomb/prime()
|
/obj/item/grenade/smokebomb/prime()
|
||||||
|
. = ..()
|
||||||
update_mob()
|
update_mob()
|
||||||
playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3)
|
playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3)
|
||||||
smoke.set_up(4, src)
|
smoke.set_up(4, src)
|
||||||
@@ -28,4 +29,4 @@
|
|||||||
var/damage = round(30/(get_dist(B,src)+1))
|
var/damage = round(30/(get_dist(B,src)+1))
|
||||||
B.take_damage(damage, BURN, "melee", 0)
|
B.take_damage(damage, BURN, "melee", 0)
|
||||||
sleep(80)
|
sleep(80)
|
||||||
qdel(src)
|
resolve()
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
var/deliveryamt = 1 // amount of type to deliver
|
var/deliveryamt = 1 // amount of type to deliver
|
||||||
|
|
||||||
/obj/item/grenade/spawnergrenade/prime() // Prime now just handles the two loops that query for people in lockers and people who can see it.
|
/obj/item/grenade/spawnergrenade/prime() // Prime now just handles the two loops that query for people in lockers and people who can see it.
|
||||||
|
. = ..()
|
||||||
update_mob()
|
update_mob()
|
||||||
if(spawner_type && deliveryamt)
|
if(spawner_type && deliveryamt)
|
||||||
// Make a quick flash
|
// Make a quick flash
|
||||||
@@ -19,7 +20,7 @@
|
|||||||
// Spawn some hostile syndicate critters and spread them out
|
// Spawn some hostile syndicate critters and spread them out
|
||||||
spawn_and_random_walk(spawner_type, T, deliveryamt, walk_chance=50, admin_spawn=((flags_1 & ADMIN_SPAWNED_1) ? TRUE : FALSE))
|
spawn_and_random_walk(spawner_type, T, deliveryamt, walk_chance=50, admin_spawn=((flags_1 & ADMIN_SPAWNED_1) ? TRUE : FALSE))
|
||||||
|
|
||||||
qdel(src)
|
resolve()
|
||||||
|
|
||||||
/obj/item/grenade/spawnergrenade/manhacks
|
/obj/item/grenade/spawnergrenade/manhacks
|
||||||
name = "viscerator delivery grenade"
|
name = "viscerator delivery grenade"
|
||||||
|
|||||||
@@ -4,27 +4,45 @@
|
|||||||
icon = 'icons/obj/grenade.dmi'
|
icon = 'icons/obj/grenade.dmi'
|
||||||
icon_state = "syndicate"
|
icon_state = "syndicate"
|
||||||
item_state = "flashbang"
|
item_state = "flashbang"
|
||||||
|
ex_dev = 1
|
||||||
|
ex_heavy = 2
|
||||||
|
ex_light = 4
|
||||||
|
ex_flame = 2
|
||||||
|
|
||||||
|
|
||||||
/obj/item/grenade/syndieminibomb/prime()
|
/obj/item/grenade/syndieminibomb/prime()
|
||||||
|
. = ..()
|
||||||
update_mob()
|
update_mob()
|
||||||
explosion(src.loc,1,2,4,flame_range = 2)
|
resolve()
|
||||||
qdel(src)
|
|
||||||
|
|
||||||
/obj/item/grenade/syndieminibomb/concussion
|
/obj/item/grenade/syndieminibomb/concussion
|
||||||
name = "HE Grenade"
|
name = "HE Grenade"
|
||||||
desc = "A compact shrapnel grenade meant to devastate nearby organisms and cause some damage in the process. Pull pin and throw opposite direction."
|
desc = "A compact shrapnel grenade meant to devastate nearby organisms and cause some damage in the process. Pull pin and throw opposite direction."
|
||||||
icon_state = "concussion"
|
icon_state = "concussion"
|
||||||
|
ex_heavy = 2
|
||||||
|
ex_light = 3
|
||||||
|
ex_flame = 3
|
||||||
|
|
||||||
/obj/item/grenade/syndieminibomb/concussion/prime()
|
/obj/item/grenade/frag
|
||||||
update_mob()
|
|
||||||
explosion(src.loc,0,2,3,flame_range = 3)
|
|
||||||
qdel(src)
|
|
||||||
|
|
||||||
/obj/item/grenade/syndieminibomb/concussion/frag
|
|
||||||
name = "frag grenade"
|
name = "frag grenade"
|
||||||
desc = "Fire in the hole."
|
desc = "An anti-personnel fragmentation grenade, this weapon excels at killing soft targets by shredding them with metal shrapnel."
|
||||||
icon_state = "frag"
|
icon_state = "frag"
|
||||||
|
shrapnel_type = /obj/projectile/bullet/shrapnel
|
||||||
|
shrapnel_radius = 4
|
||||||
|
ex_heavy = 1
|
||||||
|
ex_light = 3
|
||||||
|
ex_flame = 4
|
||||||
|
|
||||||
|
/obj/item/grenade/frag/mega
|
||||||
|
name = "FRAG grenade"
|
||||||
|
desc = "An anti-everything fragmentation grenade, this weapon excels at killing anything any everything by shredding them with metal shrapnel."
|
||||||
|
shrapnel_type = /obj/projectile/bullet/shrapnel/mega
|
||||||
|
shrapnel_radius = 12
|
||||||
|
|
||||||
|
/obj/item/grenade/frag/prime()
|
||||||
|
. = ..()
|
||||||
|
update_mob()
|
||||||
|
resolve()
|
||||||
|
|
||||||
/obj/item/grenade/gluon
|
/obj/item/grenade/gluon
|
||||||
desc = "An advanced grenade that releases a harmful stream of gluons inducing radiation in those nearby. These gluon streams will also make victims feel exhausted, and induce shivering. This extreme coldness will also likely wet any nearby floors."
|
desc = "An advanced grenade that releases a harmful stream of gluons inducing radiation in those nearby. These gluon streams will also make victims feel exhausted, and induce shivering. This extreme coldness will also likely wet any nearby floors."
|
||||||
@@ -37,6 +55,7 @@
|
|||||||
var/stamina_damage = 30
|
var/stamina_damage = 30
|
||||||
|
|
||||||
/obj/item/grenade/gluon/prime()
|
/obj/item/grenade/gluon/prime()
|
||||||
|
. = ..()
|
||||||
update_mob()
|
update_mob()
|
||||||
playsound(loc, 'sound/effects/empulse.ogg', 50, 1)
|
playsound(loc, 'sound/effects/empulse.ogg', 50, 1)
|
||||||
radiation_pulse(src, rad_damage)
|
radiation_pulse(src, rad_damage)
|
||||||
@@ -47,4 +66,4 @@
|
|||||||
for(var/mob/living/carbon/L in T)
|
for(var/mob/living/carbon/L in T)
|
||||||
L.adjustStaminaLoss(stamina_damage)
|
L.adjustStaminaLoss(stamina_damage)
|
||||||
L.adjust_bodytemperature(-230)
|
L.adjust_bodytemperature(-230)
|
||||||
qdel(src)
|
resolve()
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
if(attack_verb_on.len)
|
if(attack_verb_on.len)
|
||||||
attack_verb = attack_verb_on
|
attack_verb = attack_verb_on
|
||||||
if(embedding)
|
if(embedding)
|
||||||
AddElement(/datum/element/embed, embedding)
|
updateEmbedding()
|
||||||
icon_state = icon_state_on
|
icon_state = icon_state_on
|
||||||
w_class = w_class_on
|
w_class = w_class_on
|
||||||
else
|
else
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
if(attack_verb_off.len)
|
if(attack_verb_off.len)
|
||||||
attack_verb = attack_verb_off
|
attack_verb = attack_verb_off
|
||||||
if(embedding)
|
if(embedding)
|
||||||
RemoveElement(/datum/element/embed, embedding)
|
updateEmbedding()
|
||||||
icon_state = initial(icon_state)
|
icon_state = initial(icon_state)
|
||||||
w_class = initial(w_class)
|
w_class = initial(w_class)
|
||||||
total_mass = initial(total_mass)
|
total_mass = initial(total_mass)
|
||||||
|
|||||||
68
code/game/objects/items/shrapnel.dm
Normal file
68
code/game/objects/items/shrapnel.dm
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/obj/item/shrapnel // frag grenades
|
||||||
|
name = "shrapnel shard"
|
||||||
|
embedding = list(embed_chance=70, ignore_throwspeed_threshold=TRUE, fall_chance=4, embed_chance_turf_mod=-100)
|
||||||
|
custom_materials = list(/datum/material/iron=50)
|
||||||
|
armour_penetration = -20
|
||||||
|
icon = 'icons/obj/shards.dmi'
|
||||||
|
icon_state = "large"
|
||||||
|
w_class = WEIGHT_CLASS_TINY
|
||||||
|
item_flags = DROPDEL
|
||||||
|
|
||||||
|
/obj/item/shrapnel/stingball // stingbang grenades
|
||||||
|
name = "stingball"
|
||||||
|
embedding = list(embed_chance=90, fall_chance=3, jostle_chance=7, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.7, pain_mult=5, jostle_pain_mult=6, rip_time=15, embed_chance_turf_mod=-100)
|
||||||
|
icon_state = "tiny"
|
||||||
|
|
||||||
|
/obj/item/shrapnel/bullet // bullets
|
||||||
|
name = "bullet"
|
||||||
|
icon = 'icons/obj/ammo.dmi'
|
||||||
|
icon_state = "s-casing"
|
||||||
|
item_flags = NONE
|
||||||
|
|
||||||
|
/obj/item/shrapnel/bullet/c38 // .38 round
|
||||||
|
name = "\improper .38 bullet"
|
||||||
|
|
||||||
|
/obj/item/shrapnel/bullet/c38/dumdum // .38 DumDum round
|
||||||
|
name = "\improper .38 DumDum bullet"
|
||||||
|
embedding = list(embed_chance=70, fall_chance=7, jostle_chance=7, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10, embed_chance_turf_mod=-100)
|
||||||
|
|
||||||
|
/obj/projectile/bullet/shrapnel
|
||||||
|
name = "flying shrapnel shard"
|
||||||
|
damage = 9
|
||||||
|
range = 10
|
||||||
|
armour_penetration = -30
|
||||||
|
dismemberment = 5
|
||||||
|
ricochets_max = 2
|
||||||
|
ricochet_chance = 40
|
||||||
|
shrapnel_type = /obj/item/shrapnel
|
||||||
|
ricochet_incidence_leeway = 60
|
||||||
|
hit_stunned_targets = TRUE
|
||||||
|
|
||||||
|
/obj/projectile/bullet/shrapnel/mega
|
||||||
|
name = "flying shrapnel hunk"
|
||||||
|
range = 25
|
||||||
|
dismemberment = 10
|
||||||
|
ricochets_max = 4
|
||||||
|
ricochet_chance = 90
|
||||||
|
ricochet_decay_chance = 0.9
|
||||||
|
|
||||||
|
/obj/projectile/bullet/pellet/stingball
|
||||||
|
name = "stingball pellet"
|
||||||
|
damage = 3
|
||||||
|
stamina = 8
|
||||||
|
ricochets_max = 4
|
||||||
|
ricochet_chance = 66
|
||||||
|
ricochet_decay_chance = 1
|
||||||
|
ricochet_decay_damage = 0.9
|
||||||
|
ricochet_auto_aim_angle = 10
|
||||||
|
ricochet_auto_aim_range = 2
|
||||||
|
ricochet_incidence_leeway = 0
|
||||||
|
shrapnel_type = /obj/item/shrapnel/stingball
|
||||||
|
|
||||||
|
/obj/projectile/bullet/pellet/stingball/mega
|
||||||
|
name = "megastingball pellet"
|
||||||
|
ricochets_max = 6
|
||||||
|
ricochet_chance = 110
|
||||||
|
|
||||||
|
/obj/projectile/bullet/pellet/stingball/on_ricochet(atom/A)
|
||||||
|
hit_stunned_targets = TRUE // ducking will save you from the first wave, but not the rebounds
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
if(do_after(user, 30, target=I))
|
if(do_after(user, 30, target=I))
|
||||||
I.embedding = conferred_embed
|
I.embedding = conferred_embed
|
||||||
I.AddElement(/datum/element/embed, I.embedding)
|
I.updateEmbedding()
|
||||||
to_chat(user, "<span class='notice'>You finish wrapping [I] with [src].</span>")
|
to_chat(user, "<span class='notice'>You finish wrapping [I] with [src].</span>")
|
||||||
use(1)
|
use(1)
|
||||||
I.name = "[prefix] [I.name]"
|
I.name = "[prefix] [I.name]"
|
||||||
|
|||||||
@@ -512,16 +512,16 @@
|
|||||||
new /obj/item/grenade/smokebomb(src)
|
new /obj/item/grenade/smokebomb(src)
|
||||||
new /obj/item/grenade/empgrenade(src)
|
new /obj/item/grenade/empgrenade(src)
|
||||||
new /obj/item/grenade/empgrenade(src)
|
new /obj/item/grenade/empgrenade(src)
|
||||||
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
|
new /obj/item/grenade/frag(src)
|
||||||
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
|
new /obj/item/grenade/frag(src)
|
||||||
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
|
new /obj/item/grenade/frag(src)
|
||||||
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
|
new /obj/item/grenade/frag(src)
|
||||||
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
|
new /obj/item/grenade/frag(src)
|
||||||
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
|
new /obj/item/grenade/frag(src)
|
||||||
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
|
new /obj/item/grenade/frag(src)
|
||||||
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
|
new /obj/item/grenade/frag(src)
|
||||||
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
|
new /obj/item/grenade/frag(src)
|
||||||
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
|
new /obj/item/grenade/frag(src)
|
||||||
new /obj/item/grenade/gluon(src)
|
new /obj/item/grenade/gluon(src)
|
||||||
new /obj/item/grenade/gluon(src)
|
new /obj/item/grenade/gluon(src)
|
||||||
new /obj/item/grenade/gluon(src)
|
new /obj/item/grenade/gluon(src)
|
||||||
|
|||||||
@@ -275,6 +275,16 @@
|
|||||||
for(var/i in 1 to 7)
|
for(var/i in 1 to 7)
|
||||||
new /obj/item/grenade/flashbang(src)
|
new /obj/item/grenade/flashbang(src)
|
||||||
|
|
||||||
|
obj/item/storage/box/stingbangs
|
||||||
|
name = "box of stingbangs (WARNING)"
|
||||||
|
desc = "<B>WARNING: These devices are extremely dangerous and can cause severe injuries or death in repeated use.</B>"
|
||||||
|
icon_state = "secbox"
|
||||||
|
illustration = "flashbang"
|
||||||
|
|
||||||
|
/obj/item/storage/box/stingbangs/PopulateContents()
|
||||||
|
for(var/i in 1 to 5)
|
||||||
|
new /obj/item/grenade/stingbang(src)
|
||||||
|
|
||||||
/obj/item/storage/box/flashes
|
/obj/item/storage/box/flashes
|
||||||
name = "box of flashbulbs"
|
name = "box of flashbulbs"
|
||||||
desc = "<B>WARNING: Flashes can cause serious eye damage, protective eyewear is required.</B>"
|
desc = "<B>WARNING: Flashes can cause serious eye damage, protective eyewear is required.</B>"
|
||||||
|
|||||||
@@ -297,7 +297,9 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
|
|||||||
force = 2
|
force = 2
|
||||||
throwforce = 20 //This is never used on mobs since this has a 100% embed chance.
|
throwforce = 20 //This is never used on mobs since this has a 100% embed chance.
|
||||||
throw_speed = 4
|
throw_speed = 4
|
||||||
embedding = list("pain_mult" = 4, "embed_chance" = 100, "fall_chance" = 0)
|
embedding = list("pain_mult" = 4, "embed_chance" = 100, "fall_chance" = 0, "embed_chance_turf_mod" = 15)
|
||||||
|
armour_penetration = 40
|
||||||
|
|
||||||
w_class = WEIGHT_CLASS_SMALL
|
w_class = WEIGHT_CLASS_SMALL
|
||||||
sharpness = IS_SHARP
|
sharpness = IS_SHARP
|
||||||
custom_materials = list(/datum/material/iron=500, /datum/material/glass=500)
|
custom_materials = list(/datum/material/iron=500, /datum/material/glass=500)
|
||||||
|
|||||||
@@ -74,7 +74,8 @@
|
|||||||
/obj/bullet_act(obj/item/projectile/P)
|
/obj/bullet_act(obj/item/projectile/P)
|
||||||
. = ..()
|
. = ..()
|
||||||
playsound(src, P.hitsound, 50, 1)
|
playsound(src, P.hitsound, 50, 1)
|
||||||
visible_message("<span class='danger'>[src] is hit by \a [P]!</span>", null, null, COMBAT_MESSAGE_RANGE)
|
if(P.suppressed != SUPPRESSED_VERY)
|
||||||
|
visible_message("<span class='danger'>[src] is hit by \a [P]!</span>", null, null, COMBAT_MESSAGE_RANGE)
|
||||||
if(!QDELETED(src)) //Bullet on_hit effect might have already destroyed this object
|
if(!QDELETED(src)) //Bullet on_hit effect might have already destroyed this object
|
||||||
take_damage(P.damage, P.damage_type, P.flag, 0, turn(P.dir, 180), P.armour_penetration)
|
take_damage(P.damage, P.damage_type, P.flag, 0, turn(P.dir, 180), P.armour_penetration)
|
||||||
|
|
||||||
|
|||||||
@@ -323,3 +323,8 @@
|
|||||||
|
|
||||||
/obj/proc/rnd_crafted(obj/machinery/rnd/production/P)
|
/obj/proc/rnd_crafted(obj/machinery/rnd/production/P)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
/obj/handle_ricochet(obj/projectile/P)
|
||||||
|
. = ..()
|
||||||
|
if(. && ricochet_damage_mod)
|
||||||
|
take_damage(P.damage * ricochet_damage_mod, P.damage_type, P.flag, 0, turn(P.dir, 180), P.armour_penetration) // pass along ricochet_damage_mod damage to the structure for the ricochet
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
var/mob/living/structureclimber
|
var/mob/living/structureclimber
|
||||||
var/broken = 0 //similar to machinery's stat BROKEN
|
var/broken = 0 //similar to machinery's stat BROKEN
|
||||||
layer = BELOW_OBJ_LAYER
|
layer = BELOW_OBJ_LAYER
|
||||||
|
flags_ricochet = RICOCHET_HARD
|
||||||
|
ricochet_chance_mod = 0.5
|
||||||
|
|
||||||
/obj/structure/Initialize()
|
/obj/structure/Initialize()
|
||||||
if (!armor)
|
if (!armor)
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
|
|||||||
var/hitsound = 'sound/effects/Glasshit.ogg'
|
var/hitsound = 'sound/effects/Glasshit.ogg'
|
||||||
rad_insulation = RAD_VERY_LIGHT_INSULATION
|
rad_insulation = RAD_VERY_LIGHT_INSULATION
|
||||||
rad_flags = RAD_PROTECT_CONTENTS
|
rad_flags = RAD_PROTECT_CONTENTS
|
||||||
|
flags_ricochet = RICOCHET_HARD
|
||||||
|
ricochet_chance_mod = 0.4
|
||||||
|
|
||||||
/// Electrochromatic status
|
/// Electrochromatic status
|
||||||
var/electrochromatic_status = NOT_ELECTROCHROMATIC
|
var/electrochromatic_status = NOT_ELECTROCHROMATIC
|
||||||
@@ -525,6 +527,7 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
|
|||||||
explosion_block = 1
|
explosion_block = 1
|
||||||
glass_type = /obj/item/stack/sheet/rglass
|
glass_type = /obj/item/stack/sheet/rglass
|
||||||
rad_insulation = RAD_HEAVY_INSULATION
|
rad_insulation = RAD_HEAVY_INSULATION
|
||||||
|
ricochet_chance_mod = 0.8
|
||||||
|
|
||||||
/obj/structure/window/reinforced/spawner/east
|
/obj/structure/window/reinforced/spawner/east
|
||||||
dir = EAST
|
dir = EAST
|
||||||
@@ -690,6 +693,7 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
|
|||||||
level = 3
|
level = 3
|
||||||
glass_type = /obj/item/stack/sheet/titaniumglass
|
glass_type = /obj/item/stack/sheet/titaniumglass
|
||||||
glass_amount = 2
|
glass_amount = 2
|
||||||
|
ricochet_chance_mod = 0.9
|
||||||
|
|
||||||
/obj/structure/window/shuttle/narsie_act()
|
/obj/structure/window/shuttle/narsie_act()
|
||||||
add_atom_colour("#3C3434", FIXED_COLOUR_PRIORITY)
|
add_atom_colour("#3C3434", FIXED_COLOUR_PRIORITY)
|
||||||
|
|||||||
@@ -191,7 +191,8 @@
|
|||||||
icon = 'icons/turf/walls/shuttle_wall.dmi'
|
icon = 'icons/turf/walls/shuttle_wall.dmi'
|
||||||
icon_state = "map-shuttle"
|
icon_state = "map-shuttle"
|
||||||
explosion_block = 3
|
explosion_block = 3
|
||||||
flags_1 = CAN_BE_DIRTY_1 | CHECK_RICOCHET_1
|
flags_1 = CAN_BE_DIRTY_1
|
||||||
|
flags_ricochet = RICOCHET_SHINY | RICOCHET_HARD
|
||||||
sheet_type = /obj/item/stack/sheet/mineral/titanium
|
sheet_type = /obj/item/stack/sheet/mineral/titanium
|
||||||
smooth = SMOOTH_MORE|SMOOTH_DIAGONAL
|
smooth = SMOOTH_MORE|SMOOTH_DIAGONAL
|
||||||
canSmoothWith = list(/turf/closed/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/shuttle, /obj/structure/shuttle/engine/heater, /obj/structure/falsewall/titanium)
|
canSmoothWith = list(/turf/closed/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/shuttle, /obj/structure/shuttle/engine/heater, /obj/structure/falsewall/titanium)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
baseturfs = /turf/open/floor/plating
|
baseturfs = /turf/open/floor/plating
|
||||||
|
|
||||||
|
flags_ricochet = RICOCHET_HARD
|
||||||
|
|
||||||
///lower numbers are harder. Used to determine the probability of a hulk smashing through. Also, (hardness - 40) is used as a modifier for objects trying to embed in this (hardness of 30 results in a -10% chance)
|
///lower numbers are harder. Used to determine the probability of a hulk smashing through. Also, (hardness - 40) is used as a modifier for objects trying to embed in this (hardness of 30 results in a -10% chance)
|
||||||
var/hardness = 40
|
var/hardness = 40
|
||||||
@@ -44,17 +44,6 @@
|
|||||||
/turf/closed/wall/attack_tk()
|
/turf/closed/wall/attack_tk()
|
||||||
return
|
return
|
||||||
|
|
||||||
/turf/closed/wall/handle_ricochet(obj/item/projectile/P) //A huge pile of shitcode!
|
|
||||||
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)
|
|
||||||
return TRUE
|
|
||||||
|
|
||||||
/turf/closed/wall/proc/dismantle_wall(devastated=0, explode=0)
|
/turf/closed/wall/proc/dismantle_wall(devastated=0, explode=0)
|
||||||
if(devastated)
|
if(devastated)
|
||||||
devastate_wall()
|
devastate_wall()
|
||||||
|
|||||||
@@ -45,20 +45,8 @@
|
|||||||
desc = "A solid wall of slightly twitching tendrils with a reflective glow."
|
desc = "A solid wall of slightly twitching tendrils with a reflective glow."
|
||||||
damaged_desc = "A wall of twitching tendrils with a reflective glow."
|
damaged_desc = "A wall of twitching tendrils with a reflective glow."
|
||||||
icon_state = "blob_glow"
|
icon_state = "blob_glow"
|
||||||
flags_1 = CHECK_RICOCHET_1
|
flags_ricochet = RICOCHET_SHINY
|
||||||
point_return = 8
|
point_return = 8
|
||||||
max_integrity = 100
|
max_integrity = 100
|
||||||
brute_resist = 1
|
brute_resist = 1
|
||||||
explosion_block = 2
|
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
|
/datum/supply_pack/security
|
||||||
group = "Security"
|
group = "Security"
|
||||||
access = ACCESS_SECURITY
|
access_any = ACCESS_FORENSICS_LOCKERS //| ACCESS_SECURITY
|
||||||
crate_type = /obj/structure/closet/crate/secure/gear
|
crate_type = /obj/structure/closet/crate/secure/gear
|
||||||
can_private_buy = FALSE
|
can_private_buy = FALSE
|
||||||
|
|
||||||
@@ -228,3 +228,38 @@
|
|||||||
access = ACCESS_ARMORY
|
access = ACCESS_ARMORY
|
||||||
crate_name = "sporting crate"
|
crate_name = "sporting crate"
|
||||||
crate_type = /obj/structure/closet/crate/secure // Would have liked a wooden crate but access >:(
|
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)
|
if(!I.embedding || I.embedding == EMBED_HARMLESS)
|
||||||
I.embedding = EMBED_POINTY
|
I.embedding = EMBED_POINTY
|
||||||
I.AddElement(/datum/element/embed, I.embedding)
|
I.updateEmbedding()
|
||||||
I.name = "pointy [I.name]"
|
I.name = "pointy [I.name]"
|
||||||
|
|
||||||
GLOB.embedpocalypse = TRUE
|
GLOB.embedpocalypse = TRUE
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
if(!I.embedding)
|
if(!I.embedding)
|
||||||
I.embedding = EMBED_HARMLESS
|
I.embedding = EMBED_HARMLESS
|
||||||
I.AddElement(/datum/element/embed, I.embedding)
|
I.updateEmbedding()
|
||||||
I.name = "sticky [I.name]"
|
I.name = "sticky [I.name]"
|
||||||
|
|
||||||
GLOB.stickpocalypse = TRUE
|
GLOB.stickpocalypse = TRUE
|
||||||
|
|||||||
@@ -95,7 +95,7 @@
|
|||||||
//We want an accurate reading of .len
|
//We want an accurate reading of .len
|
||||||
listclearnulls(BP.embedded_objects)
|
listclearnulls(BP.embedded_objects)
|
||||||
for(var/obj/item/embeddies in BP.embedded_objects)
|
for(var/obj/item/embeddies in BP.embedded_objects)
|
||||||
if(!embeddies.is_embed_harmless())
|
if(!embeddies.isEmbedHarmless())
|
||||||
temp_bleed += 0.5
|
temp_bleed += 0.5
|
||||||
|
|
||||||
if(brutedamage >= 20)
|
if(brutedamage >= 20)
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ In all, this is a lot like the monkey code. /N
|
|||||||
return
|
return
|
||||||
switch(M.a_intent)
|
switch(M.a_intent)
|
||||||
if(INTENT_HELP)
|
if(INTENT_HELP)
|
||||||
|
if(M == src && check_self_for_injuries())
|
||||||
|
return
|
||||||
help_shake_act(M)
|
help_shake_act(M)
|
||||||
if(INTENT_GRAB)
|
if(INTENT_GRAB)
|
||||||
grabbedby(M)
|
grabbedby(M)
|
||||||
|
|||||||
@@ -267,7 +267,15 @@
|
|||||||
visible_message("<span class='danger'>[usr] [internal ? "opens" : "closes"] the valve on [src]'s [ITEM.name].</span>", \
|
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>", \
|
"<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>")
|
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)
|
/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
|
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>")
|
to_chat(M, "<span class='warning'>You can't put [p_them()] out with just your bare hands!</span>")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if(M == src && check_self_for_injuries())
|
||||||
|
return
|
||||||
|
|
||||||
if(health >= 0 && !(HAS_TRAIT(src, TRAIT_FAKEDEATH)))
|
if(health >= 0 && !(HAS_TRAIT(src, TRAIT_FAKEDEATH)))
|
||||||
var/friendly_check = FALSE
|
var/friendly_check = FALSE
|
||||||
if(mob_run_block(M, 0, M.name, ATTACK_TYPE_UNARMED, 0, null, null, null))
|
if(mob_run_block(M, 0, M.name, ATTACK_TYPE_UNARMED, 0, null, null, null))
|
||||||
@@ -339,6 +342,26 @@
|
|||||||
update_mobility()
|
update_mobility()
|
||||||
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
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)
|
/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))
|
else if(get_bodypart(BODY_ZONE_HEAD))
|
||||||
. += "<span class='deadsay'>It appears that [t_his] brain is missing...</span>"
|
. += "<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)
|
for(var/t in missing)
|
||||||
if(t==BODY_ZONE_HEAD)
|
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
|
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()
|
var/temp = getBruteLoss()
|
||||||
if(!(user == src && src.hal_screwyhud == SCREWYHUD_HEALTHY)) //fake healthy
|
if(!(user == src && src.hal_screwyhud == SCREWYHUD_HEALTHY)) //fake healthy
|
||||||
if(temp)
|
if(temp)
|
||||||
|
|||||||
@@ -159,7 +159,7 @@
|
|||||||
disabled += BP
|
disabled += BP
|
||||||
missing -= BP.body_zone
|
missing -= BP.body_zone
|
||||||
for(var/obj/item/I in BP.embedded_objects)
|
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"
|
msg += "<B>[t_He] [t_has] \a [icon2html(I, user)] [I] stuck to [t_his] [BP.name]!</B>\n"
|
||||||
else
|
else
|
||||||
msg += "<B>[t_He] [t_has] \a [icon2html(I, user)] [I] embedded in [t_his] [BP.name]!</B>\n"
|
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
|
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
|
if(!I || I.loc != src) //no item, no limb, or item is not in limb or in the person anymore
|
||||||
return
|
return
|
||||||
SEND_SIGNAL(src, COMSIG_HUMAN_EMBED_RIP, I, L)
|
SEND_SIGNAL(src, COMSIG_CARBON_EMBED_RIP, I, L)
|
||||||
return
|
return
|
||||||
|
|
||||||
if(href_list["item"])
|
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"
|
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)
|
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>")
|
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
|
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>")
|
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)
|
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
|
//the if "armor" check is because this is used for everything on /living, including humans
|
||||||
if(armor && armour_penetration)
|
if(armor && armour_penetration)
|
||||||
armor = max(0, armor - armour_penetration)
|
armor = max(0, armor - armour_penetration)
|
||||||
@@ -10,7 +15,7 @@
|
|||||||
else if(armor >= 100)
|
else if(armor >= 100)
|
||||||
if(absorb_text)
|
if(absorb_text)
|
||||||
to_chat(src, "<span class='danger'>[absorb_text]</span>")
|
to_chat(src, "<span class='danger'>[absorb_text]</span>")
|
||||||
else if(armor > 0)
|
else
|
||||||
if(soften_text)
|
if(soften_text)
|
||||||
to_chat(src, "<span class='danger'>[soften_text]</span>")
|
to_chat(src, "<span class='danger'>[soften_text]</span>")
|
||||||
return armor
|
return armor
|
||||||
|
|||||||
@@ -15,4 +15,4 @@
|
|||||||
/obj/item/throwing_star/ninja
|
/obj/item/throwing_star/ninja
|
||||||
name = "ninja throwing star"
|
name = "ninja throwing star"
|
||||||
throwforce = 30
|
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)
|
throwforce = initial(throwforce)
|
||||||
playsound(user, 'sound/weapons/saberoff.ogg', 5, 1)
|
playsound(user, 'sound/weapons/saberoff.ogg', 5, 1)
|
||||||
to_chat(user, "<span class='warning'>[src] can now be concealed.</span>")
|
to_chat(user, "<span class='warning'>[src] can now be concealed.</span>")
|
||||||
RemoveElement(/datum/element/embed, embedding)
|
updateEmbedding()
|
||||||
else
|
else
|
||||||
on = TRUE
|
on = TRUE
|
||||||
force = 18
|
force = 18
|
||||||
@@ -214,7 +214,7 @@
|
|||||||
throwforce = 35
|
throwforce = 35
|
||||||
playsound(user, 'sound/weapons/saberon.ogg', 5, 1)
|
playsound(user, 'sound/weapons/saberon.ogg', 5, 1)
|
||||||
to_chat(user, "<span class='warning'>[src] is now active.</span>")
|
to_chat(user, "<span class='warning'>[src] is now active.</span>")
|
||||||
AddElement(/datum/element/embed, embedding)
|
updateEmbedding()
|
||||||
update_icon()
|
update_icon()
|
||||||
|
|
||||||
/obj/item/pen/edagger/update_icon_state()
|
/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)
|
/obj/item/ammo_casing/proc/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread, atom/fired_from)
|
||||||
distro += variance
|
distro += variance
|
||||||
for (var/i = max(1, pellets), i > 0, i--)
|
var/targloc = get_turf(target)
|
||||||
var/targloc = get_turf(target)
|
ready_proj(target, user, quiet, zone_override, fired_from)
|
||||||
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(distro) //We have to spread a pixel-precision bullet. throw_proj was called before so angles should exist by now...
|
||||||
if(randomspread)
|
if(randomspread)
|
||||||
spread = round((rand() - 0.5) * distro)
|
spread = round((rand() - 0.5) * distro)
|
||||||
else //Smart spread
|
else //Smart spread
|
||||||
spread = round((i / pellets - 0.5) * distro)
|
spread = round(1 - 0.5) * distro
|
||||||
if(!throw_proj(target, targloc, user, params, spread))
|
if(!throw_proj(target, targloc, user, params, spread))
|
||||||
return 0
|
return FALSE
|
||||||
if(i > 1)
|
else
|
||||||
newshot()
|
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)
|
if(click_cooldown_override)
|
||||||
user.changeNext_move(click_cooldown_override)
|
user.changeNext_move(click_cooldown_override)
|
||||||
else
|
else
|
||||||
@@ -31,7 +35,7 @@
|
|||||||
else
|
else
|
||||||
BB.def_zone = user.zone_selected
|
BB.def_zone = user.zone_selected
|
||||||
BB.suppressed = quiet
|
BB.suppressed = quiet
|
||||||
|
|
||||||
if(isgun(fired_from))
|
if(isgun(fired_from))
|
||||||
var/obj/item/gun/G = fired_from
|
var/obj/item/gun/G = fired_from
|
||||||
BB.damage *= G.projectile_damage_multiplier
|
BB.damage *= G.projectile_damage_multiplier
|
||||||
|
|||||||
@@ -21,3 +21,16 @@
|
|||||||
name = "1.95x129mm incendiary bullet casing"
|
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."
|
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
|
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."
|
desc = "A .357 armor-piercing bullet casing."
|
||||||
projectile_type = /obj/item/projectile/bullet/a357/ap
|
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)
|
// 7.62x38mmR (Nagant Revolver)
|
||||||
|
|
||||||
/obj/item/ammo_casing/n762
|
/obj/item/ammo_casing/n762
|
||||||
@@ -47,4 +53,19 @@
|
|||||||
name = ".38 Iceblox bullet casing"
|
name = ".38 Iceblox bullet casing"
|
||||||
desc = "A .38 Iceblox bullet casing."
|
desc = "A .38 Iceblox bullet casing."
|
||||||
caliber = "38"
|
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
|
max_ammo = 7
|
||||||
multiple_sprites = 1
|
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
|
/obj/item/ammo_box/a357/ap
|
||||||
name = "speed loader (.357 AP)"
|
name = "speed loader (.357 AP)"
|
||||||
ammo_type = /obj/item/ammo_casing/a357/ap
|
ammo_type = /obj/item/ammo_casing/a357/ap
|
||||||
|
|||||||
@@ -20,3 +20,7 @@
|
|||||||
/obj/item/ammo_box/magazine/mm195x129/update_icon()
|
/obj/item/ammo_box/magazine/mm195x129/update_icon()
|
||||||
..()
|
..()
|
||||||
icon_state = "a762-[round(ammo_count(),10)]"
|
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/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
|
var/spread = 0 //amount (in degrees) of projectile spread
|
||||||
animate_movement = 0 //Use SLIDE_STEPS in conjunction with legacy
|
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 = 0
|
||||||
var/ricochets_max = 2
|
/// how many times we can ricochet max
|
||||||
var/ricochet_chance = 30
|
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
|
//Hitscan
|
||||||
var/hitscan = FALSE //Whether this is hitscan. If it is, speed is basically ignored.
|
var/hitscan = FALSE //Whether this is hitscan. If it is, speed is basically ignored.
|
||||||
@@ -131,6 +147,11 @@
|
|||||||
|
|
||||||
var/temporary_unstoppable_movement = FALSE
|
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()
|
/obj/item/projectile/Initialize()
|
||||||
. = ..()
|
. = ..()
|
||||||
permutated = list()
|
permutated = list()
|
||||||
@@ -146,6 +167,7 @@
|
|||||||
on_range()
|
on_range()
|
||||||
|
|
||||||
/obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their 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)
|
qdel(src)
|
||||||
|
|
||||||
//to get the correct limb (if any) for the projectile hit message
|
//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)
|
/obj/item/projectile/proc/on_hit(atom/target, blocked = FALSE)
|
||||||
if(fired_from)
|
if(fired_from)
|
||||||
SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_ON_HIT, firer, target, Angle)
|
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/turf/target_loca = get_turf(target)
|
||||||
|
|
||||||
var/hitx
|
var/hitx
|
||||||
@@ -219,7 +245,10 @@
|
|||||||
var/limb_hit = L.check_limb_hit(def_zone)//to get the correct message info.
|
var/limb_hit = L.check_limb_hit(def_zone)//to get the correct message info.
|
||||||
if(limb_hit)
|
if(limb_hit)
|
||||||
organ_hit_text = " in \the [parse_zone(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)
|
playsound(loc, hitsound, 5, 1, -1)
|
||||||
to_chat(L, "<span class='userdanger'>You're shot by \a [src][organ_hit_text]!</span>")
|
to_chat(L, "<span class='userdanger'>You're shot by \a [src][organ_hit_text]!</span>")
|
||||||
else
|
else
|
||||||
@@ -250,7 +279,23 @@
|
|||||||
return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume
|
return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume
|
||||||
|
|
||||||
/obj/item/projectile/proc/on_ricochet(atom/A)
|
/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)
|
/obj/item/projectile/proc/store_hitscan_collision(datum/point/pcache)
|
||||||
beam_segments[beam_index] = pcache
|
beam_segments[beam_index] = pcache
|
||||||
@@ -259,13 +304,15 @@
|
|||||||
|
|
||||||
/obj/item/projectile/Bump(atom/A)
|
/obj/item/projectile/Bump(atom/A)
|
||||||
var/turf/T = get_turf(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()
|
var/datum/point/pcache = trajectory.copy_to()
|
||||||
ricochets++
|
ricochets++
|
||||||
if(A.handle_ricochet(src))
|
if(A.handle_ricochet(src))
|
||||||
on_ricochet(A)
|
on_ricochet(A)
|
||||||
ignore_source_check = TRUE
|
ignore_source_check = TRUE
|
||||||
decayedRange = max(0, decayedRange - reflect_range_decrease)
|
decayedRange = max(0, decayedRange - reflect_range_decrease)
|
||||||
|
ricochet_chance *= ricochet_decay_chance
|
||||||
|
damage *= ricochet_decay_damage
|
||||||
range = decayedRange
|
range = decayedRange
|
||||||
if(hitscan)
|
if(hitscan)
|
||||||
store_hitscan_collision(pcache)
|
store_hitscan_collision(pcache)
|
||||||
@@ -344,13 +391,18 @@
|
|||||||
return T
|
return T
|
||||||
//Returns null if nothing at all was found.
|
//Returns null if nothing at all was found.
|
||||||
|
|
||||||
/obj/item/projectile/proc/check_ricochet()
|
/obj/projectile/proc/check_ricochet(atom/A)
|
||||||
if(prob(ricochet_chance))
|
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 TRUE
|
||||||
return FALSE
|
return FALSE
|
||||||
|
|
||||||
/obj/item/projectile/proc/check_ricochet_flag(atom/A)
|
/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 TRUE
|
||||||
return FALSE
|
return FALSE
|
||||||
|
|
||||||
@@ -391,6 +443,8 @@
|
|||||||
/obj/item/projectile/proc/fire(angle, atom/direct_target)
|
/obj/item/projectile/proc/fire(angle, atom/direct_target)
|
||||||
if(fired_from)
|
if(fired_from)
|
||||||
SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_BEFORE_FIRE, src, original) //If no angle needs to resolve it from xo/yo!
|
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)
|
if(!log_override && firer && original)
|
||||||
log_combat(firer, original, "fired at", src, "from [get_area_name(src, TRUE)]")
|
log_combat(firer, original, "fired at", src, "from [get_area_name(src, TRUE)]")
|
||||||
if(direct_target)
|
if(direct_target)
|
||||||
|
|||||||
@@ -15,6 +15,38 @@
|
|||||||
/obj/item/projectile/bullet/c38
|
/obj/item/projectile/bullet/c38
|
||||||
name = ".38 bullet"
|
name = ".38 bullet"
|
||||||
damage = 25
|
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
|
/obj/item/projectile/bullet/c38/rubber
|
||||||
name = ".38 rubber bullet"
|
name = ".38 rubber bullet"
|
||||||
@@ -24,6 +56,7 @@
|
|||||||
/obj/item/projectile/bullet/c38/trac
|
/obj/item/projectile/bullet/c38/trac
|
||||||
name = ".38 TRAC bullet"
|
name = ".38 TRAC bullet"
|
||||||
damage = 10
|
damage = 10
|
||||||
|
ricochets_max = 0
|
||||||
|
|
||||||
/obj/item/projectile/bullet/c38/trac/on_hit(atom/target, blocked = FALSE)
|
/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
|
/obj/item/projectile/bullet/c38/hotshot //similar to incendiary bullets, but do not leave a flaming trail
|
||||||
name = ".38 Hot Shot bullet"
|
name = ".38 Hot Shot bullet"
|
||||||
damage = 20
|
damage = 20
|
||||||
|
ricochets_max = 0
|
||||||
|
|
||||||
/obj/item/projectile/bullet/c38/hotshot/on_hit(atom/target, blocked = FALSE)
|
/obj/item/projectile/bullet/c38/hotshot/on_hit(atom/target, blocked = FALSE)
|
||||||
. = ..()
|
. = ..()
|
||||||
@@ -51,6 +85,7 @@
|
|||||||
name = ".38 Iceblox bullet"
|
name = ".38 Iceblox bullet"
|
||||||
damage = 20
|
damage = 20
|
||||||
var/temperature = 100
|
var/temperature = 100
|
||||||
|
ricochets_max = 0
|
||||||
|
|
||||||
/obj/item/projectile/bullet/c38/iceblox/on_hit(atom/target, blocked = FALSE)
|
/obj/item/projectile/bullet/c38/iceblox/on_hit(atom/target, blocked = FALSE)
|
||||||
. = ..()
|
. = ..()
|
||||||
@@ -68,4 +103,14 @@
|
|||||||
/obj/item/projectile/bullet/a357/ap
|
/obj/item/projectile/bullet/a357/ap
|
||||||
name = ".357 armor-piercing bullet"
|
name = ".357 armor-piercing bullet"
|
||||||
damage = 45
|
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
|
break
|
||||||
M.dropItemToGround(item_to_retrieve)
|
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
|
else
|
||||||
if(istype(item_to_retrieve.loc, /obj/machinery/portable_atmospherics/)) //Edge cases for moved machinery
|
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
|
var/obj/machinery/portable_atmospherics/P = item_to_retrieve.loc
|
||||||
|
|||||||
@@ -152,28 +152,24 @@
|
|||||||
disabled += zone
|
disabled += zone
|
||||||
return disabled
|
return disabled
|
||||||
|
|
||||||
//Remove all embedded objects from all limbs on the carbon mob
|
///Remove a specific embedded item from the carbon mob
|
||||||
/mob/living/carbon/proc/remove_all_embedded_objects()
|
/mob/living/carbon/proc/remove_embedded_object(obj/item/I)
|
||||||
var/turf/T = get_turf(src)
|
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)
|
for(var/X in bodyparts)
|
||||||
var/obj/item/bodypart/L = X
|
var/obj/item/bodypart/L = X
|
||||||
for(var/obj/item/I in L.embedded_objects)
|
for(var/obj/item/I in L.embedded_objects)
|
||||||
L.embedded_objects -= I
|
remove_embedded_object(I)
|
||||||
I.forceMove(T)
|
|
||||||
I.unembedded()
|
|
||||||
|
|
||||||
clear_alert("embeddedobject")
|
|
||||||
SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "embedded")
|
|
||||||
|
|
||||||
/mob/living/carbon/proc/has_embedded_objects(include_harmless=FALSE)
|
/mob/living/carbon/proc/has_embedded_objects(include_harmless=FALSE)
|
||||||
. = 0
|
|
||||||
for(var/X in bodyparts)
|
for(var/X in bodyparts)
|
||||||
var/obj/item/bodypart/L = X
|
var/obj/item/bodypart/L = X
|
||||||
for(var/obj/item/I in L.embedded_objects)
|
for(var/obj/item/I in L.embedded_objects)
|
||||||
if(!include_harmless && I.is_embed_harmless())
|
if(!include_harmless && I.isEmbedHarmless())
|
||||||
continue
|
continue
|
||||||
return 1
|
return TRUE
|
||||||
|
|
||||||
|
|
||||||
//Helper for quickly creating a new limb - used by augment code in species.dm spec_attacked_by
|
//Helper for quickly creating a new limb - used by augment code in species.dm spec_attacked_by
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
var/objects = 0
|
var/objects = 0
|
||||||
for(var/obj/item/I in L.embedded_objects)
|
for(var/obj/item/I in L.embedded_objects)
|
||||||
objects++
|
objects++
|
||||||
SEND_SIGNAL(H, COMSIG_HUMAN_EMBED_REMOVAL, I, L)
|
H.remove_embedded_object(I)
|
||||||
|
|
||||||
if(objects > 0)
|
if(objects > 0)
|
||||||
display_results(user, target, "<span class='notice'>You successfully remove [objects] objects from [H]'s [L.name].</span>",
|
display_results(user, target, "<span class='notice'>You successfully remove [objects] objects from [H]'s [L.name].</span>",
|
||||||
|
|||||||
@@ -189,6 +189,13 @@
|
|||||||
cost = 4
|
cost = 4
|
||||||
include_modes = list(/datum/game_mode/nuclear)
|
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
|
/datum/uplink_item/ammo/machinegun
|
||||||
cost = 6
|
cost = 6
|
||||||
surplus = 0
|
surplus = 0
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
/obj/item/clothing/suit/armor/vest/blueshirt = 1,
|
/obj/item/clothing/suit/armor/vest/blueshirt = 1,
|
||||||
/obj/item/clothing/under/rank/security/officer/blueshirt = 1,
|
/obj/item/clothing/under/rank/security/officer/blueshirt = 1,
|
||||||
/obj/item/clothing/gloves/tackler = 5,
|
/obj/item/clothing/gloves/tackler = 5,
|
||||||
|
/obj/item/grenade/stingbang = 1,
|
||||||
/obj/item/ssword_kit = 1,
|
/obj/item/ssword_kit = 1,
|
||||||
/obj/item/storage/bag/ammo = 2)
|
/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)
|
armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
|
||||||
|
|||||||
@@ -351,7 +351,6 @@
|
|||||||
#include "code\datums\datumvars.dm"
|
#include "code\datums\datumvars.dm"
|
||||||
#include "code\datums\dna.dm"
|
#include "code\datums\dna.dm"
|
||||||
#include "code\datums\dog_fashion.dm"
|
#include "code\datums\dog_fashion.dm"
|
||||||
#include "code\datums\embedding_behavior.dm"
|
|
||||||
#include "code\datums\emotes.dm"
|
#include "code\datums\emotes.dm"
|
||||||
#include "code\datums\ert.dm"
|
#include "code\datums\ert.dm"
|
||||||
#include "code\datums\explosion.dm"
|
#include "code\datums\explosion.dm"
|
||||||
@@ -415,11 +414,13 @@
|
|||||||
#include "code\datums\components\magnetic_catch.dm"
|
#include "code\datums\components\magnetic_catch.dm"
|
||||||
#include "code\datums\components\material_container.dm"
|
#include "code\datums\components\material_container.dm"
|
||||||
#include "code\datums\components\mirage_border.dm"
|
#include "code\datums\components\mirage_border.dm"
|
||||||
|
#include "code\datums\components\mirv.dm"
|
||||||
#include "code\datums\components\mood.dm"
|
#include "code\datums\components\mood.dm"
|
||||||
#include "code\datums\components\nanites.dm"
|
#include "code\datums\components\nanites.dm"
|
||||||
#include "code\datums\components\ntnet_interface.dm"
|
#include "code\datums\components\ntnet_interface.dm"
|
||||||
#include "code\datums\components\orbiter.dm"
|
#include "code\datums\components\orbiter.dm"
|
||||||
#include "code\datums\components\paintable.dm"
|
#include "code\datums\components\paintable.dm"
|
||||||
|
#include "code\datums\components\pellet_cloud.dm"
|
||||||
#include "code\datums\components\phantomthief.dm"
|
#include "code\datums\components\phantomthief.dm"
|
||||||
#include "code\datums\components\rad_insulation.dm"
|
#include "code\datums\components\rad_insulation.dm"
|
||||||
#include "code\datums\components\radioactive.dm"
|
#include "code\datums\components\radioactive.dm"
|
||||||
@@ -427,7 +428,6 @@
|
|||||||
#include "code\datums\components\riding.dm"
|
#include "code\datums\components\riding.dm"
|
||||||
#include "code\datums\components\rotation.dm"
|
#include "code\datums\components\rotation.dm"
|
||||||
#include "code\datums\components\shielded.dm"
|
#include "code\datums\components\shielded.dm"
|
||||||
#include "code\datums\components\shrapnel.dm"
|
|
||||||
#include "code\datums\components\shrink.dm"
|
#include "code\datums\components\shrink.dm"
|
||||||
#include "code\datums\components\sizzle.dm"
|
#include "code\datums\components\sizzle.dm"
|
||||||
#include "code\datums\components\slippery.dm"
|
#include "code\datums\components\slippery.dm"
|
||||||
@@ -991,6 +991,7 @@
|
|||||||
#include "code\game\objects\items\sharpener.dm"
|
#include "code\game\objects\items\sharpener.dm"
|
||||||
#include "code\game\objects\items\shields.dm"
|
#include "code\game\objects\items\shields.dm"
|
||||||
#include "code\game\objects\items\shooting_range.dm"
|
#include "code\game\objects\items\shooting_range.dm"
|
||||||
|
#include "code\game\objects\items\shrapnel.dm"
|
||||||
#include "code\game\objects\items\signs.dm"
|
#include "code\game\objects\items\signs.dm"
|
||||||
#include "code\game\objects\items\singularityhammer.dm"
|
#include "code\game\objects\items\singularityhammer.dm"
|
||||||
#include "code\game\objects\items\stunbaton.dm"
|
#include "code\game\objects\items\stunbaton.dm"
|
||||||
|
|||||||
Reference in New Issue
Block a user