Conflicts!!!

This commit is contained in:
Artur
2020-06-22 17:39:24 +03:00
395 changed files with 84463 additions and 336540 deletions

View File

@@ -141,3 +141,8 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
// melee_attack_chain() attackchain_flags
/// The attack is from a parry counterattack.
#define ATTACKCHAIN_PARRY_COUNTERATTACK (1<<0)
/// 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)

View File

@@ -11,6 +11,17 @@
#define NUKE_RESULT_WRONG_STATION 7
#define NUKE_RESULT_WRONG_STATION_DEAD 8
//fugitive end results
#define FUGITIVE_RESULT_BADASS_HUNTER 0
#define FUGITIVE_RESULT_POSTMORTEM_HUNTER 1
#define FUGITIVE_RESULT_MAJOR_HUNTER 2
#define FUGITIVE_RESULT_HUNTER_VICTORY 3
#define FUGITIVE_RESULT_MINOR_HUNTER 4
#define FUGITIVE_RESULT_STALEMATE 5
#define FUGITIVE_RESULT_MINOR_FUGITIVE 6
#define FUGITIVE_RESULT_FUGITIVE_VICTORY 7
#define FUGITIVE_RESULT_MAJOR_FUGITIVE 8
#define APPRENTICE_DESTRUCTION "destruction"
#define APPRENTICE_BLUESPACE "bluespace"
#define APPRENTICE_ROBELESS "robeless"

View File

@@ -59,6 +59,7 @@
#define ANTAG_HUD_CLOCKWORK 22
#define ANTAG_HUD_BROTHER 23
#define ANTAG_HUD_BLOODSUCKER 24
#define ANTAG_HUD_FUGITIVE 25
// Notification action types
#define NOTIFY_JUMP "jump"

View File

@@ -32,4 +32,8 @@
list("", "\improper S.T.E.A.L.T.H. pod MKVII", "A supply pod that, under normal circumstances, is completely invisible to conventional methods of detection. How are you even seeing this?"),\
list("gondolapod", "gondola", "The silent walker. This one seems to be part of a delivery agency."),\
list("", "", "")\
)
)
#define PACK_GOODY_NONE 0
#define PACK_GOODY_PUBLIC 1 //can be bought by both privates and cargo
#define PACK_GOODY_PRIVATE 2 //can be bought only by privates

View File

@@ -163,9 +163,8 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
/obj/item/gun)))
//Combat object defines
//Embedded objects
#define EMBEDDED_PAIN_CHANCE 15 //Chance for embedded objects to cause pain (damage user)
#define EMBEDDED_ITEM_FALLOUT 5 //Chance for embedded object to fall out (causing pain but removing the object)
#define EMBED_CHANCE 45 //Chance for an object to embed into somebody when thrown (if it's sharp)
@@ -174,7 +173,16 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
#define EMBEDDED_IMPACT_PAIN_MULTIPLIER 4 //Coefficient of multiplication for the damage the item does when it first embeds (this*item.w_class)
#define EMBED_THROWSPEED_THRESHOLD 4 //The minimum value of an item's throw_speed for it to embed (Unless it has embedded_ignore_throwspeed_threshold set to 1)
#define EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER 8 //Coefficient of multiplication for the damage the item does when removed without a surgery (this*item.w_class)
#define EMBEDDED_UNSAFE_REMOVAL_TIME 150 //A Time in ticks, total removal time = (this/item.w_class)
#define EMBEDDED_UNSAFE_REMOVAL_TIME 30 //A Time in ticks, total removal time = (this*item.w_class)
#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_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_SUPERIOR list("pain_mult" = 0, "jostle_pain_mult" = 0, "ignore_throwspeed_threshold" = TRUE, "embed_chance" = 100, "fall_chance" = 0.1)
#define EMBED_POINTY list("ignore_throwspeed_threshold" = TRUE)
#define EMBED_POINTY_SUPERIOR list("embed_chance" = 100, "ignore_throwspeed_threshold" = TRUE)
//Gun weapon weight
#define WEAPON_LIGHT 1
@@ -189,6 +197,14 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
#define EGUN_SELFCHARGE 1
#define EGUN_SELFCHARGE_BORG 2
//Gun suppression
#define SUPPRESSED_NONE 0
#define SUPPRESSED_QUIET 1 ///standard suppressed
#define SUPPRESSED_VERY 2 /// no message
//Nice shot bonus
#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
///Time to spend without clicking on other things required for your shots to become accurate.
#define GUN_AIMING_TIME (2 SECONDS)

View File

@@ -26,6 +26,11 @@
#define COMPONENT_ADD_TRAIT (1<<0)
#define COMPONENT_REMOVE_TRAIT (1<<1)
/// fires on the target datum when an element is attached to it (/datum/element)
#define COMSIG_ELEMENT_ATTACH "element_attach"
/// fires on the target datum when an element is attached to it (/datum/element)
#define COMSIG_ELEMENT_DETACH "element_detach"
// /atom signals
#define COMSIG_PARENT_ATTACKBY "atom_attackby" //from base of atom/attackby(): (/obj/item, /mob/living, params)
#define COMPONENT_NO_AFTERATTACK 1 //Return this in response if you don't want afterattack to be called
@@ -227,10 +232,16 @@
#define COMSIG_LIVING_STATUS_STAGGER "living_stagger" //from base of mob/living/Stagger() (amount, update, ignore)
#define COMPONENT_NO_STUN 1 //For all of them
#define COMSIG_LIVING_LIFE "life_tick" //from base of mob/living/Life() (seconds, times_fired)
#define COMPONENT_INTERRUPT_LIFE_BIOLOGICAL 1 // interrupt biological processes
#define COMPONENT_INTERRUPT_LIFE_PHYSICAL 2 // interrupt physical handling
// /mob/living/carbon signals
#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_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
#define COMSIG_ROBOT_UPDATE_ICONS "robot_update_icons" //from base of robot/update_icons(): ()
@@ -277,9 +288,15 @@
#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_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
#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
#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)
@@ -307,8 +324,15 @@
#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)
#define COMSIG_PROJECTILE_SELF_ON_HIT "projectile_self_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_FIRE "projectile_fire" ///from the base of /obj/projectile/proc/fire(): ()
#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_PROJECTILE_PREHIT "com_proj_prehit" ///sent to targets during the process_hit proc of projectiles
#define COMSIG_PELLET_CLOUD_INIT "pellet_cloud_init" // sent to targets during the process_hit proc of projectiles
// /mob/living/carbon/human signals
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target)

View File

@@ -180,6 +180,12 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
#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/item/ammo_casing))
#define isidcard(I) (istype(I, /obj/item/card/id))
#define isstructure(A) (istype(A, /obj/structure))

View File

@@ -27,6 +27,7 @@
#define BOT_NAV 15 // computing navigation
#define BOT_WAIT_FOR_NAV 16 // waiting for nav computation
#define BOT_NO_ROUTE 17 // no destination beacon found (or no route)
#define BOT_TIPPED 18 // someone tipped a medibot over ;_;
//Bot types
#define SEC_BOT (1<<0) // Secutritrons (Beepsky) and ED-209s

View File

@@ -7,6 +7,8 @@
#define SHUTTLE_STRANDED "stranded"
#define SHUTTLE_ESCAPE "escape"
#define SHUTTLE_ENDGAME "endgame: game over"
#define SHUTTLE_RECHARGING "recharging"
#define SHUTTLE_PREARRIVAL "landing"
#define EMERGENCY_IDLE_OR_RECALLED (SSshuttle.emergency && ((SSshuttle.emergency.mode == SHUTTLE_IDLE) || (SSshuttle.emergency.mode == SHUTTLE_RECALL)))
#define EMERGENCY_ESCAPED_OR_ENDGAMED (SSshuttle.emergency && ((SSshuttle.emergency.mode == SHUTTLE_ESCAPE) || (SSshuttle.emergency.mode == SHUTTLE_ENDGAME)))

View File

@@ -6,12 +6,26 @@
#define RETURN_TYPE(X) set SpacemanDMM_return_type = X
#define SHOULD_CALL_PARENT(X) set SpacemanDMM_should_call_parent = X
#define UNLINT(X) SpacemanDMM_unlint(X)
#define SHOULD_NOT_OVERRIDE(X) set SpacemanDMM_should_not_override = X
#define SHOULD_NOT_SLEEP(X) set SpacemanDMM_should_not_sleep = X
#define SHOULD_BE_PURE(X) set SpacemanDMM_should_be_pure = X
#define PRIVATE_PROC(X) set SpacemanDMM_private_proc = X
#define PROTECTED_PROC(X) set SpacemanDMM_protected_proc = X
#define VAR_FINAL var/SpacemanDMM_final
#define VAR_PRIVATE var/SpacemanDMM_private
#define VAR_PROTECTED var/SpacemanDMM_protected
#else
#define RETURN_TYPE(X)
#define SHOULD_CALL_PARENT(X)
#define UNLINT(X) X
#define SHOULD_NOT_OVERRIDE(X)
#define SHOULD_NOT_SLEEP(X)
#define SHOULD_BE_PURE(X)
#define PRIVATE_PROC(X)
#define PROTECTED_PROC(X)
#define VAR_FINAL var
#define VAR_PRIVATE var
#define VAR_PROTECTED var
#endif
/world/proc/enable_debugger()

View File

@@ -96,6 +96,8 @@
#define STATUS_EFFECT_SPASMS /datum/status_effect/spasms //causes random muscle spasms
#define STATUS_EFFECT_FAKE_VIRUS /datum/status_effect/fake_virus //gives you fluff messages for cough, sneeze, headache, etc but without an actual virus
#define STATUS_EFFECT_BREASTS_ENLARGEMENT /datum/status_effect/chem/breast_enlarger //Applied slowdown due to the ominous bulk.
#define STATUS_EFFECT_PENIS_ENLARGEMENT /datum/status_effect/chem/penis_enlarger //More applied slowdown, just like the above.

View File

@@ -196,6 +196,7 @@
#define TRAIT_NO_ALCOHOL "alcohol_intolerance"
#define TRAIT_MUTATION_STASIS "mutation_stasis" //Prevents processed genetics mutations from processing.
#define TRAIT_FAST_PUMP "fast_pump"
#define TRAIT_NICE_SHOT "nice_shot" //hnnnnnnnggggg..... you're pretty good....
// mobility flag traits
// IN THE FUTURE, IT WOULD BE NICE TO DO SOMETHING SIMILAR TO https://github.com/tgstation/tgstation/pull/48923/files (ofcourse not nearly the same because I have my.. thoughts on it)
@@ -301,3 +302,4 @@
#define ACTIVE_BLOCK_TRAIT "active_block"
/// This trait is added by the parry system.
#define ACTIVE_PARRY_TRAIT "active_parry"
#define STICKY_NODROP "sticky-nodrop" //sticky nodrop sounds like a bad soundcloud rapper's name

View File

@@ -55,7 +55,7 @@
WRITE_LOG(GLOB.world_game_log, "ADMIN: DSAY: [text]")
/proc/log_consent(text)
WRITE_LOG(GLOB.world_game_log,"CONSENT: [text]")
WRITE_LOG(GLOB.world_game_log, "CONSENT: [text]")
/* All other items are public. */
/proc/log_game(text)

View File

@@ -675,12 +675,6 @@ Turf and target are separate in case you want to teleport some distance from a t
loc = loc.loc
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
*/

View File

@@ -140,6 +140,10 @@ GLOBAL_LIST_INIT(bitfields, list(
"BLOCK_FACE_ATOM_1" = BLOCK_FACE_ATOM_1,
"PREVENT_CONTENTS_EXPLOSION_1" = PREVENT_CONTENTS_EXPLOSION_1
),
"flags_ricochet" = list(
"RICOCHET_SHINY" = RICOCHET_SHINY,
"RICOCHET_HARD" = RICOCHET_HARD
),
"clothing_flags" = list(
"LAVAPROTECT" = LAVAPROTECT,
"STOPSPRESSUREDAMAGE" = STOPSPRESSUREDAMAGE,

View File

@@ -223,3 +223,6 @@ GLOBAL_LIST_INIT(station_numerals, greek_letters + phonetic_alphabet + numbers_a
GLOBAL_LIST_INIT(admiral_messages, list("Do you know how expensive these stations are?","Stop wasting my time.","I was sleeping, thanks a lot.","Stand and fight you cowards!","You knew the risks coming in.","Stop being paranoid.","Whatever's broken just build a new one.","No.", "<i>null</i>","<i>Error: No comment given.</i>", "It's a good day to die!"))
GLOBAL_LIST_INIT(redacted_strings, list("\[REDACTED\]", "\[CLASSIFIED\]", "\[ARCHIVED\]", "\[EXPLETIVE DELETED\]", "\[EXPUNGED\]", "\[INFORMATION ABOVE YOUR SECURITY CLEARANCE\]", "\[MOVE ALONG CITIZEN\]", "\[NOTHING TO SEE HERE\]", "\[ACCESS DENIED\]"))
GLOBAL_LIST_INIT(wisdoms, world.file2list("strings/wisdoms.txt"))

View File

@@ -51,7 +51,7 @@ GLOBAL_LIST_INIT(maintenance_loot, list(
/obj/item/airlock_painter = 1,
/obj/item/stack/cable_coil/random = 4,
/obj/item/stack/cable_coil/random/five = 6,
/obj/item/stack/medical/bruise_pack = 1,
/obj/item/stack/medical/suture = 1,
/obj/item/stack/rods/ten = 9,
/obj/item/stack/rods/twentyfive = 1,
/obj/item/stack/rods/fifty = 1,
@@ -116,6 +116,7 @@ GLOBAL_LIST_INIT(maintenance_loot, list(
/obj/item/autosurgeon/testicles = 1,
/obj/item/storage/box/marshmallow = 2,
/obj/item/clothing/gloves/tackler/offbrand = 1,
/obj/item/stack/sticky_tape = 1,
"" = 3
))

View File

@@ -19,6 +19,7 @@
#define POLL_IGNORE_WIZARD "wizard"
#define POLL_IGNORE_CLONE "clone"
#define POLL_IGNORE_CONTRACTOR_SUPPORT "contractor_support"
#define POLL_IGNORE_FUGITIVE "fugitive"
GLOBAL_LIST_INIT(poll_ignore_desc, list(
POLL_IGNORE_SENTIENCE_POTION = "Sentience potion",
@@ -39,7 +40,8 @@ GLOBAL_LIST_INIT(poll_ignore_desc, list(
POLL_IGNORE_DEMON = "Demons",
POLL_IGNORE_WIZARD = "Wizards",
POLL_IGNORE_CLONE = "Defective/SDGF clones",
POLL_IGNORE_CONTRACTOR_SUPPORT = "Contractor Support Unit"
POLL_IGNORE_CONTRACTOR_SUPPORT = "Contractor Support Unit",
POLL_IGNORE_FUGITIVE = "Fugitive Hunter"
))
GLOBAL_LIST_INIT(poll_ignore, init_poll_ignore())

View File

@@ -93,6 +93,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_PASSTABLE" = TRAIT_PASSTABLE,
"TRAIT_GIANT" = TRAIT_GIANT,
"TRAIT_DWARF" = TRAIT_DWARF,
"TRAIT_NICE_SHOT" = TRAIT_NICE_SHOT,
"TRAIT_COMBAT_MODE_LOCKED" = TRAIT_COMBAT_MODE_LOCKED,
"TRAIT_SPRINT_LOCKED" = TRAIT_SPRINT_LOCKED,
"TRAIT_AUTO_CATCH_ITEM" = TRAIT_AUTO_CATCH_ITEM,
@@ -116,7 +117,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_MASO" = TRAIT_MASO,
"TRAIT_HIGH_BLOOD" = TRAIT_HIGH_BLOOD,
"TRAIT_EMPATH" = TRAIT_EMPATH,
"TRAIT_FRIENDLY" = TRAIT_FRIENDLY
"TRAIT_FRIENDLY" = TRAIT_FRIENDLY,
"TRAIT_NICE_SHOT" = TRAIT_NICE_SHOT
),
/obj/item/bodypart = list(
"TRAIT_PARALYSIS" = TRAIT_PARALYSIS

View File

@@ -76,7 +76,7 @@
if(check_click_intercept(params,A))
return
if(notransform)
if(mob_transforming)
return
if(SEND_SIGNAL(src, COMSIG_MOB_CLICKON, A, params) & COMSIG_MOB_CANCEL_CLICKON)

View File

@@ -242,7 +242,7 @@ If you're feeling frisky, examine yourself and click the underlined item to pull
/obj/screen/alert/embeddedobject/Click()
if(isliving(usr))
var/mob/living/carbon/human/M = usr
var/mob/living/carbon/M = usr
return M.help_shake_act(M)
/obj/screen/alert/weightless

View File

@@ -521,3 +521,10 @@
* Camera mobs, AIs, ghosts and some other are of course exempt from this. This also doesn't influence simplemob AI, for the best.
*/
/datum/config_entry/flag/use_field_of_vision
//Shuttle size limiter
/datum/config_entry/number/max_shuttle_count
config_entry_value = 6
/datum/config_entry/number/max_shuttle_size
config_entry_value = 250

View File

@@ -76,6 +76,8 @@ SUBSYSTEM_DEF(air)
var/list/pipenet_rebuilds = pipenets_needing_rebuilt
for(var/thing in pipenet_rebuilds)
var/obj/machinery/atmospherics/AT = thing
if(!istype(AT))
continue
AT.build_network()
cost_rebuilds = MC_AVERAGE(cost_rebuilds, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
pipenets_needing_rebuilt.Cut()

View File

@@ -1,8 +1,8 @@
SUBSYSTEM_DEF(minimaps)
name = "Minimaps"
flags = SS_NO_FIRE
var/list/station_minimaps
var/datum/minimap_group/station_minimap
var/list/station_minimaps = list()
var/datum/minimap_group/station_minimap = null
/datum/controller/subsystem/minimaps/Initialize()
if(!CONFIG_GET(flag/minimaps_enabled))
@@ -12,9 +12,9 @@ SUBSYSTEM_DEF(minimaps)
return ..()
/datum/controller/subsystem/minimaps/proc/build_minimaps()
station_minimaps = list()
for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
var/datum/space_level/SL = SSmapping.get_level(z)
var/name = (SL.name == initial(SL.name))? "[z] - Station" : "[z] - [SL.name]"
station_minimaps += new /datum/minimap(z, name = name)
station_minimap = new(station_minimaps, "Station")

View File

@@ -23,7 +23,7 @@ SUBSYSTEM_DEF(npcpool)
var/mob/living/simple_animal/SA = currentrun[currentrun.len]
--currentrun.len
if(!SA.ckey && !SA.notransform)
if(!SA.ckey && !SA.mob_transforming)
if(SA.stat != DEAD)
SA.handle_automated_movement()
if(SA.stat != DEAD)

View File

@@ -409,7 +409,7 @@ SUBSYSTEM_DEF(ticker)
var/mob/living = player.transfer_character()
if(living)
qdel(player)
living.notransform = TRUE
living.mob_transforming = TRUE
if(living.client)
if (living.client.prefs && living.client.prefs.auto_ooc)
if (living.client.prefs.chat_toggles & CHAT_OOC)
@@ -423,7 +423,7 @@ SUBSYSTEM_DEF(ticker)
/datum/controller/subsystem/ticker/proc/release_characters(list/livings)
for(var/I in livings)
var/mob/living/L = I
L.notransform = FALSE
L.mob_transforming = FALSE
/datum/controller/subsystem/ticker/proc/send_tip_of_the_round()
var/m

View File

@@ -123,7 +123,9 @@
trauma = _trauma
return ..()
/mob/living/split_personality/Life()
/mob/living/split_personality/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
if(QDELETED(body))
qdel(src) //in case trauma deletion doesn't already do it
@@ -132,8 +134,6 @@
trauma.switch_personalities()
qdel(trauma)
..()
/mob/living/split_personality/Login()
..()
to_chat(src, "<span class='notice'>As a split personality, you cannot do anything but observe. However, you will eventually gain control of your body, switching places with the current personality.</span>")

View File

@@ -97,20 +97,20 @@
[get_footer()]
"}
/datum/browser/proc/open(use_onclose = 1)
/datum/browser/proc/open(use_onclose = TRUE)
if(isnull(window_id)) //null check because this can potentially nuke goonchat
WARNING("Browser [title] tried to open with a null ID")
to_chat(user, "<span class='userdanger'>The [title] browser you tried to open failed a sanity check! Please report this on github!</span>")
return
var/window_size = ""
if (width && height)
if(width && height)
window_size = "size=[width]x[height];"
if (stylesheets.len)
if(stylesheets.len)
send_asset_list(user, stylesheets)
if (scripts.len)
if(scripts.len)
send_asset_list(user, scripts)
user << browse(get_content(), "window=[window_id];[window_size][window_options]")
if (use_onclose)
if(use_onclose)
setup_onclose()
/datum/browser/proc/setup_onclose()
@@ -157,7 +157,7 @@
close()
//designed as a drop in replacement for alert(); functions the same. (outside of needing User specified)
/proc/tgalert(var/mob/User, Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1, Timeout = 6000)
/proc/tgalert(mob/User, Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1, Timeout = 6000)
if (!User)
User = usr
switch(askuser(User, Message, Title, Button1, Button2, Button3, StealFocus, Timeout))

View File

@@ -114,7 +114,7 @@
// BYOND Bug #2563917
// Construct text
var/static/regex/html_metachars = new(@"&[A-Za-z]{1,7};", "g")
var/complete_text = "<span class='center maptext [extra_classes.Join(" ")]' style='color: [tgt_color]'>[text]</span>"
var/complete_text = "<span class='center maptext [extra_classes.Join(" ")]' style='color: [tgt_color]'>[owner.say_emphasis(text)]</span>"
var/mheight = WXH_TO_HEIGHT(owned_by.MeasureText(replacetext(complete_text, html_metachars, "m"), null, CHAT_MESSAGE_WIDTH))
approx_lines = max(1, mheight / CHAT_MESSAGE_APPROX_LHEIGHT)

View File

@@ -30,7 +30,7 @@ GLOBAL_LIST_EMPTY(cinematics)
/datum/cinematic
var/id = CINEMATIC_DEFAULT
var/list/watching = list() //List of clients watching this
var/list/locked = list() //Who had notransform set during the cinematic
var/list/locked = list() //Who had mob_transforming set during the cinematic
var/is_global = FALSE //Global cinematics will override mob-specific ones
var/obj/screen/cinematic/screen
var/datum/callback/special_callback //For special effects synced with animation (explosions after the countdown etc)
@@ -45,7 +45,7 @@ GLOBAL_LIST_EMPTY(cinematics)
GLOB.cinematics -= src
QDEL_NULL(screen)
for(var/mob/M in locked)
M.notransform = FALSE
M.mob_transforming = FALSE
return ..()
/datum/cinematic/proc/play(watchers)
@@ -70,7 +70,7 @@ GLOBAL_LIST_EMPTY(cinematics)
for(var/mob/M in GLOB.mob_list)
if(M in watchers)
M.notransform = TRUE //Should this be done for non-global cinematics or even at all ?
M.mob_transforming = TRUE //Should this be done for non-global cinematics or even at all ?
locked += M
//Close watcher ui's
SStgui.close_user_uis(M)
@@ -79,7 +79,7 @@ GLOBAL_LIST_EMPTY(cinematics)
M.client.screen += screen
else
if(is_global)
M.notransform = TRUE
M.mob_transforming = TRUE
locked += M
//Actually play it
@@ -254,4 +254,4 @@ Nuke.Explosion()
Narsie()
-> Cinematic(CULT,world)
*/
*/

View File

@@ -38,8 +38,9 @@
parent = raw_args[1]
var/list/arguments = raw_args.Copy(2)
if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE)
stack_trace("Incompatible [type] assigned to a [parent.type]! args: [json_encode(arguments)]")
qdel(src, TRUE, TRUE)
CRASH("Incompatible [type] assigned to a [parent.type]! args: [json_encode(arguments)]")
return
_JoinParent(parent)

View File

@@ -109,7 +109,7 @@
AM.visible_message("<span class='boldwarning'>[AM] falls into [parent]!</span>", "<span class='userdanger'>[oblivion_message]</span>")
if (isliving(AM))
var/mob/living/L = AM
L.notransform = TRUE
L.mob_transforming = TRUE
L.Paralyze(200)
var/oldtransform = AM.transform

View File

@@ -124,9 +124,9 @@
category = CAT_MISC
subcategory = CAT_TOOL
/datum/crafting_recipe/bruise_pack
name = "Bruise Pack"
result = /obj/item/stack/medical/bruise_pack/one
/datum/crafting_recipe/brute_pack
name = "Suture Pack"
result = /obj/item/stack/medical/suture/one
time = 1
reqs = list(/obj/item/stack/medical/gauze = 1,
/datum/reagent/medicine/styptic_powder = 10)
@@ -134,8 +134,8 @@
subcategory = CAT_TOOL
/datum/crafting_recipe/burn_pack
name = "Burn Ointment"
result = /obj/item/stack/medical/ointment/one
name = "Regenerative Mesh"
result = /obj/item/stack/medical/mesh/one
time = 1
reqs = list(/obj/item/stack/medical/gauze = 1,
/datum/reagent/medicine/silver_sulfadiazine = 10)

View File

@@ -0,0 +1,363 @@
/*
This component is responsible for handling individual instances of embedded objects. The embeddable element is what allows an item to be embeddable and stores its embedding stats,
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.
There are 2 different things that can be embedded presently: carbons, and closed turfs (see: walls)
- 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 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 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
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
- 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"
- Sticking occurs when an item should not cause any harm while embedding (imagine throwing a sticky ball of tape at someone, rather than a shuriken). An item is considered "sticky"
when it has 0 for both pain multiplier and jostle pain multiplier. It's a bit arbitrary, but fairly straightforward.
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"
-- There is no slicing sound on impact
-- All damage checks and bloodloss are skipped for carbons
-- Pointy objects create sparks when embedding into a turf
*/
/datum/component/embedded
dupe_mode = COMPONENT_DUPE_ALLOWED
var/obj/item/bodypart/limb
var/obj/item/weapon
// all of this stuff is explained in _DEFINES/combat.dm
var/embed_chance // not like we really need it once we're already stuck in but hey
var/fall_chance
var/pain_chance
var/pain_mult
var/impact_pain_mult
var/remove_pain_mult
var/rip_time
var/ignore_throwspeed_threshold
var/jostle_chance
var/jostle_pain_mult
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
var/harmful
var/mutable_appearance/overlay
/datum/component/embedded/Initialize(obj/item/I,
datum/thrownthing/throwingdatum,
obj/item/bodypart/part,
embed_chance = EMBED_CHANCE,
fall_chance = EMBEDDED_ITEM_FALLOUT,
pain_chance = EMBEDDED_PAIN_CHANCE,
pain_mult = EMBEDDED_PAIN_MULTIPLIER,
remove_pain_mult = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER,
impact_pain_mult = EMBEDDED_IMPACT_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,
embed_chance_turf_mod = EMBED_CHANCE_TURF_MOD)
if((!iscarbon(parent) && !isclosedturf(parent)) || !isitem(I))
return COMPONENT_INCOMPATIBLE
if(part)
limb = part
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
src.weapon = I
if(!weapon.isEmbedHarmless())
harmful = TRUE
weapon.embedded(parent)
if(iscarbon(parent))
initCarbon()
else if(isclosedturf(parent))
initTurf(throwingdatum)
/datum/component/embedded/RegisterWithParent()
if(iscarbon(parent))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/jostleCheck)
RegisterSignal(parent, COMSIG_CARBON_EMBED_RIP, .proc/ripOutCarbon)
RegisterSignal(parent, COMSIG_CARBON_EMBED_REMOVAL, .proc/safeRemoveCarbon)
else if(isclosedturf(parent))
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/examineTurf)
RegisterSignal(parent, COMSIG_PARENT_QDELETING, .proc/itemMoved)
/datum/component/embedded/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_MOVABLE_MOVED, COMSIG_CARBON_EMBED_RIP, COMSIG_CARBON_EMBED_REMOVAL, COMSIG_PARENT_EXAMINE))
/datum/component/embedded/process()
if(iscarbon(parent))
processCarbon()
/datum/component/embedded/Destroy()
if(weapon)
UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING))
if(overlay)
var/atom/A = parent
A.cut_overlay(overlay, TRUE)
qdel(overlay)
return ..()
////////////////////////////////////////
/////////////HUMAN PROCS////////////////
////////////////////////////////////////
/// 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/initCarbon()
START_PROCESSING(SSdcs, src)
var/mob/living/carbon/victim = parent
if(!istype(limb))
limb = pick(victim.bodyparts)
limb.embedded_objects |= weapon // on the inside... on the inside...
weapon.forceMove(victim)
RegisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING), .proc/byeItemCarbon)
if(harmful)
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)
playsound(victim,'sound/weapons/bladeslice.ogg', 40)
weapon.add_mob_blood(victim)//it embedded itself in you, of course it's bloody!
var/damage = weapon.w_class * impact_pain_mult
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)
else
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.
/datum/component/embedded/proc/jostleCheck()
var/mob/living/carbon/victim = parent
var/chance = jostle_chance
if(victim.m_intent == MOVE_INTENT_WALK || !(victim.mobility_flags & MOBILITY_STAND))
chance *= 0.5
if(harmful && prob(chance))
var/damage = weapon.w_class * jostle_pain_mult
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] jostles and stings!</span>")
/// Called when then item randomly falls out of a carbon. This handles the damage and descriptors, then calls safe_remove()
/datum/component/embedded/proc/fallOutCarbon()
var/mob/living/carbon/victim = parent
if(harmful)
var/damage = weapon.w_class * remove_pain_mult
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 [limb.name]!</span>", ignored_mobs=victim)
to_chat(victim, "<span class='userdanger'>[weapon] falls out of your [limb.name]!</span>")
else
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>")
safeRemoveCarbon()
/// 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/ripOutCarbon(datum/source, obj/item/I, obj/item/bodypart/limb)
if(I != weapon || src.limb != limb)
return
var/mob/living/carbon/victim = parent
var/time_taken = rip_time * weapon.w_class
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(!weapon || !limb || weapon.loc != victim || !(weapon in limb.embedded_objects))
qdel(src)
return
if(harmful)
var/damage = weapon.w_class * remove_pain_mult
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.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
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>")
safeRemoveCarbon(TRUE)
/// 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
/datum/component/embedded/proc/safeRemoveCarbon(to_hands)
var/mob/living/carbon/victim = parent
limb.embedded_objects -= weapon
UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING)) // have to unhook these here so they don't also register as having disappeared
if(!weapon)
if(!victim.has_embedded_objects())
victim.clear_alert("embeddedobject")
SEND_SIGNAL(victim, COMSIG_CLEAR_MOOD_EVENT, "embedded")
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)
victim.put_in_hands(weapon)
else
weapon.forceMove(get_turf(victim))
if(!victim.has_embedded_objects())
victim.clear_alert("embeddedobject")
SEND_SIGNAL(victim, COMSIG_CLEAR_MOOD_EVENT, "embedded")
qdel(src)
/// Something deleted or moved our weapon while it was embedded, how rude!
/datum/component/embedded/proc/byeItemCarbon()
var/mob/living/carbon/victim = parent
limb.embedded_objects -= weapon
UnregisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING))
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))
qdel(src)
if(victim.stat == DEAD)
return
var/damage = weapon.w_class * pain_mult
var/chance = pain_chance
if(pain_stam_pct && IS_STAMCRIT(victim)) //if it's a less-lethal embed, give them a break if they're already stamcritted
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))
fallOutCarbon()
////////////////////////////////////////
//////////////TURF PROCS////////////////
////////////////////////////////////////
/// Turfs are much lower maintenance, since we don't care if they're in pain, but since they don't bleed or scream, we draw an overlay to show their status.
/// The only difference pointy/sticky items make here is text descriptors and pointy objects making a spark shower on impact.
/datum/component/embedded/proc/initTurf(datum/thrownthing/throwingdatum)
var/turf/closed/hit = parent
// we can't store the item IN the turf (cause turfs are just kinda... there), so we fake it by making the item invisible and bailing if it moves due to a blast
weapon.forceMove(hit)
weapon.invisibility = INVISIBILITY_ABSTRACT
RegisterSignal(weapon, COMSIG_MOVABLE_MOVED, .proc/itemMoved)
var/pixelX = rand(-2, 2)
var/pixelY = rand(-1, 3) // bias this upwards since in-hands are usually on the lower end of the sprite
switch(throwingdatum.init_dir)
if(NORTH)
pixelY -= 2
if(SOUTH)
pixelY += 2
if(WEST)
pixelX += 2
if(EAST)
pixelX -= 2
if(throwingdatum.init_dir in list(NORTH, WEST, NORTHWEST, SOUTHWEST))
overlay = mutable_appearance(icon=weapon.righthand_file,icon_state=weapon.item_state)
else
overlay = mutable_appearance(icon=weapon.lefthand_file,icon_state=weapon.item_state)
var/matrix/M = matrix()
M.Translate(pixelX, pixelY)
overlay.transform = M
hit.add_overlay(overlay, TRUE)
if(harmful)
hit.visible_message("<span class='danger'>[weapon] embeds itself in [hit]!</span>")
playsound(hit,'sound/weapons/bladeslice.ogg', 70)
var/datum/effect_system/spark_spread/sparks = new
sparks.set_up(1, 1, parent)
sparks.attach(parent)
sparks.start()
else
hit.visible_message("<span class='danger'>[weapon] sticks itself to [hit]!</span>")
/datum/component/embedded/proc/examineTurf(datum/source, mob/user, list/examine_list)
if(harmful)
examine_list += "\t <a href='?src=[REF(src)];embedded_object=[REF(weapon)]' class='warning'>There is \a [weapon] embedded in [parent]!</a>"
else
examine_list += "\t <a href='?src=[REF(src)];embedded_object=[REF(weapon)]' class='warning'>There is \a [weapon] stuck to [parent]!</a>"
/// Someone is ripping out the item from the turf by hand
/datum/component/embedded/Topic(datum/source, href_list)
var/mob/living/us = usr
if(in_range(us, parent) && locate(href_list["embedded_object"]) == weapon)
if(harmful)
us.visible_message("<span class='notice'>[us] begins unwedging [weapon] from [parent].</span>", "<span class='notice'>You begin unwedging [weapon] from [parent]...</span>")
else
us.visible_message("<span class='notice'>[us] begins unsticking [weapon] from [parent].</span>", "<span class='notice'>You begin unsticking [weapon] from [parent]...</span>")
if(do_after(us, 30, target = parent))
us.put_in_hands(weapon)
weapon.unembedded()
qdel(src)
/// This proc handles if something knocked the invisible item loose from the turf somehow (probably an explosion). Just make it visible and say it fell loose, then get outta here.
/datum/component/embedded/proc/itemMoved()
weapon.invisibility = initial(weapon.invisibility)
weapon.visible_message("<span class='notice'>[weapon] falls loose from [parent].</span>")
weapon.unembedded()
qdel(src)

View File

@@ -144,7 +144,7 @@
var/obj/item/projectile/picked_projectiletype = pickweight(weighted_projectile_types)
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"
/datum/fantasy_affix/strength

View File

@@ -1,36 +1,37 @@
/datum/component/shrapnel
/datum/component/mirv
var/projectile_type
var/radius // shoots a projectile for every turf on this radius from the hit target
var/override_projectile_range
/datum/component/shrapnel/Initialize(projectile_type, radius=1, override_projectile_range)
if(!isgun(parent) && !ismachinery(parent) && !isstructure(parent))
/datum/component/mirv/Initialize(projectile_type, radius=1, override_projectile_range)
if(!isgun(parent) && !ismachinery(parent) && !isstructure(parent) && !isgrenade(parent))
return COMPONENT_INCOMPATIBLE
src.projectile_type = projectile_type
src.radius = radius
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
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
/datum/component/shrapnel/UnregisterFromParent()
/datum/component/mirv/UnregisterFromParent()
. = ..()
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)
/datum/component/shrapnel/proc/do_shrapnel(mob/firer, atom/target)
/datum/component/mirv/proc/do_shrapnel(mob/firer, atom/target)
if(radius < 1)
return
var/turf/target_turf = get_turf(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:
P.range = radius+1
if(override_projectile_range)

View File

@@ -0,0 +1,74 @@
/**
* omen.dm: For when you want someone to have a really bad day
*
* When you attach an omen component to someone, they start running the risk of all sorts of bad environmental injuries, like nearby vending machines randomly falling on you,
* or hitting your head really hard when you slip and fall, or... well, for now those two are all I have. More will come.
*
* Omens are removed once the victim is either maimed by one of the possible injuries, or if they receive a blessing (read: bashing with a bible) from the chaplain.
*/
/datum/component/omen
dupe_mode = COMPONENT_DUPE_UNIQUE
/// Whatever's causing the omen, if there is one. Destroying the vessel won't stop the omen, but we destroy the vessel (if one exists) upon the omen ending
var/obj/vessel
/datum/component/omen/Initialize(silent=FALSE, vessel)
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
var/mob/person = parent
if(!silent)
to_chat(person, "<span class='warning'>You get a bad feeling...</span>")
src.vessel = vessel
/datum/component/omen/Destroy(force, silent)
if(vessel)
vessel.visible_message("<span class='warning'>[vessel] burns up in a sinister flash, taking an evil energy with it...</span>")
vessel = null
return ..()
/datum/component/omen/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/check_accident)
RegisterSignal(parent, COMSIG_LIVING_STATUS_KNOCKDOWN, .proc/check_slip)
RegisterSignal(parent, COMSIG_ADD_MOOD_EVENT, .proc/check_bless)
/datum/component/omen/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_LIVING_STATUS_KNOCKDOWN, COMSIG_MOVABLE_MOVED, COMSIG_ADD_MOOD_EVENT))
/**
* check_accident() is called each step we take
*
* While we're walking around, roll to see if there's any environmental hazards (currently only vending machines) on one of the adjacent tiles we can trigger.
* We do the prob() at the beginning to A. add some tension for /when/ it will strike, and B. (more importantly) ameliorate the fact that we're checking up to 5 turfs's contents each time
*/
/datum/component/omen/proc/check_accident(atom/movable/our_guy)
if(!prob(15))
return
for(var/t in get_adjacent_open_turfs(our_guy))
var/turf/the_turf = t
for(var/obj/machinery/vending/darth_vendor in the_turf)
if(darth_vendor.tiltable)
darth_vendor.tilt(our_guy)
qdel(src)
return
/// If we get knocked down, see if we have a really bad slip and bash our head hard
/datum/component/omen/proc/check_slip(mob/living/our_guy, amount)
if(amount <= 0 || prob(50)) // 50% chance to bonk our head
return
var/obj/item/bodypart/the_head = our_guy.get_bodypart(BODY_ZONE_HEAD)
if(!the_head)
return
playsound(get_turf(our_guy), "sound/effects/tableheadsmash.ogg", 90, TRUE)
our_guy.visible_message("<span class='danger'>[our_guy] hits [our_guy.p_their()] head really badly falling down!</span>", "<span class='userdanger'>You hit your head really badly falling down!</span>")
the_head.receive_damage(75)
our_guy.adjustOrganLoss(ORGAN_SLOT_BRAIN, 100)
qdel(src)
/// Hijack the mood system to see if we get the blessing mood event to cancel the omen
/datum/component/omen/proc/check_bless(mob/living/our_guy, category)
if(category != "blessing")
return
to_chat(our_guy, "<span class='nicegreen'>You feel a horrible omen lifted off your shoulders!</span>")
qdel(src)

View File

@@ -0,0 +1,283 @@
/*
* 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/item/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/item/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/item/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/item/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>")
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

View File

@@ -206,7 +206,7 @@
user.set_resting(FALSE, TRUE, FALSE)
user.forceMove(get_turf(target))
target.adjustStaminaLoss(65)
target.Paralyze(10)
target.Paralyze(10)
target.DefaultCombatKnockdown(20)
if(ishuman(target) && iscarbon(user))
target.grabbedby(user)
@@ -415,10 +415,10 @@
for(var/i = 0, i < speed, i++)
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.AddElement(/datum/element/embed, shard.embedding)
shard.updateEmbedding()
user.hitby(shard, skipcatch = TRUE, hitpush = FALSE)
//shard.embedding = list()
//shard.AddElement(/datum/element/embed, shard.embedding)
shard.embedding = list()
shard.updateEmbedding()
W.obj_destruction()
user.adjustStaminaLoss(10 * speed)
user.DefaultCombatKnockdown(40)

View File

@@ -52,9 +52,9 @@
to_chat(affected_mob, pick(stage5))
if(QDELETED(affected_mob))
return
if(affected_mob.notransform)
if(affected_mob.mob_transforming)
return
affected_mob.notransform = 1
affected_mob.mob_transforming = 1
for(var/obj/item/W in affected_mob.get_equipped_items(TRUE))
affected_mob.dropItemToGround(W)
for(var/obj/item/I in affected_mob.held_items)

View File

@@ -19,11 +19,13 @@
SHOULD_CALL_PARENT(1)
if(type == /datum/element)
return ELEMENT_INCOMPATIBLE
SEND_SIGNAL(target, COMSIG_ELEMENT_ATTACH, src)
if(element_flags & ELEMENT_DETACH)
RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/Detach, override = TRUE)
/// Deactivates the functionality defines by the element on the given datum
/datum/element/proc/Detach(datum/source, force)
SEND_SIGNAL(source, COMSIG_ELEMENT_DETACH, src)
SHOULD_CALL_PARENT(1)
UnregisterSignal(source, COMSIG_PARENT_QDELETING)

View File

@@ -0,0 +1,227 @@
/*
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
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.
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.
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.
Otherwise non-embeddable or stickable items can be made embeddable/stickable through wizard events/sticky tape/admin memes.
*/
#define STANDARD_WALL_HARDNESS 40
/datum/element/embed
element_flags = ELEMENT_BESPOKE
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
var/embed_chance
var/fall_chance
var/pain_chance
var/pain_mult
var/remove_pain_mult
var/impact_pain_mult
var/rip_time
var/ignore_throwspeed_threshold
var/jostle_chance
var/jostle_pain_mult
var/pain_stam_pct
var/payload_type
var/embed_chance_turf_mod
/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)
. = ..()
if(!isitem(target) && !isprojectile(target))
return ELEMENT_INCOMPATIBLE
if(isitem(target))
RegisterSignal(target, COMSIG_MOVABLE_IMPACT_ZONE, .proc/checkEmbedMob)
RegisterSignal(target, COMSIG_MOVABLE_IMPACT, .proc/checkEmbedOther)
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/target)
. = ..()
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
/datum/element/embed/proc/checkEmbedMob(obj/item/weapon, mob/living/carbon/victim, hit_zone, datum/thrownthing/throwingdatum, blocked = FALSE, forced = FALSE)
if(blocked || !istype(victim) || HAS_TRAIT(victim, TRAIT_PIERCEIMMUNE))
return
var/actual_chance = embed_chance
if(!weapon.isEmbedHarmless()) // all the armor in the world won't save you from a kick me sign
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 && (!HAS_TRAIT(victim, TRAIT_AUTO_CATCH_ITEM) || victim.incapacitated() || victim.get_active_held_item()))
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))
return
var/chance = embed_chance + embed_chance_turf_mod
if(iswallturf(hit))
var/turf/closed/wall/W = hit
chance += 2 * (W.hardness - STANDARD_WALL_HARDNESS)
if(!forced && chance <= 0 || embed_chance_turf_mod <= -100)
return
var/pass = ((((throwingdatum ? throwingdatum.speed : weapon.throw_speed) >= EMBED_THROWSPEED_THRESHOLD) || ignore_throwspeed_threshold) && prob(chance))
if(!pass)
return
hit.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,\
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
/datum/element/embed/proc/severancePackage(obj/item/weapon, datum/element/E)
if(istype(E, /datum/element/embed))
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)
if(I.isEmbedHarmless())
examine_list += "[I] feels sticky, and could probably get stuck to someone if thrown properly!"
else
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/item/projectile/P, atom/movable/firer, atom/hit, angle, hit_zone)
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
limb = C.get_bodypart(hit_zone)
if(!limb)
limb = C.get_bodypart()
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
hit_zone = limb.body_zone
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)

View File

@@ -1,53 +0,0 @@
#define EMBEDID "embed-[embed_chance]-[embedded_fall_chance]-[embedded_pain_chance]-[embedded_pain_multiplier]-[embedded_fall_pain_multiplier]-[embedded_impact_pain_multiplier]-[embedded_unsafe_removal_pain_multiplier]-[embedded_unsafe_removal_time]"
/proc/getEmbeddingBehavior(embed_chance = EMBED_CHANCE,
embedded_fall_chance = EMBEDDED_ITEM_FALLOUT,
embedded_pain_chance = EMBEDDED_PAIN_CHANCE,
embedded_pain_multiplier = EMBEDDED_PAIN_MULTIPLIER,
embedded_fall_pain_multiplier = EMBEDDED_FALL_PAIN_MULTIPLIER,
embedded_impact_pain_multiplier = EMBEDDED_IMPACT_PAIN_MULTIPLIER,
embedded_unsafe_removal_pain_multiplier = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER,
embedded_unsafe_removal_time = EMBEDDED_UNSAFE_REMOVAL_TIME)
. = locate(EMBEDID)
if (!.)
. = new /datum/embedding_behavior(embed_chance, embedded_fall_chance, embedded_pain_chance, embedded_pain_multiplier, embedded_fall_pain_multiplier, embedded_impact_pain_multiplier, embedded_unsafe_removal_pain_multiplier, embedded_unsafe_removal_time)
/datum/embedding_behavior
var/embed_chance
var/embedded_fall_chance
var/embedded_pain_chance
var/embedded_pain_multiplier //The coefficient of multiplication for the damage this item does while embedded (this*w_class)
var/embedded_fall_pain_multiplier //The coefficient of multiplication for the damage this item does when falling out of a limb (this*w_class)
var/embedded_impact_pain_multiplier //The coefficient of multiplication for the damage this item does when first embedded (this*w_class)
var/embedded_unsafe_removal_pain_multiplier //The coefficient of multiplication for the damage removing this without surgery causes (this*w_class)
var/embedded_unsafe_removal_time //A time in ticks, multiplied by the w_class.
/datum/embedding_behavior/New(embed_chance = EMBED_CHANCE,
embedded_fall_chance = EMBEDDED_ITEM_FALLOUT,
embedded_pain_chance = EMBEDDED_PAIN_CHANCE,
embedded_pain_multiplier = EMBEDDED_PAIN_MULTIPLIER,
embedded_fall_pain_multiplier = EMBEDDED_FALL_PAIN_MULTIPLIER,
embedded_impact_pain_multiplier = EMBEDDED_IMPACT_PAIN_MULTIPLIER,
embedded_unsafe_removal_pain_multiplier = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER,
embedded_unsafe_removal_time = EMBEDDED_UNSAFE_REMOVAL_TIME)
src.embed_chance = embed_chance
src.embedded_fall_chance = embedded_fall_chance
src.embedded_pain_chance = embedded_pain_chance
src.embedded_pain_multiplier = embedded_pain_multiplier
src.embedded_fall_pain_multiplier = embedded_fall_pain_multiplier
src.embedded_impact_pain_multiplier = embedded_impact_pain_multiplier
src.embedded_unsafe_removal_pain_multiplier = embedded_unsafe_removal_pain_multiplier
src.embedded_unsafe_removal_time = embedded_unsafe_removal_time
tag = EMBEDID
/datum/embedding_behavior/proc/setRating(embed_chance, embedded_fall_chance, embedded_pain_chance, embedded_pain_multiplier, embedded_fall_pain_multiplier, embedded_impact_pain_multiplier, embedded_unsafe_removal_pain_multiplier, embedded_unsafe_removal_time)
return getEmbeddingBehavior((isnull(embed_chance) ? src.embed_chance : embed_chance),\
(isnull(embedded_fall_chance) ? src.embedded_fall_chance : embedded_fall_chance),\
(isnull(embedded_pain_chance) ? src.embedded_pain_chance : embedded_pain_chance),\
(isnull(embedded_pain_multiplier) ? src.embedded_pain_multiplier : embedded_pain_multiplier),\
(isnull(embedded_fall_pain_multiplier) ? src.embedded_fall_pain_multiplier : embedded_fall_pain_multiplier),\
(isnull(embedded_impact_pain_multiplier) ? src.embedded_impact_pain_multiplier : embedded_impact_pain_multiplier),\
(isnull(embedded_unsafe_removal_pain_multiplier) ? src.embedded_unsafe_removal_pain_multiplier : embedded_unsafe_removal_pain_multiplier),\
(isnull(embedded_unsafe_removal_time) ? src.embedded_unsafe_removal_time : embedded_unsafe_removal_time))
#undef EMBEDID

View File

@@ -27,7 +27,8 @@ GLOBAL_LIST_INIT(huds, list(
ANTAG_HUD_SOULLESS = new/datum/atom_hud/antag/hidden(),
ANTAG_HUD_CLOCKWORK = new/datum/atom_hud/antag(),
ANTAG_HUD_BROTHER = new/datum/atom_hud/antag/hidden(),
ANTAG_HUD_BLOODSUCKER = new/datum/atom_hud/antag/bloodsucker()
ANTAG_HUD_BLOODSUCKER = new/datum/atom_hud/antag/bloodsucker(),
ANTAG_HUD_FUGITIVE = new/datum/atom_hud/antag()
))
/datum/atom_hud

View File

@@ -406,6 +406,8 @@
w_class = WEIGHT_CLASS_SMALL
sharpness = IS_SHARP
var/mob/living/carbon/human/fired_by
/// if we missed our target
var/missed = TRUE
/obj/item/hardened_spike/Initialize(mapload, firedby)
. = ..()
@@ -413,13 +415,12 @@
addtimer(CALLBACK(src, .proc/checkembedded), 5 SECONDS)
/obj/item/hardened_spike/proc/checkembedded()
if(ishuman(loc))
var/mob/living/carbon/human/embedtest = loc
for(var/l in embedtest.bodyparts)
var/obj/item/bodypart/limb = l
if(src in limb.embedded_objects)
return limb
unembedded()
if(missed)
unembedded()
/obj/item/hardened_spike/embedded(atom/target)
if(isbodypart(target))
missed = FALSE
/obj/item/hardened_spike/unembedded()
var/turf/T = get_turf(src)
@@ -490,11 +491,7 @@
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
spikey.forceMove(get_turf(L))
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()

View File

@@ -165,6 +165,10 @@
port_id = "pirate"
can_be_bought = FALSE
/datum/map_template/shuttle/hunter
port_id = "hunter"
can_be_bought = FALSE
/datum/map_template/shuttle/ruin //For random shuttles in ruins
port_id = "ruin"
can_be_bought = FALSE
@@ -616,3 +620,15 @@
/datum/map_template/shuttle/snowdin/excavation
suffix = "excavation"
name = "Snowdin Excavation Elevator"
/datum/map_template/shuttle/hunter/space_cop
suffix = "space_cop"
name = "Police Spacevan"
/datum/map_template/shuttle/hunter/russian
suffix = "russian"
name = "Russian Cargo Ship"
/datum/map_template/shuttle/hunter/bounty
suffix = "bounty"
name = "Bounty Hunter Ship"

View File

@@ -795,3 +795,44 @@ datum/status_effect/pacify
name = "Genetic Breakdown"
desc = "I don't feel so good. Your body can't handle the mutations! You have one minute to remove your mutations, or you will be met with a horrible fate."
icon_state = "dna_melt"
/datum/status_effect/fake_virus
id = "fake_virus"
duration = 1800//3 minutes
status_type = STATUS_EFFECT_REPLACE
tick_interval = 1
alert_type = null
var/msg_stage = 0//so you dont get the most intense messages immediately
/datum/status_effect/fake_virus/tick()
var/fake_msg = ""
var/fake_emote = ""
switch(msg_stage)
if(0 to 300)
if(prob(1))
fake_msg = pick("<span class='warning'>[pick("Your head hurts.", "Your head pounds.")]</span>",
"<span class='warning'>[pick("You're having difficulty breathing.", "Your breathing becomes heavy.")]</span>",
"<span class='warning'>[pick("You feel dizzy.", "Your head spins.")]</span>",
"<span notice='warning'>[pick("You swallow excess mucus.", "You lightly cough.")]</span>",
"<span class='warning'>[pick("Your head hurts.", "Your mind blanks for a moment.")]</span>",
"<span class='warning'>[pick("Your throat hurts.", "You clear your throat.")]</span>")
if(301 to 600)
if(prob(2))
fake_msg = pick("<span class='warning'>[pick("Your head hurts a lot.", "Your head pounds incessantly.")]</span>",
"<span class='warning'>[pick("Your windpipe feels like a straw.", "Your breathing becomes tremendously difficult.")]</span>",
"<span class='warning'>You feel very [pick("dizzy","woozy","faint")].</span>",
"<span class='warning'>[pick("You hear a ringing in your ear.", "Your ears pop.")]</span>",
"<span class='warning'>You nod off for a moment.</span>")
else
if(prob(3))
if(prob(50))// coin flip to throw a message or an emote
fake_msg = pick("<span class='userdanger'>[pick("Your head hurts!", "You feel a burning knife inside your brain!", "A wave of pain fills your head!")]</span>",
"<span class='userdanger'>[pick("Your lungs hurt!", "It hurts to breathe!")]</span>",
"<span class='warning'>[pick("You feel nauseated.", "You feel like you're going to throw up!")]</span>")
else
fake_emote = pick("cough", "sniff", "sneeze")
if(fake_emote)
owner.emote(fake_emote)
else if(fake_msg)
to_chat(owner, fake_msg)
msg_stage++

View File

@@ -93,6 +93,13 @@
/area/shuttle/abandoned/pod
name = "Abandoned Ship Pod"
////////////////////////////Bounty Hunter Shuttles////////////////////////////
/area/shuttle/hunter
name = "Hunter Shuttle"
dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
blob_allowed = FALSE
canSmoothWithAreas = /area/shuttle/hunter
////////////////////////////Single-area shuttles////////////////////////////
/area/shuttle/transit
@@ -103,6 +110,10 @@
/area/shuttle/custom
name = "Custom player shuttle"
/area/shuttle/custom/powered
name = "Custom Powered player shuttle"
requires_power = FALSE
/area/shuttle/arrival
name = "Arrival Shuttle"
unique = TRUE // SSjob refers to this area for latejoiners

View File

@@ -8,6 +8,13 @@
var/interaction_flags_atom = NONE
var/datum/reagents/reagents = null
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
//This atom's HUD (med/sec, etc) images. Associative list.
var/list/image/hud_list = null
//HUD images that this atom can provide.
@@ -148,11 +155,20 @@
/atom/proc/check_projectile_ricochet(obj/item/projectile/P)
return (flags_1 & DEFAULT_RICOCHET_1)? PROJECTILE_RICOCHET_YES : PROJECTILE_RICOCHET_NO
/**
* Handle a projectile ricochet. Return TRUE if we did something to the projectile like reflecting it/whatnot.
*/
/atom/proc/handle_projectile_ricochet(obj/item/projectile/P)
return FALSE
/atom/proc/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))
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)
return !density
@@ -856,6 +872,14 @@
/atom/proc/GenerateTag()
return
/**
* Called after a shuttle is loaded **from map template initially**.
*
* @params
* * port - Mobile port/shuttle
* * dock - Stationary dock the shuttle's at
* * idnum - ID number of the shuttle
*/
/atom/proc/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE)
return

View File

@@ -202,7 +202,7 @@
icon_state = "moustacheg"
clumsy_check = GRENADE_NONCLUMSY_FUMBLE
/obj/item/grenade/chem_grenade/teargas/moustache/prime()
/obj/item/grenade/chem_grenade/teargas/moustache/prime(mob/living/lanced_by)
var/list/check_later = list()
for(var/mob/living/carbon/C in get_turf(src))
check_later += C

View File

@@ -288,7 +288,7 @@ datum/gang_item/clothing/shades //Addition: Why not have cool shades on a gang m
name = "Fragmentation Grenade"
id = "frag nade"
cost = 5
item_path = /obj/item/grenade/syndieminibomb/concussion/frag
item_path = /obj/item/grenade/frag
/datum/gang_item/equipment/implant_breaker
name = "Implant Breaker"

View File

@@ -92,6 +92,8 @@ Class Procs:
pressure_resistance = 15
max_integrity = 200
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
interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT

View File

@@ -336,3 +336,12 @@
name = "\improper AI upload monitor"
desc = "A telescreen that connects to the AI upload's camera network."
network = list("aiupload")
// Subtype that connects to shuttles.
/obj/machinery/computer/security/shuttle
circuit = /obj/item/circuitboard/computer/security/shuttle
/obj/machinery/computer/security/shuttle/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE)
for(var/i in network)
network -= i
network += "[idnum][i]"

View File

@@ -169,7 +169,8 @@
to_chat(user, "[src] is now in [mode] mode.")
/obj/item/grenade/barrier/prime()
/obj/item/grenade/barrier/prime(mob/living/lanced_by)
. = ..()
new /obj/structure/barricade/security(get_turf(src.loc))
switch(mode)
if(VERTICAL)

View File

@@ -417,8 +417,8 @@
// shock user with probability prb (if all connections & power are working)
// returns TRUE if shocked, FALSE otherwise
// The preceding comment was borrowed from the grille's shock script
/obj/machinery/door/airlock/proc/shock(mob/user, prb)
if(!hasPower()) // unpowered, no shock
/obj/machinery/door/airlock/proc/shock(mob/living/user, prb)
if(!istype(user) || !hasPower()) // unpowered, no shock
return FALSE
if(shockCooldown > world.time)
return FALSE //Already shocked someone recently?

View File

@@ -12,6 +12,7 @@
armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 70)
CanAtmosPass = ATMOS_PASS_DENSITY
flags_1 = PREVENT_CLICK_UNDER_1
ricochet_chance_mod = 0.8
interaction_flags_atom = INTERACT_ATOM_UI_INTERACT

View File

@@ -13,7 +13,8 @@
var/obj/item/reagent_containers/beaker
var/static/list/drip_containers = typecacheof(list(/obj/item/reagent_containers/blood,
/obj/item/reagent_containers/food,
/obj/item/reagent_containers/glass))
/obj/item/reagent_containers/glass,
/obj/item/reagent_containers/chem_pack))
/obj/machinery/iv_drip/Initialize(mapload)
. = ..()

View File

@@ -0,0 +1,33 @@
/obj/machinery/shuttle
name = "shuttle component"
desc = "Something for shuttles."
density = TRUE
obj_integrity = 250
max_integrity = 250
icon = 'icons/turf/shuttle.dmi'
icon_state = "burst_plasma"
idle_power_usage = 150
circuit = /obj/item/circuitboard/machine/shuttle/engine
var/icon_state_closed = "burst_plasma"
var/icon_state_open = "burst_plasma_open"
var/icon_state_off = "burst_plasma_off"
/obj/machinery/shuttle/Initialize()
. = ..()
GLOB.custom_shuttle_machines += src
/obj/machinery/shuttle/Destroy()
. = ..()
GLOB.custom_shuttle_machines -= src
/obj/machinery/shuttle/attackby(obj/item/I, mob/living/user, params)
if(default_deconstruction_screwdriver(user, icon_state_open, icon_state_closed, I))
return
if(default_pry_open(I))
return
if(panel_open)
if(default_change_direction_wrench(user, I))
return
if(default_deconstruction_crowbar(I))
return
return ..()

View File

@@ -0,0 +1,138 @@
//-----------------------------------------------
//-------------Engine Thrusters------------------
//-----------------------------------------------
#define ENGINE_HEAT_TARGET 600
#define ENGINE_HEATING_POWER 5000000
/obj/machinery/shuttle/engine
name = "shuttle thruster"
desc = "A thruster for shuttles."
density = TRUE
obj_integrity = 250
max_integrity = 250
icon = 'icons/turf/shuttle.dmi'
icon_state = "burst_plasma"
idle_power_usage = 150
circuit = /obj/item/circuitboard/machine/shuttle/engine
var/thrust = 0
var/fuel_use = 0
var/bluespace_capable = TRUE
var/cooldown = 0
var/thruster_active = FALSE
var/datum/weakref/attached_heater
/obj/machinery/shuttle/engine/plasma
name = "plasma thruster"
desc = "A thruster that burns plasma stored in an adjacent plasma thruster heater."
icon_state = "burst_plasma"
icon_state_off = "burst_plasma_off"
idle_power_usage = 0
circuit = /obj/item/circuitboard/machine/shuttle/engine/plasma
thrust = 25
fuel_use = 0.24
bluespace_capable = FALSE
cooldown = 45
/obj/machinery/shuttle/engine/void
name = "void thruster"
desc = "A thruster using technology to breach voidspace for propulsion."
icon_state = "burst_void"
icon_state_off = "burst_void"
icon_state_closed = "burst_void"
icon_state_open = "burst_void_open"
idle_power_usage = 0
circuit = /obj/item/circuitboard/machine/shuttle/engine/void
thrust = 400
fuel_use = 0
bluespace_capable = TRUE
cooldown = 90
/obj/machinery/shuttle/engine/Initialize()
. = ..()
check_setup()
/obj/machinery/shuttle/engine/on_construction()
. = ..()
check_setup()
/obj/machinery/shuttle/engine/Destroy()
attached_heater = FALSE
thruster_active = FALSE
return ..()
/obj/machinery/shuttle/engine/proc/check_setup()
if(!anchored)
attached_heater = null
update_engine()
return
var/heater_turf
switch(dir)
if(NORTH)
heater_turf = get_offset_target_turf(src, 0, 1)
if(SOUTH)
heater_turf = get_offset_target_turf(src, 0, -1)
if(EAST)
heater_turf = get_offset_target_turf(src, 1, 0)
if(WEST)
heater_turf = get_offset_target_turf(src, -1, 0)
if(!heater_turf)
attached_heater = null
update_engine()
return
attached_heater = null
for(var/obj/machinery/atmospherics/components/unary/shuttle/heater/as_heater in heater_turf)
if(as_heater.dir != dir)
continue
if(as_heater.panel_open)
continue
if(!as_heater.anchored)
continue
attached_heater = WEAKREF(as_heater)
break
update_engine()
return
/obj/machinery/shuttle/engine/proc/update_engine()
if(!attached_heater)
icon_state = icon_state_off
thruster_active = FALSE
return
var/obj/machinery/atmospherics/components/unary/shuttle/heater/resolved_heater = attached_heater.resolve()
if(panel_open)
thruster_active = FALSE
else if(resolved_heater?.hasFuel(1))
icon_state = icon_state_closed
thruster_active = TRUE
else
thruster_active = FALSE
icon_state = icon_state_off
return
/obj/machinery/shuttle/engine/void/update_engine()
if(panel_open)
thruster_active = FALSE
return
thruster_active = TRUE
icon_state = icon_state_closed
return
//Thanks to spaceheater.dm for inspiration :)
/obj/machinery/shuttle/engine/proc/fireEngine()
var/turf/heatTurf = loc
if(!heatTurf)
return
var/datum/gas_mixture/env = heatTurf.return_air()
var/heat_cap = env.heat_capacity()
var/req_power = abs(env.return_temperature() - ENGINE_HEAT_TARGET) * heat_cap
req_power = min(req_power, ENGINE_HEATING_POWER)
var/deltaTemperature = req_power / heat_cap
if(deltaTemperature < 0)
return
env.temperature += deltaTemperature
air_update_turf()
/obj/machinery/shuttle/engine/default_change_direction_wrench(mob/user, obj/item/I)
. = ..()
update_engine()

View File

@@ -0,0 +1,132 @@
//-----------------------------------------------
//--------------Engine Heaters-------------------
//This uses atmospherics, much like a thermomachine,
//but instead of changing temp, it stores plasma and uses
//it for the engine.
//-----------------------------------------------
/obj/machinery/atmospherics/components/unary/shuttle
name = "shuttle atmospherics device"
desc = "This does something to do with shuttle atmospherics"
icon_state = "heater"
icon = 'icons/turf/shuttle.dmi'
/obj/machinery/atmospherics/components/unary/shuttle/heater
name = "engine heater"
desc = "Directs energy into compressed particles in order to power an attached thruster."
icon_state = "heater_pipe"
var/icon_state_closed = "heater_pipe"
var/icon_state_open = "heater_pipe_open"
var/icon_state_off = "heater_pipe"
idle_power_usage = 50
circuit = /obj/item/circuitboard/machine/shuttle/heater
density = TRUE
max_integrity = 400
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 30)
layer = OBJ_LAYER
showpipe = TRUE
pipe_flags = PIPING_ONE_PER_TURF | PIPING_DEFAULT_LAYER_ONLY
var/gas_type = /datum/gas/plasma
var/efficiency_multiplier = 1
var/gas_capacity = 0
/obj/machinery/atmospherics/components/unary/shuttle/heater/New()
. = ..()
GLOB.custom_shuttle_machines += src
SetInitDirections()
update_adjacent_engines()
updateGasStats()
/obj/machinery/atmospherics/components/unary/shuttle/heater/Destroy()
. = ..()
update_adjacent_engines()
GLOB.custom_shuttle_machines -= src
/obj/machinery/atmospherics/components/unary/shuttle/heater/on_construction()
..(dir, dir)
SetInitDirections()
update_adjacent_engines()
/obj/machinery/atmospherics/components/unary/shuttle/heater/default_change_direction_wrench(mob/user, obj/item/I)
if(!..())
return FALSE
SetInitDirections()
var/obj/machinery/atmospherics/node = nodes[1]
if(node)
node.disconnect(src)
nodes[1] = null
if(!parents[1])
return
nullifyPipenet(parents[1])
atmosinit()
node = nodes[1]
if(node)
node.atmosinit()
node.addMember(src)
build_network()
return TRUE
/obj/machinery/atmospherics/components/unary/shuttle/heater/RefreshParts()
var/cap = 0
var/eff = 0
for(var/obj/item/stock_parts/matter_bin/M in component_parts)
cap += M.rating
for(var/obj/item/stock_parts/micro_laser/L in component_parts)
eff += L.rating
gas_capacity = 5000 * ((cap - 1) ** 2) + 1000
efficiency_multiplier = round(((eff / 2) / 2.8) ** 2, 0.1)
updateGasStats()
/obj/machinery/atmospherics/components/unary/shuttle/heater/examine(mob/user)
. = ..()
var/datum/gas_mixture/air_contents = airs[1]
. += "The engine heater's gas dial reads [air_contents.return_volume()] liters in internal tank.<br>"
/obj/machinery/atmospherics/components/unary/shuttle/heater/proc/updateGasStats()
var/datum/gas_mixture/air_contents = airs[1]
if(!air_contents)
return
air_contents.volume = gas_capacity
air_contents.temperature = T20C
/obj/machinery/atmospherics/components/unary/shuttle/heater/proc/hasFuel(var/required)
var/datum/gas_mixture/air_contents = airs[1]
var/moles = air_contents.total_moles()
return moles >= required
/obj/machinery/atmospherics/components/unary/shuttle/heater/proc/consumeFuel(var/amount)
var/datum/gas_mixture/air_contents = airs[1]
air_contents.remove(amount)
return
/obj/machinery/atmospherics/components/unary/shuttle/heater/attackby(obj/item/I, mob/living/user, params)
update_adjacent_engines()
if(default_deconstruction_screwdriver(user, icon_state_open, icon_state_closed, I))
return
if(default_pry_open(I))
return
if(panel_open)
if(default_change_direction_wrench(user, I))
return
if(default_deconstruction_crowbar(I))
return
return ..()
/obj/machinery/atmospherics/components/unary/shuttle/heater/proc/update_adjacent_engines()
var/engine_turf
switch(dir)
if(NORTH)
engine_turf = get_offset_target_turf(src, 0, -1)
if(SOUTH)
engine_turf = get_offset_target_turf(src, 0, 1)
if(EAST)
engine_turf = get_offset_target_turf(src, -1, 0)
if(WEST)
engine_turf = get_offset_target_turf(src, 1, 0)
if(!engine_turf)
return
for(var/obj/machinery/shuttle/engine/E in engine_turf)
E.check_setup()

View File

@@ -428,6 +428,11 @@
desc = "A poster decipting a snake shaped into an ominous 'S'!"
icon_state = "poster47"
/obj/structure/sign/poster/contraband/bountyhunters
name = "Bounty Hunters"
desc = "A poster advertising bounty hunting services. \"I hear you got a problem.\""
icon_state = "poster48"
/obj/structure/sign/poster/official
poster_item_name = "motivational poster"
poster_item_desc = "An official Nanotrasen-issued poster to foster a compliant and obedient workforce. It comes with state-of-the-art adhesive backing, for easy pinning to any vertical surface."

View File

@@ -5,19 +5,21 @@
anchored = TRUE
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "uglymine"
var/triggered = 0
/// We manually check to see if we've been triggered in case multiple atoms cross us in the time between the mine being triggered and it actually deleting, to avoid a race condition with multiple detonations
var/triggered = FALSE
/obj/effect/mine/proc/mineEffect(mob/victim)
to_chat(victim, "<span class='danger'>*click*</span>")
/obj/effect/mine/Crossed(AM as mob|obj)
if(isturf(loc))
if(ismob(AM))
var/mob/MM = AM
if(!(MM.movement_type & FLYING))
triggermine(AM)
else
triggermine(AM)
/obj/effect/mine/Crossed(atom/movable/AM)
if(triggered || !isturf(loc))
return
. = ..()
if(AM.movement_type & FLYING)
return
triggermine(AM)
/obj/effect/mine/proc/triggermine(mob/victim)
if(triggered)
@@ -27,9 +29,13 @@
s.set_up(3, 1, src)
s.start()
mineEffect(victim)
SEND_SIGNAL(src, COMSIG_MINE_TRIGGERED)
triggered = 1
qdel(src)
/obj/effect/mine/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir)
. = ..()
triggermine()
/obj/effect/mine/explosive
name = "explosive mine"
@@ -50,6 +56,18 @@
if(isliving(victim))
victim.DefaultCombatKnockdown(stun_time)
/obj/effect/mine/shrapnel
name = "shrapnel mine"
var/shrapnel_type = /obj/item/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/item/projectile/bullet/pellet/stingball
/obj/effect/mine/kickmine
name = "kick mine"
@@ -105,7 +123,7 @@
/obj/effect/mine/pickup/triggermine(mob/victim)
if(triggered)
return
triggered = 1
triggered = TRUE
invisibility = INVISIBILITY_ABSTRACT
mineEffect(victim)
qdel(src)

View File

@@ -491,7 +491,7 @@
/obj/item/kitchen/knife = 5,
/obj/item/screwdriver = 5,
/obj/item/crowbar/red = 1, //Dont you need a crowbar to open this?
/obj/item/stack/medical/bruise_pack = 3,
/obj/item/stack/medical/suture = 3,
/obj/item/reagent_containers/food/drinks/bottle/vodka = 2,
/obj/item/radio = 5,
/obj/item/flashlight = 4,

View File

@@ -4,6 +4,9 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
// if true, everyone item when created will have its name changed to be
// more... RPG-like.
GLOBAL_VAR_INIT(stickpocalypse, FALSE) // if true, all non-embeddable items will be able to harmlessly stick to people when thrown
GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to embed in people, takes precedence over stickpocalypse
/obj/item
name = "item"
icon = 'icons/obj/items_and_weapons.dmi'
@@ -104,7 +107,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
mouse_drag_pointer = MOUSE_ACTIVE_POINTER //the icon to indicate this object is being dragged
var/datum/embedding_behavior/embedding
var/list/embedding = NONE
var/flags_cover = 0 //for flags such as GLASSESCOVERSEYES
var/heat = 0
@@ -159,7 +162,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
/obj/item/Initialize()
if (attack_verb)
if(attack_verb)
attack_verb = typelist("attack_verb", attack_verb)
. = ..()
@@ -167,9 +170,6 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
new path(src)
actions_types = null
if(GLOB.rpg_loot_items)
AddComponent(/datum/component/fantasy)
if(force_string)
item_flags |= FORCE_STRING_OVERRIDE
@@ -179,16 +179,6 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
if(damtype == "brute")
hitsound = "swing_hit"
if (!embedding)
embedding = getEmbeddingBehavior()
else if (islist(embedding))
embedding = getEmbeddingBehavior(arglist(embedding))
else if (!istype(embedding, /datum/embedding_behavior))
stack_trace("Invalid type [embedding.type] found in .embedding during /obj/item Initialize()")
if(sharpness) //give sharp objects butchering functionality, for consistency
AddComponent(/datum/component/butchering, 80 * toolspeed)
/obj/item/Destroy()
item_flags &= ~DROPDEL //prevent reqdels
if(ismob(loc))
@@ -198,6 +188,26 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
qdel(X)
return ..()
/obj/item/ComponentInitialize()
. = ..()
// this proc says it's for initializing components, but we're initializing elements too because it's you and me against the world >:)
if(!LAZYLEN(embedding))
if(GLOB.embedpocalypse)
embedding = EMBED_POINTY
name = "pointy [name]"
else if(GLOB.stickpocalypse)
embedding = EMBED_HARMLESS
name = "sticky [name]"
updateEmbedding()
if(GLOB.rpg_loot_items)
AddComponent(/datum/component/fantasy)
if(sharpness) //give sharp objects butchering functionality, for consistency
AddComponent(/datum/component/butchering, 80 * toolspeed)
/obj/item/proc/check_allowed_items(atom/target, not_inside, target_self)
if(((src in target) && !target_self) || (!isturf(target.loc) && !isturf(target) && not_inside))
return 0
@@ -247,7 +257,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
. += "[src] is made of cold-resistant materials."
if(resistance_flags & FIRE_PROOF)
. += "[src] is made of fire-retardant materials."
if(item_flags & (ITEM_CAN_BLOCK | ITEM_CAN_PARRY))
var/datum/block_parry_data/data = return_block_parry_datum(block_parry_data)
. += "[src] has the capacity to be used to block and/or parry. <a href='?src=[REF(data)];name=[name];block=[item_flags & ITEM_CAN_BLOCK];parry=[item_flags & ITEM_CAN_PARRY];render=1'>\[Show Stats\]</a>"
@@ -625,6 +635,18 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
var/itempush = 1
if(w_class < 4)
itempush = 0 //too light to push anything
if(isliving(hit_atom)) //Living mobs handle hit sounds differently.
var/volume = get_volume_by_throwforce_and_or_w_class()
if (throwforce > 0)
if (throwhitsound)
playsound(hit_atom, throwhitsound, volume, TRUE, -1)
else if(hitsound)
playsound(hit_atom, hitsound, volume, TRUE, -1)
else
playsound(hit_atom, 'sound/weapons/genhit.ogg',volume, TRUE, -1)
else
playsound(hit_atom, 'sound/weapons/throwtap.ogg', 1, volume, -1)
return hit_atom.hitby(src, 0, itempush, throwingdatum=throwingdatum)
/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, messy_throw = TRUE)
@@ -918,11 +940,13 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
// if w_volume is 0 you fucked up anyways lol
return w_volume || AUTO_SCALE_VOLUME(w_class)
/obj/item/proc/embedded(mob/living/carbon/human/embedded_mob)
/obj/item/proc/embedded(atom/embedded_target)
return
/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.
@@ -938,3 +962,135 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
. = ..()
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.
/**
* Does the current embedding var meet the criteria for being harmless? Namely, does it explicitly define the pain multiplier and jostle pain mult to be 0? If so, return true.
*
*/
/obj/item/proc/isEmbedHarmless()
if(embedding)
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

View File

@@ -83,7 +83,7 @@
/obj/item/card/emag/bluespace
name = "bluespace cryptographic sequencer"
desc = "It's a blue card with a magnetic strip attached to some circuitry. It appears to have some sort of transmitter attached to it."
color = rgb(40, 130, 255)
icon_state = "emag_bs"
prox_check = FALSE
/obj/item/card/emag/attack()
@@ -166,6 +166,7 @@
slot_flags = ITEM_SLOT_ID
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
resistance_flags = FIRE_PROOF | ACID_PROOF
var/id_type_name = "identification card"
var/mining_points = 0 //For redeeming at mining equipment vendors
var/list/access = list()
var/registered_name = null // The name registered_name on the card
@@ -174,6 +175,8 @@
var/bank_support = ID_FREE_BANK_ACCOUNT
var/datum/bank_account/registered_account
var/obj/machinery/paystand/my_store
var/uses_overlays = TRUE
var/icon/cached_flat_icon
/obj/item/card/id/Initialize(mapload)
. = ..()
@@ -362,20 +365,38 @@
/obj/item/card/id/RemoveID()
return src
/*
Usage:
update_label()
Sets the id name to whatever registered_name and assignment is
/obj/item/card/id/update_overlays()
. = ..()
if(!uses_overlays)
return
cached_flat_icon = null
var/job = assignment ? ckey(GetJobName()) : null
if(registered_name == "Captain")
job = "captain"
if(registered_name && registered_name != "Captain")
. += mutable_appearance(icon, "assigned")
if(job)
. += mutable_appearance(icon, "id[job]")
/obj/item/card/id/proc/get_cached_flat_icon()
if(!cached_flat_icon)
cached_flat_icon = getFlatIcon(src)
return cached_flat_icon
/obj/item/card/id/get_examine_string(mob/user, thats = FALSE)
if(uses_overlays)
return "[icon2html(get_cached_flat_icon(), user)] [thats? "That's ":""][get_examine_name(user)]" //displays all overlays in chat
return ..()
update_label("John Doe", "Clowny")
Properly formats the name and occupation and sets the id name to the arguments
*/
/obj/item/card/id/proc/update_label(newname, newjob)
if(newname || newjob)
name = "[(!newname) ? "identification card" : "[newname]'s ID Card"][(!newjob) ? "" : " ([newjob])"]"
update_icon()
return
name = "[(!registered_name) ? "identification card" : "[registered_name]'s ID Card"][(!assignment) ? "" : " ([assignment])"]"
update_icon()
/obj/item/card/id/silver
name = "silver identification card"
@@ -388,6 +409,7 @@ update_label("John Doe", "Clowny")
/obj/item/card/id/silver/reaper
name = "Thirteen's ID Card (Reaper)"
access = list(ACCESS_MAINT_TUNNELS)
icon_state = "reaper"
assignment = "Reaper"
registered_name = "Thirteen"
@@ -539,7 +561,7 @@ update_label("John Doe", "Clowny")
/obj/item/card/id/ert
name = "\improper CentCom ID"
desc = "An ERT ID card."
icon_state = "centcom"
icon_state = "ert_commander"
registered_name = "Emergency Response Team Commander"
assignment = "Emergency Response Team Commander"
@@ -548,6 +570,7 @@ update_label("John Doe", "Clowny")
. = ..()
/obj/item/card/id/ert/Security
icon_state = "ert_security"
registered_name = "Security Response Officer"
assignment = "Security Response Officer"
@@ -556,6 +579,7 @@ update_label("John Doe", "Clowny")
. = ..()
/obj/item/card/id/ert/Engineer
icon_state = "ert_engineer"
registered_name = "Engineer Response Officer"
assignment = "Engineer Response Officer"
@@ -564,6 +588,7 @@ update_label("John Doe", "Clowny")
. = ..()
/obj/item/card/id/ert/Medical
icon_state = "ert_medical"
registered_name = "Medical Response Officer"
assignment = "Medical Response Officer"
@@ -572,6 +597,7 @@ update_label("John Doe", "Clowny")
. = ..()
/obj/item/card/id/ert/chaplain
icon_state = "ert_chaplain"
registered_name = "Religious Response Officer"
assignment = "Religious Response Officer"
@@ -624,40 +650,49 @@ update_label("John Doe", "Clowny")
. += "<span class='notice'>Your sentence is up! You're free!</span>"
/obj/item/card/id/prisoner/one
icon_state = "prisoner_001"
name = "Prisoner #13-001"
registered_name = "Prisoner #13-001"
/obj/item/card/id/prisoner/two
icon_state = "prisoner_002"
name = "Prisoner #13-002"
registered_name = "Prisoner #13-002"
/obj/item/card/id/prisoner/three
icon_state = "prisoner_003"
name = "Prisoner #13-003"
registered_name = "Prisoner #13-003"
/obj/item/card/id/prisoner/four
icon_state = "prisoner_004"
name = "Prisoner #13-004"
registered_name = "Prisoner #13-004"
/obj/item/card/id/prisoner/five
icon_state = "prisoner_005"
name = "Prisoner #13-005"
registered_name = "Prisoner #13-005"
/obj/item/card/id/prisoner/six
icon_state = "prisoner_006"
name = "Prisoner #13-006"
registered_name = "Prisoner #13-006"
/obj/item/card/id/prisoner/seven
icon_state = "prisoner_007"
name = "Prisoner #13-007"
registered_name = "Prisoner #13-007"
/obj/item/card/id/mining
name = "mining ID"
icon_state = "retro"
access = list(ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM)
/obj/item/card/id/away
name = "a perfectly generic identification card"
desc = "A perfectly generic identification card. Looks like it could use some flavor."
icon_state = "retro"
access = list(ACCESS_AWAY_GENERAL)
/obj/item/card/id/away/hotel
@@ -700,6 +735,7 @@ update_label("John Doe", "Clowny")
/obj/item/card/id/departmental_budget
name = "departmental card (FUCK)"
desc = "Provides access to the departmental budget."
icon_state = "budgetcard"
var/department_ID = ACCOUNT_CIV
var/department_name = ACCOUNT_CIV_NAME
@@ -712,6 +748,7 @@ update_label("John Doe", "Clowny")
B.bank_cards += src
name = "departmental card ([department_name])"
desc = "Provides access to the [department_name]."
icon_state = "[lowertext(department_ID)]_budget"
SSeconomy.dep_cards += src
/obj/item/card/id/departmental_budget/Destroy()

View File

@@ -14,6 +14,10 @@
name = "Security Cameras (Computer Board)"
build_path = /obj/machinery/computer/security
/obj/item/circuitboard/computer/security/shuttle
name = "Shuttlelinking Security Cameras (Computer Board)"
build_path = /obj/machinery/computer/security/shuttle
/obj/item/circuitboard/computer/xenobiology
name = "circuit board (Xenobiology Console)"
build_path = /obj/machinery/computer/camera_advanced/xenobio
@@ -379,3 +383,11 @@
/obj/item/circuitboard/computer/nanite_cloud_controller
name = "Nanite Cloud Control (Computer Board)"
build_path = /obj/machinery/computer/nanite_cloud_controller
/obj/item/circuitboard/computer/shuttle/flight_control
name = "Shuttle Flight Control (Computer Board)"
build_path = /obj/machinery/computer/custom_shuttle
/obj/item/circuitboard/computer/shuttle/docker
name = "Shuttle Navigation Computer (Computer Board)"
build_path = /obj/machinery/computer/camera_advanced/shuttle_docker/custom

View File

@@ -1102,3 +1102,28 @@
/obj/item/stock_parts/micro_laser = 2,
/obj/item/stock_parts/scanning_module = 2
)
/obj/item/circuitboard/machine/shuttle/engine
name = "Thruster (Machine Board)"
build_path = /obj/machinery/shuttle/engine
req_components = list()
/obj/item/circuitboard/machine/shuttle/engine/plasma
name = "Plasma Thruster (Machine Board)"
build_path = /obj/machinery/shuttle/engine/plasma
req_components = list(/obj/item/stock_parts/capacitor = 2,
/obj/item/stack/cable_coil = 5,
/obj/item/stock_parts/micro_laser = 1)
/obj/item/circuitboard/machine/shuttle/engine/void
name = "Void Thruster (Machine Board)"
build_path = /obj/machinery/shuttle/engine/void
req_components = list(/obj/item/stock_parts/capacitor/quadratic = 2,
/obj/item/stack/cable_coil = 5,
/obj/item/stock_parts/micro_laser/quadultra = 1)
/obj/item/circuitboard/machine/shuttle/heater
name = "Electronic Engine Heater (Machine Board)"
build_path = /obj/machinery/atmospherics/components/unary/shuttle/heater
req_components = list(/obj/item/stock_parts/micro_laser = 2,
/obj/item/stock_parts/matter_bin = 1)

View File

@@ -7,7 +7,8 @@
var/forced_value = 0
var/duration = 300
/obj/item/grenade/antigravity/prime()
/obj/item/grenade/antigravity/prime(mob/living/lanced_by)
. = ..()
update_mob()
for(var/turf/T in view(range,src))

View File

@@ -174,10 +174,11 @@
message_admins(message)
user.log_message("primed [src] ([reagent_string])",LOG_GAME)
/obj/item/grenade/chem_grenade/prime()
/obj/item/grenade/chem_grenade/prime(mob/living/lanced_by)
if(stage != READY)
return FALSE
. = ..()
var/list/datum/reagents/reactants = list()
for(var/obj/item/reagent_containers/glass/G in beakers)
reactants += G.reagents
@@ -217,7 +218,7 @@
ignition_temp = 25 // Large grenades are slightly more effective at setting off heat-sensitive mixtures than smaller grenades.
threatscale = 1.1 // 10% more effective.
/obj/item/grenade/chem_grenade/large/prime()
/obj/item/grenade/chem_grenade/large/prime(mob/living/lanced_by)
if(stage != READY)
return FALSE
@@ -286,7 +287,7 @@
return
..()
/obj/item/grenade/chem_grenade/adv_release/prime()
/obj/item/grenade/chem_grenade/adv_release/prime(mob/living/lanced_by)
if(stage != READY)
return FALSE

View File

@@ -14,7 +14,8 @@
var/max_spawned = 8
var/segment_chance = 35
/obj/item/grenade/clusterbuster/prime()
/obj/item/grenade/clusterbuster/prime(mob/living/lanced_by)
. = ..()
update_mob()
var/numspawned = rand(min_spawned,max_spawned)
var/again = 0
@@ -59,7 +60,7 @@
step_away(src,loc)
addtimer(CALLBACK(src, .proc/prime), rand(15,60))
/obj/item/grenade/clusterbuster/segment/prime()
/obj/item/grenade/clusterbuster/segment/prime(mob/living/lanced_by)
new payload_spawner(drop_location(), payload, rand(min_spawned,max_spawned))
playsound(src, prime_sound, 75, 1, -3)
qdel(src)

View File

@@ -4,7 +4,8 @@
icon_state = "emp"
item_state = "emp"
/obj/item/grenade/empgrenade/prime()
/obj/item/grenade/empgrenade/prime(mob/living/lanced_by)
. = ..()
update_mob()
empulse(src, 4, 10)
qdel(src)

View File

@@ -6,7 +6,8 @@
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
var/flashbang_range = 7 //how many tiles away the mob will be stunned.
/obj/item/grenade/flashbang/prime()
/obj/item/grenade/flashbang/prime(mob/living/lanced_by)
. = ..()
update_mob()
var/flashbang_turf = get_turf(src)
if(!flashbang_turf)
@@ -42,3 +43,92 @@
var/distance = get_dist(get_turf(M), source)
if(M.flash_act(affect_silicon = 1))
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/item/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/item/projectile/bullet/pellet/stingball/mega
shrapnel_radius = 12
/obj/item/grenade/stingbang/prime(mob/living/lanced_by)
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)
qdel(src)
/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)
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/item/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(mob/living/lanced_by)
shrapnel_radius = round(rots / rots_per_mag)
. = ..()
qdel(src)
/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/item/projectile/bullet/pellet/stingball

View File

@@ -45,12 +45,13 @@
/obj/item/grenade/iedcasing/attack_self(mob/user) //
if(!active)
if(clown_check(user))
if(!botch_check(user))
to_chat(user, "<span class='warning'>You light the [name]!</span>")
cut_overlay("improvised_grenade_filled")
preprime(user, null, FALSE)
/obj/item/grenade/iedcasing/prime() //Blowing that can up
/obj/item/grenade/iedcasing/prime(mob/living/lanced_by) //Blowing that can up
. = ..()
update_mob()
explosion(src.loc,-1,-1,2, flame_range = 4) // small explosion, plus a very large fireball.
qdel(src)

View File

@@ -17,10 +17,31 @@
var/det_time = 50
var/display_timer = 1
var/clumsy_check = GRENADE_CLUMSY_FUMBLE
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)
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)
SEND_SIGNAL(src, COMSIG_GRENADE_ARMED, det_time)
preprime(user, det_time)
user.transferItemToLoc(src, user, TRUE)//>eat a grenade set to 5 seconds >rush captain
sleep(det_time)//so you dont die instantly
@@ -32,19 +53,21 @@
if(!QDELETED(src))
qdel(src)
/obj/item/grenade/proc/clown_check(mob/living/carbon/human/user)
/obj/item/grenade/proc/botch_check(mob/living/carbon/human/user)
var/clumsy = HAS_TRAIT(user, TRAIT_CLUMSY)
if(clumsy)
if(clumsy_check == GRENADE_CLUMSY_FUMBLE && prob(50))
to_chat(user, "<span class='warning'>Huh? How does this thing work?</span>")
preprime(user, 5, FALSE)
return FALSE
return TRUE
else if(clumsy_check == GRENADE_NONCLUMSY_FUMBLE && !(user.mind && HAS_TRAIT(user.mind, TRAIT_CLOWN_MENTALITY)))
to_chat(user, "<span class='warning'>You pull the pin on [src]. Attached to it is a pink ribbon that says, \"<span class='clown'>HONK</span>\"</span>")
preprime(user, 5, FALSE)
return FALSE
return TRUE
return TRUE
else if(sticky && prob(50)) // to add risk to sticky tape grenade cheese, no return cause we still prime as normal after
to_chat(user, "<span class='warning'>What the... [src] is stuck to your hand!</span>")
ADD_TRAIT(src, TRAIT_NODROP, STICKY_NODROP)
/obj/item/grenade/examine(mob/user)
. = ..()
@@ -56,8 +79,16 @@
/obj/item/grenade/attack_self(mob/user)
if(HAS_TRAIT(src, TRAIT_NODROP))
to_chat(user, "<span class='notice'>You try prying [src] off your hand...</span>")
if(do_after(user, 70, target=src))
to_chat(user, "<span class='notice'>You manage to remove [src] from your hand.</span>")
REMOVE_TRAIT(src, TRAIT_NODROP, STICKY_NODROP)
return
if(!active)
if(clown_check(user))
if(!botch_check(user)) // if they botch the prime, it'll be handled in botch_check
preprime(user)
/obj/item/grenade/proc/log_grenade(mob/user, turf/T)
@@ -81,10 +112,18 @@
icon_state = initial(icon_state) + "_active"
addtimer(CALLBACK(src, .proc/prime), isnull(delayoverride)? det_time : delayoverride)
/obj/item/grenade/proc/prime()
/obj/item/grenade/proc/prime(mob/living/lanced_by)
var/turf/T = get_turf(src)
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, lanced_by)
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()
if(ismob(loc))
var/mob/M = loc

View File

@@ -122,7 +122,9 @@
var/obj/item/I = AM
I.throw_speed = max(1, (I.throw_speed - 3))
I.throw_range = max(1, (I.throw_range - 3))
I.embedding = I.embedding.setRating(embed_chance = 0)
if(I.embedding)
I.embedding["embed_chance"] = 0
I.updateEmbedding()
target.add_overlay(plastic_overlay, TRUE)
if(!nadeassembly)
@@ -205,9 +207,10 @@
else
return ..()
/obj/item/grenade/plastic/c4/prime()
/obj/item/grenade/plastic/c4/prime(mob/living/lanced_by)
if(QDELETED(src))
return
. = ..()
var/turf/location
if(target)
if(!QDELETED(target))

View File

@@ -17,7 +17,8 @@
qdel(smoke)
return ..()
/obj/item/grenade/smokebomb/prime()
/obj/item/grenade/smokebomb/prime(mob/living/lanced_by)
. = ..()
update_mob()
playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3)
smoke.set_up(4, src)

View File

@@ -7,7 +7,8 @@
var/spawner_type = null // must be an object path
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(mob/living/lanced_by) // Prime now just handles the two loops that query for people in lockers and people who can see it.
. = ..()
update_mob()
if(spawner_type && deliveryamt)
// Make a quick flash

View File

@@ -4,27 +4,45 @@
icon = 'icons/obj/grenade.dmi'
icon_state = "syndicate"
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(mob/living/lanced_by)
. = ..()
update_mob()
explosion(src.loc,1,2,4,flame_range = 2)
qdel(src)
/obj/item/grenade/syndieminibomb/concussion
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."
icon_state = "concussion"
ex_heavy = 2
ex_light = 3
ex_flame = 3
/obj/item/grenade/syndieminibomb/concussion/prime()
update_mob()
explosion(src.loc,0,2,3,flame_range = 3)
qdel(src)
/obj/item/grenade/syndieminibomb/concussion/frag
/obj/item/grenade/frag
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"
shrapnel_type = /obj/item/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/item/projectile/bullet/shrapnel/mega
shrapnel_radius = 12
/obj/item/grenade/frag/prime(mob/living/lanced_by)
. = ..()
update_mob()
qdel(src)
/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."
@@ -36,7 +54,8 @@
var/rad_damage = 350
var/stamina_damage = 30
/obj/item/grenade/gluon/prime()
/obj/item/grenade/gluon/prime(mob/living/lanced_by)
. = ..()
update_mob()
playsound(loc, 'sound/effects/empulse.ogg', 50, 1)
radiation_pulse(src, rad_damage)

View File

@@ -138,6 +138,7 @@
icon_state = "buckknife"
item_state = "knife"
desc = "A military combat utility survival knife."
embedding = list("pain_mult" = 4, "embed_chance" = 65, "fall_chance" = 10, "ignore_throwspeed_threshold" = TRUE)
force = 20
throwforce = 20
attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "cut")
@@ -148,6 +149,7 @@
icon_state = "survivalknife"
item_state = "knife"
desc = "A hunting grade survival knife."
embedding = list("pain_mult" = 4, "embed_chance" = 35, "fall_chance" = 10)
force = 15
throwforce = 15
bayonet = TRUE
@@ -159,6 +161,7 @@
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
desc = "A sharpened bone. The bare minimum in survival."
embedding = list("pain_mult" = 4, "embed_chance" = 35, "fall_chance" = 10)
force = 15
throwforce = 15
custom_materials = null

View File

@@ -103,7 +103,7 @@
throw_speed = 3
throw_range = 5
sharpness = IS_SHARP
embedding = list("embed_chance" = 75, "embedded_impact_pain_multiplier" = 10)
embedding = list("embed_chance" = 75, "impact_pain_mult" = 10)
armour_penetration = 35
item_flags = NEEDS_PERMIT | ITEM_CAN_PARRY
block_parry_data = /datum/block_parry_data/energy_sword

View File

@@ -53,6 +53,8 @@
throw_speed = 4
if(attack_verb_on.len)
attack_verb = attack_verb_on
if(embedding)
updateEmbedding()
icon_state = icon_state_on
w_class = w_class_on
else
@@ -62,6 +64,8 @@
throw_speed = initial(throw_speed)
if(attack_verb_off.len)
attack_verb = attack_verb_off
if(embedding)
updateEmbedding()
icon_state = initial(icon_state)
w_class = initial(w_class)
total_mass = initial(total_mass)

View File

@@ -129,6 +129,30 @@
hos_gun_list[initial(A.name)] = A
return hos_gun_list
/obj/item/choice_beacon/augments
name = "augment beacon"
desc = "Summons augmentations."
/obj/item/choice_beacon/augments/generate_display_names()
var/static/list/augment_list
if(!augment_list)
augment_list = list()
var/list/templist = list(
/obj/item/organ/cyberimp/brain/anti_drop,
/obj/item/organ/cyberimp/arm/toolset,
/obj/item/organ/cyberimp/arm/surgery,
/obj/item/organ/cyberimp/chest/thrusters,
/obj/item/organ/lungs/cybernetic,
/obj/item/organ/liver/cybernetic) //cyberimplants range from a nice bonus to fucking broken bullshit so no subtypesof
for(var/V in templist)
var/atom/A = V
augment_list[initial(A.name)] = A
return augment_list
/obj/item/choice_beacon/augments/spawn_option(obj/choice,mob/living/M)
new choice(get_turf(M))
to_chat(M, "<span class='hear'>You hear something crackle from the beacon for a moment before a voice speaks. \"Please stand by for a message from S.E.L.F. Message as follows: <b>Item request received. Your package has been transported, use the autosurgeon supplied to apply the upgrade.</b> Message ends.\"</span>")
/obj/item/skub
desc = "It's skub."
name = "skub"

View File

@@ -183,3 +183,20 @@
A.other_pair = B
B.other_pair = A
/obj/item/pinpointer/shuttle
name = "fugitive pinpointer"
desc = "A handheld tracking device that locates the bounty hunter shuttle for quick escapes."
icon_state = "pinpointer_hunter"
var/obj/shuttleport
/obj/item/pinpointer/shuttle/Initialize(mapload)
. = ..()
shuttleport = SSshuttle.getShuttle("huntership")
/obj/item/pinpointer/shuttle/scan_for_target()
target = shuttleport
/obj/item/pinpointer/shuttle/Destroy()
shuttleport = null
. = ..()

View File

@@ -545,7 +545,7 @@
to_chat(usr, "<span class='notice'>This unit already has an expand module installed!</span>")
return FALSE
R.notransform = TRUE
R.mob_transforming = TRUE
var/prev_locked_down = R.locked_down
R.SetLockdown(1)
R.anchored = TRUE
@@ -559,7 +559,7 @@
if(!prev_locked_down)
R.SetLockdown(0)
R.anchored = FALSE
R.notransform = FALSE
R.mob_transforming = FALSE
R.resize = 2
R.hasExpanded = TRUE
R.update_transform()

View File

@@ -0,0 +1,64 @@
/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/item/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
/obj/item/projectile/bullet/shrapnel/mega
name = "flying shrapnel hunk"
range = 25
dismemberment = 10
ricochets_max = 4
ricochet_chance = 90
ricochet_decay_chance = 0.9
/obj/item/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/item/projectile/bullet/pellet/stingball/mega
name = "megastingball pellet"
ricochets_max = 6
ricochet_chance = 110

View File

@@ -13,18 +13,33 @@
novariants = FALSE
item_flags = NOBLUDGEON
var/self_delay = 50
var/other_delay = 0
var/repeating = FALSE
/obj/item/stack/medical/attack(mob/living/M, mob/user)
. = ..()
try_heal(M, user)
/obj/item/stack/medical/proc/try_heal(mob/living/M, mob/user, silent = FALSE)
if(!M.can_inject(user, TRUE))
return
if(M == user)
user.visible_message("<span class='notice'>[user] starts to apply \the [src] on [user.p_them()]self...</span>", "<span class='notice'>You begin applying \the [src] on yourself...</span>")
if(!silent)
user.visible_message("<span class='notice'>[user] starts to apply \the [src] on [user.p_them()]self...</span>", "<span class='notice'>You begin applying \the [src] on yourself...</span>")
if(!do_mob(user, M, self_delay, extra_checks=CALLBACK(M, /mob/living/proc/can_inject, user, TRUE)))
return
else if(other_delay)
if(!silent)
user.visible_message("<span class='notice'>[user] starts to apply \the [src] on [M].</span>", "<span class='notice'>You begin applying \the [src] on [M]...</span>")
if(!do_mob(user, M, other_delay, extra_checks=CALLBACK(M, /mob/living/proc/can_inject, user, TRUE)))
return
if(heal(M, user))
log_combat(user, M, "healed", src.name)
use(1)
if(repeating && amount > 0)
try_heal(M, user, TRUE)
/obj/item/stack/medical/proc/heal(mob/living/M, mob/user)
@@ -174,3 +189,108 @@
/obj/item/stack/medical/ointment/suicide_act(mob/living/user)
user.visible_message("<span class='suicide'>[user] is squeezing \the [src] into [user.p_their()] mouth! [user.p_do(TRUE)]n't [user.p_they()] know that stuff is toxic?</span>")
return TOXLOSS
/obj/item/stack/medical/suture
name = "suture"
desc = "Sterile sutures used to seal up cuts and lacerations."
gender = PLURAL
singular_name = "suture"
icon_state = "suture"
self_delay = 30
other_delay = 10
amount = 15
max_amount = 15
repeating = TRUE
var/heal_brute = 10
grind_results = list(/datum/reagent/medicine/spaceacillin = 2)
/obj/item/stack/medical/suture/one
amount = 1
/obj/item/stack/medical/suture/heal(mob/living/M, mob/user)
. = ..()
if(M.stat == DEAD)
to_chat(user, "<span class='warning'>[M] is dead! You can not help [M.p_them()].</span>")
return
if(iscarbon(M))
return heal_carbon(M, user, heal_brute, 0)
if(isanimal(M))
var/mob/living/simple_animal/critter = M
if (!(critter.healable))
to_chat(user, "<span class='warning'>You cannot use \the [src] on [M]!</span>")
return FALSE
else if (critter.health == critter.maxHealth)
to_chat(user, "<span class='notice'>[M] is at full health.</span>")
return FALSE
user.visible_message("<span class='green'>[user] applies \the [src] on [M].</span>", "<span class='green'>You apply \the [src] on [M].</span>")
M.heal_bodypart_damage(heal_brute)
return TRUE
to_chat(user, "<span class='warning'>You can't heal [M] with the \the [src]!</span>")
/obj/item/stack/medical/mesh
name = "regenerative mesh"
desc = "A bacteriostatic mesh used to dress burns."
gender = PLURAL
singular_name = "regenerative mesh"
icon_state = "regen_mesh"
self_delay = 30
other_delay = 10
amount = 15
max_amount = 15
repeating = TRUE
var/heal_burn = 10
var/is_open = TRUE ///This var determines if the sterile packaging of the mesh has been opened.
grind_results = list(/datum/reagent/medicine/spaceacillin = 2)
/obj/item/stack/medical/mesh/one
amount = 1
/obj/item/stack/medical/mesh/Initialize()
. = ..()
if(amount == max_amount) //only seal full mesh packs
is_open = FALSE
update_icon()
/obj/item/stack/medical/mesh/update_icon_state()
if(!is_open)
icon_state = "regen_mesh_closed"
else
return ..()
/obj/item/stack/medical/mesh/heal(mob/living/M, mob/user)
. = ..()
if(M.stat == DEAD)
to_chat(user, "<span class='warning'>[M] is dead! You can not help [M.p_them()].</span>")
return
if(iscarbon(M))
return heal_carbon(M, user, 0, heal_burn)
to_chat(user, "<span class='warning'>You can't heal [M] with the \the [src]!</span>")
/obj/item/stack/medical/mesh/try_heal(mob/living/M, mob/user, silent = FALSE)
if(!is_open)
to_chat(user, "<span class='warning'>You need to open [src] first.</span>")
return
. = ..()
/obj/item/stack/medical/mesh/AltClick(mob/living/user)
if(!is_open)
to_chat(user, "<span class='warning'>You need to open [src] first.</span>")
return
. = ..()
/obj/item/stack/medical/mesh/attack_hand(mob/user)
if(!is_open & user.get_inactive_held_item() == src)
to_chat(user, "<span class='warning'>You need to open [src] first.</span>")
return
. = ..()
/obj/item/stack/medical/mesh/attack_self(mob/user)
if(!is_open)
is_open = TRUE
to_chat(user, "<span class='notice'>You open the sterile mesh package.</span>")
update_icon()
playsound(src, 'sound/items/poster_ripped.ogg', 20, TRUE)
return
. = ..()

View File

@@ -21,6 +21,7 @@ GLOBAL_LIST_INIT(rod_recipes, list ( \
max_amount = 50
attack_verb = list("hit", "bludgeoned", "whacked")
hitsound = 'sound/weapons/grenadelaunch.ogg'
embedding = list()
novariants = TRUE
/obj/item/stack/rods/suicide_act(mob/living/carbon/user)

View File

@@ -291,6 +291,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
max_integrity = 40
sharpness = IS_SHARP
var/icon_prefix
embedding = list("embed_chance" = 65)
/obj/item/shard/suicide_act(mob/user)

View File

@@ -780,6 +780,9 @@ GLOBAL_LIST_INIT(plastic_recipes, list(
/obj/item/stack/sheet/plastic/fifty
amount = 50
/obj/item/stack/sheet/plastic/twenty
amount = 20
/obj/item/stack/sheet/plastic/five
amount = 5

View File

@@ -0,0 +1,61 @@
/obj/item/stack/sticky_tape
name = "sticky tape"
singular_name = "sticky tape"
desc = "Used for sticking to things for sticking said things to people."
icon = 'icons/obj/tapes.dmi'
icon_state = "tape_w"
var/prefix = "sticky"
item_flags = NOBLUDGEON
amount = 5
max_amount = 5
resistance_flags = FLAMMABLE
var/list/conferred_embed = EMBED_HARMLESS
var/overwrite_existing = FALSE
/obj/item/stack/sticky_tape/afterattack(obj/item/I, mob/living/user)
if(!istype(I))
return
if(I.embedding && I.embedding == conferred_embed)
to_chat(user, "<span class='warning'>[I] is already coated in [src]!</span>")
return
user.visible_message("<span class='notice'>[user] begins wrapping [I] with [src].</span>", "<span class='notice'>You begin wrapping [I] with [src].</span>")
if(do_after(user, 30, target=I))
I.embedding = conferred_embed
I.updateEmbedding()
to_chat(user, "<span class='notice'>You finish wrapping [I] with [src].</span>")
use(1)
I.name = "[prefix] [I.name]"
if(istype(I, /obj/item/grenade))
var/obj/item/grenade/sticky_bomb = I
sticky_bomb.sticky = TRUE
/obj/item/stack/sticky_tape/super
name = "super sticky tape"
singular_name = "super sticky tape"
desc = "Quite possibly the most mischevious substance in the galaxy. Use with extreme lack of caution."
icon_state = "tape_y"
prefix = "super sticky"
conferred_embed = EMBED_HARMLESS_SUPERIOR
/obj/item/stack/sticky_tape/pointy
name = "pointy tape"
singular_name = "pointy tape"
desc = "Used for sticking to things for sticking said things inside people."
icon_state = "tape_evil"
prefix = "pointy"
conferred_embed = EMBED_POINTY
/obj/item/stack/sticky_tape/pointy/super
name = "super pointy tape"
singular_name = "super pointy tape"
desc = "You didn't know tape could look so sinister. Welcome to Space Station 13."
icon_state = "tape_spikes"
prefix = "super pointy"
conferred_embed = EMBED_POINTY_SUPERIOR

View File

@@ -266,6 +266,9 @@
/obj/item/stack/tile/carpet/blackred/twenty
amount = 20
/obj/item/stack/tile/carpet/blackred/thirty
amount = 30
/obj/item/stack/tile/carpet/blackred/fifty
amount = 50
@@ -275,6 +278,9 @@
/obj/item/stack/tile/carpet/monochrome/twenty
amount = 20
/obj/item/stack/tile/carpet/monochrome/thirty
amount = 30
/obj/item/stack/tile/carpet/monochrome/fifty
amount = 50
@@ -284,6 +290,9 @@
/obj/item/stack/tile/carpet/blue/twenty
amount = 20
/obj/item/stack/tile/carpet/blue/thirty
amount = 30
/obj/item/stack/tile/carpet/blue/fifty
amount = 50
@@ -293,6 +302,9 @@
/obj/item/stack/tile/carpet/cyan/twenty
amount = 20
/obj/item/stack/tile/carpet/cyan/thirty
amount = 30
/obj/item/stack/tile/carpet/cyan/fifty
amount = 50
@@ -302,6 +314,9 @@
/obj/item/stack/tile/carpet/green/twenty
amount = 20
/obj/item/stack/tile/carpet/green/thirty
amount = 30
/obj/item/stack/tile/carpet/green/fifty
amount = 50
@@ -311,6 +326,9 @@
/obj/item/stack/tile/carpet/orange/twenty
amount = 20
/obj/item/stack/tile/carpet/orange/thirty
amount = 30
/obj/item/stack/tile/carpet/orange/fifty
amount = 50
@@ -320,6 +338,9 @@
/obj/item/stack/tile/carpet/purple/twenty
amount = 20
/obj/item/stack/tile/carpet/purple/thirty
amount = 30
/obj/item/stack/tile/carpet/purple/fifty
amount = 50
@@ -329,6 +350,9 @@
/obj/item/stack/tile/carpet/red/twenty
amount = 20
/obj/item/stack/tile/carpet/red/thirty
amount = 30
/obj/item/stack/tile/carpet/red/fifty
amount = 50
@@ -338,6 +362,9 @@
/obj/item/stack/tile/carpet/royalblack/twenty
amount = 20
/obj/item/stack/tile/carpet/royalblack/thirty
amount = 30
/obj/item/stack/tile/carpet/royalblack/fifty
amount = 50
@@ -347,6 +374,9 @@
/obj/item/stack/tile/carpet/royalblue/twenty
amount = 20
/obj/item/stack/tile/carpet/royalblue/thirty
amount = 30
/obj/item/stack/tile/carpet/royalblue/fifty
amount = 50

View File

@@ -391,7 +391,7 @@
STR.storage_flags = STORAGE_FLAGS_VOLUME_DEFAULT
STR.max_volume = STORAGE_VOLUME_CHEMISTRY_BAG
STR.insert_preposition = "in"
STR.can_hold = typecacheof(list(/obj/item/reagent_containers/pill, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/syringe/dart))
STR.can_hold = typecacheof(list(/obj/item/reagent_containers/pill, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/syringe/dart, /obj/item/reagent_containers/chem_pack))
/*
* Biowaste bag (mostly for xenobiologists)

View File

@@ -180,7 +180,8 @@
/obj/item/implantcase,
/obj/item/implant,
/obj/item/implanter,
/obj/item/pinpointer/crew
/obj/item/pinpointer/crew,
/obj/item/reagent_containers/chem_pack
))
/obj/item/storage/belt/medical/surgery_belt_adv
@@ -512,16 +513,16 @@
new /obj/item/grenade/smokebomb(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/syndieminibomb/concussion/frag(src)
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
new /obj/item/grenade/syndieminibomb/concussion/frag(src)
new /obj/item/grenade/frag(src)
new /obj/item/grenade/frag(src)
new /obj/item/grenade/frag(src)
new /obj/item/grenade/frag(src)
new /obj/item/grenade/frag(src)
new /obj/item/grenade/frag(src)
new /obj/item/grenade/frag(src)
new /obj/item/grenade/frag(src)
new /obj/item/grenade/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)

View File

@@ -275,6 +275,16 @@
for(var/i in 1 to 7)
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
name = "box of flashbulbs"
desc = "<B>WARNING: Flashes can cause serious eye damage, protective eyewear is required.</B>"
@@ -745,8 +755,8 @@
//////
/obj/item/storage/box/hug/medical/PopulateContents()
new /obj/item/stack/medical/bruise_pack(src)
new /obj/item/stack/medical/ointment(src)
new /obj/item/stack/medical/suture(src)
new /obj/item/stack/medical/mesh(src)
new /obj/item/reagent_containers/hypospray/medipen(src)
// Clown survival box

View File

@@ -138,6 +138,22 @@
icon_type = "cigarette"
spawn_type = /obj/item/clothing/mask/cigarette/space_cigarette
custom_price = PRICE_ALMOST_CHEAP
var/spawn_coupon = TRUE
/obj/item/storage/fancy/cigarettes/attack_self(mob/user)
if(contents.len == 0 && spawn_coupon)
to_chat(user, "<span class='notice'>You rip the back off \the [src] and get a coupon!</span>")
var/obj/item/coupon/attached_coupon = new
user.put_in_hands(attached_coupon)
attached_coupon.generate()
attached_coupon = null
spawn_coupon = FALSE
name = "discarded cigarette packet"
desc = "An old cigarette packet with the back torn off, worth less than nothing now."
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
STR.max_items = 0
return
return ..()
/obj/item/storage/fancy/cigarettes/ComponentInitialize()
. = ..()
@@ -148,6 +164,8 @@
/obj/item/storage/fancy/cigarettes/examine(mob/user)
. = ..()
. += "<span class='notice'>Alt-click to extract contents.</span>"
if(spawn_coupon)
. += "<span class='notice'>There's a coupon on the back of the pack! You can tear it off once it's empty.</span>"
/obj/item/storage/fancy/cigarettes/AltClick(mob/living/carbon/user)
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
@@ -308,6 +326,7 @@
w_class = WEIGHT_CLASS_NORMAL
icon_type = "premium cigar"
spawn_type = /obj/item/clothing/mask/cigarette/cigar
spawn_coupon = FALSE
/obj/item/storage/fancy/cigarettes/cigars/ComponentInitialize()
. = ..()

View File

@@ -37,10 +37,10 @@
if(empty)
return
new /obj/item/stack/medical/gauze(src)
new /obj/item/stack/medical/bruise_pack(src)
new /obj/item/stack/medical/bruise_pack(src)
new /obj/item/stack/medical/ointment(src)
new /obj/item/stack/medical/ointment(src)
new /obj/item/stack/medical/suture(src)
new /obj/item/stack/medical/suture(src)
new /obj/item/stack/medical/mesh(src)
new /obj/item/stack/medical/mesh(src)
new /obj/item/reagent_containers/hypospray/medipen(src)
new /obj/item/healthanalyzer(src)
@@ -52,12 +52,12 @@
if(empty)
return
new /obj/item/stack/medical/gauze(src)
new /obj/item/stack/medical/bruise_pack(src)
new /obj/item/stack/medical/bruise_pack(src)
new /obj/item/stack/medical/bruise_pack(src)
new /obj/item/stack/medical/ointment(src)
new /obj/item/stack/medical/ointment(src)
new /obj/item/stack/medical/ointment(src)
new /obj/item/stack/medical/suture(src)
new /obj/item/stack/medical/suture(src)
new /obj/item/stack/medical/suture(src)
new /obj/item/stack/medical/mesh(src)
new /obj/item/stack/medical/mesh(src)
new /obj/item/stack/medical/mesh(src)
/obj/item/storage/firstaid/fire
name = "burn treatment kit"
@@ -418,7 +418,8 @@
/obj/item/circuitboard/computer/crew,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/mineral/silver,
/obj/item/organ_storage
/obj/item/organ_storage,
/obj/item/reagent_containers/chem_pack
))
//hijacking the minature first aids for hypospray boxes. <3

View File

@@ -188,19 +188,52 @@
new /obj/item/clothing/accessory/medal/plasma/nobel_science(src)
/obj/item/storage/lockbox/medal/engineering
name = "engineering medal box"
desc = "A locked box used to store medals to be given to the members of the engineering department."
req_access = list(ACCESS_CE)
name = "engineering medal box"
desc = "A locked box used to store medals to be given to the members of the engineering department."
req_access = list(ACCESS_CE)
/obj/item/storage/lockbox/medal/engineering/PopulateContents()
for(var/i in 1 to 3)
new /obj/item/clothing/accessory/medal/engineer(src)
for(var/i in 1 to 3)
new /obj/item/clothing/accessory/medal/engineer(src)
/obj/item/storage/lockbox/medal/medical
name = "medical medal box"
desc = "A locked box used to store medals to be given to the members of the medical department."
req_access = list(ACCESS_CMO)
name = "medical medal box"
desc = "A locked box used to store medals to be given to the members of the medical department."
req_access = list(ACCESS_CMO)
/obj/item/storage/lockbox/medal/medical/PopulateContents()
for(var/i in 1 to 3)
new /obj/item/clothing/accessory/medal/ribbon/medical_doctor(src)
for(var/i in 1 to 3)
new /obj/item/clothing/accessory/medal/ribbon/medical_doctor(src)
/obj/item/storage/lockbox/order
name = "order lockbox"
desc = "A box used to secure small cargo orders from being looted by those who didn't order it. Yeah, cargo tech, that means you."
icon = 'icons/obj/storage.dmi'
icon_state = "secure"
item_state = "sec-case"
lefthand_file = 'icons/mob/inhands/equipment/briefcase_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/briefcase_righthand.dmi'
w_class = WEIGHT_CLASS_HUGE
var/datum/bank_account/buyer_account
var/privacy_lock = TRUE
/obj/item/storage/lockbox/order/Initialize(datum/bank_account/_buyer_account)
. = ..()
buyer_account = _buyer_account
/obj/item/storage/lockbox/order/attackby(obj/item/W, mob/user, params)
if(!istype(W, /obj/item/card/id))
return ..()
var/obj/item/card/id/id_card = W
if(iscarbon(user))
add_fingerprint(user)
if(id_card.registered_account != buyer_account)
to_chat(user, "<span class='notice'>Bank account does not match with buyer!</span")
return
SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, !privacy_lock)
privacy_lock = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
user.visible_message("<span class='notice'>[user] [privacy_lock ? "" : "un"]locks [src]'s privacy lock.</span>",
"<span class='notice'>You [privacy_lock ? "" : "un"]lock [src]'s privacy lock.</span>")

View File

@@ -628,7 +628,7 @@
force_wielded = 18
throwforce = 20
throw_speed = 4
embedding = list("embedded_impact_pain_multiplier" = 1.5, "embed_chance" = 65)
embedding = list("impact_pain_mult" = 3)
armour_penetration = 10
custom_materials = list(/datum/material/iron=1150, /datum/material/glass=2075)
hitsound = 'sound/weapons/bladeslice.ogg'
@@ -713,7 +713,7 @@
force_wielded = 19
force_unwielded = 11
throwforce = 21
embedding = getEmbeddingBehavior(embed_chance = 75, embedded_pain_multiplier = 1.5) //plasmaglass spears are sharper
embedding = list(embed_chance = 75, pain_mult = 1.5) //plasmaglass spears are sharper
icon_prefix = "spearplasma"
qdel(tip)
var/obj/item/twohanded/spear/S = locate() in parts_list
@@ -728,7 +728,7 @@
if(G)
explosive = G
name = "explosive lance"
embedding = getEmbeddingBehavior(embed_chance = 0, embedded_pain_multiplier = 1)//elances should not be embeddable
embedding = list(embed_chance = 0, pain_mult = 1)//elances should not be embeddable
desc = "A makeshift spear with [G] attached to it."
update_icon()
@@ -1108,7 +1108,7 @@
block_slowdown = 2
// no attacking while blocking
block_lock_attacking = TRUE
parry_time_windup = 1
parry_time_active = 5
parry_time_spindown = 0

View File

@@ -299,12 +299,28 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
force = 2
throwforce = 20 //This is never used on mobs since this has a 100% embed chance.
throw_speed = 4
embedding = list("embedded_pain_multiplier" = 4, "embed_chance" = 100, "embedded_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
sharpness = IS_SHARP
custom_materials = list(/datum/material/iron=500, /datum/material/glass=500)
resistance_flags = FIRE_PROOF
/obj/item/throwing_star/stamina
name = "shock throwing star"
desc = "An aerodynamic disc designed to cause excruciating pain when stuck inside fleeing targets, hopefully without causing fatal harm."
throwforce = 5
embedding = list("pain_chance" = 5, "embed_chance" = 100, "fall_chance" = 0, "jostle_chance" = 10, "pain_stam_pct" = 0.8, "jostle_pain_mult" = 3)
/obj/item/throwing_star/toy
name = "toy throwing star"
desc = "An aerodynamic disc strapped with adhesive for sticking to people, good for playing pranks and getting yourself killed by security."
sharpness = IS_BLUNT
force = 0
throwforce = 0
embedding = list("pain_mult" = 0, "jostle_pain_mult" = 0, "embed_chance" = 100, "fall_chance" = 0)
/obj/item/switchblade
name = "switchblade"
icon_state = "switchblade"

View File

@@ -74,7 +74,8 @@
/obj/bullet_act(obj/item/projectile/P)
. = ..()
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
take_damage(P.damage, P.damage_type, P.flag, 0, turn(P.dir, 180), P.armour_penetration)

Some files were not shown because too many files have changed in this diff Show More