A biological research lab within the HD-10180 system has suffered from a complete containment failure. The SYN-C Brutus is to deliver a nuclear payload via strike team. Everything inside and outside the facility is to be killed on sight, including any research staff. Nuclear authentication codes have been sent via red phone, as have other detailed orders.
A biological research lab within the HD-10180 system has suffered from a complete containment failure. The SYN-C Brutus is to deliver a nuclear payload via strike team. Everything inside and outside the facility is to be killed on sight, including any research staff. Nuclear authentication codes have been sent via red phone, as have other detailed orders.
"
else
- bans = json_decode(response["body"])
+ bans = json_decode(response.body)
//Ignore bans from non-whitelisted sources, if a whitelist exists
var/list/valid_sources
diff --git a/code/modules/admin/verbs/adminevents.dm b/code/modules/admin/verbs/adminevents.dm
index d6120962fd1..a9d285815ad 100644
--- a/code/modules/admin/verbs/adminevents.dm
+++ b/code/modules/admin/verbs/adminevents.dm
@@ -8,6 +8,8 @@ ADMIN_VERB_AND_CONTEXT_MENU(cmd_admin_subtle_message, R_ADMIN, "Subtle Message",
message_admins("[key_name_admin(user)] decided not to answer [ADMIN_LOOKUPFLW(target)]'s prayer")
return
+ msg = user.reformat_narration(msg)
+
target.balloon_alert(target, "you hear a voice")
to_chat(target, "You hear a voice in your head... [msg]", confidential = TRUE)
@@ -53,6 +55,8 @@ ADMIN_VERB_AND_CONTEXT_MENU(cmd_admin_headset_message, R_ADMIN, "Headset Message
message_admins("[key_name_admin(src)] decided not to answer [key_name_admin(target)]'s [sender] request.")
return
+ input = reformat_narration(input)
+
log_directed_talk(mob, target, input, LOG_ADMIN, "reply")
message_admins("[key_name_admin(src)] replied to [key_name_admin(target)]'s [sender] message with: \"[input]\"")
target.balloon_alert(target, "you hear a voice")
@@ -64,6 +68,7 @@ ADMIN_VERB(cmd_admin_world_narrate, R_ADMIN, "Global Narrate", "Send a direct na
var/msg = input(user, "Message:", "Enter the text you wish to appear to everyone:") as text|null
if (!msg)
return
+ msg = user.reformat_narration(msg)
to_chat(world, "[msg]", confidential = TRUE)
log_admin("GlobalNarrate: [key_name(user)] : [msg]")
message_admins(span_adminnotice("[key_name_admin(user)] Sent a global narrate"))
@@ -76,6 +81,7 @@ ADMIN_VERB_AND_CONTEXT_MENU(cmd_admin_local_narrate, R_ADMIN, "Local Narrate", A
var/msg = input(user, "Message:", "Enter the text you wish to appear to everyone within view:") as text|null
if (!msg)
return
+ msg = user.reformat_narration(msg)
for(var/mob/M in view(range, locale))
to_chat(M, msg, confidential = TRUE)
@@ -89,6 +95,8 @@ ADMIN_VERB_AND_CONTEXT_MENU(cmd_admin_direct_narrate, R_ADMIN, "Direct Narrate",
if( !msg )
return
+ msg = user.reformat_narration(msg)
+
to_chat(target, msg, confidential = TRUE)
log_admin("DirectNarrate: [key_name(user)] to ([key_name(target)]): [msg]")
msg = span_adminnotice(" DirectNarrate: [key_name_admin(user)] to ([key_name_admin(target)]): [msg] ")
@@ -278,3 +286,14 @@ ADMIN_VERB(command_report_footnote, R_ADMIN, "Command Report Footnote", "Adds a
ADMIN_VERB(delay_command_report, R_FUN, "Delay Command Report", "Prevents the roundstart command report from being sent; or forces it to send it delayed.", ADMIN_CATEGORY_EVENTS)
GLOB.communications_controller.block_command_report = !GLOB.communications_controller.block_command_report
message_admins("[key_name_admin(user)] has [(GLOB.communications_controller.block_command_report ? "delayed" : "sent")] the roundstart command report.")
+
+///Reformats a narration message. First provides a prompt asking if the user wants to reformat their message, then allows them to pick from a list of spans to use.
+/client/proc/reformat_narration(input)
+ if(tgui_alert(mob, "Set a custom text format?", "Make it snazzy!", list("Yes", "No")) == "Yes")
+ var/text_span = tgui_input_list(mob, "Select a span!", "Immersion! Yeah!", GLOB.spanname_to_formatting)
+ if(isnull(text_span)) //In case the user just quit the prompt.
+ return text_span
+ text_span = GLOB.spanname_to_formatting[text_span]
+ input = "" + input + ""
+
+ return input
diff --git a/code/modules/admin/verbs/anonymousnames.dm b/code/modules/admin/verbs/anonymousnames.dm
index 10edb49d993..7f56155c998 100644
--- a/code/modules/admin/verbs/anonymousnames.dm
+++ b/code/modules/admin/verbs/anonymousnames.dm
@@ -94,7 +94,7 @@ GLOBAL_DATUM(current_anonymous_theme, /datum/anonymous_theme)
return
var/mob/living/carbon/human/human_mob = player
var/original_name = player.real_name //id will not be changed if you do not do this
- randomize_human(player) //do this first so the special name can be given
+ randomize_human_normie(player) //do this first so the special name can be given
player.fully_replace_character_name(original_name, anonymous_name(player))
if(extras_enabled)
player_extras(player)
diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm
index 809413e2796..5c1647a5edb 100644
--- a/code/modules/admin/verbs/mapping.dm
+++ b/code/modules/admin/verbs/mapping.dm
@@ -209,7 +209,7 @@ ADMIN_VERB(create_mapping_job_icons, R_DEBUG, "Generate job landmarks icons", "G
else
for(var/obj/item/I in D)
qdel(I)
- randomize_human(D)
+ randomize_human_normie(D)
D.dress_up_as_job(
equipping = JB,
visual_only = TRUE,
diff --git a/code/modules/admin/view_variables/debug_variables.dm b/code/modules/admin/view_variables/debug_variables.dm
index a4035acd014..d9a1b90b0af 100644
--- a/code/modules/admin/view_variables/debug_variables.dm
+++ b/code/modules/admin/view_variables/debug_variables.dm
@@ -3,11 +3,12 @@
/proc/debug_variable(name, value, level, datum/owner, sanitize = TRUE, display_flags = NONE) //if D is a list, name will be index, and value will be assoc value.
if(owner)
if(islist(owner))
+ var/list/list_owner = owner
var/index = name
if (value)
- name = owner[name] //name is really the index until this line
+ name = list_owner[name] //name is really the index until this line
else
- value = owner[name]
+ value = list_owner[name]
. = "
([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_EDIT, "E", name)]) ([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_CHANGE, "C", name)]) ([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_MASSEDIT, "M", name)]) "
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 162bc7df1a3..191b508d9a0 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -529,6 +529,7 @@ GLOBAL_LIST_EMPTY(antagonists)
"antag_team_hud_[REF(src)]",
hud_image_on(target),
antag_to_check || type,
+ get_team() && WEAKREF(get_team()),
))
// Add HUDs that they couldn't see before
diff --git a/code/modules/antagonists/_common/antag_hud.dm b/code/modules/antagonists/_common/antag_hud.dm
index 228bfc354df..863d52ef5ff 100644
--- a/code/modules/antagonists/_common/antag_hud.dm
+++ b/code/modules/antagonists/_common/antag_hud.dm
@@ -4,9 +4,12 @@ GLOBAL_LIST_EMPTY_TYPED(has_antagonist_huds, /datum/atom_hud/alternate_appearanc
/// An alternate appearance that will only show if you have the antag datum
/datum/atom_hud/alternate_appearance/basic/has_antagonist
var/antag_datum_type
+ /// Optionally, a weakref to antag team
+ var/datum/weakref/team_ref
-/datum/atom_hud/alternate_appearance/basic/has_antagonist/New(key, image/I, antag_datum_type)
+/datum/atom_hud/alternate_appearance/basic/has_antagonist/New(key, image/I, antag_datum_type, datum/weakref/team)
src.antag_datum_type = antag_datum_type
+ team_ref = team
GLOB.has_antagonist_huds += src
return ..(key, I, NONE)
@@ -15,6 +18,9 @@ GLOBAL_LIST_EMPTY_TYPED(has_antagonist_huds, /datum/atom_hud/alternate_appearanc
return ..()
/datum/atom_hud/alternate_appearance/basic/has_antagonist/mobShouldSee(mob/M)
+ var/datum/team/antag_team = team_ref?.resolve()
+ if(!isnull(antag_team))
+ return !!(M.mind in antag_team.members)
return !!M.mind?.has_antag_datum(antag_datum_type)
/// An alternate appearance that will show all the antagonists this mob has
diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm
index bc22eedbfa1..440feb91b76 100644
--- a/code/modules/antagonists/_common/antag_spawner.dm
+++ b/code/modules/antagonists/_common/antag_spawner.dm
@@ -443,7 +443,7 @@
name = "Syndicate Monkey Agent Kit"
head = /obj/item/clothing/head/fedora
- mask = /obj/item/clothing/mask/cigarette/syndicate
+ mask = /obj/item/cigarette/syndicate
uniform = /obj/item/clothing/under/syndicate
l_pocket = /obj/item/reagent_containers/cup/soda_cans/monkey_energy
r_pocket = /obj/item/storage/fancy/cigarettes/cigpack_syndicate
diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm
index 57e2ebf7d14..d84aa5131bf 100644
--- a/code/modules/antagonists/brother/brother.dm
+++ b/code/modules/antagonists/brother/brother.dm
@@ -75,9 +75,14 @@
flashed.balloon_alert(source, "unconscious!")
return
+#ifdef TESTING
+ if (isnull(flashed.mind))
+ flashed.mind_initialize()
+#else
if (isnull(flashed.mind) || !GET_CLIENT(flashed))
flashed.balloon_alert(source, "[flashed.p_their()] mind is vacant!")
return
+#endif
for(var/datum/objective/brother_objective as anything in source.mind.get_all_objectives())
// If the objective has a target, are we flashing them?
@@ -184,6 +189,11 @@
message_admins("[key_name_admin(admin)] made [key_name_admin(new_owner)] into a blood brother.")
log_admin("[key_name(admin)] made [key_name(new_owner)] into a blood brother.")
+/datum/antagonist/brother/apply_innate_effects(mob/living/mob_override)
+ . = ..()
+ var/mob/living/the_mob = owner.current || mob_override
+ add_team_hud(the_mob)
+
/datum/antagonist/brother/ui_static_data(mob/user)
var/list/data = list()
data["antag_name"] = name
@@ -220,8 +230,13 @@
/// Adds a new brother to the team
/datum/team/brother_team/proc/add_brother(mob/living/new_brother, source)
+#ifndef TESTING
if (isnull(new_brother) || isnull(new_brother.mind) || !GET_CLIENT(new_brother) || new_brother.mind.has_antag_datum(/datum/antagonist/brother))
return FALSE
+#else
+ if (isnull(new_brother) || new_brother.mind.has_antag_datum(/datum/antagonist/brother))
+ return FALSE
+#endif
set_brothers_left(brothers_left - 1)
for (var/datum/mind/brother_mind as anything in members)
diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm
index 2ea0304fae4..1a339fd1386 100644
--- a/code/modules/antagonists/changeling/powers/mutations.dm
+++ b/code/modules/antagonists/changeling/powers/mutations.dm
@@ -340,7 +340,7 @@
damage = 0
damage_type = BRUTE
range = 8
- hitsound = 'sound/weapons/thudswoosh.ogg'
+ hitsound = 'sound/weapons/shove.ogg'
var/chain
var/obj/item/ammo_casing/magic/tentacle/source //the item that shot it
///Click params that were used to fire the tentacle shot
diff --git a/code/modules/antagonists/clown_ops/clown_weapons.dm b/code/modules/antagonists/clown_ops/clown_weapons.dm
index 1c55e5416e2..130b6c9af5f 100644
--- a/code/modules/antagonists/clown_ops/clown_weapons.dm
+++ b/code/modules/antagonists/clown_ops/clown_weapons.dm
@@ -89,7 +89,7 @@
force = 0
throwforce = 0
hitsound = null
- embedding = null
+ embed_type = null
light_color = COLOR_YELLOW
sword_color_icon = "bananium"
active_heat = 0
diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm
index 6472ef9cca1..f680ab00bb6 100644
--- a/code/modules/antagonists/cult/blood_magic.dm
+++ b/code/modules/antagonists/cult/blood_magic.dm
@@ -12,6 +12,8 @@
default_button_position = DEFAULT_BLOODSPELLS
var/list/spells = list()
var/channeling = FALSE
+ /// If the magic has been enhanced somehow, likely due to a crimson focus.
+ var/magic_enhanced = FALSE
/datum/action/innate/cult/blood_magic/Remove()
for(var/X in spells)
@@ -29,7 +31,7 @@
var/atom/movable/screen/movable/action_button/button = viewers[hud]
var/position = screen_loc_to_offset(button.screen_loc)
var/list/position_list = list()
- for(var/possible_position in 1 to MAX_BLOODCHARGE)
+ for(var/possible_position in 1 to magic_enhanced ? ENHANCED_BLOODCHARGE : MAX_BLOODCHARGE)
position_list += possible_position
for(var/datum/action/innate/cult/blood_spell/blood_spell in spells)
if(blood_spell.positioned)
@@ -50,10 +52,10 @@
rune = TRUE
break
if(rune)
- limit = MAX_BLOODCHARGE
+ limit = magic_enhanced ? ENHANCED_BLOODCHARGE : MAX_BLOODCHARGE
if(length(spells) >= limit)
if(rune)
- to_chat(owner, span_cult_italic("You cannot store more than [MAX_BLOODCHARGE] spells. Pick a spell to remove."))
+ to_chat(owner, span_cult_italic("You cannot store more than [limit] spells. Pick a spell to remove."))
else
to_chat(owner, span_cult_bold_italic("You cannot store more than [RUNELESS_MAX_BLOODCHARGE] spells without an empowering rune! Pick a spell to remove."))
var/nullify_spell = tgui_input_list(owner, "Spell to remove", "Current Spells", spells)
@@ -86,10 +88,15 @@
else
to_chat(owner, span_cult_italic("You are already invoking blood magic!"))
return
- if(do_after(owner, 100 - rune*60, target = owner))
+ var/spell_carving_timer = 10 SECONDS
+ if(rune)
+ spell_carving_timer = 4 SECONDS
+ if(magic_enhanced)
+ spell_carving_timer *= 0.5
+ if(do_after(owner, spell_carving_timer, target = owner))
if(ishuman(owner))
var/mob/living/carbon/human/human_owner = owner
- human_owner.bleed(40 - rune*32)
+ human_owner.bleed(rune ? 8 : 40)
var/datum/action/innate/cult/blood_spell/new_spell = new BS(owner.mind)
new_spell.Grant(owner, src)
spells += new_spell
@@ -428,26 +435,43 @@
return
var/datum/antagonist/cult/cultist = IS_CULTIST(user)
var/datum/team/cult/cult_team = cultist.get_team()
- var/effect_coef = 1 - (cult_team.cult_risen ? 0.4 : 0) - (cult_team.cult_ascendent ? 0.5 : 0)
+ var/effect_coef = 1
+ if(cult_team.cult_ascendent)
+ effect_coef = 0.1
+ else if(cult_team.cult_risen)
+ effect_coef = 0.4
user.visible_message(
span_warning("[user] holds up [user.p_their()] hand, which explodes in a flash of red light!"),
span_cult_italic("You attempt to stun [target] with the spell!"),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
user.mob_light(range = 1.1, power = 2, color = LIGHT_COLOR_BLOOD_MAGIC, duration = 0.2 SECONDS)
+ // Heretics are momentarily disoriented by the stunning aura. Enough for both parties to go 'oh shit' but only a mild combat ability.
+ // Heretics have an identical effect on their grasp. The cultist's worse spell preparation is offset by their extra gear and teammates.
if(IS_HERETIC(target))
- to_chat(user, span_warning("Some force greater than you intervenes! [target] is protected by the Forgotten Gods!"))
- to_chat(target, span_warning("You are protected by your faith to the Forgotten Gods."))
+ target.AdjustKnockdown(0.5 SECONDS)
+ target.adjust_confusion_up_to(1.5 SECONDS, 3 SECONDS)
+ target.adjust_dizzy_up_to(1.5 SECONDS, 3 SECONDS)
+ ADD_TRAIT(target, TRAIT_NO_SIDE_KICK, REF(src)) // We don't want this to be a good stunning tool, just minor disorientation
+ addtimer(TRAIT_CALLBACK_REMOVE(target, TRAIT_NO_SIDE_KICK, REF(src)), 1 SECONDS)
+
var/old_color = target.color
- target.color = rgb(0, 128, 0)
+ target.color = COLOR_HERETIC_GREEN
+ animate(target, color = old_color, time = 4 SECONDS, easing = EASE_IN)
+ target.mob_light(range = 1.5, power = 2.5, color = COLOR_HERETIC_GREEN, duration = 0.5 SECONDS)
+ playsound(target, 'sound/magic/magic_block_mind.ogg', 150, TRUE) // insanely quiet
+
+ to_chat(user, span_warning("An eldritch force intervenes as you touch [target], absorbing most of the effects!"))
+ to_chat(target, span_warning("As [user] touches you with vile magicks, the Mansus absorbs most of the effects!"))
+ target.balloon_alert_to_viewers("absorbed!")
+ // SKYRAT EDIT ADDITION START
+ else if(IS_CLOCK(target))
+ to_chat(user, span_warning("Some force greater than you intervenes! [target] is protected by the heretic Ratvar!"))
+ to_chat(target, span_warning("You are protected by your faith to Ratvar!"))
+ var/old_color = target.color
+ target.color = rgb(190, 135, 0)
animate(target, color = old_color, time = 1 SECONDS, easing = EASE_IN)
- // SKYRAT EDIT START
- if(IS_CLOCK(target))
- to_chat(user, span_warning("Some force greater than you intervenes! [target] is protected by the heretic Ratvar!"))
- to_chat(target, span_warning("You are protected by your faith to Ratvar!"))
- target.color = rgb(190, 135, 0)
- animate(target, color = old_color, time = 1 SECONDS, easing = EASE_IN)
- // SKYRAT EDIT END
+ // SKYRAT EDIT ADDITION END
else if(target.can_block_magic())
to_chat(user, span_warning("The spell had no effect!"))
else
diff --git a/code/modules/antagonists/cult/cult_bastard_sword.dm b/code/modules/antagonists/cult/cult_bastard_sword.dm
deleted file mode 100644
index 0d70bd503fb..00000000000
--- a/code/modules/antagonists/cult/cult_bastard_sword.dm
+++ /dev/null
@@ -1,98 +0,0 @@
-
-/// Cult Bastard Sword, earned by cultists when they manage to sacrifice a heretic.
-/obj/item/cult_bastard
- name = "bloody bastard sword"
- desc = "An enormous sword used by Nar'Sien cultists to rapidly harvest the souls of non-believers."
- w_class = WEIGHT_CLASS_HUGE
- block_chance = 50
- block_sound = 'sound/weapons/parry.ogg'
- throwforce = 20
- force = 35
- armour_penetration = 45
- throw_speed = 1
- throw_range = 3
- sharpness = SHARP_EDGED
- light_color = "#ff0000"
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "rends")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "rend")
- icon = 'icons/obj/weapons/sword.dmi'
- icon_state = "cultbastard"
- inhand_icon_state = "cultbastard"
- hitsound = 'sound/weapons/bladeslice.ogg'
- lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
- inhand_x_dimension = 64
- inhand_y_dimension = 64
- actions_types = list()
- item_flags = SLOWS_WHILE_IN_HAND
- ///if we are using our attack_self ability
- var/spinning = FALSE
-
-/obj/item/cult_bastard/Initialize(mapload)
- . = ..()
- set_light(4)
- AddComponent(/datum/component/butchering, 50, 80)
- AddComponent(/datum/component/two_handed, require_twohands = TRUE)
- AddComponent(/datum/component/soul_stealer, soulstone_type = /obj/item/soulstone)
- AddComponent( \
- /datum/component/spin2win, \
- spin_cooldown_time = 25 SECONDS, \
- on_spin_callback = CALLBACK(src, PROC_REF(on_spin)), \
- on_unspin_callback = CALLBACK(src, PROC_REF(on_unspin)), \
- start_spin_message = span_danger("%USER begins swinging the sword around with inhuman strength!"), \
- end_spin_message = span_warning("%USER's inhuman strength dissipates and the sword's runes grow cold!") \
- )
-
-/obj/item/cult_bastard/proc/on_spin(mob/living/user, duration)
- var/oldcolor = user.color
- user.color = "#ff0000"
- user.add_stun_absorption(
- source = name,
- duration = duration,
- priority = 2,
- message = span_warning("%EFFECT_OWNER doesn't even flinch as the sword's power courses through [user.p_them()]!"),
- self_message = span_boldwarning("You shrug off the stun!"),
- examine_message = span_warning("%EFFECT_OWNER_THEYRE glowing with a blazing red aura!"),
- )
- user.spin(duration, 1)
- animate(user, color = oldcolor, time = duration, easing = EASE_IN)
- addtimer(CALLBACK(user, TYPE_PROC_REF(/atom, update_atom_colour)), duration)
- block_chance = 100
- slowdown += 1.5
- spinning = TRUE
-
-/obj/item/cult_bastard/proc/on_unspin(mob/living/user)
- block_chance = 50
- slowdown -= 1.5
- spinning = FALSE
-
-/obj/item/cult_bastard/can_be_pulled(user)
- return FALSE
-
-/obj/item/cult_bastard/pickup(mob/living/user)
- . = ..()
- if(!IS_CULTIST(user))
- if(!IS_HERETIC(user))
- to_chat(user, "\"I wouldn't advise that.\"")
- force = 5
- return
- else
- to_chat(user, span_cult_large("\"You cling to the Forgotten Gods, as if you're more than their pawn.\""))
- to_chat(user, span_userdanger("A horrible force yanks at your arm!"))
- user.emote("scream")
- user.apply_damage(30, BRUTE, pick(GLOB.arm_zones))
- user.dropItemToGround(src, TRUE)
- user.Paralyze(50)
- return
- force = initial(force)
-
-/obj/item/cult_bastard/IsReflect(def_zone)
- if(!spinning)
- return FALSE
- return TRUE
-
-/obj/item/cult_bastard/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
- if(!prob(final_block_chance))
- return FALSE
- owner.visible_message(span_danger("[owner] parries [attack_text] with [src]!"))
- return TRUE
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index 21fc0caf50a..595d95c168e 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -76,6 +76,8 @@ Striking a noncultist, however, will tear their flesh."}
block_sound = 'sound/weapons/parry.ogg'
attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "rends")
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "rend")
+ /// If TRUE, it can be used at will by anyone, non-cultists included
+ var/free_use = FALSE
/obj/item/melee/cultblade/Initialize(mapload)
. = ..()
@@ -93,7 +95,7 @@ Striking a noncultist, however, will tear their flesh."}
return FALSE
/obj/item/melee/cultblade/attack(mob/living/target, mob/living/carbon/human/user)
- if(!IS_CULTIST(user))
+ if(!IS_CULTIST(user) && !free_use)
user.Paralyze(100)
user.dropItemToGround(src, TRUE)
user.visible_message(span_warning("A powerful force shoves [user] away from [target]!"), \
@@ -106,6 +108,174 @@ Striking a noncultist, however, will tear their flesh."}
return
..()
+#define WIELDER_SPELLS "wielder_spell"
+#define SWORD_SPELLS "sword_spell"
+#define SWORD_PREFIX "sword_prefix"
+
+/obj/item/melee/cultblade/haunted
+ name = "haunted longsword"
+ desc = "An eerie sword with a blade that is less 'black' than it is 'absolute nothingness'. It glows with furious, restrained green energy."
+ icon_state = "hauntedblade"
+ inhand_icon_state = "hauntedblade"
+ worn_icon_state = "hauntedblade"
+ force = 35
+ throwforce = 35
+ block_chance = 55
+ wound_bonus = -25
+ bare_wound_bonus = 30
+ free_use = TRUE
+ light_color = COLOR_HERETIC_GREEN
+ light_range = 4
+ /// holder for the actual action when created.
+ var/datum/action/cooldown/spell/path_wielder_action
+ var/mob/living/trapped_entity
+ /// The heretic path that the variable below uses to index abilities. Assigned when the heretic is ensouled.
+ var/heretic_path
+ /// Nested static list used to index abilities and names.
+ var/static/list/heretic_paths_to_haunted_sword_abilities = list(
+ // Ash
+ PATH_ASH = list(
+ WIELDER_SPELLS = list(/datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash),
+ SWORD_SPELLS = list(/datum/action/cooldown/spell/pointed/ash_beams),
+ SWORD_PREFIX = "ashen",
+ ),
+ // Flesh
+ PATH_FLESH = list(
+ WIELDER_SPELLS = list(/datum/action/cooldown/spell/pointed/blood_siphon),
+ SWORD_SPELLS = list(/datum/action/cooldown/spell/pointed/cleave),
+ SWORD_PREFIX = "sanguine",
+ ),
+ // Void
+ PATH_VOID = list(
+ WIELDER_SPELLS = list(/datum/action/cooldown/spell/pointed/void_phase),
+ SWORD_SPELLS = list(/datum/action/cooldown/spell/cone/staggered/cone_of_cold/void),
+ SWORD_PREFIX = "tenebrous",
+ ),
+ // Blade
+ PATH_BLADE = list(
+ WIELDER_SPELLS = list(/datum/action/cooldown/spell/pointed/projectile/furious_steel/haunted),
+ SWORD_SPELLS = list(/datum/action/cooldown/spell/pointed/projectile/furious_steel/solo),
+ SWORD_PREFIX = "keen",
+ ),
+ // Rust
+ PATH_RUST = list(
+ WIELDER_SPELLS = list(/datum/action/cooldown/spell/cone/staggered/entropic_plume),
+ SWORD_SPELLS = list(/datum/action/cooldown/spell/aoe/rust_conversion, /datum/action/cooldown/spell/pointed/rust_construction),
+ SWORD_PREFIX = "rusted",
+ ),
+ // Cosmic
+ PATH_COSMIC = list(
+ WIELDER_SPELLS = list(/datum/action/cooldown/spell/conjure/cosmic_expansion),
+ SWORD_SPELLS = list(/datum/action/cooldown/spell/pointed/projectile/star_blast),
+ SWORD_PREFIX = "astral",
+ ),
+ // Lock
+ PATH_LOCK = list(
+ WIELDER_SPELLS = list(/datum/action/cooldown/spell/pointed/burglar_finesse),
+ SWORD_SPELLS = list(/datum/action/cooldown/spell/pointed/apetra_vulnera),
+ SWORD_PREFIX = "incisive",
+ ),
+ // Moon
+ PATH_MOON = list(
+ WIELDER_SPELLS = list(/datum/action/cooldown/spell/pointed/projectile/moon_parade),
+ SWORD_SPELLS = list(/datum/action/cooldown/spell/pointed/moon_smile),
+ SWORD_PREFIX = "shimmering",
+ ),
+ // Starter
+ PATH_START = list(
+ WIELDER_SPELLS = null,
+ SWORD_SPELLS = null,
+ SWORD_PREFIX = "stillborn", // lol loser
+ ) ,
+ )
+
+/obj/item/melee/cultblade/haunted/Initialize(mapload, mob/soul_to_bind, mob/awakener, do_bind = TRUE)
+ . = ..()
+
+ AddElement(/datum/element/heretic_focus)
+ add_traits(list(TRAIT_CASTABLE_LOC, TRAIT_SPELLS_TRANSFER_TO_LOC), INNATE_TRAIT)
+ if(do_bind && !mapload)
+ bind_soul(soul_to_bind, awakener)
+
+/obj/item/melee/cultblade/haunted/proc/bind_soul(mob/soul_to_bind, mob/awakener)
+
+ var/datum/mind/trapped_mind = soul_to_bind?.mind
+
+ if(!trapped_mind)
+ return // Can't do anything further down the list
+
+ if(trapped_mind)
+ AddComponent(/datum/component/spirit_holding,\
+ soul_to_bind = trapped_mind,\
+ awakener = awakener,\
+ allow_renaming = FALSE,\
+ allow_channeling = FALSE,\
+ )
+
+ // Get the heretic's new body and antag datum.
+ trapped_entity = trapped_mind?.current
+ trapped_entity.key = trapped_mind?.key
+ var/datum/antagonist/heretic/heretic_holder = IS_HERETIC(trapped_entity)
+ if(!heretic_holder)
+ stack_trace("[soul_to_bind] in but not a heretic on the heretic soul blade.")
+
+ // Give the spirit a spell that lets them try to fly around.
+ var/datum/action/cooldown/spell/pointed/sword_fling/fling_act = \
+ new /datum/action/cooldown/spell/pointed/sword_fling(trapped_mind, to_fling = src)
+ fling_act.Grant(trapped_entity)
+
+ // Set the sword's path for spell selection.
+ heretic_path = heretic_holder.heretic_path
+
+ // Copy the objectives to keep for roundend, remove the datum as neither us nor the heretic need it anymore
+ var/list/copied_objectives = heretic_holder.objectives.Copy()
+ trapped_entity.mind.remove_antag_datum(/datum/antagonist/heretic)
+
+ // Add the fallen antag datum, give them a heads-up of what's happening.
+ var/datum/antagonist/soultrapped_heretic/bozo = new()
+ bozo.objectives |= copied_objectives
+ trapped_entity.mind.add_antag_datum(bozo)
+ to_chat(trapped_entity, span_userdanger("You've been sacrificed to the Enemy, and trapped inside a haunted blade! While you cannot escape, you may help the Cult, your current wielder, or even pester everyone with what few abilities you kept."))
+
+ // Assigning the spells to give to the wielder and spirit.
+ // Let them cast the given spell.
+ ADD_TRAIT(trapped_entity, TRAIT_ALLOW_HERETIC_CASTING, INNATE_TRAIT)
+
+ var/list/path_spells = heretic_paths_to_haunted_sword_abilities[heretic_path]
+
+ var/list/wielder_spells = path_spells[WIELDER_SPELLS]
+ var/list/sword_spells = path_spells[SWORD_SPELLS]
+
+ name = "[path_spells[SWORD_PREFIX]] [name]"
+
+
+ // Granting the path spells. The sword spirit gains it outright, while it's just instanced for wielders to be added on pickup.
+
+ if(sword_spells)
+ for(var/datum/action/cooldown/spell/sword_spell as anything in sword_spells)
+ sword_spell = new sword_spell(trapped_entity)
+ sword_spell?.Grant(trapped_entity)
+ sword_spell?.overlay_icon_state = "bg_cult_border" // for flavor, and also helps distinguish
+
+
+ if(wielder_spells)
+ for(var/datum/action/cooldown/spell/wielder_spell as anything in wielder_spells)
+ path_wielder_action = new wielder_spell(src)
+ wielder_spell?.overlay_icon_state = "bg_cult_border"
+
+/obj/item/melee/cultblade/haunted/equipped(mob/user, slot, initial)
+ . = ..()
+ if(slot & ITEM_SLOT_HANDS)
+ path_wielder_action?.Grant(user)
+
+/obj/item/melee/cultblade/haunted/dropped(mob/user, silent)
+ . = ..()
+ path_wielder_action?.Remove(user)
+
+#undef WIELDER_SPELLS
+#undef SWORD_SPELLS
+#undef SWORD_PREFIX
+
/obj/item/melee/cultblade/ghost
name = "eldritch sword"
force = 19 //can't break normal airlocks
@@ -120,7 +290,7 @@ Striking a noncultist, however, will tear their flesh."}
/obj/item/melee/cultblade/pickup(mob/living/user)
..()
- if(!IS_CULTIST(user))
+ if(!IS_CULTIST(user) && !free_use)
to_chat(user, span_cult_large("\"I wouldn't advise that.\""))
/datum/action/innate/dash/cult
@@ -464,6 +634,10 @@ Striking a noncultist, however, will tear their flesh."}
icon_state = "blindfold"
inhand_icon_state = "blindfold"
flash_protect = FLASH_PROTECTION_WELDER
+ actions_types = null
+ color_cutoffs = list(40, 0, 0) //red
+ glass_colour_type = null
+ forced_glass_color = FALSE
/obj/item/clothing/glasses/hud/health/night/cultblind/equipped(mob/living/user, slot)
..()
@@ -564,6 +738,78 @@ Striking a noncultist, however, will tear their flesh."}
#undef MAX_SHUTTLE_CURSES
+#define GATEWAY_TURF_SCAN_RANGE 40
+
+/obj/item/proteon_orb
+ name = "summoning orb"
+ desc = "An eerie translucent orb that feels impossibly light. Legends say summoning orbs are created from corrupted scrying orbs. If you hold it close to your ears, you can hear the screams of the damned."
+ icon = 'icons/obj/antags/cult/items.dmi'
+ icon_state = "summoning_orb"
+ light_range = 3
+ light_color = COLOR_CULT_RED
+
+/obj/item/proteon_orb/examine(mob/user)
+ . = ..()
+ if(!IS_CULTIST(user) && isliving(user))
+ var/mob/living/living_user = user
+ living_user.adjustOrganLoss(ORGAN_SLOT_BRAIN, 5)
+ . += span_danger("It hurts just to look at it. Better keep away.")
+ else
+ . += span_cult("It can be used to create a gateway to Nar'Sie's domain, which will summon weak, sentient constructs over time.")
+
+/obj/item/proteon_orb/attack_self(mob/living/user)
+
+ var/list/turfs_to_scan = detect_room(get_turf(user), max_size = GATEWAY_TURF_SCAN_RANGE)
+
+ if(!IS_CULTIST(user))
+ to_chat(user, span_cult_large("\"You want to enter my domain? Go ahead.\""))
+ turfs_to_scan = null // narsie wants to have some fun and the veil wont stop her
+
+ for(var/turf/hole_candidate as anything in turfs_to_scan)
+ if(locate(/obj/structure/spawner/sentient/proteon_spawner) in hole_candidate)
+ to_chat(user, span_cult_bold("There's a gateway too close nearby. The veil is not yet weak enough to allow such close rips in its fabric."))
+ return
+ to_chat(user, span_cult_bold_italic("You focus on [src] and direct it into the ground. It rumbles..."))
+
+ var/turf/open/hole_spot = get_turf(user)
+ if(!istype(hole_spot) || isgroundlessturf(hole_spot))
+ to_chat(user, span_notice("This is not a suitable spot."))
+ return
+
+ INVOKE_ASYNC(hole_spot, TYPE_PROC_REF(/turf/open, quake_gateway), user)
+ qdel(src)
+
+/**
+ * Bespoke proc that happens when a proteon orb is activated, creating a gateway.
+ * If activated by a non-cultist, they get an unusual game over.
+*/
+/turf/open/proc/quake_gateway(mob/living/user)
+ Shake(2, 2, 5 SECONDS)
+ narsie_act(TRUE, TRUE, 100)
+ var/fucked = FALSE
+ if(!IS_CULTIST(user))
+ fucked = TRUE
+ ADD_TRAIT(user, TRAIT_NO_TRANSFORM, REF(src)) // keep em in place
+ user.add_atom_colour(COLOR_CULT_RED, TEMPORARY_COLOUR_PRIORITY)
+ user.visible_message(span_cult_bold("Dark tendrils appear from the ground and root [user] in place!"))
+ sleep(5 SECONDS) // can we still use these or. i mean its async
+ new /obj/structure/spawner/sentient/proteon_spawner(src)
+ visible_message(span_cult_bold("A mysterious hole appears out of nowhere!"))
+ if(!fucked || QDELETED(user))
+ return
+ if(get_turf(user) != src) // they get away. for now
+ REMOVE_TRAIT(user, TRAIT_NO_TRANSFORM, REF(src))
+ return
+ user.visible_message(span_cult_bold("[user] is pulled into the portal through an infinitesmally minuscule hole, shredding [user.p_their()] body!"))
+ sleep(5 SECONDS)
+ user.visible_message(span_cult_italic("An unusually large construct appears through the portal!"))
+ user.gib() // total destruction
+ var/mob/living/basic/construct/proteon/hostile/remnant = new(get_step_rand(src))
+ remnant.name = "[user]" // no, they do not become it
+ remnant.transform *= 1.5
+
+#undef GATEWAY_TURF_SCAN_RANGE
+
/obj/item/cult_shift
name = "veil shifter"
desc = "This relic instantly teleports you, and anything you're pulling, forward by a moderate distance."
diff --git a/code/modules/antagonists/cult/cult_other.dm b/code/modules/antagonists/cult/cult_other.dm
index 82b342b87ef..9435baedba1 100644
--- a/code/modules/antagonists/cult/cult_other.dm
+++ b/code/modules/antagonists/cult/cult_other.dm
@@ -16,8 +16,15 @@
/proc/is_convertable_to_cult(mob/living/target, datum/team/cult/specific_cult)
if(!istype(target))
return FALSE
- if(isnull(target.mind) || !GET_CLIENT(target))
+ if(isnull(target.mind))
return FALSE
+
+// disables client checks if testing for easier debugging
+#ifndef TESTING
+ if(!GET_CLIENT(target))
+ return FALSE
+#endif
+
if(target.mind.unconvertable)
return FALSE
if(ishuman(target) && target.mind.holy_role)
diff --git a/code/modules/antagonists/cult/cult_structure_altar.dm b/code/modules/antagonists/cult/cult_structure_altar.dm
index 9347acb3321..e38591c0c07 100644
--- a/code/modules/antagonists/cult/cult_structure_altar.dm
+++ b/code/modules/antagonists/cult/cult_structure_altar.dm
@@ -2,6 +2,7 @@
#define ELDRITCH_WHETSTONE "Eldritch Whetstone"
#define CONSTRUCT_SHELL "Construct Shell"
#define UNHOLY_WATER "Flask of Unholy Water"
+#define PROTEON_ORB "Portal Summoning Orb"
// Cult altar. Gives out consumable items.
/obj/structure/destructible/cult/item_dispenser/altar
@@ -10,6 +11,7 @@
cult_examine_tip = "Can be used to create eldritch whetstones, construct shells, and flasks of unholy water."
icon_state = "talismanaltar"
break_message = "The altar shatters, leaving only the wailing of the damned!"
+ mansus_conversion_path = /obj/effect/heretic_rune
/obj/structure/destructible/cult/item_dispenser/altar/setup_options()
var/static/list/altar_items = list(
@@ -27,7 +29,20 @@
),
)
+ var/extra_item = extra_options()
+
options = altar_items
+ if(!isnull(extra_item))
+ options += extra_item
+
+/obj/structure/destructible/cult/item_dispenser/altar/extra_options()
+ if(!cult_team?.unlocked_heretic_items[PROTEON_ORB_UNLOCKED])
+ return
+ return list(PROTEON_ORB = list(
+ PREVIEW_IMAGE = image(icon = 'icons/obj/antags/cult/items.dmi', icon_state = "summoning_orb"),
+ OUTPUT_ITEMS = list(/obj/item/proteon_orb),
+ ),
+ )
/obj/structure/destructible/cult/item_dispenser/altar/succcess_message(mob/living/user, obj/item/spawned_item)
to_chat(user, span_cult_italic("You kneel before [src] and your faith is rewarded with [spawned_item]!"))
@@ -35,3 +50,4 @@
#undef ELDRITCH_WHETSTONE
#undef CONSTRUCT_SHELL
#undef UNHOLY_WATER
+#undef PROTEON_ORB
diff --git a/code/modules/antagonists/cult/cult_structure_archives.dm b/code/modules/antagonists/cult/cult_structure_archives.dm
index a9617396633..d3a96dd1f77 100644
--- a/code/modules/antagonists/cult/cult_structure_archives.dm
+++ b/code/modules/antagonists/cult/cult_structure_archives.dm
@@ -2,6 +2,7 @@
#define CULT_BLINDFOLD "Zealot's Blindfold"
#define CURSE_ORB "Shuttle Curse"
#define VEIL_WALKER "Veil Walker Set"
+#define CRIMSON_FOCUS "Crimson Focus"
// Cult archives. Gives out utility items.
/obj/structure/destructible/cult/item_dispenser/archives
@@ -12,6 +13,7 @@
light_range = 1.5
light_color = LIGHT_COLOR_FIRE
break_message = "The books and tomes of the archives burn into ash as the desk shatters!"
+ mansus_conversion_path = /obj/item/codex_cicatrix
/obj/structure/destructible/cult/item_dispenser/archives/setup_options()
var/static/list/archive_items = list(
@@ -29,7 +31,20 @@
),
)
+ var/extra_item = extra_options()
+
options = archive_items
+ if(!isnull(extra_item))
+ options += extra_item
+
+/obj/structure/destructible/cult/item_dispenser/archives/extra_options()
+ if(!cult_team?.unlocked_heretic_items[CRIMSON_FOCUS_UNLOCKED])
+ return
+ return list(CRIMSON_FOCUS = list(
+ PREVIEW_IMAGE = image(icon = 'icons/obj/clothing/neck.dmi', icon_state = "crimson_focus"),
+ OUTPUT_ITEMS = list(/obj/item/clothing/neck/heretic_focus/crimson_focus),
+ ),
+ )
/obj/structure/destructible/cult/item_dispenser/archives/succcess_message(mob/living/user, obj/item/spawned_item)
to_chat(user, span_cult_italic("You summon [spawned_item] from [src]!"))
@@ -41,3 +56,4 @@
#undef CULT_BLINDFOLD
#undef CURSE_ORB
#undef VEIL_WALKER
+#undef CRIMSON_FOCUS
diff --git a/code/modules/antagonists/cult/cult_structure_forge.dm b/code/modules/antagonists/cult/cult_structure_forge.dm
index 912db7d37e9..12d15b9296e 100644
--- a/code/modules/antagonists/cult/cult_structure_forge.dm
+++ b/code/modules/antagonists/cult/cult_structure_forge.dm
@@ -2,6 +2,7 @@
#define NARSIE_ARMOR "Nar'Sien Hardened Armor"
#define FLAGELLANT_ARMOR "Flagellant's Robe"
#define ELDRITCH_SWORD "Eldritch Longsword"
+#define CURSED_BLADE "Cursed Ritual Blade"
// Cult forge. Gives out combat weapons.
/obj/structure/destructible/cult/item_dispenser/forge
@@ -12,6 +13,7 @@
light_range = 2
light_color = LIGHT_COLOR_LAVA
break_message = "The forge breaks apart into shards with a howling scream!"
+ mansus_conversion_path = /obj/structure/destructible/eldritch_crucible
/obj/structure/destructible/cult/item_dispenser/forge/setup_options()
var/static/list/forge_items = list(
@@ -29,7 +31,21 @@
),
)
+ var/extra_item = extra_options()
+
options = forge_items
+ if(!isnull(extra_item))
+ options += extra_item
+
+/obj/structure/destructible/cult/item_dispenser/forge/extra_options()
+ if(!cult_team?.unlocked_heretic_items[CURSED_BLADE_UNLOCKED])
+ return
+ return list(CURSED_BLADE = list(
+ PREVIEW_IMAGE = image(icon = 'icons/obj/weapons/khopesh.dmi', icon_state = "cursed_blade"),
+ OUTPUT_ITEMS = list(/obj/item/melee/sickly_blade/cursed),
+ ),
+ )
+
/obj/structure/destructible/cult/item_dispenser/forge/succcess_message(mob/living/user, obj/item/spawned_item)
to_chat(user, span_cult_italic("You work [src] as dark knowledge guides your hands, creating [spawned_item]!"))
@@ -42,3 +58,4 @@
#undef NARSIE_ARMOR
#undef FLAGELLANT_ARMOR
#undef ELDRITCH_SWORD
+#undef CURSED_BLADE
diff --git a/code/modules/antagonists/cult/cult_structures.dm b/code/modules/antagonists/cult/cult_structures.dm
index 932c3ac03c1..5067dcf9799 100644
--- a/code/modules/antagonists/cult/cult_structures.dm
+++ b/code/modules/antagonists/cult/cult_structures.dm
@@ -12,16 +12,43 @@
var/cult_examine_tip
/// The cooldown for when items can be dispensed.
COOLDOWN_DECLARE(use_cooldown)
+ /// Assigned cult team, set when cultistism is checked.
+ var/datum/team/cult/cult_team
+
+/obj/structure/destructible/cult/Destroy()
+ cult_team = null
+ return ..()
+
+/obj/structure/destructible/cult/Initialize(mapload)
+ . = ..()
+ RegisterSignal(src, COMSIG_ATOM_CONSTRUCTED, PROC_REF(on_constructed))
+
+/obj/structure/destructible/cult/proc/on_constructed(datum/source, mob/builder)
+ SIGNAL_HANDLER
+ var/datum/antagonist/cult/cultist = builder.mind?.has_antag_datum(/datum/antagonist/cult, TRUE)
+ cult_team = cultist?.get_team()
+
+/// Tries to find a cultist. If it succeeds, it also takes advantage of the moment to define the structure's cult team if it's not set yet.
+/obj/structure/destructible/cult/proc/is_cultist_check(mob/fool)
+
+ if(!IS_CULTIST(fool))
+ return FALSE
+
+ if(isnull(cult_team))
+ var/datum/antagonist/cult/cultist = fool.mind?.has_antag_datum(/datum/antagonist/cult, TRUE)
+ cult_team = cultist?.get_team()
+
+ return TRUE
/obj/structure/destructible/cult/examine_status(mob/user)
- if(IS_CULTIST(user) || isobserver(user))
+ if(is_cultist_check(user) || isobserver(user))
return span_cult("It's at [round(atom_integrity * 100 / max_integrity)]% stability.")
return ..()
/obj/structure/destructible/cult/examine(mob/user)
. = ..()
. += span_notice("[src] is [anchored ? "secured to":"unsecured from"] the floor.")
- if(IS_CULTIST(user) || isobserver(user))
+ if(is_cultist_check(user) || isobserver(user))
if(cult_examine_tip)
. += span_cult(cult_examine_tip)
if(!COOLDOWN_FINISHED(src, use_cooldown_duration))
@@ -65,16 +92,25 @@
/obj/structure/destructible/cult/item_dispenser
/// An associated list of options this structure can make. See setup_options() for format.
var/list/options
+ /// The dispenser will create this item and then delete itself if it is rust converted.
+ var/obj/mansus_conversion_path = /obj/item/skub
/obj/structure/destructible/cult/item_dispenser/Initialize(mapload)
. = ..()
setup_options()
+/obj/structure/destructible/cult/item_dispenser/rust_heretic_act()
+ visible_message(span_notice("[src] crumbles to dust. In its midst, you spot \a [initial(mansus_conversion_path.name)]."))
+ var/turf/turfy = get_turf(src)
+ new mansus_conversion_path(turfy)
+ turfy.rust_heretic_act()
+ return ..()
+
/obj/structure/destructible/cult/item_dispenser/attack_hand(mob/living/user, list/modifiers)
. = ..()
if(.)
return
- if(!isliving(user) || !IS_CULTIST(user))
+ if(!isliving(user) || !is_cultist_check(user))
to_chat(user, span_warning("You're pretty sure you know exactly what this is used for and you can't seem to touch it."))
return
if(!anchored)
@@ -84,6 +120,8 @@
to_chat(user, span_cult_italic("The magic in [src] is too weak, it will be ready to use again in [DisplayTimeText(COOLDOWN_TIMELEFT(src, use_cooldown))]."))
return
+ setup_options()
+
var/list/spawned_items = get_items_to_spawn(user)
if(!length(spawned_items))
return
@@ -109,6 +147,18 @@
/obj/structure/destructible/cult/item_dispenser/proc/setup_options()
return
+/*
+ * Extra options, currently used for items unlocked after sacrificing a heretic.
+ *
+ * The list of options is a associated list of format:
+ * item_name = list(
+ * preview = image(),
+ * output = list(paths),
+ * )
+ */
+/obj/structure/destructible/cult/item_dispenser/proc/extra_options()
+ return
+
/*
* Get all items that this cult building will spawn when interacted with.
* Opens a radial menu for the user and shows them the list of options, which they can choose from.
@@ -150,7 +200,7 @@
* Returns TRUE if the user is a living mob that is a cultist and is not incapacitated.
*/
/obj/structure/destructible/cult/item_dispenser/proc/check_menu(mob/user)
- return isliving(user) && IS_CULTIST(user) && !user.incapacitated()
+ return isliving(user) && is_cultist_check(user) && !user.incapacitated()
// Spooky looking door used in gateways. Or something.
/obj/effect/gateway
diff --git a/code/modules/antagonists/cult/datums/cult_team.dm b/code/modules/antagonists/cult/datums/cult_team.dm
index c47cc2145b5..09d4a25a321 100644
--- a/code/modules/antagonists/cult/datums/cult_team.dm
+++ b/code/modules/antagonists/cult/datums/cult_team.dm
@@ -16,9 +16,16 @@
var/reckoning_complete = FALSE
///Has the cult risen, and gotten red eyes?
var/cult_risen = FALSE
- ///Has the cult asceneded, and gotten halos?
+ ///Has the cult ascended, and gotten halos?
var/cult_ascendent = FALSE
+ /// List that keeps track of which items have been unlocked after a heretic was sacked.
+ var/list/unlocked_heretic_items = list(
+ CURSED_BLADE_UNLOCKED = FALSE,
+ CRIMSON_FOCUS_UNLOCKED = FALSE,
+ PROTEON_ORB_UNLOCKED = FALSE,
+ )
+
///Has narsie been summoned yet?
var/narsie_summoned = FALSE
///How large were we at max size.
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index 926981bb096..d8ce241caf3 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -326,16 +326,12 @@ structure_check() searches for nearby cultist structures required for the invoca
return TRUE
/obj/effect/rune/convert/proc/do_sacrifice(mob/living/sacrificial, list/invokers, datum/team/cult/cult_team)
- var/big_sac = FALSE
+ var/target_sac = FALSE
if((((ishuman(sacrificial) || iscyborg(sacrificial)) && sacrificial.stat != DEAD) || cult_team.is_sacrifice_target(sacrificial.mind)) && length(invokers) < 3)
for(var/invoker in invokers)
to_chat(invoker, span_cult_italic("[sacrificial] is too greatly linked to the world! You need three acolytes!"))
return FALSE
- var/signal_result = SEND_SIGNAL(sacrificial, COMSIG_LIVING_CULT_SACRIFICED, invokers, cult_team)
- if(signal_result & STOP_SACRIFICE)
- return FALSE
-
if(sacrificial.mind)
LAZYADD(GLOB.sacrificed, WEAKREF(sacrificial.mind))
for(var/datum/objective/sacrifice/sac_objective in cult_team.objectives)
@@ -343,15 +339,23 @@ structure_check() searches for nearby cultist structures required for the invoca
sac_objective.sacced = TRUE
sac_objective.clear_sacrifice()
sac_objective.update_explanation_text()
- big_sac = TRUE
+ target_sac = TRUE
else
LAZYADD(GLOB.sacrificed, WEAKREF(sacrificial))
new /obj/effect/temp_visual/cult/sac(loc)
- if(!(signal_result & SILENCE_SACRIFICE_MESSAGE))
+ var/signal_result = SEND_SIGNAL(sacrificial, COMSIG_LIVING_CULT_SACRIFICED, invokers, cult_team)
+
+ var/do_message = TRUE
+ if(signal_result & SILENCE_SACRIFICE_MESSAGE)
+ do_message = FALSE
+ if((signal_result & SILENCE_NONTARGET_SACRIFICE_MESSAGE) && !(target_sac))
+ do_message = FALSE
+
+ if(do_message)
for(var/invoker in invokers)
- if(big_sac)
+ if(target_sac)
to_chat(invoker, span_cult_large("\"Yes! This is the one I desire! You have done well.\""))
continue
if(ishuman(sacrificial) || iscyborg(sacrificial))
@@ -359,6 +363,10 @@ structure_check() searches for nearby cultist structures required for the invoca
else
to_chat(invoker, span_cult_large("\"I accept this meager sacrifice.\""))
+ // post-message
+ if(signal_result & STOP_SACRIFICE)
+ return FALSE
+
if(iscyborg(sacrificial))
var/construct_class = show_radial_menu(invokers[1], sacrificial, GLOB.construct_radial_images, require_near = TRUE, tooltips = TRUE)
if(QDELETED(sacrificial) || !construct_class)
@@ -370,17 +378,20 @@ structure_check() searches for nearby cultist structures required for the invoca
sacriborg.mmi = null
qdel(sacrificial)
return TRUE
-
- var/obj/item/soulstone/stone = new(loc)
- if(sacrificial.mind && !HAS_TRAIT(sacrificial, TRAIT_SUICIDED))
- stone.capture_soul(sacrificial, invokers[1], forced = TRUE)
-
- if(sacrificial)
+ if(sacrificial && (signal_result & DUST_SACRIFICE)) // No soulstone when dusted
+ playsound(sacrificial, 'sound/magic/teleport_diss.ogg', 100, TRUE)
+ sacrificial.investigate_log("has been sacrificially dusted by the cult.", INVESTIGATE_DEATHS)
+ sacrificial.dust(TRUE, FALSE, TRUE)
+ else if (sacrificial)
+ var/obj/item/soulstone/stone = new(loc)
+ if(sacrificial.mind && !HAS_TRAIT(sacrificial, TRAIT_SUICIDED))
+ stone.capture_soul(sacrificial, invokers[1], forced = TRUE)
playsound(sacrificial, 'sound/magic/disintegrate.ogg', 100, TRUE)
sacrificial.investigate_log("has been sacrificially gibbed by the cult.", INVESTIGATE_DEATHS)
sacrificial.gib(DROP_ALL_REMAINS)
try_spawn_sword() // after sharding and gibbing, which potentially dropped a null rod
+
return TRUE
/// Tries to convert a null rod over the rune to a cult sword
@@ -396,12 +407,12 @@ structure_check() searches for nearby cultist structures required for the invoca
rod.visible_message(span_cult_italic(displayed_message))
switch(num_slain)
- if(0, 1)
+ if(0)
animate_spawn_sword(rod, /obj/item/melee/cultblade/dagger)
- if(2)
+ if(1)
animate_spawn_sword(rod, /obj/item/melee/cultblade)
else
- animate_spawn_sword(rod, /obj/item/cult_bastard)
+ animate_spawn_sword(rod, /obj/item/melee/cultblade/halberd)
return TRUE
return FALSE
diff --git a/code/modules/antagonists/cult/sword_fling.dm b/code/modules/antagonists/cult/sword_fling.dm
new file mode 100644
index 00000000000..766f97917de
--- /dev/null
+++ b/code/modules/antagonists/cult/sword_fling.dm
@@ -0,0 +1,93 @@
+
+/datum/action/cooldown/spell/pointed/sword_fling
+ name = "Sword Fling"
+ desc = "Try to fling yourself around."
+ ranged_mousepointer = 'icons/effects/mouse_pointers/cult_target.dmi'
+ background_icon_state = "bg_heretic"
+ overlay_icon_state = "bg_cult_border"
+
+ button_icon = 'icons/mob/actions/actions_cult.dmi'
+ button_icon_state = "sword_fling"
+
+ school = SCHOOL_EVOCATION
+ cooldown_time = 4 SECONDS
+ invocation_type = INVOCATION_NONE
+ spell_requirements = NONE
+
+ cast_range = 6
+ active_msg = "You ready yourself to attempt to leap!"
+ var/obj/item/flinged_sword
+
+/datum/action/cooldown/spell/pointed/sword_fling/New(Target, to_fling)
+ . = ..()
+ flinged_sword = to_fling
+
+/datum/action/cooldown/spell/pointed/sword_fling/Destroy()
+ flinged_sword = null
+ . = ..()
+
+/datum/action/cooldown/spell/pointed/sword_fling/is_valid_target(atom/cast_on)
+ return isatom(cast_on)
+
+/datum/action/cooldown/spell/pointed/sword_fling/cast(turf/cast_on)
+ . = ..()
+ var/atom/sword_loc = flinged_sword.loc
+ if(ismob(sword_loc))
+ var/mob/loccer = sword_loc
+ var/resist_chance = 25
+ var/fail_text = "You struggle, but [loccer] keeps [loccer.p_their()] grip on you!"
+ var/particle_to_spawn = null
+ if(IS_CULTIST_OR_CULTIST_MOB(loccer))
+ resist_chance = 10 // your mastahs
+ fail_text = "You struggle, but [loccer]'s grip is unnaturally hard to resist!"
+ particle_to_spawn = /obj/effect/temp_visual/cult/sparks
+ if(IS_HERETIC_OR_MONSTER(loccer) || IS_LUNATIC(loccer))
+ resist_chance = 15
+ fail_text = "You struggle, but [loccer] deftly handles the grip movement."
+ particle_to_spawn = /obj/effect/temp_visual/eldritch_sparks
+ if(loccer.mind?.holy_role) // IS_PRIEST()
+ resist_chance = 12
+ fail_text = "You struggle, but [loccer]'s holy grip holds tight against your thrashing."
+ particle_to_spawn = /obj/effect/temp_visual/blessed
+ if(IS_WIZARD(loccer))
+ resist_chance = 5 // magic master
+ fail_text = "You struggle, but [loccer]'s handle on magic easily neutralizes your movement."
+ particle_to_spawn = /obj/effect/particle_effect/sparks/electricity
+
+ new particle_to_spawn(get_turf(loccer))
+
+ if(prob(resist_chance))
+ flinged_sword.forceMove(get_turf(loccer))
+ flinged_sword.visible_message(span_alert("\the [flinged_sword] yanks itself out of [loccer]'s grip!"))
+ // flung by later code
+ else
+ to_chat(owner, span_warning(fail_text))
+ return
+
+ if(isitem(sword_loc))
+ flinged_sword.forceMove(get_turf(sword_loc))
+ flinged_sword.visible_message(span_alert("\the [flinged_sword] yanks itself out of [sword_loc]!"))
+ // flung by later code
+
+ if(iscloset(sword_loc))
+ var/obj/structure/closet/sword_closet = sword_loc
+ if(!(sword_closet.open(owner, force = prob(5), special_effects = TRUE)))
+ sword_closet.container_resist_act(owner, loc_required = FALSE)
+ flinged_sword.visible_message(span_alert("\the [flinged_sword] yanks itself out of [sword_closet]!"))
+
+ // no general struct/machinery check. imagine if someone put the sword in a vendor
+
+ if(isturf(sword_loc))
+ new /obj/effect/temp_visual/sword_sparks(sword_loc)
+ flinged_sword.throw_at(cast_on, cast_range, flinged_sword.throw_speed, owner)
+ flinged_sword.visible_message(\
+ span_warning("\the [flinged_sword] lunges at \the [cast_on]!"))
+
+/obj/effect/temp_visual/eldritch_sparks
+ icon_state = "purplesparkles"
+
+/obj/effect/temp_visual/sword_sparks
+ icon_state = "mech_toxin" // only used in one place and it looks kinda good
+
+/obj/effect/temp_visual/blessed
+ icon_state = "blessed"
diff --git a/code/modules/antagonists/fugitive/fugitive_outfits.dm b/code/modules/antagonists/fugitive/fugitive_outfits.dm
index ed256acae03..e1530ba16ed 100644
--- a/code/modules/antagonists/fugitive/fugitive_outfits.dm
+++ b/code/modules/antagonists/fugitive/fugitive_outfits.dm
@@ -75,7 +75,7 @@
back = /obj/item/storage/backpack/satchel/leather
shoes = /obj/item/clothing/shoes/laceup
glasses = /obj/item/clothing/glasses/monocle
- mask = /obj/item/clothing/mask/cigarette/pipe
+ mask = /obj/item/cigarette/pipe
ears = /obj/item/radio/headset
backpack_contents = list(
diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm
index e6ae01427f2..8f788697d42 100644
--- a/code/modules/antagonists/heretic/heretic_antag.dm
+++ b/code/modules/antagonists/heretic/heretic_antag.dm
@@ -60,17 +60,17 @@
/// Wether we are allowed to ascend
var/feast_of_owls = FALSE
/// Static list of what each path converts to in the UI (colors are TGUI colors)
- var/static/list/path_to_ui_color = list(
- PATH_START = "grey",
- PATH_SIDE = "green",
- PATH_RUST = "brown",
- PATH_FLESH = "red",
- PATH_ASH = "white",
- PATH_VOID = "blue",
- PATH_BLADE = "label", // my favorite color is label
- PATH_COSMIC = "purple",
- PATH_LOCK = "yellow",
- PATH_MOON = "blue",
+ var/static/list/path_to_ui_bgr = list(
+ PATH_START = "node_side",
+ PATH_SIDE = "node_side",
+ PATH_RUST = "node_rust",
+ PATH_FLESH = "node_flesh",
+ PATH_ASH = "node_ash",
+ PATH_VOID = "node_void",
+ PATH_BLADE = "node_blade",
+ PATH_COSMIC = "node_cosmos",
+ PATH_LOCK = "node_lock",
+ PATH_MOON = "node_moon",
)
var/static/list/path_to_rune_color = list(
@@ -85,10 +85,95 @@
PATH_MOON = COLOR_BLUE_LIGHT,
)
+ /// List that keeps track of which items have been gifted to the heretic after a cultist was sacrificed. Used to alter drop chances to reduce dupes.
+ var/list/unlocked_heretic_items = list(
+ /obj/item/melee/sickly_blade/cursed = 0,
+ /obj/item/clothing/neck/heretic_focus/crimson_focus = 0,
+ /mob/living/basic/construct/harvester/heretic = 0,
+ )
+ /// Simpler version of above used to limit amount of loot that can be hoarded
+ var/rewards_given = 0
+
/datum/antagonist/heretic/Destroy()
LAZYNULL(sac_targets)
return ..()
+/datum/antagonist/heretic/proc/get_icon_of_knowledge(datum/heretic_knowledge/knowledge)
+ //basic icon parameters
+ var/icon_path = 'icons/mob/actions/actions_ecult.dmi'
+ var/icon_state = "eye"
+ var/icon_frame = knowledge.research_tree_icon_frame
+ var/icon_dir = knowledge.research_tree_icon_dir
+ //can't imagine why you would want this one, so it can't be overridden by the knowledge
+ var/icon_moving = 0
+
+ //item transmutation knowledge does not generate its own icon due to implementation difficulties, the icons have to be specified in the override vars
+
+ //if the knowledge has a special icon, use that
+ if(!isnull(knowledge.research_tree_icon_path))
+ icon_path = knowledge.research_tree_icon_path
+ icon_state = knowledge.research_tree_icon_state
+
+ //if the knowledge is a spell, use the spell's button
+ else if(ispath(knowledge,/datum/heretic_knowledge/spell))
+ var/datum/heretic_knowledge/spell/spell_knowledge = knowledge
+ var/datum/action/cooldown/spell/result_spell = spell_knowledge.spell_to_add
+ icon_path = result_spell.button_icon
+ icon_state = result_spell.button_icon_state
+
+ //if the knowledge is a summon, use the mob sprite
+ else if(ispath(knowledge,/datum/heretic_knowledge/summon))
+ var/datum/heretic_knowledge/summon/summon_knowledge = knowledge
+ var/mob/living/result_mob = summon_knowledge.mob_to_summon
+ icon_path = result_mob.icon
+ icon_state = result_mob.icon_state
+
+ //if the knowledge is an eldritch mark, use the mark sprite
+ else if(ispath(knowledge,/datum/heretic_knowledge/mark))
+ var/datum/heretic_knowledge/mark/mark_knowledge = knowledge
+ var/datum/status_effect/eldritch/mark_effect = mark_knowledge.mark_type
+ icon_path = mark_effect.effect_icon
+ icon_state = mark_effect.effect_icon_state
+
+ //if the knowledge is an ascension, use the achievement sprite
+ else if(ispath(knowledge,/datum/heretic_knowledge/ultimate))
+ var/datum/heretic_knowledge/ultimate/ascension_knowledge = knowledge
+ var/datum/award/achievement/misc/achievement = ascension_knowledge.ascension_achievement
+ if(!isnull(achievement))
+ icon_path = achievement.icon
+ icon_state = achievement.icon_state
+
+ var/list/result_parameters = list()
+ result_parameters["icon"] = icon_path
+ result_parameters["state"] = icon_state
+ result_parameters["frame"] = icon_frame
+ result_parameters["dir"] = icon_dir
+ result_parameters["moving"] = icon_moving
+ return result_parameters
+
+/datum/antagonist/heretic/proc/get_knowledge_data(datum/heretic_knowledge/knowledge, done)
+
+ var/list/knowledge_data = list()
+
+ knowledge_data["path"] = knowledge
+ knowledge_data["icon_params"] = get_icon_of_knowledge(knowledge)
+ knowledge_data["name"] = initial(knowledge.name)
+ knowledge_data["gainFlavor"] = initial(knowledge.gain_text)
+ knowledge_data["cost"] = initial(knowledge.cost)
+ knowledge_data["disabled"] = (!done) && (initial(knowledge.cost) > knowledge_points)
+ knowledge_data["bgr"] = (path_to_ui_bgr[initial(knowledge.route)] || "side")
+ knowledge_data["finished"] = done
+ knowledge_data["ascension"] = ispath(knowledge,/datum/heretic_knowledge/ultimate)
+
+ //description of a knowledge might change, make sure we are not shown the initial() value in that case
+ if(done)
+ var/datum/heretic_knowledge/knowledge_instance = researched_knowledge[knowledge]
+ knowledge_data["desc"] = knowledge_instance.desc
+ else
+ knowledge_data["desc"] = initial(knowledge.desc)
+
+ return knowledge_data
+
/datum/antagonist/heretic/ui_data(mob/user)
var/list/data = list()
@@ -96,26 +181,32 @@
data["total_sacrifices"] = total_sacrifices
data["ascended"] = ascended
+ var/list/tiers = list()
+
// This should be cached in some way, but the fact that final knowledge
// has to update its disabled state based on whether all objectives are complete,
// makes this very difficult. I'll figure it out one day maybe
+ for(var/datum/heretic_knowledge/knowledge as anything in researched_knowledge)
+ var/list/knowledge_data = get_knowledge_data(knowledge,TRUE)
+
+ while(initial(knowledge.depth) > tiers.len)
+ tiers += list(list("nodes"=list()))
+
+ tiers[initial(knowledge.depth)]["nodes"] += list(knowledge_data)
+
for(var/datum/heretic_knowledge/knowledge as anything in get_researchable_knowledge())
- var/list/knowledge_data = list()
- knowledge_data["path"] = knowledge
- knowledge_data["name"] = initial(knowledge.name)
- knowledge_data["desc"] = initial(knowledge.desc)
- knowledge_data["gainFlavor"] = initial(knowledge.gain_text)
- knowledge_data["cost"] = initial(knowledge.cost)
- knowledge_data["disabled"] = initial(knowledge.cost) > knowledge_points
+ var/list/knowledge_data = get_knowledge_data(knowledge,FALSE)
// Final knowledge can't be learned until all objectives are complete.
if(ispath(knowledge, /datum/heretic_knowledge/ultimate))
- knowledge_data["disabled"] = !can_ascend()
+ knowledge_data["disabled"] ||= !can_ascend()
- knowledge_data["hereticPath"] = initial(knowledge.route)
- knowledge_data["color"] = path_to_ui_color[initial(knowledge.route)] || "grey"
+ while(initial(knowledge.depth) > tiers.len)
+ tiers += list(list("nodes"=list()))
- data["learnableKnowledge"] += list(knowledge_data)
+ tiers[initial(knowledge.depth)]["nodes"] += list(knowledge_data)
+
+ data["knowledge_tiers"] = tiers
return data
@@ -125,18 +216,6 @@
data["objectives"] = get_objectives()
data["can_change_objective"] = can_assign_self_objectives
- for(var/path in researched_knowledge)
- var/list/knowledge_data = list()
- var/datum/heretic_knowledge/found_knowledge = researched_knowledge[path]
- knowledge_data["name"] = found_knowledge.name
- knowledge_data["desc"] = found_knowledge.desc
- knowledge_data["gainFlavor"] = found_knowledge.gain_text
- knowledge_data["cost"] = found_knowledge.cost
- knowledge_data["hereticPath"] = found_knowledge.route
- knowledge_data["color"] = path_to_ui_color[found_knowledge.route] || "grey"
-
- data["learnedKnowledge"] += list(knowledge_data)
-
return data
/datum/antagonist/heretic/ui_act(action, params)
@@ -230,6 +309,8 @@
if (!issilicon(our_mob))
GLOB.reality_smash_track.add_tracked_mind(owner)
+ ADD_TRAIT(our_mob, TRAIT_MANSUS_TOUCHED, REF(src))
+ RegisterSignal(our_mob, COMSIG_LIVING_CULT_SACRIFICED, PROC_REF(on_cult_sacrificed))
RegisterSignals(our_mob, list(COMSIG_MOB_BEFORE_SPELL_CAST, COMSIG_MOB_SPELL_ACTIVATED), PROC_REF(on_spell_cast))
RegisterSignal(our_mob, COMSIG_USER_ITEM_INTERACTION, PROC_REF(on_item_use))
RegisterSignal(our_mob, COMSIG_MOB_LOGIN, PROC_REF(fix_influence_network))
@@ -243,12 +324,14 @@
if (owner in GLOB.reality_smash_track.tracked_heretics)
GLOB.reality_smash_track.remove_tracked_mind(owner)
+ REMOVE_TRAIT(our_mob, TRAIT_MANSUS_TOUCHED, REF(src))
UnregisterSignal(our_mob, list(
COMSIG_MOB_BEFORE_SPELL_CAST,
COMSIG_MOB_SPELL_ACTIVATED,
COMSIG_USER_ITEM_INTERACTION,
COMSIG_MOB_LOGIN,
COMSIG_LIVING_POST_FULLY_HEAL,
+ COMSIG_LIVING_CULT_SACRIFICED,
))
/datum/antagonist/heretic/on_body_transfer(mob/living/old_body, mob/living/new_body)
@@ -395,6 +478,118 @@
var/datum/heretic_knowledge/living_heart/heart_knowledge = get_knowledge(/datum/heretic_knowledge/living_heart)
heart_knowledge.on_research(source, src)
+/// Signal proc for [COMSIG_LIVING_CULT_SACRIFICED] to reward cultists for sacrificing a heretic
+/datum/antagonist/heretic/proc/on_cult_sacrificed(mob/living/source, list/invokers)
+ SIGNAL_HANDLER
+
+ for(var/mob/dead/observer/ghost in GLOB.dead_mob_list) // uhh let's find the guy to shove him back in
+ if((ghost.mind?.current == source) && ghost.client) // is it the same guy and do they have the same client
+ ghost.reenter_corpse() // shove them in! it doesnt do it automatically
+
+ // Drop all items and splatter them around messily.
+ var/list/dustee_items = source.unequip_everything()
+ for(var/obj/item/loot as anything in dustee_items)
+ loot.throw_at(get_step_rand(source), 2, 4, pick(invokers), TRUE)
+
+ // Create the blade, give it the heretic and a randomly-chosen master for the soul sword component
+ var/obj/item/melee/cultblade/haunted/haunted_blade = new(get_turf(source), source, pick(invokers))
+
+ // Cool effect for the rune as well as the item
+ var/obj/effect/rune/convert/conversion_rune = locate() in get_turf(source)
+ if(conversion_rune)
+ conversion_rune.gender_reveal(
+ outline_color = COLOR_HERETIC_GREEN,
+ ray_color = null,
+ do_float = FALSE,
+ do_layer = FALSE,
+ )
+
+ haunted_blade.gender_reveal(outline_color = null, ray_color = COLOR_HERETIC_GREEN)
+
+ for(var/mob/living/culto as anything in invokers)
+ to_chat(culto, span_cult_large("\"A follower of the forgotten gods! You must be rewarded for such a valuable sacrifice.\""))
+
+ // Locate a cultist team (Is there a better way??)
+ var/mob/living/random_cultist = pick(invokers)
+ var/datum/antagonist/cult/antag = random_cultist.mind.has_antag_datum(/datum/antagonist/cult)
+ ASSERT(antag)
+ var/datum/team/cult/cult_team = antag.get_team()
+
+ // Unlock one of 3 special items!
+ var/list/possible_unlocks
+ for(var/i in cult_team.unlocked_heretic_items)
+ if(cult_team.unlocked_heretic_items[i])
+ continue
+ LAZYADD(possible_unlocks, i)
+ if(length(possible_unlocks))
+ var/result = pick(possible_unlocks)
+ cult_team.unlocked_heretic_items[result] = TRUE
+
+ for(var/datum/mind/mind as anything in cult_team.members)
+ if(mind.current)
+ SEND_SOUND(mind.current, 'sound/magic/clockwork/narsie_attack.ogg')
+ to_chat(mind.current, span_cult_large(span_warning("Arcane and forbidden knowledge floods your forges and archives. The cult has learned how to create the ")) + span_cult_large(span_hypnophrase("[result]!")))
+
+ return SILENCE_SACRIFICE_MESSAGE|DUST_SACRIFICE
+
+/**
+ * Creates an animation of the item slowly lifting up from the floor with a colored outline, then slowly drifting back down.
+ * Arguments:
+ * * outline_color: Default is between pink and light blue, is the color of the outline filter.
+ * * ray_color: Null by default. If not set, just copies outline. Used for the ray filter.
+ * * anim_time: Total time of the animation. Split into two different calls.
+ * * do_float: Lets you disable the sprite floating up and down.
+ * * do_layer: Lets you disable the layering increase.
+ */
+/obj/proc/gender_reveal(
+ outline_color = null,
+ ray_color = null,
+ anim_time = 10 SECONDS,
+ do_float = TRUE,
+ do_layer = TRUE,
+)
+
+ var/og_layer
+ if(do_layer)
+ // Layering above to stand out!
+ og_layer = layer
+ layer = ABOVE_MOB_LAYER
+
+ // Slowly floats up, then slowly goes down.
+ if(do_float)
+ animate(src, pixel_y = 12, time = anim_time * 0.5, easing = QUAD_EASING | EASE_OUT)
+ animate(pixel_y = 0, time = anim_time * 0.5, easing = QUAD_EASING | EASE_IN)
+
+ // Adding a cool outline effect
+ if(outline_color)
+ add_filter("gender_reveal_outline", 3, list("type" = "outline", "color" = outline_color, "size" = 0.5))
+ // Animating it!
+ var/gay_filter = get_filter("gender_reveal_outline")
+ animate(gay_filter, alpha = 110, time = 1.5 SECONDS, loop = -1)
+ animate(alpha = 40, time = 2.5 SECONDS)
+
+ // Adding a cool ray effect
+ if(ray_color)
+ add_filter(name = "gender_reveal_ray", priority = 1, params = list(
+ type = "rays",
+ size = 45,
+ color = ray_color,
+ density = 6
+ ))
+ // Animating it!
+ var/ray_filter = get_filter("gender_reveal_ray")
+ // I understand nothing but copypaste saves lives
+ animate(ray_filter, offset = 100, time = 30 SECONDS, loop = -1, flags = ANIMATION_PARALLEL)
+
+ addtimer(CALLBACK(src, PROC_REF(remove_gender_reveal_fx), og_layer), anim_time)
+
+/**
+ * Removes the non-animate effects from above proc
+ */
+/obj/proc/remove_gender_reveal_fx(og_layer)
+ remove_filter(list("gender_reveal_outline", "gender_reveal_ray"))
+ layer = og_layer
+
/**
* Create our objectives for our heretic.
*/
diff --git a/code/modules/antagonists/heretic/heretic_knowledge.dm b/code/modules/antagonists/heretic/heretic_knowledge.dm
index 5369e5fee8d..6cfa8db0560 100644
--- a/code/modules/antagonists/heretic/heretic_knowledge.dm
+++ b/code/modules/antagonists/heretic/heretic_knowledge.dm
@@ -38,6 +38,16 @@
var/priority = 0
/// What path is this on. If set to "null", assumed to be unreachable (or abstract).
var/route
+ /// In case we want to override the default UI icon getter and plug in our own icon instead.
+ /// if research_tree_icon_path is not null, research_tree_icon_state must also be specified or things may break
+ var/research_tree_icon_path
+ var/research_tree_icon_state
+ var/research_tree_icon_frame = 1
+ var/research_tree_icon_dir = SOUTH
+ /// Level of knowledge tree where this knowledge should be in the UI
+ var/depth = 1
+ ///Determines what kind of monster ghosts will ignore from here on out. Defaults to POLL_IGNORE_HERETIC_MONSTER, but we define other types of monsters for more granularity.
+ var/poll_ignore_define = POLL_IGNORE_HERETIC_MONSTER
/datum/heretic_knowledge/New()
if(!mutually_exclusive)
@@ -261,6 +271,7 @@
limit = 2
cost = 1
priority = MAX_KNOWLEDGE_PRIORITY - 5
+ depth = 2
/datum/heretic_knowledge/limited_amount/starting/New()
. = ..()
@@ -285,6 +296,7 @@
abstract_parent_type = /datum/heretic_knowledge/mark
mutually_exclusive = TRUE
cost = 2
+ depth = 5
/// The status effect typepath we apply on people on mansus grasp.
var/datum/status_effect/eldritch/mark_type
@@ -350,6 +362,7 @@
abstract_parent_type = /datum/heretic_knowledge/blade_upgrade
mutually_exclusive = TRUE
cost = 2
+ depth = 9
/datum/heretic_knowledge/blade_upgrade/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
RegisterSignal(user, COMSIG_HERETIC_BLADE_ATTACK, PROC_REF(on_eldritch_blade))
@@ -523,11 +536,23 @@
abstract_parent_type = /datum/heretic_knowledge/summon
/// Typepath of a mob to summon when we finish the recipe.
var/mob/living/mob_to_summon
- ///Determines what kind of monster ghosts will ignore from here on out. Defaults to POLL_IGNORE_HERETIC_MONSTER, but we define other types of monsters for more granularity.
- var/poll_ignore_define = POLL_IGNORE_HERETIC_MONSTER
/datum/heretic_knowledge/summon/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
- var/mob/living/summoned = new mob_to_summon(loc)
+ summon_ritual_mob(user, loc, mob_to_summon)
+
+/**
+ * Creates the ritual mob and grabs a ghost for it
+ *
+ * * user - the mob doing the summoning
+ * * loc - where the summon is happening
+ * * mob_to_summon - either a mob instance or a mob typepath
+ */
+/datum/heretic_knowledge/proc/summon_ritual_mob(mob/living/user, turf/loc, mob/living/mob_to_summon)
+ var/mob/living/summoned
+ if(isliving(mob_to_summon))
+ summoned = mob_to_summon
+ else
+ summoned = new mob_to_summon(loc)
summoned.ai_controller?.set_ai_status(AI_STATUS_OFF)
// Fade in the summon while the ghost poll is ongoing.
// Also don't let them mess with the summon while waiting
@@ -557,6 +582,7 @@
var/datum/antagonist/heretic_monster/heretic_monster = summoned.mind.add_antag_datum(/datum/antagonist/heretic_monster)
heretic_monster.set_owner(user.mind)
+ summoned.RegisterSignal(user, COMSIG_LIVING_DEATH, TYPE_PROC_REF(/mob/living/, on_master_death))
var/datum/objective/heretic_summon/summon_objective = locate() in user.mind.get_all_objectives()
summon_objective?.num_summoned++
@@ -577,6 +603,9 @@
mutually_exclusive = TRUE
cost = 1
priority = MAX_KNOWLEDGE_PRIORITY - 10 // A pretty important midgame ritual.
+ depth = 6
+ research_tree_icon_path = 'icons/obj/antags/eldritch.dmi'
+ research_tree_icon_state = "book_open"
/// Whether we've done the ritual. Only doable once.
var/was_completed = FALSE
@@ -670,6 +699,9 @@
cost = 2
priority = MAX_KNOWLEDGE_PRIORITY + 1 // Yes, the final ritual should be ABOVE the max priority.
required_atoms = list(/mob/living/carbon/human = 3)
+ depth = 11
+ //use this to store the achievement typepath
+ var/datum/award/achievement/misc/ascension_achievement
/datum/heretic_knowledge/ultimate/on_research(mob/user, datum/antagonist/heretic/our_heretic)
. = ..()
@@ -730,6 +762,8 @@
source = user,
header = "A Heretic is Ascending!",
)
+ if(!isnull(ascension_achievement))
+ user.client?.give_award(ascension_achievement, user)
heretic_datum.increase_rust_strength()
return TRUE
diff --git a/code/modules/antagonists/heretic/heretic_monsters.dm b/code/modules/antagonists/heretic/heretic_monsters.dm
index 31b18b6a833..fcdea512879 100644
--- a/code/modules/antagonists/heretic/heretic_monsters.dm
+++ b/code/modules/antagonists/heretic/heretic_monsters.dm
@@ -38,3 +38,7 @@
owner.announce_objectives()
to_chat(owner, span_boldnotice("You are a [ishuman(owner.current) ? "shambling corpse returned":"horrible creation brought"] to this plane through the Gates of the Mansus."))
to_chat(owner, span_notice("Your master is [master]. Assist them to all ends."))
+
+ if(istype(owner.current, /mob/living/basic/construct/harvester/heretic))
+ var/mob/living/basic/construct/harvester/heretic/shitcode = owner.current
+ shitcode.master = master
diff --git a/code/modules/antagonists/heretic/items/eldritch_flask.dm b/code/modules/antagonists/heretic/items/eldritch_flask.dm
index 95b77f95618..409bcd473ed 100644
--- a/code/modules/antagonists/heretic/items/eldritch_flask.dm
+++ b/code/modules/antagonists/heretic/items/eldritch_flask.dm
@@ -4,5 +4,5 @@
name = "flask of eldritch essence"
desc = "Toxic to the closed minded, yet refreshing to those with knowledge of the beyond."
icon = 'icons/obj/antags/eldritch.dmi'
- icon_state = "eldrich_flask"
+ icon_state = "eldritch_flask"
list_reagents = list(/datum/reagent/eldritch = 50)
diff --git a/code/modules/antagonists/heretic/items/heretic_blades.dm b/code/modules/antagonists/heretic/items/heretic_blades.dm
index 675f5f87b0a..ddfec8db20c 100644
--- a/code/modules/antagonists/heretic/items/heretic_blades.dm
+++ b/code/modules/antagonists/heretic/items/heretic_blades.dm
@@ -24,23 +24,37 @@
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "rend")
var/after_use_message = ""
+/obj/item/melee/sickly_blade/examine(mob/user)
+ . = ..()
+ if(!check_usability(user))
+ return
+
+ . += span_notice("You can shatter the blade to teleport to a random, (mostly) safe location by activating it in-hand.")
+
+/// Checks if the passed mob can use this blade without being stunned
+/obj/item/melee/sickly_blade/proc/check_usability(mob/living/user)
+ return IS_HERETIC_OR_MONSTER(user)
+
/obj/item/melee/sickly_blade/pre_attack(atom/A, mob/living/user, params)
. = ..()
if(.)
return .
- if(!IS_HERETIC_OR_MONSTER(user))
+ if(!check_usability(user))
to_chat(user, span_danger("You feel a pulse of alien intellect lash out at your mind!"))
- user.AdjustParalyzed(5 SECONDS)
+ var/mob/living/carbon/human/human_user = user
+ human_user.AdjustParalyzed(5 SECONDS)
return TRUE
+
return .
-/obj/item/melee/sickly_blade/afterattack(atom/target, mob/user, click_parameters)
- if(isliving(target))
- SEND_SIGNAL(user, COMSIG_HERETIC_BLADE_ATTACK, target, src)
-
/obj/item/melee/sickly_blade/attack_self(mob/user)
- var/turf/safe_turf = find_safe_turf(zlevel = z, extended_safety_checks = TRUE)
- if(IS_HERETIC_OR_MONSTER(user))
+ seek_safety(user)
+ return ..()
+
+/// Attempts to teleport the passed mob to somewhere safe on the station, if they can use the blade.
+/obj/item/melee/sickly_blade/proc/seek_safety(mob/user)
+ var/turf/safe_turf = find_safe_turf(zlevels = z, extended_safety_checks = TRUE)
+ if(check_usability(user))
if(do_teleport(user, safe_turf, channel = TELEPORT_CHANNEL_MAGIC))
to_chat(user, span_warning("As you shatter [src], you feel a gust of energy flow through your body. [after_use_message]"))
else
@@ -50,18 +64,15 @@
playsound(src, SFX_SHATTER, 70, TRUE) //copied from the code for smashing a glass sheet onto the ground to turn it into a shard
qdel(src)
+/obj/item/melee/sickly_blade/afterattack(atom/target, mob/user, click_parameters)
+ if(isliving(target))
+ SEND_SIGNAL(user, COMSIG_HERETIC_BLADE_ATTACK, target, src)
+
/obj/item/melee/sickly_blade/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(isliving(interacting_with))
SEND_SIGNAL(user, COMSIG_HERETIC_RANGED_BLADE_ATTACK, interacting_with, src)
return ITEM_INTERACT_BLOCKING
-/obj/item/melee/sickly_blade/examine(mob/user)
- . = ..()
- if(!IS_HERETIC_OR_MONSTER(user))
- return
-
- . += span_notice("You can shatter the blade to teleport to a random, (mostly) safe location by activating it in-hand.")
-
// Path of Rust's blade
/obj/item/melee/sickly_blade/rust
name = "\improper rusted blade"
@@ -154,3 +165,76 @@
icon_state = "moon_blade"
inhand_icon_state = "moon_blade"
after_use_message = "The Moon hears your call..."
+
+// Path of Nar'Sie's blade
+// What!? This blade is given to cultists as an altar item when they sacrifice a heretic.
+// It is also given to the heretic themself if they sacrifice a cultist.
+/obj/item/melee/sickly_blade/cursed
+ name = "\improper cursed blade"
+ desc = "A dark blade, cursed to bleed forever. In constant struggle between the eldritch and the dark, it is forced to accept any wielder as its master. \
+ Its eye's cornea drips blood endlessly into the ground, yet its piercing gaze remains on you."
+ force = 25
+ throwforce = 15
+ block_chance = 35
+ wound_bonus = 25
+ bare_wound_bonus = 15
+ armour_penetration = 35
+ icon_state = "cursed_blade"
+ inhand_icon_state = "cursed_blade"
+
+/obj/item/melee/sickly_blade/cursed/Initialize(mapload)
+ . = ..()
+
+ var/examine_text = {"Allows the scribing of blood runes of the cult of Nar'Sie.
+ The combination of eldritch power and Nar'Sie's might allows for vastly increased rune drawing speed,
+ alongside the vicious strength of the blade being more powerful than usual.\n
+ It can also be shattered in-hand by cultists (via right-click), teleporting them to relative safety."}
+
+ AddComponent(/datum/component/cult_ritual_item, span_cult(examine_text), turfs_that_boost_us = /turf) // Always fast to draw!
+
+/obj/item/melee/sickly_blade/cursed/attack_self_secondary(mob/user)
+ seek_safety(user, TRUE)
+
+/obj/item/melee/sickly_blade/cursed/seek_safety(mob/user, secondary_attack = FALSE)
+ if(IS_CULTIST(user) && !secondary_attack)
+ return FALSE
+ return ..()
+
+/obj/item/melee/sickly_blade/cursed/check_usability(mob/living/user)
+ if(IS_HERETIC_OR_MONSTER(user) || IS_CULTIST(user))
+ return TRUE
+ if(prob(15))
+ to_chat(user, span_cult_large(pick("\"An untouched mind? Amusing.\"", "\" I suppose it isn't worth the effort to stop you.\"", "\"Go ahead. I don't care.\"", "\"You'll be mine soon enough.\"")))
+ var/obj/item/bodypart/affecting = user.get_active_hand()
+ if(!affecting)
+ return
+ affecting.receive_damage(burn = 5)
+ playsound(src, SFX_SEAR, 25, TRUE)
+ to_chat(user, span_danger("Your hand sizzles.")) // Nar nar might not care but their essence still doesn't like you
+ else if(prob(15))
+ to_chat(user, span_big(span_hypnophrase("LW'NAFH'NAHOR UH'ENAH'YMG EPGOKA AH NAFL MGEMPGAH'EHYE")))
+ to_chat(user, span_danger("Horrible, unintelligible utterances flood your mind!"))
+ user.adjustOrganLoss(ORGAN_SLOT_BRAIN, 15) // This can kill you if you ignore it
+ return TRUE
+
+/obj/item/melee/sickly_blade/cursed/equipped(mob/user, slot)
+ . = ..()
+ if(IS_HERETIC_OR_MONSTER(user))
+ after_use_message = "The Mansus hears your call..."
+ else if(IS_CULTIST(user))
+ after_use_message = "Nar'Sie hears your call..."
+ else
+ after_use_message = null
+
+/obj/item/melee/sickly_blade/cursed/interact_with_atom(atom/target, mob/living/user, list/modifiers)
+ . = ..()
+
+ var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ if(!heretic_datum)
+ return NONE
+
+ // Can only carve runes with it if off combat mode.
+ if(isopenturf(target) && !user.combat_mode)
+ heretic_datum.try_draw_rune(user, target, drawing_time = 14 SECONDS) // Faster than pen, slower than cicatrix
+ return ITEM_INTERACT_BLOCKING
+ return NONE
diff --git a/code/modules/antagonists/heretic/items/heretic_necks.dm b/code/modules/antagonists/heretic/items/heretic_necks.dm
index e24c17abdee..3f140dc99df 100644
--- a/code/modules/antagonists/heretic/items/heretic_necks.dm
+++ b/code/modules/antagonists/heretic/items/heretic_necks.dm
@@ -9,6 +9,101 @@
. = ..()
AddElement(/datum/element/heretic_focus)
+/obj/item/clothing/neck/heretic_focus/crimson_focus
+ name = "Crimson Focus"
+ desc = "A blood-red focusing glass that provides a link to the world beyond, and worse. Its eye is constantly twitching and gazing in all directions. It almost seems to be silently screaming..."
+ icon_state = "crimson_focus"
+ /// The aura healing component. Used to delete it when taken off.
+ var/datum/component/component
+ /// If active or not, used to add and remove its cult and heretic buffs.
+ var/active = FALSE
+
+/obj/item/clothing/neck/heretic_focus/crimson_focus/equipped(mob/living/user, slot)
+ . = ..()
+ if(!(slot & ITEM_SLOT_NECK))
+ return
+
+ var/team_color = COLOR_ADMIN_PINK
+ if(IS_CULTIST(user))
+ var/datum/action/innate/cult/blood_magic/magic_holder = locate() in user.actions
+ team_color = COLOR_CULT_RED
+ magic_holder.magic_enhanced = TRUE
+ else if(IS_HERETIC_OR_MONSTER(user) && !active)
+ for(var/datum/action/cooldown/spell/spell_action in user.actions)
+ spell_action.cooldown_time *= 0.5
+ active = TRUE
+ team_color = COLOR_GREEN
+ else
+ team_color = pick(COLOR_CULT_RED, COLOR_GREEN)
+
+ user.add_traits(list(TRAIT_MANSUS_TOUCHED, TRAIT_BLOODY_MESS), REF(src))
+ to_chat(user, span_alert("Your heart takes on a strange yet soothing irregular rhythm, and your blood feels significantly less viscous than it used to be. You're not sure if that's a good thing."))
+ component = user.AddComponent( \
+ /datum/component/aura_healing, \
+ range = 3, \
+ brute_heal = 1, \
+ burn_heal = 1, \
+ blood_heal = 2, \
+ suffocation_heal = 5, \
+ simple_heal = 0.6, \
+ requires_visibility = FALSE, \
+ limit_to_trait = TRAIT_MANSUS_TOUCHED, \
+ healing_color = team_color, \
+ )
+
+/obj/item/clothing/neck/heretic_focus/crimson_focus/dropped(mob/living/user)
+ . = ..()
+
+ if(!istype(user))
+ return
+
+ if(HAS_TRAIT_FROM(user, TRAIT_MANSUS_TOUCHED, REF(src)))
+ to_chat(user, span_notice("Your heart and blood return to their regular old rhythm and flow."))
+
+ if(IS_HERETIC_OR_MONSTER(user) && active)
+ for(var/datum/action/cooldown/spell/spell_action in user.actions)
+ spell_action.cooldown_time *= 2
+ active = FALSE
+ QDEL_NULL(component)
+ user.remove_traits(list(TRAIT_MANSUS_TOUCHED, TRAIT_BLOODY_MESS), REF(src))
+
+ // If boosted enable is set, to prevent false dropped() calls from repeatedly nuking the max spells.
+ var/datum/action/innate/cult/blood_magic/magic_holder = locate() in user.actions
+ // Remove the last spell if over new limit, as we will reduce our max spell amount. Done beforehand as it causes a index out of bounds runtime otherwise.
+ if(magic_holder?.magic_enhanced)
+ QDEL_NULL(magic_holder.spells[ENHANCED_BLOODCHARGE])
+ magic_holder?.magic_enhanced = FALSE
+
+
+/obj/item/clothing/neck/heretic_focus/crimson_focus/attack_self(mob/living/user, modifiers)
+ . = ..()
+ to_chat(user, span_danger("You start tightly squeezing [src]..."))
+ if(!do_after(user, 1.25 SECONDS, src))
+ return
+ to_chat(user, span_danger("[src] explodes into a shower of gore and blood, drenching your arm. You can feel the blood seeping into your skin. You inmediately feel better, but soon, the feeling turns hollow as your veins itch."))
+ new /obj/effect/gibspawner/generic(get_turf(src))
+ var/heal_amt = user.adjustBruteLoss(-50)
+ user.adjustFireLoss( -(50 - abs(heal_amt)) ) // no double dipping
+
+ // I want it to poison the user but I also think it'd be neat if they got their juice as well. But that cancels most of the damage out. So I dunno.
+ user.reagents?.add_reagent(/datum/reagent/fuel/unholywater, rand(6, 10))
+ user.reagents?.add_reagent(/datum/reagent/eldritch, rand(6, 10))
+ qdel(src)
+
+/obj/item/clothing/neck/heretic_focus/crimson_focus/examine(mob/user)
+ . = ..()
+
+ var/magic_dude
+ if(IS_CULTIST(user))
+ . += span_cult_bold("This focus will allow you to store one extra spell and halve the empowering time, alongside providing a small regenerative effect.")
+ magic_dude = TRUE
+ if(IS_HERETIC_OR_MONSTER(user))
+ . += span_notice("This focus will halve your spell cooldowns, alongside granting a small regenerative effect to any nearby heretics or monsters, including you.")
+ magic_dude = TRUE
+
+ if(magic_dude)
+ . += span_red("You can also squeeze it to recover a large amount of health quickly, at a cost...")
+
/obj/item/clothing/neck/eldritch_amulet
name = "Warm Eldritch Medallion"
desc = "A strange medallion. Peering through the crystalline surface, the world around you melts away. You see your own beating heart, and the pulsing of a thousand others."
diff --git a/code/modules/antagonists/heretic/knowledge/ash_lore.dm b/code/modules/antagonists/heretic/knowledge/ash_lore.dm
index b3cefffbe51..496b90b3f12 100644
--- a/code/modules/antagonists/heretic/knowledge/ash_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/ash_lore.dm
@@ -41,6 +41,8 @@
)
result_atoms = list(/obj/item/melee/sickly_blade/ash)
route = PATH_ASH
+ research_tree_icon_path = 'icons/obj/weapons/khopesh.dmi'
+ research_tree_icon_state = "ash_blade"
/datum/heretic_knowledge/ashen_grasp
name = "Grasp of Ash"
@@ -50,6 +52,9 @@
next_knowledge = list(/datum/heretic_knowledge/spell/ash_passage)
cost = 1
route = PATH_ASH
+ depth = 3
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "grasp_ash"
/datum/heretic_knowledge/ashen_grasp/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
RegisterSignal(user, COMSIG_HERETIC_MANSUS_GRASP_ATTACK, PROC_REF(on_mansus_grasp))
@@ -82,6 +87,7 @@
spell_to_add = /datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash
cost = 1
route = PATH_ASH
+ depth = 4
/datum/heretic_knowledge/mark/ash_mark
name = "Mark of Ash"
@@ -121,6 +127,8 @@
spell_to_add = /datum/action/cooldown/spell/charged/beam/fire_blast
cost = 1
route = PATH_ASH
+ depth = 7
+ research_tree_icon_frame = 7
/datum/heretic_knowledge/mad_mask
@@ -144,6 +152,9 @@
result_atoms = list(/obj/item/clothing/mask/madness_mask)
cost = 1
route = PATH_ASH
+ research_tree_icon_path = 'icons/obj/clothing/masks.dmi'
+ research_tree_icon_state = "mad_mask"
+ depth = 8
/datum/heretic_knowledge/blade_upgrade/ash
name = "Fiery Blade"
@@ -152,6 +163,8 @@
His city, the people he swore to watch... and watch he did, as they all burnt to cinders."
next_knowledge = list(/datum/heretic_knowledge/spell/flame_birth)
route = PATH_ASH
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "blade_upgrade_ash"
/datum/heretic_knowledge/blade_upgrade/ash/do_melee_effects(mob/living/source, mob/living/target, obj/item/melee/sickly_blade/blade)
if(source == target)
@@ -175,6 +188,8 @@
spell_to_add = /datum/action/cooldown/spell/aoe/fiery_rebirth
cost = 1
route = PATH_ASH
+ depth = 10
+ research_tree_icon_frame = 5
/datum/heretic_knowledge/ultimate/ash_final
name = "Ashlord's Rite"
@@ -189,6 +204,7 @@
for the Nightwatcher brought forth the rite to mankind! His gaze continues, as now I am one with the flames, \
WITNESS MY ASCENSION, THE ASHY LANTERN BLAZES ONCE MORE!"
route = PATH_ASH
+ ascension_achievement = /datum/award/achievement/misc/ash_ascension
/// A static list of all traits we apply on ascension.
var/static/list/traits_to_apply = list(
TRAIT_BOMBIMMUNE,
@@ -235,6 +251,5 @@
var/datum/action/cooldown/spell/aoe/fiery_rebirth/fiery_rebirth = locate() in user.actions
fiery_rebirth?.cooldown_time *= 0.16
- user.client?.give_award(/datum/award/achievement/misc/ash_ascension, user)
if(length(traits_to_apply))
user.add_traits(traits_to_apply, MAGIC_TRAIT)
diff --git a/code/modules/antagonists/heretic/knowledge/blade_lore.dm b/code/modules/antagonists/heretic/knowledge/blade_lore.dm
index c6eb3259874..357e789416d 100644
--- a/code/modules/antagonists/heretic/knowledge/blade_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/blade_lore.dm
@@ -45,6 +45,8 @@
result_atoms = list(/obj/item/melee/sickly_blade/dark)
limit = 5 // It's the blade path, it's a given
route = PATH_BLADE
+ research_tree_icon_path = 'icons/obj/weapons/khopesh.dmi'
+ research_tree_icon_state = "dark_blade"
/datum/heretic_knowledge/blade_grasp
name = "Grasp of the Blade"
@@ -54,6 +56,9 @@
next_knowledge = list(/datum/heretic_knowledge/blade_dance)
cost = 1
route = PATH_BLADE
+ depth = 3
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "grasp_blade"
/datum/heretic_knowledge/blade_grasp/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
RegisterSignal(user, COMSIG_HERETIC_MANSUS_GRASP_ATTACK, PROC_REF(on_mansus_grasp))
@@ -113,6 +118,9 @@
)
cost = 1
route = PATH_BLADE
+ depth = 4
+ research_tree_icon_path = 'icons/mob/actions/actions_ecult.dmi'
+ research_tree_icon_state = "shatter"
/// Whether the counter-attack is ready or not.
/// Used instead of cooldowns, so we can give feedback when it's ready again
var/riposte_ready = TRUE
@@ -233,6 +241,7 @@
spell_to_add = /datum/action/cooldown/spell/realignment
cost = 1
route = PATH_BLADE
+ depth = 7
/// The amount of blood flow reduced per level of severity of gained bleeding wounds for Stance of the Torn Champion.
#define BLOOD_FLOW_PER_SEVEIRTY -1
@@ -253,6 +262,10 @@
)
cost = 1
route = PATH_BLADE
+ depth = 8
+ research_tree_icon_path = 'icons/effects/blood.dmi'
+ research_tree_icon_state = "suitblood"
+ research_tree_icon_dir = SOUTH
/// Whether we're currently in duelist stance, gaining certain buffs (low health)
var/in_duelist_stance = FALSE
@@ -312,6 +325,8 @@
a flurry of blades, neither hitting their mark, for the Champion was indomitable."
next_knowledge = list(/datum/heretic_knowledge/spell/furious_steel)
route = PATH_BLADE
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "blade_upgrade_blade"
/// How much force do we apply to the offhand?
var/offand_force_decrement = 0
/// How much force was the last weapon we offhanded with? If it's different, we need to re-calculate the decrement
@@ -382,6 +397,7 @@
spell_to_add = /datum/action/cooldown/spell/pointed/projectile/furious_steel
cost = 1
route = PATH_BLADE
+ depth = 10
/datum/heretic_knowledge/ultimate/blade_final
name = "Maelstrom of Silver"
@@ -395,6 +411,7 @@
gain_text = "The Torn Champion is freed! I will become the blade reunited, and with my greater ambition, \
I AM UNMATCHED! A STORM OF STEEL AND SILVER IS UPON US! WITNESS MY ASCENSION!"
route = PATH_BLADE
+ ascension_achievement = /datum/award/achievement/misc/blade_ascension
/datum/heretic_knowledge/ultimate/blade_final/is_valid_sacrifice(mob/living/carbon/human/sacrifice)
. = ..()
@@ -411,7 +428,6 @@
sound = 'sound/ambience/antag/heretic/ascend_blade.ogg',
color_override = "pink",
)
- user.client?.give_award(/datum/award/achievement/misc/blade_ascension, user)
ADD_TRAIT(user, TRAIT_NEVER_WOUNDED, name)
RegisterSignal(user, COMSIG_HERETIC_BLADE_ATTACK, PROC_REF(on_eldritch_blade))
user.apply_status_effect(/datum/status_effect/protective_blades/recharging, null, 8, 30, 0.25 SECONDS, 1 MINUTES)
diff --git a/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm b/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm
index 5484431d75e..8a94aada74a 100644
--- a/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm
@@ -40,6 +40,8 @@
)
result_atoms = list(/obj/item/melee/sickly_blade/cosmic)
route = PATH_COSMIC
+ research_tree_icon_path = 'icons/obj/weapons/khopesh.dmi'
+ research_tree_icon_state = "cosmic_blade"
/datum/heretic_knowledge/cosmic_grasp
name = "Grasp of Cosmos"
@@ -50,6 +52,9 @@
next_knowledge = list(/datum/heretic_knowledge/spell/cosmic_runes)
cost = 1
route = PATH_COSMIC
+ depth = 3
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "grasp_cosmos"
/datum/heretic_knowledge/cosmic_grasp/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
RegisterSignal(user, COMSIG_HERETIC_MANSUS_GRASP_ATTACK, PROC_REF(on_mansus_grasp))
@@ -80,6 +85,7 @@
spell_to_add = /datum/action/cooldown/spell/cosmic_rune
cost = 1
route = PATH_COSMIC
+ depth = 4
/datum/heretic_knowledge/mark/cosmic_mark
name = "Mark of Cosmos"
@@ -109,6 +115,7 @@
spell_to_add = /datum/action/cooldown/spell/touch/star_touch
cost = 1
route = PATH_COSMIC
+ depth = 7
/datum/heretic_knowledge/spell/star_blast
name = "Star Blast"
@@ -125,6 +132,7 @@
spell_to_add = /datum/action/cooldown/spell/pointed/projectile/star_blast
cost = 1
route = PATH_COSMIC
+ depth = 8
/datum/heretic_knowledge/blade_upgrade/cosmic
name = "Cosmic Blade"
@@ -137,6 +145,8 @@
The blades now glistened with fragmented power. I fell to the ground and wept at the beast's feet."
next_knowledge = list(/datum/heretic_knowledge/spell/cosmic_expansion)
route = PATH_COSMIC
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "blade_upgrade_cosmos"
/// Storage for the second target.
var/datum/weakref/second_target
/// Storage for the third target.
@@ -235,6 +245,7 @@
spell_to_add = /datum/action/cooldown/spell/conjure/cosmic_expansion
cost = 1
route = PATH_COSMIC
+ depth = 10
/datum/heretic_knowledge/ultimate/cosmic_final
name = "Creators's Gift"
@@ -252,6 +263,7 @@
I closed my eyes with my head laid against their form. I was safe. \
WITNESS MY ASCENSION!"
route = PATH_COSMIC
+ ascension_achievement = /datum/award/achievement/misc/cosmic_ascension
/// A static list of command we can use with our mob.
var/static/list/star_gazer_commands = list(
/datum/pet_command/idle,
@@ -298,5 +310,3 @@
var/datum/action/cooldown/spell/conjure/cosmic_expansion/cosmic_expansion_spell = locate() in user.actions
cosmic_expansion_spell?.ascended = TRUE
-
- user.client?.give_award(/datum/award/achievement/misc/cosmic_ascension, user)
diff --git a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
index c3e496ca89b..9e963ae9ea7 100644
--- a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
@@ -46,6 +46,8 @@
result_atoms = list(/obj/item/melee/sickly_blade/flesh)
limit = 3 // Bumped up so they can arm up their ghouls too.
route = PATH_FLESH
+ research_tree_icon_path = 'icons/obj/weapons/khopesh.dmi'
+ research_tree_icon_state = "flesh_blade"
/datum/heretic_knowledge/limited_amount/starting/base_flesh/on_research(mob/user, datum/antagonist/heretic/our_heretic)
. = ..()
@@ -66,6 +68,9 @@
limit = 1
cost = 1
route = PATH_FLESH
+ depth = 3
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "grasp_flesh"
/datum/heretic_knowledge/limited_amount/flesh_grasp/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
RegisterSignal(user, COMSIG_HERETIC_MANSUS_GRASP_ATTACK, PROC_REF(on_mansus_grasp))
@@ -139,6 +144,10 @@
limit = 2
cost = 1
route = PATH_FLESH
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "ghoul_voiceless"
+
+ depth = 4
/datum/heretic_knowledge/limited_amount/flesh_ghoul/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
. = ..()
@@ -229,6 +238,7 @@
spell_to_add = /datum/action/cooldown/spell/touch/flesh_surgery
cost = 1
route = PATH_FLESH
+ depth = 7
/datum/heretic_knowledge/summon/raw_prophet
name = "Raw Ritual"
@@ -252,6 +262,7 @@
cost = 1
route = PATH_FLESH
poll_ignore_define = POLL_IGNORE_RAW_PROPHET
+ depth = 8
/datum/heretic_knowledge/blade_upgrade/flesh
name = "Bleeding Steel"
@@ -260,6 +271,8 @@
I finally began to understand. And then, blood rained from the heavens."
next_knowledge = list(/datum/heretic_knowledge/summon/stalker)
route = PATH_FLESH
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "blade_upgrade_flesh"
///What type of wound do we apply on hit
var/wound_type = /datum/wound/slash/flesh/severe
@@ -294,6 +307,7 @@
cost = 1
route = PATH_FLESH
poll_ignore_define = POLL_IGNORE_STALKER
+ depth = 10
/datum/heretic_knowledge/ultimate/flesh_final
name = "Priest's Final Hymn"
@@ -310,6 +324,7 @@
Reality will bend to THE LORD OF THE NIGHT or be unraveled! WITNESS MY ASCENSION!"
required_atoms = list(/mob/living/carbon/human = 4)
route = PATH_FLESH
+ ascension_achievement = /datum/award/achievement/misc/flesh_ascension
/datum/heretic_knowledge/ultimate/flesh_final/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
. = ..()
@@ -323,7 +338,6 @@
var/datum/action/cooldown/spell/shapeshift/shed_human_form/worm_spell = new(user.mind)
worm_spell.Grant(user)
- user.client?.give_award(/datum/award/achievement/misc/flesh_ascension, user)
var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
var/datum/heretic_knowledge/limited_amount/flesh_grasp/grasp_ghoul = heretic_datum.get_knowledge(/datum/heretic_knowledge/limited_amount/flesh_grasp)
diff --git a/code/modules/antagonists/heretic/knowledge/general_side.dm b/code/modules/antagonists/heretic/knowledge/general_side.dm
index fced29ceb5d..8216b3b2569 100644
--- a/code/modules/antagonists/heretic/knowledge/general_side.dm
+++ b/code/modules/antagonists/heretic/knowledge/general_side.dm
@@ -12,6 +12,9 @@
)
cost = 0 // BUBBER EDIT
route = PATH_SIDE
+ depth = 8
+ research_tree_icon_path = 'icons/mob/actions/actions_animal.dmi'
+ research_tree_icon_state = "gaze"
/datum/heretic_knowledge/reroll_targets/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
diff --git a/code/modules/antagonists/heretic/knowledge/lock_lore.dm b/code/modules/antagonists/heretic/knowledge/lock_lore.dm
index 297c8925350..b238d6dd3c7 100644
--- a/code/modules/antagonists/heretic/knowledge/lock_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/lock_lore.dm
@@ -41,6 +41,8 @@
result_atoms = list(/obj/item/melee/sickly_blade/lock)
limit = 2
route = PATH_LOCK
+ research_tree_icon_path = 'icons/obj/weapons/khopesh.dmi'
+ research_tree_icon_state = "key_blade"
/datum/heretic_knowledge/lock_grasp
name = "Grasp of Lock"
@@ -51,6 +53,9 @@
next_knowledge = list(/datum/heretic_knowledge/key_ring)
cost = 1
route = PATH_LOCK
+ depth = 3
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "grasp_lock"
/datum/heretic_knowledge/lock_grasp/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
RegisterSignal(user, COMSIG_HERETIC_MANSUS_GRASP_ATTACK_SECONDARY, PROC_REF(on_secondary_mansus_grasp))
@@ -113,6 +118,9 @@
)
cost = 1
route = PATH_LOCK
+ research_tree_icon_path = 'icons/obj/card.dmi'
+ research_tree_icon_state = "card_gold"
+ depth = 4
/datum/heretic_knowledge/mark/lock_mark
name = "Mark of Lock"
@@ -142,6 +150,9 @@
next_knowledge = list(/datum/heretic_knowledge/spell/burglar_finesse)
cost = 1
route = PATH_LOCK
+ research_tree_icon_path = 'icons/obj/service/library.dmi'
+ research_tree_icon_state = "heretichandbook"
+ depth = 7
/datum/heretic_knowledge/spell/burglar_finesse
name = "Burglar's Finesse"
@@ -158,6 +169,7 @@
spell_to_add = /datum/action/cooldown/spell/pointed/burglar_finesse
cost = 1
route = PATH_LOCK
+ depth = 8
/datum/heretic_knowledge/blade_upgrade/flesh/lock //basically a chance-based weeping avulsion version of the former
name = "Opening Blade"
@@ -166,6 +178,8 @@
next_knowledge = list(/datum/heretic_knowledge/spell/caretaker_refuge)
route = PATH_LOCK
wound_type = /datum/wound/slash/flesh/critical
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "blade_upgrade_lock"
var/chance = 35
/datum/heretic_knowledge/blade_upgrade/flesh/lock/do_melee_effects(mob/living/source, mob/living/target, obj/item/melee/sickly_blade/blade)
@@ -185,6 +199,7 @@
route = PATH_LOCK
spell_to_add = /datum/action/cooldown/spell/caretaker
cost = 1
+ depth = 10
/datum/heretic_knowledge/ultimate/lock_final
name = "Unlock the Labyrinth"
@@ -201,6 +216,7 @@
The Labyrinth will be Locked no more, and freedom will be ours! WITNESS US!"
required_atoms = list(/mob/living/carbon/human = 3)
route = PATH_LOCK
+ ascension_achievement = /datum/award/achievement/misc/lock_ascension
/datum/heretic_knowledge/ultimate/lock_final/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
. = ..()
@@ -229,13 +245,11 @@
sound = 'sound/ambience/antag/heretic/ascend_knock.ogg',
color_override = "yellow",
)
- user.client?.give_award(/datum/award/achievement/misc/lock_ascension, user)
// buffs
var/datum/action/cooldown/spell/shapeshift/eldritch/ascension/transform_spell = new(user.mind)
transform_spell.Grant(user)
- user.client?.give_award(/datum/award/achievement/misc/lock_ascension, user)
var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
var/datum/heretic_knowledge/blade_upgrade/flesh/lock/blade_upgrade = heretic_datum.get_knowledge(/datum/heretic_knowledge/blade_upgrade/flesh/lock)
blade_upgrade.chance += 30
diff --git a/code/modules/antagonists/heretic/knowledge/moon_lore.dm b/code/modules/antagonists/heretic/knowledge/moon_lore.dm
index 1e88c6b0bb9..5ba55b64058 100644
--- a/code/modules/antagonists/heretic/knowledge/moon_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/moon_lore.dm
@@ -41,6 +41,8 @@
)
result_atoms = list(/obj/item/melee/sickly_blade/moon)
route = PATH_MOON
+ research_tree_icon_path = 'icons/obj/weapons/khopesh.dmi'
+ research_tree_icon_state = "moon_blade"
/datum/heretic_knowledge/base_moon/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
add_traits(user ,TRAIT_EMPATH, REF(src))
@@ -53,6 +55,9 @@
next_knowledge = list(/datum/heretic_knowledge/spell/moon_smile)
cost = 1
route = PATH_MOON
+ depth = 3
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "grasp_moon"
/datum/heretic_knowledge/moon_grasp/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
RegisterSignal(user, COMSIG_HERETIC_MANSUS_GRASP_ATTACK, PROC_REF(on_mansus_grasp))
@@ -84,6 +89,7 @@
spell_to_add = /datum/action/cooldown/spell/pointed/moon_smile
cost = 1
route = PATH_MOON
+ depth = 4
/datum/heretic_knowledge/mark/moon_mark
name = "Mark of Moon"
@@ -109,6 +115,7 @@
spell_to_add = /datum/action/cooldown/spell/pointed/projectile/moon_parade
cost = 1
route = PATH_MOON
+ depth = 7
/datum/heretic_knowledge/moon_amulet
@@ -132,6 +139,10 @@
result_atoms = list(/obj/item/clothing/neck/heretic_focus/moon_amulet)
cost = 1
route = PATH_MOON
+ depth = 8
+ research_tree_icon_path = 'icons/obj/antags/eldritch.dmi'
+ research_tree_icon_state = "moon_amulette"
+ research_tree_icon_frame = 9
/datum/heretic_knowledge/blade_upgrade/moon
name = "Moonlight Blade"
@@ -139,6 +150,8 @@
gain_text = "His wit was sharp as a blade, cutting through the lie to bring us joy."
next_knowledge = list(/datum/heretic_knowledge/spell/moon_ringleader)
route = PATH_MOON
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "blade_upgrade_moon"
/datum/heretic_knowledge/blade_upgrade/moon/do_melee_effects(mob/living/source, mob/living/target, obj/item/melee/sickly_blade/blade)
if(source == target)
@@ -166,6 +179,8 @@
spell_to_add = /datum/action/cooldown/spell/aoe/moon_ringleader
cost = 1
route = PATH_MOON
+ depth = 10
+ research_tree_icon_frame = 5
/datum/heretic_knowledge/ultimate/moon_final
name = "The Last Act"
@@ -178,6 +193,7 @@
for where the Ringleader had started the parade, I shall continue it unto the suns demise \
WITNESS MY ASCENSION, THE MOON SMILES ONCE MORE AND FOREVER MORE IT SHALL!"
route = PATH_MOON
+ ascension_achievement = /datum/award/achievement/misc/moon_ascension
/datum/heretic_knowledge/ultimate/moon_final/is_valid_sacrifice(mob/living/sacrifice)
@@ -198,7 +214,6 @@
color_override = "blue",
)
- user.client?.give_award(/datum/award/achievement/misc/moon_ascension, user)
ADD_TRAIT(user, TRAIT_MADNESS_IMMUNE, REF(src))
user.mind.add_antag_datum(/datum/antagonist/lunatic/master)
RegisterSignal(user, COMSIG_LIVING_LIFE, PROC_REF(on_life))
diff --git a/code/modules/antagonists/heretic/knowledge/rust_lore.dm b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
index 0cbf9abbb73..5e96119135f 100644
--- a/code/modules/antagonists/heretic/knowledge/rust_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
@@ -43,6 +43,8 @@
)
result_atoms = list(/obj/item/melee/sickly_blade/rust)
route = PATH_RUST
+ research_tree_icon_path = 'icons/obj/weapons/khopesh.dmi'
+ research_tree_icon_state = "rust_blade"
/datum/heretic_knowledge/rust_fist
name = "Grasp of Rust"
@@ -53,6 +55,9 @@
next_knowledge = list(/datum/heretic_knowledge/rust_regen)
cost = 1
route = PATH_RUST
+ depth = 3
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "grasp_rust"
/datum/heretic_knowledge/rust_fist/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
RegisterSignal(user, COMSIG_HERETIC_MANSUS_GRASP_ATTACK, PROC_REF(on_mansus_grasp))
@@ -74,7 +79,7 @@
SIGNAL_HANDLER
// Rusting an airlock causes it to lose power, mostly to prevent the airlock from shocking you.
- // This is a bit of a hack, but fixing this would require the enture wire cut/pulse system to be reworked.
+ // This is a bit of a hack, but fixing this would require the entire wire cut/pulse system to be reworked.
if(istype(target, /obj/machinery/door/airlock))
var/obj/machinery/door/airlock/airlock = target
airlock.loseMainPower()
@@ -94,58 +99,15 @@
)
cost = 1
route = PATH_RUST
+ research_tree_icon_path = 'icons/effects/eldritch.dmi'
+ research_tree_icon_state = "cloud_swirl"
+ depth = 4
/datum/heretic_knowledge/rust_regen/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
- RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
- RegisterSignal(user, COMSIG_LIVING_LIFE, PROC_REF(on_life))
+ user.AddElement(/datum/element/leeching_walk)
/datum/heretic_knowledge/rust_regen/on_lose(mob/user, datum/antagonist/heretic/our_heretic)
- UnregisterSignal(user, list(COMSIG_MOVABLE_MOVED, COMSIG_LIVING_LIFE))
-
-/*
- * Signal proc for [COMSIG_MOVABLE_MOVED].
- *
- * Checks if we should have baton resistance on the new turf.
- */
-/datum/heretic_knowledge/rust_regen/proc/on_move(mob/source, atom/old_loc, dir, forced, list/old_locs)
- SIGNAL_HANDLER
-
- var/turf/mover_turf = get_turf(source)
- if(HAS_TRAIT(mover_turf, TRAIT_RUSTY))
- ADD_TRAIT(source, TRAIT_BATON_RESISTANCE, type)
- return
-
- REMOVE_TRAIT(source, TRAIT_BATON_RESISTANCE, type)
-
-/**
- * Signal proc for [COMSIG_LIVING_LIFE].
- *
- * Gradually heals the heretic ([source]) on rust,
- * including baton knockdown and stamina damage.
- */
-/datum/heretic_knowledge/rust_regen/proc/on_life(mob/living/source, seconds_per_tick, times_fired)
- SIGNAL_HANDLER
-
- var/turf/our_turf = get_turf(source)
- if(!HAS_TRAIT(our_turf, TRAIT_RUSTY))
- return
-
- // Heals all damage + Stamina
- var/need_mob_update = FALSE
- need_mob_update += source.adjustBruteLoss(-3, updating_health = FALSE)
- need_mob_update += source.adjustFireLoss(-3, updating_health = FALSE)
- need_mob_update += source.adjustToxLoss(-3, updating_health = FALSE, forced = TRUE) // Slimes are people too
- need_mob_update += source.adjustOxyLoss(-1.5, updating_health = FALSE)
- need_mob_update += source.adjustStaminaLoss(-10, updating_stamina = FALSE)
- if(need_mob_update)
- source.updatehealth()
- // Reduces duration of stuns/etc
- source.AdjustAllImmobility(-0.5 SECONDS)
- // Heals blood loss
- if(source.blood_volume < BLOOD_VOLUME_NORMAL)
- source.blood_volume += 2.5 * seconds_per_tick
- // Slowly regulates your body temp
- source.adjust_bodytemperature((source.get_body_temp_normal() - source.bodytemperature)/5)
+ user.RemoveElement(/datum/element/leeching_walk)
/datum/heretic_knowledge/mark/rust_mark
name = "Mark of Rust"
@@ -175,6 +137,7 @@
spell_to_add = /datum/action/cooldown/spell/pointed/rust_construction
cost = 1
route = PATH_RUST
+ depth = 7
/datum/heretic_knowledge/spell/area_conversion
name = "Aggressive Spread"
@@ -192,6 +155,8 @@
spell_to_add = /datum/action/cooldown/spell/aoe/rust_conversion
cost = 1
route = PATH_RUST
+ depth = 8
+ research_tree_icon_frame = 5
/datum/heretic_knowledge/spell/area_conversion/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
. = ..()
@@ -204,6 +169,8 @@
The heavy rust weights it down. You stare deeply into it. The Rusted Hills call for you, now."
next_knowledge = list(/datum/heretic_knowledge/spell/entropic_plume)
route = PATH_RUST
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "blade_upgrade_rust"
/datum/heretic_knowledge/blade_upgrade/rust/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
. = ..()
@@ -228,6 +195,7 @@
spell_to_add = /datum/action/cooldown/spell/cone/staggered/entropic_plume
cost = 1
route = PATH_RUST
+ depth = 10
/datum/heretic_knowledge/spell/entropic_plume/on_gain(mob/user)
. = ..()
@@ -244,6 +212,7 @@
gain_text = "Champion of rust. Corruptor of steel. Fear the dark, for the RUSTBRINGER has come! \
The Blacksmith forges ahead! Rusted Hills, CALL MY NAME! WITNESS MY ASCENSION!"
route = PATH_RUST
+ ascension_achievement = /datum/award/achievement/misc/rust_ascension
/// If TRUE, then immunities are currently active.
var/immunities_active = FALSE
/// A typepath to an area that we must finish the ritual in.
diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
index be7ed60707f..1654918c024 100644
--- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
+++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
@@ -16,6 +16,9 @@
cost = 0
priority = MAX_KNOWLEDGE_PRIORITY // Should be at the top
route = PATH_START
+ research_tree_icon_path = 'icons/effects/eldritch.dmi'
+ research_tree_icon_state = "eye_close"
+ research_tree_icon_frame = 1
/// How many targets do we generate?
var/num_targets_to_generate = 5
/// Whether we've generated a heretic sacrifice z-level yet, from any heretic.
@@ -80,8 +83,11 @@
// If we have targets, we can check to see if we can do a sacrifice
// Let's remove any humans in our atoms list that aren't a sac target
for(var/mob/living/carbon/human/sacrifice in atoms)
- // If the mob's not in soft crit or worse, or isn't one of the sacrifices, remove it from the list
- if(sacrifice.stat < SOFT_CRIT || !(sacrifice in heretic_datum.sac_targets))
+ // If the mob's not in soft crit or worse, remove from list
+ if(sacrifice.stat < SOFT_CRIT)
+ atoms -= sacrifice
+ // Otherwise if it's neither a target nor a cultist, remove it
+ else if(!(sacrifice in heretic_datum.sac_targets) && !IS_CULTIST(sacrifice))
atoms -= sacrifice
// Finally, return TRUE if we have a target in the list
@@ -94,7 +100,9 @@
/datum/heretic_knowledge/hunt_and_sacrifice/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
- if(!LAZYLEN(heretic_datum.sac_targets))
+ // Force it to work if the sacrifice is a cultist, even if there's no targets.
+ var/mob/living/carbon/human/sac = selected_atoms[1]
+ if(!LAZYLEN(heretic_datum.sac_targets) && !IS_CULTIST(sac))
if(obtain_targets(user, heretic_datum = heretic_datum))
return TRUE
else
@@ -183,34 +191,128 @@
* * selected_atoms - a list of all atoms chosen. Should be (at least) one human.
* * loc - the turf the sacrifice is occuring on
*/
-/datum/heretic_knowledge/hunt_and_sacrifice/proc/sacrifice_process(mob/living/user, list/selected_atoms)
+/datum/heretic_knowledge/hunt_and_sacrifice/proc/sacrifice_process(mob/living/user, list/selected_atoms, turf/loc)
var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
var/mob/living/carbon/human/sacrifice = locate() in selected_atoms
if(!sacrifice)
CRASH("[type] sacrifice_process didn't have a human in the atoms list. How'd it make it so far?")
- if(!(sacrifice in heretic_datum.sac_targets))
- CRASH("[type] sacrifice_process managed to get a non-target human. This is incorrect.")
+ if(!(sacrifice in heretic_datum.sac_targets) && !IS_CULTIST(sacrifice))
+ CRASH("[type] sacrifice_process managed to get a non-target, non-cult human. This is incorrect.")
if(sacrifice.mind)
LAZYADD(target_blacklist, sacrifice.mind)
heretic_datum.remove_sacrifice_target(sacrifice)
+
var/feedback = "Your patrons accept your offer"
var/sac_job_flag = sacrifice.mind?.assigned_role?.job_flags | sacrifice.last_mind?.assigned_role?.job_flags
- if(sac_job_flag & JOB_HEAD_OF_STAFF)
- heretic_datum.knowledge_points++
+ var/datum/antagonist/cult/cultist_datum = IS_CULTIST(sacrifice)
+ // Heads give 3 points, cultists give 1 point (and a special reward), normal sacrifices give 2 points.
+ heretic_datum.total_sacrifices++
+ if((sac_job_flag & JOB_HEAD_OF_STAFF))
+ heretic_datum.knowledge_points += 3
heretic_datum.high_value_sacrifices++
feedback += " graciously"
+ else if(cultist_datum)
+ heretic_datum.knowledge_points += 1
+ grant_reward(user, sacrifice, loc)
+ // easier to read
+ var/rewards_given = heretic_datum.rewards_given
+ // Chance for it to send a warning to cultists, higher with each reward. Stops after 5 because they probably got the hint by then.
+ if(prob(min(15 * rewards_given)) && (rewards_given <= 5))
+ for(var/datum/mind/mind as anything in cultist_datum.cult_team.members)
+ if(mind.current)
+ SEND_SOUND(mind.current, 'sound/magic/clockwork/narsie_attack.ogg')
+ var/message = span_narsie("A vile heretic has ") + \
+ span_cult_large(span_hypnophrase("sacrificed")) + \
+ span_narsie(" one of our own. Destroy and sacrifice the infidel before it claims more!")
+ to_chat(mind.current, message)
+ // he(retic) gets a warn too
+ var/message = span_cult_bold("You feel that your action has attracted") + \
+ span_cult_bold_italic(" attention.")
+ to_chat(user, message)
+ return
+ else
+ heretic_datum.knowledge_points += 2
to_chat(user, span_hypnophrase("[feedback]."))
- heretic_datum.total_sacrifices++
- heretic_datum.knowledge_points += 2
+ if(!begin_sacrifice(sacrifice))
+ disembowel_target(sacrifice)
+ return
sacrifice.apply_status_effect(/datum/status_effect/heretic_curse, user)
- if(!begin_sacrifice(sacrifice))
- disembowel_target(sacrifice)
+
+/datum/heretic_knowledge/hunt_and_sacrifice/proc/grant_reward(mob/living/user, mob/living/sacrifice, turf/loc)
+
+ // Visible and audible encouragement!
+ to_chat(user, span_big(span_hypnophrase("A servant of the Sanguine Apostate!")))
+ to_chat(user, span_hierophant("Your patrons are rapturous!"))
+ playsound(sacrifice, 'sound/magic/disintegrate.ogg', 75, TRUE)
+
+ // Drop all items and splatter them around messily.
+ var/list/dustee_items = sacrifice.unequip_everything()
+ for(var/obj/item/loot as anything in dustee_items)
+ loot.throw_at(get_step_rand(sacrifice), 2, 4, user, TRUE)
+
+ // The loser is DUSTED.
+ sacrifice.dust(TRUE, TRUE)
+
+ // Increase reward counter
+ var/datum/antagonist/heretic/antag = IS_HERETIC(user)
+ antag.rewards_given++
+
+ // We limit the amount so the heretic doesn't just turn into a frickin' god (early)
+ to_chat(user, span_hierophant("You feel the rotten energies of the infidel warp and twist, mixing with that of your own..."))
+ if(prob(8 * antag.rewards_given))
+ to_chat(user, span_hierophant("Faint, dark red sparks flit around the dust, then fade. It looks like your patrons weren't able to fashion something out of it."))
+ return
+
+ // Cool effect for the rune as well as the item
+ var/obj/effect/heretic_rune/rune = locate() in range(2, user)
+ if(rune)
+ rune.gender_reveal(
+ outline_color = COLOR_CULT_RED,
+ ray_color = null,
+ do_float = FALSE,
+ do_layer = FALSE,
+ )
+
+ addtimer(CALLBACK(src, PROC_REF(deposit_reward), user, loc, null, rune), 5 SECONDS)
+
+
+/datum/heretic_knowledge/hunt_and_sacrifice/proc/deposit_reward(mob/user, turf/loc, loop = 0, obj/rune)
+ if(loop > 5) // Max limit for retrying a reward
+ return
+ // Remove the rays, we don't need them anymore.
+ rune?.remove_filter("reward_outline")
+ playsound(loc, 'sound/magic/repulse.ogg', 75, TRUE)
+ var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ ASSERT(heretic_datum)
+ // This list will be almost identical to unlocked_heretic_items, with the same keys, the difference being the values will be 1 to 5.
+ var/list/rewards = heretic_datum.unlocked_heretic_items.Copy()
+ // We will make it increasingly less likely to get a reward if you've already got it
+ for(var/possible_reward in heretic_datum.unlocked_heretic_items)
+ var/amount_already_awarded = heretic_datum.unlocked_heretic_items[possible_reward]
+ rewards[possible_reward] = min(5 - (amount_already_awarded * 2), 1)
+
+ var/atom/reward = pick_weight(rewards)
+ reward = new reward(loc)
+
+ if(isliving(reward))
+ if(summon_ritual_mob(user, loc, reward) == FALSE)
+ qdel(reward)
+ deposit_reward(user, loc, loop++, rune) // If no ghosts, try again until limit is hit
+ return
+
+ else if(isitem(reward))
+ var/obj/item/item_reward = reward
+ item_reward.gender_reveal(outline_color = null, ray_color = COLOR_CULT_RED)
+
+ ASSERT(reward)
+
+ return reward
/**
* This proc is called from [proc/sacrifice_process] after the heretic successfully sacrifices [sac_target].)
diff --git a/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm b/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm
index f933bbfda1d..758ee0548d5 100644
--- a/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm
@@ -16,6 +16,9 @@
result_atoms = list(/obj/item/clothing/neck/eldritch_amulet)
cost = 1
route = PATH_SIDE
+ research_tree_icon_path = 'icons/obj/antags/eldritch.dmi'
+ research_tree_icon_state = "eye_medalion"
+ depth = 4
/datum/heretic_knowledge/curse/paralysis
name = "Curse of Paralysis"
@@ -37,6 +40,9 @@
curse_color = "#f19a9a"
cost = 1
route = PATH_SIDE
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "curse_paralysis"
+ depth = 8
/datum/heretic_knowledge/curse/paralysis/curse(mob/living/carbon/human/chosen_mob, boosted = FALSE)
if(chosen_mob.usable_legs <= 0) // What're you gonna do, curse someone who already can't walk?
@@ -75,6 +81,7 @@
cost = 1
route = PATH_SIDE
poll_ignore_define = POLL_IGNORE_ASH_SPIRIT
+ depth = 10
/datum/heretic_knowledge/summon/ashy/cleanup_atoms(list/selected_atoms)
var/obj/item/bodypart/head/ritual_head = locate() in selected_atoms
diff --git a/code/modules/antagonists/heretic/knowledge/side_blade_rust.dm b/code/modules/antagonists/heretic/knowledge/side_blade_rust.dm
index 2a6d1e8058d..3a0f17ed483 100644
--- a/code/modules/antagonists/heretic/knowledge/side_blade_rust.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_blade_rust.dm
@@ -16,6 +16,10 @@
result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/eldritch)
cost = 1
route = PATH_SIDE
+ research_tree_icon_path = 'icons/obj/clothing/suits/armor.dmi'
+ research_tree_icon_state = "eldritch_armor"
+ research_tree_icon_frame = 12
+ depth = 4
/datum/heretic_knowledge/crucible
name = "Mawed Crucible"
@@ -34,6 +38,9 @@
result_atoms = list(/obj/structure/destructible/eldritch_crucible)
cost = 1
route = PATH_SIDE
+ research_tree_icon_path = 'icons/obj/antags/eldritch.dmi'
+ research_tree_icon_state = "crucible"
+ depth = 8
/datum/heretic_knowledge/rifle
name = "Lionhunter's Rifle"
@@ -59,9 +66,12 @@
result_atoms = list(/obj/item/gun/ballistic/rifle/lionhunter)
cost = 1
route = PATH_SIDE
+ depth = 8
+ research_tree_icon_path = 'icons/obj/weapons/guns/ballistic.dmi'
+ research_tree_icon_state = "goldrevolver"
/datum/heretic_knowledge/rifle_ammo
- name = "Lionhunter Rifle Ammunition (free)"
+ name = "Lionhunter Rifle Ammunition"
desc = "Allows you to transmute 3 ballistic ammo casings (used or unused) of any caliber, \
including shotgun shot, with any animal hide to create an extra clip of ammunition for the Lionhunter Rifle."
gain_text = "The weapon came with three rough iron balls, intended to be used as ammunition. \
@@ -74,6 +84,9 @@
result_atoms = list(/obj/item/ammo_box/strilka310/lionhunter)
cost = 0
route = PATH_SIDE
+ research_tree_icon_path = 'icons/obj/weapons/guns/ammo.dmi'
+ research_tree_icon_state = "310_strip"
+ depth = 8
/// A list of calibers that the ritual will deny. Only ballistic calibers are allowed.
var/static/list/caliber_blacklist = list(
CALIBER_LASER,
@@ -107,3 +120,4 @@
spell_to_add = /datum/action/cooldown/mob_cooldown/charge/rust
cost = 1
route = PATH_SIDE
+ depth = 10
diff --git a/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm b/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm
index 14a003ce11c..1a08236aee6 100644
--- a/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_cosmos_ash.dm
@@ -19,6 +19,8 @@
cost = 1
route = PATH_SIDE
poll_ignore_define = POLL_IGNORE_FIRE_SHARK
+ depth = 4
+ research_tree_icon_dir = EAST
/datum/heretic_knowledge/spell/space_phase
name = "Space Phase"
@@ -32,6 +34,8 @@
spell_to_add = /datum/action/cooldown/spell/jaunt/space_crawl
cost = 1
route = PATH_SIDE
+ depth = 8
+ research_tree_icon_frame = 6
/datum/heretic_knowledge/eldritch_coin
name = "Eldritch Coin"
@@ -51,3 +55,6 @@
result_atoms = list(/obj/item/coin/eldritch)
cost = 1
route = PATH_SIDE
+ research_tree_icon_path = 'icons/obj/economy.dmi'
+ research_tree_icon_state = "coin_heretic"
+ depth = 10
diff --git a/code/modules/antagonists/heretic/knowledge/side_flesh_void.dm b/code/modules/antagonists/heretic/knowledge/side_flesh_void.dm
index 4a315575d61..a958ab3f272 100644
--- a/code/modules/antagonists/heretic/knowledge/side_flesh_void.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_flesh_void.dm
@@ -19,6 +19,9 @@
result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/void)
cost = 1
route = PATH_SIDE
+ research_tree_icon_path = 'icons/obj/clothing/suits/armor.dmi'
+ research_tree_icon_state = "void_cloak"
+ depth = 4
/datum/heretic_knowledge/spell/blood_siphon
name = "Blood Siphon"
@@ -32,6 +35,7 @@
spell_to_add = /datum/action/cooldown/spell/pointed/blood_siphon
cost = 1
route = PATH_SIDE
+ depth = 8
/datum/heretic_knowledge/spell/cleave
name = "Blood Cleave"
@@ -46,3 +50,4 @@
spell_to_add = /datum/action/cooldown/spell/pointed/cleave
cost = 1
route = PATH_SIDE
+ depth = 10
diff --git a/code/modules/antagonists/heretic/knowledge/side_lock_flesh.dm b/code/modules/antagonists/heretic/knowledge/side_lock_flesh.dm
index 74013f2b0bd..706b83abac7 100644
--- a/code/modules/antagonists/heretic/knowledge/side_lock_flesh.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_lock_flesh.dm
@@ -12,6 +12,7 @@
spell_to_add = /datum/action/cooldown/spell/aoe/wave_of_desperation
cost = 1
route = PATH_SIDE
+ depth = 8
/datum/heretic_knowledge/spell/apetra_vulnera
name = "Apetra Vulnera"
@@ -26,3 +27,4 @@
spell_to_add = /datum/action/cooldown/spell/pointed/apetra_vulnera
cost = 1
route = PATH_SIDE
+ depth = 10
diff --git a/code/modules/antagonists/heretic/knowledge/side_lock_moon.dm b/code/modules/antagonists/heretic/knowledge/side_lock_moon.dm
index ac56b9ba240..1e265b97498 100644
--- a/code/modules/antagonists/heretic/knowledge/side_lock_moon.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_lock_moon.dm
@@ -13,6 +13,7 @@
spell_to_add = /datum/action/cooldown/spell/pointed/mind_gate
cost = 1
route = PATH_SIDE
+ depth = 4
/datum/heretic_knowledge/unfathomable_curio
name = "Unfathomable Curio"
@@ -33,6 +34,9 @@
result_atoms = list(/obj/item/storage/belt/unfathomable_curio)
cost = 1
route = PATH_SIDE
+ research_tree_icon_path = 'icons/obj/clothing/belts.dmi'
+ research_tree_icon_state = "unfathomable_curio"
+ depth = 8
/datum/heretic_knowledge/painting
name = "Unsealed Arts"
@@ -53,6 +57,9 @@
result_atoms = list(/obj/item/canvas)
cost = 1
route = PATH_SIDE
+ research_tree_icon_path = 'icons/obj/signs.dmi'
+ research_tree_icon_state = "eldritch_painting_weeping"
+ depth = 8
/datum/heretic_knowledge/painting/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
if(locate(/obj/item/organ/internal/eyes) in atoms)
diff --git a/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm b/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm
index 56999243b5d..11918c66a29 100644
--- a/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm
@@ -17,6 +17,9 @@
result_atoms = list(/obj/item/reagent_containers/cup/beaker/eldritch)
cost = 1
route = PATH_SIDE
+ depth = 4
+ research_tree_icon_path = 'icons/obj/antags/eldritch.dmi'
+ research_tree_icon_state = "eldritch_flask"
/datum/heretic_knowledge/entropy_pulse
name = "Pulse of Entropy"
@@ -28,6 +31,10 @@
)
cost = 0
route = PATH_SIDE
+ research_tree_icon_path = 'icons/mob/actions/actions_ecult.dmi'
+ research_tree_icon_state = "corrode"
+ research_tree_icon_frame = 10
+ depth = 4
var/rusting_range = 8
/datum/heretic_knowledge/entropy_pulse/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
@@ -60,6 +67,9 @@
curse_color = "#c1ffc9"
cost = 1
route = PATH_SIDE
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "curse_corrosion"
+ depth = 8
/datum/heretic_knowledge/curse/corrosion/curse(mob/living/carbon/human/chosen_mob, boosted = FALSE)
to_chat(chosen_mob, span_danger("You feel very ill..."))
@@ -92,6 +102,7 @@
cost = 1
route = PATH_SIDE
poll_ignore_define = POLL_IGNORE_RUST_SPIRIT
+ depth = 8
/datum/heretic_knowledge/summon/rusty/cleanup_atoms(list/selected_atoms)
var/obj/item/bodypart/head/ritual_head = locate() in selected_atoms
diff --git a/code/modules/antagonists/heretic/knowledge/side_void_blade.dm b/code/modules/antagonists/heretic/knowledge/side_void_blade.dm
index e044eee8619..56945262e3c 100644
--- a/code/modules/antagonists/heretic/knowledge/side_void_blade.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_void_blade.dm
@@ -23,6 +23,9 @@
limit = 1
cost = 1
route = PATH_SIDE
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "ghoul_shattered"
+ depth = 4
/datum/heretic_knowledge/limited_amount/risen_corpse/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
. = ..()
@@ -139,6 +142,9 @@
result_atoms = list(/obj/item/melee/rune_carver)
cost = 1
route = PATH_SIDE
+ depth = 8
+ research_tree_icon_path = 'icons/obj/antags/eldritch.dmi'
+ research_tree_icon_state = "rune_carver"
/datum/heretic_knowledge/summon/maid_in_mirror
name = "Maid in the Mirror"
@@ -162,3 +168,4 @@
route = PATH_SIDE
mob_to_summon = /mob/living/basic/heretic_summon/maid_in_the_mirror
poll_ignore_define = POLL_IGNORE_MAID_IN_MIRROR
+ depth = 10
diff --git a/code/modules/antagonists/heretic/knowledge/starting_lore.dm b/code/modules/antagonists/heretic/knowledge/starting_lore.dm
index acca3332d1d..7cb3b82a39a 100644
--- a/code/modules/antagonists/heretic/knowledge/starting_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/starting_lore.dm
@@ -49,6 +49,9 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
cost = 0
priority = MAX_KNOWLEDGE_PRIORITY - 1 // Knowing how to remake your heart is important
route = PATH_START
+ research_tree_icon_path = 'icons/obj/antags/eldritch.dmi'
+ research_tree_icon_state = "living_heart"
+ research_tree_icon_frame = 1
/// The typepath of the organ type required for our heart.
var/required_organ_type = /obj/item/organ/internal/heart
@@ -205,6 +208,8 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
cost = 0
priority = MAX_KNOWLEDGE_PRIORITY - 2 // Not as important as making a heart or sacrificing, but important enough.
route = PATH_START
+ research_tree_icon_path = 'icons/obj/clothing/neck.dmi'
+ research_tree_icon_state = "eldritch_necklace"
/datum/heretic_knowledge/spell/cloak_of_shadows
name = "Cloak of Shadow"
@@ -239,6 +244,8 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
route = PATH_START
priority = MAX_KNOWLEDGE_PRIORITY - 3 // Least priority out of the starting knowledges, as it's an optional boon.
var/static/list/non_mob_bindings = typecacheof(list(/obj/item/stack/sheet/leather, /obj/item/stack/sheet/animalhide))
+ research_tree_icon_path = 'icons/obj/antags/eldritch.dmi'
+ research_tree_icon_state = "book"
/datum/heretic_knowledge/codex_cicatrix/parse_required_item(atom/item_path, number_of_things)
if(item_path == /obj/item/pen)
@@ -301,21 +308,30 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
gain_text = "Under the soft glow of unreason there is a beast that stalks the night. I shall bring it forth and let it enter my presence. It will feast upon my amibitions and leave knowledge in its wake."
route = PATH_START
required_atoms = list()
+ research_tree_icon_path = 'icons/mob/actions/actions_animal.dmi'
+ research_tree_icon_state = "god_transmit"
/datum/heretic_knowledge/feast_of_owls/can_be_invoked(datum/antagonist/heretic/invoker)
return !invoker.feast_of_owls
/datum/heretic_knowledge/feast_of_owls/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
+ //amount of research points granted
+ var/reward = 5
var/alert = tgui_alert(user,"Do you really want to forsake your ascension? This action cannot be reverted.", "Feast of Owls", list("Yes I'm sure", "No"), 30 SECONDS)
if( alert != "Yes I'm sure")
return FALSE
- user.set_temp_blindness(5 SECONDS)
- user.AdjustParalyzed(5 SECONDS)
+ user.set_temp_blindness(reward SECONDS)
+ user.AdjustParalyzed(reward SECONDS)
+ user.playsound_local(get_turf(user), 'sound/ambience/antag/heretic/heretic_gain_intense.ogg', 100, FALSE, pressure_affected = FALSE, use_reverb = FALSE)
var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
- for(var/i in 0 to 4)
+ for(var/i in 1 to reward)
user.emote("scream")
playsound(loc, 'sound/items/eatfood.ogg', 100, TRUE)
heretic_datum.knowledge_points++
+ to_chat(user, span_danger("You feel something invisible tearing away at your very essence!"))
+ user.do_jitter_animation()
sleep(1 SECONDS)
- to_chat(user,span_danger("You feel different..."))
heretic_datum.feast_of_owls = TRUE
+ to_chat(user, span_danger(span_big("Your ambition is ravaged, but something powerful remains in its wake...")))
+ var/drain_message = pick(strings(HERETIC_INFLUENCE_FILE, "drain_message"))
+ to_chat(user, span_hypnophrase(span_big("[drain_message]")))
diff --git a/code/modules/antagonists/heretic/knowledge/void_lore.dm b/code/modules/antagonists/heretic/knowledge/void_lore.dm
index 87d321187f2..0fd4f925fa8 100644
--- a/code/modules/antagonists/heretic/knowledge/void_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/void_lore.dm
@@ -39,6 +39,8 @@
required_atoms = list(/obj/item/knife = 1)
result_atoms = list(/obj/item/melee/sickly_blade/void)
route = PATH_VOID
+ research_tree_icon_path = 'icons/obj/weapons/khopesh.dmi'
+ research_tree_icon_state = "void_blade"
/datum/heretic_knowledge/limited_amount/starting/base_void/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
if(!isopenturf(loc))
@@ -60,6 +62,9 @@
next_knowledge = list(/datum/heretic_knowledge/cold_snap)
cost = 1
route = PATH_VOID
+ depth = 3
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "grasp_void"
/datum/heretic_knowledge/void_grasp/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
RegisterSignal(user, COMSIG_HERETIC_MANSUS_GRASP_ATTACK, PROC_REF(on_mansus_grasp))
@@ -90,6 +95,9 @@
)
cost = 1
route = PATH_VOID
+ research_tree_icon_path = 'icons/effects/effects.dmi'
+ research_tree_icon_state = "the_freezer"
+ depth = 4
/datum/heretic_knowledge/cold_snap/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
user.add_traits(list(TRAIT_NOBREATH, TRAIT_RESISTCOLD), type)
@@ -121,6 +129,7 @@
spell_to_add = /datum/action/cooldown/spell/cone/staggered/cone_of_cold/void
cost = 1
route = PATH_VOID
+ depth = 7
/datum/heretic_knowledge/spell/void_phase
name = "Void Phase"
@@ -137,6 +146,8 @@
spell_to_add = /datum/action/cooldown/spell/pointed/void_phase
cost = 1
route = PATH_VOID
+ depth = 8
+ research_tree_icon_frame = 7
/datum/heretic_knowledge/blade_upgrade/void
name = "Seeking Blade"
@@ -144,6 +155,8 @@
gain_text = "Fleeting memories, fleeting feet. I mark my way with frozen blood upon the snow. Covered and forgotten."
next_knowledge = list(/datum/heretic_knowledge/spell/void_pull)
route = PATH_VOID
+ research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_state = "blade_upgrade_void"
/datum/heretic_knowledge/blade_upgrade/void/do_ranged_effects(mob/living/user, mob/living/target, obj/item/melee/sickly_blade/blade)
if(!target.has_status_effect(/datum/status_effect/eldritch))
@@ -170,6 +183,8 @@
spell_to_add = /datum/action/cooldown/spell/aoe/void_pull
cost = 1
route = PATH_VOID
+ depth = 10
+ research_tree_icon_frame = 6
/datum/heretic_knowledge/ultimate/void_final
name = "Waltz at the End of Time"
@@ -182,6 +197,7 @@
The Aristocrat stands before me, beckoning. We will play a waltz to the whispers of dying reality, \
as the world is destroyed before our eyes. The void will return all to nothing, WITNESS MY ASCENSION!"
route = PATH_VOID
+ ascension_achievement = /datum/award/achievement/misc/void_ascension
///soundloop for the void theme
var/datum/looping_sound/void_loop/sound_loop
///Reference to the ongoing voidstrom that surrounds the heretic
@@ -207,7 +223,6 @@
sound = 'sound/ambience/antag/heretic/ascend_void.ogg',
color_override = "blue",
)
- user.client?.give_award(/datum/award/achievement/misc/void_ascension, user)
ADD_TRAIT(user, TRAIT_RESISTLOWPRESSURE, MAGIC_TRAIT)
// Let's get this show on the road!
diff --git a/code/modules/antagonists/heretic/magic/aggressive_spread.dm b/code/modules/antagonists/heretic/magic/aggressive_spread.dm
index 948faaf5629..0c14550f0b2 100644
--- a/code/modules/antagonists/heretic/magic/aggressive_spread.dm
+++ b/code/modules/antagonists/heretic/magic/aggressive_spread.dm
@@ -17,7 +17,16 @@
aoe_radius = 2
/datum/action/cooldown/spell/aoe/rust_conversion/get_things_to_cast_on(atom/center)
- return RANGE_TURFS(aoe_radius, center)
+
+ var/list/things_to_convert = RANGE_TURFS(aoe_radius, center)
+
+ // Also converts things right next to you.
+ for(var/atom/movable/nearby_movable in view(1, center))
+ if(nearby_movable == owner || !isstructure(nearby_movable) )
+ continue
+ things_to_convert += nearby_movable
+
+ return things_to_convert
/datum/action/cooldown/spell/aoe/rust_conversion/cast_on_thing_in_aoe(turf/victim, mob/living/caster)
// We have less chance of rusting stuff that's further
@@ -27,9 +36,11 @@
if(prob(chance_of_not_rusting))
return
- caster.do_rust_heretic_act(victim)
+ if(ismob(caster))
+ caster.do_rust_heretic_act(victim)
+ else
+ victim.rust_heretic_act()
-/datum/action/cooldown/spell/aoe/rust_conversion/small
- name = "Rust Conversion"
- desc = "Spreads rust onto nearby surfaces."
- aoe_radius = 2
+/datum/action/cooldown/spell/aoe/rust_conversion/construct
+ name = "Construct Spread"
+ cooldown_time = 15 SECONDS
diff --git a/code/modules/antagonists/heretic/magic/ash_ascension.dm b/code/modules/antagonists/heretic/magic/ash_ascension.dm
index 9a352f68f5f..70422a7c48a 100644
--- a/code/modules/antagonists/heretic/magic/ash_ascension.dm
+++ b/code/modules/antagonists/heretic/magic/ash_ascension.dm
@@ -129,16 +129,17 @@
INVOKE_ASYNC(src, PROC_REF(fire_line), owner, line_target(offset, flame_line_length, target, owner))
/datum/action/cooldown/spell/pointed/ash_beams/proc/line_target(offset, range, atom/at, atom/user)
+ var/turf/user_loc = get_turf(user)
if(!at)
return
- var/angle = ATAN2(at.x - user.x, at.y - user.y) + offset
+ var/angle = ATAN2(at.x - user_loc.x, at.y - user_loc.y) + offset
var/turf/T = get_turf(user)
for(var/i in 1 to range)
- var/turf/check = locate(user.x + cos(angle) * i, user.y + sin(angle) * i, user.z)
+ var/turf/check = locate(user_loc.x + cos(angle) * i, user_loc.y + sin(angle) * i, user_loc.z)
if(!check)
break
T = check
- return (get_line(user, T) - get_turf(user))
+ return (get_line(user_loc, T) - user_loc)
/datum/action/cooldown/spell/pointed/ash_beams/proc/fire_line(atom/source, list/turfs)
var/list/hit_list = list()
diff --git a/code/modules/antagonists/heretic/magic/burglar_finesse.dm b/code/modules/antagonists/heretic/magic/burglar_finesse.dm
index 4fef7d50afc..c5264119bb4 100644
--- a/code/modules/antagonists/heretic/magic/burglar_finesse.dm
+++ b/code/modules/antagonists/heretic/magic/burglar_finesse.dm
@@ -26,7 +26,7 @@
return FALSE
var/obj/storage_item = locate(/obj/item/storage/backpack) in cast_on.contents
-
+
if(isnull(storage_item))
return FALSE
diff --git a/code/modules/antagonists/heretic/magic/fire_blast.dm b/code/modules/antagonists/heretic/magic/fire_blast.dm
index 9a2fd9cfd3e..8c6d632be9f 100644
--- a/code/modules/antagonists/heretic/magic/fire_blast.dm
+++ b/code/modules/antagonists/heretic/magic/fire_blast.dm
@@ -23,8 +23,8 @@
var/beam_duration = 2 SECONDS
/datum/action/cooldown/spell/charged/beam/fire_blast/cast(atom/cast_on)
- if(isliving(cast_on))
- var/mob/living/caster = cast_on
+ var/mob/living/caster = get_caster_from_target(cast_on)
+ if(istype(caster))
// Caster becomes fireblasted, but in a good way - heals damage over time
caster.apply_status_effect(/datum/status_effect/fire_blasted, beam_duration, -2)
return ..()
diff --git a/code/modules/antagonists/heretic/magic/furious_steel.dm b/code/modules/antagonists/heretic/magic/furious_steel.dm
index a611dfc27d9..36c7c07608b 100644
--- a/code/modules/antagonists/heretic/magic/furious_steel.dm
+++ b/code/modules/antagonists/heretic/magic/furious_steel.dm
@@ -67,7 +67,7 @@
QDEL_NULL(blade_effect)
var/mob/living/living_user = on_who
- blade_effect = living_user.apply_status_effect(/datum/status_effect/protective_blades, null, projectile_amount, 25, 0.66 SECONDS)
+ blade_effect = living_user.apply_status_effect(/datum/status_effect/protective_blades, null, projectile_amount, 25, 0.66 SECONDS, projectile_type)
RegisterSignal(blade_effect, COMSIG_QDELETING, PROC_REF(on_status_effect_deleted))
/datum/action/cooldown/spell/pointed/projectile/furious_steel/on_deactivation(mob/on_who, refund_cooldown = TRUE)
@@ -106,10 +106,12 @@
sharpness = SHARP_EDGED
wound_bonus = 15
pass_flags = PASSTABLE | PASSFLAPS
+ /// Color applied as an outline filter on init
+ var/outline_color = "#f8f8ff"
/obj/projectile/floating_blade/Initialize(mapload)
. = ..()
- add_filter("dio_knife", 2, list("type" = "outline", "color" = "#f8f8ff", "size" = 1))
+ add_filter("dio_knife", 2, list("type" = "outline", "color" = outline_color, "size" = 1))
/obj/projectile/floating_blade/prehit_pierce(atom/hit)
if(isliving(hit) && isliving(firer))
@@ -128,3 +130,40 @@
return PROJECTILE_DELETE_WITHOUT_HITTING
return ..()
+
+/obj/projectile/floating_blade/haunted
+ name = "ritual blade"
+ icon = 'icons/obj/weapons/khopesh.dmi'
+ icon_state = "render"
+ damage = 35
+ wound_bonus = 25
+ outline_color = "#D7CBCA"
+
+/datum/action/cooldown/spell/pointed/projectile/furious_steel/solo
+ name = "Lesser Furious Steel"
+ cooldown_time = 20 SECONDS
+ projectile_amount = 1
+ active_msg = "You summon forth a blade of furious silver."
+ deactive_msg = "You conceal the blade of furious silver."
+
+/datum/action/cooldown/spell/pointed/projectile/furious_steel/haunted
+ name = "Cursed Steel"
+ desc = "Summon two cursed blades which orbit you. \
+ While orbiting you, these blades will protect you from from attacks, but will be consumed on use. \
+ Additionally, you can click to fire the blades at a target, dealing damage and causing bleeding."
+ background_icon_state = "bg_heretic" // kept intentionally
+ overlay_icon_state = "bg_cult_border"
+ button_icon = 'icons/mob/actions/actions_ecult.dmi'
+ button_icon_state = "cursed_steel"
+ sound = 'sound/weapons/guillotine.ogg'
+
+ cooldown_time = 40 SECONDS
+ invocation = "IA!"
+ invocation_type = INVOCATION_SHOUT
+
+ spell_requirements = NONE
+
+ active_msg = "You summon forth two cursed blades."
+ deactive_msg = "You conceal the cursed blades."
+ projectile_amount = 2
+ projectile_type = /obj/projectile/floating_blade/haunted
diff --git a/code/modules/antagonists/heretic/magic/mansus_grasp.dm b/code/modules/antagonists/heretic/magic/mansus_grasp.dm
index a91abb91cae..803bdd3d218 100644
--- a/code/modules/antagonists/heretic/magic/mansus_grasp.dm
+++ b/code/modules/antagonists/heretic/magic/mansus_grasp.dm
@@ -38,11 +38,35 @@
var/mob/living/living_hit = victim
living_hit.apply_damage(10, BRUTE, wound_bonus = CANT_WOUND)
- if(iscarbon(victim))
- var/mob/living/carbon/carbon_hit = victim
- carbon_hit.adjust_timed_status_effect(4 SECONDS, /datum/status_effect/speech/slurring/heretic)
- carbon_hit.AdjustKnockdown(5 SECONDS)
- carbon_hit.adjustStaminaLoss(80)
+ if(!iscarbon(victim))
+ return TRUE
+
+ var/mob/living/carbon/carbon_hit = victim
+
+ // Cultists are momentarily disoriented by the stunning aura. Enough for both parties to go 'oh shit' but only a mild combat ability.
+ // Cultists have an identical effect on their stun hand. The heretic's faster spell charge time is made up for by their lack of teammates.
+ if(IS_CULTIST(carbon_hit))
+ carbon_hit.AdjustKnockdown(0.5 SECONDS)
+ carbon_hit.adjust_confusion_up_to(1.5 SECONDS, 3 SECONDS)
+ carbon_hit.adjust_dizzy_up_to(1.5 SECONDS, 3 SECONDS)
+ ADD_TRAIT(carbon_hit, TRAIT_NO_SIDE_KICK, REF(src)) // We don't want this to be a good stunning tool, just minor disorientation
+ addtimer(TRAIT_CALLBACK_REMOVE(carbon_hit, TRAIT_NO_SIDE_KICK, REF(src)), 1 SECONDS)
+
+ var/old_color = carbon_hit.color
+ carbon_hit.color = COLOR_CULT_RED
+ animate(carbon_hit, color = old_color, time = 4 SECONDS, easing = EASE_IN)
+ carbon_hit.mob_light(range = 1.5, power = 2.5, color = COLOR_CULT_RED, duration = 0.5 SECONDS)
+ playsound(carbon_hit, 'sound/magic/curse.ogg', 50, TRUE)
+
+ to_chat(caster, span_warning("An unholy force intervenes as you grasp [carbon_hit], absorbing most of the effects!"))
+ to_chat(carbon_hit, span_warning("As [caster] grasps you with eldritch forces, your blood magic absorbs most of the effects!"))
+ carbon_hit.balloon_alert_to_viewers("absorbed!")
+ return TRUE
+
+ carbon_hit.adjust_timed_status_effect(4 SECONDS, /datum/status_effect/speech/slurring/heretic)
+ carbon_hit.AdjustKnockdown(5 SECONDS)
+ carbon_hit.adjustStaminaLoss(80)
+ carbon_hit.apply_status_effect(/datum/status_effect/next_shove_stuns)
return TRUE
diff --git a/code/modules/antagonists/heretic/magic/rust_construction.dm b/code/modules/antagonists/heretic/magic/rust_construction.dm
index 130e3e06be2..f8d6a2ff2be 100644
--- a/code/modules/antagonists/heretic/magic/rust_construction.dm
+++ b/code/modules/antagonists/heretic/magic/rust_construction.dm
@@ -8,7 +8,7 @@
check_flags = AB_CHECK_INCAPACITATED|AB_CHECK_CONSCIOUS|AB_CHECK_HANDS_BLOCKED
school = SCHOOL_FORBIDDEN
- cooldown_time = 5 SECONDS
+ cooldown_time = 8 SECONDS
invocation = "Someone raises a wall of rust."
invocation_self_message = "You raise a wall of rust."
@@ -16,15 +16,19 @@
spell_requirements = NONE
cast_range = 4
- aim_assist = FALSE
/// How long does the filter last on walls we make?
var/filter_duration = 2 MINUTES
+/**
+ * Overrides 'aim assist' because we always want to hit just the turf we clicked on.
+ */
+/datum/action/cooldown/spell/pointed/rust_construction/aim_assist(mob/living/caller, atom/target)
+ return get_turf(target)
+
/datum/action/cooldown/spell/pointed/rust_construction/is_valid_target(atom/cast_on)
- if(!isfloorturf(cast_on))
- if(isturf(cast_on) && owner)
- cast_on.balloon_alert(owner, "not a floor!")
+ if(!isturf(cast_on))
+ cast_on.balloon_alert(owner, "not a wall or floor!")
return FALSE
if(!HAS_TRAIT(cast_on, TRAIT_RUSTY))
@@ -43,9 +47,22 @@
invocation = span_danger("[owner] drags [owner.p_their()] hand[living_owner.usable_hands == 1 ? "":"s"] upwards as a wall of rust rises out of [cast_on]!")
invocation_self_message = span_notice("You drag [living_owner.usable_hands == 1 ? "a hand":"your hands"] upwards as a wall of rust rises out of [cast_on].")
-/datum/action/cooldown/spell/pointed/rust_construction/cast(turf/open/cast_on)
+/datum/action/cooldown/spell/pointed/rust_construction/cast(turf/cast_on)
. = ..()
var/rises_message = "rises out of [cast_on]"
+
+ // If we casted at a wall we'll try to rust it. In the case of an enchanted wall it'll deconstruct it
+ if(isclosedturf(cast_on))
+ cast_on.visible_message(span_warning("\The [cast_on] quakes as the rust causes it to crumble!"))
+ var/mob/living/living_owner = owner
+ living_owner?.do_rust_heretic_act(cast_on)
+ // ref transfers to floor
+ cast_on.Shake(shake_interval = 0.1 SECONDS, duration = 0.5 SECONDS)
+ // which we need to re-rust
+ living_owner?.do_rust_heretic_act(cast_on)
+ playsound(cast_on, 'sound/effects/bang.ogg', 50, vary = TRUE)
+ return
+
var/turf/closed/wall/new_wall = cast_on.place_on_top(/turf/closed/wall)
if(!istype(new_wall))
return
@@ -53,7 +70,8 @@
playsound(new_wall, 'sound/effects/constructform.ogg', 50, TRUE)
new_wall.rust_heretic_act()
new_wall.name = "\improper enchanted [new_wall.name]"
- new_wall.hardness = 10
+ new_wall.AddComponent(/datum/component/torn_wall)
+ new_wall.hardness = 60
new_wall.sheet_amount = 0
new_wall.girder_type = null
@@ -61,8 +79,8 @@
// but I guess a fading filter will have to do for now as walls have 0 depth (currently)
// damn though with 3/4ths walls this'll look sick just imagine it
new_wall.add_filter("rust_wall", 2, list("type" = "outline", "color" = "#85be299c", "size" = 2))
- addtimer(CALLBACK(src, PROC_REF(fade_wall_filter), new_wall), filter_duration * (1/20))
- addtimer(CALLBACK(src,PROC_REF(remove_wall_filter), new_wall), filter_duration)
+ addtimer(CALLBACK(src, PROC_REF(fade_wall_filter), new_wall), filter_duration * 0.5)
+ addtimer(CALLBACK(src, PROC_REF(remove_wall_filter), new_wall), filter_duration)
var/message_shown = FALSE
for(var/mob/living/living_mob in cast_on)
@@ -108,7 +126,7 @@
if(!rust_filter)
return
- animate(rust_filter, alpha = 0, time = filter_duration * (19/20))
+ animate(rust_filter, alpha = 0, time = filter_duration * (9/20))
/datum/action/cooldown/spell/pointed/rust_construction/proc/remove_wall_filter(turf/closed/wall)
if(QDELETED(wall))
diff --git a/code/modules/antagonists/heretic/magic/rust_wave.dm b/code/modules/antagonists/heretic/magic/rust_wave.dm
index c0674a222e3..7ecb3fd0ffb 100644
--- a/code/modules/antagonists/heretic/magic/rust_wave.dm
+++ b/code/modules/antagonists/heretic/magic/rust_wave.dm
@@ -25,7 +25,10 @@
new /obj/effect/temp_visual/dir_setting/entropic(get_step(cast_on, cast_on.dir), cast_on.dir)
/datum/action/cooldown/spell/cone/staggered/entropic_plume/do_turf_cone_effect(turf/target_turf, mob/living/caster, level)
- caster.do_rust_heretic_act(target_turf)
+ if(ismob(caster))
+ caster.do_rust_heretic_act(target_turf)
+ else
+ target_turf.rust_heretic_act()
/datum/action/cooldown/spell/cone/staggered/entropic_plume/do_mob_cone_effect(mob/living/victim, atom/caster, level)
if(victim.can_block_magic(antimagic_flags) || IS_HERETIC_OR_MONSTER(victim) || victim == caster)
diff --git a/code/modules/antagonists/heretic/soultrapped_heretic.dm b/code/modules/antagonists/heretic/soultrapped_heretic.dm
new file mode 100644
index 00000000000..ffd92e09496
--- /dev/null
+++ b/code/modules/antagonists/heretic/soultrapped_heretic.dm
@@ -0,0 +1,24 @@
+///a heretic that got soultrapped by cultists. does nothing, other than signify they suck
+/datum/antagonist/soultrapped_heretic
+ name = "\improper Soultrapped Heretic"
+ roundend_category = "Heretics"
+ antagpanel_category = "Heretic"
+ job_rank = ROLE_HERETIC
+ antag_moodlet = /datum/mood_event/soultrapped_heretic
+ antag_hud_name = "heretic"
+
+// Will never show up because they're shades inside a sword
+/datum/mood_event/soultrapped_heretic
+ description = "They trapped me! I can't escape!"
+ mood_change = -20
+
+// always failure obj
+/datum/objective/heretic_trapped
+ name = "soultrapped failure"
+ explanation_text = "Help the cult. Kill the cult. Help the crew. Kill the crew. Help your wielder. Kill your wielder. Kill everyone. Rattle your chains."
+
+/datum/antagonist/soultrapped_heretic/on_gain()
+ ..()
+ var/datum/objective/epic_fail = new /datum/objective/heretic_trapped()
+ epic_fail.completed = FALSE
+ objectives += epic_fail
diff --git a/code/modules/antagonists/heretic/status_effects/buffs.dm b/code/modules/antagonists/heretic/status_effects/buffs.dm
index d2058a5b4f1..35a6ab92687 100644
--- a/code/modules/antagonists/heretic/status_effects/buffs.dm
+++ b/code/modules/antagonists/heretic/status_effects/buffs.dm
@@ -118,6 +118,8 @@
var/time_between_initial_blades = 0.25 SECONDS
/// If TRUE, we self-delete our status effect after all the blades are deleted.
var/delete_on_blades_gone = TRUE
+ /// What blade type to create
+ var/blade_type = /obj/effect/floating_blade
/// A list of blade effects orbiting / protecting our owner
var/list/obj/effect/floating_blade/blades = list()
@@ -127,12 +129,14 @@
max_num_blades = 4,
blade_orbit_radius = 20,
time_between_initial_blades = 0.25 SECONDS,
+ blade_type = /obj/effect/floating_blade,
)
src.duration = new_duration
src.max_num_blades = max_num_blades
src.blade_orbit_radius = blade_orbit_radius
src.time_between_initial_blades = time_between_initial_blades
+ src.blade_type = blade_type
return ..()
/datum/status_effect/protective_blades/on_apply()
@@ -157,7 +161,7 @@
if(QDELETED(src) || QDELETED(owner))
return
- var/obj/effect/floating_blade/blade = new(get_turf(owner))
+ var/obj/effect/floating_blade/blade = new blade_type(get_turf(owner))
blades += blade
blade.orbit(owner, blade_orbit_radius)
RegisterSignal(blade, COMSIG_QDELETING, PROC_REF(remove_blade))
diff --git a/code/modules/antagonists/heretic/structures/carving_knife.dm b/code/modules/antagonists/heretic/structures/carving_knife.dm
index 9bdad0ea5fc..70133e951af 100644
--- a/code/modules/antagonists/heretic/structures/carving_knife.dm
+++ b/code/modules/antagonists/heretic/structures/carving_knife.dm
@@ -15,15 +15,7 @@
attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "rends")
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "rend")
actions_types = list(/datum/action/item_action/rune_shatter)
- embedding = list(
- ignore_throwspeed_threshold = TRUE,
- embed_chance = 75,
- jostle_chance = 2,
- jostle_pain_mult = 5,
- pain_stam_pct = 0.4,
- pain_mult = 3,
- rip_time = 15,
- )
+ embed_type = /datum/embed_data/rune_carver
/// Whether we're currently drawing a rune
var/drawing = FALSE
@@ -34,6 +26,15 @@
/// Turfs that you cannot draw carvings on
var/static/list/blacklisted_turfs = typecacheof(list(/turf/open/space, /turf/open/openspace, /turf/open/lava))
+/datum/embed_data/rune_carver
+ ignore_throwspeed_threshold = TRUE
+ embed_chance = 75
+ jostle_chance = 2
+ jostle_pain_mult = 5
+ pain_stam_pct = 0.4
+ pain_mult = 3
+ rip_time = 15
+
/obj/item/melee/rune_carver/examine(mob/user)
. = ..()
if(!IS_HERETIC_OR_MONSTER(user) && !isobserver(user))
diff --git a/code/modules/antagonists/heretic/structures/mawed_crucible.dm b/code/modules/antagonists/heretic/structures/mawed_crucible.dm
index 8e5410f0f67..2135ffa134c 100644
--- a/code/modules/antagonists/heretic/structures/mawed_crucible.dm
+++ b/code/modules/antagonists/heretic/structures/mawed_crucible.dm
@@ -57,6 +57,10 @@
return span_notice("It's at [round(atom_integrity * 100 / max_integrity)]% stability.")
return ..()
+// no breaky herety thingy
+/obj/structure/destructible/eldritch_crucible/rust_heretic_act()
+ return
+
/obj/structure/destructible/eldritch_crucible/attacked_by(obj/item/weapon, mob/living/user)
if(!iscarbon(user))
return ..()
diff --git a/code/modules/antagonists/heretic/transmutation_rune.dm b/code/modules/antagonists/heretic/transmutation_rune.dm
index 8d5f08db749..0c510e34ff2 100644
--- a/code/modules/antagonists/heretic/transmutation_rune.dm
+++ b/code/modules/antagonists/heretic/transmutation_rune.dm
@@ -1,8 +1,9 @@
/// The heretic's rune, which they use to complete transmutation rituals.
/obj/effect/heretic_rune
name = "transmutation rune"
- desc = "A flowing circle of shapes and runes is etched into the floor, filled with a thick black tar-like fluid."
- icon_state = ""
+ desc = "A flowing circle of shapes and runes is etched into the floor, filled with a thick black tar-like fluid. This one looks pretty small."
+ icon = 'icons/obj/antags/cult/rune.dmi'
+ icon_state = "main1"
anchored = TRUE
interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
diff --git a/code/modules/antagonists/ninja/outfit.dm b/code/modules/antagonists/ninja/outfit.dm
index 9b81dd75f05..ba54c33d842 100644
--- a/code/modules/antagonists/ninja/outfit.dm
+++ b/code/modules/antagonists/ninja/outfit.dm
@@ -1,7 +1,7 @@
/datum/outfit/ninja
name = "Space Ninja"
uniform = /obj/item/clothing/under/syndicate/ninja
- glasses = /obj/item/clothing/glasses/night
+ glasses = /obj/item/clothing/glasses/night/colorless
mask = /obj/item/clothing/mask/gas/ninja
ears = /obj/item/radio/headset
shoes = /obj/item/clothing/shoes/jackboots
diff --git a/code/modules/antagonists/nukeop/outfits.dm b/code/modules/antagonists/nukeop/outfits.dm
index e8ae07ffdde..fa214aa7046 100644
--- a/code/modules/antagonists/nukeop/outfits.dm
+++ b/code/modules/antagonists/nukeop/outfits.dm
@@ -67,7 +67,7 @@
/datum/outfit/syndicate/full
name = "Syndicate Operative - Full Kit"
- glasses = /obj/item/clothing/glasses/night
+ glasses = /obj/item/clothing/glasses/night/colorless
mask = /obj/item/clothing/mask/gas/syndicate
back = /obj/item/mod/control/pre_equipped/nuclear
r_pocket = /obj/item/tank/internals/emergency_oxygen/engi
@@ -136,7 +136,7 @@
suit = /obj/item/clothing/suit/jacket/oversized
gloves = /obj/item/clothing/gloves/fingerless
glasses = /obj/item/clothing/glasses/sunglasses
- mask = /obj/item/clothing/mask/cigarette/cigar
+ mask = /obj/item/cigarette/cigar
faction = "Cybersun Industries"
/datum/outfit/syndicate/reinforcement/donk
diff --git a/code/modules/antagonists/pirate/pirate_outfits.dm b/code/modules/antagonists/pirate/pirate_outfits.dm
index 15a3d4fe2dc..aef7f1d9d4b 100644
--- a/code/modules/antagonists/pirate/pirate_outfits.dm
+++ b/code/modules/antagonists/pirate/pirate_outfits.dm
@@ -72,7 +72,7 @@
id_trim = /datum/id_trim/pirate/captain/silverscale
head = /obj/item/clothing/head/costume/crown
- mask = /obj/item/clothing/mask/cigarette/cigar/havana
+ mask = /obj/item/cigarette/cigar/havana
l_pocket = /obj/item/lighter
/datum/outfit/pirate/interdyne
diff --git a/code/modules/antagonists/traitor/contractor/contract_teammate.dm b/code/modules/antagonists/traitor/contractor/contract_teammate.dm
index 54fc958c1f8..965d99e89ac 100644
--- a/code/modules/antagonists/traitor/contractor/contract_teammate.dm
+++ b/code/modules/antagonists/traitor/contractor/contract_teammate.dm
@@ -24,7 +24,7 @@
suit = /obj/item/clothing/suit/chameleon
back = /obj/item/storage/backpack
belt = /obj/item/modular_computer/pda/chameleon
- mask = /obj/item/clothing/mask/cigarette/syndicate
+ mask = /obj/item/cigarette/syndicate
shoes = /obj/item/clothing/shoes/chameleon/noslip
ears = /obj/item/radio/headset/chameleon
id = /obj/item/card/id/advanced/chameleon
@@ -41,5 +41,5 @@
/datum/outfit/contractor_partner/post_equip(mob/living/carbon/human/H, visualsOnly)
. = ..()
- var/obj/item/clothing/mask/cigarette/syndicate/cig = H.get_item_by_slot(ITEM_SLOT_MASK)
+ var/obj/item/cigarette/syndicate/cig = H.get_item_by_slot(ITEM_SLOT_MASK)
cig.light()
diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm
index e77ed87ba55..9130883d457 100644
--- a/code/modules/antagonists/wizard/equipment/soulstone.dm
+++ b/code/modules/antagonists/wizard/equipment/soulstone.dm
@@ -265,16 +265,17 @@
icon = 'icons/mob/shells.dmi'
icon_state = "construct_cult"
desc = "A wicked machine used by those skilled in magical arts. It is inactive."
-
-/obj/structure/constructshell/examine(mob/user)
- . = ..()
- if(IS_CULTIST(user) || HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED) || user.stat == DEAD)
- . += {"A construct shell, used to house bound souls from a soulstone.\n
+ var/extra_desc = {"A construct shell, used to house bound souls from a soulstone.\n
Placing a soulstone with a soul into this shell allows you to produce your choice of the following:\n
An Artificer, which can produce more shells and soulstones, as well as fortifications.\n
A Wraith, which does high damage and can jaunt through walls, though it is quite fragile.\n
A Juggernaut, which is very hard to kill and can produce temporary walls, but is slow."}
+/obj/structure/constructshell/examine(mob/user)
+ . = ..()
+ if(IS_CULTIST(user) || HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED) || user.stat == DEAD)
+ . += extra_desc
+
/obj/structure/constructshell/attackby(obj/item/O, mob/user, params)
if(istype(O, /obj/item/soulstone))
var/obj/item/soulstone/SS = O
@@ -499,6 +500,11 @@
make_new_construct(/mob/living/basic/construct/artificer/angelic, target, creator, cultoverride, loc_override)
if(THEME_CULT)
make_new_construct(/mob/living/basic/construct/artificer/noncult, target, creator, cultoverride, loc_override)
+ if(CONSTRUCT_HARVESTER)
+ if(IS_HERETIC_OR_MONSTER(creator))
+ make_new_construct(/mob/living/basic/construct/harvester/heretic, target, creator, cultoverride, loc_override)
+ else
+ make_new_construct(/mob/living/basic/construct/harvester, target, creator, cultoverride, loc_override)
/proc/make_new_construct(mob/living/basic/construct/ctype, mob/target, mob/stoner = null, cultoverride = FALSE, loc_override = null)
if(QDELETED(target))
@@ -528,7 +534,7 @@
newstruct.clear_alert("bloodsense")
sense_alert = newstruct.throw_alert("bloodsense", /atom/movable/screen/alert/bloodsense)
if(sense_alert)
- sense_alert.Cviewer = newstruct
+ sense_alert.construct_owner = newstruct
newstruct.cancel_camera()
/obj/item/soulstone/anybody
diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
index cfb87ce7cb3..db2732be83f 100644
--- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm
+++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
@@ -676,20 +676,20 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
return FALSE
/// Pumps gas from src to output_air. Amount depends on target_pressure
-/datum/gas_mixture/proc/pump_gas_to(datum/gas_mixture/output_air, target_pressure, specific_gas = null)
- var/temperature_delta = abs(temperature - output_air.temperature)
+/datum/gas_mixture/proc/pump_gas_to(datum/gas_mixture/output_air, target_pressure, specific_gas = null, datum/gas_mixture/output_pipenet_air = null)
+ var/datum/gas_mixture/input_air = specific_gas ? remove_specific_ratio(specific_gas, 1) : src
+ var/temperature_delta = abs(input_air.temperature - output_air.temperature)
var/datum/gas_mixture/removed
- var/transfer_moles
+
+ var/transfer_moles_output = input_air.gas_pressure_calculate(output_air, target_pressure, temperature_delta <= 5)
+ var/transfer_moles_pipenet = output_pipenet_air?.volume ? input_air.gas_pressure_calculate(output_pipenet_air, target_pressure, temperature_delta <= 5) : 0
+ var/transfer_moles = max(transfer_moles_output, transfer_moles_pipenet)
if(specific_gas)
- // This is necessary because the specific heat capacity of a gas might be different from our gasmix.
- var/datum/gas_mixture/temporary = remove_specific_ratio(specific_gas, 1)
- transfer_moles = temporary.gas_pressure_calculate(output_air, target_pressure, temperature_delta <= 5)
- removed = temporary.remove_specific(specific_gas, transfer_moles)
- merge(temporary)
+ removed = input_air.remove_specific(specific_gas, transfer_moles)
+ merge(input_air) // Merge the remaining gas back to the input node
else
- transfer_moles = gas_pressure_calculate(output_air, target_pressure, temperature_delta <= 5)
- removed = remove(transfer_moles)
+ removed = input_air.remove(transfer_moles)
if(!removed)
return FALSE
@@ -698,18 +698,20 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
return removed
/// Releases gas from src to output air. This means that it can not transfer air to gas mixture with higher pressure.
-/datum/gas_mixture/proc/release_gas_to(datum/gas_mixture/output_air, target_pressure, rate=1)
+/datum/gas_mixture/proc/release_gas_to(datum/gas_mixture/output_air, target_pressure, rate=1, datum/gas_mixture/output_pipenet_air = null)
var/output_starting_pressure = output_air.return_pressure()
var/input_starting_pressure = return_pressure()
//Need at least 10 KPa difference to overcome friction in the mechanism
- if(output_starting_pressure >= min(target_pressure,input_starting_pressure-10))
+ if(output_starting_pressure >= min(target_pressure, input_starting_pressure-10))
return FALSE
//Can not have a pressure delta that would cause output_pressure > input_pressure
target_pressure = output_starting_pressure + min(target_pressure - output_starting_pressure, (input_starting_pressure - output_starting_pressure)/2)
var/temperature_delta = abs(temperature - output_air.temperature)
- var/transfer_moles = gas_pressure_calculate(output_air, target_pressure, temperature_delta <= 5)
+ var/transfer_moles_output = gas_pressure_calculate(output_air, target_pressure, temperature_delta <= 5)
+ var/transfer_moles_pipenet = output_pipenet_air?.volume ? gas_pressure_calculate(output_pipenet_air, target_pressure, temperature_delta <= 5) : 0
+ var/transfer_moles = max(transfer_moles_output, transfer_moles_pipenet)
//Actually transfer the gas
var/datum/gas_mixture/removed = remove(transfer_moles * rate)
diff --git a/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm b/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
index 4ec7830ac88..0a6aa23b34d 100644
--- a/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
+++ b/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
@@ -145,6 +145,8 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
/obj/machinery/airalarm/proc/check_enviroment()
var/turf/our_turf = connected_sensor ? get_turf(connected_sensor) : get_turf(src)
var/datum/gas_mixture/environment = our_turf.return_air()
+ if(isnull(environment))
+ return
check_danger(our_turf, environment, environment.temperature)
/obj/machinery/airalarm/proc/get_enviroment()
@@ -554,6 +556,9 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
if((machine_stat & (NOPOWER|BROKEN)) || shorted)
return
+ if(!environment)
+ return
+
var/old_danger = danger_level
danger_level = AIR_ALARM_ALERT_NONE
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm
index 572e910d3fe..1d7657dd352 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm
@@ -59,9 +59,11 @@ Passive gate is similar to the regular pump except:
if(!on)
return
- var/datum/gas_mixture/air1 = airs[1]
- var/datum/gas_mixture/air2 = airs[2]
- if(air1.release_gas_to(air2, target_pressure))
+ var/datum/gas_mixture/input_air = airs[1]
+ var/datum/gas_mixture/output_air = airs[2]
+ var/datum/gas_mixture/output_pipenet_air = parents[2].air
+
+ if(input_air.release_gas_to(output_air, target_pressure, output_pipenet_air = output_pipenet_air))
update_parents()
/obj/machinery/atmospherics/components/binary/passive_gate/relaymove(mob/living/user, direction)
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/pressure_valve.dm b/code/modules/atmospherics/machinery/components/binary_devices/pressure_valve.dm
index 399feff1214..ff9cc36d7cd 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/pressure_valve.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/pressure_valve.dm
@@ -54,11 +54,12 @@
if(!on || !is_operational)
return
- var/datum/gas_mixture/air1 = airs[1]
- var/datum/gas_mixture/air2 = airs[2]
+ var/datum/gas_mixture/input_air = airs[1]
+ var/datum/gas_mixture/output_air = airs[2]
+ var/datum/gas_mixture/output_pipenet_air = parents[2].air
- if(air1.return_pressure() > target_pressure)
- if(air1.release_gas_to(air2, air1.return_pressure()))
+ if(input_air.return_pressure() > target_pressure)
+ if(input_air.release_gas_to(output_air, input_air.return_pressure(), output_pipenet_air = output_pipenet_air))
update_parents()
is_gas_flowing = TRUE
else
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
index 63ba340f27f..1e68bf9ad69 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
@@ -61,10 +61,11 @@
if(!on || !is_operational)
return
- var/datum/gas_mixture/air1 = airs[1]
- var/datum/gas_mixture/air2 = airs[2]
+ var/datum/gas_mixture/input_air = airs[1]
+ var/datum/gas_mixture/output_air = airs[2]
+ var/datum/gas_mixture/output_pipenet_air = parents[2].air
- if(air1.pump_gas_to(air2, target_pressure))
+ if(input_air.pump_gas_to(output_air, target_pressure, output_pipenet_air = output_pipenet_air))
update_parents()
/obj/machinery/atmospherics/components/binary/pump/ui_interact(mob/user, datum/tgui/ui)
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/temperature_gate.dm b/code/modules/atmospherics/machinery/components/binary_devices/temperature_gate.dm
index b5a3740245a..879f6b7fab8 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/temperature_gate.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/temperature_gate.dm
@@ -68,19 +68,20 @@
if(!on || !is_operational)
return
- var/datum/gas_mixture/air1 = airs[1]
- var/datum/gas_mixture/air2 = airs[2]
+ var/datum/gas_mixture/input_air = airs[1]
+ var/datum/gas_mixture/output_air = airs[2]
+ var/datum/gas_mixture/output_pipenet_air = parents[2].air
if(!inverted)
- if(air1.temperature < target_temperature)
- if(air1.release_gas_to(air2, air1.return_pressure()))
+ if(input_air.temperature < target_temperature)
+ if(input_air.release_gas_to(output_air, input_air.return_pressure(), output_pipenet_air = output_pipenet_air))
update_parents()
is_gas_flowing = TRUE
else
is_gas_flowing = FALSE
else
- if(air1.temperature > target_temperature)
- if(air1.release_gas_to(air2, air1.return_pressure()))
+ if(input_air.temperature > target_temperature)
+ if(input_air.release_gas_to(output_air, input_air.return_pressure(), output_pipenet_air = output_pipenet_air))
update_parents()
is_gas_flowing = TRUE
else
diff --git a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm
index 6e0490e25ef..95b548998a1 100644
--- a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm
+++ b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm
@@ -16,7 +16,7 @@
if(atmos_device.nob_crystal_inserted)
to_chat(user, span_warning("[atmos_device] already has a hypernoblium crystal inserted in it!"))
return ITEM_INTERACT_BLOCKING
- atmos_device.nob_crystal_inserted = TRUE
+ atmos_device.insert_nob_crystal()
to_chat(user, span_notice("You insert the [src] into [atmos_device]."))
if(istype(worn_item))
diff --git a/code/modules/atmospherics/machinery/portable/pipe_scrubber.dm b/code/modules/atmospherics/machinery/portable/pipe_scrubber.dm
new file mode 100644
index 00000000000..cde38f216ad
--- /dev/null
+++ b/code/modules/atmospherics/machinery/portable/pipe_scrubber.dm
@@ -0,0 +1,167 @@
+/obj/machinery/portable_atmospherics/pipe_scrubber
+ name = "pipe scrubber"
+ desc = "A machine for cleaning out pipes of lingering gases. It is a huge tank with a pump attached to it."
+ icon_state = "pipe_scrubber"
+ density = TRUE
+ max_integrity = 250
+ volume = 200
+ ///The internal air tank obj of the mech
+ var/obj/machinery/portable_atmospherics/canister/internal_tank
+ ///Is the machine on?
+ var/on = FALSE
+ ///What direction is the machine pumping to (into scrubber or out to the port)?
+ var/direction = PUMP_IN
+ ///the rate the machine will scrub air
+ var/volume_rate = 1000
+ ///List of gases that can be scrubbed
+ var/list/scrubbing = list(
+ /datum/gas/plasma,
+ /datum/gas/carbon_dioxide,
+ /datum/gas/nitrous_oxide,
+ /datum/gas/bz,
+ /datum/gas/nitrium,
+ /datum/gas/tritium,
+ /datum/gas/hypernoblium,
+ /datum/gas/water_vapor,
+ /datum/gas/freon,
+ /datum/gas/hydrogen,
+ /datum/gas/healium,
+ /datum/gas/proto_nitrate,
+ /datum/gas/zauker,
+ /datum/gas/halon,
+ )
+
+/obj/machinery/portable_atmospherics/pipe_scrubber/Initialize(mapload)
+ . = ..()
+ internal_tank = new(src)
+ RegisterSignal(internal_tank, COMSIG_ATOM_BREAK, PROC_REF(deconstruct))
+ RegisterSignal(internal_tank, COMSIG_QDELETING, PROC_REF(deconstruct))
+
+/obj/machinery/portable_atmospherics/pipe_scrubber/atom_deconstruct(disassembled)
+ . = ..()
+ var/turf/my_turf = get_turf(src)
+ my_turf.assume_air(air_contents)
+ my_turf.assume_air(internal_tank.air_contents)
+ SSair.stop_processing_machine(internal_tank)
+ qdel(internal_tank)
+
+/obj/machinery/portable_atmospherics/pipe_scrubber/return_analyzable_air()
+ return list(
+ air_contents,
+ internal_tank.air_contents
+ )
+
+/obj/machinery/portable_atmospherics/pipe_scrubber/welder_act(mob/living/user, obj/item/tool)
+ internal_tank.welder_act(user, tool)
+ return ..()
+
+/obj/machinery/portable_atmospherics/pipe_scrubber/click_alt(mob/living/user)
+ return CLICK_ACTION_BLOCKING
+
+/obj/machinery/portable_atmospherics/pipe_scrubber/replace_tank(mob/living/user, close_valve, obj/item/tank/new_tank)
+ return FALSE
+
+/obj/machinery/portable_atmospherics/pipe_scrubber/update_icon_state()
+ icon_state = on ? "[initial(icon_state)]_active" : initial(icon_state)
+ return ..()
+
+/obj/machinery/portable_atmospherics/pipe_scrubber/process_atmos()
+ if(take_atmos_damage())
+ excited = TRUE
+ return ..()
+ if(!on)
+ return ..()
+ excited = TRUE
+ if(direction == PUMP_IN)
+ scrub(air_contents)
+ else
+ internal_tank.air_contents.pump_gas_to(air_contents, PUMP_MAX_PRESSURE)
+ return ..()
+
+/// Scrub gasses from own air_contents into internal_tank.air_contents
+/obj/machinery/portable_atmospherics/pipe_scrubber/proc/scrub()
+ if(internal_tank.air_contents.return_pressure() >= PUMP_MAX_PRESSURE)
+ return
+
+ var/transfer_moles = min(1, volume_rate / air_contents.volume) * air_contents.total_moles()
+
+ var/datum/gas_mixture/filtering = air_contents.remove(transfer_moles) // Remove part of the mixture to filter.
+ var/datum/gas_mixture/filtered = new
+ if(!filtering)
+ return
+
+ filtered.temperature = filtering.temperature
+ for(var/gas in filtering.gases & scrubbing)
+ filtered.add_gas(gas)
+ filtered.gases[gas][MOLES] = filtering.gases[gas][MOLES] // Shuffle the "bad" gasses to the filtered mixture.
+ filtering.gases[gas][MOLES] = 0
+ filtering.garbage_collect() // Now that the gasses are set to 0, clean up the mixture.
+
+ internal_tank.air_contents.merge(filtered) // Store filtered out gasses.
+ air_contents.merge(filtering) // Returned the cleaned gas.
+
+/obj/machinery/portable_atmospherics/pipe_scrubber/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "PipeScrubber", name)
+ ui.open()
+
+/obj/machinery/portable_atmospherics/pipe_scrubber/ui_data()
+ var/data = list()
+ data["on"] = on
+ data["direction"] = direction
+ data["connected"] = connected_port ? 1 : 0
+ data["pressureTank"] = round(internal_tank.air_contents.return_pressure() ? internal_tank.air_contents.return_pressure() : 0)
+ data["pressurePump"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0)
+ data["hasHypernobCrystal"] = nob_crystal_inserted
+ data["reactionSuppressionEnabled"] = suppress_reactions
+
+ data["filterTypes"] = list()
+ for(var/gas_path in GLOB.meta_gas_info)
+ var/list/gas = GLOB.meta_gas_info[gas_path]
+ data["filterTypes"] += list(list("gasId" = gas[META_GAS_ID], "gasName" = gas[META_GAS_NAME], "enabled" = (gas_path in scrubbing)))
+
+ return data
+
+/obj/machinery/portable_atmospherics/pipe_scrubber/ui_static_data()
+ var/list/data = list()
+ data["pressureLimitPump"] = pressure_limit
+ data["pressureLimitTank"] = internal_tank.pressure_limit
+ return data
+
+/obj/machinery/portable_atmospherics/pipe_scrubber/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("power")
+ on = !on
+ if(on)
+ SSair.start_processing_machine(src)
+ SSair.start_processing_machine(internal_tank)
+ . = TRUE
+ if("direction")
+ direction = !direction
+ . = TRUE
+ if("toggle_filter")
+ scrubbing ^= gas_id2path(params["val"])
+ . = TRUE
+ if("reaction_suppression")
+ if(!internal_tank.nob_crystal_inserted)
+ message_admins("[ADMIN_LOOKUPFLW(usr)] tried to toggle reaction suppression on a pipe scrubber without a noblium crystal inside, possible href exploit attempt.")
+ return
+ internal_tank.suppress_reactions = !internal_tank.suppress_reactions
+ SSair.start_processing_machine(internal_tank)
+ message_admins("[ADMIN_LOOKUPFLW(usr)] turned [internal_tank.suppress_reactions ? "on" : "off"] the [internal_tank] reaction suppression.")
+ usr.investigate_log("turned [internal_tank.suppress_reactions ? "on" : "off"] the [internal_tank] reaction suppression.")
+ . = TRUE
+ update_appearance()
+
+/obj/machinery/portable_atmospherics/pipe_scrubber/insert_nob_crystal()
+ . = ..()
+ internal_tank.nob_crystal_inserted = TRUE
+
+/obj/machinery/portable_atmospherics/pipe_scrubber/proc/toggle_reaction_suppression()
+ var/new_value = !suppress_reactions
+ suppress_reactions = new_value
+ internal_tank.suppress_reactions = new_value
diff --git a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm
index 17f3a6fb439..9729c087145 100644
--- a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm
+++ b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm
@@ -212,8 +212,16 @@
* * new_tank: the tank we are trying to put in the machine
*/
/obj/machinery/portable_atmospherics/proc/replace_tank(mob/living/user, close_valve, obj/item/tank/new_tank)
+ if(machine_stat & BROKEN)
+ return FALSE
if(!user)
return FALSE
+ if(!user.transferItemToLoc(new_tank, src))
+ return FALSE
+
+ investigate_log("had its internal [holding] swapped with [new_tank] by [key_name(user)].", INVESTIGATE_ATMOS)
+ to_chat(user, span_notice("[holding ? "In one smooth motion you pop [holding] out of [src]'s connector and replace it with [new_tank]" : "You insert [new_tank] into [src]"]."))
+
if(holding && new_tank)//for when we are actually switching tanks
user.put_in_hands(holding)
UnregisterSignal(holding, COMSIG_QDELETING)
@@ -238,17 +246,9 @@
return TRUE
/obj/machinery/portable_atmospherics/attackby(obj/item/item, mob/user, params)
- if(!istype(item, /obj/item/tank))
- return ..()
- if(machine_stat & BROKEN)
- return FALSE
- var/obj/item/tank/insert_tank = item
- if(!user.transferItemToLoc(insert_tank, src))
- return FALSE
- to_chat(user, span_notice("[holding ? "In one smooth motion you pop [holding] out of [src]'s connector and replace it with [insert_tank]" : "You insert [insert_tank] into [src]"]."))
- investigate_log("had its internal [holding] swapped with [insert_tank] by [key_name(user)].", INVESTIGATE_ATMOS)
- replace_tank(user, FALSE, insert_tank)
- update_appearance()
+ if(istype(item, /obj/item/tank))
+ return replace_tank(user, FALSE, item)
+ return ..()
/obj/machinery/portable_atmospherics/wrench_act(mob/living/user, obj/item/wrench)
if(machine_stat & BROKEN)
@@ -295,4 +295,8 @@
UnregisterSignal(holding, COMSIG_QDELETING)
holding = null
+/// Insert Hypernob crystal into the machine
+/obj/machinery/portable_atmospherics/proc/insert_nob_crystal()
+ nob_crystal_inserted = TRUE
+
#undef PORTABLE_ATMOS_IGNORE_ATMOS_LIMIT
diff --git a/code/modules/bitrunning/components/avatar_connection.dm b/code/modules/bitrunning/components/avatar_connection.dm
index 2ced012ffe2..4274893e998 100644
--- a/code/modules/bitrunning/components/avatar_connection.dm
+++ b/code/modules/bitrunning/components/avatar_connection.dm
@@ -60,7 +60,7 @@
var/datum/action/avatar_domain_info/action = new(help_datum)
action.Grant(avatar)
- var/client/our_client = old_body.client
+ var/client/our_client = avatar.client
var/alias = our_client?.prefs?.read_preference(/datum/preference/name/hacker_alias) || pick(GLOB.hacker_aliases)
if(alias && avatar.real_name != alias)
diff --git a/code/modules/bitrunning/components/bitrunning_points.dm b/code/modules/bitrunning/components/bitrunning_points.dm
index 328a70679e6..ea8f63f76d8 100644
--- a/code/modules/bitrunning/components/bitrunning_points.dm
+++ b/code/modules/bitrunning/components/bitrunning_points.dm
@@ -5,6 +5,7 @@
/// A special condition limits this from spawning a crate
var/points_received = 0
+
/datum/component/bitrunning_points/Initialize(datum/lazy_template/virtual_domain/domain)
. = ..()
if(!isturf(parent))
@@ -12,6 +13,7 @@
RegisterSignal(domain, COMSIG_BITRUNNER_GOAL_POINT, PROC_REF(on_add_points))
+
/// Listens for points to be added which will eventually spawn a crate.
/datum/component/bitrunning_points/proc/on_add_points(datum/source, points_to_add)
SIGNAL_HANDLER
@@ -23,12 +25,14 @@
reveal()
+
/// Spawns the crate with some effects
/datum/component/bitrunning_points/proc/reveal()
playsound(src, 'sound/magic/blink.ogg', 50, TRUE)
var/turf/tile = parent
- new /obj/structure/closet/crate/secure/bitrunning/encrypted(tile)
+ var/obj/structure/closet/crate/secure/bitrunning/encrypted/crate = new()
+ crate.forceMove(tile) // Triggers any on-move effects on that turf
var/datum/effect_system/spark_spread/quantum/sparks = new(tile)
sparks.set_up(number = 5, location = tile)
diff --git a/code/modules/bitrunning/netpod/_netpod.dm b/code/modules/bitrunning/netpod/_netpod.dm
new file mode 100644
index 00000000000..2208c56a741
--- /dev/null
+++ b/code/modules/bitrunning/netpod/_netpod.dm
@@ -0,0 +1,137 @@
+#define BASE_DISCONNECT_DAMAGE 40
+
+
+/obj/machinery/netpod
+ name = "netpod"
+
+ base_icon_state = "netpod"
+ circuit = /obj/item/circuitboard/machine/netpod
+ desc = "A link to the netverse. It has an assortment of cables to connect yourself to a virtual domain."
+ icon = 'icons/obj/machines/bitrunning.dmi'
+ icon_state = "netpod"
+ max_integrity = 300
+ obj_flags = BLOCKS_CONSTRUCTION
+ state_open = TRUE
+ interaction_flags_mouse_drop = NEED_HANDS | NEED_DEXTERITY
+
+ /// Whether we have an ongoing connection
+ var/connected = FALSE
+ /// A player selected outfit by clicking the netpod
+ var/datum/outfit/netsuit = /datum/outfit/job/bitrunner
+ /// Holds this to see if it needs to generate a new one
+ var/datum/weakref/avatar_ref
+ /// The linked quantum server
+ var/datum/weakref/server_ref
+ /// The amount of brain damage done from force disconnects
+ var/disconnect_damage
+ /// Static list of outfits to select from
+ var/list/cached_outfits = list()
+
+
+/obj/machinery/netpod/post_machine_initialize()
+ . = ..()
+
+ disconnect_damage = BASE_DISCONNECT_DAMAGE
+ find_server()
+
+ RegisterSignal(src, COMSIG_ATOM_TAKE_DAMAGE, PROC_REF(on_damage_taken))
+ RegisterSignal(src, COMSIG_MACHINERY_POWER_LOST, PROC_REF(on_power_loss))
+ RegisterSignals(src, list(COMSIG_QDELETING, COMSIG_MACHINERY_BROKEN),PROC_REF(on_broken))
+
+ register_context()
+ update_appearance()
+
+
+/obj/machinery/netpod/Destroy()
+ . = ..()
+
+ QDEL_LIST(cached_outfits)
+
+
+/obj/machinery/netpod/examine(mob/user)
+ . = ..()
+
+ if(isnull(server_ref?.resolve()))
+ . += span_infoplain("It's not connected to anything.")
+ . += span_infoplain("Netpods must be built within 4 tiles of a server.")
+ return
+
+ if(!isobserver(user))
+ . += span_infoplain("Drag yourself into the pod to engage the link.")
+ . += span_infoplain("It has limited resuscitation capabilities. Remaining in the pod can heal some injuries.")
+ . += span_infoplain("It has a security system that will alert the occupant if it is tampered with.")
+
+ if(isnull(occupant))
+ . += span_infoplain("It's currently unoccupied.")
+ return
+
+ . += span_infoplain("It's currently occupied by [occupant].")
+
+ if(isobserver(user))
+ . += span_notice("As an observer, you can click this netpod to jump to its avatar.")
+ return
+
+ . += span_notice("It can be pried open with a crowbar, but its safety mechanisms will alert the occupant.")
+
+
+/obj/machinery/netpod/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ . = ..()
+
+ if(isnull(held_item))
+ context[SCREENTIP_CONTEXT_LMB] = "Select Outfit"
+ return CONTEXTUAL_SCREENTIP_SET
+
+ if(istype(held_item, /obj/item/crowbar) && occupant)
+ context[SCREENTIP_CONTEXT_LMB] = "Pry Open"
+ return CONTEXTUAL_SCREENTIP_SET
+
+
+/obj/machinery/netpod/update_icon_state()
+ if(!is_operational)
+ icon_state = base_icon_state
+ return ..()
+
+ if(state_open)
+ icon_state = base_icon_state + "_open_active"
+ return ..()
+
+ if(panel_open)
+ icon_state = base_icon_state + "_panel"
+ return ..()
+
+ icon_state = base_icon_state + "_closed"
+ if(occupant)
+ icon_state += "_active"
+
+ return ..()
+
+
+/obj/machinery/netpod/mouse_drop_receive(mob/target, mob/user, params)
+ var/mob/living/carbon/player = user
+
+ if(!iscarbon(player) || !is_operational || !state_open || player.buckled)
+ return
+
+ close_machine(target)
+
+
+/obj/machinery/netpod/attack_hand(mob/living/user, list/modifiers)
+ . = ..()
+ if(!state_open && user == occupant)
+ container_resist_act(user)
+
+
+/obj/machinery/netpod/attack_ghost(mob/dead/observer/our_observer)
+ var/our_target = avatar_ref?.resolve()
+ if(isnull(our_target) || !our_observer.orbit(our_target))
+ return ..()
+
+
+/// When the server is upgraded, drops brain damage a little
+/obj/machinery/netpod/proc/on_server_upgraded(obj/machinery/quantum_server/source)
+ SIGNAL_HANDLER
+
+ disconnect_damage = BASE_DISCONNECT_DAMAGE * (1 - source.servo_bonus)
+
+
+#undef BASE_DISCONNECT_DAMAGE
diff --git a/code/modules/bitrunning/netpod/container.dm b/code/modules/bitrunning/netpod/container.dm
new file mode 100644
index 00000000000..61655445445
--- /dev/null
+++ b/code/modules/bitrunning/netpod/container.dm
@@ -0,0 +1,74 @@
+/obj/machinery/netpod/Exited(atom/movable/gone, direction)
+ . = ..()
+ if(!state_open && gone == occupant)
+ container_resist_act(gone)
+
+
+/obj/machinery/netpod/relaymove(mob/living/user, direction)
+ if(!state_open)
+ container_resist_act(user)
+
+
+/obj/machinery/netpod/container_resist_act(mob/living/user)
+ user.visible_message(span_notice("[occupant] emerges from [src]!"),
+ span_notice("You climb out of [src]!"),
+ span_notice("With a hiss, you hear a machine opening."))
+ open_machine()
+
+
+/obj/machinery/netpod/open_machine(drop = TRUE, density_to_set = FALSE)
+ playsound(src, 'sound/machines/tramopen.ogg', 60, TRUE, frequency = 65000)
+ flick("[base_icon_state]_opening", src)
+ SEND_SIGNAL(src, COMSIG_BITRUNNER_NETPOD_OPENED)
+ update_use_power(IDLE_POWER_USE)
+
+ return ..()
+
+
+/obj/machinery/netpod/close_machine(mob/user, density_to_set = TRUE)
+ if(!state_open || panel_open || !is_operational || !iscarbon(user))
+ return
+
+ playsound(src, 'sound/machines/tramclose.ogg', 60, TRUE, frequency = 65000)
+ flick("[base_icon_state]_closing", src)
+ ..()
+
+ enter_matrix()
+
+
+/obj/machinery/netpod/default_pry_open(obj/item/crowbar, mob/living/pryer)
+ if(isnull(occupant) || !iscarbon(occupant))
+ if(!state_open)
+ if(panel_open)
+ return FALSE
+ open_machine()
+ else
+ shut_pod()
+
+ return TRUE
+
+ pryer.visible_message(
+ span_danger("[pryer] starts prying open [src]!"),
+ span_notice("You start to pry open [src]."),
+ span_notice("You hear loud prying on metal.")
+ )
+ playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, TRUE)
+
+ SEND_SIGNAL(src, COMSIG_BITRUNNER_CROWBAR_ALERT, pryer)
+
+ if(do_after(pryer, 15 SECONDS, src))
+ if(!state_open)
+ sever_connection()
+ open_machine()
+
+ return TRUE
+
+
+/// Closes the machine without shoving in an occupant
+/obj/machinery/netpod/proc/shut_pod()
+ state_open = FALSE
+ playsound(src, 'sound/machines/tramclose.ogg', 60, TRUE, frequency = 65000)
+ flick("[base_icon_state]_closing", src)
+ set_density(TRUE)
+
+ update_appearance()
diff --git a/code/modules/bitrunning/netpod/outfitting.dm b/code/modules/bitrunning/netpod/outfitting.dm
new file mode 100644
index 00000000000..07f25154198
--- /dev/null
+++ b/code/modules/bitrunning/netpod/outfitting.dm
@@ -0,0 +1,30 @@
+/// Creates a list of outfit entries for the UI.
+/obj/machinery/netpod/proc/make_outfit_collection(identifier, list/outfit_list)
+ var/list/collection = list(
+ "name" = identifier,
+ "outfits" = list()
+ )
+
+ for(var/datum/outfit/outfit as anything in outfit_list)
+ var/outfit_name = initial(outfit.name)
+ if(findtext(outfit_name, "(") != 0 || findtext(outfit_name, "-") != 0) // No special variants please
+ continue
+
+ collection["outfits"] += list(list("path" = outfit, "name" = outfit_name))
+
+ return list(collection)
+
+
+/// Resolves a path to an outfit.
+/obj/machinery/netpod/proc/resolve_outfit(text)
+ var/path = text2path(text)
+ if(!ispath(path, /datum/outfit))
+ return
+
+ for(var/wardrobe in cached_outfits)
+ for(var/outfit in wardrobe["outfits"])
+ if(path == outfit["path"])
+ return path
+
+ message_admins("[usr]:[usr.ckey] attempted to select an unavailable outfit from a netpod")
+ return
diff --git a/code/modules/bitrunning/netpod/signals.dm b/code/modules/bitrunning/netpod/signals.dm
new file mode 100644
index 00000000000..8f8416aeb7b
--- /dev/null
+++ b/code/modules/bitrunning/netpod/signals.dm
@@ -0,0 +1,64 @@
+/// Machine has been broken - handles signals and reverting sprites
+/obj/machinery/netpod/proc/on_broken(datum/source)
+ SIGNAL_HANDLER
+
+ sever_connection()
+
+
+/// Checks the integrity, alerts occupants
+/obj/machinery/netpod/proc/on_damage_taken(datum/source, damage_amount)
+ SIGNAL_HANDLER
+
+ if(isnull(occupant) || !connected)
+ return
+
+ var/total = max_integrity - damage_amount
+ var/integrity = (atom_integrity / total) * 100
+ if(integrity > 50)
+ return
+
+ SEND_SIGNAL(src, COMSIG_BITRUNNER_NETPOD_INTEGRITY)
+
+
+/// Puts points on the current occupant's card account
+/obj/machinery/netpod/proc/on_domain_complete(datum/source, atom/movable/crate, reward_points)
+ SIGNAL_HANDLER
+
+ if(isnull(occupant) || !connected)
+ return
+
+ var/mob/living/player = occupant
+
+ var/datum/bank_account/account = player.get_bank_account()
+ if(isnull(account))
+ return
+
+ account.bitrunning_points += reward_points * 100
+
+
+/// The domain has been fully purged, so we should double check our avatar is deleted
+/obj/machinery/netpod/proc/on_domain_scrubbed(datum/source)
+ SIGNAL_HANDLER
+
+ var/mob/avatar = avatar_ref?.resolve()
+ if(isnull(avatar))
+ return
+
+ QDEL_NULL(avatar)
+
+
+/// Boots out anyone in the machine && opens it
+/obj/machinery/netpod/proc/on_power_loss(datum/source)
+ SIGNAL_HANDLER
+
+ if(state_open)
+ return
+
+ if(isnull(occupant) || !connected)
+ connected = FALSE
+ open_machine()
+ return
+
+ sever_connection()
+
+
diff --git a/code/modules/bitrunning/netpod/tools.dm b/code/modules/bitrunning/netpod/tools.dm
new file mode 100644
index 00000000000..0d31bdab866
--- /dev/null
+++ b/code/modules/bitrunning/netpod/tools.dm
@@ -0,0 +1,22 @@
+/obj/machinery/netpod/crowbar_act(mob/living/user, obj/item/tool)
+ if(user.combat_mode)
+ attack_hand(user)
+ return ITEM_INTERACT_SUCCESS
+
+ if(default_pry_open(tool, user) || default_deconstruction_crowbar(tool))
+ return ITEM_INTERACT_SUCCESS
+
+
+/obj/machinery/netpod/screwdriver_act(mob/living/user, obj/item/tool)
+ if(occupant)
+ balloon_alert(user, "in use!")
+ return ITEM_INTERACT_SUCCESS
+
+ if(state_open)
+ balloon_alert(user, "close first.")
+ return ITEM_INTERACT_SUCCESS
+
+ if(default_deconstruction_screwdriver(user, "[base_icon_state]_panel", "[base_icon_state]_closed", tool))
+ update_appearance() // sometimes icon doesnt properly update during flick()
+ ui_close(user)
+ return ITEM_INTERACT_SUCCESS
diff --git a/code/modules/bitrunning/netpod/ui.dm b/code/modules/bitrunning/netpod/ui.dm
new file mode 100644
index 00000000000..93719fe64eb
--- /dev/null
+++ b/code/modules/bitrunning/netpod/ui.dm
@@ -0,0 +1,41 @@
+/obj/machinery/netpod/ui_interact(mob/user, datum/tgui/ui)
+ if(!is_operational || occupant)
+ return
+
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "NetpodOutfits")
+ ui.set_autoupdate(FALSE)
+ ui.open()
+
+
+/obj/machinery/netpod/ui_data()
+ var/list/data = list()
+
+ data["netsuit"] = netsuit
+ return data
+
+
+/obj/machinery/netpod/ui_static_data()
+ var/list/data = list()
+
+ if(!length(cached_outfits))
+ cached_outfits += make_outfit_collection("Jobs", subtypesof(/datum/outfit/job))
+
+ data["collections"] = cached_outfits
+
+ return data
+
+
+/obj/machinery/netpod/ui_act(action, params)
+ . = ..()
+ if(.)
+ return TRUE
+ switch(action)
+ if("select_outfit")
+ var/datum/outfit/new_suit = resolve_outfit(params["outfit"])
+ if(new_suit)
+ netsuit = new_suit
+ return TRUE
+
+ return FALSE
diff --git a/code/modules/bitrunning/netpod/utils.dm b/code/modules/bitrunning/netpod/utils.dm
new file mode 100644
index 00000000000..6e0033fe02d
--- /dev/null
+++ b/code/modules/bitrunning/netpod/utils.dm
@@ -0,0 +1,144 @@
+/// Puts the occupant in netpod stasis, basically short-circuiting environmental conditions
+/obj/machinery/netpod/proc/add_healing(mob/living/target)
+ if(target != occupant)
+ return
+
+ target.AddComponent(/datum/component/netpod_healing, pod = src)
+ target.playsound_local(src, 'sound/effects/submerge.ogg', 20, vary = TRUE)
+ target.extinguish_mob()
+ update_use_power(ACTIVE_POWER_USE)
+
+
+/// Disconnects the occupant after a certain time so they aren't just hibernating in netpod stasis. A balance change
+/obj/machinery/netpod/proc/auto_disconnect()
+ if(isnull(occupant) || state_open || connected)
+ return
+
+ var/mob/player = occupant
+ player.playsound_local(src, 'sound/effects/splash.ogg', 60, TRUE)
+ to_chat(player, span_notice("The machine disconnects itself and begins to drain."))
+ open_machine()
+
+
+/// Handles occupant post-disconnection effects like damage, sounds, etc
+/obj/machinery/netpod/proc/disconnect_occupant(cause_damage = FALSE)
+ connected = FALSE
+
+ var/mob/living/mob_occupant = occupant
+ if(isnull(occupant) || mob_occupant.stat == DEAD)
+ open_machine()
+ return
+
+ mob_occupant.playsound_local(src, 'sound/magic/blink.ogg', 25, TRUE)
+ mob_occupant.set_static_vision(2 SECONDS)
+ mob_occupant.set_temp_blindness(1 SECONDS)
+ mob_occupant.Paralyze(2 SECONDS)
+
+ if(!is_operational)
+ open_machine()
+ return
+
+ var/heal_time = 1
+ if(mob_occupant.health < mob_occupant.maxHealth)
+ heal_time = (mob_occupant.stat + 2) * 5
+ addtimer(CALLBACK(src, PROC_REF(auto_disconnect)), heal_time SECONDS, TIMER_UNIQUE|TIMER_STOPPABLE|TIMER_DELETE_ME)
+
+ if(!cause_damage)
+ return
+
+ mob_occupant.flash_act(override_blindness_check = TRUE, visual = TRUE)
+ mob_occupant.adjustOrganLoss(ORGAN_SLOT_BRAIN, disconnect_damage)
+ INVOKE_ASYNC(mob_occupant, TYPE_PROC_REF(/mob/living, emote), "scream")
+ to_chat(mob_occupant, span_danger("You've been forcefully disconnected from your avatar! Your thoughts feel scrambled!"))
+
+
+/**
+ * ### Enter Matrix
+ * Finds any current avatars from this chair - or generates a new one
+ *
+ * New avatars cost 1 attempt, and this will eject if there's none left
+ *
+ * Connects the mind to the avatar if everything is ok
+ */
+/obj/machinery/netpod/proc/enter_matrix()
+ var/mob/living/carbon/human/neo = occupant
+ if(!ishuman(neo) || neo.stat == DEAD || isnull(neo.mind))
+ balloon_alert(neo, "invalid occupant.")
+ return
+
+ var/obj/machinery/quantum_server/server = find_server()
+ if(isnull(server))
+ balloon_alert(neo, "no server connected!")
+ return
+
+ var/datum/lazy_template/virtual_domain/generated_domain = server.generated_domain
+ if(isnull(generated_domain) || !server.is_ready)
+ balloon_alert(neo, "nothing loaded!")
+ return
+
+ var/mob/living/carbon/current_avatar = avatar_ref?.resolve()
+ if(isnull(current_avatar) || current_avatar.stat != CONSCIOUS) // We need a viable avatar
+ current_avatar = server.start_new_connection(neo, netsuit)
+ if(isnull(current_avatar))
+ balloon_alert(neo, "out of bandwidth!")
+ return
+
+ neo.set_static_vision(2 SECONDS)
+ add_healing(occupant)
+
+ if(!validate_entry(neo, current_avatar))
+ open_machine()
+ return
+
+ current_avatar.AddComponent( \
+ /datum/component/avatar_connection, \
+ old_mind = neo.mind, \
+ old_body = neo, \
+ server = server, \
+ pod = src, \
+ help_text = generated_domain.help_text, \
+ )
+
+ connected = TRUE
+
+
+/// Finds a server and sets the server_ref
+/obj/machinery/netpod/proc/find_server()
+ var/obj/machinery/quantum_server/server = server_ref?.resolve()
+ if(server)
+ return server
+
+ server = locate(/obj/machinery/quantum_server) in oview(4, src)
+ if(isnull(server))
+ return
+
+ server_ref = WEAKREF(server)
+ RegisterSignal(server, COMSIG_MACHINERY_REFRESH_PARTS, PROC_REF(on_server_upgraded))
+ RegisterSignal(server, COMSIG_BITRUNNER_DOMAIN_COMPLETE, PROC_REF(on_domain_complete))
+ RegisterSignal(server, COMSIG_BITRUNNER_DOMAIN_SCRUBBED, PROC_REF(on_domain_scrubbed))
+
+ return server
+
+
+/// Severs the connection with the current avatar
+/obj/machinery/netpod/proc/sever_connection()
+ if(isnull(occupant) || !connected)
+ return
+
+ SEND_SIGNAL(src, COMSIG_BITRUNNER_NETPOD_SEVER)
+
+
+/// Checks for cases to eject/fail connecting an avatar
+/obj/machinery/netpod/proc/validate_entry(mob/living/neo, mob/living/avatar)
+ if(!do_after(neo, 2 SECONDS, src))
+ return FALSE
+
+ // Very invalid
+ if(QDELETED(neo) || QDELETED(avatar) || QDELETED(src) || !is_operational)
+ return FALSE
+
+ // Invalid
+ if(occupant != neo || isnull(neo.mind) || neo.stat > SOFT_CRIT || avatar.stat == DEAD)
+ return FALSE
+
+ return TRUE
diff --git a/code/modules/bitrunning/objects/landmarks.dm b/code/modules/bitrunning/objects/landmarks.dm
index 3b38493edff..ea55b96979e 100644
--- a/code/modules/bitrunning/objects/landmarks.dm
+++ b/code/modules/bitrunning/objects/landmarks.dm
@@ -12,6 +12,11 @@
name = "Bitrunning hololadder spawn"
icon_state = "hololadder"
+/// A permanent exit for the domain
+/obj/effect/landmark/bitrunning/permanent_exit
+ name = "Bitrunning permanent exit"
+ icon_state = "perm_exit"
+
/// Where the crates need to be taken
/obj/effect/landmark/bitrunning/cache_goal_turf
name = "Bitrunning goal turf"
diff --git a/code/modules/bitrunning/objects/netpod.dm b/code/modules/bitrunning/objects/netpod.dm
index 2d466410065..e69de29bb2d 100644
--- a/code/modules/bitrunning/objects/netpod.dm
+++ b/code/modules/bitrunning/objects/netpod.dm
@@ -1,498 +0,0 @@
-#define BASE_DISCONNECT_DAMAGE 40
-
-/obj/machinery/netpod
- name = "netpod"
-
- base_icon_state = "netpod"
- circuit = /obj/item/circuitboard/machine/netpod
- desc = "A link to the netverse. It has an assortment of cables to connect yourself to a virtual domain."
- icon = 'icons/obj/machines/bitrunning.dmi'
- icon_state = "netpod"
- max_integrity = 300
- obj_flags = BLOCKS_CONSTRUCTION
- state_open = TRUE
- interaction_flags_mouse_drop = NEED_HANDS | NEED_DEXTERITY
-
- /// Whether we have an ongoing connection
- var/connected = FALSE
- /// A player selected outfit by clicking the netpod
- var/datum/outfit/netsuit = /datum/outfit/job/bitrunner
- /// Holds this to see if it needs to generate a new one
- var/datum/weakref/avatar_ref
- /// The linked quantum server
- var/datum/weakref/server_ref
- /// The amount of brain damage done from force disconnects
- var/disconnect_damage
- /// Static list of outfits to select from
- var/list/cached_outfits = list()
-
-/obj/machinery/netpod/post_machine_initialize()
- . = ..()
-
- disconnect_damage = BASE_DISCONNECT_DAMAGE
- find_server()
-
- RegisterSignal(src, COMSIG_ATOM_TAKE_DAMAGE, PROC_REF(on_damage_taken))
- RegisterSignal(src, COMSIG_MACHINERY_POWER_LOST, PROC_REF(on_power_loss))
- RegisterSignals(src, list(COMSIG_QDELETING, COMSIG_MACHINERY_BROKEN),PROC_REF(on_broken))
-
- register_context()
- update_appearance()
-
-/obj/machinery/netpod/Destroy()
- . = ..()
-
- QDEL_LIST(cached_outfits)
-
-/obj/machinery/netpod/examine(mob/user)
- . = ..()
-
- if(isnull(server_ref?.resolve()))
- . += span_infoplain("It's not connected to anything.")
- . += span_infoplain("Netpods must be built within 4 tiles of a server.")
- return
-
- if(!isobserver(user))
- . += span_infoplain("Drag yourself into the pod to engage the link.")
- . += span_infoplain("It has limited resuscitation capabilities. Remaining in the pod can heal some injuries.")
- . += span_infoplain("It has a security system that will alert the occupant if it is tampered with.")
-
- if(isnull(occupant))
- . += span_infoplain("It's currently unoccupied.")
- return
-
- . += span_infoplain("It's currently occupied by [occupant].")
-
- if(isobserver(user))
- . += span_notice("As an observer, you can click this netpod to jump to its avatar.")
- return
-
- . += span_notice("It can be pried open with a crowbar, but its safety mechanisms will alert the occupant.")
-
-
-
-/obj/machinery/netpod/add_context(atom/source, list/context, obj/item/held_item, mob/user)
- . = ..()
-
- if(isnull(held_item))
- context[SCREENTIP_CONTEXT_LMB] = "Select Outfit"
- return CONTEXTUAL_SCREENTIP_SET
-
- if(istype(held_item, /obj/item/crowbar) && occupant)
- context[SCREENTIP_CONTEXT_LMB] = "Pry Open"
- return CONTEXTUAL_SCREENTIP_SET
-
-
-/obj/machinery/netpod/update_icon_state()
- if(!is_operational)
- icon_state = base_icon_state
- return ..()
-
- if(state_open)
- icon_state = base_icon_state + "_open_active"
- return ..()
-
- if(panel_open)
- icon_state = base_icon_state + "_panel"
- return ..()
-
- icon_state = base_icon_state + "_closed"
- if(occupant)
- icon_state += "_active"
-
- return ..()
-
-/obj/machinery/netpod/mouse_drop_receive(mob/target, mob/user, params)
- var/mob/living/carbon/player = user
-
- if(!iscarbon(player) || !is_operational || !state_open || player.buckled)
- return
-
- close_machine(target)
-
-/obj/machinery/netpod/crowbar_act(mob/living/user, obj/item/tool)
- if(user.combat_mode)
- attack_hand(user)
- return ITEM_INTERACT_SUCCESS
-
- if(default_pry_open(tool, user) || default_deconstruction_crowbar(tool))
- return ITEM_INTERACT_SUCCESS
-
-/obj/machinery/netpod/screwdriver_act(mob/living/user, obj/item/tool)
- if(occupant)
- balloon_alert(user, "in use!")
- return ITEM_INTERACT_SUCCESS
-
- if(state_open)
- balloon_alert(user, "close first.")
- return ITEM_INTERACT_SUCCESS
-
- if(default_deconstruction_screwdriver(user, "[base_icon_state]_panel", "[base_icon_state]_closed", tool))
- update_appearance() // sometimes icon doesnt properly update during flick()
- ui_close(user)
- return ITEM_INTERACT_SUCCESS
-
-/obj/machinery/netpod/attack_hand(mob/living/user, list/modifiers)
- . = ..()
- if(!state_open && user == occupant)
- container_resist_act(user)
-
-/obj/machinery/netpod/Exited(atom/movable/gone, direction)
- . = ..()
- if(!state_open && gone == occupant)
- container_resist_act(gone)
-
-/obj/machinery/netpod/relaymove(mob/living/user, direction)
- if(!state_open)
- container_resist_act(user)
-
-/obj/machinery/netpod/container_resist_act(mob/living/user)
- user.visible_message(span_notice("[occupant] emerges from [src]!"),
- span_notice("You climb out of [src]!"),
- span_notice("With a hiss, you hear a machine opening."))
- open_machine()
-
-/obj/machinery/netpod/open_machine(drop = TRUE, density_to_set = FALSE)
- playsound(src, 'sound/machines/tramopen.ogg', 60, TRUE, frequency = 65000)
- flick("[base_icon_state]_opening", src)
- SEND_SIGNAL(src, COMSIG_BITRUNNER_NETPOD_OPENED)
- update_use_power(IDLE_POWER_USE)
-
- return ..()
-
-/obj/machinery/netpod/close_machine(mob/user, density_to_set = TRUE)
- if(!state_open || panel_open || !is_operational || !iscarbon(user))
- return
-
- playsound(src, 'sound/machines/tramclose.ogg', 60, TRUE, frequency = 65000)
- flick("[base_icon_state]_closing", src)
- ..()
-
- enter_matrix()
-
-/obj/machinery/netpod/default_pry_open(obj/item/crowbar, mob/living/pryer)
- if(isnull(occupant) || !iscarbon(occupant))
- if(!state_open)
- if(panel_open)
- return FALSE
- open_machine()
- else
- shut_pod()
-
- return TRUE
-
- pryer.visible_message(
- span_danger("[pryer] starts prying open [src]!"),
- span_notice("You start to pry open [src]."),
- span_notice("You hear loud prying on metal.")
- )
- playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, TRUE)
-
- SEND_SIGNAL(src, COMSIG_BITRUNNER_CROWBAR_ALERT, pryer)
-
- if(do_after(pryer, 15 SECONDS, src))
- if(!state_open)
- sever_connection()
- open_machine()
-
- return TRUE
-
-/obj/machinery/netpod/ui_interact(mob/user, datum/tgui/ui)
- if(!is_operational || occupant)
- return
-
- ui = SStgui.try_update_ui(user, src, ui)
- if(!ui)
- ui = new(user, src, "NetpodOutfits")
- ui.set_autoupdate(FALSE)
- ui.open()
-
-/obj/machinery/netpod/ui_data()
- var/list/data = list()
-
- data["netsuit"] = netsuit
- return data
-
-/obj/machinery/netpod/ui_static_data()
- var/list/data = list()
-
- if(!length(cached_outfits))
- cached_outfits += make_outfit_collection("Jobs", subtypesof(/datum/outfit/job))
-
- data["collections"] = cached_outfits
-
- return data
-
-/obj/machinery/netpod/ui_act(action, params)
- . = ..()
- if(.)
- return TRUE
- switch(action)
- if("select_outfit")
- var/datum/outfit/new_suit = resolve_outfit(params["outfit"])
- if(new_suit)
- netsuit = new_suit
- return TRUE
-
- return FALSE
-
-/obj/machinery/netpod/attack_ghost(mob/dead/observer/our_observer)
- var/our_target = avatar_ref?.resolve()
- if(isnull(our_target) || !our_observer.orbit(our_target))
- return ..()
-
-/// Puts the occupant in netpod stasis, basically short-circuiting environmental conditions
-/obj/machinery/netpod/proc/add_healing(mob/living/target)
- if(target != occupant)
- return
-
- target.AddComponent(/datum/component/netpod_healing, pod = src)
- target.playsound_local(src, 'sound/effects/submerge.ogg', 20, vary = TRUE)
- target.extinguish_mob()
- update_use_power(ACTIVE_POWER_USE)
-
-/// Disconnects the occupant after a certain time so they aren't just hibernating in netpod stasis. A balance change
-/obj/machinery/netpod/proc/auto_disconnect()
- if(isnull(occupant) || state_open || connected)
- return
-
- var/mob/player = occupant
- player.playsound_local(src, 'sound/effects/splash.ogg', 60, TRUE)
- to_chat(player, span_notice("The machine disconnects itself and begins to drain."))
- open_machine()
-
-/// Handles occupant post-disconnection effects like damage, sounds, etc
-/obj/machinery/netpod/proc/disconnect_occupant(cause_damage = FALSE)
- connected = FALSE
-
- var/mob/living/mob_occupant = occupant
- if(isnull(occupant) || mob_occupant.stat == DEAD)
- open_machine()
- return
-
- mob_occupant.playsound_local(src, 'sound/magic/blink.ogg', 25, TRUE)
- mob_occupant.set_static_vision(2 SECONDS)
- mob_occupant.set_temp_blindness(1 SECONDS)
- mob_occupant.Paralyze(2 SECONDS)
-
- if(!is_operational)
- open_machine()
- return
-
- var/heal_time = 1
- if(mob_occupant.health < mob_occupant.maxHealth)
- heal_time = (mob_occupant.stat + 2) * 5
- addtimer(CALLBACK(src, PROC_REF(auto_disconnect)), heal_time SECONDS, TIMER_UNIQUE|TIMER_STOPPABLE|TIMER_DELETE_ME)
-
- if(!cause_damage)
- return
-
- mob_occupant.flash_act(override_blindness_check = TRUE, visual = TRUE)
- mob_occupant.adjustOrganLoss(ORGAN_SLOT_BRAIN, disconnect_damage)
- INVOKE_ASYNC(mob_occupant, TYPE_PROC_REF(/mob/living, emote), "scream")
- to_chat(mob_occupant, span_danger("You've been forcefully disconnected from your avatar! Your thoughts feel scrambled!"))
-
-/**
- * ### Enter Matrix
- * Finds any current avatars from this chair - or generates a new one
- *
- * New avatars cost 1 attempt, and this will eject if there's none left
- *
- * Connects the mind to the avatar if everything is ok
- */
-/obj/machinery/netpod/proc/enter_matrix()
- var/mob/living/carbon/human/neo = occupant
- if(!ishuman(neo) || neo.stat == DEAD || isnull(neo.mind))
- balloon_alert(neo, "invalid occupant.")
- return
-
- var/obj/machinery/quantum_server/server = find_server()
- if(isnull(server))
- balloon_alert(neo, "no server connected!")
- return
-
- var/datum/lazy_template/virtual_domain/generated_domain = server.generated_domain
- if(isnull(generated_domain) || !server.is_ready)
- balloon_alert(neo, "nothing loaded!")
- return
-
- var/mob/living/carbon/current_avatar = avatar_ref?.resolve()
- if(isnull(current_avatar) || current_avatar.stat != CONSCIOUS) // We need a viable avatar
- var/obj/structure/hololadder/wayout = server.generate_hololadder()
- if(isnull(wayout))
- balloon_alert(neo, "out of bandwidth!")
- return
- // BUBBER EDIT BEGIN - PREFS!
- var/datum/preferences/pref
- var/load_loadout = FALSE
- var/obj/item/bitrunning_disk/prefs/prefdisk = locate() in neo.get_contents()
- if(prefdisk)
- load_loadout = prefdisk.include_loadout
- pref = prefdisk.loaded_preference
- current_avatar = server.generate_avatar(wayout, netsuit, pref, include_loadout = load_loadout) // Added the prefs argument
- // BUBBER EDIT END
- avatar_ref = WEAKREF(current_avatar)
- server.stock_gear(current_avatar, neo, generated_domain)
-
- neo.set_static_vision(3 SECONDS)
- add_healing(occupant)
-
- if(!validate_entry(neo, current_avatar))
- open_machine()
- return
-
- current_avatar.AddComponent( \
- /datum/component/avatar_connection, \
- old_mind = neo.mind, \
- old_body = neo, \
- server = server, \
- pod = src, \
- help_text = generated_domain.help_text, \
- )
-
- connected = TRUE
-
-/// Finds a server and sets the server_ref
-/obj/machinery/netpod/proc/find_server()
- var/obj/machinery/quantum_server/server = server_ref?.resolve()
- if(server)
- return server
-
- server = locate(/obj/machinery/quantum_server) in oview(4, src)
- if(isnull(server))
- return
-
- server_ref = WEAKREF(server)
- RegisterSignal(server, COMSIG_MACHINERY_REFRESH_PARTS, PROC_REF(on_server_upgraded))
- RegisterSignal(server, COMSIG_BITRUNNER_DOMAIN_COMPLETE, PROC_REF(on_domain_complete))
- RegisterSignal(server, COMSIG_BITRUNNER_DOMAIN_SCRUBBED, PROC_REF(on_domain_scrubbed))
-
- return server
-
-/// Creates a list of outfit entries for the UI.
-/obj/machinery/netpod/proc/make_outfit_collection(identifier, list/outfit_list)
- var/list/collection = list(
- "name" = identifier,
- "outfits" = list()
- )
-
- for(var/datum/outfit/outfit as anything in outfit_list)
- var/outfit_name = initial(outfit.name)
- if(findtext(outfit_name, "(") != 0 || findtext(outfit_name, "-") != 0) // No special variants please
- continue
-
- collection["outfits"] += list(list("path" = outfit, "name" = outfit_name))
-
- return list(collection)
-
-/// Machine has been broken - handles signals and reverting sprites
-/obj/machinery/netpod/proc/on_broken(datum/source)
- SIGNAL_HANDLER
-
- sever_connection()
-
-/// Checks the integrity, alerts occupants
-/obj/machinery/netpod/proc/on_damage_taken(datum/source, damage_amount)
- SIGNAL_HANDLER
-
- if(isnull(occupant) || !connected)
- return
-
- var/total = max_integrity - damage_amount
- var/integrity = (atom_integrity / total) * 100
- if(integrity > 50)
- return
-
- SEND_SIGNAL(src, COMSIG_BITRUNNER_NETPOD_INTEGRITY)
-
-/// Puts points on the current occupant's card account
-/obj/machinery/netpod/proc/on_domain_complete(datum/source, atom/movable/crate, reward_points)
- SIGNAL_HANDLER
-
- if(isnull(occupant) || !connected)
- return
-
- var/mob/living/player = occupant
-
- var/datum/bank_account/account = player.get_bank_account()
- if(isnull(account))
- return
-
- account.bitrunning_points += reward_points * 100
-
-/// The domain has been fully purged, so we should double check our avatar is deleted
-/obj/machinery/netpod/proc/on_domain_scrubbed(datum/source)
- SIGNAL_HANDLER
-
- var/mob/avatar = avatar_ref?.resolve()
- if(isnull(avatar))
- return
-
- QDEL_NULL(avatar)
-
-/// Boots out anyone in the machine && opens it
-/obj/machinery/netpod/proc/on_power_loss(datum/source)
- SIGNAL_HANDLER
-
- if(state_open)
- return
-
- if(isnull(occupant) || !connected)
- connected = FALSE
- open_machine()
- return
-
- sever_connection()
-
-/// When the server is upgraded, drops brain damage a little
-/obj/machinery/netpod/proc/on_server_upgraded(obj/machinery/quantum_server/source)
- SIGNAL_HANDLER
-
- disconnect_damage = BASE_DISCONNECT_DAMAGE * (1 - source.servo_bonus)
-
-/// Resolves a path to an outfit.
-/obj/machinery/netpod/proc/resolve_outfit(text)
- var/path = text2path(text)
- if(!ispath(path, /datum/outfit))
- return
-
- for(var/wardrobe in cached_outfits)
- for(var/outfit in wardrobe["outfits"])
- if(path == outfit["path"])
- return path
-
- message_admins("[usr]:[usr.ckey] attempted to select an unavailable outfit from a netpod")
- return
-
-/// Severs the connection with the current avatar
-/obj/machinery/netpod/proc/sever_connection()
- if(isnull(occupant) || !connected)
- return
-
- SEND_SIGNAL(src, COMSIG_BITRUNNER_NETPOD_SEVER)
-
-/// Closes the machine without shoving in an occupant
-/obj/machinery/netpod/proc/shut_pod()
- state_open = FALSE
- playsound(src, 'sound/machines/tramclose.ogg', 60, TRUE, frequency = 65000)
- flick("[base_icon_state]_closing", src)
- set_density(TRUE)
-
- update_appearance()
-
-/// Checks for cases to eject/fail connecting an avatar
-/obj/machinery/netpod/proc/validate_entry(mob/living/neo, mob/living/avatar)
- if(!do_after(neo, 2 SECONDS, src))
- return FALSE
-
- // Very invalid
- if(QDELETED(neo) || QDELETED(avatar) || QDELETED(src) || !is_operational)
- return FALSE
-
- // Invalid
- if(occupant != neo || isnull(neo.mind) || neo.stat > SOFT_CRIT || avatar.stat == DEAD)
- return FALSE
-
- return TRUE
-
-#undef BASE_DISCONNECT_DAMAGE
diff --git a/code/modules/bitrunning/outfits.dm b/code/modules/bitrunning/outfits.dm
index 41a65b228ff..c0bb01ebc06 100644
--- a/code/modules/bitrunning/outfits.dm
+++ b/code/modules/bitrunning/outfits.dm
@@ -10,12 +10,62 @@
suit = /obj/item/clothing/suit/jacket/trenchcoat
id = /obj/item/card/id/advanced
+
/datum/outfit/echolocator/post_equip(mob/living/carbon/human/user, visualsOnly)
. = ..()
user.psykerize()
+
/datum/outfit/bitductor
name = "Bitrunning Abductor"
uniform = /obj/item/clothing/under/abductor
gloves = /obj/item/clothing/gloves/fingerless
shoes = /obj/item/clothing/shoes/jackboots
+
+
+/datum/outfit/beachbum_combat
+ name = "Beachbum: Island Combat"
+ id = /obj/item/card/id/advanced
+ l_pocket = null
+ r_pocket = null
+ shoes = /obj/item/clothing/shoes/sandal
+ uniform = /obj/item/clothing/under/pants/jeans
+ /// Available ranged weapons
+ var/list/ranged_weaps = list(
+ /obj/item/gun/ballistic/automatic/pistol,
+ /obj/item/gun/ballistic/rifle/boltaction,
+ /obj/item/gun/ballistic/automatic/mini_uzi,
+ /obj/item/gun/ballistic/automatic/pistol/deagle,
+ /obj/item/gun/ballistic/rocketlauncher/unrestricted,
+ /obj/item/gun/ballistic/automatic/ar,
+
+ )
+ /// Corresponding ammo
+ var/list/corresponding_ammo = list(
+ /obj/item/ammo_box/magazine/m9mm,
+ /obj/item/ammo_box/strilka310,
+ /obj/item/ammo_box/magazine/uzim9mm,
+ /obj/item/ammo_box/magazine/m50,
+ /obj/item/food/pizzaslice/dank, // more silly, less destructive
+ /obj/item/ammo_box/magazine/m223,
+ )
+
+
+/datum/outfit/beachbum_combat/post_equip(mob/living/carbon/human/bum, visualsOnly)
+ . = ..()
+
+ var/choice = rand(1, length(ranged_weaps))
+ var/weapon = ranged_weaps[choice]
+ bum.put_in_active_hand(new weapon)
+
+ var/ammo = corresponding_ammo[choice]
+ var/obj/item/ammo1 = new ammo
+ var/obj/item/ammo2 = new ammo
+
+ if(!bum.equip_to_slot_if_possible(new ammo, ITEM_SLOT_LPOCKET))
+ ammo1.forceMove(get_turf(bum))
+ if(!bum.equip_to_slot_if_possible(new ammo, ITEM_SLOT_RPOCKET))
+ ammo2.forceMove(get_turf(bum))
+
+ if(prob(50))
+ bum.equip_to_slot_if_possible(new /obj/item/clothing/glasses/sunglasses, ITEM_SLOT_EYES)
diff --git a/code/modules/bitrunning/server/_parent.dm b/code/modules/bitrunning/server/_parent.dm
index 8a8d2eed913..541d36ad5d7 100644
--- a/code/modules/bitrunning/server/_parent.dm
+++ b/code/modules/bitrunning/server/_parent.dm
@@ -77,12 +77,18 @@
. += span_infoplain("Can be resource intensive to run. Ensure adequate power supply.")
+ var/upgraded = FALSE
if(capacitor_coefficient < 1)
. += span_infoplain("- Its coolant capacity reduces cooldown time by [(1 - capacitor_coefficient) * 100]%.")
+ upgraded = TRUE
if(servo_bonus > 0.2)
. += span_infoplain("- Its manipulation potential is increasing rewards by [servo_bonus]x.")
. += span_infoplain("- Injury from unsafe ejection reduced [servo_bonus * 100]%.")
+ upgraded = TRUE
+
+ if(!upgraded)
+ . += span_notice("Its output is suboptimal. Improved components will grant domain information, reduce cooldowns and increase rewards.")
if(!is_ready)
. += span_notice("It is currently cooling down. Give it a few moments.")
diff --git a/code/modules/bitrunning/server/loot.dm b/code/modules/bitrunning/server/loot.dm
index 83d26daa607..0e6ff30cd24 100644
--- a/code/modules/bitrunning/server/loot.dm
+++ b/code/modules/bitrunning/server/loot.dm
@@ -4,6 +4,7 @@
#define GRADE_A "A"
#define GRADE_S "S"
+
/// Handles calculating rewards based on number of players, parts, threats, etc
/obj/machinery/quantum_server/proc/calculate_rewards()
var/rewards_base = 0.8
@@ -20,6 +21,7 @@
return rewards_base
+
/// Handles spawning the (new) crate and deleting the former
/obj/machinery/quantum_server/proc/generate_loot(obj/cache, obj/machinery/byteforge/chosen_forge)
SSblackbox.record_feedback("tally", "bitrunning_domain_primary_completed", 1, generated_domain.key)
@@ -55,6 +57,8 @@
chosen_forge.start_to_spawn(reward_cache)
return TRUE
+
+/// Builds secondary loot if the achievements were met
/obj/machinery/quantum_server/proc/generate_secondary_loot(obj/curiosity, obj/machinery/byteforge/chosen_forge)
SSblackbox.record_feedback("tally", "bitrunning_domain_secondary_completed", 1, generated_domain.key)
spark_at_location(curiosity) // abracadabra!
@@ -65,6 +69,7 @@
chosen_forge.start_to_spawn(reward_curiosity)
return TRUE
+
/// Returns the markdown text containing domain completion information
/obj/machinery/quantum_server/proc/get_completion_certificate(time_difference, grade)
var/base_points = generated_domain.reward_points
@@ -126,6 +131,7 @@
return generated_domain.difficulty >= BITRUNNER_DIFFICULTY_MEDIUM && (grade in passing_grades)
+
/// Grades the player's run based on several factors
/obj/machinery/quantum_server/proc/grade_completion(completion_time)
var/score = length(spawned_threat_refs) * 5
diff --git a/code/modules/bitrunning/server/map_handling.dm b/code/modules/bitrunning/server/map_handling.dm
index 5bfa7813910..f98332f7e8d 100644
--- a/code/modules/bitrunning/server/map_handling.dm
+++ b/code/modules/bitrunning/server/map_handling.dm
@@ -24,6 +24,7 @@
reset()
+
/// Links all the loading processes together - does validation for booting a map
/obj/machinery/quantum_server/proc/cold_boot_map(map_key)
if(!is_ready)
@@ -69,8 +70,15 @@
if(broadcasting)
start_broadcasting_network(BITRUNNER_CAMERA_NET)
+ if(generated_domain.announce_to_ghosts)
+ notify_ghosts("Bitrunners have loaded a domain that offers ghost interactions. Check the spawners menu for more information.",
+ src,
+ "Matrix Glitch",
+ )
+
return TRUE
+
/// Initializes a new domain if the given key is valid and the user has enough points
/obj/machinery/quantum_server/proc/load_domain(map_key)
for(var/datum/lazy_template/virtual_domain/available in SSbitrunning.all_domains)
@@ -82,6 +90,7 @@
return FALSE
+
/// Loads in necessary map items like hololadder spawns, caches, etc
/obj/machinery/quantum_server/proc/load_map_items()
var/turf/goal_turfs = list()
@@ -116,6 +125,15 @@
var/turf/signaler_turf = get_turf(thing)
signaler_turf.AddComponent(/datum/component/bitrunning_points, generated_domain)
qdel(thing)
+ continue
+
+ if(istype(thing, /obj/effect/landmark/bitrunning/permanent_exit))
+ var/turf/tile = get_turf(thing)
+ exit_turfs += tile
+ qdel(thing)
+
+ new /obj/structure/hololadder(tile)
+
if(!length(exit_turfs))
CRASH("Failed to find exit turfs on generated domain.")
@@ -134,6 +152,7 @@
return TRUE
+
/// Stops the current virtual domain and disconnects all users
/obj/machinery/quantum_server/proc/reset(fast = FALSE)
is_ready = FALSE
@@ -155,6 +174,7 @@
stop_broadcasting_network(BITRUNNER_CAMERA_NET)
+
/// Tries to clean up everything in the domain
/obj/machinery/quantum_server/proc/scrub_vdom()
sever_connections() /// just in case someone's connected
diff --git a/code/modules/bitrunning/server/obj_generation.dm b/code/modules/bitrunning/server/obj_generation.dm
index d89aba25c8c..9c299dfbf64 100644
--- a/code/modules/bitrunning/server/obj_generation.dm
+++ b/code/modules/bitrunning/server/obj_generation.dm
@@ -15,6 +15,7 @@
new /obj/structure/closet/crate/secure/bitrunning/encrypted(chosen_turf)
return TRUE
+
/// Attempts to spawn a lootbox
/obj/machinery/quantum_server/proc/attempt_spawn_curiosity(list/possible_turfs)
if(!length(possible_turfs)) // Out of turfs to place a curiosity
@@ -35,9 +36,10 @@
new /obj/item/storage/lockbox/bitrunning/encrypted(chosen_turf)
return chosen_turf
+
/// Generates a new avatar for the bitrunner.
-/obj/machinery/quantum_server/proc/generate_avatar(obj/structure/hololadder/wayout, datum/outfit/netsuit, datum/preferences/prefs, include_loadout = FALSE) // BUBBER EDIT - Prefs argument
- var/mob/living/carbon/human/avatar = new(wayout.loc)
+/obj/machinery/quantum_server/proc/generate_avatar(turf/destination, datum/outfit/netsuit, datum/preferences/prefs) // BUBBER EDIT - Prefs argument
+ var/mob/living/carbon/human/avatar = new(destination)
// BUBBER EDIT BEGIN - PREFS!
if(!isnull(prefs))
@@ -66,8 +68,9 @@
if(istype(hat))
hat.set_armor(/datum/armor/none)
- for(var/obj/thing in avatar.held_items)
- qdel(thing)
+ if(!generated_domain.forced_outfit)
+ for(var/obj/thing in avatar.held_items)
+ qdel(thing)
var/obj/item/storage/backpack/bag = avatar.back
if(istype(bag))
@@ -79,8 +82,6 @@
new /obj/item/flashlight,
)
- if(include_loadout)
- avatar.equip_outfit_and_loadout(new /datum/outfit(), prefs) // BUBBER EDIT - LOADOUTS
var/obj/item/card/id/outfit_id = avatar.wear_id
if(outfit_id)
outfit_id.registered_account = new()
@@ -95,32 +96,9 @@
network = BITRUNNER_CAMERA_NET, \
emp_proof = TRUE, \
)
+
return avatar
-/// Generates a new hololadder for the bitrunner. Effectively a respawn attempt.
-/obj/machinery/quantum_server/proc/generate_hololadder()
- if(!length(exit_turfs))
- return
-
- if(retries_spent >= length(exit_turfs))
- return
-
- var/turf/destination
- for(var/turf/dest_turf in exit_turfs)
- if(!locate(/obj/structure/hololadder) in dest_turf)
- destination = dest_turf
- break
-
- if(isnull(destination))
- return
-
- var/obj/structure/hololadder/wayout = new(destination, src)
- if(isnull(wayout))
- return
-
- retries_spent += 1
-
- return wayout
/// Loads in any mob segments of the map
/obj/machinery/quantum_server/proc/load_mob_segments()
@@ -149,6 +127,7 @@
return TRUE
+
/// Scans over neo's contents for bitrunning tech disks. Loads the items or abilities onto the avatar.
/obj/machinery/quantum_server/proc/stock_gear(mob/living/carbon/human/avatar, mob/living/carbon/human/neo, datum/lazy_template/virtual_domain/generated_domain)
var/domain_forbids_items = generated_domain.forbids_disk_items
diff --git a/code/modules/bitrunning/server/signal_handlers.dm b/code/modules/bitrunning/server/signal_handlers.dm
index 0e5e949e8bb..f5d2840143e 100644
--- a/code/modules/bitrunning/server/signal_handlers.dm
+++ b/code/modules/bitrunning/server/signal_handlers.dm
@@ -4,12 +4,14 @@
sever_connections()
+
/// Whenever a corpse spawner makes a new corpse, add it to the list of potential mutations
/obj/machinery/quantum_server/proc/on_corpse_spawned(datum/source, mob/living/corpse)
SIGNAL_HANDLER
mutation_candidate_refs.Add(WEAKREF(corpse))
+
/// Being qdeleted - make sure the circuit and connected mobs go with it
/obj/machinery/quantum_server/proc/on_delete(datum/source)
SIGNAL_HANDLER
@@ -26,6 +28,7 @@
if(circuit)
qdel(circuit)
+
/// Whenever something enters the send tiles, check if it's a loot crate. If so, alert players.
/obj/machinery/quantum_server/proc/on_goal_turf_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
SIGNAL_HANDLER
@@ -51,6 +54,7 @@
generate_secondary_loot(arrived, chosen_forge, generated_domain)
return
+
/// Handles examining the server. Shows cooldown time and efficiency.
/obj/machinery/quantum_server/proc/on_goal_turf_examined(datum/source, mob/examiner, list/examine_text)
SIGNAL_HANDLER
@@ -58,6 +62,7 @@
examine_text += span_info("Beneath your gaze, the floor pulses subtly with streams of encoded data.")
examine_text += span_info("It seems to be part of the location designated for retrieving encrypted payloads.")
+
/// Scans over the inbound created_atoms from lazy templates
/obj/machinery/quantum_server/proc/on_template_loaded(datum/lazy_template/source, list/created_atoms)
SIGNAL_HANDLER
@@ -98,6 +103,7 @@
/// Just in case there's any special handling for the domain
generated_domain.setup_domain(created_atoms)
+
/// Handles when cybercops are summoned into the area or ghosts click a ghost role spawner
/obj/machinery/quantum_server/proc/on_threat_created(datum/source, mob/living/threat)
SIGNAL_HANDLER
diff --git a/code/modules/bitrunning/server/threats.dm b/code/modules/bitrunning/server/threats.dm
index 4416636206c..0819c68f081 100644
--- a/code/modules/bitrunning/server/threats.dm
+++ b/code/modules/bitrunning/server/threats.dm
@@ -4,6 +4,7 @@
SEND_SIGNAL(src, COMSIG_BITRUNNER_THREAT_CREATED)
threat.AddComponent(/datum/component/virtual_entity, src)
+
/// Choses which antagonist role is spawned based on threat
/obj/machinery/quantum_server/proc/get_antagonist_role()
var/list/available = list()
@@ -19,6 +20,7 @@
return chosen
+
/// Selects a target to mutate. Gives two attempts, then crashes if it fails.
/obj/machinery/quantum_server/proc/get_mutation_target()
var/datum/weakref/target_ref = pick(mutation_candidate_refs)
@@ -35,6 +37,7 @@
resolved = target_ref.resolve()
return resolved
+
/// Finds any mobs with minds in the zones and gives them the bad news
/obj/machinery/quantum_server/proc/notify_spawned_threats()
for(var/datum/weakref/baddie_ref as anything in spawned_threat_refs)
@@ -52,10 +55,12 @@
to_chat(baddie, span_userdanger("You have been flagged for deletion! Thank you for your service."))
+
/// Removes a specific threat - used when station spawning
/obj/machinery/quantum_server/proc/remove_threat(mob/living/threat)
spawned_threat_refs.Remove(WEAKREF(threat))
+
/// Selects the role and waits for a ghost orbiter
/obj/machinery/quantum_server/proc/setup_glitch(datum/antagonist/bitrunning_glitch/forced_role)
if(!validate_mutation_candidates())
@@ -83,6 +88,7 @@
spawn_glitch(chosen_role, mutation_target, chosen_one)
return mutation_target
+
/// Orbit poll has concluded - spawn the antag
/obj/machinery/quantum_server/proc/spawn_glitch(datum/antagonist/bitrunning_glitch/chosen_role, mob/living/mutation_target, mob/dead/observer/ghost)
if(QDELETED(mutation_target))
@@ -121,6 +127,7 @@
add_threats(new_mob)
+
/// Oh boy - transports the antag station side
/obj/machinery/quantum_server/proc/station_spawn(mob/living/antag, obj/machinery/byteforge/chosen_forge)
antag.balloon_alert(antag, "scanning...")
@@ -165,6 +172,7 @@
do_teleport(antag, get_turf(chosen_forge), forced = TRUE, asoundin = 'sound/magic/ethereal_enter.ogg', asoundout = 'sound/magic/ethereal_exit.ogg', channel = TELEPORT_CHANNEL_QUANTUM)
+
/// Removes any invalid candidates from the list
/obj/machinery/quantum_server/proc/validate_mutation_candidates()
for(var/datum/weakref/creature_ref as anything in mutation_candidate_refs)
diff --git a/code/modules/bitrunning/server/util.dm b/code/modules/bitrunning/server/util.dm
index 1ceeab8c48d..4d275105a6f 100644
--- a/code/modules/bitrunning/server/util.dm
+++ b/code/modules/bitrunning/server/util.dm
@@ -1,5 +1,6 @@
#define MAX_DISTANCE 4 // How far crates can spawn from the server
+
/// Resets the cooldown state and updates icons
/obj/machinery/quantum_server/proc/cool_off()
is_ready = TRUE
@@ -13,6 +14,7 @@
//BUBBER ADDITION END
radio.talk_into(src, "Thermal systems within operational parameters. Proceeding to domain configuration.", RADIO_CHANNEL_SUPPLY)
+
/// If there are hosted minds, attempts to get a list of their current virtual bodies w/ vitals
/obj/machinery/quantum_server/proc/get_avatar_data()
var/list/hosted_avatars = list()
@@ -38,6 +40,49 @@
return hosted_avatars
+
+/// I grab the atom here so I can signal it / manipulate spawners etc
+/obj/machinery/quantum_server/proc/get_avatar_destination() as /atom
+ // Branch A: Custom spawns
+ if(length(generated_domain.custom_spawns))
+ var/atom/valid_spawner
+
+ while(isnull(valid_spawner))
+ var/atom/chosen = pick(generated_domain.custom_spawns)
+ if(QDELETED(chosen))
+ generated_domain.custom_spawns -= chosen
+ continue
+
+ valid_spawner = chosen
+ break
+
+ return valid_spawner
+
+ // Branch B: Hololadders
+ if(!length(exit_turfs))
+ return
+
+ if(retries_spent >= length(exit_turfs))
+ return
+
+ var/turf/exit_tile
+ for(var/turf/dest_turf in exit_turfs)
+ if(!locate(/obj/structure/hololadder) in dest_turf)
+ exit_tile = dest_turf
+ break
+
+ if(isnull(exit_tile))
+ return
+
+ var/obj/structure/hololadder/wayout = new(exit_tile, src)
+ if(isnull(wayout))
+ return
+
+ retries_spent += 1
+
+ return wayout
+
+
/// Locates any turfs with forges on them, returns a random one
/obj/machinery/quantum_server/proc/get_random_nearby_forge()
var/list/nearby_forges = list()
@@ -47,6 +92,7 @@
return pick(nearby_forges)
+
/// Gets a random available domain given the current points.
/obj/machinery/quantum_server/proc/get_random_domain_id()
if(points < 1)
@@ -92,6 +138,7 @@
SEND_SIGNAL(src, COMSIG_BITRUNNER_QSRV_SEVER)
+
/// Do some magic teleport sparks
/obj/machinery/quantum_server/proc/spark_at_location(obj/cache)
playsound(cache, 'sound/magic/blink.ogg', 50, vary = TRUE)
@@ -99,16 +146,33 @@
sparks.set_up(5, location = get_turf(cache))
sparks.start()
-/// Returns a turf if it's not dense, else will find a neighbor.
-/obj/machinery/quantum_server/proc/validate_turf(turf/chosen_turf)
- if(!chosen_turf.is_blocked_turf())
- return chosen_turf
- for(var/turf/tile in get_adjacent_open_turfs(chosen_turf))
- if(!tile.is_blocked_turf())
- return chosen_turf
+/// Starts building a new avatar for the player.
+/// Called by netpods when they don't have a current avatar.
+/// This is a procedural proc which links several others together.
+/obj/machinery/quantum_server/proc/start_new_connection(mob/living/carbon/human/neo, datum/outfit/netsuit) as /mob/living/carbon/human
+ var/atom/entry_atom = get_avatar_destination()
+ if(isnull(entry_atom))
+ return
+
+ var/mob/living/carbon/new_avatar = generate_avatar(get_turf(entry_atom), netsuit)
+ stock_gear(new_avatar, neo, generated_domain)
+
+ // Cleanup for domains with one time use custom spawns
+ if(!length(generated_domain.custom_spawns))
+ return new_avatar
+
+ // If we're spawning from some other fuckery, no need for this
+ if(istype(entry_atom, /obj/effect/mob_spawn/ghost_role/human/virtual_domain))
+ var/obj/effect/mob_spawn/ghost_role/human/virtual_domain/spawner = entry_atom
+ spawner.artificial_spawn(new_avatar)
+
+ if(!generated_domain.keep_custom_spawns)
+ generated_domain.custom_spawns -= entry_atom
+ qdel(entry_atom)
+
+ return new_avatar
-#undef MAX_DISTANCE
/// Toggles broadcast on and off
/obj/machinery/quantum_server/proc/toggle_broadcast()
@@ -123,3 +187,16 @@
// And we only flip TVs when there's a domain, because otherwise there's no cams to watch
set_network_broadcast_status(BITRUNNER_CAMERA_NET, broadcasting)
return TRUE
+
+
+/// Returns a turf if it's not dense, else will find a neighbor.
+/obj/machinery/quantum_server/proc/validate_turf(turf/chosen_turf)
+ if(!chosen_turf.is_blocked_turf())
+ return chosen_turf
+
+ for(var/turf/tile in get_adjacent_open_turfs(chosen_turf))
+ if(!tile.is_blocked_turf())
+ return chosen_turf
+
+
+#undef MAX_DISTANCE
diff --git a/code/modules/bitrunning/spawners.dm b/code/modules/bitrunning/spawners.dm
index 5fa889ac655..4b5f79a43b1 100644
--- a/code/modules/bitrunning/spawners.dm
+++ b/code/modules/bitrunning/spawners.dm
@@ -3,12 +3,9 @@
prompt_name = "a virtual domain debug entity"
flavour_text = "You probably shouldn't be seeing this, contact a coder!"
you_are_text = "You are NOT supposed to be here. How did you let this happen?"
- important_text = "You must eliminate any bitrunners from the domain."
+ important_text = "Bitrunning is a crime, and your primary threat."
temp_body = TRUE
-/obj/effect/mob_spawn/ghost_role/human/virtual_domain/Initialize(mapload)
- . = ..()
- notify_ghosts("The [name] has been created. The virtual world calls for aid!", src, "Virtual Insanity!")
/obj/effect/mob_spawn/ghost_role/human/virtual_domain/special(mob/living/spawned_mob, mob/mob_possessor)
var/datum/mind/ghost_mind = mob_possessor.mind
@@ -19,6 +16,12 @@
spawned_mob.mind.add_antag_datum(/datum/antagonist/domain_ghost_actor)
+
+/// Simulates a ghost role spawn without calling special(), ie a bitrunner spawn instead of a ghost.
+/obj/effect/mob_spawn/ghost_role/human/virtual_domain/proc/artificial_spawn(mob/living/runner)
+ SEND_SIGNAL(src, COMSIG_BITRUNNER_SPAWNED, runner)
+
+
/obj/effect/mob_spawn/ghost_role/human/virtual_domain/pirate
name = "Virtual Pirate Remains"
desc = "Some inanimate bones. They feel like they could spring to life at any moment!"
@@ -27,22 +30,25 @@
icon_state = "remains"
prompt_name = "a virtual skeleton pirate"
you_are_text = "You are a virtual pirate. Yarrr!"
- flavour_text = "You have awoken, without instruction. There's a LANDLUBBER after yer booty. Stop them!"
+ flavour_text = " There's a LANDLUBBER after yer booty. Stop them!"
+
/obj/effect/mob_spawn/ghost_role/human/virtual_domain/pirate/special(mob/living/spawned_mob, mob/mob_possessor)
. = ..()
spawned_mob.fully_replace_character_name(spawned_mob.real_name, "[pick(strings(PIRATE_NAMES_FILE, "generic_beginnings"))][pick(strings(PIRATE_NAMES_FILE, "generic_endings"))]")
+
/obj/effect/mob_spawn/ghost_role/human/virtual_domain/syndie
name = "Virtual Syndicate Sleeper"
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper_s"
prompt_name = "a virtual syndicate operative"
you_are_text = "You are a virtual syndicate operative."
- flavour_text = "You have awoken, without instruction. Alarms blare! We are being boarded!"
+ flavour_text = "Alarms blare! We are being boarded!"
outfit = /datum/outfit/virtual_syndicate
spawner_job_path = /datum/job/space_syndicate
+
/datum/outfit/virtual_syndicate
name = "Virtual Syndie"
id = /obj/item/card/id/advanced/chameleon
@@ -53,5 +59,6 @@
shoes = /obj/item/clothing/shoes/combat
implants = list(/obj/item/implant/weapons_auth)
+
/datum/outfit/virtual_syndicate/post_equip(mob/living/carbon/human/user, visualsOnly)
user.faction |= ROLE_SYNDICATE
diff --git a/code/modules/bitrunning/virtual_domain/domains/island_brawl.dm b/code/modules/bitrunning/virtual_domain/domains/island_brawl.dm
new file mode 100644
index 00000000000..b745a4746aa
--- /dev/null
+++ b/code/modules/bitrunning/virtual_domain/domains/island_brawl.dm
@@ -0,0 +1,46 @@
+/datum/lazy_template/virtual_domain/island_brawl
+ name = "Island Brawl"
+ announce_to_ghosts = TRUE
+ cost = BITRUNNER_COST_HIGH
+ desc = "A 'peaceful' island tucked away in the middle of nowhere. This map will auto-complete after a number of deaths have occurred."
+ difficulty = BITRUNNER_DIFFICULTY_HIGH
+ forced_outfit = /datum/outfit/beachbum_combat
+ help_text = "There may be bounties laid out across the island, but the primary objective is to survive. Deaths on the island will count towards the final score."
+ key = "island_brawl"
+ map_name = "island_brawl"
+ reward_points = BITRUNNER_REWARD_HIGH
+ secondary_loot = list(
+ /obj/item/toy/beach_ball = 2,
+ /obj/item/clothing/shoes/sandal = 1,
+ /obj/item/clothing/glasses/sunglasses = 1,
+ /obj/item/gun/ballistic/automatic/mini_uzi = 1,
+ )
+
+
+/datum/lazy_template/virtual_domain/island_brawl/setup_domain(list/created_atoms)
+ for(var/obj/effect/mob_spawn/ghost_role/human/virtual_domain/islander/spawner in created_atoms)
+ custom_spawns += spawner
+
+ RegisterSignals(spawner, list(COMSIG_GHOSTROLE_SPAWNED, COMSIG_BITRUNNER_SPAWNED), PROC_REF(on_spawn))
+
+
+/// Someone has spawned in, so we check for their death
+/datum/lazy_template/virtual_domain/island_brawl/proc/on_spawn(datum/source, mob/living/spawned_mob)
+ SIGNAL_HANDLER
+
+ RegisterSignals(spawned_mob, list(COMSIG_LIVING_DEATH), PROC_REF(on_death))
+
+
+/// Mob has died, so we add a point to the domain
+/datum/lazy_template/virtual_domain/island_brawl/proc/on_death(datum/source, gibbed)
+ SIGNAL_HANDLER
+
+ add_points(1)
+
+
+/obj/effect/mob_spawn/ghost_role/human/virtual_domain/islander
+ name = "Islander"
+ outfit = /datum/outfit/beachbum_combat
+ prompt_name = "a combat beach bum"
+ you_are_text = "You are a virtual islander."
+ flavour_text = "Don't let anyone ruin your idyllic vacation spot. Coordinate with others- or don't!"
diff --git a/code/modules/bitrunning/virtual_domain/domains/pirates.dm b/code/modules/bitrunning/virtual_domain/domains/pirates.dm
index 52d86a71211..b8bff6dbeb3 100644
--- a/code/modules/bitrunning/virtual_domain/domains/pirates.dm
+++ b/code/modules/bitrunning/virtual_domain/domains/pirates.dm
@@ -1,5 +1,6 @@
/datum/lazy_template/virtual_domain/pirates
name = "Corsair Cove"
+ announce_to_ghosts = TRUE
cost = BITRUNNER_COST_MEDIUM
desc = "Battle your way to the hidden treasure, seize the booty, and make a swift escape before the pirates turn the tide."
difficulty = BITRUNNER_DIFFICULTY_MEDIUM
diff --git a/code/modules/bitrunning/virtual_domain/domains/syndicate_assault.dm b/code/modules/bitrunning/virtual_domain/domains/syndicate_assault.dm
index 5f754dd433a..f81bc6daae2 100644
--- a/code/modules/bitrunning/virtual_domain/domains/syndicate_assault.dm
+++ b/code/modules/bitrunning/virtual_domain/domains/syndicate_assault.dm
@@ -1,5 +1,6 @@
/datum/lazy_template/virtual_domain/syndicate_assault
name = "Syndicate Assault"
+ announce_to_ghosts = TRUE
cost = BITRUNNER_COST_MEDIUM
desc = "Board the enemy ship and recover the stolen cargo."
difficulty = BITRUNNER_DIFFICULTY_MEDIUM
diff --git a/code/modules/bitrunning/virtual_domain/virtual_domain.dm b/code/modules/bitrunning/virtual_domain/virtual_domain.dm
index 21898daad72..f81f6186cbe 100644
--- a/code/modules/bitrunning/virtual_domain/virtual_domain.dm
+++ b/code/modules/bitrunning/virtual_domain/virtual_domain.dm
@@ -7,50 +7,82 @@
map_name = "None"
key = "Virtual Domain"
place_on_top = TRUE
-
- /// Cost of this map to load
- var/cost = BITRUNNER_COST_NONE
- /// Any outfit that you wish to force on avatars. Overrides preferences
- var/datum/outfit/forced_outfit
- /// The description of the map for the console UI
- var/desc = "A map."
- /// Affects the ui and ability to scan info.
- var/difficulty = BITRUNNER_DIFFICULTY_NONE
+ /// Whether to tell observers this map is being used
+ var/announce_to_ghosts = FALSE
/// The map file to load
var/filename = "virtual_domain.dmm"
- /// If this domain blocks the use of items from disks, for whatever reason
- var/forbids_disk_items = FALSE
- /// If this domain blocks the use of spells from disks, for whatever reason
- var/forbids_disk_spells = FALSE
- /// Information given to connected clients via ability
- var/help_text
- /// Whether to display this as a modular map
- var/is_modular = FALSE
- /// Byond will look for modular mob segment landmarks then choose from here at random. You can make them unique also.
- var/list/datum/modular_mob_segment/mob_modules = list()
- /// An assoc list of typepath/amount to spawn on completion. Not weighted - the value is the amount
- var/list/completion_loot
- /// An accoc list of typepath/amount to spawn from secondary objectives. Not weighted - the value is the total number of items that can be obtained.
- var/list/secondary_loot = list()
- /// Number of secondary loot boxes generated. Resets when the domain is reloaded.
- var/secondary_loot_generated
- /// Forces all mob modules to only load once
- var/modular_unique_mobs = FALSE
- // Name to show in the UI
- var/name = "Virtual Domain"
- /// Points to reward for completion. Used to purchase new domains and calculate ore rewards.
- var/reward_points = BITRUNNER_REWARD_MIN
/// The start time of the map. Used to calculate time taken
var/start_time
/// This map is specifically for unit tests. Shouldn't display in game
var/test_only = FALSE
+
+ /**
+ * Generic settings / UI
+ */
+
+ /// Cost of this map to load
+ var/cost = BITRUNNER_COST_NONE
+ /// The description of the map for the console UI
+ var/desc = "A map."
+ /// Affects the ui and ability to scan info.
+ var/difficulty = BITRUNNER_DIFFICULTY_NONE
+ /// Write these to help complete puzzles and other objectives. Viewed in the domain info ability.
+ var/help_text
+ // Name to show in the UI
+ var/name = "Virtual Domain"
+ /// Points to reward for completion. Used to purchase new domains and calculate ore rewards.
+ var/reward_points = BITRUNNER_REWARD_MIN
+
+ /**
+ * Player customization
+ */
+
+ /// If this domain blocks the use of items from disks, for whatever reason
+ var/forbids_disk_items = FALSE
+ /// If this domain blocks the use of spells from disks, for whatever reason
+ var/forbids_disk_spells = FALSE
+ /// Any outfit that you wish to force on avatars. Overrides preferences
+ var/datum/outfit/forced_outfit
+
+ /**
+ * Loot
+ */
+
+ /// An assoc list of typepath/amount to spawn on completion. Not weighted - the value is the amount
+ var/list/completion_loot
+ /// An assoc list of typepath/amount to spawn from secondary objectives. Not weighted - the value is the total number of items that can be obtained.
+ var/list/secondary_loot = list()
+ /// Number of secondary loot boxes generated. Resets when the domain is reloaded.
+ var/secondary_loot_generated
/// Has this domain been beaten with high enough score to spawn a tech disk?
var/disk_reward_spawned = FALSE
+ /**
+ * Modularity
+ */
+
+ /// Whether to display this as a modular map
+ var/is_modular = FALSE
+ /// Byond will look for modular mob segment landmarks then choose from here at random. You can make them unique also.
+ var/list/datum/modular_mob_segment/mob_modules = list()
+ /// Forces all mob modules to only load once
+ var/modular_unique_mobs = FALSE
+
+ /**
+ * Spawning
+ */
+
+ /// Looks for random landmarks to spawn on.
+ var/list/custom_spawns = list()
+ /// Set TRUE if you want reusable custom spawners
+ var/keep_custom_spawns = FALSE
+
+
/// Sends a point to any loot signals on the map
/datum/lazy_template/virtual_domain/proc/add_points(points_to_add)
SEND_SIGNAL(src, COMSIG_BITRUNNER_GOAL_POINT, points_to_add)
+
/// Overridable proc to be called after the map is loaded.
/datum/lazy_template/virtual_domain/proc/setup_domain(list/created_atoms)
return
diff --git a/code/modules/capture_the_flag/medieval_sim/medisim_game.dm b/code/modules/capture_the_flag/medieval_sim/medisim_game.dm
index 18e1cd13e7e..118184698a3 100644
--- a/code/modules/capture_the_flag/medieval_sim/medisim_game.dm
+++ b/code/modules/capture_the_flag/medieval_sim/medisim_game.dm
@@ -22,7 +22,7 @@
if(!.)
return
var/mob/living/carbon/human/human_knight = .
- randomize_human(human_knight)
+ randomize_human_normie(human_knight)
human_knight.dna.add_mutation(/datum/mutation/human/medieval, MUT_OTHER)
var/oldname = human_knight.name
var/title = "error"
diff --git a/code/modules/cargo/bounties/science.dm b/code/modules/cargo/bounties/science.dm
index 67da7ad6b29..0206ea41967 100644
--- a/code/modules/cargo/bounties/science.dm
+++ b/code/modules/cargo/bounties/science.dm
@@ -9,7 +9,7 @@
if(!..())
return FALSE
var/obj/item/relic/experiment = O
- if(experiment.revealed)
+ if(experiment.activated)
return TRUE
return
diff --git a/code/modules/cargo/packs/emergency.dm b/code/modules/cargo/packs/emergency.dm
index b48ef268da6..2d19c276ec1 100644
--- a/code/modules/cargo/packs/emergency.dm
+++ b/code/modules/cargo/packs/emergency.dm
@@ -6,8 +6,8 @@
name = "Biological Emergency Crate"
desc = "This crate includes 2 complete bio suits, along with a box containing sterile masks and latex gloves, providing effective protection against viruses."
cost = CARGO_CRATE_VALUE * 2
- contains = list(/obj/item/clothing/head/bio_hood = 2,
- /obj/item/clothing/suit/bio_suit = 2,
+ contains = list(/obj/item/clothing/head/bio_hood/general = 2,
+ /obj/item/clothing/suit/bio_suit/general = 2,
/obj/item/storage/bag/bio,
/obj/item/reagent_containers/syringe/antiviral = 2,
/obj/item/clothing/gloves/latex/nitrile = 2,
diff --git a/code/modules/cargo/packs/science.dm b/code/modules/cargo/packs/science.dm
index 4059b330e2f..dfa2a663593 100644
--- a/code/modules/cargo/packs/science.dm
+++ b/code/modules/cargo/packs/science.dm
@@ -176,7 +176,8 @@
/obj/item/biopsy_tool,
/obj/item/storage/box/petridish = 2,
/obj/item/storage/box/swab,
- /obj/item/construction/plumbing/research,
+ /obj/item/circuitboard/machine/vatgrower,
+ /obj/item/reagent_containers/condiment/protein,
)
crate_name = "cytology supplies crate"
diff --git a/code/modules/client/client_colour.dm b/code/modules/client/client_colour.dm
index 1209b5c3582..4ca500a9e71 100644
--- a/code/modules/client/client_colour.dm
+++ b/code/modules/client/client_colour.dm
@@ -180,9 +180,15 @@
/datum/client_colour/glass_colour/yellow
colour = "#ffff66"
+/datum/client_colour/glass_colour/lightyellow
+ colour = "#ffffaa"
+
/datum/client_colour/glass_colour/red
colour = "#ffaaaa"
+/datum/client_colour/glass_colour/lightred
+ colour = "#ffcccc"
+
/datum/client_colour/glass_colour/darkred
colour = "#bb5555"
@@ -195,9 +201,19 @@
/datum/client_colour/glass_colour/purple
colour = "#ff99ff"
+/datum/client_colour/glass_colour/lightpurple
+ colour = "#ffccff"
+
/datum/client_colour/glass_colour/gray
colour = "#cccccc"
+///A client colour that makes the screen look a bit more grungy, halloweenesque even.
+/datum/client_colour/halloween_helmet
+ colour = list(0.75,0.13,0.13,0, 0.13,0.7,0.13,0, 0.13,0.13,0.75,0, -0.06,-0.09,-0.08,1, 0,0,0,0)
+
+/datum/client_colour/flash_hood
+ colour = COLOR_MATRIX_POLAROID
+
/datum/client_colour/glass_colour/nightmare
colour = list(255,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, -130,0,0,0) //every color is either red or black
diff --git a/code/modules/client/preferences/names.dm b/code/modules/client/preferences/names.dm
index 5f951c5aa2e..08d8f86592c 100644
--- a/code/modules/client/preferences/names.dm
+++ b/code/modules/client/preferences/names.dm
@@ -17,21 +17,26 @@
/// If the highest priority job matches this, will prioritize this name in the UI
var/relevant_job
+
/datum/preference/name/apply_to_human(mob/living/carbon/human/target, value)
// Only real_name applies directly, everything else is applied by something else
return
+
/datum/preference/name/deserialize(input, datum/preferences/preferences)
return reject_bad_name("[input]", allow_numbers)
+
/datum/preference/name/serialize(input)
// `is_valid` should always be run before `serialize`, so it should not
// be possible for this to return `null`.
return reject_bad_name(input, allow_numbers)
+
/datum/preference/name/is_valid(value)
return istext(value) && !isnull(reject_bad_name(value, allow_numbers))
+
/// A character's real name
/datum/preference/name/real_name
explanation = "Name"
@@ -181,8 +186,21 @@
explanation = "Hacker alias"
group = "bitrunning"
savefile_key = "hacker_alias"
- allow_numbers = TRUE
relevant_job = /datum/job/bitrunner
+
/datum/preference/name/hacker_alias/create_default_value()
return pick(GLOB.hacker_aliases)
+
+
+/datum/preference/name/hacker_alias/is_valid(value)
+ return !isnull(permissive_sanitize_name(value))
+
+
+/datum/preference/name/hacker_alias/deserialize(input, datum/preferences/preferences)
+ return permissive_sanitize_name(input)
+
+
+/datum/preference/name/hacker_alias/serialize(input)
+ return permissive_sanitize_name(input)
+
diff --git a/code/modules/client/preferences/prosthetic_limb.dm b/code/modules/client/preferences/prosthetic_limb.dm
index a4d5b5a577b..3d9525734e5 100644
--- a/code/modules/client/preferences/prosthetic_limb.dm
+++ b/code/modules/client/preferences/prosthetic_limb.dm
@@ -4,7 +4,7 @@
savefile_identifier = PREFERENCE_CHARACTER
/datum/preference/choiced/prosthetic/init_possible_values()
- return list("Random") + GLOB.limb_choice
+ return list("Random") + GLOB.prosthetic_limb_choice
/datum/preference/choiced/prosthetic/is_accessible(datum/preferences/preferences)
. = ..()
diff --git a/code/modules/client/preferences/sounds.dm b/code/modules/client/preferences/sounds.dm
index 81263de677b..f1778405665 100644
--- a/code/modules/client/preferences/sounds.dm
+++ b/code/modules/client/preferences/sounds.dm
@@ -7,6 +7,11 @@
/datum/preference/toggle/sound_ambience/apply_to_client(client/client, value)
client.update_ambience_pref(value)
+/datum/preference/toggle/sound_breathing
+ category = PREFERENCE_CATEGORY_GAME_PREFERENCES
+ savefile_key = "sound_breathing"
+ savefile_identifier = PREFERENCE_PLAYER
+
/// Controls hearing announcement sounds
/datum/preference/toggle/sound_announcements
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
diff --git a/code/modules/clothing/chameleon/chameleon_action_subtypes.dm b/code/modules/clothing/chameleon/chameleon_action_subtypes.dm
index 275b2c6e0fc..bd15bb908f2 100644
--- a/code/modules/clothing/chameleon/chameleon_action_subtypes.dm
+++ b/code/modules/clothing/chameleon/chameleon_action_subtypes.dm
@@ -64,6 +64,11 @@
. = ..()
chameleon_blacklist |= typecacheof(/obj/item/clothing/mask/changeling, only_root_path = TRUE)
+/datum/action/item_action/chameleon/change/mask/initialize_disguises()
+ . = ..()
+ add_chameleon_items(/obj/item/cigarette)
+ add_chameleon_items(/obj/item/vape)
+
/datum/action/item_action/chameleon/change/hat
chameleon_type = /obj/item/clothing/head
chameleon_name = "Hat"
diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm
index 1c70e259d49..6e4a7274563 100644
--- a/code/modules/clothing/glasses/_glasses.dm
+++ b/code/modules/clothing/glasses/_glasses.dm
@@ -27,15 +27,15 @@
/// Whether or not vision coloring is forcing
var/forced_glass_color = FALSE
+/obj/item/clothing/glasses/Initialize(mapload)
+ . = ..()
+ if(glass_colour_type)
+ AddElement(/datum/element/wearable_client_colour, glass_colour_type, ITEM_SLOT_EYES, forced = forced_glass_color)
+
/obj/item/clothing/glasses/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] is stabbing \the [src] into [user.p_their()] eyes! It looks like [user.p_theyre()] trying to commit suicide!"))
return BRUTELOSS
-/obj/item/clothing/glasses/examine(mob/user)
- . = ..()
- if(glass_colour_type && !forced_glass_color && ishuman(user))
- . += span_notice("Alt-click to toggle [p_their()] colors.")
-
/obj/item/clothing/glasses/visor_toggling()
. = ..()
alternate_worn_layer = up ? ABOVE_BODY_FRONT_HEAD_LAYER : initial(alternate_worn_layer) // SKYRAT EDIT CHANGE - ORIGINAL : alternate_worn_layer = up ? ABOVE_BODY_FRONT_HEAD_LAYER : null
@@ -62,37 +62,12 @@
H.set_eye_blur_if_lower(10 SECONDS)
eyes.apply_organ_damage(5)
-/obj/item/clothing/glasses/click_alt(mob/user)
- if(isnull(glass_colour_type) || forced_glass_color || !ishuman(user))
- return NONE
- var/mob/living/carbon/human/human_user = user
-
- if (HAS_TRAIT_FROM(human_user, TRAIT_SEE_GLASS_COLORS, GLASSES_TRAIT))
- REMOVE_TRAIT(human_user, TRAIT_SEE_GLASS_COLORS, GLASSES_TRAIT)
- to_chat(human_user, span_notice("You will no longer see glasses colors."))
- else
- ADD_TRAIT(human_user, TRAIT_SEE_GLASS_COLORS, GLASSES_TRAIT)
- to_chat(human_user, span_notice("You will now see glasses colors."))
- human_user.update_glasses_color(src, TRUE)
- return CLICK_ACTION_SUCCESS
-
-/obj/item/clothing/glasses/proc/change_glass_color(mob/living/carbon/human/H, datum/client_colour/glass_colour/new_color_type)
- var/old_colour_type = glass_colour_type
- if(!new_color_type || ispath(new_color_type)) //the new glass colour type must be null or a path.
- glass_colour_type = new_color_type
- if(H && H.glasses == src)
- if(old_colour_type)
- H.remove_client_colour(old_colour_type)
- if(glass_colour_type)
- H.update_glasses_color(src, 1)
-
-
-/mob/living/carbon/human/proc/update_glasses_color(obj/item/clothing/glasses/G, glasses_equipped)
- if((HAS_TRAIT(src, TRAIT_SEE_GLASS_COLORS) || G.forced_glass_color) && glasses_equipped)
- add_client_colour(G.glass_colour_type)
- else
- remove_client_colour(G.glass_colour_type)
-
+/obj/item/clothing/glasses/proc/change_glass_color(new_color_type)
+ if(glass_colour_type)
+ RemoveElement(/datum/element/wearable_client_colour, glass_colour_type, ITEM_SLOT_EYES, forced = forced_glass_color)
+ glass_colour_type = new_color_type
+ if(glass_colour_type)
+ AddElement(/datum/element/wearable_client_colour, glass_colour_type, ITEM_SLOT_EYES, forced = forced_glass_color)
/obj/item/clothing/glasses/meson
name = "optical meson scanner"
@@ -117,8 +92,14 @@
inhand_icon_state = "nvgmeson"
flash_protect = FLASH_PROTECTION_SENSITIVE
// Night vision mesons get the same but more intense
- color_cutoffs = list(10, 30, 10)
- glass_colour_type = /datum/client_colour/glass_colour/green
+ color_cutoffs = list(10, 35, 10)
+ glass_colour_type = /datum/client_colour/glass_colour/lightgreen
+ actions_types = list(/datum/action/item_action/toggle_nv)
+ forced_glass_color = TRUE
+
+/obj/item/clothing/glasses/meson/night/update_icon_state()
+ . = ..()
+ icon_state = length(color_cutoffs) ? initial(icon_state) : "nvgmeson_off"
/obj/item/clothing/glasses/meson/gar
name = "gar mesons"
@@ -163,8 +144,14 @@
icon_state = "scihudnight"
flash_protect = FLASH_PROTECTION_SENSITIVE
// Real vivid purple
- color_cutoffs = list(50, 10, 30)
- glass_colour_type = /datum/client_colour/glass_colour/green
+ color_cutoffs = list(30, 5, 15)
+ glass_colour_type = /datum/client_colour/glass_colour/lightpurple
+ actions_types = list(/datum/action/item_action/toggle_nv)
+ forced_glass_color = TRUE
+
+/obj/item/clothing/glasses/science/night/update_icon_state()
+ . = ..()
+ icon_state = length(color_cutoffs) ? initial(icon_state) : "night_off"
/obj/item/clothing/glasses/night
name = "night vision goggles"
@@ -174,8 +161,18 @@
flags_cover = GLASSESCOVERSEYES
flash_protect = FLASH_PROTECTION_SENSITIVE
// Dark green
- color_cutoffs = list(10, 30, 10)
- glass_colour_type = /datum/client_colour/glass_colour/green
+ color_cutoffs = list(10, 25, 10)
+ glass_colour_type = /datum/client_colour/glass_colour/lightgreen
+ actions_types = list(/datum/action/item_action/toggle_nv)
+ forced_glass_color = TRUE
+
+/obj/item/clothing/glasses/night/update_icon_state()
+ . = ..()
+ icon_state = length(color_cutoffs) ? initial(icon_state) : "night_off"
+
+/obj/item/clothing/glasses/night/colorless
+ desc = parent_type::desc + " Now with 50% less green!"
+ forced_glass_color = FALSE
/obj/item/clothing/glasses/eyepatch
name = "eyepatch"
@@ -351,8 +348,8 @@
/obj/item/clothing/glasses/sunglasses/proc/add_glasses_slapcraft_component()
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/hudsunsec, /datum/crafting_recipe/hudsunmed, /datum/crafting_recipe/hudsundiag, /datum/crafting_recipe/scienceglasses)
- AddComponent(
- /datum/component/slapcrafting,\
+ AddElement(
+ /datum/element/slapcrafting,\
slapcraft_recipes = slapcraft_recipe_list,\
)
@@ -371,8 +368,8 @@
/obj/item/clothing/glasses/sunglasses/chemical/add_glasses_slapcraft_component()
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/scienceglassesremoval)
- AddComponent(
- /datum/component/slapcrafting,\
+ AddElement(
+ /datum/element/slapcrafting,\
slapcraft_recipes = slapcraft_recipe_list,\
)
diff --git a/code/modules/clothing/glasses/engine_goggles.dm b/code/modules/clothing/glasses/engine_goggles.dm
index c9fbb159126..064a24cf5c7 100644
--- a/code/modules/clothing/glasses/engine_goggles.dm
+++ b/code/modules/clothing/glasses/engine_goggles.dm
@@ -47,21 +47,21 @@
if(MODE_MESON)
vision_flags = SEE_TURFS
color_cutoffs = list(15, 12, 0)
- change_glass_color(user, /datum/client_colour/glass_colour/yellow)
+ change_glass_color(/datum/client_colour/glass_colour/yellow)
if(MODE_TRAY) //undoes the last mode, meson
vision_flags = NONE
color_cutoffs = null
- change_glass_color(user, /datum/client_colour/glass_colour/lightblue)
+ change_glass_color(/datum/client_colour/glass_colour/lightblue)
if(MODE_PIPE_CONNECTABLE)
- change_glass_color(user, /datum/client_colour/glass_colour/lightblue)
+ change_glass_color(/datum/client_colour/glass_colour/lightblue)
if(MODE_SHUTTLE)
- change_glass_color(user, /datum/client_colour/glass_colour/red)
+ change_glass_color(/datum/client_colour/glass_colour/red)
if(MODE_NONE)
- change_glass_color(user, initial(glass_colour_type))
+ change_glass_color(initial(glass_colour_type))
if(ishuman(user))
var/mob/living/carbon/human/H = user
diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm
index c216528515e..4f5668f7bcc 100644
--- a/code/modules/clothing/glasses/hud.dm
+++ b/code/modules/clothing/glasses/hud.dm
@@ -67,8 +67,14 @@
flash_protect = FLASH_PROTECTION_SENSITIVE
flags_cover = GLASSESCOVERSEYES
// Blue green, dark
- color_cutoffs = list(5, 15, 30)
- glass_colour_type = /datum/client_colour/glass_colour/green
+ color_cutoffs = list(20, 20, 45)
+ glass_colour_type = /datum/client_colour/glass_colour/lightgreen
+ actions_types = list(/datum/action/item_action/toggle_nv)
+ forced_glass_color = TRUE
+
+/obj/item/clothing/glasses/hud/health/night/update_icon_state()
+ . = ..()
+ icon_state = length(color_cutoffs) ? initial(icon_state) : "night_off"
/obj/item/clothing/glasses/hud/health/night/meson
name = "night vision meson health scanner HUD"
@@ -79,7 +85,8 @@
name = "night vision medical science scanner HUD"
desc = "An clandestine medical science heads-up display that allows operatives to find \
dying captains and the perfect poison to finish them off in complete darkness."
- clothing_traits = list(TRAIT_REAGENT_SCANNER)
+ clothing_traits = list(TRAIT_REAGENT_SCANNER, TRAIT_MEDICAL_HUD)
+ forced_glass_color = FALSE
/obj/item/clothing/glasses/hud/health/sunglasses
name = "medical HUDSunglasses"
@@ -94,8 +101,8 @@
. = ..()
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/hudsunmedremoval)
- AddComponent(
- /datum/component/slapcrafting,\
+ AddElement(
+ /datum/element/slapcrafting,\
slapcraft_recipes = slapcraft_recipe_list,\
)
@@ -115,8 +122,14 @@
flash_protect = FLASH_PROTECTION_SENSITIVE
flags_cover = GLASSESCOVERSEYES
// Pale yellow
- color_cutoffs = list(30, 20, 5)
- glass_colour_type = /datum/client_colour/glass_colour/green
+ color_cutoffs = list(25, 15, 5)
+ glass_colour_type = /datum/client_colour/glass_colour/lightyellow
+ actions_types = list(/datum/action/item_action/toggle_nv)
+ forced_glass_color = TRUE
+
+/obj/item/clothing/glasses/hud/diagnostic/night/update_icon_state()
+ . = ..()
+ icon_state = length(color_cutoffs) ? initial(icon_state) : "night_off"
/obj/item/clothing/glasses/hud/diagnostic/sunglasses
name = "diagnostic sunglasses"
@@ -131,8 +144,8 @@
. = ..()
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/hudsundiagremoval)
- AddComponent(
- /datum/component/slapcrafting,\
+ AddElement(
+ /datum/element/slapcrafting,\
slapcraft_recipes = slapcraft_recipe_list,\
)
@@ -175,8 +188,8 @@
. = ..()
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/hudsunsecremoval)
- AddComponent(
- /datum/component/slapcrafting,\
+ AddElement(
+ /datum/element/slapcrafting,\
slapcraft_recipes = slapcraft_recipe_list,\
)
@@ -187,8 +200,14 @@
flash_protect = FLASH_PROTECTION_SENSITIVE
flags_cover = GLASSESCOVERSEYES
// Red with a tint of green
- color_cutoffs = list(35, 5, 5)
- glass_colour_type = /datum/client_colour/glass_colour/green
+ color_cutoffs = list(40, 15, 10)
+ glass_colour_type = /datum/client_colour/glass_colour/lightred
+ actions_types = list(/datum/action/item_action/toggle_nv)
+ forced_glass_color = TRUE
+
+/obj/item/clothing/glasses/hud/security/night/update_icon_state()
+ . = ..()
+ icon_state = length(color_cutoffs) ? initial(icon_state) : "night_off"
/obj/item/clothing/glasses/hud/security/sunglasses/gars
name = "\improper HUD gar glasses"
@@ -257,15 +276,15 @@
if (DATA_HUD_MEDICAL_ADVANCED)
icon_state = "meson"
color_cutoffs = list(5, 15, 5)
- change_glass_color(user, /datum/client_colour/glass_colour/green)
+ change_glass_color(/datum/client_colour/glass_colour/green)
if (DATA_HUD_SECURITY_ADVANCED)
icon_state = "thermal"
color_cutoffs = list(25, 8, 5)
- change_glass_color(user, /datum/client_colour/glass_colour/red)
+ change_glass_color(/datum/client_colour/glass_colour/red)
else
icon_state = "purple"
color_cutoffs = list(15, 0, 25)
- change_glass_color(user, /datum/client_colour/glass_colour/purple)
+ change_glass_color(/datum/client_colour/glass_colour/purple)
user.update_sight()
user.update_worn_glasses()
diff --git a/code/modules/clothing/gloves/boxing.dm b/code/modules/clothing/gloves/boxing.dm
index 03b1cbb5bf7..021d895f69c 100644
--- a/code/modules/clothing/gloves/boxing.dm
+++ b/code/modules/clothing/gloves/boxing.dm
@@ -13,8 +13,8 @@
. = ..()
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/extendohand_l, /datum/crafting_recipe/extendohand_r)
- AddComponent(
- /datum/component/slapcrafting,\
+ AddElement(
+ /datum/element/slapcrafting,\
slapcraft_recipes = slapcraft_recipe_list,\
)
diff --git a/code/modules/clothing/gloves/color.dm b/code/modules/clothing/gloves/color.dm
index 11230d18d37..96b7b39f5ab 100644
--- a/code/modules/clothing/gloves/color.dm
+++ b/code/modules/clothing/gloves/color.dm
@@ -32,8 +32,8 @@
. = ..()
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/radiogloves)
- AddComponent(
- /datum/component/slapcrafting,\
+ AddElement(
+ /datum/element/slapcrafting,\
slapcraft_recipes = slapcraft_recipe_list,\
)
@@ -54,8 +54,8 @@
. = ..()
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/gripperoffbrand)
- AddComponent(
- /datum/component/slapcrafting,\
+ AddElement(
+ /datum/element/slapcrafting,\
slapcraft_recipes = slapcraft_recipe_list,\
)
diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm
index 4d9ec24e335..5ee1e246c57 100644
--- a/code/modules/clothing/head/hardhat.dm
+++ b/code/modules/clothing/head/hardhat.dm
@@ -158,6 +158,14 @@
if(!up)
. += visor_state
+/obj/item/clothing/head/utility/hardhat/welding/up
+ up = TRUE // for calls to worn_overlays before init (prefs)
+
+/obj/item/clothing/head/utility/hardhat/welding/up/Initialize(mapload)
+ . = ..()
+ up = FALSE
+ visor_toggling()
+
/obj/item/clothing/head/utility/hardhat/welding/orange
icon_state = "hardhat0_orange"
inhand_icon_state = null
@@ -175,6 +183,14 @@
cold_protection = HEAD
min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT
+/obj/item/clothing/head/utility/hardhat/welding/white/up
+ up = TRUE // for calls to worn_overlays before init (prefs)
+
+/obj/item/clothing/head/utility/hardhat/welding/white/up/Initialize(mapload)
+ . = ..()
+ up = FALSE
+ visor_toggling()
+
/obj/item/clothing/head/utility/hardhat/welding/dblue
icon_state = "hardhat0_dblue"
inhand_icon_state = null
@@ -226,6 +242,10 @@
. = ..()
if(isnull(.))
return
+ if(new_value)
+ AddElement(/datum/element/wearable_client_colour, /datum/client_colour/halloween_helmet, ITEM_SLOT_HEAD, forced = TRUE)
+ else
+ RemoveElement(/datum/element/wearable_client_colour, /datum/client_colour/halloween_helmet, ITEM_SLOT_HEAD, forced = TRUE)
update_icon(UPDATE_OVERLAYS)
/obj/item/clothing/head/utility/hardhat/pumpkinhead/update_overlays()
diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm
index 27be2799b7d..4d41d9daa5c 100644
--- a/code/modules/clothing/head/helmet.dm
+++ b/code/modules/clothing/head/helmet.dm
@@ -4,6 +4,7 @@
icon = 'icons/obj/clothing/head/helmet.dmi'
worn_icon = 'icons/mob/clothing/head/helmet.dmi'
icon_state = "helmet"
+ base_icon_state = "helmet"
inhand_icon_state = "helmet"
armor_type = /datum/armor/head_helmet
cold_protection = HEAD
@@ -31,6 +32,7 @@
AddElement(/datum/element/update_icon_updates_onmob)
/obj/item/clothing/head/helmet/sec
+ var/flipped_visor = FALSE
/obj/item/clothing/head/helmet/sec/Initialize(mapload)
. = ..()
@@ -60,6 +62,19 @@
return ..()
+/obj/item/clothing/head/helmet/sec/click_alt(mob/user)
+ flipped_visor = !flipped_visor
+ balloon_alert(user, "visor flipped")
+ // base_icon_state is modified for seclight attachment component
+ base_icon_state = "[initial(base_icon_state)][flipped_visor ? "-novisor" : ""]"
+ icon_state = base_icon_state
+ if (flipped_visor)
+ flags_cover &= ~HEADCOVERSEYES
+ else
+ flags_cover |= HEADCOVERSEYES
+ update_appearance()
+ return CLICK_ACTION_SUCCESS
+
/obj/item/clothing/head/helmet/alt
name = "bulletproof helmet"
desc = "A bulletproof combat helmet that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent."
@@ -395,6 +410,7 @@
armor_type = /datum/armor/helmet_knight
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH
+ resistance_flags = NONE
strip_delay = 80
dog_fashion = null
clothing_traits = list(TRAIT_HEAD_INJURY_BLOCKED)
diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm
index 53f08faaa12..607bfc031ec 100644
--- a/code/modules/clothing/head/jobs.dm
+++ b/code/modules/clothing/head/jobs.dm
@@ -412,8 +412,8 @@
/datum/crafting_recipe/sturdy_shako,\
)
- AddComponent(
- /datum/component/slapcrafting,\
+ AddElement(
+ /datum/element/slapcrafting,\
slapcraft_recipes = slapcraft_recipe_list,\
)
diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm
index bb3021d2c18..4aed628f4d3 100644
--- a/code/modules/clothing/masks/gasmask.dm
+++ b/code/modules/clothing/masks/gasmask.dm
@@ -27,7 +27,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
///Does the mask have an FOV?
var/has_fov = TRUE
///Cigarette in the mask
- var/obj/item/clothing/mask/cigarette/cig
+ var/obj/item/cigarette/cig
voice_filter = "lowpass=f=750,volume=2"
/datum/armor/mask_gas
@@ -84,7 +84,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
/obj/item/clothing/mask/gas/attackby(obj/item/tool, mob/user)
var/valid_wearer = ismob(loc)
var/mob/wearer = loc
- if(istype(tool, /obj/item/clothing/mask/cigarette))
+ if(istype(tool, /obj/item/cigarette))
if(flags_cover & MASKCOVERSMOUTH)
balloon_alert(user, "mask's mouth is covered!")
return ..()
diff --git a/code/modules/clothing/masks/muzzle.dm b/code/modules/clothing/masks/muzzle.dm
index 32f29a54a1f..6154e7762cb 100644
--- a/code/modules/clothing/masks/muzzle.dm
+++ b/code/modules/clothing/masks/muzzle.dm
@@ -5,11 +5,14 @@
inhand_icon_state = "blindfold"
lefthand_file = 'icons/mob/inhands/clothing/glasses_lefthand.dmi'
righthand_file = 'icons/mob/inhands/clothing/glasses_righthand.dmi'
- clothing_flags = BLOCKS_SPEECH
flags_cover = MASKCOVERSMOUTH
w_class = WEIGHT_CLASS_SMALL
equip_delay_other = 20
+/obj/item/clothing/mask/muzzle/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/muffles_speech)
+
/obj/item/clothing/mask/muzzle/attack_paw(mob/user, list/modifiers)
if(iscarbon(user))
var/mob/living/carbon/carbon_user = user
@@ -26,7 +29,7 @@
lefthand_file = 'icons/mob/inhands/clothing/masks_lefthand.dmi'
righthand_file = 'icons/mob/inhands/clothing/masks_righthand.dmi'
body_parts_covered = NONE
- clothing_flags = MASKINTERNALS | BLOCKS_SPEECH
+ clothing_flags = MASKINTERNALS
armor_type = /datum/armor/muzzle_breath
equip_delay_other = 25 // my sprite has 4 straps, a-la a head harness. takes a while to equip, longer than a muzzle
@@ -37,7 +40,7 @@
worn_icon_state = "tape_piece_worn"
inhand_icon_state = null
w_class = WEIGHT_CLASS_TINY
- clothing_flags = INEDIBLE_CLOTHING|BLOCKS_SPEECH
+ clothing_flags = INEDIBLE_CLOTHING
equip_delay_other = 40
strip_delay = 40
greyscale_config = /datum/greyscale_config/tape_piece
diff --git a/code/modules/clothing/outfits/ert.dm b/code/modules/clothing/outfits/ert.dm
index db20562039b..2aa61b00460 100644
--- a/code/modules/clothing/outfits/ert.dm
+++ b/code/modules/clothing/outfits/ert.dm
@@ -242,7 +242,7 @@
/obj/item/storage/box/lights/mixed = 1,
)
belt = /obj/item/storage/belt/janitor/full
- glasses = /obj/item/clothing/glasses/night
+ glasses = /obj/item/clothing/glasses/night/colorless
l_pocket = /obj/item/grenade/chem_grenade/cleaner
r_pocket = /obj/item/grenade/chem_grenade/cleaner
l_hand = /obj/item/storage/bag/trash/bluespace
diff --git a/code/modules/clothing/outfits/standard.dm b/code/modules/clothing/outfits/standard.dm
index 6c088760f07..422cb34fa09 100644
--- a/code/modules/clothing/outfits/standard.dm
+++ b/code/modules/clothing/outfits/standard.dm
@@ -11,7 +11,7 @@
glasses = /obj/item/clothing/glasses/thermal/eyepatch
gloves = /obj/item/clothing/gloves/tackler/combat/insulated
head = /obj/item/clothing/head/helmet/space/beret
- mask = /obj/item/clothing/mask/cigarette/cigar/havana
+ mask = /obj/item/cigarette/cigar/havana
shoes = /obj/item/clothing/shoes/combat/swat
r_pocket = /obj/item/lighter
@@ -245,7 +245,7 @@
glasses = /obj/item/clothing/glasses/sunglasses
gloves = /obj/item/clothing/gloves/tackler/combat/insulated
head = /obj/item/clothing/head/hats/centhat
- mask = /obj/item/clothing/mask/cigarette/cigar/cohiba
+ mask = /obj/item/cigarette/cigar/cohiba
shoes = /obj/item/clothing/shoes/combat/swat
l_pocket = /obj/item/ammo_box/a357
r_pocket = /obj/item/lighter
diff --git a/code/modules/clothing/suits/costume.dm b/code/modules/clothing/suits/costume.dm
index e4fb756e298..75c1c5d850d 100644
--- a/code/modules/clothing/suits/costume.dm
+++ b/code/modules/clothing/suits/costume.dm
@@ -21,6 +21,10 @@
body_parts_covered = HEAD
flags_inv = HIDEHAIR|HIDEEARS|HIDEFACIALHAIR|HIDEFACE|HIDEMASK|HIDESNOUT
+/obj/item/clothing/head/hooded/flashsuit/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/wearable_client_colour, /datum/client_colour/flash_hood, ITEM_SLOT_HEAD, forced = TRUE)
+
/obj/item/clothing/suit/costume/pirate
name = "pirate coat"
desc = "Yarr."
diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm
index 177c0a16313..2b8fef5bb5b 100644
--- a/code/modules/clothing/suits/jobs.dm
+++ b/code/modules/clothing/suits/jobs.dm
@@ -61,7 +61,7 @@
body_parts_covered = CHEST|GROIN|ARMS
allowed = list(
/obj/item/assembly/flash/handheld,
- /obj/item/clothing/mask/cigarette,
+ /obj/item/cigarette,
/obj/item/disk,
/obj/item/lighter,
/obj/item/melee,
diff --git a/code/modules/deathmatch/deathmatch_loadouts.dm b/code/modules/deathmatch/deathmatch_loadouts.dm
index 024a1c2fc8b..44d2729afc2 100644
--- a/code/modules/deathmatch/deathmatch_loadouts.dm
+++ b/code/modules/deathmatch/deathmatch_loadouts.dm
@@ -352,7 +352,8 @@
name = "Deathmatch: Cowboy"
display_name = "Cowboy"
desc = "Yeehaw partner"
- r_hand = /obj/item/clothing/mask/cigarette/cigar
+
+ r_hand = /obj/item/cigarette/cigar
l_hand = /obj/item/melee/curator_whip
l_pocket = /obj/item/lighter
accessory = /obj/item/clothing/accessory/vest_sheriff
@@ -386,7 +387,7 @@
suit = /obj/item/clothing/suit/wizrobe/red
head = /obj/item/clothing/head/wizard/red
- mask = /obj/item/clothing/mask/cigarette
+ mask = /obj/item/cigarette
granted_spells = list(
/datum/action/cooldown/spell/pointed/projectile/fireball,
/datum/action/cooldown/spell/smoke,
@@ -654,7 +655,7 @@
/datum/outfit/deathmatch_loadout/battler/clown/upgraded
name = "Deathmatch: Clown (Syndicate Gear)"
- display_name = "Clown"
+ display_name = "Clown Commando"
desc = "They were bound to show up sooner or later."
shoes = /obj/item/clothing/shoes/clown_shoes/combat
@@ -708,7 +709,7 @@
/datum/outfit/deathmatch_loadout/chef/upgraded
name = "Deathmatch: Master Chef"
- display_name = "Chef"
+ display_name = "Master Chef"
desc = "Let him cook."
belt = /obj/item/gun/magic/hook
diff --git a/code/modules/deathmatch/deathmatch_lobby.dm b/code/modules/deathmatch/deathmatch_lobby.dm
index a3f49be705c..d2324d47fd0 100644
--- a/code/modules/deathmatch/deathmatch_lobby.dm
+++ b/code/modules/deathmatch/deathmatch_lobby.dm
@@ -21,6 +21,8 @@
var/list/modifiers = list()
/// Is the modifiers modal menu open (for the host)
var/mod_menu_open = FALSE
+ /// artificial time padding when we start loading to give lighting a breather (admin starts will set this to 0)
+ var/start_time = 8 SECONDS
/datum/deathmatch_lobby/New(mob/player)
. = ..()
@@ -79,7 +81,7 @@
UnregisterSignal(source, COMSIG_LAZY_TEMPLATE_LOADED)
map.template_in_use = FALSE
- addtimer(CALLBACK(src, PROC_REF(start_game_after_delay)), 8 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(start_game_after_delay)), start_time)
/datum/deathmatch_lobby/proc/start_game_after_delay()
if (!length(player_spawns) || length(player_spawns) < length(players))
@@ -152,7 +154,14 @@
GLOB.deathmatch_game.modifiers[modifier].apply(new_player, src)
// register death handling.
- RegisterSignals(new_player, list(COMSIG_LIVING_DEATH, COMSIG_MOB_GHOSTIZED, COMSIG_QDELETING), PROC_REF(player_died))
+ register_player_signals(new_player)
+
+/datum/deathmatch_lobby/proc/register_player_signals(new_player)
+ RegisterSignals(new_player, list(COMSIG_LIVING_DEATH, COMSIG_QDELETING, COMSIG_MOB_GHOSTIZED), PROC_REF(player_died))
+ RegisterSignal(new_player, COMSIG_LIVING_ON_WABBAJACKED, PROC_REF(player_wabbajacked))
+
+/datum/deathmatch_lobby/proc/unregister_player_signals(new_player)
+ UnregisterSignal(new_player, list(COMSIG_LIVING_DEATH, COMSIG_QDELETING, COMSIG_MOB_GHOSTIZED, COMSIG_LIVING_ON_WABBAJACKED))
/datum/deathmatch_lobby/proc/game_took_too_long()
if (!location || QDELING(src))
@@ -173,9 +182,9 @@
for(var/ckey in players)
var/mob/loser = players[ckey]["mob"]
- UnregisterSignal(loser, list(COMSIG_MOB_GHOSTIZED, COMSIG_QDELETING))
+ unregister_player_signals(loser)
players[ckey]["mob"] = null
- loser.ghostize()
+ loser.ghostize(can_reenter_corpse = FALSE)
qdel(loser)
for(var/datum/deathmatch_modifier/modifier in modifiers)
@@ -185,12 +194,18 @@
GLOB.deathmatch_game.remove_lobby(host)
log_game("Deathmatch game [host] ended.")
+/datum/deathmatch_lobby/proc/player_wabbajacked(mob/living/player, mob/living/new_mob)
+ SIGNAL_HANDLER
+ unregister_player_signals(player)
+ players[player.ckey]["mob"] = new_mob
+ register_player_signals(new_mob)
+
/datum/deathmatch_lobby/proc/player_died(mob/living/player, gibbed)
SIGNAL_HANDLER
- if(isnull(player) || QDELING(src))
+ if(isnull(player) || QDELING(src) || HAS_TRAIT_FROM(player, TRAIT_NO_TRANSFORM, MAGIC_TRAIT)) //this trait check fixes polymorphing
return
- var/ckey = player.ckey
+ var/ckey = player.ckey ? player.ckey : player.mind?.key
if(!islist(players[ckey])) // potentially the player info could hold a reference to this mob so we can figure the ckey out without worrying about ghosting and suicides n such
for(var/potential_ckey in players)
var/list/player_info = players[potential_ckey]
@@ -207,8 +222,9 @@
add_observer(ghost, (host == ckey))
announce(span_reallybig("[player.real_name] HAS DIED. [players.len] REMAIN."))
- if(!gibbed && !QDELING(player))
+ if(!gibbed && !QDELING(player) && !isdead(player))
if(!HAS_TRAIT(src, TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS))
+ unregister_player_signals(player)
player.dust(TRUE, TRUE, TRUE)
if (players.len <= 1)
end_game()
@@ -335,6 +351,7 @@
.["maps"] = list()
for (var/map_key in GLOB.deathmatch_game.maps)
.["maps"] += map_key
+ .["maps"] = sort_list(.["maps"])
/datum/deathmatch_lobby/ui_data(mob/user)
@@ -496,12 +513,13 @@
if ("admin") // Admin functions
if (!check_rights(R_ADMIN))
- message_admins("[usr.key] has attempted to use admin functions in a deathmatch lobby!")
+ message_admins("[usr.key] has attempted to use admin functions in a deathmatch lobby without being an admin!")
log_admin("[key_name(usr)] tried to use the deathmatch lobby admin functions without authorization.")
return
switch (params["func"])
if ("Force start")
log_admin("[key_name(usr)] force started deathmatch lobby [host].")
+ start_time = 0
start_game()
return FALSE
diff --git a/code/modules/deathmatch/deathmatch_mapping.dm b/code/modules/deathmatch/deathmatch_mapping.dm
index 320b87d2fd1..9f006e15242 100644
--- a/code/modules/deathmatch/deathmatch_mapping.dm
+++ b/code/modules/deathmatch/deathmatch_mapping.dm
@@ -2,7 +2,7 @@
name = "Deathmatch Arena"
requires_power = FALSE
has_gravity = STANDARD_GRAVITY
- area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | QUIET_LOGS
+ area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | QUIET_LOGS | NO_DEATH_MESSAGE | BINARY_JAMMING
/area/deathmatch/fullbright
static_lighting = FALSE
@@ -12,7 +12,7 @@
name = "Deathmatch Player Spawner"
/area/deathmatch/teleport //Prevent access to cross-z teleportation in the map itself (no wands of safety/teleportation scrolls). Cordons should prevent same-z teleportations outside of the arena.
- area_flags = UNIQUE_AREA | EVENT_PROTECTED | QUIET_LOGS
+ area_flags = /area/deathmatch::area_flags & ~NOTELEPORT
// for the illusion of a moving train
/turf/open/chasm/true/no_smooth/fake_motion_sand
diff --git a/code/modules/events/_event.dm b/code/modules/events/_event.dm
index 46d566e1c5a..5c3e3013438 100644
--- a/code/modules/events/_event.dm
+++ b/code/modules/events/_event.dm
@@ -22,7 +22,7 @@
var/holidayID = "" //string which should be in the SSeventss.holidays list if you wish this event to be holiday-specific
//anything with a (non-null) holidayID which does not match holiday, cannot run.
var/wizardevent = FALSE
- var/alert_observers = TRUE //should we let the ghosts and admins know this event is firing
+ var/alert_observers = TRUE //should we let the ghosts know this event is firing
//should be disabled on events that fire a lot
/// Minimum wizard rituals at which to trigger this event, inclusive
diff --git a/code/modules/events/space_vines/vine_structure.dm b/code/modules/events/space_vines/vine_structure.dm
index 5788c33aa2b..094a37371d7 100644
--- a/code/modules/events/space_vines/vine_structure.dm
+++ b/code/modules/events/space_vines/vine_structure.dm
@@ -165,7 +165,7 @@
if(!istype(stepturf))
return
- if(isspaceturf(stepturf) || isopenspaceturf(stepturf) || !stepturf.Enter(src))
+ if(is_space_or_openspace(stepturf) || !stepturf.Enter(src))
return
if(ischasm(stepturf) && !HAS_TRAIT(stepturf, TRAIT_CHASM_STOPPED))
return
diff --git a/code/modules/events/wizard/curseditems.dm b/code/modules/events/wizard/curseditems.dm
index 5b4d2f24a51..cb236e02f12 100644
--- a/code/modules/events/wizard/curseditems.dm
+++ b/code/modules/events/wizard/curseditems.dm
@@ -38,7 +38,7 @@
switch(item_set)
if(BIG_FAT_DOOBIE)
- loadout += /obj/item/clothing/mask/cigarette/rollie/trippy
+ loadout += /obj/item/cigarette/rollie/trippy
ruins_spaceworthiness = TRUE
if(BOXING)
loadout += /obj/item/clothing/mask/luchador
diff --git a/code/modules/events/wizard/embeddies.dm b/code/modules/events/wizard/embeddies.dm
index ea8c5fd176b..8b456894215 100644
--- a/code/modules/events/wizard/embeddies.dm
+++ b/code/modules/events/wizard/embeddies.dm
@@ -43,9 +43,12 @@ GLOBAL_DATUM(global_funny_embedding, /datum/global_funny_embedding)
* Makes every item in the world embed when thrown, but also hooks into global signals for new items created to also bless them with embed-ability(??).
*/
/datum/global_funny_embedding
- var/embed_type = EMBED_POINTY
+ var/embed_type = /datum/embed_data/global_funny
var/prefix = "error"
+/datum/embed_data/global_funny
+ ignore_throwspeed_threshold = TRUE
+
/datum/global_funny_embedding/New()
. = ..()
//second operation takes MUCH longer, so lets set up signals first.
@@ -61,11 +64,11 @@ GLOBAL_DATUM(global_funny_embedding, /datum/global_funny_embedding)
SIGNAL_HANDLER
// 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(created_item.embedding))
- return //already embeds to some degree, so whatever 🐀
- created_item.embedding = embed_type
+ if(created_item.get_embed())
+ return //already embeds to some degree, so whatever // No rat allowed
+
created_item.name = "[prefix] [created_item.name]"
- created_item.updateEmbedding()
+ created_item.set_embed(embed_type)
/**
* ### handle_current_items
@@ -77,17 +80,20 @@ GLOBAL_DATUM(global_funny_embedding, /datum/global_funny_embedding)
CHECK_TICK
if(!(embed_item.flags_1 & INITIALIZED_1))
continue
- if(!embed_item.embedding)
- embed_item.embedding = embed_type
- embed_item.updateEmbedding()
- embed_item.name = "[prefix] [embed_item.name]"
+ if(embed_item.get_embed())
+ continue
+ embed_item.set_embed(embed_type)
+ embed_item.name = "[prefix] [embed_item.name]"
///everything will be... POINTY!!!!
/datum/global_funny_embedding/pointy
- embed_type = EMBED_POINTY
prefix = "pointy"
///everything will be... sticky? sure, why not
/datum/global_funny_embedding/sticky
- embed_type = EMBED_HARMLESS
+ embed_type = /datum/embed_data/global_funny/sticky
prefix = "sticky"
+
+/datum/embed_data/global_funny/sticky
+ pain_mult = 0
+ jostle_pain_mult = 0
diff --git a/code/modules/experisci/experiment/experiments.dm b/code/modules/experisci/experiment/experiments.dm
index fb52ffc2c9a..95f4986d1b4 100644
--- a/code/modules/experisci/experiment/experiments.dm
+++ b/code/modules/experisci/experiment/experiments.dm
@@ -335,7 +335,6 @@
description = "Your exosuit fabricators allow for rapid production on a small scale, but the structural integrity of created parts is inferior to more traditional means."
exp_tag = "Scan"
possible_types = list(/obj/vehicle/sealed/mecha)
- total_requirement = 1
///Damage percent that each mech needs to be at for a scan to work.
var/damage_percent
@@ -345,6 +344,21 @@
possible_types = list(/obj/vehicle/sealed/mecha)
total_requirement = 1
+/// Scan a person with any mutation
+/datum/experiment/scanning/people/mutant
+ name = "Human Field Research: Genetic Mutations"
+ description = "Our new research assistants have been drinking random chemicals for science, when one of them mastered telekinesis and another started shooting lasers from the eyes. This could be useful for our studies. Repeat the experiment by making assistants drink unstable mutagen, scan them and report the results."
+ performance_hint = "Scan a person with a random mutation."
+ required_traits_desc = "random mutation"
+
+/datum/experiment/scanning/people/mutant/is_valid_scan_target(mob/living/carbon/human/check, datum/component/experiment_handler/experiment_handler)
+ . = ..()
+ if (!.)
+ return
+ if(!check.dna.mutations.len)
+ return FALSE
+ return TRUE
+
/// Scan for organs you didn't start the round with
/datum/experiment/scanning/people/novel_organs
name = "Human Field Research: Divergent Biology"
@@ -388,7 +402,7 @@
/// Scan for cybernetic organs
/datum/experiment/scanning/people/augmented_organs
name = "Human Field Research: Augmented Organs"
- description = "We need to gather data on how cybernetic vital organs integrate with human biology. Conduct a scan on a human with these implants to help us understand their compatibility"
+ description = "We need to gather data on how cybernetic vital organs integrate with human biology. Conduct a scan on a human with these implants to help us understand their compatibility."
performance_hint = "Perform an organ manipulation surgery to replace one of the vital organs with a cybernetic variant."
required_traits_desc = "augmented vital organs"
required_count = 1
@@ -407,11 +421,8 @@
)
for (var/obj/item/organ/organ as anything in check.organs)
- if (IS_ORGANIC_ORGAN(organ))
- continue
- if (!(organ.slot in vital_organ_slots))
- continue
- return TRUE
+ if ((organ.slot in vital_organ_slots) && IS_ROBOTIC_ORGAN(organ))
+ return TRUE
return FALSE
/// Scan for skillchips
@@ -434,12 +445,55 @@
return FALSE
return TRUE
+/// Scan an android
+/datum/experiment/scanning/people/android
+ name = "Human Field Research: Full Augmentation"
+ description = "Perform a full cybernetic augmentation on a crewmate then scan them to test their newfound capabilities and new sensory and cognitive functions."
+ performance_hint = "Achieve full augmentation by performing a set of surgery operations."
+ required_traits_desc = "fully augmented android"
+ required_count = 1
+
+/datum/experiment/scanning/people/android/is_valid_scan_target(mob/living/carbon/human/check, datum/component/experiment_handler/experiment_handler)
+ . = ..()
+ if (!.)
+ return
+ if (isandroid(check))
+ return TRUE
+ if (check.organs < 6 || check.bodyparts < 6)
+ return FALSE
+
+ var/static/list/augmented_organ_slots = list(
+ ORGAN_SLOT_EYES,
+ ORGAN_SLOT_EARS,
+ ORGAN_SLOT_HEART,
+ ORGAN_SLOT_LUNGS,
+ ORGAN_SLOT_LIVER,
+ ORGAN_SLOT_STOMACH,
+ )
+ for (var/obj/item/organ/organ as anything in check.organs)
+ if (!(organ.slot in augmented_organ_slots))
+ continue
+ if (!IS_ROBOTIC_ORGAN(organ))
+ return FALSE
+ for (var/obj/item/bodypart/bodypart as anything in check.bodyparts)
+ if (bodypart.bodytype != BODYTYPE_ROBOTIC)
+ return FALSE
+ return TRUE
+
/datum/experiment/scanning/reagent/cryostylane
name = "Pure Cryostylane Scan"
description = "It appears that the Cryostylane reagent can potentially halt all physiological processes in the human body. Produce Cryostylane with at least 99% purity and scan the beaker."
+ performance_hint = "Keep the temperature as high as possible during the reaction."
required_reagent = /datum/reagent/cryostylane
min_purity = 0.99
+/datum/experiment/scanning/reagent/haloperidol
+ name = "Pure Haloperidol Scan"
+ description = "We require testing related to the long-term treatment of chronic psychiatric disorders. Produce Haloperidol with at least 98% purity and scan the beaker."
+ performance_hint = "Exothermic and consumes hydrogen during reaction."
+ required_reagent = /datum/reagent/medicine/haloperidol
+ min_purity = 0.98
+
/datum/experiment/scanning/points/bluespace_crystal
name = "Bluespace Crystal Sampling"
description = "Investigate the properties of bluespace crystals by scanning either an artificial or naturally occurring variant. This will help us deepen our understanding of bluespace phenomena."
@@ -449,10 +503,16 @@
/obj/item/stack/sheet/bluespace_crystal = 1
)
+/datum/experiment/scanning/points/anomalies
+ name = "Neutralized Anomaly Analysis"
+ description = "We have the power to deal with the anomalies now. Neutralize them with an anomaly neutralizer or refine the raw cores in the refinery and scan the results."
+ required_points = 4
+ required_atoms = list(/obj/item/assembly/signaler/anomaly = 1)
+
/datum/experiment/scanning/points/machinery_tiered_scan/tier2_any
name = "Upgraded Stock Parts Benchmark"
description = "Our newly-designed machinery components require practical application tests for hints at possible further advancements, as well as a general confirmation that we didn't actually design worse parts somehow. Scan any machinery with Upgraded Parts and report the results."
- required_points = 4
+ required_points = 6
required_atoms = list(
/obj/machinery = 1
)
@@ -461,7 +521,7 @@
/datum/experiment/scanning/points/machinery_tiered_scan/tier3_any
name = "Advanced Stock Parts Benchmark"
description = "Our newly-designed machinery components require practical application tests for hints at possible further advancements, as well as a general confirmation that we didn't actually design worse parts somehow. Scan any machinery with Advanced Parts and report the results."
- required_points = 4
+ required_points = 6
required_atoms = list(
/obj/machinery = 1
)
diff --git a/code/modules/experisci/experiment/types/scanning_fish.dm b/code/modules/experisci/experiment/types/scanning_fish.dm
index b5d2c0d65f9..0c39471e071 100644
--- a/code/modules/experisci/experiment/types/scanning_fish.dm
+++ b/code/modules/experisci/experiment/types/scanning_fish.dm
@@ -12,7 +12,7 @@ GLOBAL_LIST_EMPTY(scanned_fish_by_techweb)
performance_hint = "Scan fish. Examine scanner to review progress. Unlock new fishing portals."
allowed_experimentors = list(/obj/item/experi_scanner, /obj/machinery/destructive_scanner, /obj/item/fishing_rod/tech, /obj/item/fish_analyzer)
traits = EXPERIMENT_TRAIT_TYPECACHE
- points_reward = list(TECHWEB_POINT_TYPE_GENERIC = 750)
+ points_reward = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS )
required_atoms = list(/obj/item/fish = 3) //BUBBERSTATION CHANGE: 4 TO 3.
scan_message = "Scan different species of fish"
///Further experiments added to the techweb when this one is completed.
@@ -80,7 +80,7 @@ GLOBAL_LIST_EMPTY(scanned_fish_by_techweb)
/datum/experiment/scanning/fish/second
name = "Fish Scanning Experiment 2"
description = "An experiment requiring more fish species to be scanned to unlock the 'Chasm' setting for the fishing portal."
- points_reward = list(TECHWEB_POINT_TYPE_GENERIC = 1500)
+ points_reward = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS )
required_atoms = list(/obj/item/fish = 6) //BUBBERSTATION CHANGE: 8 TO 6.
next_experiments = list(/datum/experiment/scanning/fish/third)
fish_source_reward = /datum/fish_source/portal/chasm
@@ -88,7 +88,7 @@ GLOBAL_LIST_EMPTY(scanned_fish_by_techweb)
/datum/experiment/scanning/fish/third
name = "Fish Scanning Experiment 3"
description = "An experiment requiring even more fish species to be scanned to unlock the 'Ocean' setting for the fishing portal."
- points_reward = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
+ points_reward = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS )
required_atoms = list(/obj/item/fish = 9) //BUBBERSTATION CHANGE: 14 TO 9.
next_experiments = list(/datum/experiment/scanning/fish/fourth, /datum/experiment/scanning/fish/holographic)
fish_source_reward = /datum/fish_source/portal/ocean
@@ -97,7 +97,7 @@ GLOBAL_LIST_EMPTY(scanned_fish_by_techweb)
name = "Holographic Fish Scanning Experiment"
description = "This one actually requires holographic fish to unlock the 'Randomizer' setting for the fishing portal."
performance_hint = "Load in the 'Beach' template at the Holodeck to fish some holo-fish."
- points_reward = list(TECHWEB_POINT_TYPE_GENERIC = 500)
+ points_reward = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS )
required_atoms = list(/obj/item/fish/holo = 4)
scan_message = "Scan different species of holographic fish"
next_experiments = null
@@ -110,7 +110,7 @@ GLOBAL_LIST_EMPTY(scanned_fish_by_techweb)
/datum/experiment/scanning/fish/fourth
name = "Fish Scanning Experiment 4"
description = "An experiment requiring lotsa fish species to unlock the 'Hyperspace' setting for the fishing portal."
- points_reward = list(TECHWEB_POINT_TYPE_GENERIC = 3250)
+ points_reward = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS )
required_atoms = list(/obj/item/fish = 12) //BUBBERSTATION CHANGE: 21 TO 12.
next_experiments = null
fish_source_reward = /datum/fish_source/portal/hyperspace
diff --git a/code/modules/experisci/experiment/types/scanning_reagent.dm b/code/modules/experisci/experiment/types/scanning_reagent.dm
index 1a39e2941ef..992c28d148d 100644
--- a/code/modules/experisci/experiment/types/scanning_reagent.dm
+++ b/code/modules/experisci/experiment/types/scanning_reagent.dm
@@ -31,5 +31,5 @@
return TRUE
/datum/experiment/scanning/reagent/serialize_progress_stage(atom/target, list/seen_instances)
- return EXPERIMENT_PROG_INT("Scan a reagent container with [required_reagent::name] of at least [PERCENT(min_purity)] purity.", \
+ return EXPERIMENT_PROG_INT("Scan a reagent container with [required_reagent::name] of at least [PERCENT(min_purity)]% purity.", \
seen_instances.len, required_atoms[target])
diff --git a/code/modules/experisci/experiment/types/scanning_vatgrown.dm b/code/modules/experisci/experiment/types/scanning_vatgrown.dm
index 0a7ade381a4..20cd48288ba 100644
--- a/code/modules/experisci/experiment/types/scanning_vatgrown.dm
+++ b/code/modules/experisci/experiment/types/scanning_vatgrown.dm
@@ -1,14 +1,17 @@
-/datum/experiment/scanning/random/cytology
+/datum/experiment/scanning/cytology
name = "Cytology Scanning Experiment"
- description = "Base experiment for scanning atoms that were vatgrown"
exp_tag = "Cytology Scan"
- total_requirement = 1
- possible_types = list(/mob/living/basic/slime)
- traits = EXPERIMENT_TRAIT_DESTRUCTIVE
-/datum/experiment/scanning/random/cytology/final_contributing_index_checks(datum/component/experiment_handler/experiment_handler, atom/target, typepath)
+/datum/experiment/scanning/cytology/final_contributing_index_checks(datum/component/experiment_handler/experiment_handler, atom/target, typepath)
return ..() && HAS_TRAIT(target, TRAIT_VATGROWN)
-/datum/experiment/scanning/random/cytology/serialize_progress_stage(atom/target, list/seen_instances)
- return EXPERIMENT_PROG_INT("Scan samples of \a vat-grown [initial(target.name)]", \
- traits & EXPERIMENT_TRAIT_DESTRUCTIVE ? scanned[target] : seen_instances.len, required_atoms[target])
+/datum/experiment/scanning/cytology/serialize_progress_stage(atom/target, list/seen_instances)
+ return EXPERIMENT_PROG_INT("Scan samples of \a vat-grown [initial(target.name)]", seen_instances.len, required_atoms[target])
+
+/datum/experiment/scanning/cytology/slime
+ name = "Vat-Grown Slime Scan"
+ description = "Seen the slimes in the xenobiology pens? They spawned when our researchers donked a moldy bread slice into the vat. Cultivate another one and report the results."
+ performance_hint = "Swab the slime cell lines from a moldy bread or take a biopsy sample of existing slime. And grow it in the vat."
+ required_atoms = list(/mob/living/basic/slime = 1)
+
+
diff --git a/code/modules/fishing/aquarium/aquarium.dm b/code/modules/fishing/aquarium/aquarium.dm
index 0a836eadafd..cd05d0bd34b 100644
--- a/code/modules/fishing/aquarium/aquarium.dm
+++ b/code/modules/fishing/aquarium/aquarium.dm
@@ -53,6 +53,9 @@
/// /obj/item/fish in the aquarium, sorted by type - does not include things with aquarium visuals that are not fish
var/list/tracked_fish_by_type
+ /// Var used to keep track of the current beauty of the aquarium, which can be throughfully changed by aquarium content.
+ var/current_beauty = 150
+
/obj/structure/aquarium/Initialize(mapload)
. = ..()
update_appearance()
@@ -62,6 +65,8 @@
create_reagents(6, SEALED_CONTAINER)
RegisterSignal(reagents, COMSIG_REAGENTS_NEW_REAGENT, PROC_REF(start_autofeed))
AddComponent(/datum/component/plumbing/aquarium)
+ if(current_beauty)
+ AddElement(/datum/element/beauty, current_beauty)
ADD_KEEP_TOGETHER(src, INNATE_TRAIT)
/obj/structure/aquarium/proc/track_if_fish(atom/source, atom/initialized)
@@ -192,9 +197,9 @@
var/obj/item/stack/sheet/glass/glass = item
if(istype(glass))
if(glass.get_amount() < 2)
- to_chat(user, span_warning("You need two glass sheets to fix the case!"))
+ balloon_alert(user, "it needs two sheets!")
return
- to_chat(user, span_notice("You start fixing [src]..."))
+ balloon_alert(user, "fixing the aquarium...")
if(do_after(user, 2 SECONDS, target = src))
glass.use(2)
broken = FALSE
@@ -202,10 +207,18 @@
update_appearance()
return TRUE
else
- var/datum/component/aquarium_content/content_component = item.GetComponent(/datum/component/aquarium_content)
- if(content_component && content_component.is_ready_to_insert(src) && user.transferItemToLoc(item, src))
- update_appearance()
- return TRUE
+ var/insert_attempt = SEND_SIGNAL(item, COMSIG_TRY_INSERTING_IN_AQUARIUM, src)
+ switch(insert_attempt)
+ if(COMSIG_CAN_INSERT_IN_AQUARIUM)
+ if(!user.transferItemToLoc(item, src))
+ user.balloon_alert(user, "stuck to your hand!")
+ return TRUE
+ balloon_alert(user, "added to aquarium")
+ update_appearance()
+ return TRUE
+ if(COMSIG_CANNOT_INSERT_IN_AQUARIUM)
+ balloon_alert(user, "cannot add to aquarium!")
+ return TRUE
if(istype(item, /obj/item/fish_feed) && !panel_open)
if(!item.reagents.total_volume)
@@ -270,24 +283,27 @@
///Apply mood bonus depending on aquarium status
/obj/structure/aquarium/proc/admire(mob/living/user)
- to_chat(user,span_notice("You take a moment to watch [src]."))
- if(do_after(user, 5 SECONDS, target = src))
- var/alive_fish = 0
- var/dead_fish = 0
- var/list/tracked_fish = get_fishes()
- for(var/obj/item/fish/fish in tracked_fish)
- if(fish.status == FISH_ALIVE)
- alive_fish++
- else
- dead_fish++
- //Check if there are live fish - good mood
- //All fish dead - bad mood.
- //No fish - nothing.
- if(alive_fish > 0)
- user.add_mood_event("aquarium", /datum/mood_event/aquarium_positive)
- else if(dead_fish > 0)
- user.add_mood_event("aquarium", /datum/mood_event/aquarium_negative)
- // Could maybe scale power of this mood with number/types of fish
+ user.balloon_alert(user, "admiring aquarium...")
+ if(!do_after(user, 5 SECONDS, target = src))
+ return
+ var/alive_fish = 0
+ var/dead_fish = 0
+ var/list/tracked_fish = get_fishes()
+ for(var/obj/item/fish/fish in tracked_fish)
+ if(fish.status == FISH_ALIVE)
+ alive_fish++
+ else
+ dead_fish++
+
+ var/morb = HAS_TRAIT(user, TRAIT_MORBID)
+ //Check if there are live fish - good mood
+ //All fish dead - bad mood.
+ //No fish - nothing.
+ if(alive_fish > 0)
+ user.add_mood_event("aquarium", morb ? /datum/mood_event/morbid_aquarium_bad : /datum/mood_event/aquarium_positive)
+ else if(dead_fish > 0)
+ user.add_mood_event("aquarium", morb ? /datum/mood_event/morbid_aquarium_good : /datum/mood_event/aquarium_negative)
+ // Could maybe scale power of this mood with number/types of fish
/obj/structure/aquarium/ui_data(mob/user)
. = ..()
diff --git a/code/modules/fishing/aquarium/aquarium_kit.dm b/code/modules/fishing/aquarium/aquarium_kit.dm
index 30c93003231..1161648f7d1 100644
--- a/code/modules/fishing/aquarium/aquarium_kit.dm
+++ b/code/modules/fishing/aquarium/aquarium_kit.dm
@@ -100,7 +100,8 @@
/obj/item/aquarium_kit/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/slapcrafting, /datum/crafting_recipe/aquarium)
+ var/static/list/recipes = list(/datum/crafting_recipe/aquarium)
+ AddElement(/datum/element/slapcrafting, recipes)
/obj/item/aquarium_prop
name = "generic aquarium prop"
@@ -109,10 +110,11 @@
w_class = WEIGHT_CLASS_TINY
var/layer_mode = AQUARIUM_LAYER_MODE_BOTTOM
+ var/beauty = 150
/obj/item/aquarium_prop/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/aquarium_content, icon)
+ AddComponent(/datum/component/aquarium_content, icon, beauty = beauty)
/obj/item/aquarium_prop/rocks
name = "rocks"
diff --git a/code/modules/fishing/aquarium/aquarium_upgrades.dm b/code/modules/fishing/aquarium/aquarium_upgrades.dm
index 140777d34f0..c73e6e9d230 100644
--- a/code/modules/fishing/aquarium/aquarium_upgrades.dm
+++ b/code/modules/fishing/aquarium/aquarium_upgrades.dm
@@ -24,6 +24,8 @@
icon_state = "bioelec_map"
icon_prefix = "bioelec"
+ current_beauty = 0
+
/obj/structure/aquarium/bioelec_gen/zap_act(power, zap_flags)
var/explosive = zap_flags & ZAP_MACHINE_EXPLOSIVE
if(!explosive)
diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm
index 3d2810de356..7f9f36ce1be 100644
--- a/code/modules/fishing/fish/_fish.dm
+++ b/code/modules/fishing/fish/_fish.dm
@@ -143,9 +143,12 @@
/// power of the tesla zap created by the fish in a bioelectric generator
var/electrogenesis_power = 10 MEGA JOULES
+ /// The beauty this fish provides to the aquarium it's inserted in.
+ var/beauty = FISH_BEAUTY_GENERIC
+
/obj/item/fish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
- AddComponent(/datum/component/aquarium_content, icon, PROC_REF(get_aquarium_animation), list(COMSIG_FISH_STATUS_CHANGED,COMSIG_FISH_STIRRED))
+ AddComponent(/datum/component/aquarium_content, icon, PROC_REF(get_aquarium_animation), list(COMSIG_FISH_STIRRED), beauty)
RegisterSignal(src, COMSIG_ATOM_ON_LAZARUS_INJECTOR, PROC_REF(use_lazarus))
if(do_flop_animation)
diff --git a/code/modules/fishing/fish/fish_types.dm b/code/modules/fishing/fish/fish_types.dm
index f61cb43b224..cc001560ee0 100644
--- a/code/modules/fishing/fish/fish_types.dm
+++ b/code/modules/fishing/fish/fish_types.dm
@@ -19,6 +19,8 @@
desc = "A great rubber duck tool for Lawyers who can't get a grasp over their case."
stable_population = 1
random_case_rarity = FISH_RARITY_NOPE
+ show_in_catalog = FALSE
+ beauty = FISH_BEAUTY_GOOD
/obj/item/fish/angelfish
name = "angelfish"
@@ -77,6 +79,7 @@
)
required_temperature_min = MIN_AQUARIUM_TEMP+12
required_temperature_max = MIN_AQUARIUM_TEMP+30
+ beauty = FISH_BEAUTY_GOOD
// Saltwater fish below
@@ -107,6 +110,7 @@
evolution_types = null
compatible_types = list(/obj/item/fish/clownfish)
food = /datum/reagent/lube
+ beauty = FISH_BEAUTY_GREAT
/obj/item/fish/cardinal
name = "cardinalfish"
@@ -163,8 +167,9 @@
stable_population = 3
required_temperature_min = MIN_AQUARIUM_TEMP+23
required_temperature_max = MIN_AQUARIUM_TEMP+28
-
fish_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/toxic)
+ beauty = FISH_BEAUTY_GOOD
+
/obj/item/fish/lanternfish
name = "lanternfish"
@@ -182,6 +187,7 @@
fish_traits = list(/datum/fish_trait/nocturnal)
required_temperature_min = MIN_AQUARIUM_TEMP+2 //My source is that the water at a depth 6600 feet is pretty darn cold.
required_temperature_max = MIN_AQUARIUM_TEMP+18
+ beauty = FISH_BEAUTY_NULL
//Tiziran Fish
/obj/item/fish/dwarf_moonfish
@@ -195,6 +201,7 @@
average_weight = 2000
required_temperature_min = MIN_AQUARIUM_TEMP+20
required_temperature_max = MIN_AQUARIUM_TEMP+30
+ beauty = FISH_BEAUTY_GOOD
/obj/item/fish/gunner_jellyfish
name = "gunner jellyfish"
@@ -205,6 +212,7 @@
fillet_type = /obj/item/food/fishmeat/gunner_jellyfish
required_temperature_min = MIN_AQUARIUM_TEMP+24
required_temperature_max = MIN_AQUARIUM_TEMP+32
+ beauty = FISH_BEAUTY_GOOD
/obj/item/fish/needlefish
name = "needlefish"
@@ -261,6 +269,7 @@
)
evolution_types = list(/datum/fish_evolution/ice_chrab)
compatible_types = list(/obj/item/fish/chasm_crab/ice)
+ beauty = FISH_BEAUTY_GOOD
/obj/item/fish/chasm_crab/ice
name = "arctic chrab"
@@ -271,13 +280,7 @@
required_temperature_max = MIN_AQUARIUM_TEMP+15
evolution_types = list(/datum/fish_evolution/chasm_chrab)
compatible_types = list(/obj/item/fish/chasm_crab)
-
-/obj/item/storage/box/fish_debug
- name = "box full of fish"
-
-/obj/item/storage/box/fish_debug/PopulateContents()
- for(var/fish_type in subtypesof(/obj/item/fish))
- new fish_type(src)
+ beauty = FISH_BEAUTY_GREAT
/obj/item/fish/donkfish
name = "donk co. company patent donkfish"
@@ -290,6 +293,7 @@
fish_traits = list(/datum/fish_trait/yucky)
required_temperature_min = MIN_AQUARIUM_TEMP+15
required_temperature_max = MIN_AQUARIUM_TEMP+28
+ beauty = FISH_BEAUTY_EXCELLENT
/obj/item/fish/emulsijack
name = "toxic emulsijack"
@@ -301,6 +305,7 @@
fish_traits = list(/datum/fish_trait/emulsijack)
required_temperature_min = MIN_AQUARIUM_TEMP+5
required_temperature_max = MIN_AQUARIUM_TEMP+40
+ beauty = FISH_BEAUTY_BAD
/obj/item/fish/jumpercable
name = "monocloning jumpercable"
@@ -322,6 +327,7 @@
/datum/fish_trait/mixotroph,
/datum/fish_trait/electrogenesis,
)
+ beauty = FISH_BEAUTY_UGLY
/obj/item/fish/ratfish
name = "ratfish"
@@ -341,6 +347,7 @@
"Value" = DAIRY
)
)
+ beauty = FISH_BEAUTY_DISGUSTING
/obj/item/fish/ratfish/Initialize(mapload)
. = ..()
@@ -364,6 +371,7 @@
required_temperature_min = MIN_AQUARIUM_TEMP+10
required_temperature_max = MIN_AQUARIUM_TEMP+40
evolution_types = list(/datum/fish_evolution/purple_sludgefish)
+ beauty = FISH_BEAUTY_NULL
/obj/item/fish/sludgefish/purple
name = "purple sludgefish"
@@ -401,6 +409,7 @@
),
)
required_temperature_min = MIN_AQUARIUM_TEMP+20
+ beauty = FISH_BEAUTY_GREAT
/obj/item/fish/boned
name = "unmarine bonemass"
@@ -423,6 +432,7 @@
average_weight = 2000
death_text = "%SRC stops moving." //It's dead... or is it?
evolution_types = list(/datum/fish_evolution/mastodon)
+ beauty = FISH_BEAUTY_UGLY
/obj/item/fish/mastodon
name = "unmarine mastodon"
@@ -451,6 +461,7 @@
average_weight = 5000
death_text = "%SRC stops moving."
fish_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/amphibious, /datum/fish_trait/revival, /datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/aggressive)
+ beauty = FISH_BEAUTY_BAD
/obj/item/fish/holo
name = "holographic goldfish"
@@ -501,6 +512,7 @@
sprite_height = 8
average_size = 60
average_weight = 1000
+ beauty = FISH_BEAUTY_GOOD
/obj/item/fish/holo/angel
name = "holographic angelfish"
@@ -524,6 +536,7 @@
icon_state = "checkered" //it's a meta joke, buddy.
dedicated_in_aquarium_icon_state = "checkered_small"
sprite_width = 4
+ beauty = FISH_BEAUTY_NULL
/obj/item/fish/holo/halffish
name = "holographic half-fish"
@@ -533,6 +546,7 @@
sprite_height = 4
sprite_width = 10
average_size = 50
+ beauty = FISH_BEAUTY_UGLY
/obj/item/fish/starfish
name = "cosmostarfish"
@@ -554,6 +568,7 @@
grind_results = list(/datum/reagent/bluespace = 10, /datum/reagent/consumable/liquidgibs = 5)
fillet_type = null
fish_traits = list(/datum/fish_trait/antigrav, /datum/fish_trait/mixotroph)
+ beauty = FISH_BEAUTY_GREAT
/obj/item/fish/starfish/Initialize(mapload)
. = ..()
@@ -589,6 +604,7 @@
)
hitsound = null
throwforce = 5
+ beauty = FISH_BEAUTY_GOOD
///maximum bonus damage when winded up
var/maximum_bonus = 25
@@ -662,3 +678,4 @@
)
//anxiety naturally limits the amount of zipzaps per tank, so they are stronger alone
electrogenesis_power = 20 MEGA JOULES
+ beauty = FISH_BEAUTY_GOOD
diff --git a/code/modules/fishing/fish_catalog.dm b/code/modules/fishing/fish_catalog.dm
index 3e1cb6cfcfa..49a84413ded 100644
--- a/code/modules/fishing/fish_catalog.dm
+++ b/code/modules/fishing/fish_catalog.dm
@@ -37,6 +37,25 @@
else
fish_data["feed"] = "[AQUARIUM_COMPANY] Fish Feed"
fish_data["fishing_tips"] = build_fishing_tips(fish)
+ var/beauty_score = initial(fish.beauty)
+ switch(beauty_score)
+ if(-INFINITY to FISH_BEAUTY_DISGUSTING)
+ beauty_score = "OH HELL NAW!"
+ if(FISH_BEAUTY_DISGUSTING to FISH_BEAUTY_UGLY)
+ beauty_score = "☆☆☆☆☆"
+ if(FISH_BEAUTY_UGLY to FISH_BEAUTY_BAD)
+ beauty_score = "★☆☆☆☆"
+ if(FISH_BEAUTY_BAD to FISH_BEAUTY_NULL)
+ beauty_score = "★★☆☆☆"
+ if(FISH_BEAUTY_NULL to FISH_BEAUTY_GENERIC)
+ beauty_score = "★★★☆☆"
+ if(FISH_BEAUTY_GENERIC to FISH_BEAUTY_GOOD)
+ beauty_score = "★★★★☆"
+ if(FISH_BEAUTY_GOOD to FISH_BEAUTY_GREAT)
+ beauty_score = "★★★★★"
+ if(FISH_BEAUTY_GREAT to INFINITY)
+ beauty_score = "★★★★★★"
+ fish_data["beauty"] = beauty_score
fish_info += list(fish_data)
// TODO: Custom entries for unusual stuff
diff --git a/code/modules/fishing/fishing_equipment.dm b/code/modules/fishing/fishing_equipment.dm
index df6c4eee9c2..f6b49a9b523 100644
--- a/code/modules/fishing/fishing_equipment.dm
+++ b/code/modules/fishing/fishing_equipment.dm
@@ -304,5 +304,12 @@
new /obj/item/fishing_line/reinforced(src)
new /obj/item/fishing_line/cloaked(src)
+/obj/item/storage/box/fish_debug
+ name = "box full of fish"
+
+/obj/item/storage/box/fish_debug/PopulateContents()
+ for(var/fish_type in subtypesof(/obj/item/fish))
+ new fish_type(src)
+
#undef MAGNET_HOOK_BONUS_MULTIPLIER
#undef RESCUE_HOOK_FISH_MULTIPLIER
diff --git a/code/modules/fishing/sources/_fish_source.dm b/code/modules/fishing/sources/_fish_source.dm
index 66d2bf0a880..3c94ff8277d 100644
--- a/code/modules/fishing/sources/_fish_source.dm
+++ b/code/modules/fishing/sources/_fish_source.dm
@@ -239,6 +239,21 @@ GLOBAL_LIST(fishing_property_cache)
/// Builds a fish weights table modified by bait/rod/user properties
/datum/fish_source/proc/get_modified_fish_table(obj/item/fishing_rod/rod, mob/fisherman)
var/obj/item/bait = rod.bait
+ ///An exponent used to level out the difference in probabilities between fishes/mobs on the table depending on bait quality.
+ var/leveling_exponent = 0
+ ///Multiplier used to make fishes more common compared to everything else.
+ var/result_multiplier = 1
+
+ if(bait)
+ if(HAS_TRAIT(bait, TRAIT_GREAT_QUALITY_BAIT))
+ result_multiplier = 9
+ leveling_exponent = 0.5
+ else if(HAS_TRAIT(bait, TRAIT_GOOD_QUALITY_BAIT))
+ result_multiplier = 3.5
+ leveling_exponent = 0.25
+ else if(HAS_TRAIT(bait, TRAIT_BASIC_QUALITY_BAIT))
+ result_multiplier = 2
+ leveling_exponent = 0.1
var/list/fish_list_properties = collect_fish_properties()
@@ -246,17 +261,13 @@ GLOBAL_LIST(fishing_property_cache)
for(var/result in final_table)
final_table[result] *= rod.hook?.get_hook_bonus_multiplicative(result)
final_table[result] += rod.hook?.get_hook_bonus_additive(result)//Decide on order here so it can be multiplicative
+
if(ispath(result, /obj/item/fish))
//Modify fish roll chance
var/obj/item/fish/caught_fish = result
if(bait)
- if(HAS_TRAIT(bait, TRAIT_GREAT_QUALITY_BAIT))
- final_table[result] *= 10
- else if(HAS_TRAIT(bait, TRAIT_GOOD_QUALITY_BAIT))
- final_table[result] = round(final_table[result] * 3.5, 1)
- else if(HAS_TRAIT(bait, TRAIT_BASIC_QUALITY_BAIT))
- final_table[result] *= 2
+ final_table[result] = round(final_table[result] * result_multiplier, 1)
if(!HAS_TRAIT(bait, TRAIT_OMNI_BAIT))
//Bait matching likes doubles the chance
var/list/fav_bait = fish_list_properties[result][NAMEOF(caught_fish, favorite_bait)]
@@ -286,4 +297,22 @@ GLOBAL_LIST(fishing_property_cache)
if(final_table[result] <= 0)
final_table -= result
+
+ ///here we even out the chances of fishie based on bait quality: better baits lead rarer fishes being more common.
+ if(leveling_exponent)
+ var/highest_fish_weight
+ var/list/collected_fish_weights = list()
+ for(var/fishable in final_table)
+ if(ispath(fishable, /obj/item/fish))
+ var/fish_weight = fish_table[fishable]
+ collected_fish_weights[fishable] = fish_weight
+ if(fish_weight > highest_fish_weight)
+ highest_fish_weight = fish_weight
+
+ for(var/fish in collected_fish_weights)
+ var/difference = collected_fish_weights[fish] - highest_fish_weight
+ if(!difference)
+ continue
+ final_table[fish] += round(difference**leveling_exponent, 1)
+
return final_table
diff --git a/code/modules/food_and_drinks/machinery/microwave.dm b/code/modules/food_and_drinks/machinery/microwave.dm
index 88256eaf1d2..bc5adfbd0f9 100644
--- a/code/modules/food_and_drinks/machinery/microwave.dm
+++ b/code/modules/food_and_drinks/machinery/microwave.dm
@@ -114,6 +114,7 @@
QDEL_LIST(ingredients)
QDEL_NULL(wires)
QDEL_NULL(soundloop)
+ QDEL_NULL(particles)
if(!isnull(cell))
QDEL_NULL(cell)
return ..()
@@ -366,8 +367,10 @@
return ITEM_INTERACT_SUCCESS
/obj/machinery/microwave/tool_act(mob/living/user, obj/item/tool, list/modifiers)
+ if(!tool.tool_behaviour)
+ return ..()
if(operating)
- return ITEM_INTERACT_SKIP_TO_ATTACK // Don't use tools if we're dirty
+ return ITEM_INTERACT_SKIP_TO_ATTACK // Don't use tools if we're operating
if(dirty >= MAX_MICROWAVE_DIRTINESS)
return ITEM_INTERACT_SKIP_TO_ATTACK // Don't insert items if we're dirty
if(panel_open && is_wire_tool(tool))
@@ -375,15 +378,13 @@
return ITEM_INTERACT_SUCCESS
return ..()
-/obj/machinery/microwave/attackby(obj/item/item, mob/living/user, params)
+/obj/machinery/microwave/item_interaction(mob/living/user, obj/item/item, list/modifiers)
if(operating)
- return
+ return NONE
if(broken > NOT_BROKEN)
- if(IS_EDIBLE(item))
- balloon_alert(user, "it's broken!")
- return TRUE
- return ..()
+ balloon_alert(user, "it's broken!")
+ return ITEM_INTERACT_BLOCKING
if(istype(item, /obj/item/stock_parts/power_store/cell) && cell_powered)
var/swapped = FALSE
@@ -395,27 +396,23 @@
swapped = TRUE
if(!user.transferItemToLoc(item, src))
update_appearance()
- return TRUE
+ return ITEM_INTERACT_BLOCKING
cell = item
balloon_alert(user, "[swapped ? "swapped" : "inserted"] cell")
update_appearance()
- return TRUE
+ return ITEM_INTERACT_SUCCESS
if(!anchored)
- if(IS_EDIBLE(item))
- balloon_alert(user, "not secured!")
- return TRUE
- return ..()
+ balloon_alert(user, "not secured!")
+ return ITEM_INTERACT_BLOCKING
if(dirty >= MAX_MICROWAVE_DIRTINESS) // The microwave is all dirty so can't be used!
- if(IS_EDIBLE(item))
- balloon_alert(user, "it's too dirty!")
- return TRUE
- return ..()
+ balloon_alert(user, "it's too dirty!")
+ return ITEM_INTERACT_BLOCKING
if(vampire_charging_capable && istype(item, /obj/item/modular_computer) && ingredients.len > 0)
balloon_alert(user, "max 1 device!")
- return FALSE
+ return ITEM_INTERACT_BLOCKING
if(istype(item, /obj/item/storage))
var/obj/item/storage/tray = item
@@ -425,14 +422,14 @@
// Non-tray dumping requires a do_after
to_chat(user, span_notice("You start dumping out the contents of [item] into [src]..."))
if(!do_after(user, 2 SECONDS, target = tray))
- return
+ return ITEM_INTERACT_BLOCKING
for(var/obj/tray_item in tray.contents)
if(!IS_EDIBLE(tray_item))
continue
if(ingredients.len >= max_n_of_items)
balloon_alert(user, "it's full!")
- return TRUE
+ return ITEM_INTERACT_BLOCKING
if(tray.atom_storage.attempt_remove(tray_item, src))
loaded++
ingredients += tray_item
@@ -440,23 +437,21 @@
open(autoclose = 0.6 SECONDS)
to_chat(user, span_notice("You insert [loaded] items into \the [src]."))
update_appearance()
- return
+ return ITEM_INTERACT_SUCCESS
- if(item.w_class <= WEIGHT_CLASS_NORMAL && !istype(item, /obj/item/storage) && !user.combat_mode)
+ if(item.w_class <= WEIGHT_CLASS_NORMAL && !user.combat_mode)
if(ingredients.len >= max_n_of_items)
balloon_alert(user, "it's full!")
- return TRUE
+ return ITEM_INTERACT_BLOCKING
if(!user.transferItemToLoc(item, src))
balloon_alert(user, "it's stuck to your hand!")
- return FALSE
+ return ITEM_INTERACT_BLOCKING
ingredients += item
open(autoclose = 0.6 SECONDS)
user.visible_message(span_notice("[user] adds \a [item] to \the [src]."), span_notice("You add [item] to \the [src]."))
update_appearance()
- return
-
- return ..()
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/microwave/attack_hand_secondary(mob/user, list/modifiers)
if(user.can_perform_action(src, ALLOW_SILICON_REACH))
@@ -541,8 +536,9 @@
/obj/machinery/microwave/proc/eject()
var/atom/drop_loc = drop_location()
- for(var/atom/movable/movable_ingredient as anything in ingredients)
- movable_ingredient.forceMove(drop_loc)
+ for(var/obj/item/item_ingredient as anything in ingredients)
+ item_ingredient.forceMove(drop_loc)
+ item_ingredient.dropped() //Mob holders can be on the ground if we don't do this
open(autoclose = 1.4 SECONDS)
/obj/machinery/microwave/proc/start_cycle(mob/user)
@@ -674,10 +670,34 @@
if(MICROWAVE_PRE)
pre_success(cooker)
return
+
+ if(cycles == 1) //Only needs to try to shock mobs once, towards the end of the loop
+ var/successful_shock
+ var/list/microwave_contents = list()
+ microwave_contents += get_all_contents() //Mobs are often hid inside of mob holders, which could be fried and made into a burger...
+ for(var/mob/living/victim in microwave_contents)
+ if(victim.electrocute_act(shock_damage = 100, source = src, siemens_coeff = 1, flags = SHOCK_NOGLOVES))
+ successful_shock = TRUE
+ if(victim.stat == DEAD) //This is mostly so humans that can_be_held don't get gibbed from one microwave run alone, but mice become burnt messes
+ victim.gib()
+ muck()
+ if(successful_shock) //We only want to give feedback once, regardless of how many mobs got shocked
+ var/list/cant_smell = list()
+ for(var/mob/smeller in get_hearers_in_view(DEFAULT_MESSAGE_RANGE, src))
+ if(HAS_TRAIT(smeller, TRAIT_ANOSMIA))
+ cant_smell += smeller
+ visible_message(span_danger("You smell a burnt smell coming from [src]!"), ignored_mobs = cant_smell)
+ particles = new /particles/smoke()
+ addtimer(CALLBACK(src, PROC_REF(remove_smoke)), 10 SECONDS)
+ Shake(duration = 1 SECONDS)
+
cycles--
use_energy(active_power_usage)
addtimer(CALLBACK(src, PROC_REF(cook_loop), type, cycles, wait, cooker), wait)
+/obj/machinery/microwave/proc/remove_smoke()
+ QDEL_NULL(particles)
+
/obj/machinery/microwave/power_change()
. = ..()
if(cell_powered)
diff --git a/code/modules/food_and_drinks/machinery/smartfridge.dm b/code/modules/food_and_drinks/machinery/smartfridge.dm
index 0e27b713da9..bd06c179d33 100644
--- a/code/modules/food_and_drinks/machinery/smartfridge.dm
+++ b/code/modules/food_and_drinks/machinery/smartfridge.dm
@@ -647,7 +647,7 @@
return istype(weapon, /obj/item/petri_dish)
/obj/machinery/smartfridge/petri/preloaded
- initial_contents = list(/obj/item/petri_dish = 5)
+ initial_contents = list(/obj/item/petri_dish/random = 3)
// -------------------------
// Organ Surgery Smartfridge
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_sandwich.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_sandwich.dm
index 7761a57fcfd..742e8c1f3c8 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_sandwich.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_sandwich.dm
@@ -10,6 +10,7 @@
name = "Sandwich"
reqs = list(
/obj/item/food/breadslice/plain = 2,
+ /obj/item/food/grown/cabbage = 1,
/obj/item/food/meat/steak = 1,
/obj/item/food/cheese/wedge = 1
)
diff --git a/code/modules/holodeck/items.dm b/code/modules/holodeck/items.dm
index 3d80ef6d968..7a27aecb4bc 100644
--- a/code/modules/holodeck/items.dm
+++ b/code/modules/holodeck/items.dm
@@ -14,7 +14,7 @@
throw_speed = 2
block_chance = 0
throwforce = 0
- embedding = null
+ embed_type = null
sword_color_icon = null
active_throwforce = 0
diff --git a/code/modules/hydroponics/grown/corn.dm b/code/modules/hydroponics/grown/corn.dm
index 8eab962d751..d5f9f94e1b2 100644
--- a/code/modules/hydroponics/grown/corn.dm
+++ b/code/modules/hydroponics/grown/corn.dm
@@ -50,7 +50,7 @@
/obj/item/grown/corncob/attackby(obj/item/grown/W, mob/user, params)
if(W.get_sharpness())
to_chat(user, span_notice("You use [W] to fashion a pipe out of the corn cob!"))
- new /obj/item/clothing/mask/cigarette/pipe/cobpipe (user.loc)
+ new /obj/item/cigarette/pipe/cobpipe (user.loc)
qdel(src)
else
return ..()
diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm
index 80f02328008..e55977e2dba 100644
--- a/code/modules/hydroponics/hydroitemdefines.dm
+++ b/code/modules/hydroponics/hydroitemdefines.dm
@@ -478,13 +478,18 @@
throwforce = 15
throw_speed = 4
throw_range = 7
- embedding = list("pain_mult" = 4, "embed_chance" = 35, "fall_chance" = 10)
+ embed_type = /datum/embed_data/hatchet
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*7.5)
attack_verb_continuous = list("chops", "tears", "lacerates", "cuts")
attack_verb_simple = list("chop", "tear", "lacerate", "cut")
hitsound = 'sound/weapons/bladeslice.ogg'
sharpness = SHARP_EDGED
+/datum/embed_data/hatchet
+ pain_mult = 4
+ embed_chance = 35
+ fall_chance = 10
+
/obj/item/hatchet/Initialize(mapload)
. = ..()
AddComponent(/datum/component/butchering, \
diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm
index 64bedbcdb52..9366ddc9d6f 100644
--- a/code/modules/hydroponics/plant_genes.dm
+++ b/code/modules/hydroponics/plant_genes.dm
@@ -858,12 +858,15 @@
return
var/obj/item/seeds/our_seed = our_plant.get_plant_seed()
- if(our_seed.get_gene(/datum/plant_gene/trait/stinging))
- our_plant.embedding = EMBED_POINTY
- else
- our_plant.embedding = EMBED_HARMLESS
- our_plant.updateEmbedding()
our_plant.throwforce = (our_seed.potency/20)
+ if (!our_plant.get_embed())
+ return
+
+ if(our_seed.get_gene(/datum/plant_gene/trait/stinging))
+ our_plant.set_embed(our_plant.get_embed().generate_with_values(ignore_throwspeed_threshold = TRUE))
+ return
+
+ our_plant.set_embed(our_plant.get_embed().generate_with_values(ignore_throwspeed_threshold = TRUE, pain_mult = 0, jostle_pain_mult = 0))
/**
* This trait automatically heats up the plant's chemical contents when harvested.
diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm
index 2e31af7923a..5ad3194b5e3 100755
--- a/code/modules/jobs/job_types/captain.dm
+++ b/code/modules/jobs/job_types/captain.dm
@@ -35,7 +35,7 @@
family_heirlooms = list(/obj/item/reagent_containers/cup/glass/flask/gold, /obj/item/toy/captainsaid/collector)
mail_goodies = list(
- /obj/item/clothing/mask/cigarette/cigar/havana = 20,
+ /obj/item/cigarette/cigar/havana = 20,
/obj/item/storage/fancy/cigarettes/cigars/havana = 15,
/obj/item/reagent_containers/cup/glass/bottle/champagne = 5,
/obj/item/reagent_containers/cup/glass/bottle/champagne/cursed = 5,
diff --git a/code/modules/jobs/job_types/chaplain/chaplain.dm b/code/modules/jobs/job_types/chaplain/chaplain.dm
index 8bcfaefcfc6..435e4ce7b33 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain.dm
@@ -78,8 +78,8 @@
else
holy_bible.deity_name = pick("Gay Space Jesus", "Gandalf", "Dumbledore")
human_spawned.adjustOrganLoss(ORGAN_SLOT_BRAIN, 100) // starts off brain damaged as fuck
- if("lol", "wtf", "poo", "badmin", "shitmin", "deadmin", "meme", "memes")
- new_bible = pick("Woody's Got Wood: The Aftermath", "Sweet Bro and Hella Jeff: Expanded Edition","F.A.T.A.L. Rulebook")
+ if("lol", "wtf", "poo", "badmin", "shitmin", "deadmin", "meme", "memes", "skibidi")
+ new_bible = pick("Woody's Got Wood: The Aftermath", "Sweet Bro and Hella Jeff: Expanded Edition","F.A.T.A.L. Rulebook", "Toilet Humor")
switch(new_bible)
if("Woody's Got Wood: The Aftermath")
holy_bible.deity_name = pick("Woody", "Andy", "Cherry Flavored Lube")
@@ -87,6 +87,8 @@
holy_bible.deity_name = pick("Sweet Bro", "Hella Jeff", "Stairs", "AH")
if("F.A.T.A.L. Rulebook")
holy_bible.deity_name = "Twenty Ten-Sided Dice"
+ if("Toilet Humor")
+ holy_bible.deity_name = pick("Skibidi Toilet", "Skibidi Wizard", "Skibidi Bathtub", "John Skibidi", "Skibidi Skibidi", "G-Toilet 1.0", "John Freeman")
human_spawned.adjustOrganLoss(ORGAN_SLOT_BRAIN, 100) // also starts off brain damaged as fuck
if("servicianism", "partying")
holy_bible.desc = "Happy, Full, Clean. Live it and give it."
diff --git a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
index f204518acc5..df658d71ddd 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
@@ -48,6 +48,7 @@
Allows you to behead targets for empowered strikes. \
Harms you if you dismiss the scythe without first causing harm to a creature. \
The shard also causes you to become Morbid, shifting your interests towards the macabre."
+ rods[/obj/item/melee/skateboard/holyboard] = "A skateboard that grants you flight and anti-magic abilities while ridden. Fits in your bag."
AddComponent(/datum/component/subtype_picker, rods, CALLBACK(src, PROC_REF(on_holy_weapon_picked)))
/obj/item/nullrod/proc/on_holy_weapon_picked(obj/item/nullrod/holy_weapon_type)
diff --git a/code/modules/jobs/job_types/chief_engineer.dm b/code/modules/jobs/job_types/chief_engineer.dm
index fd568464bb6..7ac1b6e29af 100644
--- a/code/modules/jobs/job_types/chief_engineer.dm
+++ b/code/modules/jobs/job_types/chief_engineer.dm
@@ -71,7 +71,7 @@
belt = /obj/item/storage/belt/utility/chief/full
ears = /obj/item/radio/headset/heads/ce
gloves = /obj/item/clothing/gloves/color/black
- head = /obj/item/clothing/head/utility/hardhat/white
+ head = /obj/item/clothing/head/utility/hardhat/welding/white/up
shoes = /obj/item/clothing/shoes/sneakers/brown
l_pocket = /obj/item/modular_computer/pda/heads/ce
diff --git a/code/modules/jobs/job_types/detective.dm b/code/modules/jobs/job_types/detective.dm
index 201344d262d..e69fc72f8f1 100644
--- a/code/modules/jobs/job_types/detective.dm
+++ b/code/modules/jobs/job_types/detective.dm
@@ -62,7 +62,7 @@
ears = /obj/item/radio/headset/headset_sec/alt
gloves = /obj/item/clothing/gloves/color/black
head = /obj/item/clothing/head/fedora/det_hat
- mask = /obj/item/clothing/mask/cigarette
+ mask = /obj/item/cigarette
neck = /obj/item/clothing/neck/tie/detective
shoes = /obj/item/clothing/shoes/sneakers/brown
l_pocket = /obj/item/modular_computer/pda/detective // SKYRAT EDIT CHANGE - ORIGINAL: /obj/item/toy/crayon/white
@@ -79,12 +79,12 @@
/datum/outfit/job/detective/pre_equip(mob/living/carbon/human/human, visualsOnly = FALSE)
. = ..()
if (human.age < AGE_MINOR)
- mask = /obj/item/clothing/mask/cigarette/candy
+ mask = /obj/item/cigarette/candy
head = /obj/item/clothing/head/fedora/det_hat/minor
/datum/outfit/job/detective/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
- var/obj/item/clothing/mask/cigarette/cig = H.wear_mask
+ var/obj/item/cigarette/cig = H.wear_mask
if(istype(cig)) //Some species specfic changes can mess this up (plasmamen)
cig.light("")
diff --git a/code/modules/jobs/job_types/shaft_miner.dm b/code/modules/jobs/job_types/shaft_miner.dm
index 8a9e4a284bb..232af2e89f9 100644
--- a/code/modules/jobs/job_types/shaft_miner.dm
+++ b/code/modules/jobs/job_types/shaft_miner.dm
@@ -113,8 +113,8 @@
var/obj/item/stack/sheet/animalhide/goliath_hide/plating = new()
explorer_suit.hood.attackby(plating)
for(var/obj/item/gun/energy/recharge/kinetic_accelerator/accelerator in miner_contents)
- var/obj/item/knife/combat/survival/knife = new(accelerator)
- accelerator.bayonet = knife
+ var/datum/component/bayonet_attachable/bayonet = accelerator.GetComponent(/datum/component/bayonet_attachable)
+ bayonet.add_bayonet(new /obj/item/knife/combat/survival(accelerator))
var/obj/item/flashlight/seclite/flashlight = new()
var/datum/component/seclite_attachable/light_component = accelerator.GetComponent(/datum/component/seclite_attachable)
light_component.add_light(flashlight)
diff --git a/code/modules/jobs/job_types/station_engineer.dm b/code/modules/jobs/job_types/station_engineer.dm
index 14e80f65ea9..3000e4a037a 100644
--- a/code/modules/jobs/job_types/station_engineer.dm
+++ b/code/modules/jobs/job_types/station_engineer.dm
@@ -48,7 +48,7 @@
uniform = /obj/item/clothing/under/rank/engineering/engineer
belt = /obj/item/storage/belt/utility/full/engi
ears = /obj/item/radio/headset/headset_eng
- head = /obj/item/clothing/head/utility/hardhat
+ head = /obj/item/clothing/head/utility/hardhat/welding/up
shoes = /obj/item/clothing/shoes/workboots
l_pocket = /obj/item/modular_computer/pda/engineering
r_pocket = /obj/item/t_scanner
diff --git a/code/modules/jobs/job_types/station_trait/veteran_advisor.dm b/code/modules/jobs/job_types/station_trait/veteran_advisor.dm
index 87ad65c2c7a..6fcb8d94707 100644
--- a/code/modules/jobs/job_types/station_trait/veteran_advisor.dm
+++ b/code/modules/jobs/job_types/station_trait/veteran_advisor.dm
@@ -63,7 +63,7 @@
uniform = /obj/item/clothing/under/rank/security/officer/formal
head = /obj/item/clothing/head/soft/veteran
- mask = /obj/item/clothing/mask/cigarette/cigar
+ mask = /obj/item/cigarette/cigar
suit = /obj/item/clothing/suit/jacket/trenchcoat
belt = /obj/item/storage/belt/holster/detective/full/ert //M1911 pistol
ears = /obj/item/radio/headset/heads/hos/advisor
diff --git a/code/modules/library/bibles.dm b/code/modules/library/bibles.dm
index 8c2ad28fead..39abe7baa50 100644
--- a/code/modules/library/bibles.dm
+++ b/code/modules/library/bibles.dm
@@ -310,25 +310,12 @@ GLOBAL_LIST_INIT(bibleitemstates, list(
if(.)
return .
- if(istype(bible_smacked, /obj/item/cult_bastard) && !IS_CULTIST(user))
- var/obj/item/cult_bastard/sword = bible_smacked
- bible_smacked.balloon_alert(user, "exorcising...")
+ if(istype(bible_smacked, /obj/item/melee/cultblade/haunted) && !IS_CULTIST(user))
+ var/obj/item/melee/cultblade/haunted/sword = bible_smacked
+ sword.balloon_alert(user, "exorcising...")
playsound(src,'sound/hallucinations/veryfar_noise.ogg',40,TRUE)
if(do_after(user, 4 SECONDS, target = sword))
playsound(src,'sound/effects/pray_chaplain.ogg',60,TRUE)
- for(var/obj/item/soulstone/stone in sword.contents)
- stone.required_role = null
- for(var/mob/living/basic/shade/shade in stone)
- var/datum/antagonist/cult/cultist = shade.mind.has_antag_datum(/datum/antagonist/cult)
- if(cultist)
- cultist.silent = TRUE
- cultist.on_removal()
- SSblackbox.record_feedback("tally", "cult_shade_purified", 1)
- shade.theme = THEME_HOLY
- shade.name = "Purified [shade.real_name]"
- shade.update_appearance(UPDATE_ICON_STATE)
- stone.release_shades(user)
- qdel(stone)
new /obj/item/nullrod/claymore(get_turf(sword))
user.visible_message(span_notice("[user] exorcises [sword]!"))
qdel(sword)
diff --git a/code/modules/loadout/loadout_helpers.dm b/code/modules/loadout/loadout_helpers.dm
index c2f83fb3ca7..ee270693ced 100644
--- a/code/modules/loadout/loadout_helpers.dm
+++ b/code/modules/loadout/loadout_helpers.dm
@@ -55,7 +55,7 @@
to_chat(preference_source.parent, span_warning("You were unable to get a loadout item ([initial(item.item_path.name)]) due to CKEY restrictions!"))
continue
- if(loadout_placement_preference == LOADOUT_OVERRIDE_CASE)
+ if(loadout_placement_preference == LOADOUT_OVERRIDE_CASE && !visuals_only)
if(!travel_suitcase)
travel_suitcase = new(loc)
new item.item_path(travel_suitcase)
diff --git a/code/modules/loadout/loadout_items.dm b/code/modules/loadout/loadout_items.dm
index 5ad95840d82..4c7d9232472 100644
--- a/code/modules/loadout/loadout_items.dm
+++ b/code/modules/loadout/loadout_items.dm
@@ -134,7 +134,13 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
if("set_name")
if(can_be_named)
- return set_name(manager, user)
+ // SKYRAT EDIT BEGIN - Description
+ return set_name(manager, user, INFO_NAMED)
+
+ if("set_desc")
+ if(can_be_named)
+ return set_name(manager, user, INFO_DESCRIBED)
+ // SKYRAT EDIT END
if("set_skin")
return set_skin(manager, user, params)
@@ -189,14 +195,17 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
return TRUE // update UI
/// Sets the name of the item.
-/datum/loadout_item/proc/set_name(datum/preference_middleware/loadout/manager, mob/user)
+// SKYRAT EDIT BEGIN - Adds descriptions with minimal copypaste
+// Generally, if this conflicts, name_slot is to be put anywhere where INFO_NAMED appears in tgcode
+/datum/loadout_item/proc/set_name(datum/preference_middleware/loadout/manager, mob/user, name_slot = INFO_NAMED)
+ var/isname = (name_slot == INFO_NAMED)
var/list/loadout = manager.preferences.read_preference(/datum/preference/loadout)
var/input_name = tgui_input_text(
user = user,
- message = "What name do you want to give the [name]? Leave blank to clear.",
- title = "[name] name",
- default = loadout?[item_path]?[INFO_NAMED], // plop in existing name (if any)
- max_length = MAX_NAME_LEN,
+ message = "What [isname ? "name" : "description"] do you want to give the [name]? Leave blank to clear.",
+ title = "[name] [isname ? "name" : "description"]",
+ default = loadout?[item_path]?[name_slot], // plop in existing name (if any)
+ max_length = isname ? MAX_NAME_LEN : MAX_DESC_LEN,
)
if(QDELETED(src) || QDELETED(user) || QDELETED(manager) || QDELETED(manager.preferences))
return FALSE
@@ -206,12 +215,13 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
return FALSE
if(input_name)
- loadout[item_path][INFO_NAMED] = input_name
+ loadout[item_path][name_slot] = input_name
else if(input_name == "")
- loadout[item_path] -= INFO_NAMED
+ loadout[item_path] -= name_slot
manager.preferences.update_preference(GLOB.preference_entries[/datum/preference/loadout], loadout)
return FALSE // no update needed
+// SKYRAT EDIT END
/// Used for reskinning an item to an alt skin.
/datum/loadout_item/proc/set_skin(datum/preference_middleware/loadout/manager, mob/user, params)
@@ -276,9 +286,18 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
equipped_item.set_greyscale(item_details[INFO_GREYSCALE])
update_flag |= equipped_item.slot_flags
- if(can_be_named && item_details?[INFO_NAMED] && !visuals_only)
- equipped_item.name = trim(item_details[INFO_NAMED], PREVENT_CHARACTER_TRIM_LOSS(MAX_NAME_LEN))
- ADD_TRAIT(equipped_item, TRAIT_WAS_RENAMED, "Loadout")
+ // SKYRAT EDIT BEGIN - DESCRIPTIONS~
+ if(can_be_named && !visuals_only)
+ var/renamed = 0
+ if(item_details?[INFO_NAMED])
+ equipped_item.name = trim(item_details[INFO_NAMED], PREVENT_CHARACTER_TRIM_LOSS(MAX_NAME_LEN))
+ renamed = 1
+ if(item_details?[INFO_DESCRIBED])
+ equipped_item.desc = trim(item_details[INFO_DESCRIBED], PREVENT_CHARACTER_TRIM_LOSS(MAX_DESC_LEN))
+ renamed = 1
+ if(renamed)
+ ADD_TRAIT(equipped_item, TRAIT_WAS_RENAMED, "Loadout")
+ // SKYRAT EDIT END
if(can_be_reskinned && item_details?[INFO_RESKIN])
var/skin_chosen = item_details[INFO_RESKIN]
@@ -394,7 +413,14 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
"button_icon" = FA_ICON_PEN,
"active_key" = INFO_NAMED,
))
-
+ // SKYRAT EDIT BEGIN - Descriptions
+ UNTYPED_LIST_ADD(button_list, list(
+ "label" = "Change description",
+ "act_key" = "set_desc",
+ "button_icon" = FA_ICON_PEN,
+ "active_key" = INFO_NAMED,
+ ))
+ // SKYRAT EDIT END
return button_list
/**
diff --git a/code/modules/lootpanel/search_object.dm b/code/modules/lootpanel/search_object.dm
index 3550c62ef41..149be76e710 100644
--- a/code/modules/lootpanel/search_object.dm
+++ b/code/modules/lootpanel/search_object.dm
@@ -25,12 +25,15 @@
if(isturf(item))
RegisterSignal(item, COMSIG_TURF_CHANGE, PROC_REF(on_turf_change))
- RegisterSignals(item, list(
- COMSIG_ATOM_ENTERED,
- COMSIG_ATOM_EXITED,
- ), PROC_REF(on_item_moved))
else
- RegisterSignal(item, COMSIG_QDELETING, PROC_REF(on_item_moved))
+ // Lest we find ourselves here again, this is intentionally stupid.
+ // It tracks items going out and user actions, otherwise they can refresh the lootpanel.
+ // If this is to be made to track everything, we'll need to make a new signal to specifically create/delete a search object
+ RegisterSignals(item, list(
+ COMSIG_ITEM_PICKUP,
+ COMSIG_MOVABLE_MOVED,
+ COMSIG_QDELETING,
+ ), PROC_REF(on_item_moved))
// Icon generation conditions //////////////
// Condition 1: Icon is complex
diff --git a/code/modules/mafia/outfits.dm b/code/modules/mafia/outfits.dm
index 5c6450adb90..3b805bd92f9 100644
--- a/code/modules/mafia/outfits.dm
+++ b/code/modules/mafia/outfits.dm
@@ -64,7 +64,7 @@
suit = /obj/item/clothing/suit/jacket/det_suit
gloves = /obj/item/clothing/gloves/color/black
head = /obj/item/clothing/head/fedora/det_hat
- mask = /obj/item/clothing/mask/cigarette
+ mask = /obj/item/cigarette
/datum/outfit/mafia/psychologist
name = "Mafia Psychologist"
diff --git a/code/modules/mapfluff/ruins/icemoonruin_code/lavaland_incursion.dm b/code/modules/mapfluff/ruins/icemoonruin_code/lavaland_incursion.dm
new file mode 100644
index 00000000000..6674123c408
--- /dev/null
+++ b/code/modules/mapfluff/ruins/icemoonruin_code/lavaland_incursion.dm
@@ -0,0 +1,20 @@
+/mob/living/basic/mining/legion/cult_skeleton
+ corpse_type = /obj/effect/mob_spawn/corpse/human/skeleton/cultist
+
+/obj/effect/mob_spawn/corpse/human/skeleton/cultist
+ name = "skeleton cultist"
+ outfit = /datum/outfit/skeleton_cultist
+
+/datum/outfit/skeleton_cultist
+ name = "Skeleton Cultist"
+ uniform = /obj/item/clothing/under/rank/civilian/chaplain
+ suit = /obj/item/clothing/suit/hooded/cultrobes
+ shoes = /obj/item/clothing/shoes/cult
+
+/obj/item/paper/crumpled/bloody/ruins/lavaland_incursion
+ name = "blood-written note"
+ default_raw_text = "
Here, far above Indecipheres, shrouded in snow and ice, I sign the death warrant of the last of a species. The Dragons kneel beneath us."
+
+/obj/item/paper/crumpled/bloody/ruins/lavaland_incursion/last
+ name = "blood-written note"
+ default_raw_text = "
SHE WAS WRONG. KILL IT? WE COULD BARELY CONTAIN IT. AND NOW, INDECIPHERES WANTS IT TO COME HOME"
diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm b/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm
index 3bb1fb48e59..4aecd64aa16 100644
--- a/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm
+++ b/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm
@@ -209,7 +209,7 @@
new /obj/item/clothing/glasses/science(src)
if(7)
new /obj/item/clothing/glasses/sunglasses/big(src)
- new /obj/item/clothing/mask/cigarette/rollie(src)
+ new /obj/item/cigarette/rollie(src)
else
//empty grave
return
@@ -289,7 +289,7 @@
deconstruct(TRUE)
return TRUE
-/obj/structure/closet/crate/grave/container_resist_act(mob/living/user)
+/obj/structure/closet/crate/grave/container_resist_act(mob/living/user, loc_required = TRUE)
if(opened)
return
// The player is trying to dig themselves out of an early grave
diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm
index e81425b9a1a..e830718e12e 100644
--- a/code/modules/mapping/mapping_helpers.dm
+++ b/code/modules/mapping/mapping_helpers.dm
@@ -1189,7 +1189,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_atoms_ontop)
if(response.errored || response.status_code != 200)
query_in_progress = FALSE
CRASH("Failed to fetch mapped custom json from url [json_url], code: [response.status_code], error: [response.error]")
- var/json_data = response["body"]
+ var/json_data = response.body
json_cache[json_url] = json_data
query_in_progress = FALSE
return json_data
diff --git a/code/modules/meteors/meteor_changeling.dm b/code/modules/meteors/meteor_changeling.dm
index 5733e2dbe93..9c25f8b6776 100644
--- a/code/modules/meteors/meteor_changeling.dm
+++ b/code/modules/meteors/meteor_changeling.dm
@@ -30,7 +30,7 @@
return TRUE
//If the meteor misses the station and deletes itself, we make absolutely sure the changeling reaches the station.
-/obj/effect/meteor/meaty/changeling/handle_stopping()
+/obj/effect/meteor/meaty/changeling/moved_off_z()
if(!landing_target)
//If our destination turf is gone for some reason, we chuck them at the observer_start landmark (usually at the center of the station) as a last resort.
landing_target = locate(/obj/effect/landmark/observer_start) in GLOB.landmarks_list
diff --git a/code/modules/meteors/meteor_dark_matteor.dm b/code/modules/meteors/meteor_dark_matteor.dm
index f9656050482..f72bbcff6fa 100644
--- a/code/modules/meteors/meteor_dark_matteor.dm
+++ b/code/modules/meteors/meteor_dark_matteor.dm
@@ -60,7 +60,7 @@
qdel(defender)
return FALSE
-/obj/effect/meteor/dark_matteor/handle_stopping()
+/obj/effect/meteor/dark_matteor/moved_off_z()
. = ..()
if(previous_security_level && SSsecurity_level.get_current_level_as_number() != SEC_LEVEL_DELTA)
SSsecurity_level.set_level(previous_security_level)
diff --git a/code/modules/meteors/meteor_types.dm b/code/modules/meteors/meteor_types.dm
index 504cca702de..199f6517abb 100644
--- a/code/modules/meteors/meteor_types.dm
+++ b/code/modules/meteors/meteor_types.dm
@@ -60,8 +60,7 @@
get_hit()
if(z != z_original || loc == get_turf(dest))
- qdel(src)
- return
+ moved_off_z()
/obj/effect/meteor/Process_Spacemove(movement_dir = 0, continuous_move = FALSE)
return TRUE //Keeps us from drifting for no reason
@@ -80,13 +79,9 @@
if(!new_loop)
return
- RegisterSignal(new_loop, COMSIG_QDELETING, PROC_REF(handle_stopping))
-
///Deals with what happens when we stop moving, IE we die
-/obj/effect/meteor/proc/handle_stopping()
- SIGNAL_HANDLER
- if(!QDELETED(src))
- qdel(src)
+/obj/effect/meteor/proc/moved_off_z()
+ qdel(src)
/obj/effect/meteor/proc/ram_turf(turf/T)
//first yell at mobs about them dying horribly
diff --git a/code/modules/mining/abandoned_crates.dm b/code/modules/mining/abandoned_crates.dm
index fa15060d282..40cb967d3a0 100644
--- a/code/modules/mining/abandoned_crates.dm
+++ b/code/modules/mining/abandoned_crates.dm
@@ -141,7 +141,7 @@
new /obj/item/lighter(src)
new /obj/item/reagent_containers/cup/glass/bottle/absinthe/premium(src)
for(var/i in 1 to 3)
- new /obj/item/clothing/mask/cigarette/rollie(src)
+ new /obj/item/cigarette/rollie(src)
if(6 to 10)
new /obj/item/melee/skateboard/pro(src)
if(11 to 15)
diff --git a/code/modules/mining/boulder_processing/boulder_types.dm b/code/modules/mining/boulder_processing/boulder_types.dm
index 1ffce8737bd..8f6889b7c84 100644
--- a/code/modules/mining/boulder_processing/boulder_types.dm
+++ b/code/modules/mining/boulder_processing/boulder_types.dm
@@ -4,7 +4,7 @@
desc = "This boulder is brimming with strange energy. Cracking it open could contain something unusual for science."
icon_state = "boulder_artifact"
/// This is the type of item that will be inside the boulder. Default is a strange object.
- var/artifact_type = /obj/item/relic
+ var/artifact_type = /obj/item/relic/lavaland
/// References to the relic inside the boulder, if any.
var/obj/item/artifact_inside
diff --git a/code/modules/mining/machine_stacking.dm b/code/modules/mining/machine_stacking.dm
index a52107ce8b2..385afb0f81f 100644
--- a/code/modules/mining/machine_stacking.dm
+++ b/code/modules/mining/machine_stacking.dm
@@ -31,13 +31,10 @@
machine = null
return ..()
-/obj/machinery/mineral/stacking_unit_console/multitool_act(mob/living/user, obj/item/I)
- if(!multitool_check_buffer(user, I))
- return
- var/obj/item/multitool/M = I
+/obj/machinery/mineral/stacking_unit_console/multitool_act(mob/living/user, obj/item/multitool/M)
M.set_buffer(src)
balloon_alert(user, "saved to multitool buffer")
- return TRUE
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/mineral/stacking_unit_console/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index a1bb35dcce6..d5abb01fec0 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -290,8 +290,12 @@
/mob/proc/is_holding_items()
return !!locate(/obj/item) in held_items
+/**
+ * Returns a list of all dropped held items.
+ * If none were dropped, returns an empty list.
+ */
/mob/proc/drop_all_held_items()
- . = FALSE
+ . = list()
for(var/obj/item/I in held_items)
. |= dropItemToGround(I)
@@ -319,24 +323,25 @@
/**
* Used to drop an item (if it exists) to the ground.
- * * Will pass as TRUE is successfully dropped, or if there is no item to drop.
- * * Will pass FALSE if the item can not be dropped due to TRAIT_NODROP via doUnEquip()
+ * * Will return null if the item wasn't dropped.
+ * * If it was, returns the item.
* If the item can be dropped, it will be forceMove()'d to the ground and the turf's Entered() will be called.
*/
/mob/proc/dropItemToGround(obj/item/I, force = FALSE, silent = FALSE, invdrop = TRUE)
if (isnull(I))
- return TRUE
+ return
SEND_SIGNAL(src, COMSIG_MOB_DROPPING_ITEM)
- . = doUnEquip(I, force, drop_location(), FALSE, invdrop = invdrop, silent = silent)
+ var/try_uneqip = doUnEquip(I, force, drop_location(), FALSE, invdrop = invdrop, silent = silent)
- if(!. || !I) //ensure the item exists and that it was dropped properly.
+ if(!try_uneqip || !I) //ensure the item exists and that it was dropped properly.
return
if(!(I.item_flags & NO_PIXEL_RANDOM_DROP))
I.pixel_x = I.base_pixel_x + rand(-6, 6)
I.pixel_y = I.base_pixel_y + rand(-6, 6)
I.do_drop_animation(src)
+ return I
//for when the item will be immediately placed in a loc other than the ground
/mob/proc/transferItemToLoc(obj/item/I, newloc = null, force = FALSE, silent = TRUE)
@@ -401,59 +406,110 @@
return items
/**
- * Used to return a list of equipped items on a human mob; does not by default include held items, see include_flags
- *
- * Argument(s):
- * * Optional - include_flags, (see obj.flags.dm) describes which optional things to include or not (pockets, accessories, held items)
+ * Returns the items that were succesfully unequipped.
*/
-
-/mob/living/carbon/human/get_equipped_items(include_flags = NONE)
- var/list/items = ..()
- if(!(include_flags & INCLUDE_POCKETS))
- items -= list(l_store, r_store, s_store)
- if((include_flags & INCLUDE_ACCESSORIES) && w_uniform)
- var/obj/item/clothing/under/worn_under = w_uniform
- items += worn_under.attached_accessories
- return items
-
/mob/living/proc/unequip_everything()
var/list/items = list()
items |= get_equipped_items(INCLUDE_POCKETS)
+ // In case something isn't actually unequipped somehow
+ var/list/dropped_items = list()
for(var/I in items)
- dropItemToGround(I)
- drop_all_held_items()
+ var/return_val = dropItemToGround(I)
+ if(!isitem(return_val))
+ continue
+ dropped_items |= return_val
+ var/return_val = drop_all_held_items()
+ if(islist(return_val))
+ dropped_items |= return_val
+ return dropped_items
+/**
+ * Try to equip an item to a slot on the mob
+ *
+ * This is a SAFE proc. Use this instead of equip_to_slot()!
+ *
+ * set qdel_on_fail to have it delete W if it fails to equip
+ *
+ * set disable_warning to disable the 'you are unable to equip that' warning.
+ *
+ * unset redraw_mob to prevent the mob icons from being redrawn at the end.
+ *
+ * Initial is used to indicate whether or not this is the initial equipment (job datums etc) or just a player doing it
+ *
+ * set indirect_action to allow insertions into "soft" locked objects, things that are easily opened by the owning mob
+ */
+/mob/proc/equip_to_slot_if_possible(obj/item/W, slot, qdel_on_fail = FALSE, disable_warning = FALSE, redraw_mob = TRUE, bypass_equip_delay_self = FALSE, initial = FALSE, indirect_action = FALSE)
+ if(!istype(W) || QDELETED(W)) //This qdeleted is to prevent stupid behavior with things that qdel during init, like say stacks
+ return FALSE
+ if(!W.mob_can_equip(src, slot, disable_warning, bypass_equip_delay_self, indirect_action = indirect_action))
+ if(qdel_on_fail)
+ qdel(W)
+ else if(!disable_warning)
+ to_chat(src, span_warning("You are unable to equip that!"))
+ return FALSE
+ equip_to_slot(W, slot, initial, redraw_mob, indirect_action = indirect_action) //This proc should not ever fail.
+ return TRUE
-/mob/living/carbon/proc/check_obscured_slots(transparent_protection)
- var/obscured = NONE
- var/hidden_slots = NONE
+/**
+ * Actually equips an item to a slot (UNSAFE)
+ *
+ * This is an UNSAFE proc. It merely handles the actual job of equipping. All the checks on
+ * whether you can or can't equip need to be done before! Use mob_can_equip() for that task.
+ *
+ *In most cases you will want to use equip_to_slot_if_possible()
+ */
+/mob/proc/equip_to_slot(obj/item/equipping, slot, initial = FALSE, redraw_mob = FALSE, indirect_action = FALSE)
+ return
- for(var/obj/item/equipped_item in get_equipped_items())
- hidden_slots |= equipped_item.flags_inv
- if(transparent_protection)
- hidden_slots |= equipped_item.transparent_protection
+/**
+ * Equip an item to the slot or delete
+ *
+ * This is just a commonly used configuration for the equip_to_slot_if_possible() proc, used to
+ * equip people when the round starts and when events happen and such.
+ *
+ * Also bypasses equip delay checks, since the mob isn't actually putting it on.
+ * Initial is used to indicate whether or not this is the initial equipment (job datums etc) or just a player doing it
+ * set indirect_action to allow insertions into "soft" locked objects, things that are easily opened by the owning mob
+ */
+/mob/proc/equip_to_slot_or_del(obj/item/W, slot, initial = FALSE, indirect_action = FALSE)
+ return equip_to_slot_if_possible(W, slot, TRUE, TRUE, FALSE, TRUE, initial, indirect_action)
- if(hidden_slots & HIDENECK)
- obscured |= ITEM_SLOT_NECK
- if(hidden_slots & HIDEMASK)
- obscured |= ITEM_SLOT_MASK
- if(hidden_slots & HIDEEYES)
- obscured |= ITEM_SLOT_EYES
- if(hidden_slots & HIDEEARS)
- obscured |= ITEM_SLOT_EARS
- if(hidden_slots & HIDEGLOVES)
- obscured |= ITEM_SLOT_GLOVES
- if(hidden_slots & HIDEJUMPSUIT)
- obscured |= ITEM_SLOT_ICLOTHING
- if(hidden_slots & HIDESHOES)
- obscured |= ITEM_SLOT_FEET
- if(hidden_slots & HIDESUITSTORAGE)
- obscured |= ITEM_SLOT_SUITSTORE
- if(hidden_slots & HIDEHEADGEAR)
- obscured |= ITEM_SLOT_HEAD
+/**
+ * Auto equip the passed in item the appropriate slot based on equipment priority
+ *
+ * puts the item "W" into an appropriate slot in a human's inventory
+ *
+ * returns 0 if it cannot, 1 if successful
+ */
+/mob/proc/equip_to_appropriate_slot(obj/item/W, qdel_on_fail = FALSE, indirect_action = FALSE, blacklist, initial) // SKYRAT EDIT CHANGE - ORIGINAL: /mob/proc/equip_to_appropriate_slot(obj/item/W, qdel_on_fail = FALSE, indirect_action = FALSE)
+ if(!istype(W))
+ return FALSE
+ var/slot_priority = W.slot_equipment_priority
- return obscured
+ if(!slot_priority)
+ slot_priority = list( \
+ ITEM_SLOT_BACK, ITEM_SLOT_ID,\
+ ITEM_SLOT_ICLOTHING, ITEM_SLOT_OCLOTHING,\
+ ITEM_SLOT_MASK, ITEM_SLOT_HEAD, ITEM_SLOT_NECK,\
+ ITEM_SLOT_FEET, ITEM_SLOT_GLOVES,\
+ ITEM_SLOT_EARS, ITEM_SLOT_EYES,\
+ ITEM_SLOT_BELT, ITEM_SLOT_SUITSTORE,\
+ ITEM_SLOT_LPOCKET, ITEM_SLOT_RPOCKET,\
+ ITEM_SLOT_DEX_STORAGE\
+ )
+ // SKYRAT EDIT ADDITION START - CUSTOMIZATION
+ if (blacklist)
+ slot_priority -= blacklist
+ // SKYRAT EDIT ADDITION END
+
+ for(var/slot in slot_priority)
+ if(equip_to_slot_if_possible(W, slot, disable_warning = TRUE, redraw_mob = TRUE, initial = initial, indirect_action = indirect_action)) // SKYRAT EDIT CHANGE - ORIGINAL: if(equip_to_slot_if_possible(W, slot, disable_warning = TRUE, redraw_mob = TRUE, indirect_action = indirect_action))
+ return TRUE
+
+ if(qdel_on_fail)
+ qdel(W)
+ return FALSE
/// Tries to equip an item, store it in open storage, or in next best storage
/obj/item/proc/equip_to_best_slot(mob/user)
@@ -536,27 +592,6 @@
if(hud_used)
hud_used.build_hand_slots()
-/mob/living/carbon/human/change_number_of_hands(amt)
- var/old_limbs = held_items.len
- if(amt < old_limbs)
- for(var/i in hand_bodyparts.len to amt step -1)
- var/obj/item/bodypart/BP = hand_bodyparts[i]
- BP.dismember()
- hand_bodyparts[i] = null
- hand_bodyparts.len = amt
- else if(amt > old_limbs)
- hand_bodyparts.len = amt
- for(var/i in old_limbs+1 to amt)
- var/path = /obj/item/bodypart/arm/left
- if(!(i % 2))
- path = /obj/item/bodypart/arm/right
-
- var/obj/item/bodypart/BP = new path ()
- BP.held_index = i
- BP.try_attach_limb(src, TRUE)
- hand_bodyparts[i] = BP
- ..() //Don't redraw hands until we have organs for them
-
//GetAllContents that is reasonable and not stupid
/mob/living/proc/get_all_gear()
var/list/processing_list = get_equipped_items(INCLUDE_POCKETS | INCLUDE_ACCESSORIES | INCLUDE_HELD)
diff --git a/code/modules/mob/living/basic/cult/constructs/_construct.dm b/code/modules/mob/living/basic/cult/constructs/_construct.dm
index 2583b3a88a6..79e6e748772 100644
--- a/code/modules/mob/living/basic/cult/constructs/_construct.dm
+++ b/code/modules/mob/living/basic/cult/constructs/_construct.dm
@@ -51,10 +51,12 @@
THEME_CULT = list(/obj/item/ectoplasm/construct),
THEME_HOLY = list(/obj/item/ectoplasm/angelic),
THEME_WIZARD = list(/obj/item/ectoplasm/mystic),
+ THEME_HERETIC = list(/obj/item/ectoplasm/construct),
)
/mob/living/basic/construct/Initialize(mapload)
. = ..()
+ throw_alert("bloodsense", /atom/movable/screen/alert/bloodsense)
AddElement(/datum/element/simple_flying)
var/list/remains = string_list(remains_by_theme[theme])
if(length(remains))
diff --git a/code/modules/mob/living/basic/cult/constructs/harvester.dm b/code/modules/mob/living/basic/cult/constructs/harvester.dm
index da8dad827b0..6d41bb43f26 100644
--- a/code/modules/mob/living/basic/cult/constructs/harvester.dm
+++ b/code/modules/mob/living/basic/cult/constructs/harvester.dm
@@ -26,13 +26,16 @@
/mob/living/basic/construct/harvester/Initialize(mapload)
. = ..()
- AddElement(\
- /datum/element/amputating_limbs,\
+ grant_abilities()
+
+/mob/living/basic/construct/harvester/proc/grant_abilities()
+ AddElement(/datum/element/wall_walker, /turf/closed/wall/mineral/cult)
+ AddComponent(\
+ /datum/component/amputating_limbs,\
surgery_time = 0,\
surgery_verb = "slicing",\
minimum_stat = CONSCIOUS,\
)
- AddElement(/datum/element/wall_walker, /turf/closed/wall/mineral/cult)
var/datum/action/innate/seek_prey/seek = new(src)
seek.Grant(src)
seek.Activate()
@@ -59,7 +62,7 @@
overlay_icon_state = "bg_demon_border"
buttontooltipstyle = "cult"
- button_icon = "icons/mob/actions/actions_cult.dmi"
+ button_icon = 'icons/mob/actions/actions_cult.dmi'
button_icon_state = "cult_mark"
/// Where is nar nar? Are we even looking?
var/tracking = FALSE
@@ -93,7 +96,6 @@
the_construct.seeking = TRUE
to_chat(the_construct, span_cult_italic("You are now tracking your master."))
-
/datum/action/innate/seek_prey
name = "Seek the Harvest"
desc = "None can hide from Nar'Sie, activate to track a survivor attempting to flee the red harvest!"
@@ -126,3 +128,115 @@
desc = "Activate to track Nar'Sie!"
button_icon_state = "sintouch"
the_construct.seeking = TRUE
+
+/mob/living/basic/construct/harvester/heretic
+ name = "Rusted Harvester"
+ real_name = "Rusted Harvester"
+ desc = "A long, thin, decrepit construct originally built to herald Nar'Sie's rise, corrupted and rusted by the forces of the Mansus to spread its will instead."
+ icon_state = "harvester"
+ icon_living = "harvester"
+ construct_spells = list(
+ /datum/action/cooldown/spell/aoe/rust_conversion,
+ /datum/action/cooldown/spell/pointed/rust_construction,
+ )
+ can_repair = FALSE
+ slowed_by_drag = FALSE
+ faction = list(FACTION_HERETIC)
+ maxHealth = 35
+ health = 35
+ melee_damage_lower = 20
+ melee_damage_upper = 25
+ // Dim green
+ lighting_cutoff_red = 10
+ lighting_cutoff_green = 20
+ lighting_cutoff_blue = 5
+ playstyle_string = "You are a Rusted Harvester, built to serve the Sanguine Apostate, twisted to work the will of the Mansus. You are fragile and weak, but you rend cultists (only) apart on each attack. Follow your Master's orders!"
+ theme = THEME_HERETIC
+
+/mob/living/basic/construct/harvester/heretic/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_MANSUS_TOUCHED, REF(src))
+ add_filter("rusted_harvester", 3, list("type" = "outline", "color" = COLOR_GREEN, "size" = 2, "alpha" = 40))
+
+
+/**
+ * Somewhat janky proc called when a heretic monster's master dies
+ * Used to kill any living Rusted Harvester
+ */
+/mob/living/proc/on_master_death()
+ return
+
+/mob/living/basic/construct/harvester/heretic/attack_animal(mob/living/simple_animal/user, list/modifiers)
+ // They're pretty fragile so this is probably necessary to prevent bullshit deaths.
+ if(user == src)
+ return
+ return ..()
+
+/mob/living/basic/construct/harvester/heretic/on_master_death()
+ to_chat(src, span_userdanger("Your link to the mansus suddenly snaps as your master perishes! Without its support, your body crumbles..."))
+ visible_message(span_alert("[src] suddenly crumbles to dust!"))
+ death()
+
+/mob/living/basic/construct/harvester/heretic/grant_abilities()
+ AddElement(/datum/element/wall_walker, or_trait = TRAIT_RUSTY)
+ AddElement(/datum/element/leeching_walk)
+ AddComponent(\
+ /datum/component/amputating_limbs,\
+ surgery_time = 1.5 SECONDS,\
+ surgery_verb = "slicing",\
+ minimum_stat = CONSCIOUS,\
+ pre_hit_callback = CALLBACK(src, PROC_REF(is_cultist_handler)),\
+ )
+ AddComponent(/datum/component/damage_aura,\
+ range = 3,\
+ brute_damage = 0.5,\
+ burn_damage = 0.5,\
+ toxin_damage = 0.5,\
+ stamina_damage = 4,\
+ simple_damage = 1.5,\
+ immune_factions = list(FACTION_HERETIC),\
+ damage_message = span_boldwarning("Your body wilts and withers as it comes near [src]'s aura."),\
+ message_probability = 7,\
+ current_owner = src,\
+ )
+ var/datum/action/innate/seek_master/heretic/seek = new(src)
+ seek.Grant(src)
+ seek.Activate()
+
+// These aren't friends they're assholes
+// Don't let them be near you!
+/mob/living/basic/construct/harvester/heretic/Life(seconds_per_tick, times_fired)
+ . = ..()
+ if(!SPT_PROB(7, seconds_per_tick))
+ return
+
+ var/turf/adjacent = get_step(src, pick(GLOB.alldirs))
+ // 90% chance to be directional, otherwise what we're on top of
+ var/turf/open/land = (isopenturf(adjacent) && prob(90)) ? adjacent : get_turf(src)
+ do_rust_heretic_act(land)
+
+ if(prob(7))
+ to_chat(src, span_notice("Eldritch energies emanate from your body."))
+
+/mob/living/basic/construct/harvester/heretic/proc/is_cultist_handler(mob/victim)
+ return IS_CULTIST(victim)
+
+/datum/action/innate/seek_master/heretic
+ name = "Seek your Master"
+ desc = "Use your direct link to the Mansus to sense where your master is located via the arrow on the top-right of your HUD."
+ button_icon = 'icons/mob/actions/actions_cult.dmi'
+ background_icon_state = "bg_heretic"
+ overlay_icon_state = "bg_heretic_border"
+ tracking = TRUE
+
+/datum/action/innate/seek_master/heretic/New(Target)
+ . = ..()
+ the_construct = Target
+ the_construct.seeking = TRUE
+ var/datum/antagonist/heretic_monster/antag = IS_HERETIC_MONSTER(the_construct)
+ if(antag)
+ the_construct.master = antag.master
+
+// no real reason for most of this weird oldcode
+/datum/action/innate/seek_master/Activate()
+ return
diff --git a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
index ce363af2364..d1b1aebf9eb 100644
--- a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
+++ b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
@@ -60,8 +60,8 @@
AddElement(/datum/element/dextrous)
AddElement(/datum/element/footstep, FOOTSTEP_MOB_BAREFOOT)
AddElement(/datum/element/basic_eating, heal_amt = 10, food_types = gorilla_food)
- AddElement(
- /datum/element/amputating_limbs, \
+ AddComponent(
+ /datum/component/amputating_limbs, \
surgery_time = 0 SECONDS, \
surgery_verb = "punches",\
)
diff --git a/code/modules/mob/living/basic/farm_animals/sheep.dm b/code/modules/mob/living/basic/farm_animals/sheep.dm
index 2fdaeda657d..5617da83a53 100644
--- a/code/modules/mob/living/basic/farm_animals/sheep.dm
+++ b/code/modules/mob/living/basic/farm_animals/sheep.dm
@@ -59,7 +59,7 @@
if(cult_converted)
for(var/mob/living/cultist as anything in invokers)
to_chat(cultist, span_cult_italic("[src] has already been sacrificed!"))
- return STOP_SACRIFICE
+ return STOP_SACRIFICE|SILENCE_SACRIFICE_MESSAGE
for(var/mob/living/cultist as anything in invokers)
to_chat(cultist, span_cult_italic("This feels a bit too cliché, don't you think?"))
diff --git a/code/modules/mob/living/basic/heretic/flesh_worm.dm b/code/modules/mob/living/basic/heretic/flesh_worm.dm
index 3c60a9b653c..92b910c717f 100644
--- a/code/modules/mob/living/basic/heretic/flesh_worm.dm
+++ b/code/modules/mob/living/basic/heretic/flesh_worm.dm
@@ -35,8 +35,8 @@
/mob/living/basic/heretic_summon/armsy/Initialize(mapload, spawn_bodyparts = TRUE, worm_length = 6)
. = ..()
AddElement(/datum/element/wall_smasher, ENVIRONMENT_SMASH_RWALLS)
- AddElement(\
- /datum/element/amputating_limbs,\
+ AddComponent(\
+ /datum/component/amputating_limbs,\
surgery_time = 0 SECONDS,\
surgery_verb = "tears",\
minimum_stat = CONSCIOUS,\
diff --git a/code/modules/mob/living/basic/heretic/rust_walker.dm b/code/modules/mob/living/basic/heretic/rust_walker.dm
index 24b77d4d0b8..230747efa5a 100644
--- a/code/modules/mob/living/basic/heretic/rust_walker.dm
+++ b/code/modules/mob/living/basic/heretic/rust_walker.dm
@@ -19,7 +19,7 @@
AddElement(/datum/element/footstep, FOOTSTEP_MOB_RUST)
var/static/list/grantable_spells = list(
- /datum/action/cooldown/spell/aoe/rust_conversion/small = BB_GENERIC_ACTION,
+ /datum/action/cooldown/spell/aoe/rust_conversion = BB_GENERIC_ACTION,
/datum/action/cooldown/spell/basic_projectile/rust_wave/short = BB_TARGETED_ACTION,
)
grant_actions_by_list(grantable_spells)
diff --git a/code/modules/mob/living/basic/lavaland/goliath/tentacle.dm b/code/modules/mob/living/basic/lavaland/goliath/tentacle.dm
index 030ae6d64b8..ba6346ea191 100644
--- a/code/modules/mob/living/basic/lavaland/goliath/tentacle.dm
+++ b/code/modules/mob/living/basic/lavaland/goliath/tentacle.dm
@@ -20,7 +20,7 @@
if (ismineralturf(loc))
var/turf/closed/mineral/floor = loc
floor.gets_drilled()
- if (!isopenturf(loc) || isspaceturf(loc) || isopenspaceturf(loc))
+ if (!isopenturf(loc) || is_space_or_openspace(loc))
return INITIALIZE_HINT_QDEL
for (var/obj/effect/goliath_tentacle/tentacle in loc)
if (tentacle != src)
diff --git a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm
index ccab4d3c031..ad10e30acec 100644
--- a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm
+++ b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm
@@ -43,8 +43,8 @@
AddElement(/datum/element/mob_grabber)
AddElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW)
AddElement(/datum/element/basic_eating, food_types = target_foods)
- AddElement(\
- /datum/element/amputating_limbs,\
+ AddComponent(\
+ /datum/component/amputating_limbs,\
surgery_verb = "begins snipping",\
target_zones = GLOB.arm_zones,\
)
diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoarding.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoarding.dm
index 7484cbfe675..ad9a56b3acf 100644
--- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoarding.dm
+++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoarding.dm
@@ -29,7 +29,7 @@
/datum/ai_behavior/find_and_set/hoard_location/search_tactic(datum/ai_controller/controller, locate_path, search_range)
for(var/turf/open/candidate in oview(search_range, controller.pawn))
- if(isspaceturf(candidate) || isopenspaceturf(candidate))
+ if(is_space_or_openspace(candidate))
continue
if(candidate.is_blocked_turf(source_atom = controller.pawn))
continue
diff --git a/code/modules/mob/living/basic/slime/defense.dm b/code/modules/mob/living/basic/slime/defense.dm
index b747c242019..a3242525170 100644
--- a/code/modules/mob/living/basic/slime/defense.dm
+++ b/code/modules/mob/living/basic/slime/defense.dm
@@ -19,7 +19,7 @@
return
attacker.visible_message(span_warning("[attacker] manages to wrestle \the [defender_slime.name] off!"), span_notice("You manage to wrestle \the [defender_slime.name] off!"))
- playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+ playsound(loc, 'sound/weapons/shove.ogg', 50, TRUE, -1)
defender_slime.discipline_slime()
diff --git a/code/modules/mob/living/basic/vermin/mouse.dm b/code/modules/mob/living/basic/vermin/mouse.dm
index 3cce5e34817..724833af16f 100644
--- a/code/modules/mob/living/basic/vermin/mouse.dm
+++ b/code/modules/mob/living/basic/vermin/mouse.dm
@@ -117,11 +117,19 @@
. = ..(TRUE)
// Now if we were't ACTUALLY gibbed, spawn the dead mouse
if(!gibbed)
- var/obj/item/food/deadmouse/mouse = new(loc)
- mouse.copy_corpse(src)
- if(HAS_TRAIT(src, TRAIT_BEING_SHOCKED))
- mouse.desc = "They're toast."
- mouse.add_atom_colour("#3A3A3A", FIXED_COLOUR_PRIORITY)
+ var/make_a_corpse = TRUE
+ var/place_to_make_corpse = loc
+ if(istype(loc, /obj/item/clothing/head/mob_holder))//If our mouse is dying in place holder we want to put the dead mouse where the place holder was
+ var/obj/item/clothing/head/mob_holder/found_holder = loc
+ place_to_make_corpse = found_holder.loc
+ if(istype(found_holder.loc, /obj/machinery/microwave))//Microwaves gib things that die when cooked, so we don't need to make a dead body too
+ make_a_corpse = FALSE
+ if(make_a_corpse)
+ var/obj/item/food/deadmouse/mouse = new(place_to_make_corpse)
+ mouse.copy_corpse(src)
+ if(HAS_TRAIT(src, TRAIT_BEING_SHOCKED))
+ mouse.desc = "They're toast."
+ mouse.add_atom_colour("#3A3A3A", FIXED_COLOUR_PRIORITY)
qdel(src)
/mob/living/basic/mouse/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 8b970605af8..5ac120fcee3 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -227,7 +227,7 @@
****************************************************/
//Gets blood from mob to a container or other mob, preserving all data in it.
-/mob/living/proc/transfer_blood_to(atom/movable/AM, amount, forced)
+/mob/living/proc/transfer_blood_to(atom/movable/AM, amount, forced, ignore_incompatibility)
if(!blood_volume || !AM.reagents)
return FALSE
if(blood_volume < BLOOD_VOLUME_BAD && !forced)
@@ -254,7 +254,7 @@
if((D.spread_flags & DISEASE_SPREAD_SPECIAL) || (D.spread_flags & DISEASE_SPREAD_NON_CONTAGIOUS))
continue
C.ForceContractDisease(D)
- if(!(blood_data["blood_type"] in get_safe_blood(C.dna.blood_type)))
+ if(!(blood_data["blood_type"] in get_safe_blood(C.dna.blood_type)) && !(ignore_incompatibility))
C.reagents.add_reagent(/datum/reagent/toxin, amount * 0.5)
return TRUE
@@ -349,6 +349,15 @@
if(safe)
. = safe
+/**
+ * Returns TRUE if src is compatible with donor's blood, otherwise FALSE.
+ * * donor: Carbon mob, the one that is donating blood.
+ */
+/mob/living/carbon/proc/get_blood_compatibility(mob/living/carbon/donor)
+ var/patient_blood_data = get_blood_data(get_blood_id())
+ var/donor_blood_data = donor.get_blood_data(donor.get_blood_id())
+ return donor_blood_data["blood_type"] in get_safe_blood(patient_blood_data["blood_type"])
+
//to add a splatter of blood or other mob liquid.
/mob/living/proc/add_splatter_floor(turf/T, small_drip)
if(get_blood_id() != /datum/reagent/blood)
diff --git a/code/modules/mob/living/carbon/alien/adult/alien_powers.dm b/code/modules/mob/living/carbon/alien/adult/alien_powers.dm
index e9c5199cf3f..2da7974318a 100644
--- a/code/modules/mob/living/carbon/alien/adult/alien_powers.dm
+++ b/code/modules/mob/living/carbon/alien/adult/alien_powers.dm
@@ -250,7 +250,8 @@ Doesn't work on other aliens/AI.*/
plasma_cost = 50
/datum/action/cooldown/alien/acid/neurotoxin/IsAvailable(feedback = FALSE)
- if(owner.is_muzzled())
+ var/mob/living/carbon/as_carbon = owner
+ if(istype(as_carbon) && as_carbon.is_mouth_covered(ITEM_SLOT_MASK))
return FALSE
if(!isturf(owner.loc))
return FALSE
diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm
index 933e6cb86d6..a96be12f3b4 100644
--- a/code/modules/mob/living/carbon/alien/alien.dm
+++ b/code/modules/mob/living/carbon/alien/alien.dm
@@ -76,6 +76,7 @@
return pick (list("xltrails_1", "xltrails2"))
else
return pick (list("xttrails_1", "xttrails2"))
+
/*----------------------------------------
Proc: AddInfectionImages()
Des: Gives the client of the alien an image on each infected mob.
diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm
index 1c1d181444a..e4a17334374 100644
--- a/code/modules/mob/living/carbon/alien/special/facehugger.dm
+++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm
@@ -24,9 +24,12 @@
layer = MOB_LAYER
max_integrity = 100
item_flags = XENOMORPH_HOLDABLE
- special_desc_requirement = EXAMINE_CHECK_JOB //SKYRAT EDIT
- special_desc_jobs = list("Scientist, Research Director") //SKYRAT EDIT
- special_desc = "This alien is an extremely dangerous life form capable of creating a xenomorph. You would know well not to approach without full body biological protection." //SKYRAT EDIT
+ // SKYRAT EDIT ADDITION START
+ special_desc_requirement = EXAMINE_CHECK_JOB
+ special_desc_jobs = list("Scientist, Research Director")
+ special_desc = "This alien is an extremely dangerous life form capable of creating a xenomorph. You would know well not to approach without full body biological protection."
+ // SKYRAT EDIT ADDITION END
+ slowdown = 2
var/stat = CONSCIOUS //UNCONSCIOUS is the idle state in this case
var/sterile = FALSE
@@ -42,6 +45,7 @@
)
AddElement(/datum/element/connect_loc, loc_connections)
AddElement(/datum/element/atmos_sensitive, mapload)
+ AddElement(/datum/element/muffles_speech)
RegisterSignal(src, COMSIG_LIVING_TRYING_TO_PULL, PROC_REF(react_to_mob))
@@ -125,7 +129,7 @@
/obj/item/clothing/mask/facehugger/proc/valid_to_attach(mob/living/hit_mob)
// valid targets: carbons except aliens and devils
- // facehugger state early exit checks
+ // facehugger state early exit checks (Note: Melbert does not want dead people to be huggable)
if(stat != CONSCIOUS)
return FALSE
if(attached)
@@ -174,9 +178,12 @@
log_combat(target, src, "was facehugged by")
return TRUE // time for a smoke
-/obj/item/clothing/mask/facehugger/proc/Attach(mob/living/M)
- if(!valid_to_attach(M))
+/obj/item/clothing/mask/facehugger/proc/Attach(mob/living/victim)
+ if(!valid_to_attach(victim))
return
+
+ if(victim.stat < UNCONSCIOUS) //sorry bro you gotta be awake
+ victim.say("AAAA!!") //triggers muffled speech and also visual feedback i guess
// early returns and validity checks done: attach.
attached++
//ensure we detach once we no longer need to be attached
@@ -184,12 +191,12 @@
if(!sterile)
- M.take_bodypart_damage(strength,0) //done here so that humans in helmets take damage
- M.Unconscious(MAX_IMPREGNATION_TIME/0.3) //something like 25 ticks = 20 seconds with the default settings
-
+ victim.take_bodypart_damage(strength,0) //done here so that humans in helmets take damage
+ if(real && !sterile)
+ victim.Knockdown(5 SECONDS)
GoIdle() //so it doesn't jump the people that tear it off
- addtimer(CALLBACK(src, PROC_REF(Impregnate), M), rand(MIN_IMPREGNATION_TIME, MAX_IMPREGNATION_TIME))
+ addtimer(CALLBACK(src, PROC_REF(Impregnate), victim), rand(MIN_IMPREGNATION_TIME, MAX_IMPREGNATION_TIME))
/obj/item/clothing/mask/facehugger/proc/detach()
attached = 0
@@ -252,6 +259,29 @@
visible_message(span_danger("[src] curls up into a ball!"))
+ // chest maybe because getting slammed in the chest would knock it off your face while dead
+ AddComponent(/datum/component/knockoff, knockoff_chance = 40, target_zones = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST), slots_knockoffable = slot_flags)
+
+/obj/item/clothing/mask/facehugger/allow_attack_hand_drop(mob/living/carbon/human/user)
+ if(!real || sterile || user.get_organ_by_type(/obj/item/organ/internal/body_egg/alien_embryo))
+ return ..()
+ if(istype(user) && ishuman(loc) && stat != DEAD)
+ if(user == loc && user.get_item_by_slot(slot_flags) == src)
+ to_chat(user, span_userdanger("[src] is latched on too tight! Get help or wait for it to let go!"))
+ return FALSE
+ return ..()
+
+/obj/item/clothing/mask/facehugger/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
+ var/mob/living/carbon/human/wearer = loc
+ if(!istype(wearer) || user != wearer)
+ return
+ if(!real || sterile || user.get_organ_by_type(/obj/item/organ/internal/body_egg/alien_embryo))
+ return ..()
+ if(wearer.get_item_by_slot(slot_flags) == src && stat != DEAD)
+ to_chat(user, span_userdanger("[src] is latched on too tight! Get help or wait for it to let go!"))
+ return
+ return ..()
+
/proc/CanHug(mob/living/M)
if(!istype(M))
return FALSE
@@ -270,10 +300,13 @@
/obj/item/clothing/mask/facehugger/lamarr
name = "Lamarr"
desc = "The Research Director's pet, a domesticated and debeaked xenomorph facehugger. Friendly, but may still try to couple with your head."
- special_desc_requirement = EXAMINE_CHECK_ROLE //SKYRAT EDIT
- special_desc_roles = list("ROLE_ALIEN") //SKYRAT EDIT
- special_desc = "This young one has been cruelly mutilated. It lacks the capability to fill a host with our sisters." //SKYRAT EDIT
+ // SKYRAT EDIT ADDITION START: job-restricted examine text
+ special_desc_requirement = EXAMINE_CHECK_ROLE
+ special_desc_roles = list("ROLE_ALIEN")
+ special_desc = "This young one has been cruelly mutilated. It lacks the capability to fill a host with our sisters."
+ // SKYRAT EDIT ADDITION END
sterile = TRUE
+ slowdown = 1.5 //lamarr is too fat after being fed in captivity to effectively slow people down or something
/obj/item/clothing/mask/facehugger/dead
icon_state = "facehugger_dead"
@@ -293,6 +326,7 @@
real = FALSE
sterile = TRUE
tint = 3 //Makes it feel more authentic when it latches on
+ slowdown = 0
/obj/item/clothing/mask/facehugger/toy/Die()
return
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 0cd546b38dd..a18b0ae3a4c 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -6,6 +6,7 @@
GLOB.carbon_list += src
ADD_TRAIT(src, TRAIT_CAN_HOLD_ITEMS, INNATE_TRAIT) // Carbons are assumed to be innately capable of having arms, we check their arms count instead
+ breathing_loop = new(src)
/mob/living/carbon/Destroy()
//This must be done first, so the mob ghosts correctly before DNA etc is nulled
@@ -21,6 +22,7 @@
qdel(scar)
remove_from_all_data_huds()
QDEL_NULL(dna)
+ QDEL_NULL(breathing_loop)
GLOB.carbon_list -= src
/mob/living/carbon/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
@@ -234,13 +236,6 @@
. = ..()
loc.handle_fall(src)//it's loc so it doesn't call the mob's handle_fall which does nothing
-/mob/living/carbon/is_muzzled()
- for (var/obj/item/clothing/clothing in get_equipped_items())
- if(clothing.clothing_flags & BLOCKS_SPEECH)
- return TRUE
- return FALSE
-
-
/mob/living/carbon/resist_buckle()
if(!HAS_TRAIT(src, TRAIT_RESTRAINED))
buckled.user_unbuckle_mob(src, src)
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index c5e0c5b515a..968a09a46b4 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -461,7 +461,7 @@
// this way, we only visibly try to examine ourselves if we have something embedded, otherwise we'll still hug ourselves :)
visible_message(span_notice("[src] examines [p_them()]self."), \
span_notice("You check yourself for shrapnel."))
- if(I.isEmbedHarmless())
+ if(I.is_embed_harmless())
to_chat(src, "\t There is \a [I] stuck to your [LB.name]!")
else
to_chat(src, "\t There is \a [I] embedded in your [LB.name]!")
@@ -742,7 +742,9 @@
/mob/living/carbon/get_shove_flags(mob/living/shover, obj/item/weapon)
. = ..()
. |= SHOVE_CAN_STAGGER
- if(IsKnockdown() && !IsParalyzed())
+ if(IsKnockdown() && !IsParalyzed() && HAS_TRAIT(src, TRAIT_STUN_ON_NEXT_SHOVE))
. |= SHOVE_CAN_KICK_SIDE
+ if(HAS_TRAIT(src, TRAIT_NO_SIDE_KICK)) // added as an extra check, just in case
+ . &= ~SHOVE_CAN_KICK_SIDE
#undef SHAKE_ANIMATION_OFFSET
diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm
index 240f45521a7..0ee71842400 100644
--- a/code/modules/mob/living/carbon/carbon_defines.dm
+++ b/code/modules/mob/living/carbon/carbon_defines.dm
@@ -53,6 +53,8 @@
///This is used to determine if the mob failed a breath. If they did fail a breath, they will attempt to breathe each tick, otherwise just once per 4 ticks.
var/failed_last_breath = FALSE
+ ///Sound loop for breathing when using internals
+ var/datum/looping_sound/breathing/breathing_loop
/// Used in [carbon/proc/check_breath] and [lungs/proc/check_breath]]
var/co2overloadtime = null
diff --git a/code/modules/mob/living/carbon/death.dm b/code/modules/mob/living/carbon/death.dm
index 5fdd3498f15..8d4e66bccda 100644
--- a/code/modules/mob/living/carbon/death.dm
+++ b/code/modules/mob/living/carbon/death.dm
@@ -3,7 +3,7 @@
return
losebreath = 0
-
+ breathing_loop.stop() //This would've happened eventually but it's nice to make it stop immediatelly in this case
if(!gibbed)
add_memory_in_range(src, 7, /datum/memory/witnessed_death, protagonist = src)
reagents.end_metabolization(src)
diff --git a/code/modules/mob/living/carbon/emote.dm b/code/modules/mob/living/carbon/emote.dm
index 399dcf3d767..fd9807c32f1 100644
--- a/code/modules/mob/living/carbon/emote.dm
+++ b/code/modules/mob/living/carbon/emote.dm
@@ -22,7 +22,6 @@
key = "clap"
key_third_person = "claps"
message = "claps."
- muzzle_ignore = TRUE
hands_use_check = TRUE
emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
audio_cooldown = 5 SECONDS
@@ -191,7 +190,6 @@
message_param = "snaps their fingers at %t."
emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
hands_use_check = TRUE
- muzzle_ignore = TRUE
/datum/emote/living/carbon/snap/get_sound(mob/living/user)
if(ishuman(user))
diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm
index 071dadac1c0..c71962eb034 100644
--- a/code/modules/mob/living/carbon/examine.dm
+++ b/code/modules/mob/living/carbon/examine.dm
@@ -39,7 +39,7 @@
var/list/msg = list("")
for(var/obj/item/bodypart/bodypart as anything in bodyparts)
for(var/obj/item/embedded_item as anything in bodypart.embedded_objects)
- if(embedded_item.isEmbedHarmless())
+ if(embedded_item.is_embed_harmless())
msg += "[t_He] [t_has] [icon2html(embedded_item, user)] \a [embedded_item] stuck to [t_his] [bodypart.name]!\n"
else
msg += "[t_He] [t_has] [icon2html(embedded_item, user)] \a [embedded_item] embedded in [t_his] [bodypart.name]!\n"
diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm
index 73d4a39ae9b..b3951731a8f 100644
--- a/code/modules/mob/living/carbon/human/dummy.dm
+++ b/code/modules/mob/living/carbon/human/dummy.dm
@@ -76,7 +76,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
cut_overlays(TRUE)
/mob/living/carbon/human/dummy/setup_human_dna()
- randomize_human(src, randomize_mutations = FALSE)
+ randomize_human_normie(src, randomize_mutations = FALSE)
/mob/living/carbon/human/dummy/log_mob_tag(text)
return
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 492db870537..2bd4867166c 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -179,7 +179,7 @@
disabled += body_part
missing -= body_part.body_zone
for(var/obj/item/I in body_part.embedded_objects)
- if(I.isEmbedHarmless())
+ if(I.is_embed_harmless())
msg += "[t_He] [t_has] [icon2html(I, user)] \a [I] stuck to [t_his] [body_part.name]!\n"
else
msg += "[t_He] [t_has] [icon2html(I, user)] \a [I] embedded in [t_his] [body_part.name]!\n"
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 022b143665d..3a2d406afc1 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -57,7 +57,7 @@
ADD_TRAIT(src, TRAIT_AGEUSIA, NO_TONGUE_TRAIT)
/mob/living/carbon/human/proc/setup_human_dna()
- randomize_human(src, randomize_mutations = TRUE)
+ randomize_human_normie(src, randomize_mutations = TRUE)
/mob/living/carbon/human/Destroy()
QDEL_NULL(physiology)
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 2b03f703af0..acec80f5652 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -146,8 +146,10 @@
return
if(!(shove_flags & SHOVE_KNOCKDOWN_BLOCKED))
target.Knockdown(SHOVE_KNOCKDOWN_HUMAN)
+ target.apply_status_effect(/datum/status_effect/next_shove_stuns)
if(!HAS_TRAIT(src, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED))
Knockdown(SHOVE_KNOCKDOWN_COLLATERAL)
+ apply_status_effect(/datum/status_effect/next_shove_stuns)
target.visible_message(span_danger("[shover] shoves [target.name] into [name]!"),
span_userdanger("You're shoved into [name] by [shover]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
to_chat(src, span_danger("You shove [target.name] into [name]!"))
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index ffc17a43b75..9bd2e0e27a5 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -26,8 +26,9 @@
/mob/living/carbon/human/Move(NewLoc, direct)
. = ..()
- if(shoes && body_position == STANDING_UP && loc == NewLoc && has_gravity(loc))
- SEND_SIGNAL(shoes, COMSIG_SHOES_STEP_ACTION)
+ if(shoes && body_position == STANDING_UP && has_gravity(loc))
+ if((. && !moving_diagonally) || (!. && moving_diagonally == SECOND_DIAG_STEP))
+ SEND_SIGNAL(shoes, COMSIG_SHOES_STEP_ACTION)
if(wear_neck && body_position == STANDING_UP && loc == NewLoc && has_gravity(loc))
SEND_SIGNAL(wear_neck, COMSIG_NECK_STEP_ACTION) //Bubbers addition: Bell collars ring. (#1537) todo: modularize this
diff --git a/code/modules/mob/living/carbon/human/human_say.dm b/code/modules/mob/living/carbon/human/human_say.dm
index ae41bad2cf3..00b703c6c71 100644
--- a/code/modules/mob/living/carbon/human/human_say.dm
+++ b/code/modules/mob/living/carbon/human/human_say.dm
@@ -69,6 +69,9 @@
var/obj/item/radio/headset/dongle = ears
if(!istype(dongle))
return FALSE
+ var/area/our_area = get_area(src)
+ if(our_area.area_flags & BINARY_JAMMING)
+ return FALSE
return dongle.translate_binary
/mob/living/carbon/human/radio(message, list/message_mods = list(), list/spans, language) //Poly has a copy of this, lazy bastard
diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm
index ee3b367d365..59abe696d5e 100644
--- a/code/modules/mob/living/carbon/human/human_update_icons.dm
+++ b/code/modules/mob/living/carbon/human/human_update_icons.dm
@@ -118,7 +118,12 @@ There are several things that need to be remembered:
var/mutant_styles = NONE // SKYRAT EDIT ADDITON - mutant styles to pass down to build_worn_icon.
//BEGIN SPECIES HANDLING
if((bodyshape & BODYSHAPE_DIGITIGRADE) && (uniform.supports_variations_flags & CLOTHING_DIGITIGRADE_VARIATION))
- icon_file = DIGITIGRADE_UNIFORM_FILE
+ icon_file = uniform.worn_icon_digi || DIGITIGRADE_UNIFORM_FILE // SKYRAT EDIT CHANGE - ORIGINAL: icon_file = DIGITIGRADE_UNIFORM_FILE
+ digi = TRUE // SKYRAT EDIT ADDITION - Digi female gender shaping
+ // SKYRAT EDIT ADDITION - birbs
+ else if(bodyshape & BODYSHAPE_CUSTOM)
+ icon_file = dna.species.generate_custom_worn_icon(OFFSET_UNIFORM, w_uniform, src) // Might have to refactor how this works eventually, maybe.
+ // SKYRAT EDIT END
//Female sprites have lower priority than digitigrade sprites
if(!dna.species.no_gender_shaping && dna.species.sexes && (bodyshape & BODYSHAPE_HUMANOID) && physique == FEMALE && !(female_sprite_flags & NO_FEMALE_UNIFORM)) // SKYRAT EDIT CHANGE - ORIGINAL: else if(dna.species.sexes && (bodyshape & BODYSHAPE_HUMANOID) && physique == FEMALE && !(uniform.female_sprite_flags & NO_FEMALE_UNIFORM)) //Agggggggghhhhh
woman = TRUE
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index 755a1e4ad54..40dd1d4c054 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -1,3 +1,20 @@
+
+/**
+ * Used to return a list of equipped items on a human mob; does not by default include held items, see include_flags
+ *
+ * Argument(s):
+ * * Optional - include_flags, (see obj.flags.dm) describes which optional things to include or not (pockets, accessories, held items)
+ */
+
+/mob/living/carbon/human/get_equipped_items(include_flags = NONE)
+ var/list/items = ..()
+ if(!(include_flags & INCLUDE_POCKETS))
+ items -= list(l_store, r_store, s_store)
+ if((include_flags & INCLUDE_ACCESSORIES) && w_uniform)
+ var/obj/item/clothing/under/worn_under = w_uniform
+ items += worn_under.attached_accessories
+ return items
+
/mob/living/carbon/human/can_equip(obj/item/equip_target, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, ignore_equipped = FALSE, indirect_action = FALSE)
if(SEND_SIGNAL(src, COMSIG_HUMAN_EQUIPPING_ITEM, equip_target, slot) == COMPONENT_BLOCK_EQUIP)
return FALSE
@@ -138,8 +155,6 @@
if(glasses)
return
glasses = equipping
- if(glasses.glass_colour_type)
- update_glasses_color(glasses, 1)
if(glasses.vision_flags || glasses.invis_override || glasses.invis_view || !isnull(glasses.lighting_cutoff))
update_sight()
update_worn_glasses()
@@ -246,10 +261,8 @@
update_worn_gloves()
else if(I == glasses)
glasses = null
- var/obj/item/clothing/glasses/G = I
- if(G.glass_colour_type)
- update_glasses_color(G, 0)
- if(G.vision_flags || G.invis_override || G.invis_view || !isnull(G.lighting_cutoff))
+ var/obj/item/clothing/glasses/old_glasses = I
+ if(old_glasses.vision_flags || old_glasses.invis_override || old_glasses.invis_view || !isnull(old_glasses.lighting_cutoff))
update_sight()
if(!QDELETED(src))
update_worn_glasses()
@@ -403,3 +416,24 @@
return
stored.attack_hand(src) // take out thing from item in storage slot
return
+
+/mob/living/carbon/human/change_number_of_hands(amt)
+ var/old_limbs = held_items.len
+ if(amt < old_limbs)
+ for(var/i in hand_bodyparts.len to amt step -1)
+ var/obj/item/bodypart/BP = hand_bodyparts[i]
+ BP.dismember()
+ hand_bodyparts[i] = null
+ hand_bodyparts.len = amt
+ else if(amt > old_limbs)
+ hand_bodyparts.len = amt
+ for(var/i in old_limbs+1 to amt)
+ var/path = /obj/item/bodypart/arm/left
+ if(!(i % 2))
+ path = /obj/item/bodypart/arm/right
+
+ var/obj/item/bodypart/BP = new path ()
+ BP.held_index = i
+ BP.try_attach_limb(src, TRUE)
+ hand_bodyparts[i] = BP
+ ..() //Don't redraw hands until we have organs for them
diff --git a/code/modules/mob/living/carbon/human/species_types/snail.dm b/code/modules/mob/living/carbon/human/species_types/snail.dm
index f0f526a1489..2c6d27a1886 100644
--- a/code/modules/mob/living/carbon/human/species_types/snail.dm
+++ b/code/modules/mob/living/carbon/human/species_types/snail.dm
@@ -90,8 +90,7 @@
. = ..()
var/obj/item/storage/backpack/bag = new_snailperson.get_item_by_slot(ITEM_SLOT_BACK)
if(!istype(bag, /obj/item/storage/backpack/snail))
- if(new_snailperson.dropItemToGround(bag)) //returns TRUE even if its null
- new_snailperson.equip_to_slot_or_del(new /obj/item/storage/backpack/snail(new_snailperson), ITEM_SLOT_BACK)
+ new_snailperson.equip_to_slot_or_del(new /obj/item/storage/backpack/snail(new_snailperson), ITEM_SLOT_BACK)
new_snailperson.AddElement(/datum/element/lube_walking, require_resting = TRUE)
new_snailperson.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/snail, multiplicative_slowdown = snail_speed_mod)
diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm
index de6a2692c9b..e59a6328aa7 100644
--- a/code/modules/mob/living/carbon/inventory.dm
+++ b/code/modules/mob/living/carbon/inventory.dm
@@ -1,3 +1,33 @@
+/mob/living/carbon/proc/check_obscured_slots(transparent_protection)
+ var/obscured = NONE
+ var/hidden_slots = NONE
+
+ for(var/obj/item/equipped_item in get_equipped_items())
+ hidden_slots |= equipped_item.flags_inv
+ if(transparent_protection)
+ hidden_slots |= equipped_item.transparent_protection
+
+ if(hidden_slots & HIDENECK)
+ obscured |= ITEM_SLOT_NECK
+ if(hidden_slots & HIDEMASK)
+ obscured |= ITEM_SLOT_MASK
+ if(hidden_slots & HIDEEYES)
+ obscured |= ITEM_SLOT_EYES
+ if(hidden_slots & HIDEEARS)
+ obscured |= ITEM_SLOT_EARS
+ if(hidden_slots & HIDEGLOVES)
+ obscured |= ITEM_SLOT_GLOVES
+ if(hidden_slots & HIDEJUMPSUIT)
+ obscured |= ITEM_SLOT_ICLOTHING
+ if(hidden_slots & HIDESHOES)
+ obscured |= ITEM_SLOT_FEET
+ if(hidden_slots & HIDESUITSTORAGE)
+ obscured |= ITEM_SLOT_SUITSTORE
+ if(hidden_slots & HIDEHEADGEAR)
+ obscured |= ITEM_SLOT_HEAD
+
+ return obscured
+
/mob/living/carbon/get_item_by_slot(slot_id)
switch(slot_id)
if(ITEM_SLOT_BACK)
@@ -280,6 +310,8 @@
internal = null
target_tank.after_internals_closed(src)
update_mob_action_buttons()
+ //To make sure it stops at a timely manner when you turn off internals
+ breathing_loop.stop()
return TRUE
/// Close the the currently open external (that's EX-ternal) air tank. Returns TRUE if successful.
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index 5b05d72e120..42a5d619926 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -71,6 +71,7 @@
// Second link in a breath chain, calls [carbon/proc/check_breath()]
/mob/living/carbon/proc/breathe(seconds_per_tick, times_fired)
var/obj/item/organ/internal/lungs = get_organ_slot(ORGAN_SLOT_LUNGS)
+ var/is_on_internals = FALSE
if(SEND_SIGNAL(src, COMSIG_CARBON_ATTEMPT_BREATHE, seconds_per_tick, times_fired) & COMSIG_CARBON_BLOCK_BREATH)
return
@@ -144,15 +145,26 @@
breath = loc.remove_air(breath_moles)
else //Breathe from loc as obj again
+ is_on_internals = TRUE
+
if(isobj(loc))
var/obj/loc_as_obj = loc
loc_as_obj.handle_internal_lifeform(src,0)
- check_breath(breath)
+ if(check_breath(breath) && is_on_internals)
+ try_breathing_sound(breath)
if(breath)
loc.assume_air(breath)
+//Tries to play the carbon a breathing sound when using internals, also invokes check_breath
+/mob/living/carbon/proc/try_breathing_sound(breath)
+ var/should_be_on = canon_client?.prefs?.read_preference(/datum/preference/toggle/sound_breathing)
+ if(should_be_on && !breathing_loop.timer_id)
+ breathing_loop.start()
+ else if(!should_be_on && breathing_loop.timer_id)
+ breathing_loop.stop()
+
/mob/living/carbon/proc/has_smoke_protection()
if(HAS_TRAIT(src, TRAIT_NOBREATH))
return TRUE
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 74c01b93621..939ecab4365 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -423,7 +423,7 @@
if(!user.get_bodypart(BODY_ZONE_HEAD))
return FALSE
- if(user.is_muzzled() || user.is_mouth_covered(ITEM_SLOT_MASK))
+ if(user.is_mouth_covered(ITEM_SLOT_MASK))
to_chat(user, span_warning("You can't bite with your mouth covered!"))
return FALSE
@@ -671,7 +671,7 @@
playsound(target, 'sound/effects/glassbash.ogg', 50, TRUE, -1)
else
do_attack_animation(target, ATTACK_EFFECT_DISARM)
- playsound(target, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+ playsound(target, 'sound/weapons/shove.ogg', 50, TRUE, -1)
if (ishuman(target) && isnull(weapon))
var/mob/living/carbon/human/human_target = target
human_target.w_uniform?.add_fingerprint(src)
@@ -712,6 +712,7 @@
return
if((shove_flags & SHOVE_BLOCKED) && !(shove_flags & (SHOVE_KNOCKDOWN_BLOCKED|SHOVE_CAN_KICK_SIDE)))
target.Knockdown(SHOVE_KNOCKDOWN_SOLID)
+ target.apply_status_effect(/datum/status_effect/next_shove_stuns)
target.visible_message(span_danger("[name] shoves [target.name], knocking [target.p_them()] down!"),
span_userdanger("You're knocked down from a shove by [name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
to_chat(src, span_danger("You shove [target.name], knocking [target.p_them()] down!"))
@@ -720,6 +721,7 @@
if(shove_flags & SHOVE_CAN_KICK_SIDE) //KICK HIM IN THE NUTS
target.Paralyze(SHOVE_CHAIN_PARALYZE)
+ target.apply_status_effect(/datum/status_effect/no_side_kick)
target.visible_message(span_danger("[name] kicks [target.name] onto [target.p_their()] side!"),
span_userdanger("You're kicked onto your side by [name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
to_chat(src, span_danger("You kick [target.name] onto [target.p_their()] side!"))
@@ -732,10 +734,8 @@
//Take their lunch money
var/target_held_item = target.get_active_held_item()
var/append_message = weapon ? " with [weapon]" : ""
- if(!is_type_in_typecache(target_held_item, GLOB.shove_disarming_types)) //It's too expensive we'll get caught
- target_held_item = null
-
- if(target_held_item && target.get_timed_status_effect_duration(/datum/status_effect/staggered))
+ // If it's in our typecache, they're staggered and it exists, disarm. If they're knocked down, disarm too.
+ if(target_held_item && target.get_timed_status_effect_duration(/datum/status_effect/staggered) && is_type_in_typecache(target_held_item, GLOB.shove_disarming_types) || target_held_item && target.body_position == LYING_DOWN)
target.dropItemToGround(target_held_item)
append_message = "causing [target.p_them()] to drop [target_held_item]"
target.visible_message(span_danger("[target.name] drops \the [target_held_item]!"),
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index cf2a5ca8a00..e5d564c978e 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -3,6 +3,7 @@
hud_possible = list(HEALTH_HUD,STATUS_HUD,ANTAG_HUD, DNR_HUD) // SKYRAT EDIT ADDITION - DNR_HUD
pressure_resistance = 10
hud_type = /datum/hud/living
+ interaction_flags_click = ALLOW_RESTING
interaction_flags_mouse_drop = ALLOW_RESTING
///Tracks the current size of the mob in relation to its original size. Use update_transform(resize) to change it.
diff --git a/code/modules/mob/living/silicon/ai/vox_sounds.dm b/code/modules/mob/living/silicon/ai/vox_sounds.dm
index dacb1eeb813..d69bb2e1cc3 100644
--- a/code/modules/mob/living/silicon/ai/vox_sounds.dm
+++ b/code/modules/mob/living/silicon/ai/vox_sounds.dm
@@ -8,12 +8,15 @@
// For vim
// :%s/\(\(.*\)\.ogg\)/"\2" = 'sound\/vox_fem\/\1',/g
GLOBAL_LIST_INIT(vox_sounds, list(
- "abduction" = 'sound/vox_fem/abduction.ogg',
"," = 'sound/vox_fem/,.ogg',
"." = 'sound/vox_fem/..ogg',
"a" = 'sound/vox_fem/a.ogg',
+ "abduction" = 'sound/vox_fem/abduction.ogg',
"abortions" = 'sound/vox_fem/abortions.ogg',
"above" = 'sound/vox_fem/above.ogg',
+ "absorb" = 'sound/vox_fem/absorb.ogg',
+ "absorbed" = 'sound/vox_fem/absorbed.ogg',
+ "absorbing" = 'sound/vox_fem/absorbing.ogg',
"abstain" = 'sound/vox_fem/abstain.ogg',
"accelerating" = 'sound/vox_fem/accelerating.ogg',
"accelerator" = 'sound/vox_fem/accelerator.ogg',
@@ -26,11 +29,17 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"across" = 'sound/vox_fem/across.ogg',
"activate" = 'sound/vox_fem/activate.ogg',
"activated" = 'sound/vox_fem/activated.ogg',
+ "activating" = 'sound/vox_fem/activating.ogg',
+ "activation" = 'sound/vox_fem/activation.ogg',
+ "active" = 'sound/vox_fem/active.ogg',
"activity" = 'sound/vox_fem/activity.ogg',
"adios" = 'sound/vox_fem/adios.ogg',
"administration" = 'sound/vox_fem/administration.ogg',
"advanced" = 'sound/vox_fem/advanced.ogg',
"advised" = 'sound/vox_fem/advised.ogg',
+ "affect" = 'sound/vox_fem/affect.ogg',
+ "affected" = 'sound/vox_fem/affected.ogg',
+ "affecting" = 'sound/vox_fem/affecting.ogg',
"aft" = 'sound/vox_fem/aft.ogg',
"after" = 'sound/vox_fem/after.ogg',
"agent" = 'sound/vox_fem/agent.ogg',
@@ -40,44 +49,59 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"alarm" = 'sound/vox_fem/alarm.ogg',
"alarmed" = 'sound/vox_fem/alarmed.ogg',
"alarming" = 'sound/vox_fem/alarming.ogg',
+ "alcohol" = 'sound/vox_fem/alcohol.ogg',
"alert" = 'sound/vox_fem/alert.ogg',
"alerted" = 'sound/vox_fem/alerted.ogg',
"alerting" = 'sound/vox_fem/alerting.ogg',
"alien" = 'sound/vox_fem/alien.ogg',
+ "align" = 'sound/vox_fem/align.ogg',
"aligned" = 'sound/vox_fem/aligned.ogg',
"all" = 'sound/vox_fem/all.ogg',
+ "allow" = 'sound/vox_fem/allow.ogg',
+ "alongside" = 'sound/vox_fem/alongside.ogg',
"alpha" = 'sound/vox_fem/alpha.ogg',
"also" = 'sound/vox_fem/also.ogg',
"am" = 'sound/vox_fem/am.ogg',
"amigo" = 'sound/vox_fem/amigo.ogg',
"ammunition" = 'sound/vox_fem/ammunition.ogg',
+ "amount" = 'sound/vox_fem/amount.ogg',
"an" = 'sound/vox_fem/an.ogg',
"and" = 'sound/vox_fem/and.ogg',
"animal" = 'sound/vox_fem/animal.ogg',
+ "annihilate" = 'sound/vox_fem/annihilate.ogg',
+ "annihilated" = 'sound/vox_fem/annihilated.ogg',
+ "annihilating" = 'sound/vox_fem/annihilating.ogg',
+ "annihilation" = 'sound/vox_fem/annihilation.ogg',
"announcement" = 'sound/vox_fem/announcement.ogg',
"anomalous" = 'sound/vox_fem/anomalous.ogg',
"answer" = 'sound/vox_fem/answer.ogg',
"antenna" = 'sound/vox_fem/antenna.ogg',
+ "anti-noblium" = 'sound/vox_fem/anti-noblium.ogg',
"any" = 'sound/vox_fem/any.ogg',
"apc" = 'sound/vox_fem/apc.ogg',
"apprehend" = 'sound/vox_fem/apprehend.ogg',
"approach" = 'sound/vox_fem/approach.ogg',
+ "arc" = 'sound/vox_fem/arc.ogg',
+ "arcs" = 'sound/vox_fem/arcs.ogg',
"are" = 'sound/vox_fem/are.ogg',
"area" = 'sound/vox_fem/area.ogg',
"arm" = 'sound/vox_fem/arm.ogg',
"armed" = 'sound/vox_fem/armed.ogg',
"armor" = 'sound/vox_fem/armor.ogg',
"armory" = 'sound/vox_fem/armory.ogg',
+ "around" = 'sound/vox_fem/around.ogg',
"array" = 'sound/vox_fem/array.ogg',
"arrest" = 'sound/vox_fem/arrest.ogg',
"artillery" = 'sound/vox_fem/artillery.ogg',
"asimov" = 'sound/vox_fem/asimov.ogg',
+ "ask" = 'sound/vox_fem/ask.ogg',
"ass" = 'sound/vox_fem/ass.ogg',
"asshole" = 'sound/vox_fem/asshole.ogg',
"assholes" = 'sound/vox_fem/assholes.ogg',
"assistance" = 'sound/vox_fem/assistance.ogg',
"assistant" = 'sound/vox_fem/assistant.ogg',
"at" = 'sound/vox_fem/at.ogg',
+ "ate" = 'sound/vox_fem/ate.ogg',
"atmosphere" = 'sound/vox_fem/atmosphere.ogg',
"atmospheric" = 'sound/vox_fem/atmospheric.ogg',
"atmospherics" = 'sound/vox_fem/atmospherics.ogg',
@@ -101,10 +125,14 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"base" = 'sound/vox_fem/base.ogg',
"bay" = 'sound/vox_fem/bay.ogg',
"be" = 'sound/vox_fem/be.ogg',
+ "beaker" = 'sound/vox_fem/beaker.ogg',
"beam" = 'sound/vox_fem/beam.ogg',
"been" = 'sound/vox_fem/been.ogg',
"beep" = 'sound/vox_fem/beep.ogg',
"before" = 'sound/vox_fem/before.ogg',
+ "began" = 'sound/vox_fem/began.ogg',
+ "begin" = 'sound/vox_fem/begin.ogg',
+ "begins" = 'sound/vox_fem/begins.ogg',
"below" = 'sound/vox_fem/below.ogg',
"beside" = 'sound/vox_fem/beside.ogg',
"beware" = 'sound/vox_fem/beware.ogg',
@@ -133,6 +161,7 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"bone" = 'sound/vox_fem/bone.ogg',
"botanist" = 'sound/vox_fem/botanist.ogg',
"botany" = 'sound/vox_fem/botany.ogg',
+ "bottle" = 'sound/vox_fem/bottle.ogg',
"bottom" = 'sound/vox_fem/bottom.ogg',
"bravo" = 'sound/vox_fem/bravo.ogg',
"breach" = 'sound/vox_fem/breach.ogg',
@@ -140,6 +169,11 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"break" = 'sound/vox_fem/break.ogg',
"bridge" = 'sound/vox_fem/bridge.ogg',
"brig" = 'sound/vox_fem/brig.ogg',
+ "broke" = 'sound/vox_fem/broke.ogg',
+ "broken" = 'sound/vox_fem/broken.ogg',
+ "bump" = 'sound/vox_fem/bump.ogg',
+ "bumped" = 'sound/vox_fem/bumped.ogg',
+ "bumps" = 'sound/vox_fem/bumps.ogg',
"bust" = 'sound/vox_fem/bust.ogg',
"but" = 'sound/vox_fem/but.ogg',
"button" = 'sound/vox_fem/button.ogg',
@@ -148,6 +182,7 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"cable" = 'sound/vox_fem/cable.ogg',
"call" = 'sound/vox_fem/call.ogg',
"called" = 'sound/vox_fem/called.ogg',
+ "can" = 'sound/vox_fem/can.ogg',
"canal" = 'sound/vox_fem/canal.ogg',
"canister" = 'sound/vox_fem/canister.ogg',
"cap" = 'sound/vox_fem/cap.ogg',
@@ -155,7 +190,12 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"capture" = 'sound/vox_fem/capture.ogg',
"carbon" = 'sound/vox_fem/carbon.ogg',
"cargo" = 'sound/vox_fem/cargo.ogg',
+ "cascade" = 'sound/vox_fem/cascade.ogg',
"cat" = 'sound/vox_fem/cat.ogg',
+ "cause" = 'sound/vox_fem/cause.ogg',
+ "caused" = 'sound/vox_fem/caused.ogg',
+ "causes" = 'sound/vox_fem/causes.ogg',
+ "causing" = 'sound/vox_fem/causing.ogg',
"ce" = 'sound/vox_fem/ce.ogg',
"cease" = 'sound/vox_fem/cease.ogg',
"ceiling" = 'sound/vox_fem/ceiling.ogg',
@@ -171,6 +211,7 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"changeling" = 'sound/vox_fem/changeling.ogg',
"chapel" = 'sound/vox_fem/chapel.ogg',
"chaplain" = 'sound/vox_fem/chaplain.ogg',
+ "charge" = 'sound/vox_fem/charge.ogg',
"charlie" = 'sound/vox_fem/charlie.ogg',
"check" = 'sound/vox_fem/check.ogg',
"checkpoint" = 'sound/vox_fem/checkpoint.ogg',
@@ -185,35 +226,51 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"clear" = 'sound/vox_fem/clear.ogg',
"clearance" = 'sound/vox_fem/clearance.ogg',
"clockwork" = 'sound/vox_fem/clockwork.ogg',
+ "clog" = 'sound/vox_fem/clog.ogg',
"close" = 'sound/vox_fem/close.ogg',
"closed" = 'sound/vox_fem/closed.ogg',
"closing" = 'sound/vox_fem/closing.ogg',
+ "clothing" = 'sound/vox_fem/clothing.ogg',
"clown" = 'sound/vox_fem/clown.ogg',
"clowning" = 'sound/vox_fem/clowning.ogg',
"cmo" = 'sound/vox_fem/cmo.ogg',
"code" = 'sound/vox_fem/code.ogg',
"coded" = 'sound/vox_fem/coded.ogg',
+ "coil" = 'sound/vox_fem/coil.ogg',
+ "coils" = 'sound/vox_fem/coils.ogg',
"cold" = 'sound/vox_fem/cold.ogg',
"collider" = 'sound/vox_fem/collider.ogg',
+ "combat" = 'sound/vox_fem/combat.ogg',
+ "combatant" = 'sound/vox_fem/combatant.ogg',
"come" = 'sound/vox_fem/come.ogg',
"command" = 'sound/vox_fem/command.ogg',
"communication" = 'sound/vox_fem/communication.ogg',
+ "complete" = 'sound/vox_fem/complete.ogg',
+ "completed" = 'sound/vox_fem/completed.ogg',
+ "completion" = 'sound/vox_fem/completion.ogg',
"complex" = 'sound/vox_fem/complex.ogg',
"comply" = 'sound/vox_fem/comply.ogg',
"computer" = 'sound/vox_fem/computer.ogg',
"condition" = 'sound/vox_fem/condition.ogg',
+ "conditions" = 'sound/vox_fem/conditions.ogg',
"condom" = 'sound/vox_fem/condom.ogg',
+ "configure" = 'sound/vox_fem/configure.ogg',
+ "configured" = 'sound/vox_fem/configured.ogg',
+ "configuring" = 'sound/vox_fem/configuring.ogg',
"confirmed" = 'sound/vox_fem/confirmed.ogg',
"connor" = 'sound/vox_fem/connor.ogg',
"console" = 'sound/vox_fem/console.ogg',
"console2" = 'sound/vox_fem/console2.ogg',
"construct" = 'sound/vox_fem/construct.ogg',
+ "container" = 'sound/vox_fem/container.ogg',
"containment" = 'sound/vox_fem/containment.ogg',
"contamination" = 'sound/vox_fem/contamination.ogg',
"contraband" = 'sound/vox_fem/contraband.ogg',
"control" = 'sound/vox_fem/control.ogg',
"cook" = 'sound/vox_fem/cook.ogg',
+ "cool" = 'sound/vox_fem/cool.ogg',
"coolant" = 'sound/vox_fem/coolant.ogg',
+ "cooling" = 'sound/vox_fem/cooling.ogg',
"coomer" = 'sound/vox_fem/coomer.ogg',
"core" = 'sound/vox_fem/core.ogg',
"corgi" = 'sound/vox_fem/corgi.ogg',
@@ -221,10 +278,15 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"correct" = 'sound/vox_fem/correct.ogg',
"corridor" = 'sound/vox_fem/corridor.ogg',
"corridors" = 'sound/vox_fem/corridors.ogg',
+ "could" = 'sound/vox_fem/could.ogg',
+ "couldnt" = 'sound/vox_fem/couldnt.ogg',
+ "countdown" = 'sound/vox_fem/countdown.ogg',
"coward" = 'sound/vox_fem/coward.ogg',
"cowards" = 'sound/vox_fem/cowards.ogg',
"crate" = 'sound/vox_fem/crate.ogg',
+ "create" = 'sound/vox_fem/create.ogg',
"created" = 'sound/vox_fem/created.ogg',
+ "creating" = 'sound/vox_fem/creating.ogg',
"creature" = 'sound/vox_fem/creature.ogg',
"crew" = 'sound/vox_fem/crew.ogg',
"critical" = 'sound/vox_fem/critical.ogg',
@@ -251,9 +313,12 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"deeoo" = 'sound/vox_fem/deeoo.ogg',
"defense" = 'sound/vox_fem/defense.ogg',
"degrees" = 'sound/vox_fem/degrees.ogg',
+ "delaminating" = 'sound/vox_fem/delaminating.ogg',
+ "delamination" = 'sound/vox_fem/delamination.ogg',
"delta" = 'sound/vox_fem/delta.ogg',
"demon" = 'sound/vox_fem/demon.ogg',
"denied" = 'sound/vox_fem/denied.ogg',
+ "deny" = 'sound/vox_fem/deny.ogg',
"departures" = 'sound/vox_fem/departures.ogg',
"deploy" = 'sound/vox_fem/deploy.ogg',
"deployed" = 'sound/vox_fem/deployed.ogg',
@@ -263,7 +328,9 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"destroyed" = 'sound/vox_fem/destroyed.ogg',
"destruction" = 'sound/vox_fem/destruction.ogg',
"detain" = 'sound/vox_fem/detain.ogg',
+ "detect" = 'sound/vox_fem/detect.ogg',
"detected" = 'sound/vox_fem/detected.ogg',
+ "detecting" = 'sound/vox_fem/detecting.ogg',
"detective" = 'sound/vox_fem/detective.ogg',
"detonation" = 'sound/vox_fem/detonation.ogg',
"device" = 'sound/vox_fem/device.ogg',
@@ -271,8 +338,10 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"did" = 'sound/vox_fem/did.ogg',
"die" = 'sound/vox_fem/die.ogg',
"died" = 'sound/vox_fem/died.ogg',
+ "different" = 'sound/vox_fem/different.ogg',
"dimensional" = 'sound/vox_fem/dimensional.ogg',
"dioxide" = 'sound/vox_fem/dioxide.ogg',
+ "direct" = 'sound/vox_fem/direct.ogg',
"director" = 'sound/vox_fem/director.ogg',
"dirt" = 'sound/vox_fem/dirt.ogg',
"disabled" = 'sound/vox_fem/disabled.ogg',
@@ -286,31 +355,47 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"do" = 'sound/vox_fem/do.ogg',
"doctor" = 'sound/vox_fem/doctor.ogg',
"dog" = 'sound/vox_fem/dog.ogg',
+ "dont" = 'sound/vox_fem/dont.ogg',
"doomsday" = 'sound/vox_fem/doomsday.ogg',
"doop" = 'sound/vox_fem/doop.ogg',
"door" = 'sound/vox_fem/door.ogg',
"dormitory" = 'sound/vox_fem/dormitory.ogg',
"dot" = 'sound/vox_fem/dot.ogg',
+ "double" = 'sound/vox_fem/double.ogg',
"down" = 'sound/vox_fem/down.ogg',
+ "dress" = 'sound/vox_fem/dress.ogg',
+ "dressed" = 'sound/vox_fem/dressed.ogg',
+ "dressing" = 'sound/vox_fem/dressing.ogg',
"drone" = 'sound/vox_fem/drone.ogg',
"dual" = 'sound/vox_fem/dual.ogg',
"duct" = 'sound/vox_fem/duct.ogg',
"e" = 'sound/vox_fem/e.ogg',
+ "easily" = 'sound/vox_fem/easily.ogg',
"east" = 'sound/vox_fem/east.ogg',
+ "eat" = 'sound/vox_fem/eat.ogg',
+ "eaten" = 'sound/vox_fem/eaten.ogg',
"echo" = 'sound/vox_fem/echo.ogg',
"ed" = 'sound/vox_fem/ed.ogg',
+ "education" = 'sound/vox_fem/education.ogg',
"effect" = 'sound/vox_fem/effect.ogg',
+ "effects" = 'sound/vox_fem/effects.ogg',
"egress" = 'sound/vox_fem/egress.ogg',
"eight" = 'sound/vox_fem/eight.ogg',
"eighteen" = 'sound/vox_fem/eighteen.ogg',
"eighty" = 'sound/vox_fem/eighty.ogg',
"electric" = 'sound/vox_fem/electric.ogg',
+ "electrical" = 'sound/vox_fem/electrical.ogg',
"electromagnetic" = 'sound/vox_fem/electromagnetic.ogg',
"elevator" = 'sound/vox_fem/elevator.ogg',
"eleven" = 'sound/vox_fem/eleven.ogg',
"eliminate" = 'sound/vox_fem/eliminate.ogg',
"emergency" = 'sound/vox_fem/emergency.ogg',
+ "emitted" = 'sound/vox_fem/emitted.ogg',
+ "emitter" = 'sound/vox_fem/emitter.ogg',
+ "emitting" = 'sound/vox_fem/emitting.ogg',
"enabled" = 'sound/vox_fem/enabled.ogg',
+ "end" = 'sound/vox_fem/end.ogg',
+ "ends" = 'sound/vox_fem/ends.ogg',
"energy" = 'sound/vox_fem/energy.ogg',
"engage" = 'sound/vox_fem/engage.ogg',
"engaged" = 'sound/vox_fem/engaged.ogg',
@@ -318,6 +403,7 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"engineer" = 'sound/vox_fem/engineer.ogg',
"engineering" = 'sound/vox_fem/engineering.ogg',
"enormous" = 'sound/vox_fem/enormous.ogg',
+ "enough" = 'sound/vox_fem/enough.ogg',
"enter" = 'sound/vox_fem/enter.ogg',
"entity" = 'sound/vox_fem/entity.ogg',
"entry" = 'sound/vox_fem/entry.ogg',
@@ -329,7 +415,11 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"ethereal" = 'sound/vox_fem/ethereal.ogg',
"eva" = 'sound/vox_fem/eva.ogg',
"evacuate" = 'sound/vox_fem/evacuate.ogg',
+ "even" = 'sound/vox_fem/even.ogg',
"ever" = 'sound/vox_fem/ever.ogg',
+ "every" = 'sound/vox_fem/every.ogg',
+ "everybody" = 'sound/vox_fem/everybody.ogg',
+ "everyone" = 'sound/vox_fem/everyone.ogg',
"exchange" = 'sound/vox_fem/exchange.ogg',
"execute" = 'sound/vox_fem/execute.ogg',
"exit" = 'sound/vox_fem/exit.ogg',
@@ -337,12 +427,16 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"experiment" = 'sound/vox_fem/experiment.ogg',
"experimental" = 'sound/vox_fem/experimental.ogg',
"explode" = 'sound/vox_fem/explode.ogg',
+ "exploded" = 'sound/vox_fem/exploded.ogg',
+ "exploding" = 'sound/vox_fem/exploding.ogg',
"explosion" = 'sound/vox_fem/explosion.ogg',
"explosive" = 'sound/vox_fem/explosive.ogg',
"exposure" = 'sound/vox_fem/exposure.ogg',
"exterminate" = 'sound/vox_fem/exterminate.ogg',
+ "external" = 'sound/vox_fem/external.ogg',
"extinguish" = 'sound/vox_fem/extinguish.ogg',
"extinguisher" = 'sound/vox_fem/extinguisher.ogg',
+ "extra" = 'sound/vox_fem/extra.ogg',
"extreme" = 'sound/vox_fem/extreme.ogg',
"f" = 'sound/vox_fem/f.ogg',
"facility" = 'sound/vox_fem/facility.ogg',
@@ -354,12 +448,19 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"farthest" = 'sound/vox_fem/farthest.ogg',
"fast" = 'sound/vox_fem/fast.ogg',
"fauna" = 'sound/vox_fem/fauna.ogg',
+ "feature" = 'sound/vox_fem/feature.ogg',
+ "featured" = 'sound/vox_fem/featured.ogg',
+ "features" = 'sound/vox_fem/features.ogg',
+ "featuring" = 'sound/vox_fem/featuring.ogg',
"feet" = 'sound/vox_fem/feet.ogg',
"felinid" = 'sound/vox_fem/felinid.ogg',
+ "few" = 'sound/vox_fem/few.ogg',
"field" = 'sound/vox_fem/field.ogg',
"fifteen" = 'sound/vox_fem/fifteen.ogg',
"fifth" = 'sound/vox_fem/fifth.ogg',
"fifty" = 'sound/vox_fem/fifty.ogg',
+ "filter" = 'sound/vox_fem/filter.ogg',
+ "filters" = 'sound/vox_fem/filters.ogg',
"final" = 'sound/vox_fem/final.ogg',
"fine" = 'sound/vox_fem/fine.ogg',
"fire" = 'sound/vox_fem/fire.ogg',
@@ -370,6 +471,7 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"floor" = 'sound/vox_fem/floor.ogg',
"flyman" = 'sound/vox_fem/flyman.ogg',
"fool" = 'sound/vox_fem/fool.ogg',
+ "foolish" = 'sound/vox_fem/foolish.ogg',
"for" = 'sound/vox_fem/for.ogg',
"forbidden" = 'sound/vox_fem/forbidden.ogg',
"force" = 'sound/vox_fem/force.ogg',
@@ -389,6 +491,7 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"freeze" = 'sound/vox_fem/freeze.ogg',
"freezer" = 'sound/vox_fem/freezer.ogg',
"freezing" = 'sound/vox_fem/freezing.ogg',
+ "freon" = 'sound/vox_fem/freon.ogg',
"from" = 'sound/vox_fem/from.ogg',
"front" = 'sound/vox_fem/front.ogg',
"froze" = 'sound/vox_fem/froze.ogg',
@@ -399,9 +502,19 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"fuel" = 'sound/vox_fem/fuel.ogg',
"g" = 'sound/vox_fem/g.ogg',
"gas" = 'sound/vox_fem/gas.ogg',
+ "gases" = 'sound/vox_fem/gases.ogg',
+ "gave" = 'sound/vox_fem/gave.ogg',
+ "gear" = 'sound/vox_fem/gear.ogg',
+ "geared" = 'sound/vox_fem/geared.ogg',
+ "gearing" = 'sound/vox_fem/gearing.ogg',
+ "generate" = 'sound/vox_fem/generate.ogg',
+ "generated" = 'sound/vox_fem/generated.ogg',
+ "generating" = 'sound/vox_fem/generating.ogg',
"generator" = 'sound/vox_fem/generator.ogg',
"geneticist" = 'sound/vox_fem/geneticist.ogg',
"get" = 'sound/vox_fem/get.ogg',
+ "give" = 'sound/vox_fem/give.ogg',
+ "given" = 'sound/vox_fem/given.ogg',
"glory" = 'sound/vox_fem/glory.ogg',
"go" = 'sound/vox_fem/go.ogg',
"god" = 'sound/vox_fem/god.ogg',
@@ -428,17 +541,29 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"had" = 'sound/vox_fem/had.ogg',
"hall" = 'sound/vox_fem/hall.ogg',
"hallway" = 'sound/vox_fem/hallway.ogg',
+ "halon" = 'sound/vox_fem/halon.ogg',
"handling" = 'sound/vox_fem/handling.ogg',
"hangar" = 'sound/vox_fem/hangar.ogg',
+ "hard" = 'sound/vox_fem/hard.ogg',
+ "hardly" = 'sound/vox_fem/hardly.ogg',
"harm" = 'sound/vox_fem/harm.ogg',
"harmful" = 'sound/vox_fem/harmful.ogg',
+ "harness" = 'sound/vox_fem/harness.ogg',
+ "harnessed" = 'sound/vox_fem/harnessed.ogg',
+ "harnessing" = 'sound/vox_fem/harnessing.ogg',
"has" = 'sound/vox_fem/has.ogg',
"have" = 'sound/vox_fem/have.ogg',
"hazard" = 'sound/vox_fem/hazard.ogg',
"he" = 'sound/vox_fem/he.ogg',
"head" = 'sound/vox_fem/head.ogg',
+ "heal" = 'sound/vox_fem/heal.ogg',
+ "healed" = 'sound/vox_fem/healed.ogg',
+ "healing" = 'sound/vox_fem/healing.ogg',
+ "healium" = 'sound/vox_fem/healium.ogg',
"health" = 'sound/vox_fem/health.ogg',
"heat" = 'sound/vox_fem/heat.ogg',
+ "heated" = 'sound/vox_fem/heated.ogg',
+ "heating" = 'sound/vox_fem/heating.ogg',
"helicopter" = 'sound/vox_fem/helicopter.ogg',
"helium" = 'sound/vox_fem/helium.ogg',
"hello" = 'sound/vox_fem/hello.ogg',
@@ -468,7 +593,9 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"hunger" = 'sound/vox_fem/hunger.ogg',
"hurt" = 'sound/vox_fem/hurt.ogg',
"hydro" = 'sound/vox_fem/hydro.ogg',
+ "hydrogen" = 'sound/vox_fem/hydrogen.ogg',
"hydroponics" = 'sound/vox_fem/hydroponics.ogg',
+ "hyper-noblium" = 'sound/vox_fem/hyper-noblium.ogg',
"i" = 'sound/vox_fem/i.ogg',
"ian" = 'sound/vox_fem/ian.ogg',
"idiot" = 'sound/vox_fem/idiot.ogg',
@@ -482,26 +609,33 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"in" = 'sound/vox_fem/in.ogg',
"inches" = 'sound/vox_fem/inches.ogg',
"india" = 'sound/vox_fem/india.ogg',
+ "inert" = 'sound/vox_fem/inert.ogg',
"ing" = 'sound/vox_fem/ing.ogg',
"inoperative" = 'sound/vox_fem/inoperative.ogg',
"inside" = 'sound/vox_fem/inside.ogg',
"inspection" = 'sound/vox_fem/inspection.ogg',
"inspector" = 'sound/vox_fem/inspector.ogg',
"interchange" = 'sound/vox_fem/interchange.ogg',
+ "internal" = 'sound/vox_fem/internal.ogg',
"internals" = 'sound/vox_fem/internals.ogg',
"intruder" = 'sound/vox_fem/intruder.ogg',
"invalid" = 'sound/vox_fem/invalid.ogg',
"invalidate" = 'sound/vox_fem/invalidate.ogg',
"invasion" = 'sound/vox_fem/invasion.ogg',
+ "irradiate" = 'sound/vox_fem/irradiate.ogg',
"is" = 'sound/vox_fem/is.ogg',
"it" = 'sound/vox_fem/it.ogg',
+ "its" = 'sound/vox_fem/its.ogg',
"j" = 'sound/vox_fem/j.ogg',
"janitor" = 'sound/vox_fem/janitor.ogg',
"jesus" = 'sound/vox_fem/jesus.ogg',
+ "job" = 'sound/vox_fem/job.ogg',
+ "jobs" = 'sound/vox_fem/jobs.ogg',
"johnson" = 'sound/vox_fem/johnson.ogg',
"jolly" = 'sound/vox_fem/jolly.ogg',
"juliet" = 'sound/vox_fem/juliet.ogg',
"k" = 'sound/vox_fem/k.ogg',
+ "kelvin" = 'sound/vox_fem/kelvin.ogg',
"key" = 'sound/vox_fem/key.ogg',
"kidnapped" = 'sound/vox_fem/kidnapped.ogg',
"kidnapping" = 'sound/vox_fem/kidnapping.ogg',
@@ -536,7 +670,10 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"light" = 'sound/vox_fem/light.ogg',
"lightbulb" = 'sound/vox_fem/lightbulb.ogg',
"lima" = 'sound/vox_fem/lima.ogg',
+ "limit" = 'sound/vox_fem/limit.ogg',
+ "limited" = 'sound/vox_fem/limited.ogg',
"liquid" = 'sound/vox_fem/liquid.ogg',
+ "list" = 'sound/vox_fem/list.ogg',
"live" = 'sound/vox_fem/live.ogg',
"live2" = 'sound/vox_fem/live2.ogg',
"lizard" = 'sound/vox_fem/lizard.ogg',
@@ -559,12 +696,14 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"lusty" = 'sound/vox_fem/lusty.ogg',
"m" = 'sound/vox_fem/m.ogg',
"machine" = 'sound/vox_fem/machine.ogg',
+ "made" = 'sound/vox_fem/made.ogg',
"magic" = 'sound/vox_fem/magic.ogg',
"magnetic" = 'sound/vox_fem/magnetic.ogg',
"main" = 'sound/vox_fem/main.ogg',
"maintainer" = 'sound/vox_fem/maintainer.ogg',
"maintenance" = 'sound/vox_fem/maintenance.ogg',
"major" = 'sound/vox_fem/major.ogg',
+ "making" = 'sound/vox_fem/making.ogg',
"malfunction" = 'sound/vox_fem/malfunction.ogg',
"man" = 'sound/vox_fem/man.ogg',
"many" = 'sound/vox_fem/many.ogg',
@@ -573,15 +712,21 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"maximum" = 'sound/vox_fem/maximum.ogg',
"may" = 'sound/vox_fem/may.ogg',
"me" = 'sound/vox_fem/me.ogg',
+ "mean" = 'sound/vox_fem/mean.ogg',
+ "means" = 'sound/vox_fem/means.ogg',
"meat" = 'sound/vox_fem/meat.ogg',
"medbay" = 'sound/vox_fem/medbay.ogg',
"medical" = 'sound/vox_fem/medical.ogg',
+ "medium" = 'sound/vox_fem/medium.ogg',
"megafauna" = 'sound/vox_fem/megafauna.ogg',
"men" = 'sound/vox_fem/men.ogg',
"mercy" = 'sound/vox_fem/mercy.ogg',
"mesa" = 'sound/vox_fem/mesa.ogg',
+ "meson" = 'sound/vox_fem/meson.ogg',
"message" = 'sound/vox_fem/message.ogg',
"meter" = 'sound/vox_fem/meter.ogg',
+ "method" = 'sound/vox_fem/method.ogg',
+ "miasma" = 'sound/vox_fem/miasma.ogg',
"micro" = 'sound/vox_fem/micro.ogg',
"middle" = 'sound/vox_fem/middle.ogg',
"mike" = 'sound/vox_fem/mike.ogg',
@@ -594,17 +739,22 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"miner" = 'sound/vox_fem/miner.ogg',
"minimum" = 'sound/vox_fem/minimum.ogg',
"minor" = 'sound/vox_fem/minor.ogg',
+ "minute" = 'sound/vox_fem/minute.ogg',
"minutes" = 'sound/vox_fem/minutes.ogg',
"mister" = 'sound/vox_fem/mister.ogg',
+ "mixture" = 'sound/vox_fem/mixture.ogg',
"mode" = 'sound/vox_fem/mode.ogg',
"modification" = 'sound/vox_fem/modification.ogg',
"money" = 'sound/vox_fem/money.ogg',
"monkey" = 'sound/vox_fem/monkey.ogg',
+ "most" = 'sound/vox_fem/most.ogg',
"moth" = 'sound/vox_fem/moth.ogg',
"mothperson" = 'sound/vox_fem/mothperson.ogg',
"motor" = 'sound/vox_fem/motor.ogg',
"motorpool" = 'sound/vox_fem/motorpool.ogg',
"move" = 'sound/vox_fem/move.ogg',
+ "moved" = 'sound/vox_fem/moved.ogg',
+ "moving" = 'sound/vox_fem/moving.ogg',
"multitude" = 'sound/vox_fem/multitude.ogg',
"murder" = 'sound/vox_fem/murder.ogg',
"murderer" = 'sound/vox_fem/murderer.ogg',
@@ -613,7 +763,9 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"mythic" = 'sound/vox_fem/mythic.ogg',
"n" = 'sound/vox_fem/n.ogg',
"nanotrasen" = 'sound/vox_fem/nanotrasen.ogg',
+ "near" = 'sound/vox_fem/near.ogg',
"nearest" = 'sound/vox_fem/nearest.ogg',
+ "nearly" = 'sound/vox_fem/nearly.ogg',
"need" = 'sound/vox_fem/need.ogg',
"never" = 'sound/vox_fem/never.ogg',
"nice" = 'sound/vox_fem/nice.ogg',
@@ -624,16 +776,21 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"nitrogen" = 'sound/vox_fem/nitrogen.ogg',
"no" = 'sound/vox_fem/no.ogg',
"nominal" = 'sound/vox_fem/nominal.ogg',
+ "none" = 'sound/vox_fem/none.ogg',
+ "normal" = 'sound/vox_fem/normal.ogg',
+ "normally" = 'sound/vox_fem/normally.ogg',
"north" = 'sound/vox_fem/north.ogg',
"northeast" = 'sound/vox_fem/northeast.ogg',
"northwest" = 'sound/vox_fem/northwest.ogg',
"not" = 'sound/vox_fem/not.ogg',
+ "notably" = 'sound/vox_fem/notably.ogg',
"november" = 'sound/vox_fem/november.ogg',
"now" = 'sound/vox_fem/now.ogg',
"nuclear" = 'sound/vox_fem/nuclear.ogg',
"nuke" = 'sound/vox_fem/nuke.ogg',
"number" = 'sound/vox_fem/number.ogg',
"o" = 'sound/vox_fem/o.ogg',
+ "object" = 'sound/vox_fem/object.ogg',
"objective" = 'sound/vox_fem/objective.ogg',
"obliterate" = 'sound/vox_fem/obliterate.ogg',
"obliterated" = 'sound/vox_fem/obliterated.ogg',
@@ -660,13 +817,17 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"option" = 'sound/vox_fem/option.ogg',
"or" = 'sound/vox_fem/or.ogg',
"order" = 'sound/vox_fem/order.ogg',
+ "ordered" = 'sound/vox_fem/ordered.ogg',
+ "ordering" = 'sound/vox_fem/ordering.ogg',
"organic" = 'sound/vox_fem/organic.ogg',
"oscar" = 'sound/vox_fem/oscar.ogg',
"out" = 'sound/vox_fem/out.ogg',
+ "output" = 'sound/vox_fem/output.ogg',
"outside" = 'sound/vox_fem/outside.ogg',
"over" = 'sound/vox_fem/over.ogg',
"overload" = 'sound/vox_fem/overload.ogg',
"override" = 'sound/vox_fem/override.ogg',
+ "own" = 'sound/vox_fem/own.ogg',
"oxygen" = 'sound/vox_fem/oxygen.ogg',
"p" = 'sound/vox_fem/p.ogg',
"pacification" = 'sound/vox_fem/pacification.ogg',
@@ -676,6 +837,7 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"panel" = 'sound/vox_fem/panel.ogg',
"panting" = 'sound/vox_fem/panting.ogg',
"pathetic" = 'sound/vox_fem/pathetic.ogg',
+ "pda" = 'sound/vox_fem/pda.ogg',
"percent" = 'sound/vox_fem/percent.ogg',
"perfect" = 'sound/vox_fem/perfect.ogg',
"perhaps" = 'sound/vox_fem/perhaps.ogg',
@@ -690,28 +852,46 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"plasma" = 'sound/vox_fem/plasma.ogg',
"plasmaman" = 'sound/vox_fem/plasmaman.ogg',
"platform" = 'sound/vox_fem/platform.ogg',
+ "plating" = 'sound/vox_fem/plating.ogg',
"plausible" = 'sound/vox_fem/plausible.ogg',
"please" = 'sound/vox_fem/please.ogg',
+ "pluoxium" = 'sound/vox_fem/pluoxium.ogg',
"point" = 'sound/vox_fem/point.ogg',
"port" = 'sound/vox_fem/port.ogg',
"portal" = 'sound/vox_fem/portal.ogg',
+ "portion" = 'sound/vox_fem/portion.ogg',
"possible" = 'sound/vox_fem/possible.ogg',
"power" = 'sound/vox_fem/power.ogg',
+ "powered" = 'sound/vox_fem/powered.ogg',
+ "powering" = 'sound/vox_fem/powering.ogg',
+ "premature" = 'sound/vox_fem/premature.ogg',
+ "prematurely" = 'sound/vox_fem/prematurely.ogg',
"presence" = 'sound/vox_fem/presence.ogg',
"present" = 'sound/vox_fem/present.ogg',
"presents" = 'sound/vox_fem/presents.ogg',
"press" = 'sound/vox_fem/press.ogg',
"pressure" = 'sound/vox_fem/pressure.ogg',
"primary" = 'sound/vox_fem/primary.ogg',
+ "priority" = 'sound/vox_fem/priority.ogg',
"prison" = 'sound/vox_fem/prison.ogg',
"prisoner" = 'sound/vox_fem/prisoner.ogg',
"proceed" = 'sound/vox_fem/proceed.ogg',
"processing" = 'sound/vox_fem/processing.ogg',
"progress" = 'sound/vox_fem/progress.ogg',
+ "projectile" = 'sound/vox_fem/projectile.ogg',
"proper" = 'sound/vox_fem/proper.ogg',
"propulsion" = 'sound/vox_fem/propulsion.ogg',
"prosecute" = 'sound/vox_fem/prosecute.ogg',
+ "protect" = 'sound/vox_fem/protect.ogg',
+ "protected" = 'sound/vox_fem/protected.ogg',
+ "protection" = 'sound/vox_fem/protection.ogg',
"protective" = 'sound/vox_fem/protective.ogg',
+ "proto-nitrate" = 'sound/vox_fem/proto-nitrate.ogg',
+ "pull" = 'sound/vox_fem/pull.ogg',
+ "pulled" = 'sound/vox_fem/pulled.ogg',
+ "pulling" = 'sound/vox_fem/pulling.ogg',
+ "pump" = 'sound/vox_fem/pump.ogg',
+ "pumps" = 'sound/vox_fem/pumps.ogg',
"push" = 'sound/vox_fem/push.ogg',
"put" = 'sound/vox_fem/put.ogg',
"q" = 'sound/vox_fem/q.ogg',
@@ -737,9 +917,14 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"reactor" = 'sound/vox_fem/reactor.ogg',
"red" = 'sound/vox_fem/red.ogg',
"relay" = 'sound/vox_fem/relay.ogg',
+ "release" = 'sound/vox_fem/release.ogg',
"released" = 'sound/vox_fem/released.ogg',
+ "releasing" = 'sound/vox_fem/releasing.ogg',
"remaining" = 'sound/vox_fem/remaining.ogg',
"removal" = 'sound/vox_fem/removal.ogg',
+ "remove" = 'sound/vox_fem/remove.ogg',
+ "removed" = 'sound/vox_fem/removed.ogg',
+ "removing" = 'sound/vox_fem/removing.ogg',
"renegade" = 'sound/vox_fem/renegade.ogg',
"repair" = 'sound/vox_fem/repair.ogg',
"report" = 'sound/vox_fem/report.ogg',
@@ -752,6 +937,9 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"research" = 'sound/vox_fem/research.ogg',
"resevoir" = 'sound/vox_fem/resevoir.ogg',
"resistance" = 'sound/vox_fem/resistance.ogg',
+ "resistant" = 'sound/vox_fem/resistant.ogg',
+ "resisting" = 'sound/vox_fem/resisting.ogg',
+ "resonance" = 'sound/vox_fem/resonance.ogg',
"rest" = 'sound/vox_fem/rest.ogg',
"restoration" = 'sound/vox_fem/restoration.ogg',
"revolution" = 'sound/vox_fem/revolution.ogg',
@@ -770,18 +958,28 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"runtime" = 'sound/vox_fem/runtime.ogg',
"s" = 'sound/vox_fem/s.ogg',
"sabotage" = 'sound/vox_fem/sabotage.ogg',
+ "sabotaged" = 'sound/vox_fem/sabotaged.ogg',
+ "sabotaging" = 'sound/vox_fem/sabotaging.ogg',
"safe" = 'sound/vox_fem/safe.ogg',
"safety" = 'sound/vox_fem/safety.ogg',
"sairhorn" = 'sound/vox_fem/sairhorn.ogg',
+ "same" = 'sound/vox_fem/same.ogg',
"sarah" = 'sound/vox_fem/sarah.ogg',
"sargeant" = 'sound/vox_fem/sargeant.ogg',
"satellite" = 'sound/vox_fem/satellite.ogg',
"save" = 'sound/vox_fem/save.ogg',
+ "saw" = 'sound/vox_fem/saw.ogg',
+ "scan" = 'sound/vox_fem/scan.ogg',
+ "scanned" = 'sound/vox_fem/scanned.ogg',
+ "scanner" = 'sound/vox_fem/scanner.ogg',
+ "scanners" = 'sound/vox_fem/scanners.ogg',
+ "scanning" = 'sound/vox_fem/scanning.ogg',
"scensor" = 'sound/vox_fem/scensor.ogg',
"science" = 'sound/vox_fem/science.ogg',
"scientist" = 'sound/vox_fem/scientist.ogg',
"scream" = 'sound/vox_fem/scream.ogg',
"screen" = 'sound/vox_fem/screen.ogg',
+ "screw" = 'sound/vox_fem/screw.ogg',
"search" = 'sound/vox_fem/search.ogg',
"second" = 'sound/vox_fem/second.ogg',
"secondary" = 'sound/vox_fem/secondary.ogg',
@@ -791,21 +989,32 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"secure" = 'sound/vox_fem/secure.ogg',
"secured" = 'sound/vox_fem/secured.ogg',
"security" = 'sound/vox_fem/security.ogg',
+ "seen" = 'sound/vox_fem/seen.ogg',
"select" = 'sound/vox_fem/select.ogg',
"selected" = 'sound/vox_fem/selected.ogg',
"self" = 'sound/vox_fem/self.ogg',
"sensors" = 'sound/vox_fem/sensors.ogg',
"server" = 'sound/vox_fem/server.ogg',
"service" = 'sound/vox_fem/service.ogg',
+ "set" = 'sound/vox_fem/set.ogg',
"seven" = 'sound/vox_fem/seven.ogg',
"seventeen" = 'sound/vox_fem/seventeen.ogg',
"seventy" = 'sound/vox_fem/seventy.ogg',
+ "sever" = 'sound/vox_fem/sever.ogg',
"severe" = 'sound/vox_fem/severe.ogg',
+ "severed" = 'sound/vox_fem/severed.ogg',
+ "severing" = 'sound/vox_fem/severing.ogg',
"sewage" = 'sound/vox_fem/sewage.ogg',
"sewer" = 'sound/vox_fem/sewer.ogg',
"shaft" = 'sound/vox_fem/shaft.ogg',
+ "shame" = 'sound/vox_fem/shame.ogg',
+ "shameful" = 'sound/vox_fem/shameful.ogg',
+ "shameless" = 'sound/vox_fem/shameless.ogg',
+ "shard" = 'sound/vox_fem/shard.ogg',
"she" = 'sound/vox_fem/she.ogg',
"shield" = 'sound/vox_fem/shield.ogg',
+ "shift" = 'sound/vox_fem/shift.ogg',
+ "shifts" = 'sound/vox_fem/shifts.ogg',
"shipment" = 'sound/vox_fem/shipment.ogg',
"shirt" = 'sound/vox_fem/shirt.ogg',
"shit" = 'sound/vox_fem/shit.ogg',
@@ -820,11 +1029,15 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"shuttle" = 'sound/vox_fem/shuttle.ogg',
"sick" = 'sound/vox_fem/sick.ogg',
"side" = 'sound/vox_fem/side.ogg',
+ "sides" = 'sound/vox_fem/sides.ogg',
"sierra" = 'sound/vox_fem/sierra.ogg',
"sight" = 'sound/vox_fem/sight.ogg',
"silicon" = 'sound/vox_fem/silicon.ogg',
"silo" = 'sound/vox_fem/silo.ogg',
+ "single" = 'sound/vox_fem/single.ogg',
"singularity" = 'sound/vox_fem/singularity.ogg',
+ "siphon" = 'sound/vox_fem/siphon.ogg',
+ "siphoning" = 'sound/vox_fem/siphoning.ogg',
"six" = 'sound/vox_fem/six.ogg',
"sixteen" = 'sound/vox_fem/sixteen.ogg',
"sixty" = 'sound/vox_fem/sixty.ogg',
@@ -837,6 +1050,7 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"sm" = 'sound/vox_fem/sm.ogg',
"small" = 'sound/vox_fem/small.ogg',
"sockmuncher" = 'sound/vox_fem/sockmuncher.ogg',
+ "soft" = 'sound/vox_fem/soft.ogg',
"solar" = 'sound/vox_fem/solar.ogg',
"solars" = 'sound/vox_fem/solars.ogg',
"soldier" = 'sound/vox_fem/soldier.ogg',
@@ -845,16 +1059,23 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"something" = 'sound/vox_fem/something.ogg',
"son" = 'sound/vox_fem/son.ogg',
"sorry" = 'sound/vox_fem/sorry.ogg',
+ "source" = 'sound/vox_fem/source.ogg',
"south" = 'sound/vox_fem/south.ogg',
"southeast" = 'sound/vox_fem/southeast.ogg',
"southwest" = 'sound/vox_fem/southwest.ogg',
"space" = 'sound/vox_fem/space.ogg',
+ "special" = 'sound/vox_fem/special.ogg',
+ "spew" = 'sound/vox_fem/spew.ogg',
"squad" = 'sound/vox_fem/squad.ogg',
"square" = 'sound/vox_fem/square.ogg',
"ss13" = 'sound/vox_fem/ss13.ogg',
"stairway" = 'sound/vox_fem/stairway.ogg',
"starboard" = 'sound/vox_fem/starboard.ogg',
+ "start" = 'sound/vox_fem/start.ogg',
+ "starts" = 'sound/vox_fem/starts.ogg',
"station" = 'sound/vox_fem/station.ogg',
+ "stations" = 'sound/vox_fem/stations.ogg',
+ "stationwide" = 'sound/vox_fem/stationwide.ogg',
"status" = 'sound/vox_fem/status.ogg',
"stay" = 'sound/vox_fem/stay.ogg',
"sterile" = 'sound/vox_fem/sterile.ogg',
@@ -865,9 +1086,12 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"stuck" = 'sound/vox_fem/stuck.ogg',
"sub" = 'sound/vox_fem/sub.ogg',
"subsurface" = 'sound/vox_fem/subsurface.ogg',
+ "such" = 'sound/vox_fem/such.ogg',
"sudden" = 'sound/vox_fem/sudden.ogg',
"suffer" = 'sound/vox_fem/suffer.ogg',
"suit" = 'sound/vox_fem/suit.ogg',
+ "suited" = 'sound/vox_fem/suited.ogg',
+ "super" = 'sound/vox_fem/super.ogg',
"superconducting" = 'sound/vox_fem/superconducting.ogg',
"supercooled" = 'sound/vox_fem/supercooled.ogg',
"supermatter" = 'sound/vox_fem/supermatter.ogg',
@@ -904,6 +1128,7 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"terminate" = 'sound/vox_fem/terminate.ogg',
"terminated" = 'sound/vox_fem/terminated.ogg',
"termination" = 'sound/vox_fem/termination.ogg',
+ "tesla" = 'sound/vox_fem/tesla.ogg',
"test" = 'sound/vox_fem/test.ogg',
"text" = 'sound/vox_fem/text.ogg',
"thank" = 'sound/vox_fem/thank.ogg',
@@ -924,23 +1149,33 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"threat" = 'sound/vox_fem/threat.ogg',
"three" = 'sound/vox_fem/three.ogg',
"through" = 'sound/vox_fem/through.ogg',
+ "tick" = 'sound/vox_fem/tick.ogg',
"tide" = 'sound/vox_fem/tide.ogg',
+ "tile" = 'sound/vox_fem/tile.ogg',
"time" = 'sound/vox_fem/time.ogg',
"tiny" = 'sound/vox_fem/tiny.ogg',
"to" = 'sound/vox_fem/to.ogg',
"top" = 'sound/vox_fem/top.ogg',
"topside" = 'sound/vox_fem/topside.ogg',
"touch" = 'sound/vox_fem/touch.ogg',
+ "touched" = 'sound/vox_fem/touched.ogg',
+ "touching" = 'sound/vox_fem/touching.ogg',
"towards" = 'sound/vox_fem/towards.ogg',
"toxins" = 'sound/vox_fem/toxins.ogg',
"track" = 'sound/vox_fem/track.ogg',
"train" = 'sound/vox_fem/train.ogg',
"traitor" = 'sound/vox_fem/traitor.ogg',
"transportation" = 'sound/vox_fem/transportation.ogg',
+ "trigger" = 'sound/vox_fem/trigger.ogg',
+ "triggered" = 'sound/vox_fem/triggered.ogg',
+ "triggering" = 'sound/vox_fem/triggering.ogg',
+ "triple" = 'sound/vox_fem/triple.ogg',
+ "tritium" = 'sound/vox_fem/tritium.ogg',
"truck" = 'sound/vox_fem/truck.ogg',
"true" = 'sound/vox_fem/true.ogg',
"tunnel" = 'sound/vox_fem/tunnel.ogg',
"turn" = 'sound/vox_fem/turn.ogg',
+ "turned" = 'sound/vox_fem/turned.ogg',
"turret" = 'sound/vox_fem/turret.ogg',
"twelve" = 'sound/vox_fem/twelve.ogg',
"twenty" = 'sound/vox_fem/twenty.ogg',
@@ -952,10 +1187,13 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"unauthorized" = 'sound/vox_fem/unauthorized.ogg',
"under" = 'sound/vox_fem/under.ogg',
"uniform" = 'sound/vox_fem/uniform.ogg',
+ "unique" = 'sound/vox_fem/unique.ogg',
"unknown" = 'sound/vox_fem/unknown.ogg',
"unlocked" = 'sound/vox_fem/unlocked.ogg',
"unsafe" = 'sound/vox_fem/unsafe.ogg',
"until" = 'sound/vox_fem/until.ogg',
+ "unwrench" = 'sound/vox_fem/unwrench.ogg',
+ "unwrenching" = 'sound/vox_fem/unwrenching.ogg',
"up" = 'sound/vox_fem/up.ogg',
"update" = 'sound/vox_fem/update.ogg',
"updated" = 'sound/vox_fem/updated.ogg',
@@ -967,6 +1205,8 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"usa" = 'sound/vox_fem/usa.ogg',
"use" = 'sound/vox_fem/use.ogg',
"used" = 'sound/vox_fem/used.ogg',
+ "useful" = 'sound/vox_fem/useful.ogg',
+ "useless" = 'sound/vox_fem/useless.ogg',
"user" = 'sound/vox_fem/user.ogg',
"v" = 'sound/vox_fem/v.ogg',
"vacate" = 'sound/vox_fem/vacate.ogg',
@@ -987,8 +1227,8 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"vitals" = 'sound/vox_fem/vitals.ogg',
"voltage" = 'sound/vox_fem/voltage.ogg',
"vox" = 'sound/vox_fem/vox.ogg',
- "vox_login" = 'sound/vox_fem/vox_login.ogg',
"voxtest" = 'sound/vox_fem/voxtest.ogg',
+ "vox_login" = 'sound/vox_fem/vox_login.ogg',
"w" = 'sound/vox_fem/w.ogg',
"walk" = 'sound/vox_fem/walk.ogg',
"wall" = 'sound/vox_fem/wall.ogg',
@@ -1002,15 +1242,19 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"was" = 'sound/vox_fem/was.ogg',
"waste" = 'sound/vox_fem/waste.ogg',
"water" = 'sound/vox_fem/water.ogg',
+ "way" = 'sound/vox_fem/way.ogg',
+ "ways" = 'sound/vox_fem/ways.ogg',
"we" = 'sound/vox_fem/we.ogg',
"weak" = 'sound/vox_fem/weak.ogg',
"weapon" = 'sound/vox_fem/weapon.ogg',
"welcome" = 'sound/vox_fem/welcome.ogg',
+ "weld" = 'sound/vox_fem/weld.ogg',
"west" = 'sound/vox_fem/west.ogg',
"wew" = 'sound/vox_fem/wew.ogg',
"what" = 'sound/vox_fem/what.ogg',
"when" = 'sound/vox_fem/when.ogg',
"where" = 'sound/vox_fem/where.ogg',
+ "which" = 'sound/vox_fem/which.ogg',
"while" = 'sound/vox_fem/while.ogg',
"whiskey" = 'sound/vox_fem/whiskey.ogg',
"white" = 'sound/vox_fem/white.ogg',
@@ -1025,7 +1269,15 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"wood" = 'sound/vox_fem/wood.ogg',
"woody" = 'sound/vox_fem/woody.ogg',
"woop" = 'sound/vox_fem/woop.ogg',
+ "work" = 'sound/vox_fem/work.ogg',
+ "worked" = 'sound/vox_fem/worked.ogg',
+ "working" = 'sound/vox_fem/working.ogg',
+ "works" = 'sound/vox_fem/works.ogg',
+ "would" = 'sound/vox_fem/would.ogg',
+ "wouldnt" = 'sound/vox_fem/wouldnt.ogg',
"wow" = 'sound/vox_fem/wow.ogg',
+ "wrench" = 'sound/vox_fem/wrench.ogg',
+ "wrenching" = 'sound/vox_fem/wrenching.ogg',
"x" = 'sound/vox_fem/x.ogg',
"xeno" = 'sound/vox_fem/xeno.ogg',
"xenobiology" = 'sound/vox_fem/xenobiology.ogg',
@@ -1041,6 +1293,8 @@ GLOBAL_LIST_INIT(vox_sounds, list(
"your" = 'sound/vox_fem/your.ogg',
"yourself" = 'sound/vox_fem/yourself.ogg',
"z" = 'sound/vox_fem/z.ogg',
+ "zap" = 'sound/vox_fem/zap.ogg',
+ "zauker" = 'sound/vox_fem/zauker.ogg',
"zero" = 'sound/vox_fem/zero.ogg',
"zombie" = 'sound/vox_fem/zombie.ogg',
"zone" = 'sound/vox_fem/zone.ogg',
diff --git a/code/modules/mob/living/silicon/robot/robot_model.dm b/code/modules/mob/living/silicon/robot/robot_model.dm
index 72efab2833f..c4b5ec89976 100644
--- a/code/modules/mob/living/silicon/robot/robot_model.dm
+++ b/code/modules/mob/living/silicon/robot/robot_model.dm
@@ -53,17 +53,9 @@
var/list/ride_offset_y = list("north" = 4, "south" = 4, "east" = 3, "west" = 3)
///List of skins the borg can be reskinned to, optional
var/list/borg_skins
- ///Omnitoolbox, holder of certain borg tools. Not all models have one
- var/obj/item/cyborg_omnitoolbox/toolbox
- ///Path to toolbox, if a model gets one
- var/toolbox_path
/obj/item/robot_model/Initialize(mapload)
. = ..()
-
- if(toolbox_path)
- toolbox = new toolbox_path(src)
-
for(var/path in basic_modules)
var/obj/item/new_module = new path(src)
basic_modules += new_module
@@ -442,7 +434,6 @@
model_select_icon = "engineer"
model_traits = list(TRAIT_NEGATES_GRAVITY)
hat_offset = -4
- toolbox_path = /obj/item/cyborg_omnitoolbox/engineering
/obj/item/robot_model/janitor
name = "Janitor"
@@ -723,7 +714,6 @@
/obj/item/reagent_containers/syringe,
/obj/item/borg/cyborg_omnitool/medical,
/obj/item/borg/cyborg_omnitool/medical,
- /obj/item/surgical_drapes/cyborg,
/obj/item/blood_filter,
/obj/item/extinguisher/mini,
/obj/item/emergency_bed/silicon,
@@ -741,7 +731,6 @@
model_select_icon = "medical"
model_traits = list(TRAIT_PUSHIMMUNE)
hat_offset = 3
- toolbox_path = /obj/item/cyborg_omnitoolbox/medical
borg_skins = list(
"Machinified Doctor" = list(SKIN_ICON_STATE = "medical"),
"Qualified Doctor" = list(SKIN_ICON_STATE = "qualified_doctor"),
@@ -944,7 +933,6 @@
/obj/item/healthanalyzer,
/obj/item/borg/cyborg_omnitool/medical,
/obj/item/borg/cyborg_omnitool/medical,
- /obj/item/surgical_drapes/cyborg,
/obj/item/blood_filter,
/obj/item/melee/energy/sword/cyborg/saw,
/obj/item/emergency_bed/silicon,
@@ -960,7 +948,6 @@
model_select_icon = "malf"
model_traits = list(TRAIT_PUSHIMMUNE)
hat_offset = 3
- toolbox_path = /obj/item/cyborg_omnitoolbox/medical
/obj/item/robot_model/saboteur
name = "Syndicate Saboteur"
@@ -990,7 +977,6 @@
model_select_icon = "malf"
model_traits = list(TRAIT_PUSHIMMUNE, TRAIT_NEGATES_GRAVITY)
hat_offset = -4
- toolbox_path = /obj/item/cyborg_omnitoolbox/engineering
canDispose = TRUE
/obj/item/robot_model/syndicate/kiltborg
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index e0d98d6e971..7862d9cb442 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -76,6 +76,7 @@
TRAIT_MARTIAL_ARTS_IMMUNE,
TRAIT_NOFIRE_SPREAD,
TRAIT_BRAWLING_KNOCKDOWN_BLOCKED,
+ TRAIT_FENCE_CLIMBER,
)
add_traits(traits_to_apply, ROUNDSTART_TRAIT)
diff --git a/code/modules/mob/living/silicon/silicon_say.dm b/code/modules/mob/living/silicon/silicon_say.dm
index 1468b9c7365..9310211aa0e 100644
--- a/code/modules/mob/living/silicon/silicon_say.dm
+++ b/code/modules/mob/living/silicon/silicon_say.dm
@@ -70,6 +70,9 @@
)
/mob/living/silicon/binarycheck()
+ var/area/our_area = get_area(src)
+ if(our_area.area_flags & BINARY_JAMMING)
+ return FALSE
return TRUE
/mob/living/silicon/radio(message, list/message_mods = list(), list/spans, language)
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
index 623e18e0494..ae0011f998a 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
@@ -272,8 +272,8 @@ While using this makes the system rely on OnFire, it still gives options for tim
var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(mychild))
H.color = COLOR_RED
-/obj/structure/elite_tumor/attackby(obj/item/attacking_item, mob/user, params)
- . = ..()
+/obj/structure/elite_tumor/item_interaction(mob/living/user, obj/item/attacking_item, list/modifiers)
+ . = NONE
if(istype(attacking_item, /obj/item/organ/internal/monster_core/regenerative_core) && activity == TUMOR_INACTIVE && !boosted)
var/obj/item/organ/internal/monster_core/regenerative_core/core = attacking_item
visible_message(span_boldwarning("As [user] drops the core into [src], [src] appears to swell."))
@@ -282,7 +282,7 @@ While using this makes the system rely on OnFire, it still gives options for tim
set_light_range(6)
desc = "[desc] This one seems to glow with a strong intensity."
qdel(core)
- return TRUE
+ return ITEM_INTERACT_SUCCESS
/obj/structure/elite_tumor/proc/arena_checks()
if(activity != TUMOR_ACTIVE || QDELETED(src))
diff --git a/code/modules/mob/living/simple_animal/hostile/ooze.dm b/code/modules/mob/living/simple_animal/hostile/ooze.dm
index e2a9d7659d5..f25fd4dc0fe 100644
--- a/code/modules/mob/living/simple_animal/hostile/ooze.dm
+++ b/code/modules/mob/living/simple_animal/hostile/ooze.dm
@@ -376,7 +376,7 @@
name = "mending globule"
icon_state = "glob_projectile"
shrapnel_type = /obj/item/mending_globule
- embedding = list("embed_chance" = 100, ignore_throwspeed_threshold = TRUE, "pain_mult" = 0, "jostle_pain_mult" = 0, "fall_chance" = 0.5)
+ embed_type = /datum/embed_data/mending_globule
damage = 0
///This item is what is embedded into the mob, and actually handles healing of mending globules
@@ -385,10 +385,17 @@
desc = "It somehow heals those who touch it."
icon = 'icons/obj/science/vatgrowing.dmi'
icon_state = "globule"
- embedding = list("embed_chance" = 100, ignore_throwspeed_threshold = TRUE, "pain_mult" = 0, "jostle_pain_mult" = 0, "fall_chance" = 0.5)
+ embed_type = /datum/embed_data/mending_globule
var/obj/item/bodypart/bodypart
var/heals_left = 35
+/datum/embed_data/mending_globule
+ embed_chance = 100
+ ignore_throwspeed_threshold = TRUE
+ pain_mult = 0
+ jostle_pain_mult = 0
+ fall_chance = 0.5
+
/obj/item/mending_globule/Destroy()
. = ..()
bodypart = null
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 0922ac6ae51..16ccf461e25 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -470,97 +470,6 @@
return FALSE
-/**
- * Try to equip an item to a slot on the mob
- *
- * This is a SAFE proc. Use this instead of equip_to_slot()!
- *
- * set qdel_on_fail to have it delete W if it fails to equip
- *
- * set disable_warning to disable the 'you are unable to equip that' warning.
- *
- * unset redraw_mob to prevent the mob icons from being redrawn at the end.
- *
- * Initial is used to indicate whether or not this is the initial equipment (job datums etc) or just a player doing it
- *
- * set indirect_action to allow insertions into "soft" locked objects, things that are easily opened by the owning mob
- */
-/mob/proc/equip_to_slot_if_possible(obj/item/W, slot, qdel_on_fail = FALSE, disable_warning = FALSE, redraw_mob = TRUE, bypass_equip_delay_self = FALSE, initial = FALSE, indirect_action = FALSE)
- if(!istype(W) || QDELETED(W)) //This qdeleted is to prevent stupid behavior with things that qdel during init, like say stacks
- return FALSE
- if(!W.mob_can_equip(src, slot, disable_warning, bypass_equip_delay_self, indirect_action = indirect_action))
- if(qdel_on_fail)
- qdel(W)
- else if(!disable_warning)
- to_chat(src, span_warning("You are unable to equip that!"))
- return FALSE
- equip_to_slot(W, slot, initial, redraw_mob, indirect_action = indirect_action) //This proc should not ever fail.
- return TRUE
-
-/**
- * Actually equips an item to a slot (UNSAFE)
- *
- * This is an UNSAFE proc. It merely handles the actual job of equipping. All the checks on
- * whether you can or can't equip need to be done before! Use mob_can_equip() for that task.
- *
- *In most cases you will want to use equip_to_slot_if_possible()
- */
-/mob/proc/equip_to_slot(obj/item/equipping, slot, initial = FALSE, redraw_mob = FALSE, indirect_action = FALSE)
- return
-
-/**
- * Equip an item to the slot or delete
- *
- * This is just a commonly used configuration for the equip_to_slot_if_possible() proc, used to
- * equip people when the round starts and when events happen and such.
- *
- * Also bypasses equip delay checks, since the mob isn't actually putting it on.
- * Initial is used to indicate whether or not this is the initial equipment (job datums etc) or just a player doing it
- * set indirect_action to allow insertions into "soft" locked objects, things that are easily opened by the owning mob
- */
-/mob/proc/equip_to_slot_or_del(obj/item/W, slot, initial = FALSE, indirect_action = FALSE)
- return equip_to_slot_if_possible(W, slot, TRUE, TRUE, FALSE, TRUE, initial, indirect_action)
-
-/**
- * Auto equip the passed in item the appropriate slot based on equipment priority
- *
- * puts the item "W" into an appropriate slot in a human's inventory
- *
- * returns 0 if it cannot, 1 if successful
- */
-/mob/proc/equip_to_appropriate_slot(obj/item/W, qdel_on_fail = FALSE, indirect_action = FALSE, blacklist, initial) //SKYRAT EDIT CHANGE
-
- if(!istype(W))
- return FALSE
- var/slot_priority = W.slot_equipment_priority
-
- if(!slot_priority)
- slot_priority = list( \
- ITEM_SLOT_BACK, ITEM_SLOT_ID,\
- ITEM_SLOT_ICLOTHING, ITEM_SLOT_OCLOTHING,\
- ITEM_SLOT_MASK, ITEM_SLOT_HEAD, ITEM_SLOT_NECK,\
- ITEM_SLOT_FEET, ITEM_SLOT_GLOVES,\
- ITEM_SLOT_EARS, ITEM_SLOT_EYES,\
- ITEM_SLOT_BELT, ITEM_SLOT_SUITSTORE,\
- ITEM_SLOT_LPOCKET, ITEM_SLOT_RPOCKET,\
- ITEM_SLOT_DEX_STORAGE\
- )
-
- //SKYRAT EDIT CHANGE BEGIN - CUSTOMIZATION
- /*
- for(var/slot in slot_priority)
- if(equip_to_slot_if_possible(W, slot, FALSE, TRUE, TRUE, FALSE, FALSE)) //qdel_on_fail = FALSE; disable_warning = TRUE; redraw_mob = TRUE;
- */
- if (blacklist)
- slot_priority -= blacklist
- for(var/slot in slot_priority)
- if(equip_to_slot_if_possible(W, slot, FALSE, TRUE, TRUE, FALSE, initial, indirect_action = indirect_action)) //qdel_on_fail = FALSE; disable_warning = TRUE; redraw_mob = TRUE;
- //SKYRAT EDIT CHANGE END
- return TRUE
-
- if(qdel_on_fail)
- qdel(W)
- return FALSE
/**
* Reset the attached clients perspective (viewpoint)
*
@@ -942,10 +851,6 @@
set category = null
return
-///Is the mob muzzled (default false)
-/mob/proc/is_muzzled()
- return FALSE
-
/// Adds this list to the output to the stat browser
/mob/proc/get_status_tab_items()
. = list("") //we want to offset unique stuff from standard stuff
diff --git a/code/modules/mob/mob_say.dm b/code/modules/mob/mob_say.dm
index 9d00c4c1b67..a58d15801c0 100644
--- a/code/modules/mob/mob_say.dm
+++ b/code/modules/mob/mob_say.dm
@@ -106,9 +106,6 @@
if(!allow_mimes && HAS_MIND_TRAIT(src, TRAIT_MIMING))
return FALSE
- if(is_muzzled())
- return FALSE
-
return ..()
///Speak as a dead person (ghost etc)
diff --git a/code/modules/mob_spawn/corpses/nanotrasen_corpses.dm b/code/modules/mob_spawn/corpses/nanotrasen_corpses.dm
index 0940815f1fc..0ed30e5a521 100644
--- a/code/modules/mob_spawn/corpses/nanotrasen_corpses.dm
+++ b/code/modules/mob_spawn/corpses/nanotrasen_corpses.dm
@@ -25,7 +25,7 @@
suit = /obj/item/clothing/suit/armor/bulletproof
ears = /obj/item/radio/headset/heads/captain
glasses = /obj/item/clothing/glasses/eyepatch
- mask = /obj/item/clothing/mask/cigarette/cigar/cohiba
+ mask = /obj/item/cigarette/cigar/cohiba
head = /obj/item/clothing/head/hats/centhat
gloves = /obj/item/clothing/gloves/tackler/combat
shoes = /obj/item/clothing/shoes/combat/swat
diff --git a/code/modules/mob_spawn/ghost_roles/space_roles.dm b/code/modules/mob_spawn/ghost_roles/space_roles.dm
index 764d20c9a76..79d028bdbcb 100644
--- a/code/modules/mob_spawn/ghost_roles/space_roles.dm
+++ b/code/modules/mob_spawn/ghost_roles/space_roles.dm
@@ -181,6 +181,6 @@
ears = /obj/item/radio/headset/syndicate/alt/leader
glasses = /obj/item/clothing/glasses/thermal/eyepatch
head = /obj/item/clothing/head/hats/hos/cap/syndicate
- mask = /obj/item/clothing/mask/cigarette/cigar/havana
+ mask = /obj/item/cigarette/cigar/havana
l_pocket = /obj/item/melee/energy/sword/saber/red
r_pocket = /obj/item/melee/baton/telescopic
diff --git a/code/modules/mob_spawn/ghost_roles/unused_roles.dm b/code/modules/mob_spawn/ghost_roles/unused_roles.dm
index 817b2154986..102501f9b39 100644
--- a/code/modules/mob_spawn/ghost_roles/unused_roles.dm
+++ b/code/modules/mob_spawn/ghost_roles/unused_roles.dm
@@ -308,7 +308,7 @@
/datum/outfit/syndicatespace/syndicrew
name = "Syndicate Ship Crew Member"
- glasses = /obj/item/clothing/glasses/night
+ glasses = /obj/item/clothing/glasses/night/colorless
mask = /obj/item/clothing/mask/gas/syndicate
l_pocket = /obj/item/gun/ballistic/automatic/pistol
r_pocket = /obj/item/knife/combat/survival
diff --git a/code/modules/mob_spawn/mob_spawn.dm b/code/modules/mob_spawn/mob_spawn.dm
index 6d92d31e4da..32836bf7c5e 100644
--- a/code/modules/mob_spawn/mob_spawn.dm
+++ b/code/modules/mob_spawn/mob_spawn.dm
@@ -60,26 +60,15 @@
spawned_human.undershirt = "Nude"
spawned_human.socks = "Nude"
spawned_human.bra = "Nude" //SKYRAT EDIT ADDITION
+ randomize_human_normie(spawned_human)
if(hairstyle)
- spawned_human.hairstyle = hairstyle
- else
- spawned_human.hairstyle = random_hairstyle(spawned_human.gender)
+ spawned_human.set_hairstyle(hairstyle, update = FALSE)
if(facial_hairstyle)
- spawned_human.facial_hairstyle = facial_hairstyle
- else
- spawned_human.facial_hairstyle = random_facial_hairstyle(spawned_human.gender)
+ spawned_human.set_facial_hairstyle(facial_hairstyle, update = FALSE)
if(haircolor)
- spawned_human.hair_color = haircolor
- else
- spawned_human.hair_color = "#[random_color()]"
+ spawned_human.set_haircolor(haircolor, update = FALSE)
if(facial_haircolor)
- spawned_human.facial_hair_color = facial_haircolor
- else
- spawned_human.facial_hair_color = "#[random_color()]"
- if(skin_tone)
- spawned_human.skin_tone = skin_tone
- else
- spawned_human.skin_tone = pick(GLOB.skin_tones)
+ spawned_human.set_facial_haircolor(facial_haircolor, update = FALSE)
spawned_human.update_body(is_creating = TRUE)
/obj/effect/mob_spawn/proc/name_mob(mob/living/spawned_mob, forced_name)
diff --git a/code/modules/mod/mod_link.dm b/code/modules/mod/mod_link.dm
index 345b9f84216..bcbf90ed442 100644
--- a/code/modules/mod/mod_link.dm
+++ b/code/modules/mod/mod_link.dm
@@ -77,29 +77,34 @@
)
/obj/item/mod/control/multitool_act_secondary(mob/living/user, obj/item/multitool/tool)
- if(!multitool_check_buffer(user, tool))
- return
+ . = NONE
+
var/tool_frequency = null
if(istype(tool.buffer, /datum/mod_link))
var/datum/mod_link/buffer_link = tool.buffer
tool_frequency = buffer_link.frequency
balloon_alert(user, "frequency set")
+ . = ITEM_INTERACT_SUCCESS
if(!tool_frequency && mod_link.frequency)
tool.set_buffer(mod_link)
balloon_alert(user, "frequency copied")
+ . = ITEM_INTERACT_SUCCESS
else if(tool_frequency && !mod_link.frequency)
mod_link.frequency = tool_frequency
+ . = ITEM_INTERACT_SUCCESS
else if(tool_frequency && mod_link.frequency)
var/response = tgui_alert(user, "Would you like to copy or imprint the frequency?", "MODlink Frequency", list("Copy", "Imprint"))
if(!user.is_holding(tool))
- return
+ return ITEM_INTERACT_BLOCKING
switch(response)
if("Copy")
tool.set_buffer(mod_link)
balloon_alert(user, "frequency copied")
+ . = ITEM_INTERACT_SUCCESS
if("Imprint")
mod_link.frequency = tool_frequency
balloon_alert(user, "frequency set")
+ . = ITEM_INTERACT_SUCCESS
/obj/item/mod/control/proc/can_call()
return get_charge() && wearer && wearer.stat < DEAD
@@ -231,29 +236,34 @@
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/item/clothing/neck/link_scryer/multitool_act_secondary(mob/living/user, obj/item/multitool/tool)
- if(!multitool_check_buffer(user, tool))
- return
+ . = NONE
+
var/tool_frequency = null
if(istype(tool.buffer, /datum/mod_link))
var/datum/mod_link/buffer_link = tool.buffer
tool_frequency = buffer_link.frequency
balloon_alert(user, "frequency set")
+ . = ITEM_INTERACT_SUCCESS
if(!tool_frequency && mod_link.frequency)
tool.set_buffer(mod_link)
balloon_alert(user, "frequency copied")
+ . = ITEM_INTERACT_SUCCESS
else if(tool_frequency && !mod_link.frequency)
mod_link.frequency = tool_frequency
+ . = ITEM_INTERACT_SUCCESS
else if(tool_frequency && mod_link.frequency)
var/response = tgui_alert(user, "Would you like to copy or imprint the frequency?", "MODlink Frequency", list("Copy", "Imprint"))
if(!user.is_holding(tool))
- return
+ return ITEM_INTERACT_BLOCKING
switch(response)
if("Copy")
tool.set_buffer(mod_link)
balloon_alert(user, "frequency copied")
+ . = ITEM_INTERACT_SUCCESS
if("Imprint")
mod_link.frequency = tool_frequency
balloon_alert(user, "frequency set")
+ . = ITEM_INTERACT_SUCCESS
/obj/item/clothing/neck/link_scryer/worn_overlays(mutable_appearance/standing, isinhands)
. = ..()
diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm
index d6465960beb..e751d1e943b 100644
--- a/code/modules/mod/mod_types.dm
+++ b/code/modules/mod/mod_types.dm
@@ -197,13 +197,13 @@
/obj/item/mod/module/storage/large_capacity,
/obj/item/mod/module/hat_stabilizer,
/obj/item/mod/module/magnetic_harness,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/pathfinder,
/obj/item/mod/module/quick_cuff,
/obj/item/mod/module/headprotector,
)
default_pins = list(
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
)
/obj/item/mod/control/pre_equipped/cosmohonk
@@ -245,7 +245,7 @@
/obj/item/mod/module/shock_absorber,
/obj/item/mod/module/emp_shield,
/obj/item/mod/module/magnetic_harness,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/jump_jet,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/dna_lock,
@@ -254,7 +254,7 @@
)
default_pins = list(
/obj/item/mod/module/armor_booster,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/jump_jet,
)
@@ -268,7 +268,7 @@
/obj/item/mod/module/shock_absorber,
/obj/item/mod/module/emp_shield,
/obj/item/mod/module/magnetic_harness,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/jump_jet,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/hat_stabilizer/syndicate,
@@ -276,14 +276,14 @@
)
default_pins = list(
/obj/item/mod/module/armor_booster,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/jump_jet,
)
/obj/item/mod/control/pre_equipped/nuclear/no_jetpack
/obj/item/mod/control/pre_equipped/nuclear/no_jetpack/Initialize(mapload, new_theme, new_skin, new_core)
- applied_modules -= list(/obj/item/mod/module/jetpack/advanced, /obj/item/mod/module/jump_jet)
+ applied_modules -= list(/obj/item/mod/module/jetpack, /obj/item/mod/module/jump_jet)
return ..()
/obj/item/mod/control/pre_equipped/nuclear/plasmaman
@@ -305,7 +305,7 @@
/obj/item/mod/module/shock_absorber,
/obj/item/mod/module/emp_shield,
/obj/item/mod/module/magnetic_harness,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/jump_jet,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/hat_stabilizer/syndicate,
@@ -313,7 +313,7 @@
)
default_pins = list(
/obj/item/mod/module/armor_booster,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/jump_jet,
)
@@ -324,7 +324,7 @@
/obj/item/mod/module/emp_shield,
/obj/item/mod/module/magnetic_harness,
/obj/item/mod/module/thermal_regulator,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/jump_jet,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/hat_stabilizer/syndicate,
@@ -333,7 +333,7 @@
)
default_pins = list(
/obj/item/mod/module/armor_booster,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/jump_jet,
/obj/item/mod/module/flamethrower,
)
@@ -357,7 +357,7 @@
starting_frequency = MODLINK_FREQ_SYNDICATE
applied_cell = /obj/item/stock_parts/power_store/cell/super
applied_modules = list(
- /obj/item/mod/module/organ_thrower,
+ /obj/item/mod/module/organizer,
/obj/item/mod/module/defibrillator/combat,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/health_analyzer,
@@ -426,13 +426,13 @@
applied_modules = list(
/obj/item/mod/module/storage,
/obj/item/mod/module/magnetic_harness,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/jump_jet,
/obj/item/mod/module/flashlight,
)
default_pins = list(
/obj/item/mod/module/armor_booster,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/jump_jet,
)
@@ -623,7 +623,7 @@
/obj/item/mod/module/stealth/ninja,
/obj/item/mod/module/quick_carry/advanced,
/obj/item/mod/module/magboot/advanced,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/anomaly_locked/kinesis/admin,
/obj/item/mod/module/shove_blocker,
/obj/item/mod/module/quick_cuff,
@@ -631,7 +631,7 @@
default_pins = list(
/obj/item/mod/module/stealth/ninja,
/obj/item/mod/module/magboot/advanced,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/anomaly_locked/kinesis/admin,
)
diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm
index 3ef6b955871..815cfb0a144 100644
--- a/code/modules/mod/modules/modules_general.dm
+++ b/code/modules/mod/modules/modules_general.dm
@@ -108,8 +108,6 @@
overlay_state_inactive = "module_jetpack"
overlay_state_active = "module_jetpack_on"
required_slots = list(ITEM_SLOT_BACK)
- /// Do we give the wearer a speed buff.
- var/full_speed = FALSE
/// Do we have stabilizers? If yes the user won't move from inertia.
var/stabilize = TRUE
/// Callback to see if we can thrust the user.
@@ -143,14 +141,6 @@
/datum/effect_system/trail_follow/ion/grav_allowed \
)
-/obj/item/mod/module/jetpack/on_activation()
- if(full_speed)
- mod.wearer.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed)
-
-/obj/item/mod/module/jetpack/on_deactivation(display_message = TRUE, deleting = FALSE)
- if(full_speed)
- mod.wearer.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed)
-
/obj/item/mod/module/jetpack/get_configuration()
. = ..()
.["stabilizers"] = add_ui_configuration("Stabilizers", "bool", stabilize)
@@ -167,15 +157,6 @@
return FALSE
return TRUE
-/obj/item/mod/module/jetpack/advanced
- name = "MOD advanced ion jetpack module"
- desc = "An improvement on the previous model of electric thrusters. This one achieves higher speeds through \
- mounting of more jets and application of red paint."
- icon_state = "jetpack_advanced"
- overlay_state_inactive = "module_jetpackadv"
- overlay_state_active = "module_jetpackadv_on"
- full_speed = TRUE
-
/// Cooldown to use if we didn't actually launch a jump jet
#define FAILED_ACTIVATION_COOLDOWN 3 SECONDS
diff --git a/code/modules/mod/modules/modules_medical.dm b/code/modules/mod/modules/modules_medical.dm
index 3cf1d34a63a..a0d58d721f3 100644
--- a/code/modules/mod/modules/modules_medical.dm
+++ b/code/modules/mod/modules/modules_medical.dm
@@ -119,19 +119,19 @@
volume = 30
inject_flags = INJECT_CHECK_PENETRATE_THICK
-///Organ Thrower - Lets you shoot organs, immediately replacing them if the target has the organ manipulation surgery.
-/obj/item/mod/module/organ_thrower
- name = "MOD organ thrower module"
+///Organizer - Lets you shoot organs, immediately replacing them if the target has the organ manipulation surgery.
+/obj/item/mod/module/organizer
+ name = "MOD organizer module"
desc = "A device recovered from a crashed Interdyne Pharmaceuticals vessel, \
this module has been unearthed for better or for worse. \
- It's an arm-mounted device utilizing technology similar to modern-day part replacers, \
- capable of storing and inserting organs into open patients. \
+ It's an arm-mounted device utilizing technology similar to modern rapid part exchange devices, \
+ capable of instantly replacing up to 5 organs at once in surgery without the need to remove them first, even from range. \
It's recommended by the DeForest Medical Corporation to not inform patients it has been used."
- icon_state = "organ_thrower"
+ icon_state = "organizer"
module_type = MODULE_ACTIVE
complexity = 2
use_energy_cost = DEFAULT_CHARGE_DRAIN
- incompatible_modules = list(/obj/item/mod/module/organ_thrower, /obj/item/mod/module/microwave_beam)
+ incompatible_modules = list(/obj/item/mod/module/organizer, /obj/item/mod/module/microwave_beam)
cooldown_time = 0.5 SECONDS
required_slots = list(ITEM_SLOT_GLOVES)
/// How many organs the module can hold.
@@ -139,7 +139,7 @@
/// A list of all our organs.
var/organ_list = list()
-/obj/item/mod/module/organ_thrower/on_select_use(atom/target)
+/obj/item/mod/module/organizer/on_select_use(atom/target)
. = ..()
if(!.)
return
diff --git a/code/modules/mod/modules/modules_ninja.dm b/code/modules/mod/modules/modules_ninja.dm
index a868eb62056..0e0de691e03 100644
--- a/code/modules/mod/modules/modules_ninja.dm
+++ b/code/modules/mod/modules/modules_ninja.dm
@@ -418,11 +418,7 @@
if(IS_SPACE_NINJA(mod.wearer))
mod.wearer.say(pick_list_replacements(NINJA_FILE, "lines"), forced = type)
to_chat(mod.wearer, span_notice("You have used the adrenaline boost."))
- mod.wearer.SetUnconscious(0)
- mod.wearer.SetStun(0)
- mod.wearer.SetKnockdown(0)
- mod.wearer.SetImmobilized(0)
- mod.wearer.SetParalyzed(0)
+ mod.wearer.SetAllImmobility(0)
mod.wearer.adjustStaminaLoss(-200)
mod.wearer.remove_status_effect(/datum/status_effect/speech/stutter)
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/stimulants, 5)
@@ -430,24 +426,18 @@
addtimer(CALLBACK(src, PROC_REF(boost_aftereffects), mod.wearer), 7 SECONDS)
/obj/item/mod/module/adrenaline_boost/on_install()
- RegisterSignal(mod, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby))
+ RegisterSignal(mod, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(try_boost))
/obj/item/mod/module/adrenaline_boost/on_uninstall(deleting = FALSE)
- UnregisterSignal(mod, COMSIG_ATOM_ATTACKBY)
+ UnregisterSignal(mod, COMSIG_ATOM_ITEM_INTERACTION)
-/obj/item/mod/module/adrenaline_boost/attackby(obj/item/attacking_item, mob/user, params)
- if(charge_boost(attacking_item, user))
- return TRUE
- return ..()
-
-/obj/item/mod/module/adrenaline_boost/proc/on_attackby(datum/source, obj/item/attacking_item, mob/user)
+/obj/item/mod/module/adrenaline_boost/proc/try_boost(source, mob/user, obj/item/attacking_item)
SIGNAL_HANDLER
-
- if(charge_boost(attacking_item, user))
+ if(charge_boost(attacking_item))
return COMPONENT_NO_AFTERATTACK
return NONE
-/obj/item/mod/module/adrenaline_boost/proc/charge_boost(obj/item/attacking_item, mob/user)
+/obj/item/mod/module/adrenaline_boost/proc/charge_boost(obj/item/attacking_item)
if(!attacking_item.is_open_container())
return FALSE
if(reagents.has_reagent(reagent_required, reagent_required_amount))
diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm
index 2c4a672874b..c4aa48a1122 100644
--- a/code/modules/mod/modules/modules_security.dm
+++ b/code/modules/mod/modules/modules_security.dm
@@ -308,6 +308,8 @@
var/field_radius = 2
/// Damage multiplier on projectiles.
var/damage_multiplier = 0.75
+ /// Debuff multiplier on projectiles.
+ var/debuff_multiplier = 0.66
/// Speed multiplier on projectiles, higher means slower.
var/speed_multiplier = 2.5
/// List of all tracked projectiles.
@@ -338,6 +340,9 @@
SIGNAL_HANDLER
projectile.damage *= damage_multiplier
+ projectile.stamina *= damage_multiplier
+ projectile.stun *= debuff_multiplier
+ projectile.knockdown *= debuff_multiplier
projectile.speed *= speed_multiplier
projectile.add_overlay(projectile_effect)
@@ -346,6 +351,9 @@
projectile.damage /= damage_multiplier
projectile.speed /= speed_multiplier
+ projectile.stamina /= damage_multiplier
+ projectile.stun /= debuff_multiplier
+ projectile.knockdown /= debuff_multiplier
projectile.cut_overlay(projectile_effect)
///Active Sonar - Displays a hud circle on the turf of any living creatures in the given radius
diff --git a/code/modules/mod/modules/modules_service.dm b/code/modules/mod/modules/modules_service.dm
index 044137f0f2d..4d2650585b1 100644
--- a/code/modules/mod/modules/modules_service.dm
+++ b/code/modules/mod/modules/modules_service.dm
@@ -35,7 +35,7 @@
module_type = MODULE_ACTIVE
complexity = 1
use_energy_cost = DEFAULT_CHARGE_DRAIN * 5
- incompatible_modules = list(/obj/item/mod/module/microwave_beam, /obj/item/mod/module/organ_thrower)
+ incompatible_modules = list(/obj/item/mod/module/microwave_beam, /obj/item/mod/module/organizer)
cooldown_time = 10 SECONDS
required_slots = list(ITEM_SLOT_GLOVES)
diff --git a/code/modules/mod/modules/modules_supply.dm b/code/modules/mod/modules/modules_supply.dm
index 8564dabb9ef..701861897dc 100644
--- a/code/modules/mod/modules/modules_supply.dm
+++ b/code/modules/mod/modules/modules_supply.dm
@@ -559,7 +559,7 @@
light_range = 1
light_power = 1
light_color = COLOR_LIGHT_ORANGE
- embedding = null
+ embed_type = null
/obj/projectile/bullet/mining_bomb/Initialize(mapload)
. = ..()
diff --git a/code/modules/modular_computers/computers/item/pda.dm b/code/modules/modular_computers/computers/item/pda.dm
index 8df5cad8850..f0ba52b0763 100644
--- a/code/modules/modular_computers/computers/item/pda.dm
+++ b/code/modules/modular_computers/computers/item/pda.dm
@@ -50,7 +50,7 @@
/obj/item/lipstick,
/obj/item/flashlight/pen,
/obj/item/reagent_containers/hypospray/medipen,
- /obj/item/clothing/mask/cigarette,
+ /obj/item/cigarette,
)
/obj/item/modular_computer/pda/Initialize(mapload)
diff --git a/code/modules/movespeed/modifiers/items.dm b/code/modules/movespeed/modifiers/items.dm
index 6bdf2f31760..601ecc22892 100644
--- a/code/modules/movespeed/modifiers/items.dm
+++ b/code/modules/movespeed/modifiers/items.dm
@@ -5,9 +5,6 @@
/datum/movespeed_modifier/jetpack/cybernetic
multiplicative_slowdown = -0.5
-/datum/movespeed_modifier/jetpack/fullspeed
- multiplicative_slowdown = -0.5
-
/datum/movespeed_modifier/die_of_fate
multiplicative_slowdown = 1
diff --git a/code/modules/pai/pai.dm b/code/modules/pai/pai.dm
index 6f19dc7d7ca..83adc05919a 100644
--- a/code/modules/pai/pai.dm
+++ b/code/modules/pai/pai.dm
@@ -460,7 +460,7 @@
for(var/mob/living/cultist as anything in invokers)
to_chat(cultist, span_cult_italic("You don't think this is what Nar'Sie had in mind when She asked for blood sacrifices..."))
- return STOP_SACRIFICE
+ return STOP_SACRIFICE|SILENCE_SACRIFICE_MESSAGE
/// Updates the distance we can be from our pai card
/mob/living/silicon/pai/proc/increment_range(increment_amount)
diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm
index 36949b65c7c..41d24ea9c6d 100644
--- a/code/modules/paperwork/pen.dm
+++ b/code/modules/paperwork/pen.dm
@@ -29,7 +29,7 @@
var/degrees = 0
var/font = PEN_FONT
var/requires_gravity = TRUE // can you use this to write in zero-g
- embedding = list(embed_chance = 50)
+ embed_type = /datum/embed_data/pen
sharpness = SHARP_POINTY
var/dart_insert_icon = 'icons/obj/weapons/guns/toy.dmi'
var/dart_insert_casing_icon_state = "overlay_pen"
@@ -37,6 +37,9 @@
/// If this pen can be clicked in order to retract it
var/can_click = TRUE
+/datum/embed_data/pen
+ embed_chance = 50
+
/obj/item/pen/Initialize(mapload)
. = ..()
AddComponent(/datum/component/dart_insert, \
@@ -86,7 +89,7 @@
return list(
"damage" = max(5, throwforce),
"speed" = max(0, throw_speed - 3),
- "embedding" = embedding,
+ "embedding" = get_embed(),
"armour_penetration" = armour_penetration,
"wound_bonus" = wound_bonus,
"bare_wound_bonus" = bare_wound_bonus,
@@ -191,7 +194,7 @@
"Black and Silver" = "pen-fountain-b",
"Command Blue" = "pen-fountain-cb"
)
- embedding = list("embed_chance" = 75)
+ embed_type = /datum/embed_data/pen/captain
dart_insert_casing_icon_state = "overlay_fountainpen_gold"
dart_insert_projectile_icon_state = "overlay_fountainpen_gold_proj"
var/list/overlay_reskin = list(
@@ -202,6 +205,9 @@
"Command Blue" = "overlay_fountainpen_gold"
)
+/datum/embed_data/pen/captain
+ embed_chance = 50
+
/obj/item/pen/fountain/captain/Initialize(mapload)
. = ..()
AddComponent(/datum/component/butchering, \
@@ -414,7 +420,7 @@
inhand_icon_state = hidden_icon
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
- embedding = list(embed_chance = 100) // Rule of cool
+ set_embed(/datum/embed_data/edagger_active)
else
name = initial(name)
desc = initial(desc)
@@ -422,15 +428,17 @@
inhand_icon_state = initial(inhand_icon_state)
lefthand_file = initial(lefthand_file)
righthand_file = initial(righthand_file)
- embedding = list(embed_chance = EMBED_CHANCE)
+ set_embed(embed_type)
- updateEmbedding()
if(user)
balloon_alert(user, "[hidden_name] [active ? "active" : "concealed"]")
playsound(src, active ? 'sound/weapons/saberon.ogg' : 'sound/weapons/saberoff.ogg', 5, TRUE)
set_light_on(active)
return COMPONENT_NO_DEFAULT_MESSAGE
+/datum/embed_data/edagger_active
+ embed_chance = 100
+
/obj/item/pen/edagger/proc/on_scan(datum/source, mob/user, list/extra_data)
SIGNAL_HANDLER
LAZYADD(extra_data[DETSCAN_CATEGORY_ILLEGAL], "Hard-light generator detected.")
diff --git a/code/modules/paperwork/ticketmachine.dm b/code/modules/paperwork/ticketmachine.dm
index 5c849f4a530..b4e97615a92 100644
--- a/code/modules/paperwork/ticketmachine.dm
+++ b/code/modules/paperwork/ticketmachine.dm
@@ -49,13 +49,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/ticket_machine, 32)
. += span_notice("The ticket machine shows that ticket #[current_number] is currently being served.")
. += span_notice("You can take a ticket out with Left-Click to be number [ticket_number + 1] in queue.")
-/obj/machinery/ticket_machine/multitool_act(mob/living/user, obj/item/I)
- if(!multitool_check_buffer(user, I)) //make sure it has a data buffer
- return
- var/obj/item/multitool/M = I
+/obj/machinery/ticket_machine/multitool_act(mob/living/user, obj/item/multitool/M)
M.set_buffer(src)
balloon_alert(user, "saved to multitool buffer")
- return TRUE
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/ticket_machine/emag_act(mob/user, obj/item/card/emag/emag_card) //Emag the ticket machine to dispense burning tickets, as well as randomize its number to destroy the HoP's mind.
if(obj_flags & EMAGGED)
diff --git a/code/modules/plumbing/plumbers/_plumb_machinery.dm b/code/modules/plumbing/plumbers/_plumb_machinery.dm
index 2e8eaee9d6d..1f60a4eefb4 100644
--- a/code/modules/plumbing/plumbers/_plumb_machinery.dm
+++ b/code/modules/plumbing/plumbers/_plumb_machinery.dm
@@ -12,6 +12,7 @@
active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION * 2.75
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
interaction_flags_machine = parent_type::interaction_flags_machine | INTERACT_MACHINE_OFFLINE
+ reagents = /datum/reagents/plumbing
///Plumbing machinery is always gonna need reagents, so we might aswell put it here
var/buffer = 50
@@ -25,6 +26,12 @@
AddComponent(/datum/component/simple_rotation)
register_context()
+/obj/machinery/plumbing/create_reagents(max_vol, flags)
+ if(!ispath(reagents))
+ qdel(reagents)
+ reagents = new reagents(max_vol, flags)
+ reagents.my_atom = src
+
/obj/machinery/plumbing/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = NONE
if(isnull(held_item))
@@ -96,58 +103,94 @@
reagents.expose(get_turf(src), TOUCH) //splash on the floor
reagents.clear_reagents()
-///We can empty beakers in here and everything
-/obj/machinery/plumbing/input
- name = "input gate"
- desc = "Can be manually filled with reagents from containers."
- icon_state = "pipe_input"
- pass_flags_self = PASSMACHINE | LETPASSTHROW // Small
- reagent_flags = TRANSPARENT | REFILLABLE
+/**
+ * Specialized reagent container for plumbing. Uses the round robin approach of transferring reagents
+ * so transfer 5 from 15 water, 15 sugar and 15 plasma becomes 10, 15, 15 instead of 13.3333, 13.3333 13.3333. Good if you hate floating point errors
+ */
+/datum/reagents/plumbing
+/datum/reagents/plumbing/trans_to(
+ atom/target,
+ amount = 1,
+ multiplier = 1, //unused for plumbing
+ datum/reagent/target_id,
+ preserve_data = TRUE, //unused for plumbing
+ no_react = FALSE, //unused for plumbing we always want reactions
+ mob/transferred_by, //unused for plumbing logging is not important inside plumbing machines
+ remove_blacklisted = FALSE, //unused for plumbing, we don't care what reagents are inside us
+ methods = NONE, //unused for plumbing
+ show_message = TRUE, //unused for plumbing, used for logging only
+ ignore_stomach = FALSE //unused for plumbing, reagents flow only between machines & is not injected to mobs at any point in time
+)
+ if(QDELETED(target) || !total_volume)
+ return FALSE
-/obj/machinery/plumbing/input/Initialize(mapload, bolt, layer)
- . = ..()
- AddComponent(/datum/component/plumbing/simple_supply, bolt, layer)
+ if(!IS_FINITE(amount))
+ stack_trace("non finite amount passed to trans_to [amount] amount of reagents")
+ return FALSE
-///We can fill beakers in here and everything. we dont inheret from input because it has nothing that we need
-/obj/machinery/plumbing/output
- name = "output gate"
- desc = "A manual output for plumbing systems, for taking reagents directly into containers."
- icon_state = "pipe_output"
- pass_flags_self = PASSMACHINE | LETPASSTHROW // Small
- reagent_flags = TRANSPARENT | DRAINABLE
+ if(!isnull(target_id) && !ispath(target_id))
+ stack_trace("invalid target reagent id [target_id] passed to trans_to")
+ return FALSE
-/obj/machinery/plumbing/output/Initialize(mapload, bolt, layer)
- . = ..()
- AddComponent(/datum/component/plumbing/simple_demand, bolt, layer)
+ var/datum/reagents/target_holder
+ if(istype(target, /datum/reagents))
+ target_holder = target
+ else
+ target_holder = target.reagents
-/obj/machinery/plumbing/output/tap
- name = "drinking tap"
- desc = "A manual output for plumbing systems, for taking drinks directly into glasses."
- icon_state = "tap_output"
+ var/cached_amount = amount
-/obj/machinery/plumbing/tank
- name = "chemical tank"
- desc = "A massive chemical holding tank."
- icon_state = "tank"
- buffer = 400
+ // Prevents small amount problems, as well as zero and below zero amounts.
+ amount = round(min(amount, total_volume, target_holder.maximum_volume - target_holder.total_volume), CHEMICAL_QUANTISATION_LEVEL)
+ if(amount <= 0)
+ return FALSE
-/obj/machinery/plumbing/tank/Initialize(mapload, bolt, layer)
- . = ..()
- AddComponent(/datum/component/plumbing/tank, bolt, layer)
+ //Set up new reagents to inherit the old ongoing reactions
+ transfer_reactions(target_holder)
-///Layer manifold machine that connects a bunch of layers
-/obj/machinery/plumbing/layer_manifold
- name = "layer manifold"
- desc = "A plumbing manifold for layers."
- icon_state = "manifold"
- density = FALSE
+ var/list/cached_reagents = reagent_list
+ var/list/reagents_to_remove = list()
+ var/transfer_amount
+ var/transfered_amount
+ var/to_transfer = amount
+ var/total_transfered_amount = 0
-/obj/machinery/plumbing/layer_manifold/Initialize(mapload, bolt, layer)
- . = ..()
+ //first add reagents to target
+ for(var/datum/reagent/reagent as anything in cached_reagents)
+ if(!to_transfer)
+ break
- AddComponent(/datum/component/plumbing/manifold, bolt, FIRST_DUCT_LAYER)
- AddComponent(/datum/component/plumbing/manifold, bolt, SECOND_DUCT_LAYER)
- AddComponent(/datum/component/plumbing/manifold, bolt, THIRD_DUCT_LAYER)
- AddComponent(/datum/component/plumbing/manifold, bolt, FOURTH_DUCT_LAYER)
- AddComponent(/datum/component/plumbing/manifold, bolt, FIFTH_DUCT_LAYER)
+ if(!isnull(target_id))
+ if(reagent.type == target_id)
+ force_stop_reagent_reacting(reagent)
+ transfer_amount = min(to_transfer, reagent.volume)
+ else
+ continue
+ else
+ transfer_amount = min(to_transfer, reagent.volume)
+
+ if(reagent.intercept_reagents_transfer(target_holder, cached_amount))
+ continue
+
+ transfered_amount = target_holder.add_reagent(reagent.type, transfer_amount * multiplier, copy_data(reagent), chem_temp, reagent.purity, reagent.ph, no_react = TRUE, ignore_splitting = reagent.chemical_flags & REAGENT_DONOTSPLIT) //we only handle reaction after every reagent has been transferred.
+ if(!transfered_amount)
+ continue
+ reagents_to_remove += list(list("R" = reagent, "T" = transfer_amount))
+ total_transfered_amount += transfered_amount
+ to_transfer -= transfered_amount
+
+ if(!isnull(target_id))
+ break
+
+ //remove chemicals that were added above
+ for(var/list/data as anything in reagents_to_remove)
+ var/datum/reagent/reagent = data["R"]
+ transfer_amount = data["T"]
+ remove_reagent(reagent.type, transfer_amount)
+
+ //handle reactions
+ target_holder.handle_reactions()
+ src.handle_reactions()
+
+ return round(total_transfered_amount, CHEMICAL_VOLUME_ROUNDING)
diff --git a/code/modules/plumbing/plumbers/iv_drip.dm b/code/modules/plumbing/plumbers/iv_drip.dm
index 6e258555384..45c2ebca27a 100644
--- a/code/modules/plumbing/plumbers/iv_drip.dm
+++ b/code/modules/plumbing/plumbers/iv_drip.dm
@@ -10,7 +10,7 @@
/obj/machinery/iv_drip/plumbing/Initialize(mapload, bolt, layer)
. = ..()
- AddComponent(/datum/component/plumbing/iv_drip, bolt, layer)
+ AddComponent(/datum/component/plumbing/simple_demand, bolt, layer)
AddComponent(/datum/component/simple_rotation)
/obj/machinery/iv_drip/plumbing/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
diff --git a/code/modules/plumbing/plumbers/simple_machines.dm b/code/modules/plumbing/plumbers/simple_machines.dm
new file mode 100644
index 00000000000..c2ef3082973
--- /dev/null
+++ b/code/modules/plumbing/plumbers/simple_machines.dm
@@ -0,0 +1,57 @@
+///We can empty beakers in here and everything
+/obj/machinery/plumbing/input
+ name = "input gate"
+ desc = "Can be manually filled with reagents from containers."
+ icon_state = "pipe_input"
+ pass_flags_self = PASSMACHINE | LETPASSTHROW // Small
+ reagent_flags = TRANSPARENT | REFILLABLE
+
+/obj/machinery/plumbing/input/Initialize(mapload, bolt, layer)
+ . = ..()
+ AddComponent(/datum/component/plumbing/simple_supply, bolt, layer)
+
+///We can fill beakers in here and everything. we dont inheret from input because it has nothing that we need
+/obj/machinery/plumbing/output
+ name = "output gate"
+ desc = "A manual output for plumbing systems, for taking reagents directly into containers."
+ icon_state = "pipe_output"
+ pass_flags_self = PASSMACHINE | LETPASSTHROW // Small
+ reagent_flags = TRANSPARENT | DRAINABLE
+ reagents = /datum/reagents
+
+/obj/machinery/plumbing/output/Initialize(mapload, bolt, layer)
+ . = ..()
+ AddComponent(/datum/component/plumbing/simple_demand, bolt, layer)
+
+///For pouring reagents from ducts directly into cups
+/obj/machinery/plumbing/output/tap
+ name = "drinking tap"
+ desc = "A manual output for plumbing systems, for taking drinks directly into glasses."
+ icon_state = "tap_output"
+
+///For storing large volume of reagents
+/obj/machinery/plumbing/tank
+ name = "chemical tank"
+ desc = "A massive chemical holding tank."
+ icon_state = "tank"
+ buffer = 400
+
+/obj/machinery/plumbing/tank/Initialize(mapload, bolt, layer)
+ . = ..()
+ AddComponent(/datum/component/plumbing/tank, bolt, layer)
+
+///Layer manifold machine that connects a bunch of layers
+/obj/machinery/plumbing/layer_manifold
+ name = "layer manifold"
+ desc = "A plumbing manifold for layers."
+ icon_state = "manifold"
+ density = FALSE
+
+/obj/machinery/plumbing/layer_manifold/Initialize(mapload, bolt, layer)
+ . = ..()
+
+ AddComponent(/datum/component/plumbing/manifold, bolt, FIRST_DUCT_LAYER)
+ AddComponent(/datum/component/plumbing/manifold, bolt, SECOND_DUCT_LAYER)
+ AddComponent(/datum/component/plumbing/manifold, bolt, THIRD_DUCT_LAYER)
+ AddComponent(/datum/component/plumbing/manifold, bolt, FOURTH_DUCT_LAYER)
+ AddComponent(/datum/component/plumbing/manifold, bolt, FIFTH_DUCT_LAYER)
diff --git a/code/modules/plumbing/plumbers/teleporter.dm b/code/modules/plumbing/plumbers/teleporter.dm
index df79220d15a..46c46d594f6 100644
--- a/code/modules/plumbing/plumbers/teleporter.dm
+++ b/code/modules/plumbing/plumbers/teleporter.dm
@@ -12,15 +12,10 @@
. = ..()
AddComponent(/datum/component/plumbing/simple_demand, bolt, layer)
-/obj/machinery/plumbing/sender/multitool_act(mob/living/user, obj/item/I)
- if(!multitool_check_buffer(user, I))
- return
-
- var/obj/item/multitool/M = I
-
+/obj/machinery/plumbing/sender/multitool_act(mob/living/user, obj/item/multitool/M)
if(!istype(M.buffer, /obj/machinery/plumbing/receiver))
to_chat(user, span_warning("Invalid buffer."))
- return
+ return ITEM_INTERACT_BLOCKING
if(target)
lose_teleport_target()
@@ -28,7 +23,7 @@
set_teleport_target(M.buffer)
to_chat(user, span_green("You succesfully link [src] to the [M.buffer]."))
- return TRUE
+ return ITEM_INTERACT_SUCCESS
///Lose our previous target and make our previous target lose us. Seperate proc because I feel like I'll need this again
/obj/machinery/plumbing/sender/proc/lose_teleport_target()
@@ -67,14 +62,10 @@
. = ..()
AddComponent(/datum/component/plumbing/simple_supply, bolt)
-/obj/machinery/plumbing/receiver/multitool_act(mob/living/user, obj/item/I)
- if(!multitool_check_buffer(user, I))
- return
-
- var/obj/item/multitool/M = I
+/obj/machinery/plumbing/receiver/multitool_act(mob/living/user, obj/item/multitool/M)
M.set_buffer(src)
balloon_alert(user, "saved to multitool buffer")
- return TRUE
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/plumbing/receiver/process(seconds_per_tick)
if(!is_operational || panel_open)
diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm
index 1d982c98b5c..2704a577e59 100644
--- a/code/modules/power/singularity/field_generator.dm
+++ b/code/modules/power/singularity/field_generator.dm
@@ -51,6 +51,8 @@ no power level overlay is currently in the overlays list.
var/list/obj/machinery/field/generator/connected_gens = list()
///Check for asynk cleanups for this and the connected gens
var/clean_up = FALSE
+ /// we warm up and cool down instantly
+ var/instantenous = FALSE
/datum/armor/field_generator
melee = 25
@@ -207,8 +209,11 @@ no power level overlay is currently in the overlays list.
can_atmos_pass = ATMOS_PASS_YES
air_update_turf(TRUE, FALSE)
INVOKE_ASYNC(src, PROC_REF(cleanup))
- addtimer(CALLBACK(src, PROC_REF(cool_down)), 5 SECONDS)
RemoveElement(/datum/element/give_turf_traits, string_list(list(TRAIT_CONTAINMENT_FIELD)))
+ if(instantenous)
+ warming_up = 0
+ return
+ addtimer(CALLBACK(src, PROC_REF(cool_down)), 5 SECONDS)
/obj/machinery/field/generator/proc/cool_down()
if(active || warming_up <= 0)
@@ -219,9 +224,14 @@ no power level overlay is currently in the overlays list.
addtimer(CALLBACK(src, PROC_REF(cool_down)), 5 SECONDS)
/obj/machinery/field/generator/proc/turn_on()
+ AddElement(/datum/element/give_turf_traits, string_list(list(TRAIT_CONTAINMENT_FIELD)))
+ if(instantenous)
+ active = FG_ONLINE
+ warming_up = 3
+ start_fields()
+ return
active = FG_CHARGING
addtimer(CALLBACK(src, PROC_REF(warm_up)), 5 SECONDS)
- AddElement(/datum/element/give_turf_traits, string_list(list(TRAIT_CONTAINMENT_FIELD)))
/obj/machinery/field/generator/proc/warm_up()
if(!active)
@@ -420,9 +430,20 @@ no power level overlay is currently in the overlays list.
state = FG_WELDED
/obj/machinery/field/generator/starts_on/Initialize(mapload)
+ . = ..()
+ return INITIALIZE_HINT_LATELOAD
+
+/obj/machinery/field/generator/starts_on/post_machine_initialize()
. = ..()
turn_on()
+/obj/machinery/field/generator/starts_on/magic
+ power_level = 6 //forces the highest level overlay
+ instantenous = TRUE
+
+/obj/machinery/field/generator/starts_on/magic/process()
+ return PROCESS_KILL // this is the only place calc_power is called, and doing it here avoids one unnecessary proc call
+
#undef FG_UNSECURED
#undef FG_SECURED
#undef FG_WELDED
diff --git a/code/modules/projectiles/ammunition/ballistic/shotgun.dm b/code/modules/projectiles/ammunition/ballistic/shotgun.dm
index 26ecb8fc1d8..c3816608800 100644
--- a/code/modules/projectiles/ammunition/ballistic/shotgun.dm
+++ b/code/modules/projectiles/ammunition/ballistic/shotgun.dm
@@ -141,8 +141,8 @@
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/meteorslug, /datum/crafting_recipe/pulseslug, /datum/crafting_recipe/dragonsbreath, /datum/crafting_recipe/ionslug)
- AddComponent(
- /datum/component/slapcrafting,\
+ AddElement(
+ /datum/element/slapcrafting,\
slapcraft_recipes = slapcraft_recipe_list,\
)
diff --git a/code/modules/projectiles/ammunition/energy/special.dm b/code/modules/projectiles/ammunition/energy/special.dm
index f9d5ca5d612..c42bcdc746e 100644
--- a/code/modules/projectiles/ammunition/energy/special.dm
+++ b/code/modules/projectiles/ammunition/energy/special.dm
@@ -85,3 +85,10 @@
harmful = FALSE
e_cost = LASER_SHOTS(2, STANDARD_CELL_CHARGE * 0.5)
fire_sound = 'sound/weapons/gun/general/heavy_shot_suppressed.ogg' // fwip fwip fwip fwip
+
+// Used by /obj/item/gun/energy/photon
+/obj/item/ammo_casing/energy/photon
+ fire_sound = 'sound/weapons/lasercannonfire.ogg'
+ e_cost = LASER_SHOTS(4, STANDARD_CELL_CHARGE)
+ select_name = "flare"
+ projectile_type = /obj/projectile/energy/photon
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 80d26452fc4..c0a64007c13 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -63,11 +63,6 @@
/// True if a gun dosen't need a pin, mostly used for abstract guns like tentacles and meathooks
var/pinless = FALSE
- var/can_bayonet = FALSE //if a bayonet can be added or removed if it already has one.
- var/obj/item/knife/bayonet
- var/knife_x_offset = 0
- var/knife_y_offset = 0
-
var/ammo_x_offset = 0 //used for positioning ammo count overlay on sprite
var/ammo_y_offset = 0
@@ -84,12 +79,11 @@
add_seclight_point()
// give_gun_safeties() // SKYRAT EDIT ADDITION - GUN SAFETIES //BUBBER EDIT REMOVAL
give_manufacturer_examine() // SKYRAT EDIT ADDITON - MANUFACTURER EXAMINE
+ add_bayonet_point()
/obj/item/gun/Destroy()
if(isobj(pin)) //Can still be the initial path, then we skip
QDEL_NULL(pin)
- if(bayonet)
- QDEL_NULL(bayonet)
if(chambered) //Not all guns are chambered (EMP'ed energy guns etc)
QDEL_NULL(chambered)
if(isatom(suppressed)) //SUPPRESSED IS USED AS BOTH A TRUE/FALSE AND AS A REF, WHAT THE FUCKKKKKKKKKKKKKKKKK
@@ -112,6 +106,10 @@
/obj/item/gun/proc/add_seclight_point()
return
+/// Similarly to add_seclight_point(), handles [the bayonet attachment component][/datum/component/bayonet_attachable]
+/obj/item/gun/proc/add_bayonet_point()
+ return
+
/obj/item/gun/Exited(atom/movable/gone, direction)
. = ..()
if(gone == pin)
@@ -121,10 +119,6 @@
update_appearance()
if(gone == suppressed)
clear_suppressor()
- if(gone == bayonet)
- bayonet = null
- if(!QDELING(src))
- update_appearance()
///Clears var and updates icon. In the case of ballistic weapons, also updates the gun's weight.
/obj/item/gun/proc/clear_suppressor()
@@ -145,13 +139,6 @@
else
. += "It doesn't have a firing pin installed, and won't fire."
- if(bayonet)
- . += "It has \a [bayonet] [can_bayonet ? "" : "permanently "]affixed to it."
- if(can_bayonet) //if it has a bayonet and this is false, the bayonet is permanent.
- . += span_info("[bayonet] looks like it can be unscrewed from [src].")
- if(can_bayonet)
- . += "It has a bayonet lug on it."
-
//called after the gun has successfully fired its chambered ammo.
/obj/item/gun/proc/process_chamber(empty_chamber = TRUE, from_firing = TRUE, chamber_next_round = TRUE)
handle_chamber(empty_chamber, from_firing, chamber_next_round)
@@ -249,31 +236,6 @@
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-/obj/item/gun/pre_attack(atom/A, mob/living/user, params)
- . = ..()
- if(.)
- return .
- if(isnull(bayonet) || !user.combat_mode)
- return .
- return bayonet.melee_attack_chain(user, A, params)
-
-/obj/item/gun/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
- if(user.combat_mode)
- return NONE
-
- if(istype(tool, /obj/item/knife))
- var/obj/item/knife/new_stabber = tool
- if(!can_bayonet || !new_stabber.bayonet || !isnull(bayonet)) //ensure the gun has an attachment point available, and that the knife is compatible with it.
- return ITEM_INTERACT_BLOCKING
- if(!user.transferItemToLoc(new_stabber, src))
- return ITEM_INTERACT_BLOCKING
- to_chat(user, span_notice("You attach [new_stabber] to [src]'s bayonet lug."))
- bayonet = new_stabber
- update_appearance()
- return ITEM_INTERACT_SUCCESS
-
- return NONE
-
/obj/item/gun/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(user.combat_mode && isliving(interacting_with))
return ITEM_INTERACT_SKIP_TO_ATTACK // Gun bash / bayonet attack
@@ -509,17 +471,7 @@
return
if(!user.can_perform_action(src, FORBID_TELEKINESIS_REACH))
return
-
- if(bayonet && can_bayonet) //if it has a bayonet, and the bayonet can be removed
- I.play_tool_sound(src)
- to_chat(user, span_notice("You unfix [bayonet] from [src]."))
- bayonet.forceMove(drop_location())
-
- if(Adjacent(user) && !issilicon(user))
- user.put_in_hands(bayonet)
- return ITEM_INTERACT_SUCCESS
-
- else if(pin?.pin_removable && user.is_holding(src))
+ if(pin?.pin_removable && user.is_holding(src))
user.visible_message(span_warning("[user] attempts to remove [pin] from [src] with [I]."),
span_notice("You attempt to remove [pin] from [src]. (It will take [DisplayTimeText(FIRING_PIN_REMOVAL_DELAY)].)"), null, 3)
if(I.use_tool(src, user, FIRING_PIN_REMOVAL_DELAY, volume = 50))
@@ -564,19 +516,6 @@
QDEL_NULL(pin)
return TRUE
-/obj/item/gun/update_overlays()
- . = ..()
- if(bayonet)
- var/mutable_appearance/knife_overlay
- var/state = "bayonet" //Generic state.
- if(bayonet.icon_state in icon_states('icons/obj/weapons/guns/bayonets.dmi')) //Snowflake state?
- state = bayonet.icon_state
- var/icon/bayonet_icons = 'icons/obj/weapons/guns/bayonets.dmi'
- knife_overlay = mutable_appearance(bayonet_icons, state)
- knife_overlay.pixel_x = knife_x_offset
- knife_overlay.pixel_y = knife_y_offset
- . += knife_overlay
-
/obj/item/gun/animate_atom_living(mob/living/owner)
new /mob/living/simple_animal/hostile/mimic/copy/ranged(drop_location(), src, owner)
diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm
index 2a688960a1f..25fbdd1f626 100644
--- a/code/modules/projectiles/guns/ballistic.dm
+++ b/code/modules/projectiles/guns/ballistic.dm
@@ -645,8 +645,7 @@ GLOBAL_LIST_INIT(gun_saw_types, typecacheof(list(
if(sawn_off)
balloon_alert(user, "it's already shortened!")
return
- if(bayonet)
- balloon_alert(user, "[bayonet.name] must be removed!")
+ if (SEND_SIGNAL(src, COMSIG_GUN_BEING_SAWNOFF, user) & COMPONENT_CANCEL_SAWING_OFF)
return
user.changeNext_move(CLICK_CD_MELEE)
user.visible_message(span_notice("[user] begins to shorten [src]."), span_notice("You begin to shorten [src]..."))
@@ -656,27 +655,30 @@ GLOBAL_LIST_INIT(gun_saw_types, typecacheof(list(
user.visible_message(span_danger("[src] goes off!"), span_danger("[src] goes off in your face!"))
return
- if(do_after(user, 3 SECONDS, target = src))
- if(sawn_off)
- return
- user.visible_message(span_notice("[user] shortens [src]!"), span_notice("You shorten [src]."))
- sawn_off = TRUE
- if(handle_modifications)
- name = "sawn-off [src.name]"
- desc = sawn_desc
- update_weight_class(WEIGHT_CLASS_NORMAL)
- //The file might not have a "gun" icon, let's prepare for this
- lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
- inhand_x_dimension = 32
- inhand_y_dimension = 32
- inhand_icon_state = "gun"
- worn_icon_state = "gun"
- slot_flags &= ~ITEM_SLOT_BACK //you can't sling it on your back
- slot_flags |= ITEM_SLOT_BELT //but you can wear it on your belt (poorly concealed under a trenchcoat, ideally)
- recoil = SAWN_OFF_RECOIL
- update_appearance()
+ if(!do_after(user, 3 SECONDS, target = src))
+ return
+ if(sawn_off)
+ return
+ user.visible_message(span_notice("[user] shortens [src]!"), span_notice("You shorten [src]."))
+ sawn_off = TRUE
+ SEND_SIGNAL(src, COMSIG_GUN_SAWN_OFF)
+ if(!handle_modifications)
return TRUE
+ name = "sawn-off [src.name]"
+ desc = sawn_desc
+ update_weight_class(WEIGHT_CLASS_NORMAL)
+ //The file might not have a "gun" icon, let's prepare for this
+ lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
+ inhand_x_dimension = 32
+ inhand_y_dimension = 32
+ inhand_icon_state = "gun"
+ worn_icon_state = "gun"
+ slot_flags &= ~ITEM_SLOT_BACK //you can't sling it on your back
+ slot_flags |= ITEM_SLOT_BELT //but you can wear it on your belt (poorly concealed under a trenchcoat, ideally)
+ recoil = SAWN_OFF_RECOIL
+ update_appearance()
+ return TRUE
/obj/item/gun/ballistic/proc/guncleaning(mob/user, obj/item/A)
if(misfire_probability == initial(misfire_probability))
diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm
index 30ba65dc94a..b86e2a99389 100644
--- a/code/modules/projectiles/guns/ballistic/automatic.dm
+++ b/code/modules/projectiles/guns/ballistic/automatic.dm
@@ -41,13 +41,13 @@
fire_delay = 2
burst_size = 3
pin = /obj/item/firing_pin/implant/pindicate
- can_bayonet = TRUE
- knife_x_offset = 26
- knife_y_offset = 12
mag_display = TRUE
mag_display_ammo = TRUE
empty_indicator = TRUE
+/obj/item/gun/ballistic/automatic/c20r/add_bayonet_point()
+ AddComponent(/datum/component/bayonet_attachable, offset_x = 26, offset_y = 12)
+
/obj/item/gun/ballistic/automatic/c20r/update_overlays()
. = ..()
if(!chambered && empty_indicator) //this is duplicated due to a layering issue with the select fire icon.
@@ -75,9 +75,6 @@
can_suppress = FALSE
burst_size = 1
actions_types = list()
- can_bayonet = TRUE
- knife_x_offset = 25
- knife_y_offset = 12
mag_display = TRUE
mag_display_ammo = TRUE
empty_indicator = TRUE
@@ -86,6 +83,9 @@
. = ..()
AddComponent(/datum/component/automatic_fire, 0.3 SECONDS)
+/obj/item/gun/ballistic/automatic/wt550/add_bayonet_point()
+ AddComponent(/datum/component/bayonet_attachable, offset_x = 25, offset_y = 12)
+
/obj/item/gun/ballistic/automatic/plastikov
name = "\improper PP-95 SMG"
desc = "An ancient 9mm submachine gun pattern updated and simplified to lower costs, though perhaps simplified too much."
diff --git a/code/modules/projectiles/guns/ballistic/bows/bow_arrows.dm b/code/modules/projectiles/guns/ballistic/bows/bow_arrows.dm
index 713790049b5..4ed74bc9793 100644
--- a/code/modules/projectiles/guns/ballistic/bows/bow_arrows.dm
+++ b/code/modules/projectiles/guns/ballistic/bows/bow_arrows.dm
@@ -33,16 +33,17 @@
speed = 1
range = 25
shrapnel_type = null
- embedding = list(
- embed_chance = 90,
- fall_chance = 2,
- jostle_chance = 2,
- ignore_throwspeed_threshold = TRUE,
- pain_stam_pct = 0.5,
- pain_mult = 3,
- jostle_pain_mult = 3,
- rip_time = 1 SECONDS
- )
+ embed_type = /datum/embed_data/arrow
+
+/datum/embed_data/arrow
+ embed_chance = 90
+ fall_chance = 2
+ jostle_chance = 2
+ ignore_throwspeed_threshold = TRUE
+ pain_stam_pct = 0.5
+ pain_mult = 3
+ jostle_pain_mult = 3
+ rip_time = 1 SECONDS
/// holy arrows
/obj/item/ammo_casing/arrow/holy
@@ -59,15 +60,15 @@
desc = "Here it comes, cultist scum!"
icon_state = "holy_arrow_projectile"
damage = 20 //still a lot but this is roundstart gear so far less
- embedding = list(
- embed_chance = 50,
- fall_chance = 2,
- jostle_chance = 0,
- ignore_throwspeed_threshold = TRUE,
- pain_stam_pct = 0.5,
- pain_mult = 3,
- rip_time = 1 SECONDS
- )
+
+/datum/embed_data/holy_arrow
+ embed_chance = 50
+ fall_chance = 2
+ jostle_chance = 0
+ ignore_throwspeed_threshold = TRUE
+ pain_stam_pct = 0.5
+ pain_mult = 3
+ rip_time = 1 SECONDS
/obj/projectile/bullet/arrow/holy/Initialize(mapload)
. = ..()
@@ -87,7 +88,7 @@
desc = "THE UNMATCHED POWER OF THE SUN"
icon_state = "holy_arrow_projectile"
damage = 20
- embedding = null
+ embed_type = null
/obj/projectile/bullet/arrow/blazing/on_hit(atom/target, blocked, pierce_hit)
. = ..()
diff --git a/code/modules/projectiles/guns/ballistic/rifle.dm b/code/modules/projectiles/guns/ballistic/rifle.dm
index a8161ddb396..21ddb63211e 100644
--- a/code/modules/projectiles/guns/ballistic/rifle.dm
+++ b/code/modules/projectiles/guns/ballistic/rifle.dm
@@ -54,9 +54,6 @@
slot_flags = ITEM_SLOT_BACK
accepted_magazine_type = /obj/item/ammo_box/magazine/internal/boltaction
- can_bayonet = TRUE
- knife_x_offset = 42
- knife_y_offset = 12
can_be_sawn_off = TRUE
weapon_weight = WEAPON_HEAVY
var/jamming_chance = 20
@@ -67,11 +64,13 @@
SET_BASE_PIXEL(-8, 0)
+/obj/item/gun/ballistic/rifle/boltaction/add_bayonet_point()
+ AddComponent(/datum/component/bayonet_attachable, offset_x = 32, offset_y = 12)
+
/obj/item/gun/ballistic/rifle/boltaction/sawoff(mob/user)
. = ..()
if(.)
spread = 36
- can_bayonet = FALSE
SET_BASE_PIXEL(0, 0)
update_appearance()
@@ -271,14 +270,14 @@
projectile_damage_multiplier = 1.35
obj_flags = UNIQUE_RENAME
- can_bayonet = TRUE
- knife_x_offset = 35
- knife_y_offset = 10
can_be_sawn_off = FALSE
trigger_guard = TRIGGER_GUARD_ALLOW_ALL
SET_BASE_PIXEL(-8, 0)
+/obj/item/gun/ballistic/rifle/boltaction/pipegun/add_bayonet_point()
+ AddComponent(/datum/component/bayonet_attachable, offset_x = 35, offset_y = 10)
+
/obj/item/gun/ballistic/rifle/boltaction/pipegun/handle_chamber()
. = ..()
do_sparks(1, TRUE, src)
@@ -305,11 +304,13 @@
spread = 15 //kinda inaccurate
slot_flags = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_NORMAL
- can_bayonet = FALSE
weapon_weight = WEAPON_MEDIUM
SET_BASE_PIXEL(0, 0)
+/obj/item/gun/ballistic/rifle/boltaction/pipegun/pipepistol/add_bayonet_point()
+ return
+
/obj/item/gun/ballistic/rifle/boltaction/pipegun/prime
name = "regal pipegun"
desc = "To call this 'regal' is a cruel irony. For the only noteworthy quality of nobility is in how it is wielded to kill. \
diff --git a/code/modules/projectiles/guns/energy/beam_rifle.dm b/code/modules/projectiles/guns/energy/beam_rifle.dm
index 0bda1930c62..c9550e0fc2d 100644
--- a/code/modules/projectiles/guns/energy/beam_rifle.dm
+++ b/code/modules/projectiles/guns/energy/beam_rifle.dm
@@ -52,6 +52,7 @@
hitscan = TRUE
tracer_type = /obj/effect/projectile/tracer/tracer/beam_rifle
+/* SKYRAT EDIT REMOVE - God
/obj/projectile/beam/event_horizon/on_hit(atom/target, blocked, pierce_hit)
. = ..()
@@ -62,3 +63,4 @@
var/obj/reality_tear/temporary/tear = new(rift_loc)
tear.start_disaster()
message_admins("[ADMIN_LOOKUPFLW(target)] has been hit by an anti-existential beam at [ADMIN_VERBOSEJMP(rift_loc)], creating a singularity.")
+*/
diff --git a/code/modules/projectiles/guns/energy/crank_guns.dm b/code/modules/projectiles/guns/energy/crank_guns.dm
index 64ffa86f360..fa56075990e 100644
--- a/code/modules/projectiles/guns/energy/crank_guns.dm
+++ b/code/modules/projectiles/guns/energy/crank_guns.dm
@@ -7,9 +7,9 @@
ammo_type = list(/obj/item/ammo_casing/energy/laser/musket)
slot_flags = ITEM_SLOT_BACK
obj_flags = UNIQUE_RENAME
- can_bayonet = TRUE
- knife_x_offset = 22
- knife_y_offset = 11
+
+/obj/item/gun/energy/laser/musket/add_bayonet_point()
+ AddComponent(/datum/component/bayonet_attachable, offset_x = 22, offset_y = 11)
/obj/item/gun/energy/laser/musket/Initialize(mapload)
. = ..()
@@ -84,12 +84,12 @@
shaded_charge = TRUE
ammo_x_offset = 1
obj_flags = UNIQUE_RENAME
- can_bayonet = TRUE
- knife_x_offset = 19
- knife_y_offset = 13
w_class = WEIGHT_CLASS_NORMAL
dual_wield_spread = 5 //as intended by the coders
+/obj/item/gun/energy/laser/thermal/add_bayonet_point()
+ AddComponent(/datum/component/bayonet_attachable, offset_x = 19, offset_y = 13)
+
/obj/item/gun/energy/laser/thermal/Initialize(mapload)
. = ..()
AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_CONTENTS)
diff --git a/code/modules/projectiles/guns/energy/energy_gun.dm b/code/modules/projectiles/guns/energy/energy_gun.dm
index 3ce3338bf87..e826e1392bf 100644
--- a/code/modules/projectiles/guns/energy/energy_gun.dm
+++ b/code/modules/projectiles/guns/energy/energy_gun.dm
@@ -16,8 +16,8 @@
return
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/advancedegun, /datum/crafting_recipe/tempgun, /datum/crafting_recipe/beam_rifle)
- AddComponent(
- /datum/component/slapcrafting,\
+ AddElement(
+ /datum/element/slapcrafting,\
slapcraft_recipes = slapcraft_recipe_list,\
)
diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
index a4998304509..ea84c84ee83 100644
--- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
+++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
@@ -9,9 +9,6 @@
obj_flags = UNIQUE_RENAME
resistance_flags = FIRE_PROOF
weapon_weight = WEAPON_LIGHT
- can_bayonet = TRUE
- knife_x_offset = 20
- knife_y_offset = 12
gun_flags = NOT_A_REAL_GUN
///List of all mobs that projectiles fired from this gun will ignore.
var/list/ignored_mob_types
@@ -22,6 +19,9 @@
var/disablemodification = FALSE // Bubber edit, stops removal and addition of mods.
+/obj/item/gun/energy/recharge/kinetic_accelerator/add_bayonet_point()
+ AddComponent(/datum/component/bayonet_attachable, offset_x = 20, offset_y = 12)
+
/obj/item/gun/energy/recharge/kinetic_accelerator/Initialize(mapload)
. = ..()
// Only actual KAs can be converted
@@ -29,8 +29,8 @@
return
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/ebow)
- AddComponent(
- /datum/component/slapcrafting,\
+ AddElement(
+ /datum/element/slapcrafting,\
slapcraft_recipes = slapcraft_recipe_list,\
)
diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm
index 6852b07ee1b..ea36ad633db 100644
--- a/code/modules/projectiles/guns/energy/laser.dm
+++ b/code/modules/projectiles/guns/energy/laser.dm
@@ -16,8 +16,8 @@
return
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/xraylaser, /datum/crafting_recipe/hellgun, /datum/crafting_recipe/ioncarbine)
- AddComponent(
- /datum/component/slapcrafting,\
+ AddElement(
+ /datum/element/slapcrafting,\
slapcraft_recipes = slapcraft_recipe_list,\
)
diff --git a/code/modules/projectiles/guns/energy/pulse.dm b/code/modules/projectiles/guns/energy/pulse.dm
index 0635de8037a..839689144c2 100644
--- a/code/modules/projectiles/guns/energy/pulse.dm
+++ b/code/modules/projectiles/guns/energy/pulse.dm
@@ -59,6 +59,9 @@
/obj/item/gun/energy/pulse/carbine/loyalpin
pin = /obj/item/firing_pin/implant/mindshield
+/obj/item/gun/energy/pulse/carbine/taserless
+ ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse, /obj/item/ammo_casing/energy/laser)
+
/obj/item/gun/energy/pulse/destroyer
name = "pulse destroyer"
desc = "A heavy-duty energy rifle built for pure destruction."
@@ -79,6 +82,9 @@
inhand_icon_state = "gun"
cell_type = /obj/item/stock_parts/power_store/cell/pulse/pistol
+/obj/item/gun/energy/pulse/pistol/taserless
+ ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse, /obj/item/ammo_casing/energy/laser)
+
/obj/item/gun/energy/pulse/pistol/loyalpin
pin = /obj/item/firing_pin/implant/mindshield
diff --git a/code/modules/projectiles/guns/energy/recharge.dm b/code/modules/projectiles/guns/energy/recharge.dm
index 0aa14b48c32..504dc5c9e0f 100644
--- a/code/modules/projectiles/guns/energy/recharge.dm
+++ b/code/modules/projectiles/guns/energy/recharge.dm
@@ -111,9 +111,9 @@
recharge_time = 2 SECONDS
holds_charge = TRUE
unique_frequency = TRUE
- can_bayonet = TRUE
- knife_x_offset = 20
- knife_y_offset = 12
+
+/obj/item/gun/energy/recharge/ebow/add_bayonet_point()
+ AddComponent(/datum/component/bayonet_attachable, offset_x = 20, offset_y = 12)
/obj/item/gun/energy/recharge/ebow/halloween
name = "candy corn crossbow"
diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm
index 4d8a2b1f3fb..9b5aeb4ae0e 100644
--- a/code/modules/projectiles/guns/energy/special.dm
+++ b/code/modules/projectiles/guns/energy/special.dm
@@ -428,3 +428,21 @@
new_coin.preparePixelProjectile(target_turf, user)
new_coin.fire()
return ITEM_INTERACT_SUCCESS
+
+/obj/item/gun/energy/photon
+ name = "photon cannon"
+ desc = "A competitive design to the tesla cannon, that instead of charging latent electrons, releases energy into photons. Eye protection is recommended."
+ icon_state = "photon"
+ inhand_icon_state = "tesla"
+ fire_sound = 'sound/weapons/lasercannonfire.ogg'
+ ammo_type = list(/obj/item/ammo_casing/energy/photon)
+ shaded_charge = TRUE
+ weapon_weight = WEAPON_HEAVY
+ light_color = LIGHT_COLOR_DEFAULT
+ light_system = OVERLAY_LIGHT
+ light_power = 2
+ light_range = 1
+
+/obj/item/gun/energy/photon/Initialize(mapload)
+ . = ..()
+ set_light_on(TRUE) // The gun quite literally shoots mini-suns.
diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm
index e8f41ef0b48..b2fe293eae8 100644
--- a/code/modules/projectiles/guns/magic/wand.dm
+++ b/code/modules/projectiles/guns/magic/wand.dm
@@ -277,3 +277,78 @@
charges--
user.AddComponent(/datum/component/shrink, -1) // small forever
return ..()
+
+// Wand of debugging
+
+#ifdef TESTING
+
+/obj/item/gun/magic/wand/antag
+ name = "wand of antag"
+ desc = "This wand uses the powers of bullshit to turn anyone it hits into an antag"
+ school = SCHOOL_FORBIDDEN
+ ammo_type = /obj/item/ammo_casing/magic/antag
+ icon_state = "revivewand"
+ base_icon_state = "revivewand"
+ color = COLOR_ADMIN_PINK
+ max_charges = 99999
+
+/obj/item/gun/magic/wand/antag/zap_self(mob/living/user)
+ . = ..()
+ var/obj/item/ammo_casing/magic/antag/casing = new ammo_type()
+ var/obj/projectile/magic/magic_proj = casing.projectile_type
+ magic_proj = new magic_proj(src)
+ magic_proj.on_hit(user)
+ QDEL_NULL(casing)
+
+/obj/item/ammo_casing/magic/antag
+ projectile_type = /obj/projectile/magic/antag
+ harmful = FALSE
+
+/obj/projectile/magic/antag
+ name = "bolt of antag"
+ icon_state = "ion"
+ var/antag = /datum/antagonist/traitor
+
+/obj/projectile/magic/antag/on_hit(atom/target, blocked, pierce_hit)
+ . = ..()
+
+ if(isliving(target))
+ var/mob/living/victim = target
+ if(isnull(victim.mind))
+ victim.mind_initialize()
+ if(victim.mind.has_antag_datum(antag))
+ victim.mind.remove_antag_datum(antag)
+ to_chat(world, "removed")
+ else
+ victim.mind.add_antag_datum(antag)
+ to_chat(world, "added")
+
+/obj/item/gun/magic/wand/antag/heretic
+ name = "wand of antag heretic"
+ desc = "This wand uses the powers of bullshit to turn anyone it hits into an antag heretic"
+ color = COLOR_GREEN
+ ammo_type = /obj/item/ammo_casing/magic/antag/heretic
+
+/obj/item/ammo_casing/magic/antag/heretic
+ projectile_type = /obj/projectile/magic/antag/heretic
+
+/obj/projectile/magic/antag/heretic
+ name = "bolt of antag heretic"
+ icon_state = "ion"
+ antag = /datum/antagonist/heretic
+
+/obj/item/gun/magic/wand/antag/cult
+ name = "wand of antag cultist"
+ desc = "This wand uses the powers of bullshit to turn anyone it hits into an antag cultist"
+ color = COLOR_CULT_RED
+ ammo_type = /obj/item/ammo_casing/magic/antag/cult
+
+/obj/item/ammo_casing/magic/antag/cult
+ projectile_type = /obj/projectile/magic/antag/cult
+
+/obj/projectile/magic/antag/cult
+ name = "bolt of antag cult"
+ icon_state = "ion"
+ antag = /datum/antagonist/cult
+
+#endif
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 81055e9d8bc..2f9ed7b44e0 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -190,7 +190,9 @@
///If defined, on hit we create an item of this type then call hitby() on the hit target with this, mainly used for embedding items (bullets) in targets
var/shrapnel_type
///If we have a shrapnel_type defined, these embedding stats will be passed to the spawned shrapnel type, which will roll for embedding on the target
- var/list/embedding
+ var/embed_type
+ ///Saves embedding data
+ var/datum/embed_data/embed_data
///If TRUE, hit mobs, even if they are lying on the floor and are not our target within MAX_RANGE_HIT_PRONE_TARGETS tiles
var/hit_prone_targets = FALSE
///if TRUE, ignores the range of MAX_RANGE_HIT_PRONE_TARGETS tiles of hit_prone_targets
@@ -222,8 +224,8 @@
/obj/projectile/Initialize(mapload)
. = ..()
decayedRange = range
- if(embedding)
- updateEmbedding()
+ if(get_embed())
+ AddElement(/datum/element/embed)
AddElement(/datum/element/connect_loc, projectile_connections)
/obj/projectile/proc/Range()
@@ -231,8 +233,8 @@
if(wound_bonus != CANT_WOUND)
wound_bonus += wound_falloff_tile
bare_wound_bonus = max(0, bare_wound_bonus + wound_falloff_tile)
- if(embedding)
- embedding["embed_chance"] += embed_falloff_tile
+ if(get_embed())
+ set_embed(embed_data.generate_with_values(embed_data.embed_chance + embed_falloff_tile)) // Should be rewritten in projecitle refactor
if(damage_falloff_tile && damage >= 0)
damage += damage_falloff_tile
if(stamina_falloff_tile && stamina >= 0)
@@ -1148,26 +1150,6 @@
/obj/projectile/experience_pressure_difference()
return
-///Like [/obj/item/proc/updateEmbedding] but for projectiles instead, call this when you want to add embedding or update the stats on the embedding element
-/obj/projectile/proc/updateEmbedding()
- if(!shrapnel_type || !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),\
- projectile_payload = shrapnel_type)
- return TRUE
-
/**
* Is this projectile considered "hostile"?
*
@@ -1187,7 +1169,7 @@
///Checks if the projectile can embed into someone
/obj/projectile/proc/can_embed_into(atom/hit)
- return embedding && shrapnel_type && iscarbon(hit) && !HAS_TRAIT(hit, TRAIT_PIERCEIMMUNE)
+ return get_embed() && shrapnel_type && iscarbon(hit) && !HAS_TRAIT(hit, TRAIT_PIERCEIMMUNE)
/// Reflects the projectile off of something
/obj/projectile/proc/reflect(atom/hit_atom)
@@ -1230,3 +1212,16 @@
bullet.preparePixelProjectile(target, src)
bullet.fire()
return bullet
+
+/// Fetches embedding data
+/obj/projectile/proc/get_embed()
+ return embed_type ? (embed_data ||= get_embed_by_type(embed_type)) : null
+
+/obj/projectile/proc/set_embed(datum/embed_data/embed)
+ if(embed_data == embed)
+ return
+ // GLOB.embed_by_type stores shared "default" embedding values of datums
+ // Dynamically generated embeds use the base class and thus are not present in there, and should be qdeleted upon being discarded
+ if(!isnull(embed_data) && !GLOB.embed_by_type[embed_data.type])
+ qdel(embed_data)
+ embed_data = ispath(embed) ? get_embed_by_type(armor) : embed
diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm
index 5e938c49953..1d1313d9e55 100644
--- a/code/modules/projectiles/projectile/bullets.dm
+++ b/code/modules/projectiles/projectile/bullets.dm
@@ -8,7 +8,7 @@
sharpness = SHARP_POINTY
impact_effect_type = /obj/effect/temp_visual/impact_effect
shrapnel_type = /obj/item/shrapnel/bullet
- embedding = list(embed_chance=20, fall_chance=2, jostle_chance=0, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.5, pain_mult=3, rip_time=10)
+ embed_type = /datum/embed_data/bullet
wound_bonus = 0
wound_falloff_tile = -5
embed_falloff_tile = -3
@@ -16,3 +16,12 @@
/obj/projectile/bullet/smite
name = "divine retribution"
damage = 10
+
+/datum/embed_data/bullet
+ embed_chance=20
+ fall_chance=2
+ jostle_chance=0
+ ignore_throwspeed_threshold=TRUE
+ pain_stam_pct=0.5
+ pain_mult=3
+ rip_time=10
diff --git a/code/modules/projectiles/projectile/bullets/_incendiary.dm b/code/modules/projectiles/projectile/bullets/_incendiary.dm
index 85c2dce80c5..e151afa294d 100644
--- a/code/modules/projectiles/projectile/bullets/_incendiary.dm
+++ b/code/modules/projectiles/projectile/bullets/_incendiary.dm
@@ -30,7 +30,7 @@
pass_flags = PASSTABLE | PASSMOB
sharpness = NONE
shrapnel_type = null
- embedding = null
+ embed_type = null
impact_effect_type = null
suppressed = SUPPRESSED_VERY
damage_type = BURN
diff --git a/code/modules/projectiles/projectile/bullets/cannonball.dm b/code/modules/projectiles/projectile/bullets/cannonball.dm
index 1d6108ed33f..358a23eb63c 100644
--- a/code/modules/projectiles/projectile/bullets/cannonball.dm
+++ b/code/modules/projectiles/projectile/bullets/cannonball.dm
@@ -8,7 +8,7 @@
dismemberment = 0
paralyze = 5 SECONDS
stutter = 20 SECONDS
- embedding = null
+ embed_type = null
hitsound = 'sound/effects/meteorimpact.ogg'
hitsound_wall = 'sound/weapons/sonic_jackhammer.ogg'
/// If our cannonball hits something, it reduces the damage by this value.
diff --git a/code/modules/projectiles/projectile/bullets/dart_syringe.dm b/code/modules/projectiles/projectile/bullets/dart_syringe.dm
index 405552a8909..1bd88d75fde 100644
--- a/code/modules/projectiles/projectile/bullets/dart_syringe.dm
+++ b/code/modules/projectiles/projectile/bullets/dart_syringe.dm
@@ -2,7 +2,7 @@
name = "dart"
icon_state = "cbbolt"
damage = 6
- embedding = null
+ embed_type = null
shrapnel_type = null
var/inject_flags = null
diff --git a/code/modules/projectiles/projectile/bullets/dnainjector.dm b/code/modules/projectiles/projectile/bullets/dnainjector.dm
index fdb051e7f80..b4029b07f2d 100644
--- a/code/modules/projectiles/projectile/bullets/dnainjector.dm
+++ b/code/modules/projectiles/projectile/bullets/dnainjector.dm
@@ -4,7 +4,7 @@
var/obj/item/dnainjector/injector
damage = 5
hitsound_wall = SFX_SHATTER
- embedding = null
+ embed_type = null
shrapnel_type = null
/obj/projectile/bullet/dnainjector/on_hit(atom/target, blocked = 0, pierce_hit)
diff --git a/code/modules/projectiles/projectile/bullets/foam_dart.dm b/code/modules/projectiles/projectile/bullets/foam_dart.dm
index 3f086166e6a..7eaa1ce6c8e 100644
--- a/code/modules/projectiles/projectile/bullets/foam_dart.dm
+++ b/code/modules/projectiles/projectile/bullets/foam_dart.dm
@@ -8,7 +8,7 @@
base_icon_state = "foamdart"
range = 10
shrapnel_type = null
- embedding = null
+ embed_type = null
var/modified = FALSE
var/obj/item/pen/pen = null
diff --git a/code/modules/projectiles/projectile/bullets/grenade.dm b/code/modules/projectiles/projectile/bullets/grenade.dm
index a99a7b57ff3..0814a844be4 100644
--- a/code/modules/projectiles/projectile/bullets/grenade.dm
+++ b/code/modules/projectiles/projectile/bullets/grenade.dm
@@ -5,7 +5,7 @@
desc = "USE A WEEL GUN"
icon_state= "bolter"
damage = 60
- embedding = null
+ embed_type = null
shrapnel_type = null
/obj/projectile/bullet/a40mm/on_hit(atom/target, blocked = 0, pierce_hit)
diff --git a/code/modules/projectiles/projectile/bullets/junk.dm b/code/modules/projectiles/projectile/bullets/junk.dm
index 344a7329113..285c3ea2815 100644
--- a/code/modules/projectiles/projectile/bullets/junk.dm
+++ b/code/modules/projectiles/projectile/bullets/junk.dm
@@ -4,11 +4,21 @@
name = "junk bullet"
icon_state = "trashball"
damage = 30
- embedding = list(embed_chance=15, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10)
+ embed_type = /datum/embed_data/bullet_junk
var/bane_mob_biotypes = MOB_ROBOTIC
var/bane_multiplier = 1.5
var/bane_added_damage = 0
+/datum/embed_data/bullet_junk
+ embed_chance=15
+ fall_chance=3
+ jostle_chance=4
+ ignore_throwspeed_threshold=TRUE
+ pain_stam_pct=0.4
+ pain_mult=5
+ jostle_pain_mult=6
+ rip_time=10
+
/obj/projectile/bullet/junk/Initialize(mapload)
. = ..()
AddElement(/datum/element/bane, mob_biotypes = bane_mob_biotypes, target_type = /mob/living, damage_multiplier = bane_multiplier, added_damage = bane_added_damage, requires_combat_mode = FALSE)
@@ -28,7 +38,7 @@
name = "bundle of live electrical parts"
icon_state = "tesla_projectile"
damage = 15
- embedding = null
+ embed_type = null
shrapnel_type = null
bane_multiplier = 3
@@ -49,10 +59,20 @@
name = "junk ripper bullet"
icon_state = "redtrac"
damage = 10
- embedding = list(embed_chance=100, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10)
+ embed_type = /datum/embed_data/bullet_junk_ripper
wound_bonus = 10
bare_wound_bonus = 30
+/datum/embed_data/bullet_junk_ripper
+ embed_chance=100
+ fall_chance=3
+ jostle_chance=4
+ ignore_throwspeed_threshold=TRUE
+ pain_stam_pct=0.4
+ pain_mult=5
+ jostle_pain_mult=6
+ rip_time=10
+
/obj/projectile/bullet/junk/reaper
name = "junk reaper bullet"
tracer_type = /obj/effect/projectile/tracer/sniper
diff --git a/code/modules/projectiles/projectile/bullets/pistol.dm b/code/modules/projectiles/projectile/bullets/pistol.dm
index 8fccc510ff8..6bd355219f9 100644
--- a/code/modules/projectiles/projectile/bullets/pistol.dm
+++ b/code/modules/projectiles/projectile/bullets/pistol.dm
@@ -3,13 +3,23 @@
/obj/projectile/bullet/c9mm
name = "9mm bullet"
damage = 30
- embedding = list(embed_chance=15, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10)
+ embed_type = /datum/embed_data/bullet_c9mm
+
+/datum/embed_data/bullet_c9mm
+ embed_chance=15
+ fall_chance=3
+ jostle_chance=4
+ ignore_throwspeed_threshold=TRUE
+ pain_stam_pct=0.4
+ pain_mult=5
+ jostle_pain_mult=6
+ rip_time=10
/obj/projectile/bullet/c9mm/ap
name = "9mm armor-piercing bullet"
damage = 27
armour_penetration = 40
- embedding = null
+ embed_type = null
shrapnel_type = null
/obj/projectile/bullet/c9mm/hp
diff --git a/code/modules/projectiles/projectile/bullets/revolver.dm b/code/modules/projectiles/projectile/bullets/revolver.dm
index 417f61534bc..e9d6389c89d 100644
--- a/code/modules/projectiles/projectile/bullets/revolver.dm
+++ b/code/modules/projectiles/projectile/bullets/revolver.dm
@@ -21,9 +21,19 @@
ricochet_auto_aim_range = 3
wound_bonus = -20
bare_wound_bonus = 10
- embedding = list(embed_chance=25, fall_chance=2, jostle_chance=2, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=3, jostle_pain_mult=5, rip_time=1 SECONDS)
+ embed_type = /datum/embed_data/bullet_c38
embed_falloff_tile = -4
+/datum/embed_data/bullet_c38
+ embed_chance=25
+ fall_chance=2
+ jostle_chance=2
+ ignore_throwspeed_threshold=TRUE
+ pain_stam_pct=0.4
+ pain_mult=3
+ jostle_pain_mult=5
+ rip_time=1 SECONDS
+
/obj/projectile/bullet/c38/match
name = ".38 Match bullet"
ricochets_max = 4
@@ -45,7 +55,7 @@
ricochet_decay_damage = 0.8
shrapnel_type = null
sharpness = NONE
- embedding = null
+ embed_type = null
// premium .38 ammo from cargo, weak against armor, lower base damage, but excellent at embedding and causing slice wounds at close range
/obj/projectile/bullet/c38/dumdum
@@ -56,10 +66,20 @@
sharpness = SHARP_EDGED
wound_bonus = 20
bare_wound_bonus = 20
- embedding = list(embed_chance=75, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=1 SECONDS)
+ embed_type = /datum/embed_data/bullet_c38_dumdum
wound_falloff_tile = -5
embed_falloff_tile = -15
+/datum/embed_data/bullet_c38_dumdum
+ embed_chance=75
+ fall_chance=3
+ jostle_chance=4
+ ignore_throwspeed_threshold=TRUE
+ pain_stam_pct=0.4
+ pain_mult=5
+ jostle_pain_mult=6
+ rip_time=1 SECONDS
+
/obj/projectile/bullet/c38/trac
name = ".38 TRAC bullet"
damage = 10
diff --git a/code/modules/projectiles/projectile/bullets/rifle.dm b/code/modules/projectiles/projectile/bullets/rifle.dm
index 3cbb894b8fb..7e29b7e5c24 100644
--- a/code/modules/projectiles/projectile/bullets/rifle.dm
+++ b/code/modules/projectiles/projectile/bullets/rifle.dm
@@ -44,10 +44,20 @@
armour_penetration = 50
wound_bonus = -20
bare_wound_bonus = 80
- embedding = list(embed_chance=100, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10)
+ embed_type = /datum/embed_data/harpoon
wound_falloff_tile = -5
shrapnel_type = null
+/datum/embed_data/harpoon
+ embed_chance=100
+ fall_chance=3
+ jostle_chance=4
+ ignore_throwspeed_threshold=TRUE
+ pain_stam_pct=0.4
+ pain_mult=5
+ jostle_pain_mult=6
+ rip_time=10
+
// Rebar (Rebar Crossbow)
/obj/projectile/bullet/rebar
name = "rebar"
@@ -58,11 +68,21 @@
armour_penetration = 10
wound_bonus = -20
bare_wound_bonus = 20
- embedding = list("embed_chance" = 60, "fall_chance" = 2, "jostle_chance" = 2, "ignore_throwspeed_threshold" = TRUE, "pain_stam_pct" = 0.4, "pain_mult" = 4, "jostle_pain_mult" = 2, "rip_time" = 10)
+ embed_type = /datum/embed_data/rebar
embed_falloff_tile = -5
wound_falloff_tile = -2
shrapnel_type = /obj/item/ammo_casing/rebar
+/datum/embed_data/rebar
+ embed_chance = 60
+ fall_chance = 2
+ jostle_chance = 2
+ ignore_throwspeed_threshold = TRUE
+ pain_stam_pct = 0.4
+ pain_mult = 4
+ jostle_pain_mult = 2
+ rip_time = 10
+
/obj/projectile/bullet/rebar/proc/handle_drop(datum/source, obj/item/ammo_casing/rebar/newcasing)
/obj/projectile/bullet/rebar/syndie
@@ -74,10 +94,20 @@
armour_penetration = 20 //A bit better versus armor. Gets past anti laser armor or a vest, but doesnt wound proc on sec armor.
wound_bonus = 10
bare_wound_bonus = 20
- embedding = list("embed_chance" = 80, "fall_chance" = 1, "jostle_chance" = 3, "ignore_throwspeed_threshold" = TRUE, "pain_stam_pct" = 0.4, "pain_mult" = 3, "jostle_pain_mult" = 2, "rip_time" = 14)
embed_falloff_tile = -3
+ embed_type = /datum/embed_data/rebar_syndie
shrapnel_type = /obj/item/ammo_casing/rebar/syndie
+/datum/embed_data/rebar_syndie
+ embed_chance = 80
+ fall_chance = 1
+ jostle_chance = 3
+ ignore_throwspeed_threshold
+ pain_stam_pct = 0.4
+ pain_mult = 3
+ jostle_pain_mult = 2
+ rip_time = 14
+
/obj/projectile/bullet/rebar/zaukerite
name = "zaukerite shard"
icon_state = "rebar_zaukerite"
@@ -89,10 +119,20 @@
armour_penetration = 20 // not nearly as good, as its not as sharp.
wound_bonus = 10
bare_wound_bonus = 40
- embedding = list("embed_chance" =100, "fall_chance" = 0, "jostle_chance" = 5, "ignore_throwspeed_threshold" = TRUE, "pain_stam_pct" = 0.8, "pain_mult" = 6, "jostle_pain_mult" = 2, "rip_time" = 30)
+ embed_type = /datum/embed_data/rebar_zaukerite
embed_falloff_tile = 0 // very spiky.
shrapnel_type = /obj/item/ammo_casing/rebar/zaukerite
+/datum/embed_data/rebar_zaukerite
+ embed_chance = 100
+ fall_chance = 0
+ jostle_chance = 5
+ ignore_throwspeed_threshold = TRUE
+ pain_stam_pct = 0.8
+ pain_mult = 6
+ jostle_pain_mult = 2
+ rip_time = 30
+
/obj/projectile/bullet/rebar/hydrogen
name = "metallic hydrogen bolt"
icon_state = "rebar_hydrogen"
@@ -104,10 +144,20 @@
projectile_piercing = PASSMOB //felt this might have been a nice compromise for the lower damage for the difficulty of getting it
wound_bonus = -15
bare_wound_bonus = 10
- embedding = list("embed_chance" = 50, "fall_chance" = 2, "jostle_chance" = 3, "ignore_throwspeed_threshold" = TRUE, "pain_stam_pct" = 0.6, "pain_mult" = 4, "jostle_pain_mult" = 2, "rip_time" =18)
+ embed_type = /datum/embed_data/rebar_hydrogen
embed_falloff_tile = -3
shrapnel_type = /obj/item/ammo_casing/rebar/hydrogen
+/datum/embed_data/rebar_hydrogen
+ embed_chance = 50
+ fall_chance = 2
+ jostle_chance = 3
+ ignore_throwspeed_threshold = TRUE
+ pain_stam_pct = 0.6
+ pain_mult = 4
+ jostle_pain_mult = 2
+ rip_time =18
+
/obj/projectile/bullet/rebar/healium
name = "healium bolt"
icon_state = "rebar_healium"
@@ -118,7 +168,7 @@
armour_penetration = 100
wound_bonus = -100
bare_wound_bonus = -100
- embedding = list(embed_chance = 0)
+ embed_type = null
embed_falloff_tile = -3
shrapnel_type = /obj/item/ammo_casing/rebar/healium
@@ -135,7 +185,6 @@
return BULLET_ACT_HIT
-
/obj/projectile/bullet/rebar/supermatter
name = "supermatter bolt"
icon_state = "rebar_supermatter"
@@ -143,6 +192,7 @@
speed = 0.4
dismemberment = 0
damage_type = TOX
+ embed_type = null
armour_penetration = 100
shrapnel_type = /obj/item/ammo_casing/rebar/supermatter
@@ -160,7 +210,6 @@
return BULLET_ACT_HIT
-
/obj/projectile/bullet/rebar/supermatter/proc/dust_feedback(atom/target)
playsound(get_turf(src), 'sound/effects/supermatter.ogg', 10, TRUE)
visible_message(span_danger("[target] is hit by [src], turning [target.p_them()] to dust in a brilliant flash of light!"))
@@ -170,7 +219,7 @@
damage = 1 // It's a damn toy.
range = 10
shrapnel_type = null
- embedding = null
+ embed_type = null
name = "paper ball"
desc = "doink!"
damage_type = BRUTE
diff --git a/code/modules/projectiles/projectile/bullets/shotgun.dm b/code/modules/projectiles/projectile/bullets/shotgun.dm
index b3ddaf9bc90..a013aaba691 100644
--- a/code/modules/projectiles/projectile/bullets/shotgun.dm
+++ b/code/modules/projectiles/projectile/bullets/shotgun.dm
@@ -22,7 +22,7 @@
stamina = 55
wound_bonus = 20
sharpness = NONE
- embedding = null
+ embed_type = null
/obj/projectile/bullet/shotgun_beanbag/a40mm
name = "rubber slug"
@@ -55,7 +55,7 @@
range = 7
icon_state = "spark"
color = COLOR_YELLOW
- embedding = null
+ embed_type = null
/obj/projectile/bullet/shotgun_frag12
name ="frag12 slug"
@@ -84,7 +84,7 @@
damage = 3
stamina = 11
sharpness = NONE
- embedding = null
+ embed_type = null
speed = 1.2
stamina_falloff_tile = -0.25
ricochets_max = 4
@@ -106,7 +106,7 @@
name = "incapacitating pellet"
damage = 1
stamina = 6
- embedding = null
+ embed_type = null
// Mech Scattershot
diff --git a/code/modules/projectiles/projectile/bullets/smg.dm b/code/modules/projectiles/projectile/bullets/smg.dm
index 8befcb690ec..ce78abf0662 100644
--- a/code/modules/projectiles/projectile/bullets/smg.dm
+++ b/code/modules/projectiles/projectile/bullets/smg.dm
@@ -33,7 +33,7 @@
name = "4.6x30mm armor-piercing bullet"
damage = 15
armour_penetration = 40
- embedding = null
+ embed_type = null
/obj/projectile/bullet/incendiary/c46x30mm
name = "4.6x30mm incendiary bullet"
diff --git a/code/modules/projectiles/projectile/bullets/special.dm b/code/modules/projectiles/projectile/bullets/special.dm
index f595c3e1165..2af0fe7b9cb 100644
--- a/code/modules/projectiles/projectile/bullets/special.dm
+++ b/code/modules/projectiles/projectile/bullets/special.dm
@@ -9,7 +9,7 @@
icon = 'icons/obj/service/hydroponics/harvest.dmi'
icon_state = "banana"
range = 200
- embedding = null
+ embed_type = null
shrapnel_type = null
/obj/projectile/bullet/honker/Initialize(mapload)
diff --git a/code/modules/projectiles/projectile/energy/photon.dm b/code/modules/projectiles/projectile/energy/photon.dm
new file mode 100644
index 00000000000..7caac3e062e
--- /dev/null
+++ b/code/modules/projectiles/projectile/energy/photon.dm
@@ -0,0 +1,58 @@
+#define MULTIPLY_PIXELSPEED 0.8
+
+/obj/projectile/energy/photon
+ name = "photon bolt"
+ icon_state = "solarflare"
+ damage_type = STAMINA
+ armor_flag = ENERGY
+ impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser
+ damage = 5 //It's literally a weaker tesla bolt, which is already weak. Don't worry, we'll fix that.
+ range = 20
+ speed = 1
+ pixel_speed_multiplier = 1
+ projectile_piercing = PASSMOB
+ light_color = LIGHT_COLOR_DEFAULT
+ light_system = OVERLAY_LIGHT
+ light_power = 5
+ light_range = 6
+
+
+/obj/projectile/energy/photon/Initialize(mapload)
+ . = ..()
+ RegisterSignals(src, list(COMSIG_MOVABLE_CROSS, COMSIG_MOVABLE_CROSS_OVER), PROC_REF(blast_touched))
+ RegisterSignal(src, COMSIG_ATOM_ENTERED, PROC_REF(scorch_earth))
+ set_light_on(TRUE)
+
+/**
+ * Handle side effects for the phonon bolt.
+ * behaves like a higher power direct flash if hit, and sparks silicons like they're getting microwaved.
+ */
+/obj/projectile/energy/photon/proc/blast_touched(datum/source, atom/flashed)
+ SIGNAL_HANDLER
+ if(isliving(flashed))
+ var/mob/living/flashed_creature = flashed
+ flashed_creature.flash_act(intensity = 3, affect_silicon = TRUE, length = 6)
+ flashed_creature.adjust_confusion(1.5 SECONDS)
+ if(issilicon(flashed))
+ do_sparks(rand(1, 4), FALSE, src)
+
+/**
+ * When traveling to a new turf, throws a probability to generate a hotspot across it's path.
+ */
+/obj/projectile/energy/photon/proc/scorch_earth(turf/open/floor/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
+ SIGNAL_HANDLER
+ if(prob(40))
+ new /obj/effect/hotspot(arrived)
+
+/obj/projectile/energy/photon/Range()
+ . = ..()
+ pixel_speed_multiplier *= MULTIPLY_PIXELSPEED
+
+/obj/projectile/energy/photon/on_range()
+ do_sparks(rand(4, 9), FALSE, src)
+ playsound(loc, 'sound/weapons/solarflare.ogg', 100, FALSE, 8, 0.9)
+ for(var/mob/living/flashed_mob in viewers(5, loc))
+ flashed_mob.flash_act()
+ return ..()
+
+#undef MULTIPLY_PIXELSPEED
diff --git a/code/modules/projectiles/projectile/special/rocket.dm b/code/modules/projectiles/projectile/special/rocket.dm
index db452385c98..fe04fc7d986 100644
--- a/code/modules/projectiles/projectile/special/rocket.dm
+++ b/code/modules/projectiles/projectile/special/rocket.dm
@@ -2,7 +2,7 @@
name ="explosive bolt"
icon_state= "bolter"
damage = 50
- embedding = null
+ embed_type = null
shrapnel_type = null
/obj/projectile/bullet/gyro/on_hit(atom/target, blocked = 0, pierce_hit)
@@ -17,7 +17,7 @@
icon_state= "missile"
damage = 50
sharpness = NONE
- embedding = null
+ embed_type = null
shrapnel_type = null
ricochets_max = 0
/// Whether we do extra damage when hitting a mech or silicon
diff --git a/code/modules/reagents/chemistry/holder/holder.dm b/code/modules/reagents/chemistry/holder/holder.dm
index 3fe2e482f3e..0c300c3896b 100644
--- a/code/modules/reagents/chemistry/holder/holder.dm
+++ b/code/modules/reagents/chemistry/holder/holder.dm
@@ -235,6 +235,7 @@
var/total_removed_amount = 0
var/remove_amount = 0
var/list/cached_reagents = reagent_list
+ var/list/removed_reagents = list()
for(var/datum/reagent/cached_reagent as anything in cached_reagents)
//check for specific type or subtypes
if(!include_subtypes)
@@ -243,19 +244,26 @@
else if(!istype(cached_reagent, reagent_type))
continue
+ //reduce the volume
remove_amount = min(cached_reagent.volume, amount)
cached_reagent.volume -= remove_amount
- update_total()
- if(!safety)//So it does not handle reactions when it need not to
- handle_reactions()
- SEND_SIGNAL(src, COMSIG_REAGENTS_REM_REAGENT, QDELING(cached_reagent) ? reagent_type : cached_reagent, amount)
-
+ //record the changes
+ removed_reagents += cached_reagent
total_removed_amount += remove_amount
//if we reached here means we have found our specific reagent type so break
if(!include_subtypes)
- return total_removed_amount
+ break
+
+ //inform others about our reagents being removed
+ for(var/datum/reagent/removed_reagent as anything in cached_reagents)
+ SEND_SIGNAL(src, COMSIG_REAGENTS_REM_REAGENT, removed_reagent, amount)
+
+ //update the holder & handle reactions
+ update_total()
+ if(!safety)
+ handle_reactions()
return round(total_removed_amount, CHEMICAL_VOLUME_ROUNDING)
@@ -504,8 +512,6 @@
log_target.add_hiddenprint(transferred_by) //log prints so admins can figure out who touched it last.
log_combat(transferred_by, log_target, "transferred reagents to", my_atom, "which had [get_external_reagent_log_string(transfer_log)]")
- update_total()
- target_holder.update_total()
if(!no_react)
target_holder.handle_reactions()
src.handle_reactions()
diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm
index d87ff89bff7..87fb4cbf279 100644
--- a/code/modules/reagents/chemistry/reagents/food_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm
@@ -144,6 +144,7 @@
brute_heal = 0.8 //Rewards the player for eating a balanced diet.
nutriment_factor = 9 //45% as calorie dense as oil.
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
+ default_container = /obj/item/reagent_containers/condiment/protein
/datum/reagent/consumable/nutriment/fat
name = "Fat"
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 0f129a15860..4faf255361a 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -909,7 +909,7 @@
return
to_chat(affected_mob, span_warning("You grit your teeth in pain as your body rapidly mutates!"))
affected_mob.visible_message("[affected_mob] suddenly transforms!")
- randomize_human(affected_mob)
+ randomize_human_normie(affected_mob)
/datum/reagent/aslimetoxin
name = "Advanced Mutation Toxin"
diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm
index dfffdad6ab8..19b6b5d3bd8 100644
--- a/code/modules/reagents/reagent_containers.dm
+++ b/code/modules/reagents/reagent_containers.dm
@@ -276,9 +276,6 @@
reagents.expose_temperature(1000)
return ..() | COMPONENT_MICROWAVE_SUCCESS
-/obj/item/reagent_containers/fire_act(temperature, volume)
- reagents.expose_temperature(temperature)
-
/// Updates the icon of the container when the reagents change. Eats signal args
/obj/item/reagent_containers/proc/on_reagent_change(datum/reagents/holder, ...)
SIGNAL_HANDLER
diff --git a/code/modules/reagents/reagent_containers/condiment.dm b/code/modules/reagents/reagent_containers/condiment.dm
index af8cc250712..4172b48b46e 100644
--- a/code/modules/reagents/reagent_containers/condiment.dm
+++ b/code/modules/reagents/reagent_containers/condiment.dm
@@ -370,6 +370,13 @@
list_reagents = list(/datum/reagent/consumable/grounding_solution = 50)
fill_icon_thresholds = null
+/obj/item/reagent_containers/condiment/protein
+ name = "protein powder"
+ desc = "Fuel for your inner Hulk - because you can't spell 'swole' without 'whey'!"
+ icon_state = "protein"
+ list_reagents = list(/datum/reagent/consumable/nutriment/protein = 40)
+ fill_icon_thresholds = null
+
//technically condiment packs but they are non transparent
/obj/item/reagent_containers/condiment/creamer
diff --git a/code/modules/reagents/reagent_containers/cups/glassbottle.dm b/code/modules/reagents/reagent_containers/cups/glassbottle.dm
index a293aac7aa1..b246cc01e07 100644
--- a/code/modules/reagents/reagent_containers/cups/glassbottle.dm
+++ b/code/modules/reagents/reagent_containers/cups/glassbottle.dm
@@ -42,9 +42,8 @@
/obj/item/reagent_containers/cup/glass/bottle/Initialize(mapload, vol)
. = ..()
- AddComponent(/datum/component/slapcrafting,\
- slapcraft_recipes = list(/datum/crafting_recipe/molotov)\
- )
+ var/static/list/recipes = list(/datum/crafting_recipe/molotov)
+ AddElement(/datum/element/slapcrafting, recipes)
/obj/item/reagent_containers/cup/glass/bottle/small
name = "small glass bottle"
diff --git a/code/modules/reagents/reagent_containers/cups/soda.dm b/code/modules/reagents/reagent_containers/cups/soda.dm
index 5bf0eb782c5..2e85b2273f4 100644
--- a/code/modules/reagents/reagent_containers/cups/soda.dm
+++ b/code/modules/reagents/reagent_containers/cups/soda.dm
@@ -23,9 +23,7 @@
/obj/item/reagent_containers/cup/soda_cans/Initialize(mapload, vol)
. = ..()
- AddComponent(/datum/component/slapcrafting,\
- slapcraft_recipes = list(/datum/crafting_recipe/improv_explosive)\
- )
+ AddElement(/datum/element/slapcrafting, string_list(list(/datum/crafting_recipe/improv_explosive)))
/obj/item/reagent_containers/cup/soda_cans/random/Initialize(mapload)
..()
diff --git a/code/modules/recycling/disposal/construction.dm b/code/modules/recycling/disposal/construction.dm
index 903a59a9305..13f43b1f4a4 100644
--- a/code/modules/recycling/disposal/construction.dm
+++ b/code/modules/recycling/disposal/construction.dm
@@ -109,7 +109,7 @@
var/turf/T = get_turf(src)
if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE && isfloorturf(T))
- var/obj/item/crowbar/held_crowbar = user.is_holding_item_of_type(/obj/item/crowbar)
+ var/obj/item/crowbar/held_crowbar = user.is_holding_tool_quality(TOOL_CROWBAR)
if(!held_crowbar || !T.crowbar_act(user, held_crowbar))
to_chat(user, span_warning("You can only attach the [pipename] if the floor plating is removed!"))
return TRUE
diff --git a/code/modules/religion/burdened/psyker.dm b/code/modules/religion/burdened/psyker.dm
index 1531d07f5ea..20f478c1af1 100644
--- a/code/modules/religion/burdened/psyker.dm
+++ b/code/modules/religion/burdened/psyker.dm
@@ -252,7 +252,7 @@
ricochet_auto_aim_angle = 10
ricochet_auto_aim_range = 3
wound_bonus = -10
- embedding = null
+ embed_type = null
/obj/projectile/bullet/c38/holy/on_hit(atom/target, blocked = 0, pierce_hit)
. = ..()
diff --git a/code/modules/research/designs/computer_part_designs.dm b/code/modules/research/designs/computer_part_designs.dm
index 8dab08c046d..7a8242642a1 100644
--- a/code/modules/research/designs/computer_part_designs.dm
+++ b/code/modules/research/designs/computer_part_designs.dm
@@ -5,10 +5,11 @@
/datum/design/portabledrive/basic
name = "Data Disk"
id = "portadrive_basic"
- build_type = IMPRINTER | AWAY_IMPRINTER
- materials = list(/datum/material/glass = SMALL_MATERIAL_AMOUNT*8)
+ build_type = AUTOLATHE | PROTOLATHE | AWAY_LATHE
+ materials = list(/datum/material/glass = SHEET_MATERIAL_AMOUNT)
build_path = /obj/item/computer_disk
category = list(
+ RND_CATEGORY_INITIAL,
RND_CATEGORY_MODULAR_COMPUTERS + RND_SUBCATEGORY_MODULAR_COMPUTERS_PARTS
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE | DEPARTMENT_BITFLAG_ENGINEERING
@@ -16,10 +17,11 @@
/datum/design/portabledrive/advanced
name = "Advanced Data Disk"
id = "portadrive_advanced"
- build_type = IMPRINTER | AWAY_IMPRINTER
- materials = list(/datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT*1.5)
+ build_type = AUTOLATHE | PROTOLATHE | AWAY_LATHE
+ materials = list(/datum/material/glass = SHEET_MATERIAL_AMOUNT * 2)
build_path = /obj/item/computer_disk/advanced
category = list(
+ RND_CATEGORY_INITIAL,
RND_CATEGORY_MODULAR_COMPUTERS + RND_SUBCATEGORY_MODULAR_COMPUTERS_PARTS
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE | DEPARTMENT_BITFLAG_ENGINEERING
@@ -27,10 +29,11 @@
/datum/design/portabledrive/super
name = "Super Data Disk"
id = "portadrive_super"
- build_type = IMPRINTER | AWAY_IMPRINTER
- materials = list(/datum/material/glass = SHEET_MATERIAL_AMOUNT*1.5)
+ build_type = AUTOLATHE | PROTOLATHE | AWAY_LATHE
+ materials = list(/datum/material/glass = SHEET_MATERIAL_AMOUNT * 4)
build_path = /obj/item/computer_disk/super
category = list(
+ RND_CATEGORY_INITIAL,
RND_CATEGORY_MODULAR_COMPUTERS + RND_SUBCATEGORY_MODULAR_COMPUTERS_PARTS
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE | DEPARTMENT_BITFLAG_ENGINEERING
diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm
index 8f857f77e82..b8b11e7b44f 100644
--- a/code/modules/research/designs/machine_designs.dm
+++ b/code/modules/research/designs/machine_designs.dm
@@ -582,6 +582,16 @@
)
departmental_flags = DEPARTMENT_BITFLAG_SERVICE
+/datum/design/board/vatgrower
+ name = "Growing Vat Board"
+ desc = "The circuit board for a growing vat."
+ id = "vatgrower"
+ build_path = /obj/item/circuitboard/machine/vatgrower
+ category = list(
+ RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_RESEARCH
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
+
/datum/design/board/monkey_recycler
name = "Monkey Recycler Board"
desc = "The circuit board for a monkey recycler."
@@ -1178,6 +1188,36 @@
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE | DEPARTMENT_BITFLAG_ENGINEERING
+/datum/design/board/scrubber
+ name = "Portable Air Scrubber Board"
+ desc = "The circuit board for a portable air scrubber."
+ id = "scrubber"
+ build_path = /obj/item/circuitboard/machine/scrubber
+ category = list(
+ RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ATMOS
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_SCIENCE | DEPARTMENT_BITFLAG_ENGINEERING
+
+/datum/design/board/pump
+ name = "Portable Air Pump Board"
+ desc = "The circuit board for a portable air pump."
+ id = "pump"
+ build_path = /obj/item/circuitboard/machine/pump
+ category = list(
+ RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ATMOS
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_SCIENCE | DEPARTMENT_BITFLAG_ENGINEERING
+
+/datum/design/board/pipe_scrubber
+ name = "Portable Pipe Scrubber Board"
+ desc = "The circuit board for a portable pipe scrubber."
+ id = "pipe_scrubber"
+ build_path = /obj/item/circuitboard/machine/pipe_scrubber
+ category = list(
+ RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ATMOS
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_SCIENCE | DEPARTMENT_BITFLAG_ENGINEERING
+
/datum/design/board/bookbinder
name = "Book Binder"
desc = "The circuit board for a book binder"
diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm
index a00139a47c9..2f059b5b538 100644
--- a/code/modules/research/designs/mechfabricator_designs.dm
+++ b/code/modules/research/designs/mechfabricator_designs.dm
@@ -910,6 +910,104 @@
RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_CHASSIS
)
+//Justice (emaged only)
+/datum/design/justice_chassis
+ name = "Exosuit Chassis (\"Justice\")"
+ id = "justice_chassis"
+ build_type = MECHFAB
+ build_path = /obj/item/mecha_parts/chassis/justice
+ materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT*20)
+ construction_time = 10 SECONDS
+ category = list(
+ RND_CATEGORY_MECHFAB_JUSTICE + RND_SUBCATEGORY_MECHFAB_CHASSIS
+ )
+
+/datum/design/justice_torso
+ name = "Exosuit Torso (\"Justice\")"
+ id = "justice_torso"
+ build_type = MECHFAB
+ build_path = /obj/item/mecha_parts/part/justice_torso
+ materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 50,
+ /datum/material/silver = SHEET_MATERIAL_AMOUNT * 5,
+ )
+ construction_time = 30 SECONDS
+ category = list(
+ RND_CATEGORY_MECHFAB_JUSTICE + RND_SUBCATEGORY_MECHFAB_CHASSIS
+ )
+
+/datum/design/justice_left_arm
+ name = "Exosuit Left Arm (\"Justice\")"
+ id = "justice_left_arm"
+ build_type = MECHFAB
+ build_path = /obj/item/mecha_parts/part/justice_left_arm
+ materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 5,
+ /datum/material/silver = SHEET_MATERIAL_AMOUNT * 2,
+ )
+ construction_time = 10 SECONDS
+ category = list(
+ RND_CATEGORY_MECHFAB_JUSTICE + RND_SUBCATEGORY_MECHFAB_CHASSIS
+ )
+
+/datum/design/justice_right_arm
+ name = "Exosuit Right Arm (\"Justice\")"
+ id = "justice_right_arm"
+ build_type = MECHFAB
+ build_path = /obj/item/mecha_parts/part/justice_right_arm
+ materials = list(
+ /datum/material/iron=SHEET_MATERIAL_AMOUNT*5,
+ /datum/material/silver=SHEET_MATERIAL_AMOUNT*2,
+ )
+ construction_time = 10 SECONDS
+ category = list(
+ RND_CATEGORY_MECHFAB_JUSTICE + RND_SUBCATEGORY_MECHFAB_CHASSIS
+ )
+
+/datum/design/justice_left_leg
+ name = "Exosuit Left Leg (\"Justice\")"
+ id = "justice_left_leg"
+ build_type = MECHFAB
+ build_path = /obj/item/mecha_parts/part/justice_left_leg
+ materials = list(
+ /datum/material/iron=SHEET_MATERIAL_AMOUNT*5,
+ /datum/material/titanium=SHEET_MATERIAL_AMOUNT*2,
+ )
+ construction_time = 10 SECONDS
+ category = list(
+ RND_CATEGORY_MECHFAB_JUSTICE + RND_SUBCATEGORY_MECHFAB_CHASSIS
+ )
+
+/datum/design/justice_right_leg
+ name = "Exosuit Right Leg (\"Justice\")"
+ id = "justice_right_leg"
+ build_type = MECHFAB
+ build_path = /obj/item/mecha_parts/part/justice_right_leg
+ materials = list(
+ /datum/material/iron=SHEET_MATERIAL_AMOUNT*5,
+ /datum/material/titanium=SHEET_MATERIAL_AMOUNT*2,
+ )
+ construction_time = 10 SECONDS
+ category = list(
+ RND_CATEGORY_MECHFAB_JUSTICE + RND_SUBCATEGORY_MECHFAB_CHASSIS
+ )
+
+/datum/design/justice_armor
+ name = "Exosuit Armor (\"Justice\")"
+ id = "justice_armor"
+ build_type = MECHFAB
+ build_path = /obj/item/mecha_parts/part/justice_armor
+ materials = list(
+ /datum/material/silver=SHEET_MATERIAL_AMOUNT*10,
+ /datum/material/titanium=SHEET_MATERIAL_AMOUNT*10,
+ /datum/material/plastic=SHEET_MATERIAL_AMOUNT*5,
+ /datum/material/diamond=SHEET_MATERIAL_AMOUNT*1,
+ )
+ construction_time = 20 SECONDS
+ category = list(
+ RND_CATEGORY_MECHFAB_JUSTICE + RND_SUBCATEGORY_MECHFAB_CHASSIS
+ )
+
//Clarke
/datum/design/clarke_chassis
name = "Exosuit Chassis (\"Clarke\")"
@@ -2341,14 +2439,14 @@
RND_CATEGORY_MODSUIT_MODULES + RND_SUBCATEGORY_MODSUIT_MODULES_SUPPLY
)
-/datum/design/module/mod_organ_thrower
- name = "Organ Thrower Module"
- id = "mod_organ_thrower"
+/datum/design/module/mod_organizer
+ name = "Organizer Module"
+ id = "mod_organizer"
materials = list(
/datum/material/iron =HALF_SHEET_MATERIAL_AMOUNT,
/datum/material/glass =HALF_SHEET_MATERIAL_AMOUNT,
)
- build_path = /obj/item/mod/module/organ_thrower
+ build_path = /obj/item/mod/module/organizer
category = list(
RND_CATEGORY_MODSUIT_MODULES + RND_SUBCATEGORY_MODSUIT_MODULES_MEDICAL
)
diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm
index 0ea6c41e164..d9feba5179e 100644
--- a/code/modules/research/designs/misc_designs.dm
+++ b/code/modules/research/designs/misc_designs.dm
@@ -166,18 +166,6 @@
)
departmental_flags = DEPARTMENT_BITFLAG_SERVICE
-/datum/design/air_horn
- name = "Air Horn"
- desc = "Damn son, where'd you find this?"
- id = "air_horn"
- build_type = PROTOLATHE | AWAY_LATHE
- materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*2, /datum/material/bananium =HALF_SHEET_MATERIAL_AMOUNT)
- build_path = /obj/item/bikehorn/airhorn
- category = list(
- RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_SERVICE
- )
- departmental_flags = DEPARTMENT_BITFLAG_SERVICE
-
/datum/design/clown_firing_pin
name = "Hilarious Firing Pin"
id = "clown_firing_pin"
diff --git a/code/modules/research/designs/tool_designs.dm b/code/modules/research/designs/tool_designs.dm
index 392921795b9..87362f4e302 100644
--- a/code/modules/research/designs/tool_designs.dm
+++ b/code/modules/research/designs/tool_designs.dm
@@ -226,17 +226,6 @@
)
departmental_flags = DEPARTMENT_BITFLAG_SERVICE
-/datum/design/plumbing_rcd_sci
- name = "Research Plumbing Constructor"
- id = "plumbing_rcd_sci"
- build_type = PROTOLATHE | AWAY_LATHE
- materials = list(/datum/material/iron =SHEET_MATERIAL_AMOUNT*37.5, /datum/material/glass =SHEET_MATERIAL_AMOUNT*18.75, /datum/material/plastic =HALF_SHEET_MATERIAL_AMOUNT)
- build_path = /obj/item/construction/plumbing/research
- category = list(
- RND_CATEGORY_TOOLS + RND_SUBCATEGORY_TOOLS_PLUMBING
- )
- departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
-
/datum/design/plumbing_rcd_service
name = "Service Plumbing Constructor"
id = "plumbing_rcd_service"
diff --git a/code/modules/research/designs/weapon_designs.dm b/code/modules/research/designs/weapon_designs.dm
index 1aaeeec77d2..1412c0f0d8b 100644
--- a/code/modules/research/designs/weapon_designs.dm
+++ b/code/modules/research/designs/weapon_designs.dm
@@ -449,3 +449,15 @@
RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_RANGED
)
departmental_flags = DEPARTMENT_BITFLAG_SECURITY
+
+/datum/design/photon_cannon
+ name = "Photon Cannon Part Kit (Nonlethal)"
+ desc = "A kit to reverse-engineer a photon cannon, a weapon that generates a shortly-lived miniature sun. Technically brightens up the room, effectively blinds everyone in it. Requires a flux anomaly core to finish."
+ id = "photon_cannon"
+ build_type = PROTOLATHE | AWAY_LATHE
+ materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 3, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 7, /datum/material/gold = SHEET_MATERIAL_AMOUNT * 5)
+ build_path = /obj/item/weaponcrafting/gunkit/photon
+ category = list(
+ RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_KITS
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_SECURITY
diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm
index 6ec29557fb8..1360c25a23c 100644
--- a/code/modules/research/experimentor.dm
+++ b/code/modules/research/experimentor.dm
@@ -461,9 +461,9 @@
if(exp == SCANTYPE_DISCOVER)
visible_message(span_notice("[src] scans the [exp_on], revealing its true nature!"))
playsound(src, 'sound/effects/supermatter.ogg', 50, 3, -1)
- var/obj/item/relic/R = loaded_item
- R.reveal()
- investigate_log("Experimentor has revealed a relic with [span_danger("[R.realProc]")] effect.", INVESTIGATE_EXPERIMENTOR)
+ var/obj/item/relic/loaded_artifact = loaded_item
+ loaded_artifact.reveal()
+ investigate_log("Experimentor has revealed a relic with [span_danger("[loaded_artifact.hidden_power]")] effect.", INVESTIGATE_EXPERIMENTOR)
ejectItem()
//Global reactions
@@ -553,34 +553,74 @@
#undef FAIL
-//////////////////////////////////SPECIAL ITEMS////////////////////////////////////////
+// Relic \\
/obj/item/relic
name = "strange object"
desc = "What mysteries could this hold? Maybe Research & Development could find out."
- icon = 'icons/obj/devices/artifacts.dmi'
- var/realName = "defined object"
- var/revealed = FALSE
- var/realProc
- var/reset_timer = 60
+ icon = 'icons/obj/devices/artefacts.dmi'
+ icon_state = "debug_artefact"
+ //The name this artefact will have when it's activated.
+ var/real_name = "artefact"
+ //Has this artefact been activated?
+ var/activated = FALSE
+ //What effect this artefact has when used. Randomly determined when activated.
+ var/hidden_power
+ //Minimum possible cooldown.
+ var/min_cooldown = 6 SECONDS
+ //Max possible cooldown.
+ var/max_cooldown = 30 SECONDS
+ //Cooldown length. Randomly determined at activation if it isn't determined here.
+ var/cooldown_timer
COOLDOWN_DECLARE(cooldown)
+ //What visual theme this artefact has. Current possible choices: "prototype", "necrotech"
+ var/artifact_theme = "prototype"
/obj/item/relic/Initialize(mapload)
. = ..()
- icon_state = pick("prototype1","prototype2","prototype3","prototype4","prototype5","prototype6","prototype7","prototype8","prototype9")
- realName = "[pick("broken","twisted","spun","improved","silly","regular","badly made")] [pick("device","object","toy","illegal tech","weapon")]"
+ random_themed_appearance()
+/obj/item/relic/proc/random_themed_appearance()
+ var/themed_name_prefix
+ var/themed_name_suffix
+ if(artifact_theme == "prototype")
+ icon_state = pick("prototype1", "prototype2", "prototype3", "prototype4", "prototype5", "prototype6", "prototype7", "prototype8","prototype9")
+ themed_name_prefix = pick("experimental","prototype","artificial","handcrafted","ramshackle","odd")
+ themed_name_suffix = pick("device","assembly","gadget","gizmo","contraption","machine","widget","object")
+ real_name = "[pick(themed_name_prefix)] [pick(themed_name_suffix)]"
+ name = "strange [pick(themed_name_suffix)]"
+ if(artifact_theme == "necrotech")
+ icon_state = pick("necrotech1", "necrotech2", "necrotech3", "necrotech4", "necrotech5", "necrotech6")
+ themed_name_prefix = pick("dark","bloodied","unholy","archeotechnological","dismal","ruined","thrumming")
+ themed_name_suffix = pick("instrument","shard","fetish","bibelot","trinket","offering","relic")
+ real_name = "[pick(themed_name_prefix)] [pick(themed_name_suffix)]"
+ name = "strange relic"
+ update_appearance()
+
+/obj/item/relic/lavaland
+ name = "strange relic"
+ artifact_theme = "necrotech"
/obj/item/relic/proc/reveal()
- if(revealed) //Re-rolling your relics seems a bit overpowered, yes?
+ if(activated) //no rerolling
return
- revealed = TRUE
- name = realName
- reset_timer = rand(reset_timer, reset_timer * 5)
- realProc = pick(PROC_REF(teleport), PROC_REF(explode), PROC_REF(rapidDupe), PROC_REF(petSpray), PROC_REF(flash), PROC_REF(clean), PROC_REF(corgicannon))
+ activated = TRUE
+ name = real_name
+ if(!cooldown_timer)
+ cooldown_timer = rand(min_cooldown, max_cooldown)
+ if(!hidden_power)
+ hidden_power = pick(
+ PROC_REF(corgi_cannon),
+ PROC_REF(cleaning_foam),
+ PROC_REF(flashbanger),
+ PROC_REF(summon_animals),
+ PROC_REF(uncontrolled_teleport),
+ PROC_REF(heat_and_explode),
+ PROC_REF(rapid_self_dupe),
+ )
/obj/item/relic/attack_self(mob/user)
- if(!revealed)
+ if(!activated)
to_chat(user, span_notice("You aren't quite sure what this is. Maybe R&D knows what to do with it?"))
return
if(!COOLDOWN_FINISHED(src, cooldown))
@@ -588,40 +628,39 @@
return
if(loc != user)
return
- COOLDOWN_START(src, cooldown, reset_timer)
- call(src,realProc)(user)
+ COOLDOWN_START(src, cooldown, cooldown_timer)
+ call(src, hidden_power)(user)
-//////////////// RELIC PROCS /////////////////////////////
-
-/obj/item/relic/proc/throwSmoke(turf/where)
+/obj/item/relic/proc/throw_smoke(turf/where)
var/datum/effect_system/fluid_spread/smoke/smoke = new
smoke.set_up(0, holder = src, location = get_turf(where))
smoke.start()
-/obj/item/relic/proc/corgicannon(mob/user)
+// Artefact Powers \\
+
+/obj/item/relic/proc/corgi_cannon(mob/user)
playsound(src, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
var/mob/living/basic/pet/dog/corgi/sad_corgi = new(get_turf(user))
- sad_corgi.throw_at(pick(oview(10,user)), 10, rand(3,8), callback = CALLBACK(src, PROC_REF(throwSmoke), sad_corgi))
+ sad_corgi.throw_at(pick(oview(10,user)), 10, rand(3,8), callback = CALLBACK(src, PROC_REF(throw_smoke), sad_corgi))
warn_admins(user, "Corgi Cannon", 0)
-/obj/item/relic/proc/clean(mob/user)
+/obj/item/relic/proc/cleaning_foam(mob/user)
playsound(src, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
- var/obj/item/grenade/chem_grenade/cleaner/CL = new/obj/item/grenade/chem_grenade/cleaner(get_turf(user))
- CL.detonate()
- qdel(CL)
+ var/obj/item/grenade/chem_grenade/cleaner/spawned_foamer = new/obj/item/grenade/chem_grenade/cleaner(get_turf(user))
+ spawned_foamer.detonate()
+ qdel(spawned_foamer)
warn_admins(user, "Foam", 0)
-/obj/item/relic/proc/flash(mob/user)
+/obj/item/relic/proc/flashbanger(mob/user)
playsound(src, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
- var/obj/item/grenade/flashbang/CB = new/obj/item/grenade/flashbang(user.loc)
- CB.detonate()
+ var/obj/item/grenade/flashbang/spawned_flashbang = new/obj/item/grenade/flashbang(user.loc)
+ spawned_flashbang.detonate()
warn_admins(user, "Flash")
-/obj/item/relic/proc/petSpray(mob/user)
+/obj/item/relic/proc/summon_animals(mob/user)
var/message = span_danger("[src] begins to shake, and in the distance the sound of rampaging animals arises!")
visible_message(message)
to_chat(user, message)
-
var/static/list/valid_animals = list(
/mob/living/basic/bear,
/mob/living/basic/bee,
@@ -637,42 +676,40 @@
/mob/living/basic/pet/fox,
)
for(var/counter in 1 to rand(1, 25))
- var/mobType = pick(valid_animals)
- new mobType(get_turf(src))
-
+ var/animal_spawn = pick(valid_animals)
+ new animal_spawn(get_turf(src))
warn_admins(user, "Mass Mob Spawn")
if(prob(60))
to_chat(user, span_warning("[src] falls apart!"))
qdel(src)
-/obj/item/relic/proc/rapidDupe(mob/user)
+/obj/item/relic/proc/rapid_self_dupe(mob/user)
audible_message("[src] emits a loud pop!")
- var/list/dupes = list()
+ var/list/dummy_artifacts = list()
for(var/counter in 1 to rand(5,10))
- var/obj/item/relic/R = new type(get_turf(src))
- R.name = name
- R.desc = desc
- R.realName = realName
- R.realProc = realProc
- R.revealed = TRUE
- dupes += R
- R.throw_at(pick(oview(7,get_turf(src))),10,1)
-
- QDEL_LIST_IN(dupes, rand(10, 100))
+ var/obj/item/relic/duped = new type(get_turf(src))
+ duped.name = name
+ duped.desc = desc
+ duped.real_name = real_name
+ duped.hidden_power = hidden_power
+ duped.activated = TRUE
+ dummy_artifacts += duped
+ duped.throw_at(pick(oview(7,get_turf(src))),10,1)
+ QDEL_LIST_IN(dummy_artifacts, rand(1 SECONDS, 10 SECONDS))
warn_admins(user, "Rapid duplicator", 0)
-/obj/item/relic/proc/explode(mob/user)
+/obj/item/relic/proc/heat_and_explode(mob/user)
to_chat(user, span_danger("[src] begins to heat up!"))
- addtimer(CALLBACK(src, PROC_REF(do_explode), user), rand(3.5 SECONDS, 10 SECONDS))
+ addtimer(CALLBACK(src, PROC_REF(blow_up), user), rand(3.5 SECONDS, 10 SECONDS))
-/obj/item/relic/proc/do_explode(mob/user)
+/obj/item/relic/proc/blow_up(mob/user)
if(loc == user)
visible_message(span_notice("\The [src]'s top opens, releasing a powerful blast!"))
explosion(src, heavy_impact_range = rand(1,5), light_impact_range = rand(1,5), flame_range = 2, flash_range = rand(1,5), adminlog = TRUE)
warn_admins(user, "Explosion")
qdel(src) //Comment this line to produce a light grenade (the bomb that keeps on exploding when used)!!
-/obj/item/relic/proc/teleport(mob/user)
+/obj/item/relic/proc/uncontrolled_teleport(mob/user)
to_chat(user, span_notice("[src] begins to vibrate!"))
addtimer(CALLBACK(src, PROC_REF(do_the_teleport), user), rand(1 SECONDS, 3 SECONDS))
@@ -680,16 +717,16 @@
var/turf/userturf = get_turf(user)
if(loc == user && !is_centcom_level(userturf.z)) //Because Nuke Ops bringing this back on their shuttle, then looting the ERT area is 2fun4you!
visible_message(span_notice("[src] twists and bends, relocating itself!"))
- throwSmoke(userturf)
+ throw_smoke(userturf)
do_teleport(user, userturf, 8, asoundin = 'sound/effects/phasein.ogg', channel = TELEPORT_CHANNEL_BLUESPACE)
- throwSmoke(get_turf(user))
+ throw_smoke(get_turf(user))
warn_admins(user, "Teleport", 0)
//Admin Warning proc for relics
-/obj/item/relic/proc/warn_admins(mob/user, RelicType, priority = 1)
- var/turf/T = get_turf(src)
- var/log_msg = "[RelicType] relic used by [key_name(user)] in [AREACOORD(T)]"
+/obj/item/relic/proc/warn_admins(mob/user, relic_type, priority = 1)
+ var/turf/location = get_turf(src)
+ var/log_msg = "[relic_type] relic used by [key_name(user)] in [AREACOORD(location)]"
if(priority) //For truly dangerous relics that may need an admin's attention. BWOINK!
- message_admins("[RelicType] relic activated by [ADMIN_LOOKUPFLW(user)] in [ADMIN_VERBOSEJMP(T)]")
+ message_admins("[relic_type] relic activated by [ADMIN_LOOKUPFLW(user)] in [ADMIN_VERBOSEJMP(location)]")
log_game(log_msg)
investigate_log(log_msg, "experimentor")
diff --git a/code/modules/research/rdmachines.dm b/code/modules/research/rdmachines.dm
index 069447c6f45..0cfca236d24 100644
--- a/code/modules/research/rdmachines.dm
+++ b/code/modules/research/rdmachines.dm
@@ -72,7 +72,7 @@
return CONTEXTUAL_SCREENTIP_SET
else
if(held_item.tool_behaviour == TOOL_MULTITOOL)
- var/obj/item/multitool/tool = held_item
+ var/obj/item/multitool/tool = held_item.get_proxy_attacker_for(src, user)
if(!QDELETED(tool.buffer) && istype(tool.buffer, /datum/techweb))
context[SCREENTIP_CONTEXT_LMB] = "Upload Techweb"
context[SCREENTIP_CONTEXT_RMB] = "Upload Techweb"
diff --git a/code/modules/research/techweb/_techweb_node.dm b/code/modules/research/techweb/_techweb_node.dm
index c36eb886271..7714946a4d2 100644
--- a/code/modules/research/techweb/_techweb_node.dm
+++ b/code/modules/research/techweb/_techweb_node.dm
@@ -40,6 +40,8 @@
var/list/experiments_to_unlock = list()
/// Whether or not this node should show on the wiki
var/show_on_wiki = TRUE
+ /// Hidden Mech nodes unlocked when mech fabricator emaged.
+ var/illegal_mech_node = FALSE
/datum/techweb_node/error_node
id = "ERROR"
diff --git a/code/modules/research/techweb/nodes/atmos_nodes.dm b/code/modules/research/techweb/nodes/atmos_nodes.dm
index a35c5f4c185..ef4fee5ed92 100644
--- a/code/modules/research/techweb/nodes/atmos_nodes.dm
+++ b/code/modules/research/techweb/nodes/atmos_nodes.dm
@@ -8,6 +8,7 @@
"atmosalerts",
"thermomachine",
"space_heater",
+ "scrubber",
"generic_tank",
"oxygen_tank",
"plasma_tank",
@@ -27,6 +28,7 @@
prereq_ids = list(TECHWEB_NODE_ATMOS)
design_ids = list(
"tank_compressor",
+ "pump",
"emergency_oxygen",
"emergency_oxygen_engi",
"power_turbine_console",
@@ -47,8 +49,8 @@
description = "Experiments with high-pressure gases and electricity resulting in crystallization and controlled plasma reactions."
prereq_ids = list(TECHWEB_NODE_GAS_COMPRESSION, TECHWEB_NODE_ENERGY_MANIPULATION)
design_ids = list(
- "crystallizer",
"electrolyzer",
+ "pipe_scrubber",
"pacman",
"mech_generator",
"plasmacutter",
@@ -72,6 +74,7 @@
"bolter_wrench",
"rpd_loaded",
"engine_goggles",
+ "crystallizer",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
required_experiments = list(/datum/experiment/ordnance/gaseous/bz)
@@ -89,6 +92,7 @@
"jawsoflife",
"rangedanalyzer",
"rtd_loaded",
+ "mech_rcd",
"rcd_loaded",
"rcd_ammo",
"weldingmask",
diff --git a/code/modules/research/techweb/nodes/bepis_nodes.dm b/code/modules/research/techweb/nodes/bepis_nodes.dm
index ad5bb4c62bb..baefd8c11d0 100644
--- a/code/modules/research/techweb/nodes/bepis_nodes.dm
+++ b/code/modules/research/techweb/nodes/bepis_nodes.dm
@@ -5,6 +5,7 @@
design_ids = list(
"bright_helmet",
"rld_mini",
+ "photon_cannon",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
hidden = TRUE
diff --git a/code/modules/research/techweb/nodes/biology_nodes.dm b/code/modules/research/techweb/nodes/biology_nodes.dm
index f8ee05c042c..3599dfb9fc5 100644
--- a/code/modules/research/techweb/nodes/biology_nodes.dm
+++ b/code/modules/research/techweb/nodes/biology_nodes.dm
@@ -23,6 +23,7 @@
design_ids = list(
"limbgrower",
"pandemic",
+ "vatgrower",
"petri_dish",
"swab",
"biopsy_tool",
@@ -43,7 +44,7 @@
"limbdesign_plasmaman",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
- required_experiments = list(/datum/experiment/scanning/random/cytology)
+ required_experiments = list(/datum/experiment/scanning/cytology/slime)
/datum/techweb_node/gene_engineering
id = TECHWEB_NODE_GENE_ENGINEERING
@@ -58,7 +59,45 @@
"mod_dna_lock",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
- discount_experiments = list(
- /datum/experiment/scanning/random/plants/traits = TECHWEB_TIER_2_POINTS,
- /datum/experiment/scanning/points/slime/hard = TECHWEB_TIER_2_POINTS,
- )
+ discount_experiments = list(/datum/experiment/scanning/people/mutant = TECHWEB_TIER_4_POINTS)
+
+// Botany root node
+/datum/techweb_node/botany_equip
+ id = TECHWEB_NODE_BOTANY_EQUIP
+ starting_node = TRUE
+ display_name = "Botany Equipment"
+ description = "Essential tools for maintaining onboard gardens, supporting plant growth in the unique environment of the space station."
+ design_ids = list(
+ "seed_extractor",
+ "plant_analyzer",
+ "watering_can",
+ "spade",
+ "cultivator",
+ "secateurs",
+ "hatchet",
+ )
+
+/datum/techweb_node/hydroponics
+ id = TECHWEB_NODE_HYDROPONICS
+ display_name = "Hydroponics"
+ description = "Research into advanced hydroponic systems for efficient and sustainable plant cultivation."
+ prereq_ids = list(TECHWEB_NODE_BOTANY_EQUIP, TECHWEB_NODE_CHEM_SYNTHESIS)
+ design_ids = list(
+ "biogenerator",
+ "hydro_tray",
+ "portaseeder",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+
+/datum/techweb_node/selection
+ id = TECHWEB_NODE_SELECTION
+ display_name = "Artificial Selection"
+ description = "Advancement in plant cultivation techniques through artificial selection, enabling precise manipulation of plant DNA."
+ prereq_ids = list(TECHWEB_NODE_HYDROPONICS)
+ design_ids = list(
+ "flora_gun",
+ "gene_shears",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+ required_experiments = list(/datum/experiment/scanning/random/plants/wild)
+ discount_experiments = list(/datum/experiment/scanning/random/plants/traits = TECHWEB_TIER_3_POINTS)
diff --git a/code/modules/research/techweb/nodes/cyborg_nodes.dm b/code/modules/research/techweb/nodes/cyborg_nodes.dm
index da9f485f2bf..447ee2dc7f3 100644
--- a/code/modules/research/techweb/nodes/cyborg_nodes.dm
+++ b/code/modules/research/techweb/nodes/cyborg_nodes.dm
@@ -19,6 +19,9 @@
"cybernetic_liver",
"cybernetic_heart",
)
+ experiments_to_unlock = list(
+ /datum/experiment/scanning/people/android,
+ )
/datum/techweb_node/cybernetics
id = TECHWEB_NODE_CYBERNETICS
@@ -213,7 +216,7 @@
"cybernetic_liver_tier3",
"cybernetic_heart_tier3",
)
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
required_experiments = list(/datum/experiment/scanning/people/augmented_organs)
/datum/techweb_node/cyber/cyber_organs_adv
@@ -229,3 +232,4 @@
"ci-xray-moth",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
+ discount_experiments = list(/datum/experiment/scanning/people/android = TECHWEB_TIER_5_POINTS)
diff --git a/code/modules/research/techweb/nodes/mech_nodes.dm b/code/modules/research/techweb/nodes/mech_nodes.dm
index 5f3d3021323..6b7a9803ae4 100644
--- a/code/modules/research/techweb/nodes/mech_nodes.dm
+++ b/code/modules/research/techweb/nodes/mech_nodes.dm
@@ -112,7 +112,6 @@
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
required_experiments = list(/datum/experiment/scanning/random/mecha_equipped_scan)
- discount_experiments = list(/datum/experiment/scanning/random/mecha_damage_scan = TECHWEB_TIER_2_POINTS)
/datum/techweb_node/mech_assault
id = TECHWEB_NODE_MECH_ASSAULT
@@ -194,6 +193,22 @@
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+/datum/techweb_node/justice
+ id = "mecha_justice"
+ display_name = "EXOSUIT: Justice"
+ description = "Justice exosuit designs"
+ design_ids = list(
+ "justice_armor",
+ "justice_chassis",
+ "justice_left_arm",
+ "justice_left_leg",
+ "justice_right_arm",
+ "justice_right_leg",
+ "justice_torso",
+ )
+ hidden = TRUE
+ illegal_mech_node = TRUE
+
/datum/techweb_node/mech_energy_guns
id = TECHWEB_NODE_MECH_ENERGY_GUNS
display_name = "Mech Energy Guns"
@@ -206,6 +221,7 @@
"mech_tesla",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+ discount_experiments = list(/datum/experiment/scanning/random/mecha_damage_scan = TECHWEB_TIER_4_POINTS)
/datum/techweb_node/mech_firearms
id = TECHWEB_NODE_MECH_FIREARMS
@@ -246,6 +262,5 @@
"mech_gravcatapult",
"mech_teleporter",
"mech_wormhole_gen",
- "mech_rcd",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
diff --git a/code/modules/research/techweb/nodes/medbay_nodes.dm b/code/modules/research/techweb/nodes/medbay_nodes.dm
index 51bc5f15049..7d6db08f584 100644
--- a/code/modules/research/techweb/nodes/medbay_nodes.dm
+++ b/code/modules/research/techweb/nodes/medbay_nodes.dm
@@ -33,6 +33,8 @@
/datum/experiment/autopsy/human,
/datum/experiment/autopsy/nonhuman,
/datum/experiment/autopsy/xenomorph,
+ /datum/experiment/scanning/reagent/haloperidol,
+ /datum/experiment/scanning/reagent/cryostylane,
)
/datum/techweb_node/chem_synthesis
@@ -62,7 +64,6 @@
design_ids = list(
"plumbing_rcd",
"plumbing_rcd_service",
- "plumbing_rcd_sci",
"plunger",
"fluid_ducts",
"meta_beaker",
@@ -70,27 +71,13 @@
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
-/datum/techweb_node/cryostasis
- id = TECHWEB_NODE_CRYOSTASIS
- display_name = "Cryostasis"
- description = "The result of clown accidentally drinking a chemical, now repurposed for safely preserving crew members in suspended animation."
- prereq_ids = list(TECHWEB_NODE_PLUMBING, TECHWEB_NODE_PLASMA_CONTROL)
- design_ids = list(
- "cryotube",
- "mech_sleeper",
- "stasis",
- "cryo_grenade",
- "splitbeaker",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
- required_experiments = list(/datum/experiment/scanning/reagent/cryostylane)
-
/datum/techweb_node/medbay_equip_adv
id = TECHWEB_NODE_MEDBAY_EQUIP_ADV
display_name = "Advanced Medbay Equipment"
description = "State-of-the-art medical gear for keeping the crew in one piece — mostly."
- prereq_ids = list(TECHWEB_NODE_CRYOSTASIS)
+ prereq_ids = list(TECHWEB_NODE_PLUMBING)
design_ids = list(
+ "smoke_machine",
"chem_mass_spec",
"healthanalyzer_advanced",
"mod_health_analyzer",
@@ -99,4 +86,20 @@
"defibmount",
"medicalbed_emergency",
)
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+ required_experiments = list(/datum/experiment/scanning/reagent/haloperidol)
+
+/datum/techweb_node/cryostasis
+ id = TECHWEB_NODE_CRYOSTASIS
+ display_name = "Cryostasis"
+ description = "The result of clown accidentally drinking a chemical, now repurposed for safely preserving crew members in suspended animation."
+ prereq_ids = list(TECHWEB_NODE_FUSION)
+ design_ids = list(
+ "cryotube",
+ "mech_sleeper",
+ "stasis",
+ "cryo_grenade",
+ "splitbeaker",
+ )
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+ discount_experiments = list(/datum/experiment/scanning/reagent/cryostylane = TECHWEB_TIER_4_POINTS)
diff --git a/code/modules/research/techweb/nodes/modsuit_nodes.dm b/code/modules/research/techweb/nodes/modsuit_nodes.dm
index 2e74663eed4..7fc7342ea41 100644
--- a/code/modules/research/techweb/nodes/modsuit_nodes.dm
+++ b/code/modules/research/techweb/nodes/modsuit_nodes.dm
@@ -58,7 +58,7 @@
"mod_plating_medical",
"mod_quick_carry",
"mod_injector",
- "mod_organ_thrower",
+ "mod_organizer",
"mod_patienttransport",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
diff --git a/code/modules/research/techweb/nodes/research_nodes.dm b/code/modules/research/techweb/nodes/research_nodes.dm
index 0cdbdb6548b..0a37fb19868 100644
--- a/code/modules/research/techweb/nodes/research_nodes.dm
+++ b/code/modules/research/techweb/nodes/research_nodes.dm
@@ -91,4 +91,5 @@
"gravitygun",
"polymorph_belt"
)
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
+ discount_experiments = list(/datum/experiment/scanning/points/anomalies = TECHWEB_TIER_5_POINTS)
diff --git a/code/modules/research/techweb/nodes/security_nodes.dm b/code/modules/research/techweb/nodes/security_nodes.dm
index c85639b0aec..2d3dd63864f 100644
--- a/code/modules/research/techweb/nodes/security_nodes.dm
+++ b/code/modules/research/techweb/nodes/security_nodes.dm
@@ -4,6 +4,7 @@
display_name = "Basic Arms"
description = "Ballistics can be unpredictable in space."
design_ids = list(
+ "toy_armblade",
"toygun",
"c38_rubber",
"sec_38",
@@ -33,7 +34,6 @@
"dragnet_beacon",
"inspector",
"evidencebag",
- "handcuffs_s",
"zipties",
"seclite",
"electropack",
@@ -46,10 +46,12 @@
description = "When you are on the opposing side of a revolutionary movement."
prereq_ids = list(TECHWEB_NODE_SEC_EQUIP)
design_ids = list(
+ "clown_firing_pin",
"pin_testing",
"pin_loyalty",
"tele_shield",
"ballistic_shield",
+ "handcuffs_s",
"bola_energy",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
@@ -66,7 +68,6 @@
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
required_experiments = list(/datum/experiment/ordnance/explosive/lowyieldbomb)
- discount_experiments = list(/datum/experiment/ordnance/explosive/highyieldbomb = TECHWEB_TIER_3_POINTS)
/datum/techweb_node/exotic_ammo
id = TECHWEB_NODE_EXOTIC_AMMO
@@ -79,6 +80,7 @@
"techshotshell",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+ discount_experiments = list(/datum/experiment/ordnance/explosive/highyieldbomb = TECHWEB_TIER_4_POINTS)
/datum/techweb_node/electric_weapons
id = TECHWEB_NODE_ELECTRIC_WEAPONS
diff --git a/code/modules/research/techweb/nodes/service_nodes.dm b/code/modules/research/techweb/nodes/service_nodes.dm
index da7c48f0878..1553f3a0c20 100644
--- a/code/modules/research/techweb/nodes/service_nodes.dm
+++ b/code/modules/research/techweb/nodes/service_nodes.dm
@@ -52,19 +52,6 @@
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
discount_experiments = list(/datum/experiment/scanning/random/janitor_trash = TECHWEB_TIER_2_POINTS)
-/datum/techweb_node/toys
- id = TECHWEB_NODE_TOYS
- display_name = "New Toys"
- description = "For new pranks."
- prereq_ids = list(TECHWEB_NODE_OFFICE_EQUIP)
- design_ids = list(
- "smoke_machine",
- "toy_armblade",
- "air_horn",
- "clown_firing_pin",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
-
/datum/techweb_node/consoles
id = TECHWEB_NODE_CONSOLES
display_name = "Civilian Consoles"
@@ -96,7 +83,7 @@
id = TECHWEB_NODE_GAMING
display_name = "Gaming"
description = "For the slackers on the station."
- prereq_ids = list(TECHWEB_NODE_TOYS, TECHWEB_NODE_CONSOLES)
+ prereq_ids = list(TECHWEB_NODE_CONSOLES)
design_ids = list(
"arcade_battle",
"arcade_orion",
@@ -195,44 +182,3 @@
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
// only available if you've done the first fishing experiment (thus unlocking fishing tech), but not a strict requirement to get the tech
discount_experiments = list(/datum/experiment/scanning/fish/second = TECHWEB_TIER_3_POINTS)
-
-// Botany root node
-/datum/techweb_node/botany_equip
- id = TECHWEB_NODE_BOTANY_EQUIP
- starting_node = TRUE
- display_name = "Botany Equipment"
- description = "Essential tools for maintaining onboard gardens, supporting plant growth in the unique environment of the space station."
- design_ids = list(
- "seed_extractor",
- "plant_analyzer",
- "watering_can",
- "spade",
- "cultivator",
- "secateurs",
- "hatchet",
- )
-
-/datum/techweb_node/hydroponics
- id = TECHWEB_NODE_HYDROPONICS
- display_name = "Hydroponics"
- description = "Research into advanced hydroponic systems for efficient and sustainable plant cultivation."
- prereq_ids = list(TECHWEB_NODE_BOTANY_EQUIP, TECHWEB_NODE_CHEM_SYNTHESIS)
- design_ids = list(
- "biogenerator",
- "hydro_tray",
- "portaseeder",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
-
-/datum/techweb_node/selection
- id = TECHWEB_NODE_SELECTION
- display_name = "Artificial Selection"
- description = "Advancement in plant cultivation techniques through artificial selection, enabling precise manipulation of plant DNA."
- prereq_ids = list(TECHWEB_NODE_HYDROPONICS)
- design_ids = list(
- "flora_gun",
- "gene_shears",
- )
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
- required_experiments = list(/datum/experiment/scanning/random/plants/wild)
- discount_experiments = list(/datum/experiment/scanning/random/plants/traits = TECHWEB_TIER_3_POINTS)
diff --git a/code/modules/research/techweb/nodes/surgery_nodes.dm b/code/modules/research/techweb/nodes/surgery_nodes.dm
index 2bd1f6d5df6..0b8812191e2 100644
--- a/code/modules/research/techweb/nodes/surgery_nodes.dm
+++ b/code/modules/research/techweb/nodes/surgery_nodes.dm
@@ -56,7 +56,7 @@
"surgery_viral_bond",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
- required_experiments = list(/datum/experiment/autopsy/nonhuman)
+ discount_experiments = list(/datum/experiment/autopsy/nonhuman = TECHWEB_TIER_3_POINTS)
/datum/techweb_node/surgery_tools
id = TECHWEB_NODE_SURGERY_TOOLS
diff --git a/code/modules/research/xenobiology/crossbreeding/_clothing.dm b/code/modules/research/xenobiology/crossbreeding/_clothing.dm
index c174856a62a..9bd9e2d881b 100644
--- a/code/modules/research/xenobiology/crossbreeding/_clothing.dm
+++ b/code/modules/research/xenobiology/crossbreeding/_clothing.dm
@@ -38,8 +38,13 @@ Slimecrossing Armor
icon = 'icons/obj/science/slimecrossing.dmi'
icon_state = "prismglasses"
actions_types = list(/datum/action/item_action/change_prism_colour, /datum/action/item_action/place_light_prism)
+ forced_glass_color = TRUE
var/glasses_color = COLOR_WHITE
+/obj/item/clothing/glasses/prism_glasses/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/wearable_client_colour, /datum/client_colour/glass_colour, ITEM_SLOT_EYES, glasses_color, forced_glass_color)
+
/obj/item/clothing/glasses/prism_glasses/item_action_slot_check(slot)
if(slot & ITEM_SLOT_EYES)
return TRUE
@@ -76,7 +81,9 @@ Slimecrossing Armor
var/new_color = input(owner, "Choose the lens color:", "Color change",glasses.glasses_color) as color|null
if(!new_color)
return
+ RemoveElement(/datum/element/wearable_client_colour, /datum/client_colour/glass_colour, ITEM_SLOT_EYES, glasses.glasses_color, glasses.forced_glass_color)
glasses.glasses_color = new_color
+ AddElement(/datum/element/wearable_client_colour, /datum/client_colour/glass_colour, ITEM_SLOT_EYES, new_color, glasses.forced_glass_color)
/datum/action/item_action/place_light_prism
name = "Fabricate Light Prism"
diff --git a/code/modules/research/xenobiology/vatgrowing/biopsy_tool.dm b/code/modules/research/xenobiology/vatgrowing/biopsy_tool.dm
index 8b0b007adee..d04a7f852da 100644
--- a/code/modules/research/xenobiology/vatgrowing/biopsy_tool.dm
+++ b/code/modules/research/xenobiology/vatgrowing/biopsy_tool.dm
@@ -1,7 +1,7 @@
///Tool capable of taking biological samples from mobs
/obj/item/biopsy_tool
name = "biopsy tool"
- desc = "Don't worry, it won't sting."
+ desc = "Used to retrieve cell lines from organisms. Don't worry, it won't sting."
icon = 'icons/obj/science/vatgrowing.dmi'
icon_state = "biopsy"
worn_icon_state = "biopsy"
diff --git a/code/modules/research/xenobiology/vatgrowing/microscope.dm b/code/modules/research/xenobiology/vatgrowing/microscope.dm
index df065698436..c3c7369981e 100644
--- a/code/modules/research/xenobiology/vatgrowing/microscope.dm
+++ b/code/modules/research/xenobiology/vatgrowing/microscope.dm
@@ -128,7 +128,5 @@
reqs = list(
/obj/item/stack/sheet/glass = 1,
/obj/item/stack/sheet/plastic = 1,
- /obj/item/stock_parts/scanning_module = 1,
- /obj/item/flashlight = 1,
)
category = CAT_CHEMISTRY
diff --git a/code/modules/research/xenobiology/vatgrowing/samples/_micro_organism.dm b/code/modules/research/xenobiology/vatgrowing/samples/_micro_organism.dm
index b8af4dd062c..46d643aa532 100644
--- a/code/modules/research/xenobiology/vatgrowing/samples/_micro_organism.dm
+++ b/code/modules/research/xenobiology/vatgrowing/samples/_micro_organism.dm
@@ -31,7 +31,7 @@
var/resulting_atom_count = 1
///Handles growth of the micro_organism. This only runs if the micro organism is in the growing vat. Reagents is the growing vats reagents
-/datum/micro_organism/cell_line/proc/handle_growth(obj/machinery/plumbing/growing_vat/vat)
+/datum/micro_organism/cell_line/proc/handle_growth(obj/machinery/vatgrower/vat)
if(!try_eat(vat.reagents))
return FALSE
growth = max(growth, growth + calculate_growth(vat.reagents, vat.biological_sample)) //Prevent you from having minus growth.
@@ -74,7 +74,7 @@
. -= virus_suspectibility
///Called once a cell line reaches 100 growth. Then we check if any cell_line is too far so we can perform an epic fail roll
-/datum/micro_organism/cell_line/proc/finish_growing(obj/machinery/plumbing/growing_vat/vat)
+/datum/micro_organism/cell_line/proc/finish_growing(obj/machinery/vatgrower/vat)
var/risk = 0 //Penalty for failure, goes up based on how much growth the other cell_lines have
for(var/datum/micro_organism/cell_line/cell_line in vat.biological_sample.micro_organisms)
@@ -90,7 +90,7 @@
succeed_growing(vat)
return TRUE
-/datum/micro_organism/cell_line/proc/fuck_up_growing(obj/machinery/plumbing/growing_vat/vat)
+/datum/micro_organism/cell_line/proc/fuck_up_growing(obj/machinery/vatgrower/vat)
vat.visible_message(span_warning("The biological sample in [vat] seems to have dissipated!"))
if(prob(50))
new /obj/effect/gibspawner/generic(get_turf(vat)) //Spawn some gibs.
@@ -98,7 +98,7 @@
return
QDEL_NULL(vat.biological_sample)
-/datum/micro_organism/cell_line/proc/succeed_growing(obj/machinery/plumbing/growing_vat/vat)
+/datum/micro_organism/cell_line/proc/succeed_growing(obj/machinery/vatgrower/vat)
var/datum/effect_system/fluid_spread/smoke/smoke = new
smoke.set_up(0, holder = vat, location = vat.loc)
smoke.start()
@@ -112,18 +112,18 @@
///Overriden to show more info like needs, supplementary and supressive reagents and also growth.
/datum/micro_organism/cell_line/get_details(show_details)
- . += "[span_notice("[desc] - growth progress: [growth]%")]\n"
+ . += "[span_notice("[desc] - growth progress: [growth]%")]"
if(show_details)
- . += return_reagent_text("It requires:", required_reagents)
- . += return_reagent_text("It likes:", supplementary_reagents)
- . += return_reagent_text("It hates:", suppressive_reagents)
+ . += "\n- " + return_reagent_text("Requires:", required_reagents)
+ . += "\n- " + return_reagent_text("Likes:", supplementary_reagents)
+ . += "\n- " + return_reagent_text("Hates:", suppressive_reagents)
///Return a nice list of all the reagents in a specific category with a specific prefix. This needs to be reworked because the formatting sucks ass.
/datum/micro_organism/cell_line/proc/return_reagent_text(prefix_text = "It requires:", list/reagentlist)
if(!reagentlist.len)
return
- var/all_reagents_text
+ var/list/reagent_names = list()
for(var/i in reagentlist)
var/datum/reagent/reagent = i
- all_reagents_text += " - [initial(reagent.name)]\n"
- return span_notice("[prefix_text]\n[all_reagents_text]")
+ reagent_names += initial(reagent.name)
+ return span_notice("[prefix_text] [jointext(reagent_names, ", ")]")
diff --git a/code/modules/research/xenobiology/vatgrowing/samples/_sample.dm b/code/modules/research/xenobiology/vatgrowing/samples/_sample.dm
index eda9d2771eb..7838977bd4d 100644
--- a/code/modules/research/xenobiology/vatgrowing/samples/_sample.dm
+++ b/code/modules/research/xenobiology/vatgrowing/samples/_sample.dm
@@ -31,7 +31,7 @@
return TRUE
///Call handle_growth on all our microorganisms.
-/datum/biological_sample/proc/handle_growth(obj/machinery/plumbing/growing_vat/vat)
+/datum/biological_sample/proc/handle_growth(obj/machinery/vatgrower/vat)
for(var/datum/micro_organism/cell_line/organism in micro_organisms) //Types because we don't grow viruses.
organism.handle_growth(vat)
diff --git a/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/common.dm b/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/common.dm
index 8dd4594d47d..571440f8442 100644
--- a/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/common.dm
+++ b/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/common.dm
@@ -450,7 +450,7 @@
virus_suspectibility = 0.5
resulting_atom = /mob/living/simple_animal/hostile/vatbeast
-/datum/micro_organism/cell_line/vat_beast/succeed_growing(obj/machinery/plumbing/growing_vat/vat)
+/datum/micro_organism/cell_line/vat_beast/succeed_growing(obj/machinery/vatgrower/vat)
. = ..()
qdel(vat)
@@ -476,11 +476,11 @@
virus_suspectibility = 0
-/datum/micro_organism/cell_line/netherworld/succeed_growing(obj/machinery/plumbing/growing_vat/vat)
+/datum/micro_organism/cell_line/netherworld/succeed_growing(obj/machinery/vatgrower/vat)
resulting_atom = pick(/mob/living/basic/creature, /mob/living/basic/migo, /mob/living/basic/blankbody) //i looked myself, pretty much all of them are reasonably strong and somewhat on the same level. except migo is the jackpot and the blank body is whiff.
return ..()
-/datum/micro_organism/cell_line/clown/fuck_up_growing(obj/machinery/plumbing/growing_vat/vat)
+/datum/micro_organism/cell_line/clown/fuck_up_growing(obj/machinery/vatgrower/vat)
vat.visible_message(span_warning("The biological sample in [vat] seems to have created something horrific!"))
var/mob/selected_mob = pick(list(/mob/living/basic/clown/mutant/slow, /mob/living/basic/clown/fleshclown))
@@ -656,7 +656,7 @@
virus_suspectibility = 0
resulting_atom = /obj/item/queen_bee/bought
-/datum/micro_organism/cell_line/queen_bee/fuck_up_growing(obj/machinery/plumbing/growing_vat/vat) //we love job hazards
+/datum/micro_organism/cell_line/queen_bee/fuck_up_growing(obj/machinery/vatgrower/vat) //we love job hazards
vat.visible_message(span_warning("You hear angry buzzing coming from the inside of the vat!"))
for(var/i in 1 to 5)
new /mob/living/basic/bee(get_turf(vat))
diff --git a/code/modules/plumbing/plumbers/vatgrower.dm b/code/modules/research/xenobiology/vatgrowing/vatgrower.dm
similarity index 56%
rename from code/modules/plumbing/plumbers/vatgrower.dm
rename to code/modules/research/xenobiology/vatgrowing/vatgrower.dm
index 1dcfb08e0cc..5b35bd10edf 100644
--- a/code/modules/plumbing/plumbers/vatgrower.dm
+++ b/code/modules/research/xenobiology/vatgrowing/vatgrower.dm
@@ -1,33 +1,52 @@
///Used to make mobs from microbiological samples. Grow grow grow.
-/obj/machinery/plumbing/growing_vat
+/obj/machinery/vatgrower
name = "growing vat"
desc = "Tastes just like the chef's soup."
+ icon = 'icons/obj/science/vatgrowing.dmi'
icon_state = "growing_vat"
- buffer = 300
-
+ density = TRUE
+ pass_flags_self = PASSMACHINE | LETPASSTHROW
+ circuit = /obj/item/circuitboard/machine/vatgrower
+ use_power = NO_POWER_USE
+ ///Soup container reagents
+ var/reagent_volume = 300
+ var/reagent_flags = OPENCONTAINER | DUNKABLE
///List of all microbiological samples in this soup.
var/datum/biological_sample/biological_sample
///If the vat will restart the sample upon completion
var/resampler_active = FALSE
-///Add that sexy demnand component
-/obj/machinery/plumbing/growing_vat/Initialize(mapload, bolt, layer)
+/obj/machinery/vatgrower/Initialize(mapload, bolt, layer)
. = ..()
- AddComponent(/datum/component/plumbing/simple_demand, bolt, layer)
+ create_reagents(reagent_volume, reagent_flags)
-/obj/machinery/plumbing/growing_vat/create_reagents(max_vol, flags)
+ AddComponent(/datum/component/simple_rotation)
+ AddComponent(/datum/component/plumbing/simple_demand)
+
+ var/static/list/hovering_item_typechecks = list(
+ /obj/item/petri_dish = list(
+ SCREENTIP_CONTEXT_LMB = "Add Sample",
+ ),
+ /obj/item/reagent_containers = list(
+ SCREENTIP_CONTEXT_LMB = "Pour Reagents",
+ ),
+ )
+ AddElement(/datum/element/contextual_screentip_item_typechecks, hovering_item_typechecks)
+ AddElement(/datum/element/contextual_screentip_bare_hands, lmb_text = "Toggle Resampler", rmb_text = "Flush Soup")
+
+/obj/machinery/vatgrower/create_reagents(max_vol, flags)
. = ..()
RegisterSignals(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_REM_REAGENT), PROC_REF(on_reagent_change))
RegisterSignal(reagents, COMSIG_QDELETING, PROC_REF(on_reagents_del))
/// Handles properly detaching signal hooks.
-/obj/machinery/plumbing/growing_vat/proc/on_reagents_del(datum/reagents/reagents)
+/obj/machinery/vatgrower/proc/on_reagents_del(datum/reagents/reagents)
SIGNAL_HANDLER
UnregisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_REM_REAGENT, COMSIG_QDELETING))
return NONE
///When we process, we make use of our reagents to try and feed the samples we have.
-/obj/machinery/plumbing/growing_vat/process(seconds_per_tick)
+/obj/machinery/vatgrower/process(seconds_per_tick)
if(!is_operational)
return
if(!biological_sample)
@@ -39,35 +58,71 @@
audible_message(pick(list(span_notice("[src] grumbles!"), span_notice("[src] makes a splashing noise!"), span_notice("[src] sloshes!"))))
use_energy(active_power_usage * seconds_per_tick)
-///Handles the petri dish depositing into the vat.
-/obj/machinery/plumbing/growing_vat/attacked_by(obj/item/I, mob/living/user)
- if(!istype(I, /obj/item/petri_dish))
- return ..()
+/obj/machinery/vatgrower/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ . = ..()
+ if(istype(tool, /obj/item/petri_dish))
+ return deposit_sample(user, tool)
- var/obj/item/petri_dish/petri = I
+/obj/machinery/vatgrower/screwdriver_act(mob/living/user, obj/item/tool)
+ . = ..()
+ if(default_deconstruction_screwdriver(user, icon_state, icon_state, tool))
+ return ITEM_INTERACT_SUCCESS
- if(!petri.sample)
- return ..()
+/obj/machinery/vatgrower/crowbar_act(mob/living/user, obj/item/tool)
+ . = ..()
+ if(default_deconstruction_crowbar(tool))
+ return ITEM_INTERACT_SUCCESS
- if(biological_sample)
- to_chat(user, span_warning("There is already a sample in the vat!"))
+/obj/machinery/vatgrower/wrench_act(mob/living/user, obj/item/tool)
+ . = ..()
+ if(default_unfasten_wrench(user, tool))
+ return ITEM_INTERACT_SUCCESS
+
+/obj/machinery/vatgrower/attack_hand(mob/living/user, list/modifiers)
+ . = ..()
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ if(obj_flags & EMAGGED)
return
- deposit_sample(user, petri)
+ resampler_active = !resampler_active
+ balloon_alert(user, "resampler [resampler_active ? "activated" : "deactivated"]")
+ update_appearance()
+
+/obj/machinery/vatgrower/attack_hand_secondary(mob/user, list/modifiers)
+ . = ..()
+ if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
+ return
+ if(!anchored)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ var/warning = tgui_alert(user, "Are you sure you want to empty the soup container?","Flush soup container?", list("Flush", "Cancel"))
+ if(warning == "Flush" && user.can_perform_action(src))
+ reagents.clear_reagents()
+ if(biological_sample)
+ QDEL_NULL(biological_sample)
+ balloon_alert(user, "container empty")
+ update_appearance()
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
///Creates a clone of the supplied sample and puts it in the vat
-/obj/machinery/plumbing/growing_vat/proc/deposit_sample(mob/user, obj/item/petri_dish/petri)
+/obj/machinery/vatgrower/proc/deposit_sample(mob/user, obj/item/petri_dish/petri)
+ if(!petri.sample)
+ balloon_alert(user, "dish empty")
+ return ITEM_INTERACT_FAILURE
+ if(biological_sample)
+ balloon_alert(user, "already has a sample")
+ return ITEM_INTERACT_FAILURE
biological_sample = new
for(var/datum/micro_organism/m in petri.sample.micro_organisms)
biological_sample.micro_organisms += new m.type()
biological_sample.sample_layers = petri.sample.sample_layers
biological_sample.sample_color = petri.sample.sample_color
- to_chat(user, span_warning("You put some of the sample in the vat!"))
+ balloon_alert(user, "added sample")
playsound(src, 'sound/effects/bubbles.ogg', 50, TRUE)
update_appearance()
RegisterSignal(biological_sample, COMSIG_SAMPLE_GROWTH_COMPLETED, PROC_REF(on_sample_growth_completed))
+ return ITEM_INTERACT_SUCCESS
///Adds text for when there is a sample in the vat
-/obj/machinery/plumbing/growing_vat/examine_more(mob/user)
+/obj/machinery/vatgrower/examine(mob/user)
. = ..()
if(!biological_sample)
return
@@ -76,18 +131,14 @@
var/datum/micro_organism/MO = i
. += MO.get_details(HAS_TRAIT(user, TRAIT_RESEARCH_SCANNER))
-/obj/machinery/plumbing/growing_vat/plunger_act(obj/item/plunger/P, mob/living/user, reinforced)
- . = ..()
- QDEL_NULL(biological_sample)
-
/// Call update icon when reagents change to update the reagent content icons. Eats signal args.
-/obj/machinery/plumbing/growing_vat/proc/on_reagent_change(datum/reagents/holder, ...)
+/obj/machinery/vatgrower/proc/on_reagent_change(datum/reagents/holder, ...)
SIGNAL_HANDLER
update_appearance()
return NONE
///Adds overlays to show the reagent contents
-/obj/machinery/plumbing/growing_vat/update_overlays()
+/obj/machinery/vatgrower/update_overlays()
. = ..()
var/static/image/on_overlay
var/static/image/off_overlay
@@ -113,16 +164,7 @@
var/mutable_appearance/bubbles_overlay = mutable_appearance(icon, "vat_bubbles")
. += bubbles_overlay
-/obj/machinery/plumbing/growing_vat/attack_hand(mob/living/user, list/modifiers)
- . = ..()
- playsound(src, 'sound/machines/click.ogg', 30, TRUE)
- if(obj_flags & EMAGGED)
- return
- resampler_active = !resampler_active
- balloon_alert_to_viewers("resampler [resampler_active ? "activated" : "deactivated"]")
- update_appearance()
-
-/obj/machinery/plumbing/growing_vat/emag_act(mob/user, obj/item/card/emag/emag_card)
+/obj/machinery/vatgrower/emag_act(mob/user, obj/item/card/emag/emag_card)
if(obj_flags & EMAGGED)
return FALSE
obj_flags |= EMAGGED
@@ -131,7 +173,7 @@
flick("growing_vat_emagged", src)
return TRUE
-/obj/machinery/plumbing/growing_vat/proc/on_sample_growth_completed()
+/obj/machinery/vatgrower/proc/on_sample_growth_completed()
SIGNAL_HANDLER
if(resampler_active)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), get_turf(src), 'sound/effects/servostep.ogg', 100, 1), 1.5 SECONDS)
diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm
index dc30f36149b..f76ecb104e7 100644
--- a/code/modules/spells/spell.dm
+++ b/code/modules/spells/spell.dm
@@ -246,7 +246,11 @@
return target // They're just standing around, proceed as normal
if(HAS_TRAIT(cast_loc, TRAIT_CASTABLE_LOC))
- return cast_loc // They're in an atom which allows casting, so redirect the caster to loc
+ if(HAS_TRAIT(cast_loc, TRAIT_SPELLS_TRANSFER_TO_LOC) && ismob(cast_loc.loc))
+ return cast_loc.loc
+ else
+ return cast_loc
+ // They're in an atom which allows casting, so redirect the caster to loc
return null
diff --git a/code/modules/spells/spell_types/jaunt/bloodcrawl.dm b/code/modules/spells/spell_types/jaunt/bloodcrawl.dm
index 00327111e89..f61e48e8896 100644
--- a/code/modules/spells/spell_types/jaunt/bloodcrawl.dm
+++ b/code/modules/spells/spell_types/jaunt/bloodcrawl.dm
@@ -164,6 +164,10 @@
var/consume_sound = 'sound/magic/demon_consume.ogg'
/// consume count (statistics and stuff)
var/consume_count = 0
+ /// Apply damage every 20 seconds if we bloodcrawling
+ var/jaunt_damage_timer
+ /// When demon first appears, it does not take damage while in Jaunt. He also doesn't take damage while he's eating someone.
+ var/resist_jaunt_damage = TRUE
/datum/action/cooldown/spell/jaunt/bloodcrawl/slaughter_demon/try_enter_jaunt(obj/effect/decal/cleanable/blood, mob/living/jaunter)
// Save this before the actual jaunt
@@ -174,12 +178,14 @@
if(!.)
return
+ jaunt_damage_timer = addtimer(CALLBACK(src, PROC_REF(damage_for_lazy_demon), jaunter), 20 SECONDS, TIMER_STOPPABLE)
+
var/turf/jaunt_turf = get_turf(jaunter)
// if we're not pulling anyone, or we can't what we're pulling
- if(!isliving(coming_with))
+ if(!ishuman(coming_with))
return
- var/mob/living/victim = coming_with
+ var/mob/living/carbon/human/victim = coming_with
if(victim.stat == CONSCIOUS)
jaunt_turf.visible_message(
@@ -204,6 +210,28 @@
return TRUE
+/datum/action/cooldown/spell/jaunt/bloodcrawl/slaughter_demon/on_jaunt_exited(obj/effect/dummy/phased_mob/jaunt, mob/living/unjaunter)
+ deltimer(jaunt_damage_timer)
+ resist_jaunt_damage = FALSE
+ return ..()
+
+/**
+ * Apply damage to demon when he using bloodcrawl.
+ * Every 20 SECONDS check if demon still crawling and update timer.
+ */
+/datum/action/cooldown/spell/jaunt/bloodcrawl/slaughter_demon/proc/damage_for_lazy_demon(mob/living/lazy_demon)
+ if(QDELETED(lazy_demon))
+ return
+ if(resist_jaunt_damage)
+ return
+ if(isturf(lazy_demon.loc))
+ return
+ if(isnull(jaunt_damage_timer))
+ return
+ lazy_demon.apply_damage(lazy_demon.maxHealth * 0.05, BRUTE)
+ jaunt_damage_timer = addtimer(CALLBACK(src, PROC_REF(damage_for_lazy_demon), lazy_demon), 20 SECONDS, TIMER_STOPPABLE)
+ to_chat(lazy_demon, span_warning("You feel your flesh dissolving into the sea of blood. You shouldn't stay in Blood Crawl for too long!"))
+
/**
* Consumes the [victim] from the [jaunter], fully healing them
* and calling [proc/on_victim_consumed] if successful.)
@@ -236,12 +264,18 @@
* Called when a victim starts to be consumed.
*/
/datum/action/cooldown/spell/jaunt/bloodcrawl/slaughter_demon/proc/on_victim_start_consume(mob/living/victim, mob/living/jaunter)
+ if(!iscarbon(jaunter))
+ resist_jaunt_damage = TRUE
+ deltimer(jaunt_damage_timer)
to_chat(jaunter, span_danger("You begin to feast on [victim]... You can not move while you are doing this."))
/**
* Called when a victim is successfully consumed.
*/
/datum/action/cooldown/spell/jaunt/bloodcrawl/slaughter_demon/proc/on_victim_consumed(mob/living/victim, mob/living/jaunter)
+ if(!iscarbon(jaunter))
+ resist_jaunt_damage = FALSE
+ jaunt_damage_timer = addtimer(CALLBACK(src, PROC_REF(damage_for_lazy_demon), jaunter), 20 SECONDS, TIMER_STOPPABLE)
to_chat(jaunter, span_danger("You devour [victim]. Your health is fully restored."))
qdel(victim)
diff --git a/code/modules/spells/spell_types/pointed/_pointed.dm b/code/modules/spells/spell_types/pointed/_pointed.dm
index 04c3ed47944..edf3dab2179 100644
--- a/code/modules/spells/spell_types/pointed/_pointed.dm
+++ b/code/modules/spells/spell_types/pointed/_pointed.dm
@@ -16,7 +16,7 @@
var/deactive_msg
/// The casting range of our spell
var/cast_range = 7
- /// Variable dictating if the spell will use turf based aim assist
+ /// If aim asisst is used. Disable to disable
var/aim_assist = TRUE
/datum/action/cooldown/spell/pointed/New(Target)
@@ -65,17 +65,18 @@
return TRUE
/datum/action/cooldown/spell/pointed/InterceptClickOn(mob/living/caller, params, atom/target)
-
var/atom/aim_assist_target
- if(aim_assist && isturf(target))
- // Find any human in the list. We aren't picky, it's aim assist after all
- aim_assist_target = locate(/mob/living/carbon/human) in target
- if(!aim_assist_target)
- // If we didn't find a human, we settle for any living at all
- aim_assist_target = locate(/mob/living) in target
-
+ if(aim_assist)
+ aim_assist_target = aim_assist(caller, target)
return ..(caller, params, aim_assist_target || target)
+/datum/action/cooldown/spell/pointed/proc/aim_assist(mob/living/caller, atom/target)
+ if(!isturf(target))
+ return
+
+ // Find any human, or if that fails, any living target
+ return locate(/mob/living/carbon/human) in target || locate(/mob/living) in target
+
/datum/action/cooldown/spell/pointed/is_valid_target(atom/cast_on)
if(cast_on == owner)
to_chat(owner, span_warning("You cannot cast [src] on yourself!"))
diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm
index a4b1287aa53..60799f546c8 100644
--- a/code/modules/station_goals/bsa.dm
+++ b/code/modules/station_goals/bsa.dm
@@ -52,13 +52,10 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE)
. = ..()
AddComponent(/datum/component/simple_rotation)
-/obj/machinery/bsa/back/multitool_act(mob/living/user, obj/item/I)
- if(!multitool_check_buffer(user, I)) //make sure it has a data buffer
- return
- var/obj/item/multitool/M = I
+/obj/machinery/bsa/back/multitool_act(mob/living/user, obj/item/multitool/M)
M.set_buffer(src)
balloon_alert(user, "saved to multitool buffer")
- return TRUE
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/bsa/front
name = "Bluespace Artillery Bore"
@@ -69,13 +66,10 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE)
. = ..()
AddComponent(/datum/component/simple_rotation)
-/obj/machinery/bsa/front/multitool_act(mob/living/user, obj/item/I)
- if(!multitool_check_buffer(user, I)) //make sure it has a data buffer
- return
- var/obj/item/multitool/M = I
+/obj/machinery/bsa/front/multitool_act(mob/living/user, obj/item/multitool/M)
M.set_buffer(src)
balloon_alert(user, "saved to multitool buffer")
- return TRUE
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/bsa/middle
name = "Bluespace Artillery Fusor"
@@ -88,22 +82,19 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE)
. = ..()
AddComponent(/datum/component/simple_rotation)
-/obj/machinery/bsa/middle/multitool_act(mob/living/user, obj/item/I)
- if(!multitool_check_buffer(user, I))
- return
- var/obj/item/multitool/M = I
- if(M.buffer)
- if(istype(M.buffer, /obj/machinery/bsa/back))
- back_ref = WEAKREF(M.buffer)
- to_chat(user, span_notice("You link [src] with [M.buffer]."))
- M.set_buffer(null)
- else if(istype(M.buffer, /obj/machinery/bsa/front))
- front_ref = WEAKREF(M.buffer)
- to_chat(user, span_notice("You link [src] with [M.buffer]."))
- M.set_buffer(null)
- else
- to_chat(user, span_warning("[I]'s data buffer is empty!"))
- return TRUE
+/obj/machinery/bsa/middle/multitool_act(mob/living/user, obj/item/multitool/tool)
+ . = NONE
+
+ if(istype(tool.buffer, /obj/machinery/bsa/back))
+ back_ref = WEAKREF(tool.buffer)
+ to_chat(user, span_notice("You link [src] with [tool.buffer]."))
+ tool.set_buffer(null)
+ return ITEM_INTERACT_SUCCESS
+ else if(istype(tool.buffer, /obj/machinery/bsa/front))
+ front_ref = WEAKREF(tool.buffer)
+ to_chat(user, span_notice("You link [src] with [tool.buffer]."))
+ tool.set_buffer(null)
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/bsa/middle/proc/check_completion()
var/obj/machinery/bsa/front/front = front_ref?.resolve()
diff --git a/code/modules/surgery/blood_filter.dm b/code/modules/surgery/blood_filter.dm
index 5634f04d830..14be3c09a6d 100644
--- a/code/modules/surgery/blood_filter.dm
+++ b/code/modules/surgery/blood_filter.dm
@@ -40,7 +40,7 @@
*/
/datum/surgery_step/filter_blood/proc/has_filterable_chems(mob/living/carbon/target, obj/item/blood_filter/bloodfilter)
if(!length(target.reagents?.reagent_list))
- bloodfilter.audible_message(span_notice("The [bloodfilter] pings as it reports no chemicals detected in [target]'s blood."))
+ bloodfilter.audible_message(span_notice("[bloodfilter] pings as it reports no chemicals detected in [target]'s blood."))
playsound(get_turf(target), 'sound/machines/ping.ogg', 75, TRUE, falloff_exponent = 12, falloff_distance = 1)
return FALSE
@@ -58,7 +58,7 @@
implements = list(/obj/item/blood_filter = 95)
repeatable = TRUE
time = 2.5 SECONDS
- success_sound = 'sound/machines/fan_loop.ogg'
+ success_sound = 'sound/machines/card_slide.ogg'
/datum/surgery_step/filter_blood/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
display_pain(target, "You feel a throbbing pain in your chest!")
@@ -68,7 +68,7 @@
if(target.reagents?.total_volume)
for(var/datum/reagent/chem as anything in target.reagents.reagent_list)
if(!length(bloodfilter.whitelist) || (chem.type in bloodfilter.whitelist))
- target.reagents.remove_reagent(chem.type, min(chem.volume * 0.22, 10))
+ target.reagents.remove_reagent(chem.type, min(round(chem.volume * 0.22, 0.2), 10))
target.adjustToxLoss(-2, forced = TRUE) //BUBBER EDIT - Filtration fixes toxins
display_results(
user,
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index 0e8ae76f6e6..3d31d8f91ed 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -355,7 +355,7 @@
check_list += "\t [span_boldwarning("Your [name] is suffering [wound.a_or_from] [wound.get_topic_name(owner)]!!")]" // SKYRAT EDIT - Medical overhaul-ish
for(var/obj/item/embedded_thing in embedded_objects)
- var/stuck_word = embedded_thing.isEmbedHarmless() ? "stuck" : "embedded"
+ var/stuck_word = embedded_thing.is_embed_harmless() ? "stuck" : "embedded"
check_list += "\t There is \a [embedded_thing] [stuck_word] in your [name]!"
/obj/item/bodypart/blob_act()
@@ -1189,15 +1189,15 @@
if(embed in embedded_objects) // go away
return
// We don't need to do anything with projectile embedding, because it will never reach this point
- RegisterSignal(embed, COMSIG_ITEM_EMBEDDING_UPDATE, PROC_REF(embedded_object_changed))
embedded_objects += embed
+ RegisterSignal(embed, COMSIG_ITEM_EMBEDDING_UPDATE, PROC_REF(embedded_object_changed))
refresh_bleed_rate()
/// INTERNAL PROC, DO NOT USE
/// Cleans up any attachment we have to the embedded object, removes it from our list
/obj/item/bodypart/proc/_unembed_object(obj/item/unembed)
- UnregisterSignal(unembed, COMSIG_ITEM_EMBEDDING_UPDATE)
embedded_objects -= unembed
+ UnregisterSignal(unembed, COMSIG_ITEM_EMBEDDING_UPDATE)
refresh_bleed_rate()
/obj/item/bodypart/proc/embedded_object_changed(obj/item/embedded_source)
@@ -1250,7 +1250,7 @@
cached_bleed_rate += 0.5
for(var/obj/item/embeddies in embedded_objects)
- if(!embeddies.isEmbedHarmless())
+ if(!embeddies.is_embed_harmless())
cached_bleed_rate += 0.25
for(var/datum/wound/iter_wound as anything in wounds)
diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm
index fb0647d0fb5..a0955af4e46 100644
--- a/code/modules/surgery/bodyparts/helpers.dm
+++ b/code/modules/surgery/bodyparts/helpers.dm
@@ -48,6 +48,15 @@
which_hand = BODY_ZONE_PRECISE_R_HAND
return get_bodypart(check_zone(which_hand))
+/// Gets the inactive hand of the mob. Returns FALSE on non-carbons, otherwise returns the /obj/item/bodypart.
+/mob/proc/get_inactive_hand()
+ return null
+
+/mob/living/carbon/get_inactive_hand()
+ var/which_hand = BODY_ZONE_PRECISE_R_HAND
+ if(!(active_hand_index % RIGHT_HANDS))
+ which_hand = BODY_ZONE_PRECISE_L_HAND
+ return get_bodypart(check_zone(which_hand))
/mob/proc/has_left_hand(check_disabled = TRUE)
return TRUE
@@ -130,7 +139,7 @@
/mob/living/carbon/proc/has_embedded_objects(include_harmless=FALSE)
for(var/obj/item/bodypart/bodypart as anything in bodyparts)
for(var/obj/item/embedded in bodypart.embedded_objects)
- if(!include_harmless && embedded.isEmbedHarmless())
+ if(!include_harmless && embedded.is_embed_harmless())
continue
return TRUE
diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm
index 17444590cf2..a555548e432 100644
--- a/code/modules/surgery/surgery.dm
+++ b/code/modules/surgery/surgery.dm
@@ -124,10 +124,6 @@
if(isnull(step))
return FALSE
var/obj/item/tool = user.get_active_held_item()
- if(istype(tool, /obj/item/borg/cyborg_omnitool)) //catches borg surgeries
- var/obj/item/borg/cyborg_omnitool/toolarm = tool
- if(toolarm.selected)
- tool = toolarm.selected
if(step.try_op(user, target, user.zone_selected, tool, src, try_to_fail))
return TRUE
if(tool && tool.item_flags & SURGICAL_TOOL) //Just because you used the wrong tool it doesn't mean you meant to whack the patient with it
diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm
index 6c3ee9c0147..2bd8f485887 100644
--- a/code/modules/surgery/tools.dm
+++ b/code/modules/surgery/tools.dm
@@ -286,8 +286,8 @@
//saws are very accurate and fast at butchering
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/chainsaw)
- AddComponent(
- /datum/component/slapcrafting,\
+ AddElement(
+ /datum/element/slapcrafting,\
slapcraft_recipes = slapcraft_recipe_list,\
)
@@ -316,6 +316,7 @@
attack_verb_continuous = list("slaps")
attack_verb_simple = list("slap")
interaction_flags_atom = parent_type::interaction_flags_atom | INTERACT_ATOM_IGNORE_MOBILITY
+ tool_behaviour = TOOL_DRAPES
/obj/item/surgical_drapes/Initialize(mapload)
. = ..()
diff --git a/code/modules/unit_tests/cyborg_tool.dm b/code/modules/unit_tests/cyborg_tool.dm
index 711f0948aee..39aed619ec5 100644
--- a/code/modules/unit_tests/cyborg_tool.dm
+++ b/code/modules/unit_tests/cyborg_tool.dm
@@ -5,7 +5,7 @@
/datum/unit_test/cyborg_tool/Run()
var/mob/living/carbon/human/consistent/not_a_borg = allocate(__IMPLIED_TYPE__)
var/obj/item/borg/cyborg_omnitool/engineering/tool = allocate(__IMPLIED_TYPE__)
- tool.selected = allocate(/obj/item/wrench/cyborg)
+ tool.tool_behaviour = TOOL_WRENCH
not_a_borg.put_in_active_hand(tool)
diff --git a/code/modules/unit_tests/lootpanel.dm b/code/modules/unit_tests/lootpanel.dm
index 1903c22d546..41374a1950c 100644
--- a/code/modules/unit_tests/lootpanel.dm
+++ b/code/modules/unit_tests/lootpanel.dm
@@ -2,8 +2,8 @@
abstract_type = /datum/unit_test/lootpanel
/datum/unit_test/lootpanel/contents/Run()
- var/datum/client_interface/mock_client = new()
- var/datum/lootpanel/panel = new(mock_client)
+ var/datum/client_interface/mock_client = allocate(/datum/client_interface)
+ var/datum/lootpanel/panel = allocate(/datum/lootpanel, mock_client)
var/mob/living/carbon/human/labrat = allocate(/mob/living/carbon/human/consistent)
mock_client.mob = labrat
var/turf/one_over = locate(run_loc_floor_bottom_left.x + 1, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z)
@@ -32,4 +32,3 @@
TEST_ASSERT_EQUAL(length(panel.contents), 2, "Panel shouldnt dupe searchables if reopened")
mock_client.mob = null
-
diff --git a/code/modules/unit_tests/outfit_sanity.dm b/code/modules/unit_tests/outfit_sanity.dm
index 554d226ed2e..36fc4570540 100644
--- a/code/modules/unit_tests/outfit_sanity.dm
+++ b/code/modules/unit_tests/outfit_sanity.dm
@@ -16,7 +16,7 @@
uniform = /obj/item/clothing/under/suit/tuxedo
glasses = /obj/item/clothing/glasses/sunglasses
- mask = /obj/item/clothing/mask/cigarette/cigar/havana
+ mask = /obj/item/cigarette/cigar/havana
shoes = /obj/item/clothing/shoes/laceup
l_hand = /obj/item/stack/spacecash/c1000
r_hand = /obj/item/stack/spacecash/c1000
@@ -25,19 +25,19 @@
/datum/outfit/duffel_user
name = "Mr. Runtime"
back = /obj/item/storage/backpack/duffelbag
- backpack_contents = list(/obj/item/clothing/mask/cigarette/cigar/havana)
+ backpack_contents = list(/obj/item/cigarette/cigar/havana)
/// Satchels too
/datum/outfit/stachel_user
name = "Mr. Runtime"
back = /obj/item/storage/backpack/satchel
- backpack_contents = list(/obj/item/clothing/mask/cigarette/cigar/havana)
+ backpack_contents = list(/obj/item/cigarette/cigar/havana)
/// And just in case we'll check backpacks
/datum/outfit/backpack_user
name = "Mr. Runtime"
back = /obj/item/storage/backpack
- backpack_contents = list(/obj/item/clothing/mask/cigarette/cigar/havana)
+ backpack_contents = list(/obj/item/cigarette/cigar/havana)
/datum/unit_test/outfit_sanity/Run()
var/datum/outfit/prototype_outfit = /datum/outfit
diff --git a/code/modules/unit_tests/screenshots/screenshot_antag_icons_syndicateinfiltrator.png b/code/modules/unit_tests/screenshots/screenshot_antag_icons_syndicateinfiltrator.png
index 0a9a5ef08e4..4c9212509d2 100644
Binary files a/code/modules/unit_tests/screenshots/screenshot_antag_icons_syndicateinfiltrator.png and b/code/modules/unit_tests/screenshots/screenshot_antag_icons_syndicateinfiltrator.png differ
diff --git a/code/modules/unit_tests/screenshots/screenshot_antag_icons_syndicatesleeperagent.png b/code/modules/unit_tests/screenshots/screenshot_antag_icons_syndicatesleeperagent.png
index 0a9a5ef08e4..4c9212509d2 100644
Binary files a/code/modules/unit_tests/screenshots/screenshot_antag_icons_syndicatesleeperagent.png and b/code/modules/unit_tests/screenshots/screenshot_antag_icons_syndicatesleeperagent.png differ
diff --git a/code/modules/unit_tests/screenshots/screenshot_antag_icons_traitor.png b/code/modules/unit_tests/screenshots/screenshot_antag_icons_traitor.png
index 0a9a5ef08e4..4c9212509d2 100644
Binary files a/code/modules/unit_tests/screenshots/screenshot_antag_icons_traitor.png and b/code/modules/unit_tests/screenshots/screenshot_antag_icons_traitor.png differ
diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_lizard_ashwalker.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_lizard_ashwalker.png
index bfc70e7aab8..6fa25ad9159 100644
Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_lizard_ashwalker.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_lizard_ashwalker.png differ
diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_teshari.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_teshari.png
index 1f888e57c58..2bf863348c0 100644
Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_teshari.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_teshari.png differ
diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_vox.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_vox.png
index ce404cc1deb..dbee83aaba7 100644
Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_vox.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_vox.png differ
diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_vox_primalis.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_vox_primalis.png
index 163347515fe..73dff7a6448 100644
Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_vox_primalis.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_vox_primalis.png differ
diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_xeno.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_xeno.png
index d93785e8385..39f3200caa4 100644
Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_xeno.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_xeno.png differ
diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm
index 34dd7dfeaf4..f1d450325c8 100644
--- a/code/modules/unit_tests/unit_test.dm
+++ b/code/modules/unit_tests/unit_test.dm
@@ -286,6 +286,10 @@ GLOBAL_VAR_INIT(focused_tests, focused_tests())
returnable_list += typesof(/obj/effect/anomaly/grav/high)
//See above
returnable_list += typesof(/obj/effect/timestop)
+ //Sparks can ignite a number of things, causing a fire to burn the floor away. Only you can prevent CI fires
+ returnable_list += typesof(/obj/effect/particle_effect/sparks)
+ //See above - These are one of those things.
+ returnable_list += typesof(/obj/effect/decal/cleanable/fuel_pool)
//Invoke async in init, skippppp
returnable_list += typesof(/mob/living/silicon/robot/model)
//This lad also sleeps
diff --git a/code/modules/uplink/uplink_items/job.dm b/code/modules/uplink/uplink_items/job.dm
index 2610f88e9e6..e8fea4df2d6 100644
--- a/code/modules/uplink/uplink_items/job.dm
+++ b/code/modules/uplink/uplink_items/job.dm
@@ -393,3 +393,4 @@ BUBBER REMOVE END*/
restricted = TRUE
refundable = FALSE
purchasable_from = parent_type::purchasable_from & ~UPLINK_SPY
+
diff --git a/code/modules/vehicles/_vehicle.dm b/code/modules/vehicles/_vehicle.dm
index 5c757413e0e..155ef2b4570 100644
--- a/code/modules/vehicles/_vehicle.dm
+++ b/code/modules/vehicles/_vehicle.dm
@@ -30,7 +30,7 @@
var/canmove = TRUE
var/list/autogrant_actions_passenger //plain list of typepaths
var/list/autogrant_actions_controller //assoc list "[bitflag]" = list(typepaths)
- var/list/mob/occupant_actions //assoc list mob = list(type = action datum assigned to mob)
+ var/list/list/datum/action/occupant_actions //assoc list mob = list(type = action datum assigned to mob)
///This vehicle will follow us when we move (like atrailer duh)
var/obj/vehicle/trailer
var/are_legs_exposed = FALSE
diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm
index 32aa4f1f7a3..d35df312663 100644
--- a/code/modules/vehicles/mecha/_mecha.dm
+++ b/code/modules/vehicles/mecha/_mecha.dm
@@ -137,6 +137,15 @@
var/stepsound = 'sound/mecha/mechstep.ogg'
///Sound played when the mech walks
var/turnsound = 'sound/mecha/mechturn.ogg'
+ ///Sounds for types of melee attack
+ var/brute_attack_sound = 'sound/weapons/punch4.ogg'
+ var/burn_attack_sound = 'sound/items/welder.ogg'
+ var/tox_attack_sound = 'sound/effects/spray2.ogg'
+ ///Sound on wall destroying
+ var/destroy_wall_sound = 'sound/effects/meteorimpact.ogg'
+
+ ///Melee attack verb
+ var/list/attack_verbs = list("hit", "hits", "hitting")
///Cooldown duration between melee punches
var/melee_cooldown = CLICK_CD_SLOW
@@ -151,6 +160,8 @@
var/is_currently_ejecting = FALSE
///Safety for weapons. Won't fire if enabled, and toggled by middle click.
var/weapons_safety = FALSE
+ ///Don't play standard sound when set safety if TRUE.
+ var/safety_sound_custom = FALSE
var/datum/effect_system/fluid_spread/smoke/smoke_system
@@ -365,7 +376,8 @@
*/
/obj/vehicle/sealed/mecha/proc/set_safety(mob/user)
weapons_safety = !weapons_safety
- SEND_SOUND(user, sound('sound/machines/beep.ogg', volume = 25))
+ if(!safety_sound_custom)
+ SEND_SOUND(user, sound('sound/machines/beep.ogg', volume = 25))
balloon_alert(user, "equipment [weapons_safety ? "safe" : "ready"]")
set_mouse_pointer()
SEND_SIGNAL(src, COMSIG_MECH_SAFETIES_TOGGLE, user, weapons_safety)
@@ -905,3 +917,9 @@
act.button_icon_state = "mech_lights_off"
balloon_alert(occupant, "lights [mecha_flags & LIGHTS_ON ? "on":"off"]")
act.build_all_button_icons()
+
+/obj/vehicle/sealed/mecha/proc/melee_attack_effect(mob/living/victim, heavy)
+ if(heavy)
+ victim.Unconscious(2 SECONDS)
+ else
+ victim.Knockdown(4 SECONDS)
diff --git a/code/modules/vehicles/mecha/combat/justice.dm b/code/modules/vehicles/mecha/combat/justice.dm
new file mode 100644
index 00000000000..babdb8af17b
--- /dev/null
+++ b/code/modules/vehicles/mecha/combat/justice.dm
@@ -0,0 +1,535 @@
+#define DISMEMBER_CHANCE_HIGH 50
+#define DISMEMBER_CHANCE_LOW 25
+
+#define MOVEDELAY_ANGRY 4.5
+#define MOVEDELAY_SAFETY 2.5
+
+/obj/vehicle/sealed/mecha/justice
+ name = "\improper Justice"
+ desc = "Black and red syndicate mech designed for execution orders. \
+ For safety reasons, the syndicate advises against standing too close."
+ icon_state = "justice"
+ base_icon_state = "justice"
+ movedelay = MOVEDELAY_SAFETY // fast
+ max_integrity = 200 // but weak
+ accesses = list(ACCESS_SYNDICATE)
+ armor_type = /datum/armor/mecha_justice
+ max_temperature = 40000
+ force = 60 // dangerous in melee
+ damtype = BRUTE
+ destruction_sleep_duration = 10
+ exit_delay = 10
+ wreckage = /obj/structure/mecha_wreckage/justice
+ mech_type = EXOSUIT_MODULE_JUSTICE
+ resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ mecha_flags = ID_LOCK_ON | QUIET_STEPS | QUIET_TURNS | CAN_STRAFE | HAS_LIGHTS | MMI_COMPATIBLE | IS_ENCLOSED
+ destroy_wall_sound = 'sound/mecha/mech_blade_break_wall.ogg'
+ brute_attack_sound = 'sound/mecha/mech_blade_attack.ogg'
+ attack_verbs = list("cut", "cuts", "cutting")
+ weapons_safety = TRUE
+ safety_sound_custom = TRUE
+ max_equip_by_category = list(
+ MECHA_L_ARM = null,
+ MECHA_R_ARM = null,
+ MECHA_UTILITY = 3,
+ MECHA_POWER = 1,
+ MECHA_ARMOR = 2,
+ )
+ step_energy_drain = 2
+
+/datum/armor/mecha_justice
+ melee = 30
+ bullet = 20
+ laser = 20
+ energy = 30
+ fire = 100
+ acid = 100
+
+/obj/vehicle/sealed/mecha/justice/Initialize(mapload, built_manually)
+ . = ..()
+ RegisterSignal(src, COMSIG_MECHA_MELEE_CLICK, PROC_REF(justice_fatality)) //We do not hit those who are in crit or stun. We are finishing them.
+ transform = transform.Scale(1.04, 1.04)
+
+/obj/vehicle/sealed/mecha/justice/generate_actions()
+ . = ..()
+ initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/invisibility)
+ initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/charge_attack)
+
+/obj/vehicle/sealed/mecha/justice/update_icon_state()
+ . = ..()
+ if(LAZYLEN(occupants))
+ icon_state = weapons_safety ? "[base_icon_state]" : "[base_icon_state]-angry"
+ if(!has_gravity())
+ icon_state = "[icon_state]-fly"
+
+/obj/vehicle/sealed/mecha/justice/set_safety(mob/user)
+ . = ..()
+ if(weapons_safety)
+ movedelay = MOVEDELAY_SAFETY
+ else
+ movedelay = MOVEDELAY_ANGRY
+
+ playsound(src, 'sound/mecha/mech_blade_safty.ogg', 75, FALSE) //everyone need to hear this sound
+
+ update_appearance(UPDATE_ICON_STATE)
+
+/obj/vehicle/sealed/mecha/justice/Move(newloc, dir)
+ if(HAS_TRAIT(src, TRAIT_IMMOBILIZED))
+ return
+ . = ..()
+ update_appearance(UPDATE_ICON_STATE)
+
+/// Says 1 of 3 epic phrases before attacking and make a finishing blow to targets in stun or crit after 1 SECOND.
+/obj/vehicle/sealed/mecha/justice/proc/justice_fatality(datum/source, mob/living/pilot, atom/target, on_cooldown, is_adjacent)
+ SIGNAL_HANDLER
+
+ if(!ishuman(target))
+ return FALSE
+ var/mob/living/carbon/human/live_or_dead = target
+ if(live_or_dead.stat < UNCONSCIOUS && live_or_dead.getStaminaLoss() < 100)
+ return FALSE
+ var/obj/item/bodypart/check_head = live_or_dead.get_bodypart(BODY_ZONE_HEAD)
+ if(!check_head)
+ return FALSE
+ INVOKE_ASYNC(src, PROC_REF(finish_him), src, pilot, live_or_dead)
+ return TRUE
+
+/**
+ * ## finish_him
+ *
+ * Target's head is cut off (if it has one)
+ * Attack from invisibility and charged attack have higher priority.
+ * Arguments:
+ * * finisher - Mech pilot who makes an attack.
+ * * him - Target at which the mech makes an attack.
+ */
+/obj/vehicle/sealed/mecha/justice/proc/finish_him(obj/vehicle/sealed/mecha/my_mech, mob/finisher, mob/living/him)
+ say(pick("Take my Justice-Slash!", "A falling leaf...", "Justice is quite a lonely path"), forced = "Justice Mech")
+ playsound(src, 'sound/mecha/mech_stealth_pre_attack.ogg', 75, FALSE)
+ if(!do_after(finisher, 1 SECONDS, him))
+ return
+ if(QDELETED(finisher))
+ return
+ if(QDELETED(him))
+ return
+ if(QDELETED(my_mech))
+ return
+ if(!LAZYLEN(my_mech.occupants))
+ return
+ var/turf/finish_turf = get_step(him, get_dir(my_mech, him))
+ var/turf/for_line_turf = get_turf(my_mech)
+ var/obj/item/bodypart/in_your_head = him.get_bodypart(BODY_ZONE_HEAD)
+ in_your_head?.dismember(BRUTE)
+ playsound(src, brute_attack_sound, 75, FALSE)
+ for_line_turf.Beam(src, icon_state = "mech_charge", time = 8)
+ forceMove(finish_turf)
+
+/obj/vehicle/sealed/mecha/justice/melee_attack_effect(mob/living/victim, heavy)
+ if(!heavy)
+ victim.Knockdown(4 SECONDS)
+ return
+ if(!prob(DISMEMBER_CHANCE_HIGH))
+ return
+ var/obj/item/bodypart/cut_bodypart = victim.get_bodypart(pick(BODY_ZONE_R_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_ARM, BODY_ZONE_L_LEG))
+ cut_bodypart?.dismember(BRUTE)
+
+
+/obj/vehicle/sealed/mecha/justice/mob_exit(mob/M, silent, randomstep, forced)
+ . = ..()
+ if(alpha == 255)
+ return
+ animate(src, alpha = 255, time = 0.5 SECONDS)
+ playsound(src, 'sound/mecha/mech_stealth_effect.ogg' , 75, FALSE)
+
+/obj/vehicle/sealed/mecha/justice/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration)
+ if(LAZYLEN(occupants))
+ if(prob(60))
+ new /obj/effect/temp_visual/mech_sparks(get_turf(src))
+ playsound(src, 'sound/mecha/mech_stealth_effect.ogg' , 75, FALSE)
+ return
+ return ..()
+
+/datum/action/vehicle/sealed/mecha/invisibility
+ name = "Invisibility"
+ button_icon_state = "mech_stealth_off"
+ /// Is invisibility activated.
+ var/on = FALSE
+ /// Recharge check.
+ var/charge = TRUE
+ /// Varset for invisibility timer
+ var/invisibility_timer
+ /// Energy cost to become invisibile
+ var/energy_cost = 200
+ /// Aoe pre attack sound.
+ var/stealth_pre_attack_sound = 'sound/mecha/mech_stealth_pre_attack.ogg'
+ /// Aoe attack sound.
+ var/stealth_attack_sound = 'sound/mecha/mech_stealth_attack.ogg'
+
+/datum/action/vehicle/sealed/mecha/invisibility/set_chassis(passed_chassis)
+ . = ..()
+ RegisterSignal(chassis, COMSIG_MECH_SAFETIES_TOGGLE, PROC_REF(on_toggle_safety))
+
+/// update button icon when toggle safety.
+/datum/action/vehicle/sealed/mecha/invisibility/proc/on_toggle_safety()
+ SIGNAL_HANDLER
+
+ build_all_button_icons(UPDATE_BUTTON_STATUS)
+
+/datum/action/vehicle/sealed/mecha/invisibility/Trigger(trigger_flags)
+ . = ..()
+ if(!.)
+ return
+ on = !on
+ if(on)
+ invisibility_on()
+ else
+ invisibility_off()
+
+/datum/action/vehicle/sealed/mecha/invisibility/IsAvailable(feedback)
+ . = ..()
+ if(!.)
+ return FALSE
+ if(!chassis.has_charge(energy_cost))
+ if(feedback)
+ owner.balloon_alert(owner, "not enough energy!")
+ return FALSE
+ if(chassis.weapons_safety)
+ if(feedback)
+ owner.balloon_alert(owner, "safety is on!")
+ return FALSE
+ if(!charge)
+ if(feedback)
+ owner.balloon_alert(owner, "recharging!")
+ return FALSE
+
+ return TRUE
+
+///Called when invisibility activated.
+/datum/action/vehicle/sealed/mecha/invisibility/proc/invisibility_on()
+ new /obj/effect/temp_visual/mech_sparks(get_turf(chassis))
+ playsound(chassis, 'sound/mecha/mech_stealth_effect.ogg' , 75, FALSE)
+ check_charge_attack()
+ animate(chassis, alpha = 0, time = 0.5 SECONDS)
+ button_icon_state = "mech_stealth_on"
+ invisibility_timer = addtimer(CALLBACK(src, PROC_REF(end_stealth)), 20 SECONDS)
+ RegisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK, PROC_REF(stealth_attack_aoe))
+ RegisterSignal(chassis, COMSIG_MOVABLE_BUMP, PROC_REF(bumb_on))
+ RegisterSignal(chassis, COMSIG_ATOM_BUMPED, PROC_REF(bumbed_on))
+ RegisterSignal(chassis, COMSIG_ATOM_TAKE_DAMAGE, PROC_REF(take_damage))
+ chassis.use_energy(energy_cost)
+ build_all_button_icons()
+
+///Called when invisibility deactivated.
+/datum/action/vehicle/sealed/mecha/invisibility/proc/invisibility_off()
+ new /obj/effect/temp_visual/mech_sparks(get_turf(chassis))
+ playsound(chassis, 'sound/mecha/mech_stealth_effect.ogg' , 75, FALSE)
+ invisibility_timer = null
+ charge = FALSE
+ addtimer(CALLBACK(src, PROC_REF(charge)), 5 SECONDS)
+ button_icon_state = "mech_stealth_cooldown"
+ animate(chassis, alpha = 255, time = 0.5 SECONDS)
+ UnregisterSignal(chassis, list(
+ COMSIG_MECHA_MELEE_CLICK,
+ COMSIG_MOVABLE_BUMP,
+ COMSIG_ATOM_BUMPED,
+ COMSIG_ATOM_TAKE_DAMAGE
+ ))
+ build_all_button_icons()
+
+///Check if mech use charge attack and deactivate it when we activate invisibility.
+/datum/action/vehicle/sealed/mecha/invisibility/proc/check_charge_attack()
+ for(var/mob/living/occupant in chassis.occupants)
+ var/datum/action/vehicle/sealed/mecha/charge_attack/charge_action = LAZYACCESSASSOC(chassis.occupant_actions, occupant, /datum/action/vehicle/sealed/mecha/charge_attack)
+ if(charge_action?.on)
+ charge_action.on = !on
+ charge_action.charge_attack_off()
+/**
+ * ## end_stealth
+ *
+ * Called when mech runs out of invisibility time.
+ */
+/datum/action/vehicle/sealed/mecha/invisibility/proc/end_stealth()
+ make_visible()
+
+/**
+ * ## bumb_on
+ *
+ * Called when mech bumb on somthing. If is living somthing shutdown mech invisibility.
+ */
+/datum/action/vehicle/sealed/mecha/invisibility/proc/bumb_on(obj/vehicle/sealed/mecha/our_mech, atom/obstacle)
+ SIGNAL_HANDLER
+
+ if(!isliving(obstacle))
+ return
+ make_visible()
+
+/**
+ * ## bumbed_on
+ *
+ * Called when somthing bumbed on mech. If is living somthing shutdown mech invisibility.
+ */
+/datum/action/vehicle/sealed/mecha/invisibility/proc/bumbed_on(obj/vehicle/sealed/mecha/our_mech, atom/movable/bumped_atom)
+ SIGNAL_HANDLER
+
+ if(!isliving(bumped_atom))
+ return
+ make_visible()
+
+/**
+ * ## take_damage
+ *
+ * Called when mech take damage. Shutdown mech invisibility.
+ */
+/datum/action/vehicle/sealed/mecha/invisibility/proc/take_damage(obj/vehicle/sealed/mecha/our_mech)
+ SIGNAL_HANDLER
+
+ make_visible()
+
+/**
+ * ## make_visible
+ *
+ * Called when somthing force invisibility shutdown.
+ */
+/datum/action/vehicle/sealed/mecha/invisibility/proc/make_visible()
+ if(!on)
+ return
+ on = !on
+ invisibility_off()
+
+/**
+ * Proc makes an AOE attack after 1 SECOND.
+ * Called by the mech pilot when he is in stealth mode and wants to attack.
+ * During this, mech cannot move.
+*/
+/datum/action/vehicle/sealed/mecha/invisibility/proc/stealth_attack_aoe(datum/source, mob/living/pilot, atom/target, on_cooldown, is_adjacent)
+ SIGNAL_HANDLER
+
+ if(!charge)
+ return FALSE
+ if(chassis.alpha != 0)
+ UnregisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK)
+ return FALSE
+ UnregisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK)
+ new /obj/effect/temp_visual/mech_attack_aoe_charge(get_turf(chassis))
+ ADD_TRAIT(chassis, TRAIT_IMMOBILIZED, REF(src))
+ playsound(chassis, stealth_pre_attack_sound, 75, FALSE)
+ addtimer(CALLBACK(src, PROC_REF(attack_in_aoe), pilot), 1 SECONDS)
+ return TRUE
+
+/**
+ * ## attack_in_aoe
+ *
+ * Brings mech out of invisibility.
+ * Deal everyone in range 3x3 35 damage and 25 chanse to cut off limb.
+ * Arguments:
+ * * pilot - occupant inside mech.
+ */
+/datum/action/vehicle/sealed/mecha/invisibility/proc/attack_in_aoe(mob/living/pilot)
+ invisibility_off()
+ new /obj/effect/temp_visual/mech_attack_aoe_attack(get_turf(chassis))
+ for(var/mob/living/something_living in range(1, get_turf(chassis)))
+ if(something_living.stat >= UNCONSCIOUS)
+ continue
+ if(something_living.getStaminaLoss() >= 100)
+ continue
+ if(something_living == pilot)
+ continue
+ if(prob(DISMEMBER_CHANCE_LOW))
+ var/obj/item/bodypart/cut_bodypart = something_living.get_bodypart(pick(BODY_ZONE_R_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_ARM, BODY_ZONE_L_LEG))
+ cut_bodypart?.dismember(BRUTE)
+ something_living.apply_damage(35, BRUTE)
+ playsound(chassis, stealth_attack_sound, 75, FALSE)
+ REMOVE_TRAIT(chassis, TRAIT_IMMOBILIZED, REF(src))
+ on = !on
+ charge = FALSE
+ button_icon_state = "mech_stealth_cooldown"
+ build_all_button_icons()
+ addtimer(CALLBACK(src, PROC_REF(charge)), 5 SECONDS)
+
+/**
+ * ## charge
+ *
+ * Recharge invisibility action after 5 SECONDS.
+ */
+/datum/action/vehicle/sealed/mecha/invisibility/proc/charge()
+ button_icon_state = "mech_stealth_off"
+ charge = TRUE
+ build_all_button_icons()
+
+/datum/action/vehicle/sealed/mecha/charge_attack
+ name = "Charge Attack"
+ button_icon_state = "mech_charge_off"
+ /// Is charge attack activated.
+ var/on = FALSE
+ /// Recharge check.
+ var/charge = TRUE
+ /// Energy cost to perform charge attack
+ var/energy_cost = 400
+ /// Maximum range of charge attack.
+ var/max_charge_range = 7
+ /// Sound when mech do charge attack.
+ var/charge_attack_sound = 'sound/mecha/mech_charge_attack.ogg'
+
+/datum/action/vehicle/sealed/mecha/charge_attack/set_chassis(passed_chassis)
+ . = ..()
+ RegisterSignal(chassis, COMSIG_MECH_SAFETIES_TOGGLE, PROC_REF(on_toggle_safety))
+
+/// update button icon when toggle safety.
+/datum/action/vehicle/sealed/mecha/charge_attack/proc/on_toggle_safety()
+ SIGNAL_HANDLER
+
+ build_all_button_icons(UPDATE_BUTTON_STATUS)
+
+/datum/action/vehicle/sealed/mecha/charge_attack/Trigger(trigger_flags)
+ . = ..()
+ if(!.)
+ return
+ on = !on
+ if(on)
+ charge_attack_on()
+ else
+ charge_attack_off()
+
+/datum/action/vehicle/sealed/mecha/charge_attack/IsAvailable(feedback)
+ . = ..()
+ if(!.)
+ return FALSE
+ if(!chassis.has_charge(energy_cost))
+ if(feedback)
+ owner.balloon_alert(owner, "not enough energy!")
+ return FALSE
+ if(chassis.weapons_safety)
+ if(feedback)
+ owner.balloon_alert(owner, "safety is on!")
+ return FALSE
+ if(!charge)
+ if(feedback)
+ owner.balloon_alert(owner, "recharging!")
+ return FALSE
+
+ return TRUE
+
+///Called when charge attack activated
+/datum/action/vehicle/sealed/mecha/charge_attack/proc/charge_attack_on()
+ check_visability()
+ button_icon_state = "mech_charge_on"
+ RegisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK, PROC_REF(click_try_charge))
+ build_all_button_icons()
+
+///Called when charge attack deactivated
+/datum/action/vehicle/sealed/mecha/charge_attack/proc/charge_attack_off()
+ button_icon_state = "mech_charge_off"
+ UnregisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK)
+ build_all_button_icons()
+
+///Check if mech use invisibility and deactivate it when we activate charge attack.
+/datum/action/vehicle/sealed/mecha/charge_attack/proc/check_visability()
+ for(var/who_inside in chassis.occupants)
+ var/mob/living/occupant = who_inside
+ var/datum/action/vehicle/sealed/mecha/invisibility/stealth_action = LAZYACCESSASSOC(chassis.occupant_actions, occupant, /datum/action/vehicle/sealed/mecha/invisibility)
+ if(stealth_action?.on)
+ stealth_action.make_visible()
+
+///Called when mech attacks with charge attack enabled.
+/datum/action/vehicle/sealed/mecha/charge_attack/proc/click_try_charge(datum/source, mob/living/pilot, atom/target, on_cooldown, is_adjacent)
+ SIGNAL_HANDLER
+
+ var/turf = get_turf(target)
+ if(!on)
+ UnregisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK)
+ return FALSE
+ if(isnull(turf))
+ pilot.balloon_alert(pilot, "invalid direction!")
+ return FALSE
+ if(!charge)
+ pilot.balloon_alert(pilot, "recharging!")
+ return FALSE
+ else
+ if(charge_attack(pilot, turf))
+ return TRUE
+ return FALSE
+
+/**
+ * ## charge_attack
+ *
+ * Deal everyone in line for mech location to mouse location 35 damage and 25 chanse to cut off limb.
+ * Teleport mech to the end of line.
+ * Arguments:
+ * * charger - occupant inside mech.
+ * * target - occupant inside mech.
+ */
+/datum/action/vehicle/sealed/mecha/charge_attack/proc/charge_attack(mob/living/charger, turf/target)
+ var/turf/start_charge_here = get_turf(charger)
+ var/charge_range = min(get_dist_euclidean(start_charge_here, target), max_charge_range)
+ var/turf/but_we_gonna_here = get_ranged_target_turf(start_charge_here, get_dir(start_charge_here, target), floor(charge_range))
+ var/turf/here_we_go = start_charge_here
+ for(var/turf/line_turf in get_line(get_step(start_charge_here, get_dir(start_charge_here, target)), but_we_gonna_here))
+ if(get_turf(charger) == get_turf(line_turf))
+ continue
+ if(isclosedturf(line_turf))
+ break
+ var/obj/machinery/power/supermatter_crystal/funny_crystal = locate() in line_turf
+ if(funny_crystal)
+ funny_crystal.Bumped(chassis)
+ break
+ var/obj/machinery/door/airlock/like_a_wall = locate() in line_turf
+ if(like_a_wall?.density)
+ break
+ if(locate(/obj/structure/window) in line_turf)
+ break
+ for(var/mob/living/something_living in line_turf.contents)
+ if(something_living.stat >= UNCONSCIOUS || something_living.getStaminaLoss() >= 100 || something_living == charger)
+ continue
+ if(prob(DISMEMBER_CHANCE_LOW))
+ var/obj/item/bodypart/cut_bodypart = something_living.get_bodypart(pick(BODY_ZONE_R_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_ARM, BODY_ZONE_L_LEG, BODY_ZONE_HEAD))
+ cut_bodypart?.dismember(BRUTE)
+ something_living.apply_damage(35, BRUTE)
+ here_we_go = line_turf
+
+ // If the mech didn't move, it didn't charge
+ if(here_we_go == start_charge_here)
+ charger.balloon_alert(charger, "invalid direction!")
+ return FALSE
+ chassis.forceMove(here_we_go)
+ start_charge_here.Beam(chassis, icon_state = "mech_charge", time = 8)
+ playsound(chassis, charge_attack_sound, 75, FALSE)
+ on = !on
+ chassis.use_energy(energy_cost)
+ UnregisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK)
+ charge = FALSE
+ button_icon_state = "mech_charge_cooldown"
+ build_all_button_icons()
+ addtimer(CALLBACK(src, PROC_REF(charge)), 5 SECONDS)
+ return TRUE
+
+/**
+ * ## charge
+ *
+ * Recharge charge attack action after 5 SECONDS.
+ */
+/datum/action/vehicle/sealed/mecha/charge_attack/proc/charge()
+ charge = TRUE
+ button_icon_state = "mech_charge_off"
+ build_all_button_icons()
+
+/obj/vehicle/sealed/mecha/justice/loaded
+ equip_by_category = list(
+ MECHA_L_ARM = null,
+ MECHA_R_ARM = null,
+ MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/radio, /obj/item/mecha_parts/mecha_equipment/air_tank/full, /obj/item/mecha_parts/mecha_equipment/thrusters/ion),
+ MECHA_POWER = list(),
+ MECHA_ARMOR = list(),
+ )
+
+/obj/vehicle/sealed/mecha/justice/loaded/populate_parts()
+ cell = new /obj/item/stock_parts/power_store/cell/bluespace(src)
+ scanmod = new /obj/item/stock_parts/scanning_module/triphasic(src)
+ capacitor = new /obj/item/stock_parts/capacitor/quadratic(src)
+ servo = new /obj/item/stock_parts/servo/femto(src)
+ update_part_values()
+
+#undef DISMEMBER_CHANCE_HIGH
+#undef DISMEMBER_CHANCE_LOW
+
+#undef MOVEDELAY_ANGRY
+#undef MOVEDELAY_SAFETY
diff --git a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
index 5b50ac93523..0bb691160b3 100644
--- a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
+++ b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
@@ -69,7 +69,7 @@
projectile_obj.log_override = TRUE //we log being fired ourselves a little further down.
projectile_obj.firer = chassis
projectile_obj.preparePixelProjectile(target, source, modifiers, spread)
- if(source.client && isliving(source)) //dont want it to happen from syndie mecha npc mobs, they do direct fire anyways
+ if(isliving(source) && source.client) //dont want it to happen from syndie mecha npc mobs, they do direct fire anyways
var/mob/living/shooter = source
projectile_obj.hit_prone_targets = shooter.combat_mode
projectile_obj.fire()
diff --git a/code/modules/vehicles/mecha/mech_fabricator.dm b/code/modules/vehicles/mecha/mech_fabricator.dm
index 33edb9e47c7..c2d37ee6e83 100644
--- a/code/modules/vehicles/mecha/mech_fabricator.dm
+++ b/code/modules/vehicles/mecha/mech_fabricator.dm
@@ -52,10 +52,14 @@
//looping sound for printing items
var/datum/looping_sound/lathe_print/print_sound
+ /// Local designs that only this mechfab have(using when mechfab emaged so it's illegal designs).
+ var/list/datum/design/illegal_local_designs
+
/obj/machinery/mecha_part_fabricator/Initialize(mapload)
print_sound = new(src, FALSE)
rmat = AddComponent(/datum/component/remote_materials, mapload && link_on_init)
cached_designs = list()
+ illegal_local_designs = list()
RefreshParts() //Recalculating local material sizes if the fab isn't linked
return ..()
@@ -142,6 +146,26 @@
balloon_alert(user, "rotated to [dir2text(dir)].")
return CLICK_ACTION_SUCCESS
+/obj/machinery/mecha_part_fabricator/emag_act(mob/user, obj/item/card/emag/emag_card)
+ if(obj_flags & EMAGGED)
+ return FALSE
+ if(user.job != JOB_ROBOTICIST)
+ to_chat(user, span_warning("You clicking and typing but don’t understand what to do with it"))
+ return FALSE
+ obj_flags |= EMAGGED
+ for(var/found_illegal_mech_nods in SSresearch.techweb_nodes)
+ var/datum/techweb_node/illegal_mech_node = SSresearch.techweb_nodes[found_illegal_mech_nods]
+ if(!illegal_mech_node?.illegal_mech_node)
+ continue
+ for(var/id in illegal_mech_node.design_ids)
+ var/datum/design/illegal_mech_design = SSresearch.techweb_design_by_id(id)
+ illegal_local_designs |= illegal_mech_design
+ cached_designs |= illegal_mech_design
+ say("R$c!i&ed ERROR de#i$ns. C@n%ec$%ng to ~NULL~ se%ve$s.")
+ playsound(src, 'sound/machines/uplinkerror.ogg', 50, TRUE)
+ update_static_data_for_all_viewers()
+ return TRUE
+
/**
* Updates the `final_sets` and `buildable_parts` for the current mecha fabricator.
*/
@@ -351,7 +375,7 @@
for(var/datum/design/design in cached_designs)
var/cost = list()
- var/list/materials = design["materials"]
+ var/list/materials = design.materials
for(var/datum/material/mat in materials)
cost[mat.name] = OPTIMAL_COST(materials[mat] * component_coeff)
@@ -418,7 +442,7 @@
if(!istext(design_id))
continue
- if(!stored_research.researched_designs.Find(design_id))
+ if(!(stored_research.researched_designs.Find(design_id) || is_type_in_list(SSresearch.techweb_design_by_id(design_id), illegal_local_designs)))
continue
var/datum/design/design = SSresearch.techweb_design_by_id(design_id)
diff --git a/code/modules/vehicles/mecha/mech_melee_attack.dm b/code/modules/vehicles/mecha/mech_melee_attack.dm
index e5c7b0aac72..655a2f35339 100644
--- a/code/modules/vehicles/mecha/mech_melee_attack.dm
+++ b/code/modules/vehicles/mecha/mech_melee_attack.dm
@@ -10,7 +10,8 @@
/atom/proc/mech_melee_attack(obj/vehicle/sealed/mecha/mecha_attacker, mob/living/user)
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_MECH, mecha_attacker, user)
- log_combat(user, src, "attacked", mecha_attacker, "(COMBAT MODE: [uppertext(user.combat_mode)] (DAMTYPE: [uppertext(mecha_attacker.damtype)])")
+ if(!isnull(user))
+ log_combat(user, src, "attacked", mecha_attacker, "(COMBAT MODE: [uppertext(user?.combat_mode)] (DAMTYPE: [uppertext(mecha_attacker.damtype)])")
return 0
/turf/closed/wall/mech_melee_attack(obj/vehicle/sealed/mecha/mecha_attacker, mob/living/user)
@@ -20,15 +21,15 @@
mecha_attacker.do_attack_animation(src)
switch(mecha_attacker.damtype)
if(BRUTE)
- playsound(src, 'sound/weapons/punch4.ogg', 50, TRUE)
+ playsound(src, mecha_attacker.brute_attack_sound, 50, TRUE)
if(BURN)
- playsound(src, 'sound/items/welder.ogg', 50, TRUE)
+ playsound(src, mecha_attacker.burn_attack_sound, 50, TRUE)
else
return 0
mecha_attacker.visible_message(span_danger("[mecha_attacker] hits [src]!"), span_danger("You hit [src]!"), null, COMBAT_MESSAGE_RANGE)
if(prob(hardness + mecha_attacker.force) && mecha_attacker.force > 20)
dismantle_wall(1)
- playsound(src, 'sound/effects/meteorimpact.ogg', 100, TRUE)
+ playsound(src, mecha_attacker.destroy_wall_sound, 100, TRUE)
else
add_dent(WALL_DENT_HIT)
..()
@@ -57,9 +58,9 @@
mecha_attacker.do_attack_animation(src)
switch(mecha_attacker.damtype)
if(BRUTE)
- playsound(src, 'sound/weapons/punch4.ogg', 50, TRUE)
+ playsound(src, mecha_attacker.brute_attack_sound, 50, TRUE)
if(BURN)
- playsound(src, 'sound/items/welder.ogg', 50, TRUE)
+ playsound(src, mecha_attacker.burn_attack_sound, 50, TRUE)
else
return 0
mecha_attacker.visible_message(span_danger("[mecha_attacker] hits [src]!"), span_danger("You hit [src]!"), null, COMBAT_MESSAGE_RANGE)
@@ -72,7 +73,7 @@
return ..()
/mob/living/mech_melee_attack(obj/vehicle/sealed/mecha/mecha_attacker, mob/living/user)
- if(!user.combat_mode)
+ if(istype(user) && !user.combat_mode)
step_away(src, mecha_attacker)
log_combat(user, src, "pushed", mecha_attacker)
visible_message(span_warning("[mecha_attacker] pushes [src] out of the way."), \
@@ -80,36 +81,41 @@
to_chat(mecha_attacker, span_danger("You push [src] out of the way."))
return 0
- if(HAS_TRAIT(user, TRAIT_PACIFISM))
+ if(!isnull(user) && HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, span_warning("You don't want to harm other living beings!"))
return 0
mecha_attacker.do_attack_animation(src)
if(mecha_attacker.damtype == BRUTE)
step_away(src, mecha_attacker, 15)
var/obj/item/bodypart/selected_zone = get_bodypart(pick(BODY_ZONE_CHEST, BODY_ZONE_CHEST, BODY_ZONE_CHEST, BODY_ZONE_HEAD))
- if(selected_zone)
- var/dmg = rand(mecha_attacker.force * 0.5, mecha_attacker.force)
- switch(mecha_attacker.damtype)
- if(BRUTE)
- if(mecha_attacker.force > 35) // durand and other heavy mechas
- Unconscious(20)
- else if(mecha_attacker.force > 20 && !IsKnockdown()) // lightweight mechas like gygax
- Knockdown(40)
+ var/dmg = rand(mecha_attacker.force * 0.5, mecha_attacker.force)
+ switch(mecha_attacker.damtype)
+ if(BRUTE)
+ if(mecha_attacker.force > 35) // durand and other heavy mechas
+ mecha_attacker.melee_attack_effect(src, heavy = TRUE)
+ else if(mecha_attacker.force > 20 && !IsKnockdown()) // lightweight mechas like gygax
+ mecha_attacker.melee_attack_effect(src, heavy = FALSE)
+ if(selected_zone)
selected_zone.receive_damage(dmg, 0, updating_health = TRUE)
- playsound(src, 'sound/weapons/punch4.ogg', 50, TRUE)
- if(FIRE)
- selected_zone.receive_damage(0, dmg, updating_health = TRUE)
- playsound(src, 'sound/items/welder.ogg', 50, TRUE)
- if(TOX)
- playsound(src, 'sound/effects/spray2.ogg', 50, TRUE)
- if((reagents.get_reagent_amount(/datum/reagent/cryptobiolin) + mecha_attacker.force) < mecha_attacker.force*2)
- reagents.add_reagent(/datum/reagent/cryptobiolin, mecha_attacker.force/2)
- if((reagents.get_reagent_amount(/datum/reagent/toxin) + mecha_attacker.force) < mecha_attacker.force*2)
- reagents.add_reagent(/datum/reagent/toxin, mecha_attacker.force/2.5)
else
- return 0
- . = dmg
- visible_message(span_danger("[mecha_attacker.name] hits [src]!"), \
- span_userdanger("[mecha_attacker.name] hits you!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, list(mecha_attacker))
- to_chat(mecha_attacker, span_danger("You hit [src]!"))
+ apply_damage(dmg, BRUTE)
+ playsound(src, mecha_attacker.brute_attack_sound, 50, TRUE)
+ if(FIRE)
+ if(selected_zone)
+ selected_zone.receive_damage(0, dmg, updating_health = TRUE)
+ else
+ apply_damage(dmg, BURN)
+ playsound(src, mecha_attacker.burn_attack_sound, 50, TRUE)
+ if(TOX)
+ playsound(src, mecha_attacker.tox_attack_sound, 50, TRUE)
+ if((reagents.get_reagent_amount(/datum/reagent/cryptobiolin) + mecha_attacker.force) < mecha_attacker.force*2)
+ reagents.add_reagent(/datum/reagent/cryptobiolin, mecha_attacker.force/2)
+ if((reagents.get_reagent_amount(/datum/reagent/toxin) + mecha_attacker.force) < mecha_attacker.force*2)
+ reagents.add_reagent(/datum/reagent/toxin, mecha_attacker.force/2.5)
+ else
+ return 0
+ . = dmg
+ visible_message(span_danger("[mecha_attacker.name] [mecha_attacker.attack_verbs[1]] [src]!"), \
+ span_userdanger("[mecha_attacker.name] [mecha_attacker.attack_verbs[2]] you!"), span_hear("You hear a sickening sound of flesh [mecha_attacker.attack_verbs[3]] flesh!"), COMBAT_MESSAGE_RANGE, list(mecha_attacker))
+ to_chat(mecha_attacker, span_danger("You [mecha_attacker.attack_verbs[1]] [src]!"))
..()
diff --git a/code/modules/vehicles/mecha/mecha_construction_paths.dm b/code/modules/vehicles/mecha/mecha_construction_paths.dm
index f4b1a679596..0a305a5db66 100644
--- a/code/modules/vehicles/mecha/mecha_construction_paths.dm
+++ b/code/modules/vehicles/mecha/mecha_construction_paths.dm
@@ -819,3 +819,39 @@
outer_plating = /obj/item/stack/sheet/plasteel
outer_plating_amount = 5
+
+//Justice
+/datum/component/construction/unordered/mecha_chassis/justice
+ result = /datum/component/construction/mecha/justice
+ steps = list(
+ /obj/item/mecha_parts/part/justice_torso,
+ /obj/item/mecha_parts/part/justice_left_arm,
+ /obj/item/mecha_parts/part/justice_right_arm,
+ /obj/item/mecha_parts/part/justice_left_leg,
+ /obj/item/mecha_parts/part/justice_right_leg
+ )
+
+/datum/component/construction/mecha/justice
+ result = /obj/vehicle/sealed/mecha/justice
+ base_icon = "justice"
+
+ inner_plating = /obj/item/stack/telecrystal
+ inner_plating_amount = 8
+
+ outer_plating = /obj/item/mecha_parts/part/justice_armor
+ outer_plating_amount = 1
+
+/datum/component/construction/mecha/justice/get_circuit_steps()
+ return list()
+
+/datum/component/construction/mecha/justice/get_inner_plating_steps()
+ return list(
+ list(
+ "key" = inner_plating,
+ "amount" = inner_plating_amount,
+ "back_key" = TOOL_SCREWDRIVER,
+ "desc" = "The power cell is secured, and [inner_plating_amount] telecrystals can be added.",
+ "forward_message" = "added telecrystal",
+ "backward_message" = "unsecured power cell"
+ )
+ )
diff --git a/code/modules/vehicles/mecha/mecha_parts.dm b/code/modules/vehicles/mecha/mecha_parts.dm
index 7fcee209259..474716568eb 100644
--- a/code/modules/vehicles/mecha/mecha_parts.dm
+++ b/code/modules/vehicles/mecha/mecha_parts.dm
@@ -332,6 +332,42 @@
desc="Savannah-Ivanov armor plates. They are uniquely shaped and reinforced to deal with the stresses of two pilots, grandiose leaps, and missiles."
icon_state = "savannah_ivanov_armor"
+// Justice
+
+/obj/item/mecha_parts/chassis/justice
+ name = "\improper Justice chassis"
+ construct_type = /datum/component/construction/unordered/mecha_chassis/justice
+
+/obj/item/mecha_parts/part/justice_torso
+ name="\improper Justice torso"
+ desc="A Justice torso part."
+ icon_state = "justice_torso"
+
+/obj/item/mecha_parts/part/justice_left_arm
+ name="\improper Justice left arm"
+ desc="A Justice left arm."
+ icon_state = "justice_l_arm"
+
+/obj/item/mecha_parts/part/justice_right_arm
+ name="\improper Justice right arm"
+ desc="A Justice left arm."
+ icon_state = "justice_r_arm"
+
+/obj/item/mecha_parts/part/justice_left_leg
+ name="\improper Justice left leg"
+ desc="A Justice left leg."
+ icon_state = "justice_l_leg"
+
+/obj/item/mecha_parts/part/justice_right_leg
+ name="\improper Justice right leg"
+ desc="A Justice left leg."
+ icon_state = "justice_r_leg"
+
+/obj/item/mecha_parts/part/justice_armor
+ name="Justice armor"
+ desc="Justice armor plates."
+ icon_state = "justice_armor"
+
///////// Circuitboards
/obj/item/circuitboard/mecha
diff --git a/code/modules/vehicles/mecha/mecha_ui.dm b/code/modules/vehicles/mecha/mecha_ui.dm
index 712d5f0d712..1113a853813 100644
--- a/code/modules/vehicles/mecha/mecha_ui.dm
+++ b/code/modules/vehicles/mecha/mecha_ui.dm
@@ -111,16 +111,17 @@
var/module_index = 0
for(var/category in max_equip_by_category)
var/max_per_category = max_equip_by_category[category]
- for(var/i = 1 to max_per_category)
- var/equipment = equip_by_category[category]
- var/is_slot_free = islist(equipment) ? i > length(equipment) : isnull(equipment)
- if(is_slot_free)
- data += list(list(
- "slot" = category
- ))
- if(ui_selected_module_index == module_index)
- ui_selected_module_index = null
- else
+ if(max_per_category)
+ for(var/i = 1 to max_per_category)
+ var/equipment = equip_by_category[category]
+ var/is_slot_free = islist(equipment) ? i > length(equipment) : isnull(equipment)
+ if(is_slot_free)
+ data += list(list(
+ "slot" = category
+ ))
+ if(ui_selected_module_index == module_index)
+ ui_selected_module_index = null
+ continue
var/obj/item/mecha_parts/mecha_equipment/module = islist(equipment) ? equipment[i] : equipment
data += list(list(
"slot" = category,
@@ -140,7 +141,7 @@
))
if(isnull(ui_selected_module_index))
ui_selected_module_index = module_index
- module_index++
+ module_index++
return data
/obj/vehicle/sealed/mecha/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
diff --git a/code/modules/vehicles/mecha/mecha_wreckage.dm b/code/modules/vehicles/mecha/mecha_wreckage.dm
index dc0414e8606..8f972ef4781 100644
--- a/code/modules/vehicles/mecha/mecha_wreckage.dm
+++ b/code/modules/vehicles/mecha/mecha_wreckage.dm
@@ -245,3 +245,8 @@
/obj/item/mecha_parts/part/odysseus_right_arm,
/obj/item/mecha_parts/part/odysseus_left_leg,
/obj/item/mecha_parts/part/odysseus_right_leg)
+
+/obj/structure/mecha_wreckage/justice
+ name = "\improper Justice wreckage"
+ icon_state = "justice-broken"
+ welder_salvage = list(/obj/item/stack/sheet/iron, /obj/item/stack/rods)
diff --git a/code/modules/vehicles/scooter.dm b/code/modules/vehicles/scooter.dm
index 0b842b8ed9c..08edbf78867 100644
--- a/code/modules/vehicles/scooter.dm
+++ b/code/modules/vehicles/scooter.dm
@@ -206,6 +206,21 @@
to_chat(rider, span_warning("[src] [p_are()] not powerful enough to fly upwards."))
return FALSE
+/obj/vehicle/ridden/scooter/skateboard/hoverboard/holyboarded
+ name = "holy skateboard"
+ desc = "A board blessed by the gods with the power to grind for our sins. Has the initials 'J.C.' on the underside."
+ board_item_type = /obj/item/melee/skateboard/holyboard
+ instability = 3
+ icon_state = "hoverboard_holy"
+/obj/vehicle/ridden/scooter/skateboard/hoverboard/holyboarded/post_buckle_mob(mob/living/M)
+ M.AddComponent(/datum/component/anti_magic, MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY)
+ return ..()
+
+/obj/vehicle/ridden/scooter/skateboard/hoverboard/holyboarded/post_unbuckle_mob(mob/living/M)
+ if(!has_buckled_mobs())
+ qdel (M.GetComponent(/datum/component/anti_magic, MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY))
+ return ..()
+
/obj/vehicle/ridden/scooter/skateboard/hoverboard/admin
name = "\improper Board Of Directors"
desc = "The engineering complexity of a spaceship concentrated inside of a board. Just as expensive, too."
diff --git a/code/modules/vehicles/vehicle_key.dm b/code/modules/vehicles/vehicle_key.dm
index f8db861449f..5f57895d8ac 100644
--- a/code/modules/vehicles/vehicle_key.dm
+++ b/code/modules/vehicles/vehicle_key.dm
@@ -33,10 +33,15 @@
attack_verb_continuous = list("stubs", "pokes")
attack_verb_simple = list("stub", "poke")
sharpness = SHARP_EDGED
- embedding = list("pain_mult" = 1, "embed_chance" = 30, "fall_chance" = 70)
+ embed_type = /datum/embed_data/janicart_key
wound_bonus = -1
bare_wound_bonus = 2
+/datum/embed_data/janicart_key
+ pain_mult = 1
+ embed_chance = 30
+ fall_chance = 70
+
/obj/item/key/janitor/suicide_act(mob/living/carbon/user)
switch(user.mind?.get_skill_level(/datum/skill/cleaning))
if(SKILL_LEVEL_NONE to SKILL_LEVEL_NOVICE) //Their mind is too weak to ascend as a janny
diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm
index f85fe033e54..30b9ed53715 100644
--- a/code/modules/vending/_vending.dm
+++ b/code/modules/vending/_vending.dm
@@ -1074,11 +1074,9 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
var/mob/living/carbon/carbon_target = atom_target
for(var/i in 1 to num_shards)
var/obj/item/shard/shard = new /obj/item/shard(get_turf(carbon_target))
- shard.embedding = list(embed_chance = 100, ignore_throwspeed_threshold = TRUE, impact_pain_mult = 1, pain_chance = 5)
- shard.updateEmbedding()
+ shard.set_embed(/datum/embed_data/glass_candy)
carbon_target.hitby(shard, skipcatch = TRUE, hitpush = FALSE)
- shard.embedding = list()
- shard.updateEmbedding()
+ shard.set_embed(initial(shard.embed_type))
return TRUE
if (VENDOR_CRUSH_CRIT_PIN) // pin them beneath the machine until someone untilts it
if (!isliving(atom_target))
diff --git a/code/modules/vending/cigarette.dm b/code/modules/vending/cigarette.dm
index ddcfd9bb244..61379e56354 100644
--- a/code/modules/vending/cigarette.dm
+++ b/code/modules/vending/cigarette.dm
@@ -17,7 +17,7 @@
/obj/item/storage/fancy/rollingpapers = 5,
)
contraband = list(
- /obj/item/clothing/mask/vape = 5,
+ /obj/item/vape = 5,
)
premium = list(
/obj/item/storage/fancy/cigarettes/cigpack_robustgold = 3,
@@ -66,7 +66,7 @@
)
premium = list(
/obj/item/storage/fancy/cigarettes/cigpack_mindbreaker = 5,
- /obj/item/clothing/mask/vape = 5,
+ /obj/item/vape = 5,
/obj/item/lighter = 3,
)
initial_language_holder = /datum/language_holder/beachbum
diff --git a/code/modules/vending/subtype.dm b/code/modules/vending/subtype.dm
new file mode 100644
index 00000000000..9b4f212224e
--- /dev/null
+++ b/code/modules/vending/subtype.dm
@@ -0,0 +1,51 @@
+
+/obj/machinery/vending/subtype_vendor
+ name = "\improper subtype vendor"
+ desc = "A vending machine that vends all subtypes of a specific type."
+ color = COLOR_ADMIN_PINK
+ verb_say = "codes"
+ verb_ask = "queries"
+ verb_exclaim = "compiles"
+ armor_type = /datum/armor/machinery_vending
+ circuit = null
+ product_slogans = "Spawn \" too annoying? Too lazy to open game panel? This one's for you!;Subtype vendor, for all your debugging woes!"
+ default_price = 0
+ /// Spawns coders by default
+ var/type_to_vend = /obj/item/food/grown/citrus
+
+/obj/machinery/vending/subtype_vendor/Initialize(mapload, type_to_vend)
+ . = ..()
+ if(type_to_vend)
+ src.type_to_vend = type_to_vend
+ load_subtypes()
+
+/obj/machinery/vending/subtype_vendor/proc/load_subtypes()
+ products = list()
+ product_records = list()
+
+ for(var/type in typesof(type_to_vend))
+ LAZYADDASSOC(products, type, 50)
+
+ build_inventories()
+
+/obj/machinery/vending/subtype_vendor/attack_hand_secondary(mob/user, list/modifiers)
+ . = ..()
+ if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
+ return
+
+ if(!can_interact(user) || !user.can_perform_action(src, ALLOW_SILICON_REACH|FORBID_TELEKINESIS_REACH))
+ return
+
+ if(!user.client?.holder?.check_for_rights(R_SERVER|R_DEBUG))
+ speak("Hey! You can't use this! Get outta here!")
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+ var/type_to_vend_now = tgui_input_text(user, "What type to set it to?", "Set type to vend", "/obj/item/toy/plush")
+ type_to_vend_now = text2path(type_to_vend_now)
+ if(!ispath(type_to_vend_now))
+ speak("That's not a real path, dumbass! Try again!")
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+ type_to_vend = type_to_vend_now
+ load_subtypes()
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
diff --git a/config/game_options.txt b/config/game_options.txt
index b8e7fd6a92b..c008ddf7eca 100644
--- a/config/game_options.txt
+++ b/config/game_options.txt
@@ -581,6 +581,12 @@ NEGATIVE_STATION_TRAITS 3 1
# If set to 0, then players won't be able to select any positive quirks.
# If commented-out or undefined, the maximum default is 6.
MAX_POSITIVE_QUIRKS 6
+
+# A config that skews with the random spawners weights
+# If the value is lower than 1, it'll tend to even out the odds
+# If higher than 1, it'll lean toward common spawns even more.
+RANDOM_LOOT_WEIGHT_MODIFIER 1
+
## Gamemode configurations
## Multipliers for points gained over time for event tracks.
diff --git a/html/changelogs/AutoChangeLog-pr-28446.yml b/html/changelogs/AutoChangeLog-pr-28446.yml
deleted file mode 100644
index fac0619d7fc..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28446.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "LT3"
-delete-after: True
-changes:
- - bugfix: "Fixed tram door warning lights, returning a visual warning for those who don't enjoy being bitten"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28451.yml b/html/changelogs/AutoChangeLog-pr-28451.yml
deleted file mode 100644
index d2f6c867288..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28451.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "SkyratBot"
-delete-after: True
-changes:
- - bugfix: "Inducers now accept megacells AND regular cells."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28457.yml b/html/changelogs/AutoChangeLog-pr-28457.yml
deleted file mode 100644
index c63e2d9b5f8..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28457.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: "Melbert"
-delete-after: True
-changes:
- - bugfix: "Fixed modsuit interactions slightly. No longer requires combat mode to use tools on it, plasma core works as intended as well. (Using combat mode, however, will make you insert the item)"
- - refactor: "Refactored lockboxes"
- - refactor: "Refactored medbot skin application"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28469.yml b/html/changelogs/AutoChangeLog-pr-28469.yml
deleted file mode 100644
index 4d71b8ef096..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28469.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "projectkepler-ru"
-delete-after: True
-changes:
- - balance: "cocain does not give you speed bonus and instead give you toxin damage"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28538.yml b/html/changelogs/AutoChangeLog-pr-28538.yml
deleted file mode 100644
index 756a66f1251..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28538.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-author: "ShizCalev"
-delete-after: True
-changes:
- - bugfix: "Fixed a number of shuttles having parts (such as lattices) completely disappearing."
- - bugfix: "Fixed the ceilings above shuttles on station maps being full-bright."
- - bugfix: "Fixed lattices sometimes appearing at random locations in space on station maps."
- - bugfix: "Cleaned up a number of accidentally placed objects in space across all station maps."
- - bugfix: "Fixed a false positive with the mapload_space_verification unit test failing on turfs that weren't actually part of shuttles."
- - code_imp: "Added a unit test that automatically finds all base space turfs with objects on them, as well as non-space turfs that are set to space areas (meaning that these squares weren't lit properly.)"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28540.yml b/html/changelogs/AutoChangeLog-pr-28540.yml
deleted file mode 100644
index 2e83efc91b9..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28540.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: "Majkl-J, StrangeWeirdKitten"
-delete-after: True
-changes:
- - bugfix: "You can rename items again"
- - bugfix: "Donator items properly handle inside the loadout."
- - bugfix: "Ckey and Role restricted items now show in the loadout"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28544.yml b/html/changelogs/AutoChangeLog-pr-28544.yml
deleted file mode 100644
index 9daca7b8690..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28544.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "SkyratBot"
-delete-after: True
-changes:
- - qol: "you can now swap pens in pdas"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28546.yml b/html/changelogs/AutoChangeLog-pr-28546.yml
deleted file mode 100644
index d44ce6d19a3..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28546.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-author: "SkyratBot"
-delete-after: True
-changes:
- - code_imp: "Deconstructing light switches now uses the proper tool action and tool usage code, please report any issues."
- - bugfix: "Attempting to deconstruct a light switch by unscrewing it no longer makes you hit it even on a success."
- - sound: "Deconstructing a light switch actually plays tool usage and deconstruction sounds."
- - qol: "Deconstructing light switches is now a left click with a screwdriver parallel to other unscrewing actions."
- - bugfix: "Screentips for deconstructing a light switch no longer show up on every item EXCEPT screwdrivers."
- - qol: "Added an examine hint denoting light switches are screwed to the wall."
- - qol: "Added visible messages for someone deconstructing a light switch parallel to deconstructing intercoms."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28547.yml b/html/changelogs/AutoChangeLog-pr-28547.yml
deleted file mode 100644
index b0896ba5749..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28547.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Bisar"
-delete-after: True
-changes:
- - qol: "Loot panels should update more predictably and informatively now."
- - code_imp: "Lootpanels have more consistent logic on when they automatically update."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28548.yml b/html/changelogs/AutoChangeLog-pr-28548.yml
deleted file mode 100644
index d240e98bf39..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28548.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-author: "SkyratBot"
-delete-after: True
-changes:
- - code_imp: "Moved bedsheet bin interactions to the item interaction code. Please report any issues."
- - qol: "Made bedsheet bin tool interactions right click, such that left click is consistently for putting in items."
- - qol: "Added usage screentips to bedsheet bins."
- - qol: "Added more feedback to failing to hide items in bedsheet bins."
- - sound: "Made putting items in bedsheet bins not silent (If the items have associated pickup/drop sounds)."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28549.yml b/html/changelogs/AutoChangeLog-pr-28549.yml
deleted file mode 100644
index c373ae7b87d..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28549.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "SkyratBot"
-delete-after: True
-changes:
- - rscadd: "Being cursed now enables disembowlements/cranial fissures outside of hardcrit"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28550.yml b/html/changelogs/AutoChangeLog-pr-28550.yml
deleted file mode 100644
index 96f0d7f7b7c..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28550.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "MrDas"
-delete-after: True
-changes:
- - bugfix: "Bolas now slowdown properly."
- - bugfix: "Cult bolas no longer leave in-hand sprite when they ensnare to non-cult user."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28551.yml b/html/changelogs/AutoChangeLog-pr-28551.yml
deleted file mode 100644
index 2f82365579f..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28551.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "SkyratBot"
-delete-after: True
-changes:
- - rscadd: "Ghost role food truck merchants may occasionally turn up at the station."
- - rscadd: "Adds a new fugitive hunter type, MI13 secret agents."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28552.yml b/html/changelogs/AutoChangeLog-pr-28552.yml
deleted file mode 100644
index 60504144581..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28552.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-author: "SkyratBot"
-delete-after: True
-changes:
- - qol: "It is easier to do some required techweb experiments on Charlie station now"
- - bugfix: "Fixed experimental dissection surgeries giving too many points"
- - balance: "Techweb: Moved NTNet relay back to tcomms node"
- - balance: "Techweb: Moved blood pack and chem pack to the starting node"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28553.yml b/html/changelogs/AutoChangeLog-pr-28553.yml
deleted file mode 100644
index 44187af2d36..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28553.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "SkyratBot"
-delete-after: True
-changes:
- - bugfix: "fixed Birdshot bar and surrounding areas not properly connected to the power grid."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28558.yml b/html/changelogs/AutoChangeLog-pr-28558.yml
deleted file mode 100644
index 27f8d0c9958..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28558.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-author: "SkyratBot"
-delete-after: True
-changes:
- - rscdel: "Removed Trichromatic Larynx per @ mothblocks decision that it looks uggo."
- - rscdel: "Replaced heckacious laryncks' color and size changes with random bolding, italics, and underlining."
- - rscadd: "Stoner has been un-locked and replaces TL in the above's recipe."
- - bugfix: "Fixed elastic arms users being unable to use abstract items like mending touch or shock touch."
- - bugfix: "Fixed mending touch being bad"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28560.yml b/html/changelogs/AutoChangeLog-pr-28560.yml
deleted file mode 100644
index 55a4b32cef6..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28560.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "LT3"
-delete-after: True
-changes:
- - bugfix: "CMO and deluxe hypospray can again hold large vials"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28562.yml b/html/changelogs/AutoChangeLog-pr-28562.yml
deleted file mode 100644
index e793e52c0dc..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28562.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "SkyratBot"
-delete-after: True
-changes:
- - bugfix: "portascrubbers also scrub the tile theyre on"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28563.yml b/html/changelogs/AutoChangeLog-pr-28563.yml
deleted file mode 100644
index b8582a687ae..00000000000
--- a/html/changelogs/AutoChangeLog-pr-28563.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "SkyratBot"
-delete-after: True
-changes:
- - image: "Fixed shading on some metal ingots."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28684.yml b/html/changelogs/AutoChangeLog-pr-28684.yml
new file mode 100644
index 00000000000..bb4742bd710
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28684.yml
@@ -0,0 +1,4 @@
+author: "Majkl-J"
+delete-after: True
+changes:
+ - bugfix: "Brings loadouts back on par with the old system mechanically"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28685.yml b/html/changelogs/AutoChangeLog-pr-28685.yml
new file mode 100644
index 00000000000..0eca3c2c136
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28685.yml
@@ -0,0 +1,4 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - rscadd: "12% chance of a ouija board spawning in the chaplain office. Wawastation always has one."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28686.yml b/html/changelogs/AutoChangeLog-pr-28686.yml
new file mode 100644
index 00000000000..16846b52e85
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28686.yml
@@ -0,0 +1,4 @@
+author: "Melbert"
+delete-after: True
+changes:
+ - qol: "Vending Machines have a search bar"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28689.yml b/html/changelogs/AutoChangeLog-pr-28689.yml
new file mode 100644
index 00000000000..e0efeafca0b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28689.yml
@@ -0,0 +1,4 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - bugfix: "raptors don't easily stress out in lavaland environments"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28690.yml b/html/changelogs/AutoChangeLog-pr-28690.yml
new file mode 100644
index 00000000000..c281fd4bc3c
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28690.yml
@@ -0,0 +1,5 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - balance: "All jetpacks are now modsuit jetpack speed"
+ - rscdel: "Adv. Ion Jetpack module has been removed"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28691.yml b/html/changelogs/AutoChangeLog-pr-28691.yml
new file mode 100644
index 00000000000..f7127976f3e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28691.yml
@@ -0,0 +1,5 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - qol: "Mood more promptly reacts to the beauty of an area as it changes, and not just when changing areas."
+ - rscadd: "Coroners (and potentially other mobs with the morbid trait), now like degradated-looking rooms."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28692.yml b/html/changelogs/AutoChangeLog-pr-28692.yml
new file mode 100644
index 00000000000..1e08c79f2bd
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28692.yml
@@ -0,0 +1,4 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - bugfix: "Fixed the bluescreen while setting a custom font in tgui panel"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28693.yml b/html/changelogs/AutoChangeLog-pr-28693.yml
new file mode 100644
index 00000000000..938e5b67884
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28693.yml
@@ -0,0 +1,10 @@
+author: "LT3"
+delete-after: True
+changes:
+ - balance: "Advanced Medbay Equipment research node lowered to tier 3"
+ - balance: "Advanced Medbay Equipment now requires haloperidol scan experiment"
+ - balance: "Haloperidol and cryostylane experiments can be performed roundstart"
+ - balance: "Cryostasis research node raised to tier 4"
+ - balance: "Cryostasis now has Fusion instead of Controlled Plasma as a prerequisite"
+ - balance: "Cryostasis cryostylane scan is now a discount experiment"
+ - balance: "Crystallizer moved from Controlled Plasma to Fusion"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28695.yml b/html/changelogs/AutoChangeLog-pr-28695.yml
new file mode 100644
index 00000000000..996ba9fcd03
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28695.yml
@@ -0,0 +1,5 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - rscadd: "Night vision goggles now subtly tint your screen."
+ - rscadd: "You can turn your night vision goggles off. Doing so removes the tint and the eye protection malus."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28696.yml b/html/changelogs/AutoChangeLog-pr-28696.yml
new file mode 100644
index 00000000000..82c8c93f0de
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28696.yml
@@ -0,0 +1,4 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - admin: "Added a config that regulares random spawners weights."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28697.yml b/html/changelogs/AutoChangeLog-pr-28697.yml
new file mode 100644
index 00000000000..99bc1155132
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28697.yml
@@ -0,0 +1,4 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - bugfix: "Hoverboards properly dysfunction in space without any kind of support underneath them."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28699.yml b/html/changelogs/AutoChangeLog-pr-28699.yml
new file mode 100644
index 00000000000..3a1c99cee80
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28699.yml
@@ -0,0 +1,5 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - bugfix: "Fixed glass shards generated from falling vending machines or tackling windows not being able to embed into anyone."
+ - refactor: "Refactored embedding code to use datums instead of bespoke elements and ugly associated lists."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28701.yml b/html/changelogs/AutoChangeLog-pr-28701.yml
new file mode 100644
index 00000000000..e9fc577c8e5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28701.yml
@@ -0,0 +1,4 @@
+author: "xXPawnStarrXx"
+delete-after: True
+changes:
+ - bugfix: "fixed my medical tools not being printable."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28702.yml b/html/changelogs/AutoChangeLog-pr-28702.yml
new file mode 100644
index 00000000000..8a3e5a545d2
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28702.yml
@@ -0,0 +1,4 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - bugfix: "Fixed the orbit icons scrolling sometimes"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28703.yml b/html/changelogs/AutoChangeLog-pr-28703.yml
new file mode 100644
index 00000000000..9e21d301657
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28703.yml
@@ -0,0 +1,5 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - rscadd: "Aquariums now influence the beauty score of an area based on their contents."
+ - rscadd: "Morbid people now get a positive moodlet when admiring an aquarium full of dead fish, and a negative one when fish are alive."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28704.yml b/html/changelogs/AutoChangeLog-pr-28704.yml
new file mode 100644
index 00000000000..7700255dd26
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28704.yml
@@ -0,0 +1,4 @@
+author: "LT3"
+delete-after: True
+changes:
+ - bugfix: "Fixed medical stacks like mesh and sutures not treating wounds"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28705.yml b/html/changelogs/AutoChangeLog-pr-28705.yml
new file mode 100644
index 00000000000..1907d456f8e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28705.yml
@@ -0,0 +1,5 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - balance: "Demon: bloodcrawl now deal damage when you using it."
+ - balance: "Demon: demon can eat only carbon human beings."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28706.yml b/html/changelogs/AutoChangeLog-pr-28706.yml
new file mode 100644
index 00000000000..39b1ca2da6f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28706.yml
@@ -0,0 +1,4 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - balance: "Bait quality now influences the probabilities of getting a rarer fish compared to the most common one(s)"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28707.yml b/html/changelogs/AutoChangeLog-pr-28707.yml
new file mode 100644
index 00000000000..0c133098bb9
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28707.yml
@@ -0,0 +1,4 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - image: "You can now flip your security helmet's visor up with alt-click! However, doing so will (obviously) expose your eyes."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28708.yml b/html/changelogs/AutoChangeLog-pr-28708.yml
new file mode 100644
index 00000000000..56a0f30d37f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28708.yml
@@ -0,0 +1,4 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - refactor: "refactored `GetExactComponent` to be 1641 compatible"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28709.yml b/html/changelogs/AutoChangeLog-pr-28709.yml
new file mode 100644
index 00000000000..ef87b3d8127
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28709.yml
@@ -0,0 +1,4 @@
+author: "Rhials"
+delete-after: True
+changes:
+ - admin: "Narrate verbs will now allow you to pick what text formatting span you want to use before you send them, if any."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28710.yml b/html/changelogs/AutoChangeLog-pr-28710.yml
new file mode 100644
index 00000000000..1185eaaf2e5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28710.yml
@@ -0,0 +1,5 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - code_imp: "Added the subtype vendor which lets admins and coders vend subtypes of a path"
+ - code_imp: "Added the vendor and both varieties of omnitool to runtime station"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28711.yml b/html/changelogs/AutoChangeLog-pr-28711.yml
new file mode 100644
index 00000000000..33ec4a5402e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28711.yml
@@ -0,0 +1,5 @@
+author: "SkyratBot"
+delete-after: True
+changes:
+ - qol: "blood bros get a hud to see eachother"
+ - code_imp: "teambased antagonists may not see the HUDs of other teams"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-28713.yml b/html/changelogs/AutoChangeLog-pr-28713.yml
new file mode 100644
index 00000000000..aa281e763ca
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-28713.yml
@@ -0,0 +1,4 @@
+author: "theselfish"
+delete-after: True
+changes:
+ - bugfix: "Donator Item loadout was fixed."
\ No newline at end of file
diff --git a/html/changelogs/archive/2024-07.yml b/html/changelogs/archive/2024-07.yml
index f4ba46bd432..8b313c04bcb 100644
--- a/html/changelogs/archive/2024-07.yml
+++ b/html/changelogs/archive/2024-07.yml
@@ -125,3 +125,335 @@
lorwp:
- bugfix: extinguishers now can be filled with subtypes of water again (Namely,
holy water)
+2024-07-03:
+ Bisar:
+ - qol: Loot panels should update more predictably and informatively now.
+ - code_imp: Lootpanels have more consistent logic on when they automatically update.
+ LT3:
+ - bugfix: CMO and deluxe hypospray can again hold large vials
+ - bugfix: Fixed tram door warning lights, returning a visual warning for those who
+ don't enjoy being bitten
+ Majkl-J, StrangeWeirdKitten:
+ - bugfix: You can rename items again
+ - bugfix: Donator items properly handle inside the loadout.
+ - bugfix: Ckey and Role restricted items now show in the loadout
+ Melbert:
+ - bugfix: Fixed modsuit interactions slightly. No longer requires combat mode to
+ use tools on it, plasma core works as intended as well. (Using combat mode,
+ however, will make you insert the item)
+ - refactor: Refactored lockboxes
+ - refactor: Refactored medbot skin application
+ MrDas:
+ - bugfix: Bolas now slowdown properly.
+ - bugfix: Cult bolas no longer leave in-hand sprite when they ensnare to non-cult
+ user.
+ ShizCalev:
+ - bugfix: Fixed a number of shuttles having parts (such as lattices) completely
+ disappearing.
+ - bugfix: Fixed the ceilings above shuttles on station maps being full-bright.
+ - bugfix: Fixed lattices sometimes appearing at random locations in space on station
+ maps.
+ - bugfix: Cleaned up a number of accidentally placed objects in space across all
+ station maps.
+ - bugfix: Fixed a false positive with the mapload_space_verification unit test failing
+ on turfs that weren't actually part of shuttles.
+ - code_imp: Added a unit test that automatically finds all base space turfs with
+ objects on them, as well as non-space turfs that are set to space areas (meaning
+ that these squares weren't lit properly.)
+ SkyratBot:
+ - bugfix: fixed Birdshot bar and surrounding areas not properly connected to the
+ power grid.
+ - bugfix: Inducers now accept megacells AND regular cells.
+ - rscdel: "Removed Trichromatic Larynx per @\_mothblocks decision that it looks\
+ \ uggo."
+ - rscdel: Replaced heckacious laryncks' color and size changes with random bolding,
+ italics, and underlining.
+ - rscadd: Stoner has been un-locked and replaces TL in the above's recipe.
+ - bugfix: Fixed elastic arms users being unable to use abstract items like mending
+ touch or shock touch.
+ - bugfix: Fixed mending touch being bad
+ - qol: It is easier to do some required techweb experiments on Charlie station now
+ - bugfix: Fixed experimental dissection surgeries giving too many points
+ - balance: 'Techweb: Moved NTNet relay back to tcomms node'
+ - balance: 'Techweb: Moved blood pack and chem pack to the starting node'
+ - code_imp: Moved bedsheet bin interactions to the item interaction code. Please
+ report any issues.
+ - qol: Made bedsheet bin tool interactions right click, such that left click is
+ consistently for putting in items.
+ - qol: Added usage screentips to bedsheet bins.
+ - qol: Added more feedback to failing to hide items in bedsheet bins.
+ - sound: Made putting items in bedsheet bins not silent (If the items have associated
+ pickup/drop sounds).
+ - image: Fixed shading on some metal ingots.
+ - code_imp: Deconstructing light switches now uses the proper tool action and tool
+ usage code, please report any issues.
+ - bugfix: Attempting to deconstruct a light switch by unscrewing it no longer makes
+ you hit it even on a success.
+ - sound: Deconstructing a light switch actually plays tool usage and deconstruction
+ sounds.
+ - qol: Deconstructing light switches is now a left click with a screwdriver parallel
+ to other unscrewing actions.
+ - bugfix: Screentips for deconstructing a light switch no longer show up on every
+ item EXCEPT screwdrivers.
+ - qol: Added an examine hint denoting light switches are screwed to the wall.
+ - qol: Added visible messages for someone deconstructing a light switch parallel
+ to deconstructing intercoms.
+ - bugfix: portascrubbers also scrub the tile theyre on
+ - rscadd: Ghost role food truck merchants may occasionally turn up at the station.
+ - rscadd: Adds a new fugitive hunter type, MI13 secret agents.
+ - rscadd: Being cursed now enables disembowlements/cranial fissures outside of hardcrit
+ - qol: you can now swap pens in pdas
+ projectkepler-ru:
+ - balance: cocain does not give you speed bonus and instead give you toxin damage
+2024-07-04:
+ DrDiasyl aka DrTuxedo:
+ - sound: Shoves now produce more meaty sound!
+ LT3:
+ - qol: Crew monitor defaults to sort by vitals
+ Rhials:
+ - balance: All cameras in bathrooms and showers have been removed.
+ SkyratBot:
+ - bugfix: cigarettes and vapes are no longer treated as clothing (eg. They no longer
+ get shredded and need to be repaired with... cloth).
+ - bugfix: Brought the fishing rewards experiments to normal values.
+ - image: New linen bin / basket sprites
+ - bugfix: Dark matteors no longer claim that they have missed when they actually
+ impacted the station
+ - rscadd: Added flatpacker & multitool to all R&D labs
+ - bugfix: Removed three traitor posters from Cargo Warehouse's walls. (Metastation)
+ - balance: Added one traitor poster each to the QM's Office, Vault and Evidence
+ Storage to compensate. (Metastation)
+ - bugfix: Bio Emergency crates now bring Bio Suits and Hoods compatible with the
+ Security Hoods and Suits Schematics.
+ - rscadd: 'Added a bitrunning deathmatch map: Island Brawl. Both ghosts and runners
+ get many more spawns than normal.'
+ - bugfix: Lowered the static vision time in domain load in.
+ - bugfix: Fixed the basic Sandwich recipe and tags, now it matches the description
+ and original intent.
+2024-07-05:
+ Bisar:
+ - bugfix: Dastardly clowns will no longer be able to get counted for three times
+ the steps by moving diagonally.
+ DATA_, with sounds made by Beebblie:
+ - sound: You will now be able to hear your muffled breathing while using internals.
+ This can be turned off in the game settings.
+ LT3:
+ - sound: Changed blood filter cycle sound
+ Majkl-J:
+ - bugfix: Fixed the infinite damage exploit on smithing weapons
+ SkyratBot:
+ - qol: You no longer need to reach the very edge of the game screen to reach the
+ maximum zoom range when scoped.
+ - rscadd: Heretics can now sacrifice Cultists for sweet loot.
+ - rscadd: Cultists can now sacrifice Heretics for sick trinkets.
+ - rscdel: Removed Bastard Blade from the game, code, and life
+ - balance: Aggressive Spread now rusts non-turfs next to you as well
+ - balance: Raise Construction cd raised to 7, now breaks rust walls as well, improved
+ autoaim making it actually usable midfight
+ - refactor: Auto-aim code now works on any atoms if configured
+ - bugfix: omni crowbar tool interaction for replacing tiles has been fixed
+ - bugfix: techfab screentip does not runtime when you hover over it with an omnitool
+ multitool
+ - bugfix: medi borgs can do brain surgery again
+ - code_imp: improved multitool & general tool code for some machines
+ - bugfix: 'Fixed the following shuttles flying in unexpected directions: Pubby escape
+ pod, Humpback emergency shuttle, Pubby monastery shuttle, Birdboat emergency
+ shuttle, Birdshot emergency shuttle'
+ - code_imp: improved mouse drag & drop code
+ - bugfix: deathmatch OSHA Violator map has actually functioning field gens now
+ - bugfix: being polymorphed in deathmatch does not count you dead
+ - bugfix: deathmatch cyborgs may not talk in binary
+ - qol: deathmatch map names are sorted alphabetically
+ - spellcheck: deathmatch final destination clown and chef loadouts have been renamed
+ to avoid confusion
+ - bugfix: Fixed toggleable screen colors for glasses.
+ - code_imp: De-hardcoded, refactored the above, now-fixed feature.
+ - rscadd: Pumpkin hardhats and the hood of the flash suit now affect the color of
+ your screen.
+ - rscadd: Prism glasses, obtainable through xenobiology crossbreeding, now also
+ affect the color of your screen.
+ - rscadd: You can now microwave station pets that you can pick up, with predictable
+ outcomes
+ - image: The sprites for the projectile dampener field have been updated
+ - code_imp: Moved bedsheet interactions to the item interaction code. Please report
+ any issues.
+ - bugfix: Bedsheets adjust their offset to match that of the living they're tucking
+ in.
+ - sound: Bedsheets use the cloth drop/pickup sounds instead of being silent.
+ - qol: You can tuck someone in telekinetically.
+ - rscadd: mecha wire that shoots you or anyone nearby if pulsed or cut
+ - bugfix: mecha shock wire shocks you
+ - bugfix: Reverts the fully interactive lootpanel, please just refresh it if you
+ want to see new contents
+ - bugfix: Fixed telekinesis letting you grab people at a distance, and teleport
+ them to you by strangling them.
+ - image: Updated diamond stacks to fit its "unstacked" version.
+ - bugfix: plumbing machinery should have consistent volumes throughout the course
+ of its operations
+ - bugfix: plumbing iv drop now only accepts reagents from ducts but won't put reagents
+ back into it i.e. it only has a input pipe
+ - rscadd: The Illumination Applications technode now includes a gunkit for a new
+ gun, the Photon Cannon.
+ - qol: Transhumanist and prosthetic limb no longer conflict. If you pick the same
+ limb for both it uses the weaker prosthetic (dumbass)
+ - code_imp: Made the name for prosthetic limb global list more intelligible
+ - bugfix: Birdshot's AI core no longer has windoors concealed under open shutters.
+ - balance: removed tasers, the unfirable turret gun, death wand, polymorph wands
+ and the enchanted modsuit from lootbox loot tables
+ - bugfix: lootbox guns should now mostly not have syndicate firing pins so you can
+ actually use them
+ - image: Some gloves have received new sprites
+ - rscadd: Added Pipe Scrubber portable atmos machine
+ - rscadd: 254 new vox lines
+ - bugfix: fixes not being able to clean microwaves
+ - bugfix: 'Techweb: Moved upgraded cyber organs to tier 3 from tier 4'
+ - qol: 'Techweb: Added fully augmented android scan discount experiment for Advanced
+ Cybernetic Organs techweb node'
+ - rscadd: updated Icebox's lavaland ruin. The incursion grows...
+ - bugfix: Bitrunning hacker aliases are now much more permissive
+ - bugfix: you can hold your wound while resting via ctrl click
+ - bugfix: Mending touch no longer damages non-humans
+ - bugfix: fixes mansus grasp not clearing runes
+ - image: Fixed the cargo crate having wrong stripe width in open state
+ - bugfix: material container won't consume the contents of an item if that item
+ itself is rejected for any reason
+ - qol: you can use any storage medium & not just bags/boxes to dump stuff into material
+ containers
+ - balance: Facehuggers dont make people go to sleep but muffles speech
+ - code_imp: Very very minor xenomorph code cleanup
+ - refactor: Muzzles are now an element
+ - refactor: Bayonet attachment is now a component.
+ - code_imp: Improves cpu performance of transferring & removing reagents
+ - rscadd: the Feast of Owls now leaves more dramatic messages in your chat
+ - sound: the Feast of Owls now has a little theme
+ - rscdel: You can no longer get turned into a dullahan by a genetic meltdown
+ - refactor: Refactored genetic meltdown code into datums, making it more extendable.
+ - qol: Data disks are now printed in lathes instead of circuit imprinter
+ - bugfix: Instruments enhanced with portable tuning can perform the rites that have
+ been granted
+ thegrb93:
+ - bugfix: Fix mask sprites for teshari/vox
+ theselfish:
+ - rscdel: Minecraft Youtuber plush is gone.
+2024-07-06:
+ Jolly:
+ - code_imp: Behind the scenes, maps had a little bit of tweaking. If you see things
+ rendering in weird ways, or they don't look like how they used to/supposed to,
+ please report them on GitHub!
+ Majkl-J:
+ - bugfix: Several techwebs now handle points and experiments correctly
+ Melbert:
+ - rscadd: Humonkeys and random corpse spawns now look more... human.
+ SkyratBot:
+ - rscadd: You can now craft a singularity hammer using all nine anomaly cores and
+ the supermatter.
+ - refactor: Vat grower is a normal machine now that doesn't need plumbing
+ - qol: Cytology petri dish smartfridge starts with 3 random samples
+ - rscadd: Added protein powder condiment bottle, available in the cytology supplies
+ locker and the cargo pack
+ - rscdel: Removed science variant of plumbing RCD
+ - bugfix: Fixed a bug when machines without stock parts didn't spawn frames on deconstruction
+ - bugfix: Fixed vat grower not growing
+ - balance: 'Techweb: High yield explosive is now a discount experiment for Exotic
+ Ammo node, giving more free points'
+ - balance: 'Techweb: Replaced the botany/xenobio discount experiments with a new
+ mutant scan experiment for the Gene Engineering node'
+ - balance: 'Techweb: Damaged mech scan is a discount experiment for tier 4 Mech
+ Energy Guns node, giving twice more free points'
+ - balance: 'Techweb: Added a new anomaly scan experiment that gives full discount
+ for the Advanced Anomaly Shells, also moving the node to tier 5'
+ - balance: 'Techweb: Moved mech RCD to the Experimental Tools node'
+ - balance: 'Techweb: Moved handcuffs to T2 security node, leaving only zipties in
+ T1'
+ - balance: 'Techweb: Non-human Autopsy is a discount experiment instead of a required
+ one now'
+ - balance: 'Techweb: Parts scan experiments require 6 machines instead of 4'
+ - rscdel: 'Techweb: Removed the New Toys node moving contents to sec and medbay
+ trees'
+ - qol: Made air horn craftable from a spraycan and a bikehorn
+ - qol: 'Techweb: Better wording on the slime scan experiment'
+ - balance: Settlers are a bit slower on their steeds.
+ - image: Goody case locked/unlocked sprites have been swapped again, having a gap
+ for unlocked and no gap for locked.
+ - qol: Increased view_range on the Psyker Bounty Hunters' Shuttle navigation console.
+ - bugfix: 'Fixed the shuttle navigation console camera eye being incorrectly offset
+ on: the SpacePol van, the Russian bounty hunters, the default bounty hunters,
+ the Psyker bounty hunters, and the MI13 Foodtruck.'
+ - bugfix: Fixed the SpacePol van, Russian bounty hunters, default bounty hunters,
+ and Psyker bounty hunters' shuttles all flying in incorrect directions.
+ - qol: Linked speech now prevents messages from highlighting if its a message you
+ sent, or if you are the owner of said link..
+ - qol: Linked speech messages are now marked as radio messages, for chat tab purposes.
+ - rscadd: 'New traitor mech: Justice. Emag fabricator to get it.'
+ - qol: portable scubber, pump and pipe scrubber are buildable as any other machines
+ Sosmaster9000:
+ - balance: 'Xenobiologists now have a BZ-filled containment pen. Don''t breathe
+ that!
+
+ balance:Some items which either cannot be printed elsewhere or are part of game
+ progression (e.g. teleporter endpoints and, of course, Nanners) have been moved.
+ Alien Spawnpoints are in the same spot.'
+ thegrb93:
+ - bugfix: Fixes the rest of vox/teshari custom sprites
+ - bugfix: Borg affection module tongue/nose not working
+2024-07-07:
+ Majkl-J:
+ - bugfix: Fixed some loadout items missing
+ - balance: Changed the event horizon to not be a "Destroy 5x5 area every 10 seconds"
+ weapon, instead making it into a single target annihilator
+ Seven:
+ - balance: Bloody footprint trails go a longer distance now.
+ SkyratBot:
+ - bugfix: Autolathe won't eat the borg omni tool
+ - qol: Examining a windoor will now provide the construction steps, like other standard
+ buildable objects.
+ - bugfix: Reagent containers are no longer mysteriously fireproof.
+ - rscadd: Projectile Dampener field now reduces stuns and stamina damage from incoming
+ projectiles
+ - bugfix: Binary devices no longer ignore the volume of the output pipe network
+ - bugfix: Firefighting backpack tank nozzle can be used to hit objects and destroy
+ atmos resin again.
+ - code_imp: Cleaned up relic code.
+ - image: Added necrotech themed relics.
+ - bugfix: Fixed temp n space adaptations not conflicting
+ - image: fixed and touched up the icons for the Doorjack and Cryptographic Sequencer
+ (EMAG)
+ - bugfix: Fixed the augmented organs experiment
+ - bugfix: fixed ninja's adrenaline boost module not being rechargable
+ - qol: CE and Engineer start with their hardhat as the respective welding hard hat
+ variant
+2024-07-08:
+ Imaginos16 (sprites) TheSelfish (port):
+ - rscadd: New, ship like Captain clothes, now in the vendor!
+ Majkl-J:
+ - bugfix: Digitigrade pant sprites work again
+ - bugfix: Fixed loadout descriptions being gone
+ SkyratBot:
+ - bugfix: ' Birdshot botany is resized. bar is also smaller.'
+ - bugfix: Birdshot public shrine now uses the correct dirt. (Goodbye chasms)
+ - bugfix: ' Examining a fishing spot twice with sufficiently high fishing skill
+ (or the skillchip) will get you a list of fishes that can ACTUALLY be caught'
+ - bugfix: Fixes hacker alias name preference not working.
+ - bugfix: Added a forgotten firelock on second floor of wawa
+ - bugfix: you may now use a legion core on an elite tumor
+ - spellcheck: Renamed the organ thrower module to the 'organizer'
+ - spellcheck: Made its description more accurate. Did you know it instantly replaces
+ up to 5 organs in active surgery?
+ - bugfix: defibrillator no longer has an abstract cell
+ - code_imp: defibrillator attackby replaced with item_interaction
+ - bugfix: You require breathing once more.
+ - bugfix: you can craft the singulo hammer with either a supermatter engine/shard
+ again
+ - rscadd: There's a previously undiscovered variant of a nullrod, recently revealed
+ from the depths of the skating rink..
+ - balance: You can only stun someone with a shove if they have been shoved into
+ objects or people, or have been hit by telescopic batons, contractor batons
+ or the Mansus Grasp.
+ - balance: If someone is knocked to the floor, however, you can still disarm them
+ of any object with a shove.
+ - bugfix: Knight helmets no longer burn as though made of cloth.
+ lorwp:
+ - rscadd: using 'skibidi' as a chaplain deity now gives you brain damage
+ theselfish:
+ - qol: Telecomms Sysadmin no longer has to crawl around to get a belt.
+ - bugfix: You can now get Telecomms Drip if it burns off.
diff --git a/icons/effects/96x96.dmi b/icons/effects/96x96.dmi
index 31f26c3e6e1..38921a6e484 100644
Binary files a/icons/effects/96x96.dmi and b/icons/effects/96x96.dmi differ
diff --git a/icons/effects/beam.dmi b/icons/effects/beam.dmi
index fd7bee8ed60..ae668b0495a 100644
Binary files a/icons/effects/beam.dmi and b/icons/effects/beam.dmi differ
diff --git a/icons/effects/bitrunning.dmi b/icons/effects/bitrunning.dmi
index 8efa429389c..9a2e9c0228a 100644
Binary files a/icons/effects/bitrunning.dmi and b/icons/effects/bitrunning.dmi differ
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index a6723e6b234..efbe4989c64 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/effects/fields.dmi b/icons/effects/fields.dmi
index f87e1f3975e..1e428707876 100644
Binary files a/icons/effects/fields.dmi and b/icons/effects/fields.dmi differ
diff --git a/icons/effects/random_spawners.dmi b/icons/effects/random_spawners.dmi
index ed6c0c8702e..4e560833003 100644
Binary files a/icons/effects/random_spawners.dmi and b/icons/effects/random_spawners.dmi differ
diff --git a/icons/mob/actions/actions_cult.dmi b/icons/mob/actions/actions_cult.dmi
index f73f5bb6367..7725ae691b4 100644
Binary files a/icons/mob/actions/actions_cult.dmi and b/icons/mob/actions/actions_cult.dmi differ
diff --git a/icons/mob/actions/actions_ecult.dmi b/icons/mob/actions/actions_ecult.dmi
index 67b6bf1fd22..3ce72ae54ff 100644
Binary files a/icons/mob/actions/actions_ecult.dmi and b/icons/mob/actions/actions_ecult.dmi differ
diff --git a/icons/mob/actions/actions_mecha.dmi b/icons/mob/actions/actions_mecha.dmi
index 7c659ca3b57..60df1b5ed1a 100644
Binary files a/icons/mob/actions/actions_mecha.dmi and b/icons/mob/actions/actions_mecha.dmi differ
diff --git a/icons/mob/clothing/belt.dmi b/icons/mob/clothing/belt.dmi
index 44ede20f2a5..a2d318adfb7 100644
Binary files a/icons/mob/clothing/belt.dmi and b/icons/mob/clothing/belt.dmi differ
diff --git a/icons/mob/clothing/eyes.dmi b/icons/mob/clothing/eyes.dmi
index 20bf4d18381..92b155f62d5 100644
Binary files a/icons/mob/clothing/eyes.dmi and b/icons/mob/clothing/eyes.dmi differ
diff --git a/icons/mob/clothing/hands.dmi b/icons/mob/clothing/hands.dmi
index a670c33b7dc..ded01542e31 100644
Binary files a/icons/mob/clothing/hands.dmi and b/icons/mob/clothing/hands.dmi differ
diff --git a/icons/mob/clothing/head/helmet.dmi b/icons/mob/clothing/head/helmet.dmi
index 8b29c935f5e..116f0256a62 100644
Binary files a/icons/mob/clothing/head/helmet.dmi and b/icons/mob/clothing/head/helmet.dmi differ
diff --git a/icons/mob/clothing/neck.dmi b/icons/mob/clothing/neck.dmi
index a193fbd74a8..ed0bb34b464 100644
Binary files a/icons/mob/clothing/neck.dmi and b/icons/mob/clothing/neck.dmi differ
diff --git a/icons/mob/inhands/64x64_lefthand.dmi b/icons/mob/inhands/64x64_lefthand.dmi
index e0545c0d893..d15a47206f9 100644
Binary files a/icons/mob/inhands/64x64_lefthand.dmi and b/icons/mob/inhands/64x64_lefthand.dmi differ
diff --git a/icons/mob/inhands/64x64_righthand.dmi b/icons/mob/inhands/64x64_righthand.dmi
index bf101ad0ea7..88ad954734b 100644
Binary files a/icons/mob/inhands/64x64_righthand.dmi and b/icons/mob/inhands/64x64_righthand.dmi differ
diff --git a/icons/mob/inhands/clothing/gloves_lefthand.dmi b/icons/mob/inhands/clothing/gloves_lefthand.dmi
index 6277d146ffc..4d191e42939 100644
Binary files a/icons/mob/inhands/clothing/gloves_lefthand.dmi and b/icons/mob/inhands/clothing/gloves_lefthand.dmi differ
diff --git a/icons/mob/inhands/clothing/gloves_righthand.dmi b/icons/mob/inhands/clothing/gloves_righthand.dmi
index 7479dd8c505..f8ce306cc98 100644
Binary files a/icons/mob/inhands/clothing/gloves_righthand.dmi and b/icons/mob/inhands/clothing/gloves_righthand.dmi differ
diff --git a/icons/mob/inhands/clothing/masks_lefthand.dmi b/icons/mob/inhands/clothing/masks_lefthand.dmi
index 64fcf4d70bd..cac122916bd 100644
Binary files a/icons/mob/inhands/clothing/masks_lefthand.dmi and b/icons/mob/inhands/clothing/masks_lefthand.dmi differ
diff --git a/icons/mob/inhands/clothing/masks_righthand.dmi b/icons/mob/inhands/clothing/masks_righthand.dmi
index 99f45bd1b17..f68f2d905e6 100644
Binary files a/icons/mob/inhands/clothing/masks_righthand.dmi and b/icons/mob/inhands/clothing/masks_righthand.dmi differ
diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi
index 967968b339b..86bd5020f4f 100644
Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ
diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi
index beb3c84860d..a6de8b97464 100644
Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ
diff --git a/icons/mob/nonhuman-player/cult.dmi b/icons/mob/nonhuman-player/cult.dmi
index 9241b138227..683ee9bd6fe 100644
Binary files a/icons/mob/nonhuman-player/cult.dmi and b/icons/mob/nonhuman-player/cult.dmi differ
diff --git a/icons/mob/rideables/mech_construct.dmi b/icons/mob/rideables/mech_construct.dmi
index c25eb971f2e..656322b8187 100644
Binary files a/icons/mob/rideables/mech_construct.dmi and b/icons/mob/rideables/mech_construct.dmi differ
diff --git a/icons/mob/rideables/mech_construction.dmi b/icons/mob/rideables/mech_construction.dmi
index b7b54e3f70e..f9b2adf8b34 100644
Binary files a/icons/mob/rideables/mech_construction.dmi and b/icons/mob/rideables/mech_construction.dmi differ
diff --git a/icons/mob/rideables/mecha.dmi b/icons/mob/rideables/mecha.dmi
index f6dcbdec2b7..c7f46a51466 100644
Binary files a/icons/mob/rideables/mecha.dmi and b/icons/mob/rideables/mecha.dmi differ
diff --git a/icons/mob/rideables/vehicles.dmi b/icons/mob/rideables/vehicles.dmi
index 7e2b8a05f20..08923668d26 100644
Binary files a/icons/mob/rideables/vehicles.dmi and b/icons/mob/rideables/vehicles.dmi differ
diff --git a/icons/obj/antags/cult/items.dmi b/icons/obj/antags/cult/items.dmi
index 9a3435dcd83..fcd5f13c85b 100644
Binary files a/icons/obj/antags/cult/items.dmi and b/icons/obj/antags/cult/items.dmi differ
diff --git a/icons/obj/antags/cult/structures.dmi b/icons/obj/antags/cult/structures.dmi
index 373371f5a2f..982742e8764 100644
Binary files a/icons/obj/antags/cult/structures.dmi and b/icons/obj/antags/cult/structures.dmi differ
diff --git a/icons/obj/antags/eldritch.dmi b/icons/obj/antags/eldritch.dmi
index 7f6af6bfe2e..664311e5c7c 100644
Binary files a/icons/obj/antags/eldritch.dmi and b/icons/obj/antags/eldritch.dmi differ
diff --git a/icons/obj/card.dmi b/icons/obj/card.dmi
index de23f65b516..d926f462c8a 100644
Binary files a/icons/obj/card.dmi and b/icons/obj/card.dmi differ
diff --git a/icons/obj/cigarettes.dmi b/icons/obj/cigarettes.dmi
index 3612c747f50..1be85df6c15 100644
Binary files a/icons/obj/cigarettes.dmi and b/icons/obj/cigarettes.dmi differ
diff --git a/icons/obj/clothing/glasses.dmi b/icons/obj/clothing/glasses.dmi
index fd898d3105f..97f692551f9 100644
Binary files a/icons/obj/clothing/glasses.dmi and b/icons/obj/clothing/glasses.dmi differ
diff --git a/icons/obj/clothing/gloves.dmi b/icons/obj/clothing/gloves.dmi
index d5099c64e7f..465340870dd 100644
Binary files a/icons/obj/clothing/gloves.dmi and b/icons/obj/clothing/gloves.dmi differ
diff --git a/icons/obj/clothing/head/helmet.dmi b/icons/obj/clothing/head/helmet.dmi
index 4d43b542051..fe52d505e43 100644
Binary files a/icons/obj/clothing/head/helmet.dmi and b/icons/obj/clothing/head/helmet.dmi differ
diff --git a/icons/obj/clothing/masks.dmi b/icons/obj/clothing/masks.dmi
index 5cb090978ae..436785ce6e5 100644
Binary files a/icons/obj/clothing/masks.dmi and b/icons/obj/clothing/masks.dmi differ
diff --git a/icons/obj/clothing/modsuit/mod_modules.dmi b/icons/obj/clothing/modsuit/mod_modules.dmi
index a43d6e42b79..52a4f2cfdd5 100644
Binary files a/icons/obj/clothing/modsuit/mod_modules.dmi and b/icons/obj/clothing/modsuit/mod_modules.dmi differ
diff --git a/icons/obj/clothing/neck.dmi b/icons/obj/clothing/neck.dmi
index e937d125ec2..e8726cfcb73 100644
Binary files a/icons/obj/clothing/neck.dmi and b/icons/obj/clothing/neck.dmi differ
diff --git a/icons/obj/devices/artefacts.dmi b/icons/obj/devices/artefacts.dmi
new file mode 100644
index 00000000000..75a6f754fbb
Binary files /dev/null and b/icons/obj/devices/artefacts.dmi differ
diff --git a/icons/obj/devices/artifacts.dmi b/icons/obj/devices/artifacts.dmi
deleted file mode 100644
index f9402be79a6..00000000000
Binary files a/icons/obj/devices/artifacts.dmi and /dev/null differ
diff --git a/icons/obj/food/containers.dmi b/icons/obj/food/containers.dmi
index 6c2eda19866..f20b54d9c31 100644
Binary files a/icons/obj/food/containers.dmi and b/icons/obj/food/containers.dmi differ
diff --git a/icons/obj/pipes_n_cables/atmos.dmi b/icons/obj/pipes_n_cables/atmos.dmi
index 91badbf3ccf..fc67ff54158 100644
Binary files a/icons/obj/pipes_n_cables/atmos.dmi and b/icons/obj/pipes_n_cables/atmos.dmi differ
diff --git a/icons/obj/pipes_n_cables/hydrochem/plumbers.dmi b/icons/obj/pipes_n_cables/hydrochem/plumbers.dmi
index 555b6c6328b..2f10725a0a8 100644
Binary files a/icons/obj/pipes_n_cables/hydrochem/plumbers.dmi and b/icons/obj/pipes_n_cables/hydrochem/plumbers.dmi differ
diff --git a/icons/obj/science/vatgrowing.dmi b/icons/obj/science/vatgrowing.dmi
index 646aad055cc..ec8b5dcb6ac 100644
Binary files a/icons/obj/science/vatgrowing.dmi and b/icons/obj/science/vatgrowing.dmi differ
diff --git a/icons/obj/stack_objects.dmi b/icons/obj/stack_objects.dmi
index 3d1e736ff61..020e1c87099 100644
Binary files a/icons/obj/stack_objects.dmi and b/icons/obj/stack_objects.dmi differ
diff --git a/icons/obj/storage/case.dmi b/icons/obj/storage/case.dmi
index a47c86eea9f..a29fd3a6f37 100644
Binary files a/icons/obj/storage/case.dmi and b/icons/obj/storage/case.dmi differ
diff --git a/icons/obj/storage/crates.dmi b/icons/obj/storage/crates.dmi
index 0a8640860ec..34d5db6f3e8 100644
Binary files a/icons/obj/storage/crates.dmi and b/icons/obj/storage/crates.dmi differ
diff --git a/icons/obj/structures.dmi b/icons/obj/structures.dmi
index 94b88301f7a..becab20e591 100644
Binary files a/icons/obj/structures.dmi and b/icons/obj/structures.dmi differ
diff --git a/icons/obj/wallmounts.dmi b/icons/obj/wallmounts.dmi
index e020a8818e1..e70024a9edb 100644
Binary files a/icons/obj/wallmounts.dmi and b/icons/obj/wallmounts.dmi differ
diff --git a/icons/obj/weapons/guns/energy.dmi b/icons/obj/weapons/guns/energy.dmi
index 5d607026f01..6e8e5c60f68 100644
Binary files a/icons/obj/weapons/guns/energy.dmi and b/icons/obj/weapons/guns/energy.dmi differ
diff --git a/icons/obj/weapons/guns/projectiles.dmi b/icons/obj/weapons/guns/projectiles.dmi
index a13ebcc6360..3be26b2c43f 100644
Binary files a/icons/obj/weapons/guns/projectiles.dmi and b/icons/obj/weapons/guns/projectiles.dmi differ
diff --git a/icons/obj/weapons/khopesh.dmi b/icons/obj/weapons/khopesh.dmi
index 95774e7f6c7..3c4ba40b34a 100644
Binary files a/icons/obj/weapons/khopesh.dmi and b/icons/obj/weapons/khopesh.dmi differ
diff --git a/icons/obj/weapons/sword.dmi b/icons/obj/weapons/sword.dmi
index 9b5a989a051..d9a443cfc26 100644
Binary files a/icons/obj/weapons/sword.dmi and b/icons/obj/weapons/sword.dmi differ
diff --git a/icons/ui_icons/antags/heretic/knowledge.dmi b/icons/ui_icons/antags/heretic/knowledge.dmi
new file mode 100644
index 00000000000..d24de1a5f0e
Binary files /dev/null and b/icons/ui_icons/antags/heretic/knowledge.dmi differ
diff --git a/modular_skyrat/master_files/code/game/objects/items/storage/backpack.dm b/modular_skyrat/master_files/code/game/objects/items/storage/backpack.dm
index f736ae58f2a..673acde12f1 100644
--- a/modular_skyrat/master_files/code/game/objects/items/storage/backpack.dm
+++ b/modular_skyrat/master_files/code/game/objects/items/storage/backpack.dm
@@ -21,7 +21,7 @@
/obj/item/reagent_containers/heroinbrick = 3,
/obj/item/reagent_containers/blacktar = 12,
/obj/item/storage/pill_bottle/stimulant = 9, //ephedrine and coffee. Can actually change whether someone gets out of a runaway situation
- /obj/item/clothing/mask/cigarette/pipe/crackpipe = 15,
+ /obj/item/cigarette/pipe/crackpipe = 15,
/obj/item/toy/cards/deck/syndicate = 10, //1tc, not balance breaking, small but premium commodity
/obj/item/reagent_containers/cup/bottle/morphine = 8,
/obj/item/reagent_containers/syringe/contraband/methamphetamine = 12,
diff --git a/modular_skyrat/master_files/code/modules/clothing/under/jobs/command.dm b/modular_skyrat/master_files/code/modules/clothing/under/jobs/command.dm
index 81742893dd7..ee02a535ac7 100644
--- a/modular_skyrat/master_files/code/modules/clothing/under/jobs/command.dm
+++ b/modular_skyrat/master_files/code/modules/clothing/under/jobs/command.dm
@@ -15,6 +15,12 @@
desc = "A midnight blue kilt, padded with nano-kevlar and adorned with gold and a tartan sash."
icon_state = "capkilt"
+/obj/item/clothing/under/rank/captain/skyrat/shipdress
+ name = "captain's dress"
+ desc = "A blue dress adorned with a gold trim. Perfect for a formal dinner."
+ icon_state = "solgov_dress"
+ can_adjust = FALSE
+
/obj/item/clothing/under/rank/captain/skyrat/imperial
name = "captain's naval jumpsuit"
desc = "A white naval suit adorned with golden epaulets and a rank badge denoting a Captain. There are two ways to destroy a person, kill him, or ruin his reputation."
diff --git a/modular_skyrat/master_files/code/modules/loadout/categories/donator_personal.dm b/modular_skyrat/master_files/code/modules/loadout/categories/donator_personal.dm
index 701e6fe3363..f52d1c1e0e3 100644
--- a/modular_skyrat/master_files/code/modules/loadout/categories/donator_personal.dm
+++ b/modular_skyrat/master_files/code/modules/loadout/categories/donator_personal.dm
@@ -24,7 +24,7 @@
item_path = /obj/item/clothing/suit/furcoat
//ckeywhitelist = list("thedragmeme")
-/datum/loadout_item/under/jumpsuit/black_turtleneck
+/datum/loadout_item/uniform/black_turtleneck
name = "Black turtleneck"
item_path = /obj/item/clothing/under/syndicate/tacticool/black
//ckeywhitelist = list("thedragmeme")
@@ -34,7 +34,7 @@
item_path = /obj/item/clothing/suit/jacket/ryddid
//ckeywhitelist = list("grunnyyy")
-/datum/loadout_item/under/jumpsuit/draculass
+/datum/loadout_item/uniform/draculass
name = "Draculass Dress"
item_path = /obj/item/clothing/under/costume/draculass
//ckeywhitelist = list("grunnyyy", "joe_duhan")
@@ -65,7 +65,7 @@
// ckeywhitelist = list("raxraus")
restricted_roles = list(JOB_BLUESHIELD, JOB_HEAD_OF_SECURITY, JOB_SECURITY_OFFICER, JOB_WARDEN, JOB_DETECTIVE, JOB_CORRECTIONS_OFFICER)
-/datum/loadout_item/under/jumpsuit/rax_banded_uniform
+/datum/loadout_item/uniform/rax_banded_uniform
name = "Banded Uniform"
item_path = /obj/item/clothing/under/rank/security/rax
// ckeywhitelist = list("raxraus")
@@ -76,7 +76,7 @@
item_path = /obj/item/clothing/glasses/welding/steampunk_goggles
//ckeywhitelist = list("goldenalpharex")
-/datum/loadout_item/under/jumpsuit/plasmaman_jax
+/datum/loadout_item/uniform/plasmaman_jax
name = "XuraCorp Biohazard Underfitting"
item_path = /obj/item/clothing/under/plasmaman/jax2
//ckeywhitelist = list("candlejax")
@@ -92,12 +92,12 @@
item_path = /obj/item/clothing/head/helmet/space/plasmaman/candlejax2
// ckeywhitelist = list("candlejax")
-/datum/loadout_item/under/jumpsuit/emissionsuit
+/datum/loadout_item/uniform/emissionsuit
name = "Emission's Suit"
item_path = /obj/item/clothing/under/plasmaman/candlejax
//ckeywhitelist = list("candlejax")
-/datum/loadout_item/under/jumpsuit/anasuit
+/datum/loadout_item/uniform/anasuit
name = "Azulean's Enviro-Suit"
item_path = /obj/item/clothing/under/plasmaman/candlejax2
// ckeywhitelist = list("candlejax")
@@ -117,7 +117,7 @@
item_path = /obj/item/clothing/shoes/clown_shoes/britches
//ckeywhitelist = list("bloodrite")
-/datum/loadout_item/under/jumpsuit/britches_dress
+/datum/loadout_item/uniform/britches_dress
name = "Britches' dress"
item_path = /obj/item/clothing/under/rank/civilian/clown/britches
//ckeywhitelist = list("bloodrite")
@@ -147,7 +147,7 @@
item_path = /obj/item/clothing/head/caligram_cap_tan
// ckeywhitelist = list("farsightednightlight", "raxraus", "1ceres", "marcoalbaredaa", "itzshift_yt", "drifter7371", "AvianAviator", "Katty Kat", "Investigator77", "Dalao Azure", "Socialistion", "ChillyLobster", "Sylvara", "AmZee", "Tf4", "rb303", "Kay_Nite", "whataboutism", "taac", "Halkyon", "Lupo_di_rosa", "Merek2", "lowpowermia", "RyeanBread", "Jesterz7", "Saund_Minah", "Ruediger4")
-/datum/loadout_item/under/jumpsuit/caligram_fatigues_tan
+/datum/loadout_item/uniform/caligram_fatigues_tan
name = "Caligram Tan Fatigues"
item_path = /obj/item/clothing/under/jumpsuit/caligram_fatigues_tan
// ckeywhitelist = list("farsightednightlight", "raxraus", "1ceres", "marcoalbaredaa", "itzshift_yt", "drifter7371", "AvianAviator", "Katty Kat", "Investigator77", "Dalao Azure", "Socialistion", "ChillyLobster", "Sylvara", "AmZee", "Tf4", "rb303", "Kay_Nite", "whataboutism", "taac", "Halkyon", "Lupo_di_rosa", "Merek2", "lowpowermia", "RyeanBread", "Jesterz7", "Saund_Minah", "Ruediger4")
@@ -198,18 +198,18 @@
item_path = /obj/item/clothing/suit/blutigen_kimono
//ckeywhitelist = list("random516")
-/datum/loadout_item/under/jumpsuit/blutigen_undergarment
+/datum/loadout_item/uniform/blutigen_undergarment
name = "Dragon Undergarment"
item_path = /obj/item/clothing/under/custom/blutigen_undergarment
//ckeywhitelist = list("random516")
-/datum/loadout_item/under/jumpsuit/captain_dress
+/datum/loadout_item/uniform/captain_dress
name = "Captain's Dress"
item_path = /obj/item/clothing/under/rank/captain/dress
//ckeywhitelist = list("netrakyram")
restricted_roles = list(JOB_CAPTAIN)
-/datum/loadout_item/under/jumpsuit/netra_dress
+/datum/loadout_item/uniform/netra_dress
name = "Black and Silver Armored Dress"
item_path = /obj/item/clothing/under/rank/blueshield/netra
//ckeywhitelist = list("netrakyram")
@@ -267,15 +267,15 @@
/datum/loadout_item/pocket_items/brightcosmos
name = "Bright Cosmos cigar"
- item_path = /obj/item/clothing/mask/holocigarette/cigar
- //ckeywhitelist = list("lyricalpaws")
+ item_path = /obj/item/holocigarette/cigar
+ ckeywhitelist = list("lyricalpaws")
-/datum/loadout_item/under/jumpsuit/lannese
+/datum/loadout_item/uniform/lannese
name = "Lannese Dress"
item_path = /obj/item/clothing/under/custom/lannese
//ckeywhitelist = list("kathrinbailey")
-/datum/loadout_item/under/jumpsuit/lannese/vambrace
+/datum/loadout_item/uniform/lannese/vambrace
name = "Lannese Dress w/ Vambraces"
item_path = /obj/item/clothing/under/custom/lannese/vambrace
//ckeywhitelist = list("kathrinbailey")
@@ -285,12 +285,12 @@
item_path = /obj/item/clothing/suit/scraparmour
//ckeywhitelist = list("hackertdog")
-/datum/loadout_item/under/jumpsuit/mechanic
+/datum/loadout_item/uniform/mechanic
name = "Mechanic's Overalls"
item_path = /obj/item/clothing/under/misc/skyrat/mechanic
//ckeywhitelist = list("cypressb")
-/datum/loadout_item/under/jumpsuit/mikubikini
+/datum/loadout_item/uniform/mikubikini
name = "starlight singer bikini"
item_path = /obj/item/clothing/under/mikubikini
//ckeywhitelist = list("grandvegeta")
@@ -332,7 +332,7 @@
//ckeywhitelist = list("hackertdog")
restricted_roles = list(JOB_NT_REP)
-/datum/loadout_item/under/jumpsuit/hubertcc
+/datum/loadout_item/uniform/hubertcc
name = "CC Ensign's uniform"
item_path = /obj/item/clothing/under/rank/nanotrasen_consultant/hubert
//ckeywhitelist = list("hackertdog")
@@ -353,7 +353,7 @@
item_path = /obj/item/clothing/suit/hooded/occult
//ckeywhitelist = list("gamerguy14948")
-/datum/loadout_item/under/jumpsuit/occult_outfit
+/datum/loadout_item/uniform/occult_outfit
name = "Occult Collector's Outfit"
item_path = /obj/item/clothing/under/occult
//ckeywhitelist = list("gamerguy14948")
@@ -438,7 +438,7 @@
item_path = /obj/item/toy/plush/skyrat/tesh
//ckeywhitelist = list("tobjv")
-/datum/loadout_item/under/jumpsuit/redhosneck
+/datum/loadout_item/uniform/redhosneck
name = "Black and Red Turtleneck"
item_path = /obj/item/clothing/under/rank/security/head_of_security/alt/roselia
//ckeywhitelist = list("ultimarifox")
@@ -483,7 +483,7 @@
item_path = /obj/item/clothing/suit/toggle/recruiter_jacket
//ckeywhitelist = list("m97screwsyourparents")
-/datum/loadout_item/under/jumpsuit/recruiter_uniform
+/datum/loadout_item/uniform/recruiter_uniform
name = "Recruiter's Uniform"
item_path = /obj/item/clothing/under/recruiter_uniform
//ckeywhitelist = list("m97screwsyourparents")
@@ -493,7 +493,7 @@
item_path = /obj/item/clothing/accessory/hypno_watch
ckeywhitelist = list("slippyjoe")
-/datum/loadout_item/under/jumpsuit/nt_idol
+/datum/loadout_item/uniform/nt_idol
name = "NT Idol's Skirt"
item_path = /obj/item/clothing/under/nt_idol_skirt
//ckeywhitelist = list("tetrako")
@@ -526,7 +526,7 @@
item_path = /obj/item/toy/plush/skyrat/plushie_dan
//ckeywhitelist = list("srq", "superlagg")
-/datum/loadout_item/under/jumpsuit/bubbly_clown
+/datum/loadout_item/uniform/bubbly_clown
name = "Bubbly Clown Dress"
item_path = /obj/item/clothing/under/bubbly_clown/skirt
restricted_roles = list(JOB_CLOWN)
@@ -542,7 +542,7 @@
item_path = /obj/item/toy/plush/skyrat/fox/plushie_jeanne
//ckeywhitelist = list("klb100") // Asked it to be public.
-/datum/loadout_item/under/jumpsuit/tactichill
+/datum/loadout_item/uniform/tactichill
name = "Tactichill Jacket"
item_path = /obj/item/clothing/under/tactichill
//ckeywhitelist = list("kaynite")
@@ -567,7 +567,7 @@
item_path = /obj/item/clothing/shoes/fancy_heels/drag
//ckeywhitelist = list("thedragmeme")
-/datum/loadout_item/under/jumpsuit/bimpcap
+/datum/loadout_item/uniform/bimpcap
name = "Formal Matte Black Captain Uniform"
item_path = /obj/item/clothing/under/bimpcap
restricted_roles = list(JOB_CAPTAIN)
@@ -620,7 +620,7 @@
item_path = /obj/item/clothing/glasses/gold_aviators
//ckeywhitelist = list("nikohyena")
-/datum/loadout_item/under/jumpsuit/caged_dress
+/datum/loadout_item/uniform/caged_dress
name = "Caged Purple Dress"
item_path = /obj/item/clothing/under/caged_dress
//ckeywhitelist = list("thedragmeme")
@@ -635,7 +635,7 @@
item_path = /obj/item/clothing/neck/flower_collar
//ckeywhitelist = list("thedragmeme")
-/datum/loadout_item/under/jumpsuit/goldenkimono
+/datum/loadout_item/uniform/goldenkimono
name = "Short-Sleeved Kimono"
item_path = /obj/item/clothing/under/costume/skyrat/kimono/sigmar
//ckeywhitelist = list("sigmaralkahest")
@@ -682,7 +682,7 @@
/datum/loadout_item/pocket_items/masvedishcigar
name = "Holocigar"
- item_path = /obj/item/clothing/mask/holocigarette/masvedishcigar
+ item_path = /obj/item/holocigarette/masvedishcigar
// Asked it to be public, and as such has no whitelist.
/datum/loadout_item/suit/lt3_armor
@@ -696,7 +696,7 @@
item_path = /obj/item/clothing/suit/jacket/skyy
//ckeywhitelist = list("lt3")
-/datum/loadout_item/under/miscellaneous/lt3_jeans
+/datum/loadout_item/uniform/miscellaneous/lt3_jeans
name = "Silver Jeans"
item_path = /obj/item/clothing/under/pants/skyy
//ckeywhitelist = list("lt3")
@@ -726,7 +726,7 @@
item_path = /obj/item/holosign_creator/hardlight_wheelchair
// ckeywhitelist = list("sqnztb")
-/datum/loadout_item/under/jumpsuit/noble_gambeson
+/datum/loadout_item/uniform/noble_gambeson
name = "Noble Gambeson"
item_path = /obj/item/clothing/under/rank/civilian/chaplain/divine_archer/noble
// ckeywhitelist = list("grasshand")
@@ -741,12 +741,12 @@
item_path = /obj/item/clothing/suit/toggle/labcoat/vic_dresscoat_donator
// ckeywhitelist = list("nikotheguydude")
-/datum/loadout_item/suit/anubite_headpiece
+/datum/loadout_item/head/anubite_headpiece
name = "Anubite Headpiece"
item_path = /obj/item/clothing/head/anubite
//ckeywhitelist = list("vexcint")
-/datum/loadout_item/under/formal/dragon_maid
+/datum/loadout_item/uniform/formal/dragon_maid
name = "Dragon Maid Uniform"
item_path = /obj/item/clothing/under/costume/dragon_maid
//ckeywhitelist = list("sigmaralkahest")
@@ -761,7 +761,7 @@
item_path = /obj/item/clothing/neck/trenchcoat
ckeywhitelist = list("Smol42", "Awers855")
-/datum/loadout_item/under/jumpsuit/old_qm_jumpskirt
+/datum/loadout_item/uniform/old_qm_jumpskirt
name = "Old Quartermaster's Jumpskirt"
item_path = /obj/item/clothing/under/rank/cargo/qm/skirt/old
// ckeywhitelist = list("jasohavents")
diff --git a/modular_skyrat/master_files/code/modules/loadout/categories/mask.dm b/modular_skyrat/master_files/code/modules/loadout/categories/mask.dm
index b7de4ae7edd..cc59cf09f61 100644
--- a/modular_skyrat/master_files/code/modules/loadout/categories/mask.dm
+++ b/modular_skyrat/master_files/code/modules/loadout/categories/mask.dm
@@ -123,11 +123,11 @@
/datum/loadout_item/mask/pipe
name = "Pipe"
- item_path = /obj/item/clothing/mask/cigarette/pipe
+ item_path = /obj/item/cigarette/pipe
/datum/loadout_item/mask/corn_pipe
name = "Corn Cob Pipe"
- item_path = /obj/item/clothing/mask/cigarette/pipe/cobpipe
+ item_path = /obj/item/cigarette/pipe/cobpipe
/datum/loadout_item/mask/plague_doctor
name = "Plague Doctor Mask"
diff --git a/modular_skyrat/master_files/code/modules/loadout/categories/pocket.dm b/modular_skyrat/master_files/code/modules/loadout/categories/pocket.dm
index 449d8d368e4..c49597a9925 100644
--- a/modular_skyrat/master_files/code/modules/loadout/categories/pocket.dm
+++ b/modular_skyrat/master_files/code/modules/loadout/categories/pocket.dm
@@ -76,7 +76,7 @@
/datum/loadout_item/pocket_items/cigar //smoking is bad mkay
name = "Cigar"
- item_path = /obj/item/clothing/mask/cigarette/cigar
+ item_path = /obj/item/cigarette/cigar
/datum/loadout_item/pocket_items/flask
name = "Flask"
@@ -272,4 +272,4 @@
/datum/loadout_item/pocket_items/donator/vape
name = "E-Cigarette"
- item_path = /obj/item/clothing/mask/vape
+ item_path = /obj/item/vape
diff --git a/modular_skyrat/master_files/code/modules/loadout/categories/toys.dm b/modular_skyrat/master_files/code/modules/loadout/categories/toys.dm
index 2177efa0fac..e556808344d 100644
--- a/modular_skyrat/master_files/code/modules/loadout/categories/toys.dm
+++ b/modular_skyrat/master_files/code/modules/loadout/categories/toys.dm
@@ -228,10 +228,6 @@
name = "Edgy Bird Plushie"
item_path = /obj/item/toy/plush/skyrat/edgy_bird
-/datum/loadout_item/toys/tree_ferret
- name = "Tree Ferret Plushie"
- item_path = /obj/item/toy/plush/skyrat/tree_ferret
-
/datum/loadout_item/toys/xixi
name = "Familiar Harpy Plushie"
item_path = /obj/item/toy/plush/skyrat/xixi
diff --git a/modular_skyrat/master_files/code/modules/loadout/categories/uniform.dm b/modular_skyrat/master_files/code/modules/loadout/categories/uniform.dm
index fe44fdcc656..ad27f298f7c 100644
--- a/modular_skyrat/master_files/code/modules/loadout/categories/uniform.dm
+++ b/modular_skyrat/master_files/code/modules/loadout/categories/uniform.dm
@@ -945,17 +945,17 @@
restricted_roles = list(JOB_CAPTAIN)
donator_only = TRUE
-/datum/loadout_item/under/jumpsuit/donator
+/datum/loadout_item/uniform/donator
donator_only = TRUE
-/datum/loadout_item/under/jumpsuit/donator/enclavesergeant
+/datum/loadout_item/uniform/donator/enclavesergeant
name = "Enclave - Sergeant"
item_path = /obj/item/clothing/under/syndicate/skyrat/enclave
-/datum/loadout_item/under/jumpsuit/donator/enclaveofficer
+/datum/loadout_item/uniform/donator/enclaveofficer
name = "Enclave - Officer"
item_path = /obj/item/clothing/under/syndicate/skyrat/enclave/officer
-/datum/loadout_item/under/jumpsuit/donator/blondie
+/datum/loadout_item/uniform/donator/blondie
name = "Blonde Cowboy Uniform"
item_path = /obj/item/clothing/under/rank/security/detective/cowboy/armorless
diff --git a/modular_skyrat/master_files/code/modules/loadout/category_rebalance.dm b/modular_skyrat/master_files/code/modules/loadout/category_rebalance.dm
new file mode 100644
index 00000000000..334f9a6ee66
--- /dev/null
+++ b/modular_skyrat/master_files/code/modules/loadout/category_rebalance.dm
@@ -0,0 +1,25 @@
+/datum/loadout_category/pocket
+ max_allowed = 3
+
+/datum/loadout_category/toys
+ VAR_PRIVATE/max_allowed = 3
+
+/datum/loadout_category/toys/New()
+ . = ..()
+ category_info = "([max_allowed] allowed)"
+
+/datum/loadout_category/toys/handle_duplicate_entires(
+ datum/preference_middleware/loadout/manager,
+ datum/loadout_item/conflicting_item,
+ datum/loadout_item/added_item,
+ list/datum/loadout_item/all_loadout_items,
+)
+ var/list/datum/loadout_item/toys/other_toys_items = list()
+ for(var/datum/loadout_item/toys/other_toys_item in all_loadout_items)
+ other_toys_items += other_toys_item
+
+ if(length(other_toys_items) >= max_allowed)
+ // We only need to deselect something if we're above the limit
+ // (And if we are we prioritize the first item found, FIFO)
+ manager.deselect_item(other_toys_items[1])
+ return TRUE
diff --git a/modular_skyrat/master_files/code/modules/mod/mod_types.dm b/modular_skyrat/master_files/code/modules/mod/mod_types.dm
index f7228cf7499..e1353be35da 100644
--- a/modular_skyrat/master_files/code/modules/mod/mod_types.dm
+++ b/modular_skyrat/master_files/code/modules/mod/mod_types.dm
@@ -4,6 +4,6 @@
/obj/item/mod/module/storage/syndicate,
/obj/item/mod/module/emp_shield,
/obj/item/mod/module/magnetic_harness,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/chameleon,
)
diff --git a/modular_skyrat/master_files/code/modules/projectiles/guns/energy/beam_rifle.dm b/modular_skyrat/master_files/code/modules/projectiles/guns/energy/beam_rifle.dm
new file mode 100644
index 00000000000..c60c9983578
--- /dev/null
+++ b/modular_skyrat/master_files/code/modules/projectiles/guns/energy/beam_rifle.dm
@@ -0,0 +1,12 @@
+/obj/item/gun/energy/event_horizon
+ name = "\improper Event Horizon anti-existential beam rifle"
+ desc = "Nanotrasen developed experimental weapons platform for accelerating heated sub-atomic particles at near-lightspeed by utilizing a\
+ blackhole generator to accelerate them past an immense gravitational field. Keep your fingers away from the barrel during firing.\
+ Might cause unexpected spaggetification"
+
+/obj/projectile/beam/event_horizon
+ damage = HUMAN_HEALTH_MODIFIER * 100
+ damage_type = BRUTE
+ armor_flag = ENERGY
+ range = 150
+ jitter = 5 SECONDS
diff --git a/modular_skyrat/master_files/icons/mob/clothing/head.dmi b/modular_skyrat/master_files/icons/mob/clothing/head.dmi
index 2848ab08cd4..e59edd6bbbf 100644
Binary files a/modular_skyrat/master_files/icons/mob/clothing/head.dmi and b/modular_skyrat/master_files/icons/mob/clothing/head.dmi differ
diff --git a/modular_skyrat/master_files/icons/mob/clothing/suits/armor.dmi b/modular_skyrat/master_files/icons/mob/clothing/suits/armor.dmi
index 9d6de8b7cfc..26f313b6f14 100644
Binary files a/modular_skyrat/master_files/icons/mob/clothing/suits/armor.dmi and b/modular_skyrat/master_files/icons/mob/clothing/suits/armor.dmi differ
diff --git a/modular_skyrat/master_files/icons/mob/clothing/under/command.dmi b/modular_skyrat/master_files/icons/mob/clothing/under/command.dmi
index 9688cb42e71..00f5bdaf4dd 100644
Binary files a/modular_skyrat/master_files/icons/mob/clothing/under/command.dmi and b/modular_skyrat/master_files/icons/mob/clothing/under/command.dmi differ
diff --git a/modular_skyrat/master_files/icons/mob/clothing/under/command_digi.dmi b/modular_skyrat/master_files/icons/mob/clothing/under/command_digi.dmi
index f52dc48fcd7..bd318e134ec 100644
Binary files a/modular_skyrat/master_files/icons/mob/clothing/under/command_digi.dmi and b/modular_skyrat/master_files/icons/mob/clothing/under/command_digi.dmi differ
diff --git a/modular_skyrat/master_files/icons/obj/clothing/hats.dmi b/modular_skyrat/master_files/icons/obj/clothing/hats.dmi
index 24230558ba7..790edbed3d6 100644
Binary files a/modular_skyrat/master_files/icons/obj/clothing/hats.dmi and b/modular_skyrat/master_files/icons/obj/clothing/hats.dmi differ
diff --git a/modular_skyrat/master_files/icons/obj/clothing/suits/armor.dmi b/modular_skyrat/master_files/icons/obj/clothing/suits/armor.dmi
index 948d2d783ed..348808392f6 100644
Binary files a/modular_skyrat/master_files/icons/obj/clothing/suits/armor.dmi and b/modular_skyrat/master_files/icons/obj/clothing/suits/armor.dmi differ
diff --git a/modular_skyrat/master_files/icons/obj/clothing/under/command.dmi b/modular_skyrat/master_files/icons/obj/clothing/under/command.dmi
index 9c5ac713db1..8924a99a6b8 100644
Binary files a/modular_skyrat/master_files/icons/obj/clothing/under/command.dmi and b/modular_skyrat/master_files/icons/obj/clothing/under/command.dmi differ
diff --git a/modular_skyrat/master_files/icons/obj/plushes.dmi b/modular_skyrat/master_files/icons/obj/plushes.dmi
index 5dbd054159a..fd6a83fd508 100644
Binary files a/modular_skyrat/master_files/icons/obj/plushes.dmi and b/modular_skyrat/master_files/icons/obj/plushes.dmi differ
diff --git a/modular_skyrat/modules/additional_circuit/code/_designs.dm b/modular_skyrat/modules/additional_circuit/code/_designs.dm
index bafc9e53281..a7892ddbe1e 100644
--- a/modular_skyrat/modules/additional_circuit/code/_designs.dm
+++ b/modular_skyrat/modules/additional_circuit/code/_designs.dm
@@ -39,7 +39,7 @@
id = "comp_reagent_injector_bluespace"
build_path = /obj/item/circuit_component/reagent_injector_bluespace
-/datum/techweb_node/adv_shells/New()
+/datum/techweb_node/riot_supression/New()
. = ..()
design_ids += "comp_tscanner"
design_ids += "comp_ccharge"
diff --git a/modular_skyrat/modules/advanced_engineering/code/adv_engineering.dm b/modular_skyrat/modules/advanced_engineering/code/adv_engineering.dm
index 29058530673..4031e4b4ea6 100644
--- a/modular_skyrat/modules/advanced_engineering/code/adv_engineering.dm
+++ b/modular_skyrat/modules/advanced_engineering/code/adv_engineering.dm
@@ -16,11 +16,11 @@
/obj/item/mod/module/rad_protection,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/t_ray,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/visor/meson,
)
default_pins = list(
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/magboot/advanced,
)
diff --git a/modular_skyrat/modules/assault_operatives/code/armaments/armament_modules.dm b/modular_skyrat/modules/assault_operatives/code/armaments/armament_modules.dm
index 49a3b58b5f1..b39558276e3 100644
--- a/modular_skyrat/modules/assault_operatives/code/armaments/armament_modules.dm
+++ b/modular_skyrat/modules/assault_operatives/code/armaments/armament_modules.dm
@@ -31,5 +31,5 @@
cost = 3
/datum/armament_entry/assault_operatives/modules/jetpack
- item_type = /obj/item/mod/module/jetpack/advanced
+ item_type = /obj/item/mod/module/jetpack
cost = 5
diff --git a/modular_skyrat/modules/better_vox/code/vox_species.dm b/modular_skyrat/modules/better_vox/code/vox_species.dm
index 261443499a2..2741df87bd7 100644
--- a/modular_skyrat/modules/better_vox/code/vox_species.dm
+++ b/modular_skyrat/modules/better_vox/code/vox_species.dm
@@ -38,13 +38,13 @@
custom_worn_icons = list(
OFFSET_HEAD = VOX_PRIMALIS_HEAD_ICON,
OFFSET_FACEMASK = VOX_PRIMALIS_MASK_ICON,
- LOADOUT_ITEM_SUIT = VOX_PRIMALIS_SUIT_ICON,
- LOADOUT_ITEM_UNIFORM = VOX_PRIMALIS_UNIFORM_ICON,
- LOADOUT_ITEM_GLOVES = VOX_PRIMALIS_GLOVES_ICON,
+ OFFSET_SUIT = VOX_PRIMALIS_SUIT_ICON,
+ OFFSET_UNIFORM = VOX_PRIMALIS_UNIFORM_ICON,
+ OFFSET_GLOVES = VOX_PRIMALIS_GLOVES_ICON,
OFFSET_SHOES = VOX_PRIMALIS_FEET_ICON,
- LOADOUT_ITEM_GLASSES = VOX_PRIMALIS_EYES_ICON,
+ OFFSET_GLASSES = VOX_PRIMALIS_EYES_ICON,
OFFSET_BELT = VOX_PRIMALIS_BELT_ICON,
- LOADOUT_ITEM_MISC = VOX_PRIMALIS_BACK_ICON,
+ OFFSET_BACK = VOX_PRIMALIS_BACK_ICON,
OFFSET_EARS = VOX_PRIMALIS_EARS_ICON,
)
diff --git a/modular_skyrat/modules/black_mesa/code/armaments/armament_miscellaneous.dm b/modular_skyrat/modules/black_mesa/code/armaments/armament_miscellaneous.dm
index 896cb212fac..9edd015a6f3 100644
--- a/modular_skyrat/modules/black_mesa/code/armaments/armament_miscellaneous.dm
+++ b/modular_skyrat/modules/black_mesa/code/armaments/armament_miscellaneous.dm
@@ -13,7 +13,7 @@
cost = 1
/datum/armament_entry/hecu/misc/rollie
- item_type = /obj/item/clothing/mask/cigarette/rollie/cannabis
+ item_type = /obj/item/cigarette/rollie/cannabis
max_purchase = 8
cost = 1
diff --git a/modular_skyrat/modules/borgs/code/robot_items.dm b/modular_skyrat/modules/borgs/code/robot_items.dm
index 5e8727b5e13..2cce354b3a5 100644
--- a/modular_skyrat/modules/borgs/code/robot_items.dm
+++ b/modular_skyrat/modules/borgs/code/robot_items.dm
@@ -781,10 +781,10 @@
desc = "For giving affectionate kisses."
item_flags = NOBLUDGEON
-/obj/item/quadborg_tongue/afterattack(atom/target, mob/user, proximity)
- . = ..()
- if(!proximity || !isliving(target))
- return
+/obj/item/quadborg_tongue/interact_with_atom(atom/target, mob/living/user, list/modifiers)
+ if(!isliving(target))
+ return NONE
+
var/mob/living/silicon/robot/borg = user
var/mob/living/mob = target
@@ -795,8 +795,10 @@
else
borg.visible_message(span_warning("\the [borg] affectionally licks \the [mob]!"), span_notice("You affectionally lick \the [mob]!"))
playsound(borg, 'sound/effects/attackblob.ogg', 50, 1)
+ return ITEM_INTERACT_SUCCESS
else
to_chat(user, span_warning("ERROR: [target] is on the Do Not Lick registry!"))
+ return ITEM_INTERACT_BLOCKING
// Quadruped nose - Boop
/obj/item/quadborg_nose
@@ -808,16 +810,14 @@
item_flags = NOBLUDGEON
force = 0
-/obj/item/quadborg_nose/afterattack(atom/target, mob/user, proximity)
- . = ..()
- if(!proximity)
- return
-
+/obj/item/quadborg_nose/interact_with_atom(atom/target, mob/living/user, list/modifiers)
if(!HAS_TRAIT(target, TRAIT_AFFECTION_AVERSION)) // Checks for Affection Aversion trait
do_attack_animation(target, null, src)
user.visible_message(span_notice("[user] [pick("nuzzles", "pushes", "boops")] \the [target.name] with their nose!"))
+ return ITEM_INTERACT_SUCCESS
else
to_chat(user, span_warning("ERROR: [target] is on the No Nosing registry!"))
+ return ITEM_INTERACT_BLOCKING
/// Better Clamp
/obj/item/borg/hydraulic_clamp/better
diff --git a/modular_skyrat/modules/bsa_overhaul/code/bsa_cannon.dm b/modular_skyrat/modules/bsa_overhaul/code/bsa_cannon.dm
index c43f9f34dc7..96cf0b74db2 100644
--- a/modular_skyrat/modules/bsa_overhaul/code/bsa_cannon.dm
+++ b/modular_skyrat/modules/bsa_overhaul/code/bsa_cannon.dm
@@ -35,26 +35,18 @@
desc = "Generates cannon pulse. Needs to be linked with a fusor."
icon_state = "power_box"
-/obj/machinery/bsa/back/multitool_act(mob/living/user, obj/item/tool)
- if(!multitool_check_buffer(user, tool)) //make sure it has a data buffer
- return
- var/obj/item/multitool/multitool = tool
- multitool.buffer = src
- to_chat(user, span_notice("You store linkage information in [multitool]'s buffer."))
- return TRUE
+/obj/machinery/bsa/back/multitool_act(mob/living/user, obj/item/multitool/tool)
+ tool.buffer = src
+ to_chat(user, span_notice("You store linkage information in [tool]'s buffer."))
/obj/machinery/bsa/front
name = "Bluespace Artillery Bore"
desc = "Do not stand in front of cannon during operation. Needs to be linked with a fusor."
icon_state = "emitter_center"
-/obj/machinery/bsa/front/multitool_act(mob/living/user, obj/item/tool)
- if(!multitool_check_buffer(user, tool)) //make sure it has a data buffer
- return
- var/obj/item/multitool/multitool = tool
- multitool.buffer = src
- to_chat(user, span_notice("You store linkage information in [multitool]'s buffer."))
- return TRUE
+/obj/machinery/bsa/front/multitool_act(mob/living/user, obj/item/multitool/tool)
+ tool.buffer = src
+ to_chat(user, span_notice("You store linkage information in [tool]'s buffer."))
/obj/machinery/bsa/middle
name = "Bluespace Artillery Fusor"
@@ -65,21 +57,18 @@
/// Our linked front piece
var/datum/weakref/front_piece
-/obj/machinery/bsa/middle/multitool_act(mob/living/user, obj/item/tool)
- if(!multitool_check_buffer(user, tool))
- return
- var/obj/item/multitool/multitool = tool
- if(multitool.buffer)
- if(istype(multitool.buffer, /obj/machinery/bsa/back))
- back_piece = WEAKREF(multitool.buffer)
- to_chat(user, span_notice("You link [src] with [multitool.buffer]."))
- multitool.buffer = null
- else if(istype(multitool.buffer, /obj/machinery/bsa/front))
- front_piece = WEAKREF(multitool.buffer)
- to_chat(user, span_notice("You link [src] with [multitool.buffer]."))
- multitool.buffer = null
+/obj/machinery/bsa/middle/multitool_act(mob/living/user, obj/item/multitool/tool)
+ if(tool.buffer)
+ if(istype(tool.buffer, /obj/machinery/bsa/back))
+ back_piece = WEAKREF(tool.buffer)
+ to_chat(user, span_notice("You link [src] with [tool.buffer]."))
+ tool.buffer = null
+ else if(istype(tool.buffer, /obj/machinery/bsa/front))
+ front_piece = WEAKREF(tool.buffer)
+ to_chat(user, span_notice("You link [src] with [tool.buffer]."))
+ tool.buffer = null
else
- to_chat(user, span_warning("[multitool]'s data buffer is empty!"))
+ to_chat(user, span_warning("[tool]'s data buffer is empty!"))
return TRUE
/obj/machinery/bsa/middle/proc/check_completion()
diff --git a/modular_skyrat/modules/cellguns/code/medigun_cells.dm b/modular_skyrat/modules/cellguns/code/medigun_cells.dm
index 1be7712509a..459be7edfa0 100644
--- a/modular_skyrat/modules/cellguns/code/medigun_cells.dm
+++ b/modular_skyrat/modules/cellguns/code/medigun_cells.dm
@@ -425,9 +425,16 @@
name = "salve globule"
icon_state = "glob_projectile"
shrapnel_type = /obj/item/mending_globule/hardlight
- embedding = list("embed_chance" = 100, ignore_throwspeed_threshold = TRUE, "pain_mult" = 0, "jostle_pain_mult" = 0, "fall_chance" = 0)
+ embed_type = /datum/embed_data/salve_globule
damage = 0
+/datum/embed_data/salve_globule
+ embed_chance = 100
+ ignore_throwspeed_threshold = TRUE
+ pain_mult = 0
+ jostle_pain_mult = 0
+ fall_chance = 0
+
/obj/projectile/energy/medical/utility/salve/on_hit(mob/living/target, blocked = 0, pierce_hit)
if(!IsLivingHuman(target)) //No using this on the dead or synths.
return FALSE
@@ -507,7 +514,7 @@
/obj/item/mending_globule/hardlight
name = "salve globule"
desc = "A ball of regenerative synthetic plant matter, contained within a soft hardlight field."
- embedding = list("embed_chance" = 100, ignore_throwspeed_threshold = TRUE, "pain_mult" = 0, "jostle_pain_mult" = 0, "fall_chance" = 0)
+ embed_type = /datum/embed_data/salve_globule
icon = 'modular_skyrat/modules/cellguns/icons/obj/guns/mediguns/misc.dmi'
icon_state = "globule"
heals_left = 40 //This means it'll be heaing 15 damage per type max.
diff --git a/modular_skyrat/modules/clock_cult/code/items/weaponry.dm b/modular_skyrat/modules/clock_cult/code/items/weaponry.dm
index 92bd53531dd..1f40edc6101 100644
--- a/modular_skyrat/modules/clock_cult/code/items/weaponry.dm
+++ b/modular_skyrat/modules/clock_cult/code/items/weaponry.dm
@@ -56,11 +56,14 @@
name = "brass spear"
desc = "A razor-sharp spear made of brass. It thrums with barely-contained energy."
icon_state = "ratvarian_spear"
- embedding = list("max_damage_mult" = 15, "armour_block" = 80)
+ embed_type = /datum/embed_data/spear/brass
throwforce = 36
force = 25
armour_penetration = 24
+/datum/embed_data/spear/brass
+ impact_pain_mult = parent_type::impact_pain_mult + 8
+ remove_pain_mult = parent_type::remove_pain_mult + 8
/obj/item/clockwork/weapon/brass_battlehammer
name = "brass battle-hammer"
diff --git a/modular_skyrat/modules/colony_fabricator/code/design_datums/appliances.dm b/modular_skyrat/modules/colony_fabricator/code/design_datums/appliances.dm
index 1c20413164e..e04345e8ac9 100644
--- a/modular_skyrat/modules/colony_fabricator/code/design_datums/appliances.dm
+++ b/modular_skyrat/modules/colony_fabricator/code/design_datums/appliances.dm
@@ -29,7 +29,7 @@
"frontier_range",
"tabletop_griddle",
)
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000000000000000) // God save you
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = INFINITE) // God save you
hidden = TRUE
show_on_wiki = FALSE
starting_node = TRUE
diff --git a/modular_skyrat/modules/colony_fabricator/code/design_datums/construction.dm b/modular_skyrat/modules/colony_fabricator/code/design_datums/construction.dm
index 3f14c6e0310..3ff31ede14b 100644
--- a/modular_skyrat/modules/colony_fabricator/code/design_datums/construction.dm
+++ b/modular_skyrat/modules/colony_fabricator/code/design_datums/construction.dm
@@ -16,7 +16,7 @@
"prefab_cat_floor_tile",
"colony_fab_plastic_wall_panel",
)
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 50000000000000) // God save you
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = INFINITE) // God save you
hidden = TRUE
show_on_wiki = FALSE
starting_node = TRUE
diff --git a/modular_skyrat/modules/colony_fabricator/code/design_datums/flatpack_machines.dm b/modular_skyrat/modules/colony_fabricator/code/design_datums/flatpack_machines.dm
index d242abc9388..33dde108670 100644
--- a/modular_skyrat/modules/colony_fabricator/code/design_datums/flatpack_machines.dm
+++ b/modular_skyrat/modules/colony_fabricator/code/design_datums/flatpack_machines.dm
@@ -26,7 +26,7 @@
"flatpack_turbine_team_fortress_two",
"flatpack_bootleg_teg",
)
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 50000000000000) // God save you
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = INFINITE) // God save you
hidden = TRUE
show_on_wiki = FALSE
starting_node = TRUE
diff --git a/modular_skyrat/modules/colony_fabricator/code/design_datums/tools.dm b/modular_skyrat/modules/colony_fabricator/code/design_datums/tools.dm
index f44aadeccb5..690e7d7e985 100644
--- a/modular_skyrat/modules/colony_fabricator/code/design_datums/tools.dm
+++ b/modular_skyrat/modules/colony_fabricator/code/design_datums/tools.dm
@@ -8,7 +8,7 @@
"colony_arc_welder",
"colony_compact_drill",
)
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 50000000000000) // God save you
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = INFINITE) // God save you
hidden = TRUE
show_on_wiki = FALSE
starting_node = TRUE
diff --git a/modular_skyrat/modules/command_vendor/code/vending.dm b/modular_skyrat/modules/command_vendor/code/vending.dm
index 684b32a6822..f774d5ced18 100644
--- a/modular_skyrat/modules/command_vendor/code/vending.dm
+++ b/modular_skyrat/modules/command_vendor/code/vending.dm
@@ -27,14 +27,24 @@
/obj/item/clothing/under/rank/captain/skirt = 1,
/obj/item/clothing/under/rank/captain/dress = 1,
/obj/item/clothing/under/rank/captain/skyrat/kilt = 1,
+ /obj/item/clothing/under/rank/captain/skyrat/shipdress = 1,
/obj/item/clothing/under/rank/captain/skyrat/imperial = 1,
/obj/item/clothing/under/rank/captain/skyrat/pilot = 1,
/obj/item/clothing/under/rank/captain/skyrat/pilot/skirt = 1,
/obj/item/clothing/head/hats/caphat/parade = 1,
/obj/item/clothing/head/costume/crown/fancy = 1,
+ /obj/item/clothing/head/hats/caphat/bicorne = 1,
+ /obj/item/clothing/head/hats/caphat/sonnensoldner = 1,
/obj/item/clothing/under/rank/captain/parade = 1,
/obj/item/clothing/suit/armor/vest/capcarapace/captains_formal = 1,
/obj/item/clothing/suit/armor/vest/capcarapace/jacket = 1,
+ /obj/item/clothing/suit/armor/vest/capcarapace/robe = 1,
+ /obj/item/clothing/suit/armor/vest/capcarapace/robe/overseer = 1,
+ /obj/item/clothing/suit/armor/vest/capcarapace/bathrobe = 1,
+ /obj/item/clothing/suit/armor/vest/capcarapace/suitjacket = 1,
+ /obj/item/clothing/suit/armor/vest/capcarapace/gambison = 1,
+ /obj/item/clothing/suit/armor/vest/capcarapace/winterovercoat = 1,
+ /obj/item/clothing/suit/armor/vest/capcarapace/overcoat = 1,
/obj/item/clothing/suit/jacket/capjacket = 1,
/obj/item/clothing/suit/hooded/wintercoat/captain = 1,
/obj/item/clothing/neck/cloak/cap = 1,
@@ -200,6 +210,7 @@
/obj/item/clothing/head/hats/imperial/white = 2,
/obj/item/clothing/head/hats/imperial/red = 5,
/obj/item/clothing/head/hats/imperial/helmet = 5,
+ /obj/item/clothing/suit/armor/vest/bridge = 5,
/obj/item/clothing/glasses/sunglasses/gar/giga = 5,
/obj/item/clothing/head/hats/caphat/naval/fleet_admiral = 5,
/obj/item/clothing/neck/cloak/fleet_admiral = 5,
diff --git a/modular_skyrat/modules/company_imports/code/armament_datums/deforest_medical.dm b/modular_skyrat/modules/company_imports/code/armament_datums/deforest_medical.dm
index 17fa20b6cf1..e39da4ad29b 100644
--- a/modular_skyrat/modules/company_imports/code/armament_datums/deforest_medical.dm
+++ b/modular_skyrat/modules/company_imports/code/armament_datums/deforest_medical.dm
@@ -294,9 +294,9 @@ BUBBER EDIT END*/
name = "MOD injector module"
item_type = /obj/item/mod/module/injector
-/datum/armament_entry/company_import/deforest/medical_modules/organ_thrower
- name = "MOD organ thrower module"
- item_type = /obj/item/mod/module/organ_thrower
+/datum/armament_entry/company_import/deforest/medical_modules/organizer
+ name = "MOD organizer module"
+ item_type = /obj/item/mod/module/organizer
/datum/armament_entry/company_import/deforest/medical_modules/patient_transport
name = "MOD patient transport module"
diff --git a/modular_skyrat/modules/contractor/code/items/modsuit/modsuit.dm b/modular_skyrat/modules/contractor/code/items/modsuit/modsuit.dm
index 24a702f7172..196021816bb 100644
--- a/modular_skyrat/modules/contractor/code/items/modsuit/modsuit.dm
+++ b/modular_skyrat/modules/contractor/code/items/modsuit/modsuit.dm
@@ -39,7 +39,7 @@
applied_modules = list(
/obj/item/mod/module/baton_holster/preloaded/upgraded,
/obj/item/mod/module/emp_shield,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/scorpion_hook,
/obj/item/mod/module/springlock/contractor/no_complexity,
/obj/item/mod/module/storage/syndicate,
diff --git a/modular_skyrat/modules/customization/game/objects/items/plushes.dm b/modular_skyrat/modules/customization/game/objects/items/plushes.dm
index 80cc6d1ead2..9bc6ba494e0 100644
--- a/modular_skyrat/modules/customization/game/objects/items/plushes.dm
+++ b/modular_skyrat/modules/customization/game/objects/items/plushes.dm
@@ -314,14 +314,6 @@
attack_verb_simple = list("peck", "teleport behind", "caw at")
squeak_override = list('modular_skyrat/modules/emotes/sound/voice/caw.ogg' = 1)
-/obj/item/toy/plush/skyrat/tree_ferret
- name = "tree ferret plushy"
- desc = "This plush will always put on a smile to make your day as bright as the sun. Hugging him makes you feel warm and fuzzy. Comes with plush chemical vials to fix your non emotional traumas too!"
- icon_state = "plushie_sels"
- attack_verb_continuous = list("hugs", "cackles at", "health analyzes")
- attack_verb_simple = list("hug", "cackle at", "health analyze")
- squeak_override = list('sound/effects/crunchybushwhack1.ogg' = 1)
-
/obj/item/toy/plush/skyrat/fox/mia
name = "silver fox plushie" //bubber edit on request
desc = "A small stuffed silver fox with a collar tag that says “Eavy” and a tiny bell in its fluffy tail."
diff --git a/modular_skyrat/modules/customization/modules/clothing/head/head.dm b/modular_skyrat/modules/customization/modules/clothing/head/head.dm
index 51a6a734619..67365822350 100644
--- a/modular_skyrat/modules/customization/modules/clothing/head/head.dm
+++ b/modular_skyrat/modules/customization/modules/clothing/head/head.dm
@@ -255,3 +255,19 @@
greyscale_config_worn = /datum/greyscale_config/sweet_bow/worn
greyscale_colors = "#7b9ab5"
flags_1 = IS_PLAYER_COLORABLE_1
+
+/obj/item/clothing/head/hats/caphat/bicorne
+ name = "captain's biscorne"
+ icon = 'modular_skyrat/master_files/icons/obj/clothing/hats.dmi'
+ worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/head.dmi'
+ desc = "It's good overthrowing the king."
+ icon_state = "solgov_bicorne"
+ worn_y_offset = 2
+
+/obj/item/clothing/head/hats/caphat/sonnensoldner
+ name = "captain's feathered hat"
+ desc = "Cannons ready!"
+ icon = 'modular_skyrat/master_files/icons/obj/clothing/hats.dmi'
+ worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/head.dmi'
+ icon_state = "sonnensoldner_hat"
+ worn_y_offset = 4
diff --git a/modular_skyrat/modules/customization/modules/clothing/suits/armor.dm b/modular_skyrat/modules/customization/modules/clothing/suits/armor.dm
index 4f44155ee81..e68eb944678 100644
--- a/modular_skyrat/modules/customization/modules/clothing/suits/armor.dm
+++ b/modular_skyrat/modules/customization/modules/clothing/suits/armor.dm
@@ -30,3 +30,73 @@
worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/suits/armor.dmi'
icon_state = "capjacket_casual"
body_parts_covered = CHEST|ARMS
+
+/obj/item/clothing/suit/armor/vest/capcarapace/robe
+ name = "captain's robe"
+ desc = "A lightweight armored robe in the Captain's colors. For when you want something classy, but regal."
+ icon = 'modular_skyrat/master_files/icons/obj/clothing/suits/armor.dmi'
+ worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/suits/armor.dmi'
+ icon_state = "solgov_robe"
+ body_parts_covered = CHEST|ARMS
+
+/obj/item/clothing/suit/armor/vest/capcarapace/robe/overseer
+ name = "captain's ordained robe"
+ desc = "An armored robe in the Captain's colors. For when you want to lead a cult."
+ icon = 'modular_skyrat/master_files/icons/obj/clothing/suits/armor.dmi'
+ worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/suits/armor.dmi'
+ icon_state = "solgov_overseer_robe"
+
+/obj/item/clothing/suit/armor/vest/capcarapace/bathrobe
+ name = "captain's bathrobe"
+ desc = "Well that's just like, your opinion man."
+ icon = 'modular_skyrat/master_files/icons/obj/clothing/suits/armor.dmi'
+ worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/suits/armor.dmi'
+ icon_state = "solgov_jacket"
+ body_parts_covered = CHEST|ARMS
+
+/obj/item/clothing/suit/armor/vest/capcarapace/bathrobe/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/toggle_icon)
+
+/obj/item/clothing/suit/armor/vest/capcarapace/suitjacket
+ name = "captain's suit jacket"
+ desc = "For the humble Captain."
+ icon = 'modular_skyrat/master_files/icons/obj/clothing/suits/armor.dmi'
+ worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/suits/armor.dmi'
+ icon_state = "solgov_suit"
+ body_parts_covered = CHEST|ARMS
+
+/obj/item/clothing/suit/armor/vest/capcarapace/gambison
+ name = "captain's gambison"
+ desc = "Unrelated to that other Bison fellow. Our lawyers say No! No!"
+ icon = 'modular_skyrat/master_files/icons/obj/clothing/suits/armor.dmi'
+ worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/suits/armor.dmi'
+ icon_state = "solgov_gambison"
+ body_parts_covered = CHEST|ARMS
+
+/obj/item/clothing/suit/armor/vest/capcarapace/winterovercoat
+ name = "captain's winter overcoat"
+ desc = "Not as warm as it looks, especially since it was stol- err, borrowed from the Head of Security."
+ icon = 'modular_skyrat/master_files/icons/obj/clothing/suits/armor.dmi'
+ worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/suits/armor.dmi'
+ icon_state = "solgov_coat"
+ body_parts_covered = CHEST|ARMS
+
+/obj/item/clothing/suit/armor/vest/capcarapace/overcoat
+ name = "captain's overcoat"
+ desc = "Bid our father the Sea King rise from the depths full foul in his fury!"
+ icon = 'modular_skyrat/master_files/icons/obj/clothing/suits/armor.dmi'
+ worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/suits/armor.dmi'
+ icon_state = "solgov_overcoat"
+ body_parts_covered = CHEST|ARMS
+
+/obj/item/clothing/suit/armor/vest/bridge
+ name = "bridge assistant coat"
+ desc = "For those big enough to live on the bridge, but not so big they can reach the buttons."
+ icon = 'modular_skyrat/master_files/icons/obj/clothing/suits/armor.dmi'
+ worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/suits/armor.dmi'
+ icon_state = "coat_solgov"
+
+/obj/item/clothing/suit/armor/vest/bridge/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/toggle_icon)
diff --git a/modular_skyrat/modules/customization/modules/clothing/~donator/donator_clothing.dm b/modular_skyrat/modules/customization/modules/clothing/~donator/donator_clothing.dm
index 012a696e886..056221c318f 100644
--- a/modular_skyrat/modules/customization/modules/clothing/~donator/donator_clothing.dm
+++ b/modular_skyrat/modules/customization/modules/clothing/~donator/donator_clothing.dm
@@ -1498,7 +1498,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/contraband/korpstech, 32)
icon_state = "razurath_coat"
// Donation reward for MaSvedish
-/obj/item/clothing/mask/holocigarette/masvedishcigar
+/obj/item/holocigarette/masvedishcigar
name = "holocigar"
desc = "A soft buzzing device that, using holodeck technology, replicates a slow burn cigar. Now with less-shock technology. It has a small inscription of 'MG' on the golden label."
icon = 'modular_skyrat/master_files/icons/donator/obj/clothing/masks.dmi'
diff --git a/modular_skyrat/modules/customization/modules/clothing/~donator/donator_items.dm b/modular_skyrat/modules/customization/modules/clothing/~donator/donator_items.dm
index 2d6f9d4f501..1fda8c83c65 100644
--- a/modular_skyrat/modules/customization/modules/clothing/~donator/donator_items.dm
+++ b/modular_skyrat/modules/customization/modules/clothing/~donator/donator_items.dm
@@ -1,5 +1,5 @@
//Donator reward for UltramariFox
-/obj/item/clothing/mask/cigarette/khi
+/obj/item/cigarette/khi
name = "\improper Kitsuhana Singularity cigarette"
icon = 'modular_skyrat/master_files/icons/obj/clothing/masks.dmi'
worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/mask.dmi'
@@ -18,7 +18,7 @@
icon = 'modular_skyrat/master_files/icons/obj/cigarettes_khi.dmi'
icon_state = "khi_cig_packet"
base_icon_state = "khi_cig_packet"
- spawn_type = /obj/item/clothing/mask/cigarette/khi
+ spawn_type = /obj/item/cigarette/khi
//Donator reward for Stonetear
/obj/item/hairbrush/switchblade
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/vox.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/vox.dm
index e4dc07e1022..8109b6817fc 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/vox.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/species/vox.dm
@@ -35,13 +35,13 @@
custom_worn_icons = list(
OFFSET_HEAD = VOX_HEAD_ICON,
OFFSET_FACEMASK = VOX_MASK_ICON,
- LOADOUT_ITEM_SUIT = VOX_SUIT_ICON,
- LOADOUT_ITEM_UNIFORM = VOX_UNIFORM_ICON,
- LOADOUT_ITEM_HANDS = VOX_HANDS_ICON,
+ OFFSET_SUIT = VOX_SUIT_ICON,
+ OFFSET_UNIFORM = VOX_UNIFORM_ICON,
+ OFFSET_GLOVES = VOX_HANDS_ICON,
OFFSET_SHOES = VOX_FEET_ICON,
- LOADOUT_ITEM_GLASSES = VOX_EYES_ICON,
+ OFFSET_GLASSES = VOX_EYES_ICON,
OFFSET_BELT = VOX_BELT_ICON,
- LOADOUT_ITEM_MISC = VOX_BACK_ICON,
+ OFFSET_BACK = VOX_BACK_ICON,
OFFSET_EARS = VOX_EARS_ICON
)
diff --git a/modular_skyrat/modules/emotes/code/emotes.dm b/modular_skyrat/modules/emotes/code/emotes.dm
index 472542a145c..312c22b2917 100644
--- a/modular_skyrat/modules/emotes/code/emotes.dm
+++ b/modular_skyrat/modules/emotes/code/emotes.dm
@@ -6,6 +6,7 @@
/datum/emote
// cooldown = EMOTE_DELAY BUBBERSTATION CHANGE: REMOVES ABSURD EMOTE DELAY
+ var/muzzle_ignore = FALSE
//Disables the custom emote blacklist from TG that normally applies to slimes.
/datum/emote/living/custom
@@ -105,7 +106,6 @@
message = "snaps twice."
message_param = "snaps twice at %t."
emote_type = EMOTE_AUDIBLE
- muzzle_ignore = TRUE
hands_use_check = TRUE
vary = TRUE
sound = 'modular_skyrat/modules/emotes/sound/voice/snap2.ogg'
@@ -116,7 +116,6 @@
message = "snaps thrice."
message_param = "snaps thrice at %t."
emote_type = EMOTE_AUDIBLE
- muzzle_ignore = TRUE
hands_use_check = TRUE
vary = TRUE
sound = 'modular_skyrat/modules/emotes/sound/voice/snap3.ogg'
@@ -182,6 +181,7 @@
key_third_person = "squishes"
message = "squishes!"
emote_type = EMOTE_AUDIBLE
+ muzzle_ignore = TRUE
vary = TRUE
sound = 'modular_skyrat/modules/emotes/sound/voice/slime_squish.ogg'
@@ -225,6 +225,7 @@
/datum/emote/living/sniff
vary = TRUE
+ muzzle_ignore = TRUE
/datum/emote/living/sniff/get_sound(mob/living/user)
if(iscarbon(user))
@@ -268,7 +269,6 @@
key = "clap"
key_third_person = "claps"
message = "claps."
- muzzle_ignore = TRUE
hands_use_check = TRUE
emote_type = EMOTE_AUDIBLE
audio_cooldown = 5 SECONDS
@@ -291,7 +291,6 @@
key_third_person = "claps once"
message = "claps once."
emote_type = EMOTE_AUDIBLE
- muzzle_ignore = TRUE
hands_use_check = TRUE
vary = TRUE
mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai)
@@ -529,6 +528,7 @@
key_third_person = "purrs!"
message = "purrs!"
emote_type = EMOTE_AUDIBLE
+ muzzle_ignore = TRUE
vary = TRUE
sound = 'modular_skyrat/modules/emotes/sound/voice/raptor_purr.ogg'
@@ -537,6 +537,7 @@
key_third_person = "purrs!"
message = "purrs!"
emote_type = EMOTE_AUDIBLE
+ muzzle_ignore = TRUE
vary = TRUE
sound = 'modular_skyrat/modules/emotes/sound/voice/feline_purr.ogg'
@@ -569,9 +570,13 @@
key_third_person = "thumps"
message = "thumps their foot!"
emote_type = EMOTE_AUDIBLE
+ muzzle_ignore = TRUE
vary = TRUE
sound = 'sound/effects/glassbash.ogg'
+/datum/emote/living/surrender
+ muzzle_ignore = TRUE
+
/datum/emote/living/mggaow
key = "mggaow"
key_third_person = "meows loudly"
diff --git a/modular_skyrat/modules/emotes/code/laugh_emotes.dm b/modular_skyrat/modules/emotes/code/laugh_emotes.dm
index 5b32018cfc7..0c003d1138f 100644
--- a/modular_skyrat/modules/emotes/code/laugh_emotes.dm
+++ b/modular_skyrat/modules/emotes/code/laugh_emotes.dm
@@ -3,8 +3,6 @@
mob_type_blacklist_typecache = list(/mob/living/carbon/human, /mob/living/basic/slime, /mob/living/brain) //Humans get specialized scream.
/datum/emote/living/scream/get_sound(mob/living/user)
- if(user.is_muzzled())
- return
if(issilicon(user))
var/mob/living/silicon/silicon_user = user
var/datum/scream_type/selected_scream = silicon_user.selected_scream
@@ -39,8 +37,6 @@
/datum/emote/living/carbon/human/scream/get_sound(mob/living/carbon/human/user)
if(!istype(user))
return
- if(user.is_muzzled())
- return
if(isnull(user.selected_scream) || (LAZYLEN(user.selected_scream.male_screamsounds) && LAZYLEN(user.selected_scream.female_screamsounds))) //For things that don't have a selected scream(npcs)
if(prob(1))
return 'sound/voice/human/wilhelm_scream.ogg'
diff --git a/modular_skyrat/modules/emotes/code/scream_emote.dm b/modular_skyrat/modules/emotes/code/scream_emote.dm
index cb28731a8f3..b5f46e6d91f 100644
--- a/modular_skyrat/modules/emotes/code/scream_emote.dm
+++ b/modular_skyrat/modules/emotes/code/scream_emote.dm
@@ -3,8 +3,6 @@
mob_type_blacklist_typecache = list(/mob/living/carbon/human, /mob/living/basic/slime, /mob/living/brain) //Humans get specialized scream.
/datum/emote/living/scream/get_sound(mob/living/user)
- if(user.is_muzzled())
- return
if(issilicon(user))
var/mob/living/silicon/silicon_user = user
var/datum/scream_type/selected_scream = silicon_user.selected_scream
@@ -39,8 +37,6 @@
/datum/emote/living/carbon/human/scream/get_sound(mob/living/carbon/human/user)
if(!istype(user))
return
- if(user.is_muzzled())
- return
if(isnull(user.selected_scream) || (LAZYLEN(user.selected_scream.male_screamsounds) && LAZYLEN(user.selected_scream.female_screamsounds))) //For things that don't have a selected scream(npcs)
if(prob(1))
return 'sound/voice/human/wilhelm_scream.ogg'
diff --git a/modular_skyrat/modules/exp_corps/code/clothing.dm b/modular_skyrat/modules/exp_corps/code/clothing.dm
index d932d13b3ad..7020aaf146c 100644
--- a/modular_skyrat/modules/exp_corps/code/clothing.dm
+++ b/modular_skyrat/modules/exp_corps/code/clothing.dm
@@ -191,7 +191,7 @@
supports_variations_flags = CLOTHING_SNOUTED_VARIATION_NO_NEW_ICON
var/nightvision = FALSE
var/mob/living/carbon/current_user
- actions_types = list(/datum/action/item_action/toggle_nv)
+ actions_types = list(/datum/action/item_action/toggle_nv_helmet)
/datum/armor/helmet_expeditionary_corps
melee = 20
@@ -203,10 +203,10 @@
acid = 100
wound = 10
-/datum/action/item_action/toggle_nv
+/datum/action/item_action/toggle_nv_helmet
name = "Toggle Nightvision"
-/datum/action/item_action/toggle_nv/Trigger(trigger_flags)
+/datum/action/item_action/toggle_nv_helmet/Trigger(trigger_flags)
var/obj/item/clothing/head/helmet/expeditionary_corps/my_helmet = target
if(!my_helmet.current_user)
return
diff --git a/modular_skyrat/modules/exp_corps/code/tomahawk.dm b/modular_skyrat/modules/exp_corps/code/tomahawk.dm
index b08a1e473a5..b03135607d1 100644
--- a/modular_skyrat/modules/exp_corps/code/tomahawk.dm
+++ b/modular_skyrat/modules/exp_corps/code/tomahawk.dm
@@ -13,12 +13,17 @@
throwforce = 18
throw_speed = 4
throw_range = 8
- embedding = list("pain_mult" = 6, "embed_chance" = 60, "fall_chance" = 10)
+ embed_type = /datum/embed_data/tomahawk
attack_verb_continuous = list("chops", "tears", "lacerates", "cuts")
attack_verb_simple = list("chop", "tear", "lacerate", "cut")
hitsound = 'sound/weapons/bladeslice.ogg'
sharpness = SHARP_EDGED
+/datum/embed_data/tomahawk
+ pain_mult = 6
+ embed_chance = 60
+ fall_chance = 10
+
/obj/item/melee/tomahawk/Initialize(mapload)
. = ..()
AddComponent(/datum/component/butchering, 70, 100)
diff --git a/modular_skyrat/modules/filtersandsetters/code/filtersandsetters.dm b/modular_skyrat/modules/filtersandsetters/code/filtersandsetters.dm
index 2f10e2d05c5..858342a9166 100644
--- a/modular_skyrat/modules/filtersandsetters/code/filtersandsetters.dm
+++ b/modular_skyrat/modules/filtersandsetters/code/filtersandsetters.dm
@@ -11,10 +11,11 @@
return list(TOOL_BONESET, TOOL_ALIEN_BONESET)
/datum/wound/item_can_treat(obj/item/potential_treater, mob/user)
- // check if we have a valid treatable tool
- for(var/behaviour in potential_treater.get_all_tool_behaviours())
- if(behaviour in treatable_tools)
- return TRUE
+ . = ..()
+ // check if we have a valid treatable tool
+ for(var/behaviour in potential_treater.get_all_tool_behaviours())
+ if(behaviour in treatable_tools)
+ return TRUE
/datum/wound/blunt/bone/severe
treatable_tools = list(TOOL_ALIEN_BONESET)
@@ -161,15 +162,16 @@
)
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
-/datum/techweb_node/exp_tools
- design_ids = list(
- "exwelder",
- "handdrill",
- "jawsoflife",
- "laserscalpel",
- "mechanicalpinches",
- "rangedanalyzer",
- "searingtool",
- "adv_fire_extinguisher",
+/datum/techweb_node/surgery_tools/New()
+ design_ids += list(
"combitool",
)
+ return ..()
+
+/datum/techweb_node/alien_surgery/New()
+ design_ids += list(
+ "alien_bloodfilter",
+ "alien_bonesetter",
+ )
+ return ..()
+
diff --git a/modular_skyrat/modules/horrorform/code/true_changeling.dm b/modular_skyrat/modules/horrorform/code/true_changeling.dm
index 81f94b1c39d..9024d826885 100644
--- a/modular_skyrat/modules/horrorform/code/true_changeling.dm
+++ b/modular_skyrat/modules/horrorform/code/true_changeling.dm
@@ -148,7 +148,17 @@
ricochet_incidence_leeway = 0
embed_falloff_tile = -2
shrapnel_type = /obj/item/shrapnel/bone_fragment
- embedding = list(embed_chance=55, fall_chance=2, jostle_chance=7, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.7, pain_mult=3, jostle_pain_mult=3, rip_time=15)
+ embed_type = /datum/embed_data/tomahawk
+
+/datum/embed_data/tomahawk
+ embed_chance = 55
+ fall_chance = 2
+ jostle_chance = 7
+ ignore_throwspeed_threshold = TRUE
+ pain_stam_pct = 0.7
+ pain_mult = 3
+ jostle_pain_mult = 3
+ rip_time = 15
/obj/item/shrapnel/bone_fragment
name = "bone fragment"
diff --git a/modular_skyrat/modules/mapping/code/telescreens.dm b/modular_skyrat/modules/mapping/code/telescreens.dm
new file mode 100644
index 00000000000..b3314191dbe
--- /dev/null
+++ b/modular_skyrat/modules/mapping/code/telescreens.dm
@@ -0,0 +1,19 @@
+/* Modular Telescreens */
+
+/obj/machinery/computer/security/telescreen/disposals
+ name = "Disposal Driver Monitor"
+ network = list("Trash")
+
+MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/security/telescreen/disposals, 32)
+
+/obj/machinery/computer/security/telescreen/xeno
+ name = "Xenobiology Monitor"
+ network = list("xeno")
+
+MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/security/telescreen/xeno, 32)
+
+/obj/machinery/computer/security/telescreen/cargo
+ name = "Cargo Camera Monitor"
+ network = list("cargo")
+
+MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/security/telescreen/cargo, 32)
diff --git a/modular_skyrat/modules/marines/code/gear.dm b/modular_skyrat/modules/marines/code/gear.dm
index d468f414361..7f5b6478a84 100644
--- a/modular_skyrat/modules/marines/code/gear.dm
+++ b/modular_skyrat/modules/marines/code/gear.dm
@@ -12,7 +12,6 @@
spread = 6
pin = /obj/item/firing_pin/implant/mindshield
can_suppress = FALSE
- can_bayonet = FALSE
mag_display = TRUE
mag_display_ammo = FALSE
accepted_magazine_type = /obj/item/ammo_box/magazine/m44a
@@ -51,7 +50,7 @@
name = ".300 caseless bullet"
damage = 13
armour_penetration = 30 //gonna actually kill the brit that made this var require a U in armor
- embedding = null
+ embed_data = null
shrapnel_type = null
/obj/item/gun/ballistic/automatic/ar/modular/m44a/scoped
diff --git a/modular_skyrat/modules/microfusion/code/microfusion_energy_master.dm b/modular_skyrat/modules/microfusion/code/microfusion_energy_master.dm
index bb8f396e7b6..1c502060083 100644
--- a/modular_skyrat/modules/microfusion/code/microfusion_energy_master.dm
+++ b/modular_skyrat/modules/microfusion/code/microfusion_energy_master.dm
@@ -11,7 +11,6 @@
inhand_icon_state = "mcr01"
lefthand_file = 'modular_skyrat/modules/microfusion/icons/guns_lefthand.dmi'
righthand_file = 'modular_skyrat/modules/microfusion/icons/guns_righthand.dmi'
- can_bayonet = FALSE
w_class = WEIGHT_CLASS_BULKY
obj_flags = UNIQUE_RENAME
ammo_x_offset = 2
diff --git a/modular_skyrat/modules/microfusion/code/microfusion_gun_attachments.dm b/modular_skyrat/modules/microfusion/code/microfusion_gun_attachments.dm
index 12ceacd5cee..0f5379c6548 100644
--- a/modular_skyrat/modules/microfusion/code/microfusion_gun_attachments.dm
+++ b/modular_skyrat/modules/microfusion/code/microfusion_gun_attachments.dm
@@ -429,17 +429,17 @@ Allows for flashlights bayonets and adds 1 slot to equipment.
microfusion_gun.AddComponent(/datum/component/seclite_attachable, \
light_overlay_icon = 'modular_skyrat/modules/microfusion/icons/microfusion_gun40x32.dmi', \
light_overlay = "flight")
- microfusion_gun.can_bayonet = TRUE
+ microfusion_gun = TRUE
/obj/item/microfusion_gun_attachment/rail/remove_attachment(obj/item/gun/microfusion/microfusion_gun)
. = ..()
var/component_to_delete = microfusion_gun.GetComponent(/datum/component/seclite_attachable)
if(component_to_delete)
qdel(component_to_delete)
- microfusion_gun.can_bayonet = initial(microfusion_gun.can_bayonet)
- if(microfusion_gun.bayonet)
- microfusion_gun.bayonet.forceMove(get_turf(microfusion_gun))
- microfusion_gun.bayonet = null
+ microfusion_gun = initial(microfusion_gun)
+ if(microfusion_gun)
+ microfusion_gun.forceMove(get_turf(microfusion_gun))
+ microfusion_gun = null
microfusion_gun.update_appearance()
microfusion_gun.remove_all_attachments()
diff --git a/modular_skyrat/modules/microfusion/code/microfusion_techweb.dm b/modular_skyrat/modules/microfusion/code/microfusion_techweb.dm
index 4c8b1d82461..08c2db03f1a 100644
--- a/modular_skyrat/modules/microfusion/code/microfusion_techweb.dm
+++ b/modular_skyrat/modules/microfusion/code/microfusion_techweb.dm
@@ -113,7 +113,6 @@
description = "Microfusion tech that is proprietary tech of Honkicron Clownery Systems. HONK!!"
prereq_ids = list(
TECHWEB_NODE_BASIC_MICROFUSION,
- TECHWEB_NODE_TOYS,
)
design_ids = list(
"microfusion_gun_attachment_honk",
diff --git a/modular_skyrat/modules/modular_implants/code/nifsofts/prop_summoner.dm b/modular_skyrat/modules/modular_implants/code/nifsofts/prop_summoner.dm
index aab1cc52b7a..7fbdeb04e6f 100644
--- a/modular_skyrat/modules/modular_implants/code/nifsofts/prop_summoner.dm
+++ b/modular_skyrat/modules/modular_implants/code/nifsofts/prop_summoner.dm
@@ -35,7 +35,7 @@
/obj/item/toy/foamblade/nanite,
/obj/item/cane/white/nanite,
/obj/item/lighter/nanite,
- /obj/item/clothing/mask/holocigarette/nanite,
+ /obj/item/holocigarette/nanite,
)
///The objects currently summoned by the NIFSoft
@@ -198,7 +198,7 @@
name = "catchflame"
special_desc = "A work of art by the standards of normal Icons, this one was worked on for five continuous years by a single summoner; now known as Neophyte Inverse after adoption of the Icon by the few physically-bodied members of the Altspace Coven. The engram's work involves the use of nanites to ignite atmospheric hydrogen molecules, the blue glow from the Icon arising from perfect and complete combustion. This allows the lighter to function exactly as a normal zippo would, the description reading '...useful for lighting hearth fires, candles, and incense; try white sage if you can order some off the net, just pray it doesn't dispel engrams lol.'"
-/obj/item/clothing/mask/holocigarette/nanite
+/obj/item/holocigarette/nanite
name = "cloudstick"
special_desc = "One of mankind's many attempts at ending the legacy of Big Tobacco. Contributed by a fully anonymous engram and then forked countless times into countless replications of brands and flavors, the 'Cloudstick' is more of a genre than a single Icon. Most downloadable ones even allow the summoner to change the pixelation of the smoke, to grant them a more 'detached' experience from the real thing. Several summoners report these to help them in quitting smoking, others simply commenting 'It's why I first downloaded the Catchflame.'"
diff --git a/modular_skyrat/modules/modular_items/code/ciggies.dm b/modular_skyrat/modules/modular_items/code/ciggies.dm
index 359a90efe4a..f49223a89c6 100644
--- a/modular_skyrat/modules/modular_items/code/ciggies.dm
+++ b/modular_skyrat/modules/modular_items/code/ciggies.dm
@@ -1,12 +1,12 @@
-/obj/item/clothing/mask/holocigarette
+/obj/item/holocigarette
name = "Holocigarette"
desc = "A cigarette created using holodeck technology. Want to smoke without all the downsides? Try Holocigarettes!"
- icon = 'icons/obj/clothing/masks.dmi'
+ icon = 'icons/obj/cigarettes.dmi'
icon_state = "cigoff"
throw_speed = 0.5
inhand_icon_state = "cigoff"
w_class = WEIGHT_CLASS_TINY
- body_parts_covered = null
+ slot_flags= ITEM_SLOT_MASK
// Note - these are in masks.dmi not in cigarette.dmi
/// The icon state used when this is lit.
var/icon_on = "cigon"
@@ -15,7 +15,7 @@
var/lit = FALSE
actions_types = list(/datum/action/item_action/toggle_lit)
-/obj/item/clothing/mask/holocigarette/cigar
+/obj/item/holocigarette/cigar
name = "Bright Cosmos cigar"
desc = "A fancy cigar created using holodeck technology. They look like they have a \"Bright Cosmos\" branding on their wrap."
icon_state = "cigar2off"
@@ -27,7 +27,7 @@
desc = "Light or extinguish the holocigarette"
/datum/action/item_action/toggle_lit/Trigger(trigger_flags)
- var/obj/item/clothing/mask/holocigarette/smoked = target
+ var/obj/item/holocigarette/smoked = target
var/mob/living/carbon/smoker = owner
if(smoked.lit == FALSE)
smoked.icon_state = smoked.icon_on
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_structures/construction.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_structures/construction.dm
index 2ca816203a7..711ea3f9700 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_structures/construction.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_structures/construction.dm
@@ -21,10 +21,6 @@
. += span_purple("[src] can be assembled by using Ctrl+Shift+Click while [src] is on the floor.")
/obj/item/construction_kit/click_ctrl_shift(mob/user)
- . = ..()
- if(. == FALSE)
- return FALSE
-
if((item_flags & IN_INVENTORY) || (item_flags & IN_STORAGE))
return FALSE
diff --git a/modular_skyrat/modules/modular_vending/code/cigarette.dm b/modular_skyrat/modules/modular_vending/code/cigarette.dm
index f91ba68bcdd..6d55159db99 100644
--- a/modular_skyrat/modules/modular_vending/code/cigarette.dm
+++ b/modular_skyrat/modules/modular_vending/code/cigarette.dm
@@ -1,7 +1,7 @@
/obj/machinery/vending/cigarette/
contraband = list()
skyrat_products = list(
- /obj/item/clothing/mask/vape = 5,
+ /obj/item/vape = 5,
/obj/item/reagent_containers/vapecart = 10,
)
skyrat_contraband = list(
diff --git a/modular_skyrat/modules/modular_vending/code/games.dm b/modular_skyrat/modules/modular_vending/code/games.dm
index bddc6e2e6d6..dae76d0f253 100644
--- a/modular_skyrat/modules/modular_vending/code/games.dm
+++ b/modular_skyrat/modules/modular_vending/code/games.dm
@@ -14,7 +14,7 @@
"products" = list(
/obj/item/hairbrush = 3,
/obj/item/clothing/head/hair_tie/scrunchie = 3,
- /obj/item/clothing/mask/holocigarette = 5,
+ /obj/item/cigarette = 5,
/obj/item/attachable_soulcatcher = 5,
),
)
diff --git a/modular_skyrat/modules/modular_vending/code/wardrobes.dm b/modular_skyrat/modules/modular_vending/code/wardrobes.dm
index 7220b3a1e4f..d1b60334c6b 100644
--- a/modular_skyrat/modules/modular_vending/code/wardrobes.dm
+++ b/modular_skyrat/modules/modular_vending/code/wardrobes.dm
@@ -21,13 +21,16 @@
/obj/item/clothing/under/rank/engineering/engineer/skyrat/trouser = 3,
/obj/item/clothing/under/rank/engineering/engineer/skyrat/utility = 3,
/obj/item/clothing/under/rank/engineering/engineer/skyrat/hazard_chem = 3,
+ /obj/item/clothing/under/rank/engineering/engineer/skyrat/utility/telecomm = 3,
+ /obj/item/clothing/under/rank/engineering/engineer/skyrat/utility/telecomm/skirt = 3,
/obj/item/clothing/under/misc/overalls = 3,
/obj/item/clothing/suit/toggle/jacket/engi = 3,
- /obj/item/clothing/head/utility/hardhat/orange = 2,
- /obj/item/clothing/head/utility/hardhat/welding/orange = 2,
- /obj/item/clothing/head/utility/hardhat/dblue = 2,
- /obj/item/clothing/head/utility/hardhat/welding/dblue = 2,
- /obj/item/clothing/head/utility/hardhat/red = 2,
+ /obj/item/clothing/suit/toggle/jacket/tcomm = 3,
+ /obj/item/clothing/head/utility/hardhat/orange = 3,
+ /obj/item/clothing/head/utility/hardhat/welding/orange = 3,
+ /obj/item/clothing/head/utility/hardhat/dblue = 3,
+ /obj/item/clothing/head/utility/hardhat/welding/dblue = 3,
+ /obj/item/clothing/head/utility/hardhat/red = 3,
/obj/item/storage/backpack/messenger/eng = 3,
)
diff --git a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/ammo/pistol.dm b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/ammo/pistol.dm
index e20070dcef5..cc125a9b997 100644
--- a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/ammo/pistol.dm
+++ b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/ammo/pistol.dm
@@ -75,7 +75,7 @@
shrapnel_type = null
sharpness = NONE
- embedding = null
+ embed_data = null
/obj/item/ammo_box/c35sol/incapacitator
@@ -111,19 +111,19 @@
wound_bonus = 20
bare_wound_bonus = 20
- embedding = list(
- embed_chance = 75,
- fall_chance = 3,
- jostle_chance = 4,
- ignore_throwspeed_threshold = TRUE,
- pain_stam_pct = 0.4,
- pain_mult = 5,
- jostle_pain_mult = 6,
- rip_time = 1 SECONDS,
- )
+ embed_type = /datum/embed_data/c35sol_ripper
embed_falloff_tile = -15
+/datum/embed_data/c35sol_ripper
+ embed_chance = 75
+ fall_chance = 3
+ jostle_chance = 4
+ ignore_throwspeed_threshold = TRUE
+ pain_stam_pct = 0.4
+ pain_mult = 5
+ jostle_pain_mult = 6
+ rip_time = 1 SECONDS
/obj/item/ammo_box/c35sol/ripper
name = "ammo box (.35 Sol Short ripper)"
diff --git a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/ammo/rifle.dm b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/ammo/rifle.dm
index 4cb969f7b65..dd335c46454 100644
--- a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/ammo/rifle.dm
+++ b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/ammo/rifle.dm
@@ -69,19 +69,19 @@
bare_wound_bonus = 10
shrapnel_type = /obj/item/shrapnel/stingball
- embedding = list(
- embed_chance = 50,
- fall_chance = 5,
- jostle_chance = 5,
- ignore_throwspeed_threshold = TRUE,
- pain_stam_pct = 0.4,
- pain_mult = 2,
- jostle_pain_mult = 3,
- rip_time = 0.5 SECONDS,
- )
+ embed_type = /datum/embed_data/c40sol_fragmentation
embed_falloff_tile = -5
+/datum/embed_data/c40sol_fragmentation
+ embed_chance = 50
+ fall_chance = 5
+ jostle_chance = 5
+ ignore_throwspeed_threshold = TRUE
+ pain_stam_pct = 0.4
+ pain_mult = 2
+ jostle_pain_mult = 3
+ rip_time = 0.5 SECONDS
/obj/item/ammo_box/c40sol/fragmentation
name = "ammo box (.40 Sol Long fragmentation)"
diff --git a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/grenade_launcher.dm b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/grenade_launcher.dm
index 8748a1c8f71..a964e6cf362 100644
--- a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/grenade_launcher.dm
+++ b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/grenade_launcher.dm
@@ -29,7 +29,6 @@
fire_sound = 'modular_skyrat/modules/modular_weapons/sounds/grenade_launcher.ogg'
can_suppress = FALSE
- can_bayonet = FALSE
burst_size = 1
fire_delay = 5
diff --git a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/rifle.dm b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/rifle.dm
index f57668114e6..d6e0bf207a6 100644
--- a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/rifle.dm
+++ b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/rifle.dm
@@ -31,8 +31,6 @@
suppressed_sound = 'modular_skyrat/modules/modular_weapons/sounds/suppressed_rifle.ogg'
can_suppress = TRUE
- can_bayonet = FALSE
-
suppressor_x_offset = 12
burst_size = 1
diff --git a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/submachinegun.dm b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/submachinegun.dm
index 2f708723437..28ac6d7ba8c 100644
--- a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/submachinegun.dm
+++ b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/carwo_defense_systems/submachinegun.dm
@@ -25,8 +25,6 @@
fire_sound = 'modular_skyrat/modules/modular_weapons/sounds/smg_light.ogg'
can_suppress = TRUE
- can_bayonet = FALSE
-
suppressor_x_offset = 11
burst_size = 3
diff --git a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/szot_dynamica/rifle.dm b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/szot_dynamica/rifle.dm
index 52fc0d56006..e8c290ec17e 100644
--- a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/szot_dynamica/rifle.dm
+++ b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/szot_dynamica/rifle.dm
@@ -33,8 +33,6 @@
suppressor_x_offset = 0
suppressor_y_offset = 0
- can_bayonet = FALSE
-
burst_size = 1
fire_delay = 1.2 SECONDS
actions_types = list()
@@ -96,7 +94,6 @@
accepted_magazine_type = /obj/item/ammo_box/magazine/wylom
can_suppress = FALSE
- can_bayonet = FALSE
fire_sound = 'modular_skyrat/modules/novaya_ert/sound/amr_fire.ogg'
fire_sound_volume = 100 // BOOM BABY
diff --git a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/szot_dynamica/submachinegun.dm b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/szot_dynamica/submachinegun.dm
index 2bfc722b168..b99d0582447 100644
--- a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/szot_dynamica/submachinegun.dm
+++ b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/szot_dynamica/submachinegun.dm
@@ -29,8 +29,6 @@
suppressor_x_offset = 0
suppressor_y_offset = 0
- can_bayonet = FALSE
-
burst_size = 1
fire_delay = 0.2 SECONDS
actions_types = list()
diff --git a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/trappiste_fabriek/ammo.dm b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/trappiste_fabriek/ammo.dm
index 498f55a5627..163128be97c 100644
--- a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/trappiste_fabriek/ammo.dm
+++ b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/trappiste_fabriek/ammo.dm
@@ -52,7 +52,7 @@
shrapnel_type = null
sharpness = NONE
- embedding = null
+ embed_data = null
/obj/item/ammo_box/c585trappiste/incapacitator
name = "ammo box (.585 Trappiste flathead)"
diff --git a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/xhihao_light_arms/ammo.dm b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/xhihao_light_arms/ammo.dm
index 7c43be2c19a..8cf2c497562 100644
--- a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/xhihao_light_arms/ammo.dm
+++ b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/xhihao_light_arms/ammo.dm
@@ -24,7 +24,7 @@
ricochet_decay_damage = 0.7
shrapnel_type = null
sharpness = NONE
- embedding = null
+ embed_data = null
/obj/item/ammo_casing/strilka310/ap
name = ".310 Strilka armor-piercing bullet casing"
diff --git a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/xhihao_light_arms/rifle.dm b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/xhihao_light_arms/rifle.dm
index 5ac6950ad66..a0e80f7ce61 100644
--- a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/xhihao_light_arms/rifle.dm
+++ b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/xhihao_light_arms/rifle.dm
@@ -12,13 +12,15 @@
inhand_icon_state = "enchanted_rifle"
accepted_magazine_type = /obj/item/ammo_box/magazine/internal/boltaction/bubba
can_be_sawn_off = FALSE
- knife_x_offset = 35
/obj/item/gun/ballistic/rifle/boltaction/sporterized/Initialize(mapload)
. = ..()
AddComponent(/datum/component/scope, range_modifier = 1.5)
+/obj/item/gun/energy/recharge/ebow/add_bayonet_point()
+ AddComponent(/datum/component/bayonet_attachable, offset_x = 35)
+
/obj/item/gun/ballistic/rifle/boltaction/sporterized/give_manufacturer_examine()
AddElement(/datum/element/manufacturer_examine, COMPANY_XHIHAO)
diff --git a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/xhihao_light_arms/submachinegun.dm b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/xhihao_light_arms/submachinegun.dm
index a0dce177792..5d724abbca9 100644
--- a/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/xhihao_light_arms/submachinegun.dm
+++ b/modular_skyrat/modules/modular_weapons/code/company_and_or_faction_based/xhihao_light_arms/submachinegun.dm
@@ -25,8 +25,6 @@
fire_sound = 'modular_skyrat/modules/modular_weapons/sounds/smg_heavy.ogg'
can_suppress = TRUE
- can_bayonet = FALSE
-
suppressor_x_offset = 9
burst_size = 1
diff --git a/modular_skyrat/modules/modular_weapons/code/modular_projectiles.dm b/modular_skyrat/modules/modular_weapons/code/modular_projectiles.dm
index 4a0625b491f..3870c89e500 100644
--- a/modular_skyrat/modules/modular_weapons/code/modular_projectiles.dm
+++ b/modular_skyrat/modules/modular_weapons/code/modular_projectiles.dm
@@ -30,7 +30,7 @@
ricochet_decay_damage = 0.7
shrapnel_type = null
sharpness = NONE
- embedding = null
+ embed_data = null
wound_bonus = -50
/obj/item/ammo_casing/c45/hp
@@ -58,7 +58,7 @@
ricochet_decay_damage = 0.7
shrapnel_type = null
sharpness = NONE
- embedding = null
+ embed_data = null
wound_bonus = -50
/obj/item/ammo_casing/c46x30mm/rubber
@@ -92,7 +92,7 @@
ricochet_decay_damage = 0.7
shrapnel_type = null
sharpness = NONE
- embedding = null
+ embed_data = null
wound_bonus = -50
/obj/item/ammo_casing/a223/ap
@@ -138,7 +138,7 @@
wound_bonus = -75
shrapnel_type = null
sharpness = NONE
- embedding = null
+ embed_data = null
/obj/item/ammo_casing/c34/ap
name = ".34 armor-piercing bullet casing"
diff --git a/modular_skyrat/modules/modular_weapons/code/pepperball_gun.dm b/modular_skyrat/modules/modular_weapons/code/pepperball_gun.dm
index 990581bfb4a..db8efb6da16 100644
--- a/modular_skyrat/modules/modular_weapons/code/pepperball_gun.dm
+++ b/modular_skyrat/modules/modular_weapons/code/pepperball_gun.dm
@@ -43,7 +43,7 @@
stamina = 5
shrapnel_type = null
sharpness = NONE
- embedding = null
+ embed_data = null
impact_effect_type = /obj/effect/temp_visual/impact_effect
var/contained_reagent = /datum/reagent/consumable/condensedcapsaicin
var/reagent_volume = 5
diff --git a/modular_skyrat/modules/morenarcotics/code/crackpipe.dm b/modular_skyrat/modules/morenarcotics/code/crackpipe.dm
index e17bf805a37..fa842c6ac5a 100644
--- a/modular_skyrat/modules/morenarcotics/code/crackpipe.dm
+++ b/modular_skyrat/modules/morenarcotics/code/crackpipe.dm
@@ -1,4 +1,4 @@
-/obj/item/clothing/mask/cigarette/pipe/crackpipe
+/obj/item/cigarette/pipe/crackpipe
name = "crack pipe"
desc = "A slick glass pipe made for smoking one thing: crack."
icon = 'modular_skyrat/modules/morenarcotics/icons/crack.dmi'
@@ -8,7 +8,7 @@
icon_off = "glass_pipeoff"
chem_volume = 20
-/obj/item/clothing/mask/cigarette/pipe/crackpipe/process(seconds_per_tick)
+/obj/item/cigarette/pipe/crackpipe/process(seconds_per_tick)
smoketime -= seconds_per_tick
if(smoketime <= 0)
if(ismob(loc))
@@ -27,7 +27,7 @@
handle_reagents()
-/obj/item/clothing/mask/cigarette/pipe/crackpipe/attackby(obj/item/used_item, mob/user, params)
+/obj/item/cigarette/pipe/crackpipe/attackby(obj/item/used_item, mob/user, params)
if(is_type_in_list(used_item, list(/obj/item/reagent_containers/crack,/obj/item/reagent_containers/blacktar)))
to_chat(user, span_notice("You stuff [used_item] into [src]."))
smoketime = 2 * 60
@@ -47,7 +47,7 @@
/datum/crafting_recipe/crackpipe
name = "Crack pipe"
- result = /obj/item/clothing/mask/cigarette/pipe/crackpipe
+ result = /obj/item/cigarette/pipe/crackpipe
reqs = list(/obj/item/stack/cable_coil = 5,
/obj/item/shard = 1,
/obj/item/stack/rods = 10)
diff --git a/modular_skyrat/modules/morenarcotics/code/vapecarts.dm b/modular_skyrat/modules/morenarcotics/code/vapecarts.dm
index 8e9d242484e..c5a9a0a86c0 100644
--- a/modular_skyrat/modules/morenarcotics/code/vapecarts.dm
+++ b/modular_skyrat/modules/morenarcotics/code/vapecarts.dm
@@ -10,20 +10,19 @@
fill_icon_thresholds = list(0, 5, 20, 40)
custom_price = PAYCHECK_CREW
-/obj/item/reagent_containers/vapecart/afterattack(atom/target, mob/user, proximity)
- . = ..()
- if(!proximity)
- return
- if(istype(target, /obj/item/clothing/mask/vape))
- var/obj/item/clothing/mask/vape/target_vape = target
- if(target_vape.screw == TRUE && !target_vape.reagents.total_volume)
- src.reagents.trans_to(target_vape, src.volume, transferred_by = user)
- qdel(src)
- to_chat(user, span_notice("You plug the [src.name] into the vape."))
- else if(!target_vape.screw)
- to_chat(user, span_warning("You need to open the cap to do that!"))
- else
- to_chat(user, span_warning("[target_vape] is already full!"))
+/obj/item/reagent_containers/vapecart/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ var/obj/item/vape/target_vape = interacting_with
+ if(!istype(target_vape))
+ return NONE
+ if(target_vape.screw == TRUE && !target_vape.reagents.total_volume)
+ src.reagents.trans_to(target_vape, src.volume, transferred_by = user)
+ to_chat(user, span_notice("You plug the [src.name] into the vape."))
+ qdel(src)
+ else if(!target_vape.screw)
+ to_chat(user, span_warning("You need to open the cap to do that!"))
+ else
+ to_chat(user, span_warning("[target_vape] is already full!"))
+ return ITEM_INTERACT_SUCCESS
/obj/item/reagent_containers/vapecart/empty
name = "customizable vape cart"
diff --git a/modular_skyrat/modules/moretraitoritems/code/weapons.dm b/modular_skyrat/modules/moretraitoritems/code/weapons.dm
index 1a29d0242d6..48a823694ec 100644
--- a/modular_skyrat/modules/moretraitoritems/code/weapons.dm
+++ b/modular_skyrat/modules/moretraitoritems/code/weapons.dm
@@ -182,11 +182,21 @@
/obj/projectile/bullet/c14mm
name = "14mm bullet"
damage = 60
- embedding = list(embed_chance = 90, fall_chance = 3, jostle_chance = 4, ignore_throwspeed_threshold = TRUE, pain_stam_pct = 0.4, pain_mult = 5, jostle_pain_mult = 9, rip_time = 10)
+ embed_type = /datum/embed_data/c14mm
dismemberment = 50
pierces = 1
projectile_piercing = PASSCLOSEDTURF|PASSGRILLE|PASSGLASS
+/datum/embed_data/c14mm
+ embed_chance = 90
+ fall_chance = 3
+ jostle_chance = 4
+ ignore_throwspeed_threshold = TRUE
+ pain_stam_pct = 0.4
+ pain_mult = 5
+ jostle_pain_mult = 9
+ rip_time = 10
+
//nullrod katana
/obj/item/katana/weak/curator //This has the same stats as the curator's claymore
desc = "An ancient Katana. Forged by... Well, it doesn't really say, but surely it's authentic! And sharp to boot!"
diff --git a/modular_skyrat/modules/nanotrasen_rep/code/nanotrasen_consultant.dm b/modular_skyrat/modules/nanotrasen_rep/code/nanotrasen_consultant.dm
index 00ec24f2de7..ed3c60ed7b0 100644
--- a/modular_skyrat/modules/nanotrasen_rep/code/nanotrasen_consultant.dm
+++ b/modular_skyrat/modules/nanotrasen_rep/code/nanotrasen_consultant.dm
@@ -32,7 +32,7 @@
family_heirlooms = list(/obj/item/book/manual/wiki/security_space_law)
mail_goodies = list(
- /obj/item/clothing/mask/cigarette/cigar/havana = 20,
+ /obj/item/cigarette/cigar/havana = 20,
/obj/item/storage/fancy/cigarettes/cigars/havana = 15,
/obj/item/reagent_containers/cup/glass/bottle/champagne = 10
)
diff --git a/modular_skyrat/modules/novaya_ert/code/mod_suit.dm b/modular_skyrat/modules/novaya_ert/code/mod_suit.dm
index f81495baa79..51317a89327 100644
--- a/modular_skyrat/modules/novaya_ert/code/mod_suit.dm
+++ b/modular_skyrat/modules/novaya_ert/code/mod_suit.dm
@@ -87,12 +87,12 @@
/obj/item/mod/module/status_readout/operational,
/obj/item/mod/module/auto_doc,
/obj/item/mod/module/visor/thermal,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/magboot/advanced,
)
default_pins = list(
/obj/item/mod/module/visor/thermal,
- /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/jetpack,
/obj/item/mod/module/magboot/advanced,
)
diff --git a/modular_skyrat/modules/opposing_force/code/equipment/modsuit.dm b/modular_skyrat/modules/opposing_force/code/equipment/modsuit.dm
index 748ba7cb211..d3fd4aca43a 100644
--- a/modular_skyrat/modules/opposing_force/code/equipment/modsuit.dm
+++ b/modular_skyrat/modules/opposing_force/code/equipment/modsuit.dm
@@ -70,7 +70,7 @@
category = OPFOR_EQUIPMENT_CATEGORY_MODSUIT_MODULES
/datum/opposing_force_equipment/modsuit_modules/ion_advanced
- item_type = /obj/item/mod/module/jetpack/advanced
+ item_type = /obj/item/mod/module/jetpack
/datum/opposing_force_equipment/modsuit_modules/storage_syndicate
item_type = /obj/item/mod/module/storage/syndicate
diff --git a/modular_skyrat/modules/power/code/teg.dm b/modular_skyrat/modules/power/code/teg.dm
index fd49724a5a9..6420cc832fe 100644
--- a/modular_skyrat/modules/power/code/teg.dm
+++ b/modular_skyrat/modules/power/code/teg.dm
@@ -24,4 +24,5 @@
description = "How to get different types of zap."
prereq_ids = list("parts_adv")
design_ids = list("teg", "circulator")
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 10)
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
+ discount_experiments = list(/datum/experiment/scanning/points/machinery_pinpoint_scan/tier2_capacitors = TECHWEB_TIER_3_POINTS)
diff --git a/modular_skyrat/modules/reagent_forging/code/forge_weapons.dm b/modular_skyrat/modules/reagent_forging/code/forge_weapons.dm
index e93141134d8..fa60c021e8c 100644
--- a/modular_skyrat/modules/reagent_forging/code/forge_weapons.dm
+++ b/modular_skyrat/modules/reagent_forging/code/forge_weapons.dm
@@ -72,6 +72,7 @@
worn_icon_state = "dagger_back"
belt_icon_state = "dagger_belt"
hitsound = 'sound/weapons/bladeslice.ogg'
+ embed_type = /datum/embed_data/forged_dagger
throwforce = 10
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK
w_class = WEIGHT_CLASS_SMALL
@@ -80,6 +81,11 @@
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
sharpness = SHARP_EDGED
+/datum/embed_data/forged_dagger
+ embed_chance = 50
+ fall_chance = 1
+ pain_mult = 2
+
/obj/item/forging/reagent_weapon/dagger/Initialize(mapload)
. = ..()
AddComponent(/datum/component/butchering, speed = 10 SECONDS, effectiveness = 70)
@@ -116,6 +122,7 @@
inhand_icon_state = "spear"
worn_icon_state = "spear_back"
throwforce = 15 //not a javelin, throwing specialty is for the axe.
+ embed_data = /datum/embed_data/forged_spear
slot_flags = ITEM_SLOT_BACK
w_class = WEIGHT_CLASS_BULKY
resistance_flags = FIRE_PROOF
@@ -126,6 +133,11 @@
bare_wound_bonus = 15
sharpness = SHARP_POINTY
+/datum/embed_data/forged_spear
+ embed_chance = 75
+ fall_chance = 0
+ pain_mult = 6
+
/obj/item/forging/reagent_weapon/spear/Initialize(mapload)
. = ..()
AddComponent(/datum/component/jousting, max_tile_charge = 9, min_tile_charge = 6)
@@ -144,7 +156,7 @@
worn_icon_state = "axe_back"
throwforce = 22 //ouch
throw_speed = 4
- embedding = list("impact_pain_mult" = 2, "remove_pain_mult" = 4, "jostle_chance" = 2.5)
+ embed_type = /datum/embed_data/forged_axe
slot_flags = ITEM_SLOT_BACK
w_class = WEIGHT_CLASS_BULKY
resistance_flags = FIRE_PROOF
@@ -152,6 +164,11 @@
attack_verb_simple = list("slash", "bash")
sharpness = SHARP_EDGED
+/datum/embed_data/forged_axe
+ embed_chance = 65
+ fall_chance = 10
+ pain_mult = 4
+
/obj/item/forging/reagent_weapon/axe/Initialize(mapload)
. = ..()
AddComponent(/datum/component/butchering, speed = 10 SECONDS, effectiveness = 70)
diff --git a/modular_skyrat/modules/sec_haul/code/guns/bullets.dm b/modular_skyrat/modules/sec_haul/code/guns/bullets.dm
index c7bc249b351..d342340466f 100644
--- a/modular_skyrat/modules/sec_haul/code/guns/bullets.dm
+++ b/modular_skyrat/modules/sec_haul/code/guns/bullets.dm
@@ -33,7 +33,17 @@
name = "9x25mm IHDF bullet"
damage = 30
damage_type = STAMINA
- embedding = list(embed_chance=0, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10)
+ embed_type = /datum/embed_data/c9mm_ihdf
+
+/datum/embed_data/c9mm_ihdf
+ embed_chance = 0
+ fall_chance = 3
+ jostle_chance = 4
+ pain_mult = 5
+ pain_stam_pct = 0.4
+ ignore_throwspeed_threshold = TRUE
+ jostle_pain_mult = 6
+ rip_time = 1 SECONDS
/obj/item/ammo_casing/c9mm/rubber
name = "9x25mm Mk.12 rubber casing"
@@ -52,7 +62,7 @@
ricochet_decay_damage = 0.8
shrapnel_type = null
sharpness = NONE
- embedding = null
+ embed_type = null
/*
* 10mm Auto
@@ -90,7 +100,7 @@
ricochet_decay_damage = 0.8
shrapnel_type = null
sharpness = NONE
- embedding = null
+ embed_type = null
/obj/item/ammo_casing/c10mm/ihdf
name = "10mm IHDF bullet casing"
@@ -102,4 +112,14 @@
name = "10mm IHDF bullet"
damage = 40
damage_type = STAMINA
- embedding = list(embed_chance=0, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10)
+ embed_type = /datum/embed_data/c10mm_ihdf
+
+/datum/embed_data/c10mm_ihdf
+ embed_chance = 0
+ fall_chance = 3
+ jostle_chance = 4
+ ignore_throwspeed_threshold = TRUE
+ pain_stam_pct = 0.4
+ pain_mult = 5
+ jostle_pain_mult = 6
+ rip_time = 1 SECONDS
diff --git a/modular_skyrat/modules/shotgunrebalance/code/shotgun.dm b/modular_skyrat/modules/shotgunrebalance/code/shotgun.dm
index a4e237b0f11..0ef23660a3a 100644
--- a/modular_skyrat/modules/shotgunrebalance/code/shotgun.dm
+++ b/modular_skyrat/modules/shotgunrebalance/code/shotgun.dm
@@ -232,7 +232,17 @@
eyeblur = 1 SECONDS
sharpness = NONE
range = 8
- embedding = list(embed_chance=70, pain_chance=25, fall_chance=15, jostle_chance=80, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.9, pain_mult=2, rip_time=10)
+ embed_type = /datum/embed_data/shotgun_buckshot/antitide
+
+/datum/embed_data/shotgun_buckshot/antitide
+ embed_chance = 70
+ pain_chance = 25
+ fall_chance = 15
+ jostle_chance = 80
+ ignore_throwspeed_threshold = TRUE
+ pain_stam_pct = 0.9
+ pain_mult = 2
+ rip_time = 1 SECONDS
/obj/projectile/bullet/pellet/shotgun_buckshot/antitide/on_range()
do_sparks(1, TRUE, src)
diff --git a/modular_skyrat/modules/space_vines/scythes.dm b/modular_skyrat/modules/space_vines/scythes.dm
index 4298bad036e..d947bb833d0 100644
--- a/modular_skyrat/modules/space_vines/scythes.dm
+++ b/modular_skyrat/modules/space_vines/scythes.dm
@@ -90,8 +90,8 @@
design_ids = list(
"scythet1",
)
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
- discount_experiments = list(/datum/experiment/scanning/random/plants/wild = 500)
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
+ discount_experiments = list(/datum/experiment/scanning/random/plants/wild = TECHWEB_TIER_1_POINTS)
/datum/techweb_node/scythe_t2
id = TECHWEB_NODE_SCYTHE_2
@@ -101,8 +101,8 @@
design_ids = list(
"scythet2",
)
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
- discount_experiments = list(/datum/experiment/scanning/random/plants/wild = 1000)
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_3_POINTS)
+ discount_experiments = list(/datum/experiment/scanning/random/plants/wild = TECHWEB_TIER_2_POINTS)
/datum/supply_pack/organic/tier3_scythe
name = "Tier 3 Scythe"
diff --git a/modular_skyrat/modules/stasisrework/code/all_nodes.dm b/modular_skyrat/modules/stasisrework/code/all_nodes.dm
index 21be4a26977..c36619a09c3 100644
--- a/modular_skyrat/modules/stasisrework/code/all_nodes.dm
+++ b/modular_skyrat/modules/stasisrework/code/all_nodes.dm
@@ -1,8 +1,3 @@
-/datum/techweb_node/cryostasis
- display_name = "Cryostasis Technology"
- description = "Smart freezing of objects to preserve them!"
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
-
/datum/techweb_node/cryostasis/New()
design_ids += list(
"stasisbag",
diff --git a/modular_skyrat/modules/tarkon/code/misc-fluff/research.dm b/modular_skyrat/modules/tarkon/code/misc-fluff/research.dm
index 2dbfabd86d0..84c7e1a39ec 100644
--- a/modular_skyrat/modules/tarkon/code/misc-fluff/research.dm
+++ b/modular_skyrat/modules/tarkon/code/misc-fluff/research.dm
@@ -29,7 +29,7 @@
"arcs",
"rcd_tarkon"
)
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 10000)
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS)
hidden = TRUE
/datum/design/mod_plating/tarkon
@@ -37,10 +37,10 @@
id = "mod_plating_tarkon"
build_path = /obj/item/mod/construction/plating/tarkon
materials = list(
- /datum/material/iron =SHEET_MATERIAL_AMOUNT*3,
- /datum/material/uranium =SHEET_MATERIAL_AMOUNT,
- /datum/material/glass =HALF_SHEET_MATERIAL_AMOUNT,
- /datum/material/plasma =HALF_SHEET_MATERIAL_AMOUNT,
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 3,
+ /datum/material/uranium = SHEET_MATERIAL_AMOUNT,
+ /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT,
+ /datum/material/plasma = HALF_SHEET_MATERIAL_AMOUNT,
)
departmental_flags = DEPARTMENT_BITFLAG_SECURITY
research_icon_state = "tarkon-plating"
@@ -51,11 +51,11 @@
id = "arcs"
build_type = PROTOLATHE | AWAY_LATHE | AUTOLATHE
materials = list(
- /datum/material/iron = SHEET_MATERIAL_AMOUNT*5,
- /datum/material/glass =SHEET_MATERIAL_AMOUNT * 3,
- /datum/material/silver =SHEET_MATERIAL_AMOUNT * 2,
- /datum/material/uranium =SHEET_MATERIAL_AMOUNT * 2,
- /datum/material/diamond =SHEET_MATERIAL_AMOUNT
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 5,
+ /datum/material/glass = SHEET_MATERIAL_AMOUNT * 3,
+ /datum/material/silver = SHEET_MATERIAL_AMOUNT * 2,
+ /datum/material/uranium = SHEET_MATERIAL_AMOUNT * 2,
+ /datum/material/diamond = SHEET_MATERIAL_AMOUNT,
)
build_path = /obj/item/gun/energy/recharge/resonant_system
category = list(
@@ -69,10 +69,10 @@
id = "rcd_tarkon"
build_type = PROTOLATHE | AWAY_LATHE
materials = list(
- /datum/material/iron =SHEET_MATERIAL_AMOUNT*30,
- /datum/material/glass =SHEET_MATERIAL_AMOUNT * 10,
- /datum/material/uranium =SHEET_MATERIAL_AMOUNT * 4,
- /datum/material/diamond =SHEET_MATERIAL_AMOUNT * 2,
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 30,
+ /datum/material/glass = SHEET_MATERIAL_AMOUNT * 10,
+ /datum/material/uranium = SHEET_MATERIAL_AMOUNT * 4,
+ /datum/material/diamond = SHEET_MATERIAL_AMOUNT * 2,
/datum/material/bluespace = SHEET_MATERIAL_AMOUNT * 3
)
build_path = /obj/item/construction/rcd/arcd/tarkon
diff --git a/modular_skyrat/modules/telecomms_specialist/telecomms_specialist.dm b/modular_skyrat/modules/telecomms_specialist/telecomms_specialist.dm
index f2ea8a933b2..f4aa24cdfe2 100644
--- a/modular_skyrat/modules/telecomms_specialist/telecomms_specialist.dm
+++ b/modular_skyrat/modules/telecomms_specialist/telecomms_specialist.dm
@@ -57,12 +57,12 @@
uniform = /obj/item/clothing/under/rank/engineering/engineer/skyrat/utility/telecomm
suit = /obj/item/clothing/suit/toggle/jacket/tcomm
neck = /obj/item/clothing/neck/link_scryer
- belt = /obj/item/screwdriver
+ belt = /obj/item/storage/belt/utility/full/engi
ears = /obj/item/radio/headset/headset_eng
head = /obj/item/clothing/head/utility/hardhat/dblue
shoes = /obj/item/clothing/shoes/laceup
l_pocket = /obj/item/modular_computer/pda/telecomms
- r_pocket = /obj/item/multitool
+ r_pocket = /obj/item/holosign_creator/atmos
backpack = /obj/item/storage/backpack/industrial
satchel = /obj/item/storage/backpack/satchel/eng
@@ -75,8 +75,6 @@
backpack_contents = list(
/obj/item/paper/monitorkey,
- /obj/item/wirecutters,
- /obj/item/holosign_creator/atmos,
)
/obj/item/modular_computer/pda/telecomms
diff --git a/modular_skyrat/modules/teshari/code/_teshari.dm b/modular_skyrat/modules/teshari/code/_teshari.dm
index 455fd8cfcec..e1ffe68dc30 100644
--- a/modular_skyrat/modules/teshari/code/_teshari.dm
+++ b/modular_skyrat/modules/teshari/code/_teshari.dm
@@ -22,13 +22,13 @@
OFFSET_HEAD = TESHARI_HEAD_ICON,
OFFSET_FACEMASK = TESHARI_MASK_ICON,
OFFSET_NECK = TESHARI_NECK_ICON,
- LOADOUT_ITEM_SUIT = TESHARI_SUIT_ICON,
- LOADOUT_ITEM_UNIFORM = TESHARI_UNIFORM_ICON,
- LOADOUT_ITEM_HANDS = TESHARI_HANDS_ICON,
+ OFFSET_SUIT = TESHARI_SUIT_ICON,
+ OFFSET_UNIFORM = TESHARI_UNIFORM_ICON,
+ OFFSET_GLOVES = TESHARI_HANDS_ICON,
OFFSET_SHOES = TESHARI_FEET_ICON,
- LOADOUT_ITEM_GLASSES = TESHARI_EYES_ICON,
+ OFFSET_GLASSES = TESHARI_EYES_ICON,
OFFSET_BELT = TESHARI_BELT_ICON,
- LOADOUT_ITEM_MISC = TESHARI_BACK_ICON,
+ OFFSET_BACK = TESHARI_BACK_ICON,
OFFSET_ACCESSORY = TESHARI_ACCESSORIES_ICON,
OFFSET_EARS = TESHARI_EARS_ICON
)
diff --git a/modular_skyrat/modules/tribal_extended/code/ammo/reusable/arrow.dm b/modular_skyrat/modules/tribal_extended/code/ammo/reusable/arrow.dm
index 0fdfb0967d1..5fd451bb0f4 100644
--- a/modular_skyrat/modules/tribal_extended/code/ammo/reusable/arrow.dm
+++ b/modular_skyrat/modules/tribal_extended/code/ammo/reusable/arrow.dm
@@ -39,16 +39,17 @@
/mob/living/basic/wumborian_fugu,
)
shrapnel_type = /obj/item/ammo_casing/arrow/bone
- embedding = list(
- embed_chance = 33,
- fall_chance = 3,
- jostle_chance = 4,
- ignore_throwspeed_threshold = TRUE,
- pain_stam_pct = 0.4,
- pain_mult = 5,
- jostle_pain_mult = 6,
- rip_time = 0.5 SECONDS
- )
+ embed_type = /datum/embed_data/bone_arrow
+
+/datum/embed_data/bone_arrow
+ embed_chance = 33
+ fall_chance = 3
+ jostle_chance = 4
+ ignore_throwspeed_threshold = TRUE
+ pain_stam_pct = 0.4
+ pain_mult = 5
+ jostle_pain_mult = 6
+ rip_time = 0.5 SECONDS
/obj/projectile/bullet/arrow/bronze
name = "bronze arrow"
diff --git a/sound/ambience/antag/heretic/heretic_gain_intense.ogg b/sound/ambience/antag/heretic/heretic_gain_intense.ogg
new file mode 100644
index 00000000000..5e6d4f4174f
Binary files /dev/null and b/sound/ambience/antag/heretic/heretic_gain_intense.ogg differ
diff --git a/sound/attributions.txt b/sound/attributions.txt
index dff6c31cb7f..123fde0794c 100644
--- a/sound/attributions.txt
+++ b/sound/attributions.txt
@@ -201,6 +201,9 @@ ayylien.ogg was made by remixing:
SCIRetro_Energy Swells Synth_Funky Audio_Sonics Spices by Funky_Audio under CC0 -- https://freesound.org/people/realtheremin/sounds/119011/
scifi_scare_a.aiff by realtheremin under CC0 -- https://freesound.org/people/Funky_Audio/sounds/729392/
+solarflare.ogg was made by ArcaneMusic, using the following sources from Freesound.org.
+"Flashbang Explosion Designed 14m" by modusmogulus https://freesound.org/people/modusmogulus/sounds/734095/ under CC 0
+"Hammer and anvil" by Duasun -- https://freesound.org/people/Duasun/sounds/321889/ under CC 0
beaker_pickup.ogg was made by lowering pitch:
Bottle Tap.wav by alex_alexalex -- https://freesound.org/s/395492/ -- License: Attribution NonCommercial 3.0
diff --git a/sound/items/attributions.txt b/sound/items/attributions.txt
new file mode 100644
index 00000000000..de55b8daebe
--- /dev/null
+++ b/sound/items/attributions.txt
@@ -0,0 +1 @@
+night_vision_on.ogg by Syna-Max -- https://freesound.org/s/60345/ -- License: Attribution NonCommercial 4.0
diff --git a/sound/items/night_vision_on.ogg b/sound/items/night_vision_on.ogg
new file mode 100644
index 00000000000..13ca202669c
Binary files /dev/null and b/sound/items/night_vision_on.ogg differ
diff --git a/sound/mecha/mech_blade_attack.ogg b/sound/mecha/mech_blade_attack.ogg
new file mode 100644
index 00000000000..f6c9274762b
Binary files /dev/null and b/sound/mecha/mech_blade_attack.ogg differ
diff --git a/sound/mecha/mech_blade_break_wall.ogg b/sound/mecha/mech_blade_break_wall.ogg
new file mode 100644
index 00000000000..cec9c7a667c
Binary files /dev/null and b/sound/mecha/mech_blade_break_wall.ogg differ
diff --git a/sound/mecha/mech_blade_safty.ogg b/sound/mecha/mech_blade_safty.ogg
new file mode 100644
index 00000000000..80e06388127
Binary files /dev/null and b/sound/mecha/mech_blade_safty.ogg differ
diff --git a/sound/mecha/mech_charge_attack.ogg b/sound/mecha/mech_charge_attack.ogg
new file mode 100644
index 00000000000..b6d202c8e26
Binary files /dev/null and b/sound/mecha/mech_charge_attack.ogg differ
diff --git a/sound/mecha/mech_stealth_attack.ogg b/sound/mecha/mech_stealth_attack.ogg
new file mode 100644
index 00000000000..e12ff890c7d
Binary files /dev/null and b/sound/mecha/mech_stealth_attack.ogg differ
diff --git a/sound/mecha/mech_stealth_effect.ogg b/sound/mecha/mech_stealth_effect.ogg
new file mode 100644
index 00000000000..b7a5a513308
Binary files /dev/null and b/sound/mecha/mech_stealth_effect.ogg differ
diff --git a/sound/mecha/mech_stealth_pre_attack.ogg b/sound/mecha/mech_stealth_pre_attack.ogg
new file mode 100644
index 00000000000..15b2c04ab5c
Binary files /dev/null and b/sound/mecha/mech_stealth_pre_attack.ogg differ
diff --git a/sound/voice/breathing.ogg b/sound/voice/breathing.ogg
new file mode 100644
index 00000000000..f940a1b211a
Binary files /dev/null and b/sound/voice/breathing.ogg differ
diff --git a/sound/vox_fem/absorb.ogg b/sound/vox_fem/absorb.ogg
new file mode 100644
index 00000000000..e05b4aa4645
Binary files /dev/null and b/sound/vox_fem/absorb.ogg differ
diff --git a/sound/vox_fem/absorbed.ogg b/sound/vox_fem/absorbed.ogg
new file mode 100644
index 00000000000..726f1aa8c6f
Binary files /dev/null and b/sound/vox_fem/absorbed.ogg differ
diff --git a/sound/vox_fem/absorbing.ogg b/sound/vox_fem/absorbing.ogg
new file mode 100644
index 00000000000..8b8c1f575c7
Binary files /dev/null and b/sound/vox_fem/absorbing.ogg differ
diff --git a/sound/vox_fem/activating.ogg b/sound/vox_fem/activating.ogg
new file mode 100644
index 00000000000..8082efb6911
Binary files /dev/null and b/sound/vox_fem/activating.ogg differ
diff --git a/sound/vox_fem/activation.ogg b/sound/vox_fem/activation.ogg
new file mode 100644
index 00000000000..f06d33245df
Binary files /dev/null and b/sound/vox_fem/activation.ogg differ
diff --git a/sound/vox_fem/active.ogg b/sound/vox_fem/active.ogg
new file mode 100644
index 00000000000..54d0c1eb45f
Binary files /dev/null and b/sound/vox_fem/active.ogg differ
diff --git a/sound/vox_fem/affect.ogg b/sound/vox_fem/affect.ogg
new file mode 100644
index 00000000000..4c9b7aaeaad
Binary files /dev/null and b/sound/vox_fem/affect.ogg differ
diff --git a/sound/vox_fem/affected.ogg b/sound/vox_fem/affected.ogg
new file mode 100644
index 00000000000..c634e08f734
Binary files /dev/null and b/sound/vox_fem/affected.ogg differ
diff --git a/sound/vox_fem/affecting.ogg b/sound/vox_fem/affecting.ogg
new file mode 100644
index 00000000000..18f2e3c0c7a
Binary files /dev/null and b/sound/vox_fem/affecting.ogg differ
diff --git a/sound/vox_fem/alcohol.ogg b/sound/vox_fem/alcohol.ogg
new file mode 100644
index 00000000000..a304167a27f
Binary files /dev/null and b/sound/vox_fem/alcohol.ogg differ
diff --git a/sound/vox_fem/align.ogg b/sound/vox_fem/align.ogg
new file mode 100644
index 00000000000..bcf4b6465a8
Binary files /dev/null and b/sound/vox_fem/align.ogg differ
diff --git a/sound/vox_fem/allow.ogg b/sound/vox_fem/allow.ogg
new file mode 100644
index 00000000000..ea0b8098fd7
Binary files /dev/null and b/sound/vox_fem/allow.ogg differ
diff --git a/sound/vox_fem/alongside.ogg b/sound/vox_fem/alongside.ogg
new file mode 100644
index 00000000000..a96b9e7ba25
Binary files /dev/null and b/sound/vox_fem/alongside.ogg differ
diff --git a/sound/vox_fem/amount.ogg b/sound/vox_fem/amount.ogg
new file mode 100644
index 00000000000..19b59e8263c
Binary files /dev/null and b/sound/vox_fem/amount.ogg differ
diff --git a/sound/vox_fem/annihilate.ogg b/sound/vox_fem/annihilate.ogg
new file mode 100644
index 00000000000..375dfda691f
Binary files /dev/null and b/sound/vox_fem/annihilate.ogg differ
diff --git a/sound/vox_fem/annihilated.ogg b/sound/vox_fem/annihilated.ogg
new file mode 100644
index 00000000000..823cc93dc6a
Binary files /dev/null and b/sound/vox_fem/annihilated.ogg differ
diff --git a/sound/vox_fem/annihilating.ogg b/sound/vox_fem/annihilating.ogg
new file mode 100644
index 00000000000..95a507df850
Binary files /dev/null and b/sound/vox_fem/annihilating.ogg differ
diff --git a/sound/vox_fem/annihilation.ogg b/sound/vox_fem/annihilation.ogg
new file mode 100644
index 00000000000..6acb8550338
Binary files /dev/null and b/sound/vox_fem/annihilation.ogg differ
diff --git a/sound/vox_fem/anti-noblium.ogg b/sound/vox_fem/anti-noblium.ogg
new file mode 100644
index 00000000000..70f89e82229
Binary files /dev/null and b/sound/vox_fem/anti-noblium.ogg differ
diff --git a/sound/vox_fem/arc.ogg b/sound/vox_fem/arc.ogg
new file mode 100644
index 00000000000..3ebed29740d
Binary files /dev/null and b/sound/vox_fem/arc.ogg differ
diff --git a/sound/vox_fem/arcs.ogg b/sound/vox_fem/arcs.ogg
new file mode 100644
index 00000000000..cb32908d5b8
Binary files /dev/null and b/sound/vox_fem/arcs.ogg differ
diff --git a/sound/vox_fem/around.ogg b/sound/vox_fem/around.ogg
new file mode 100644
index 00000000000..76183f92cda
Binary files /dev/null and b/sound/vox_fem/around.ogg differ
diff --git a/sound/vox_fem/ask.ogg b/sound/vox_fem/ask.ogg
new file mode 100644
index 00000000000..c8d00b00399
Binary files /dev/null and b/sound/vox_fem/ask.ogg differ
diff --git a/sound/vox_fem/ate.ogg b/sound/vox_fem/ate.ogg
new file mode 100644
index 00000000000..8dcdd461d11
Binary files /dev/null and b/sound/vox_fem/ate.ogg differ
diff --git a/sound/vox_fem/beaker.ogg b/sound/vox_fem/beaker.ogg
new file mode 100644
index 00000000000..6863afc5c79
Binary files /dev/null and b/sound/vox_fem/beaker.ogg differ
diff --git a/sound/vox_fem/began.ogg b/sound/vox_fem/began.ogg
new file mode 100644
index 00000000000..548143aff53
Binary files /dev/null and b/sound/vox_fem/began.ogg differ
diff --git a/sound/vox_fem/begin.ogg b/sound/vox_fem/begin.ogg
new file mode 100644
index 00000000000..5c70cebbb09
Binary files /dev/null and b/sound/vox_fem/begin.ogg differ
diff --git a/sound/vox_fem/begins.ogg b/sound/vox_fem/begins.ogg
new file mode 100644
index 00000000000..5fedd2ef4b5
Binary files /dev/null and b/sound/vox_fem/begins.ogg differ
diff --git a/sound/vox_fem/bottle.ogg b/sound/vox_fem/bottle.ogg
new file mode 100644
index 00000000000..b5bd1f34e76
Binary files /dev/null and b/sound/vox_fem/bottle.ogg differ
diff --git a/sound/vox_fem/broke.ogg b/sound/vox_fem/broke.ogg
new file mode 100644
index 00000000000..a1a075d6769
Binary files /dev/null and b/sound/vox_fem/broke.ogg differ
diff --git a/sound/vox_fem/broken.ogg b/sound/vox_fem/broken.ogg
new file mode 100644
index 00000000000..589dabc0a39
Binary files /dev/null and b/sound/vox_fem/broken.ogg differ
diff --git a/sound/vox_fem/bump.ogg b/sound/vox_fem/bump.ogg
new file mode 100644
index 00000000000..c6892a3412f
Binary files /dev/null and b/sound/vox_fem/bump.ogg differ
diff --git a/sound/vox_fem/bumped.ogg b/sound/vox_fem/bumped.ogg
new file mode 100644
index 00000000000..6d111a8731d
Binary files /dev/null and b/sound/vox_fem/bumped.ogg differ
diff --git a/sound/vox_fem/bumps.ogg b/sound/vox_fem/bumps.ogg
new file mode 100644
index 00000000000..a7fff16f048
Binary files /dev/null and b/sound/vox_fem/bumps.ogg differ
diff --git a/sound/vox_fem/can.ogg b/sound/vox_fem/can.ogg
new file mode 100644
index 00000000000..0d77353ad99
Binary files /dev/null and b/sound/vox_fem/can.ogg differ
diff --git a/sound/vox_fem/cascade.ogg b/sound/vox_fem/cascade.ogg
new file mode 100644
index 00000000000..cc7e70ed00d
Binary files /dev/null and b/sound/vox_fem/cascade.ogg differ
diff --git a/sound/vox_fem/cause.ogg b/sound/vox_fem/cause.ogg
new file mode 100644
index 00000000000..2294d0405d0
Binary files /dev/null and b/sound/vox_fem/cause.ogg differ
diff --git a/sound/vox_fem/caused.ogg b/sound/vox_fem/caused.ogg
new file mode 100644
index 00000000000..c91eecaa90d
Binary files /dev/null and b/sound/vox_fem/caused.ogg differ
diff --git a/sound/vox_fem/causes.ogg b/sound/vox_fem/causes.ogg
new file mode 100644
index 00000000000..019430ccb4a
Binary files /dev/null and b/sound/vox_fem/causes.ogg differ
diff --git a/sound/vox_fem/causing.ogg b/sound/vox_fem/causing.ogg
new file mode 100644
index 00000000000..e41ab06c2b4
Binary files /dev/null and b/sound/vox_fem/causing.ogg differ
diff --git a/sound/vox_fem/charge.ogg b/sound/vox_fem/charge.ogg
new file mode 100644
index 00000000000..b5adb81d83b
Binary files /dev/null and b/sound/vox_fem/charge.ogg differ
diff --git a/sound/vox_fem/clog.ogg b/sound/vox_fem/clog.ogg
new file mode 100644
index 00000000000..31e6bf604ec
Binary files /dev/null and b/sound/vox_fem/clog.ogg differ
diff --git a/sound/vox_fem/clothing.ogg b/sound/vox_fem/clothing.ogg
new file mode 100644
index 00000000000..912a7532fe3
Binary files /dev/null and b/sound/vox_fem/clothing.ogg differ
diff --git a/sound/vox_fem/coil.ogg b/sound/vox_fem/coil.ogg
new file mode 100644
index 00000000000..d9a76618eb4
Binary files /dev/null and b/sound/vox_fem/coil.ogg differ
diff --git a/sound/vox_fem/coils.ogg b/sound/vox_fem/coils.ogg
new file mode 100644
index 00000000000..12d0d54fd3e
Binary files /dev/null and b/sound/vox_fem/coils.ogg differ
diff --git a/sound/vox_fem/combat.ogg b/sound/vox_fem/combat.ogg
new file mode 100644
index 00000000000..51a438ea953
Binary files /dev/null and b/sound/vox_fem/combat.ogg differ
diff --git a/sound/vox_fem/combatant.ogg b/sound/vox_fem/combatant.ogg
new file mode 100644
index 00000000000..d82f5dddd00
Binary files /dev/null and b/sound/vox_fem/combatant.ogg differ
diff --git a/sound/vox_fem/complete.ogg b/sound/vox_fem/complete.ogg
new file mode 100644
index 00000000000..134d280d068
Binary files /dev/null and b/sound/vox_fem/complete.ogg differ
diff --git a/sound/vox_fem/completed.ogg b/sound/vox_fem/completed.ogg
new file mode 100644
index 00000000000..bdeca3b4fe3
Binary files /dev/null and b/sound/vox_fem/completed.ogg differ
diff --git a/sound/vox_fem/completion.ogg b/sound/vox_fem/completion.ogg
new file mode 100644
index 00000000000..c4d65fbdad0
Binary files /dev/null and b/sound/vox_fem/completion.ogg differ
diff --git a/sound/vox_fem/conditions.ogg b/sound/vox_fem/conditions.ogg
new file mode 100644
index 00000000000..79e0a6bfe8f
Binary files /dev/null and b/sound/vox_fem/conditions.ogg differ
diff --git a/sound/vox_fem/configure.ogg b/sound/vox_fem/configure.ogg
new file mode 100644
index 00000000000..753339e761e
Binary files /dev/null and b/sound/vox_fem/configure.ogg differ
diff --git a/sound/vox_fem/configured.ogg b/sound/vox_fem/configured.ogg
new file mode 100644
index 00000000000..544ed189add
Binary files /dev/null and b/sound/vox_fem/configured.ogg differ
diff --git a/sound/vox_fem/configuring.ogg b/sound/vox_fem/configuring.ogg
new file mode 100644
index 00000000000..eb81a1bb1df
Binary files /dev/null and b/sound/vox_fem/configuring.ogg differ
diff --git a/sound/vox_fem/container.ogg b/sound/vox_fem/container.ogg
new file mode 100644
index 00000000000..59fe11b5f48
Binary files /dev/null and b/sound/vox_fem/container.ogg differ
diff --git a/sound/vox_fem/cool.ogg b/sound/vox_fem/cool.ogg
new file mode 100644
index 00000000000..0e8c39a9260
Binary files /dev/null and b/sound/vox_fem/cool.ogg differ
diff --git a/sound/vox_fem/cooling.ogg b/sound/vox_fem/cooling.ogg
new file mode 100644
index 00000000000..eef866ca1b1
Binary files /dev/null and b/sound/vox_fem/cooling.ogg differ
diff --git a/sound/vox_fem/could.ogg b/sound/vox_fem/could.ogg
new file mode 100644
index 00000000000..fdbd14f696c
Binary files /dev/null and b/sound/vox_fem/could.ogg differ
diff --git a/sound/vox_fem/couldnt.ogg b/sound/vox_fem/couldnt.ogg
new file mode 100644
index 00000000000..80550544ee4
Binary files /dev/null and b/sound/vox_fem/couldnt.ogg differ
diff --git a/sound/vox_fem/countdown.ogg b/sound/vox_fem/countdown.ogg
new file mode 100644
index 00000000000..93b0f649615
Binary files /dev/null and b/sound/vox_fem/countdown.ogg differ
diff --git a/sound/vox_fem/create.ogg b/sound/vox_fem/create.ogg
new file mode 100644
index 00000000000..f4f1797202a
Binary files /dev/null and b/sound/vox_fem/create.ogg differ
diff --git a/sound/vox_fem/creating.ogg b/sound/vox_fem/creating.ogg
new file mode 100644
index 00000000000..ca2a92d5d72
Binary files /dev/null and b/sound/vox_fem/creating.ogg differ
diff --git a/sound/vox_fem/delaminating.ogg b/sound/vox_fem/delaminating.ogg
new file mode 100644
index 00000000000..3c023387abf
Binary files /dev/null and b/sound/vox_fem/delaminating.ogg differ
diff --git a/sound/vox_fem/delamination.ogg b/sound/vox_fem/delamination.ogg
new file mode 100644
index 00000000000..993c06fd4ea
Binary files /dev/null and b/sound/vox_fem/delamination.ogg differ
diff --git a/sound/vox_fem/deny.ogg b/sound/vox_fem/deny.ogg
new file mode 100644
index 00000000000..df6b5cb40fb
Binary files /dev/null and b/sound/vox_fem/deny.ogg differ
diff --git a/sound/vox_fem/detect.ogg b/sound/vox_fem/detect.ogg
new file mode 100644
index 00000000000..e5ea9092f0a
Binary files /dev/null and b/sound/vox_fem/detect.ogg differ
diff --git a/sound/vox_fem/detecting.ogg b/sound/vox_fem/detecting.ogg
new file mode 100644
index 00000000000..1b849cd836d
Binary files /dev/null and b/sound/vox_fem/detecting.ogg differ
diff --git a/sound/vox_fem/different.ogg b/sound/vox_fem/different.ogg
new file mode 100644
index 00000000000..dfce0903818
Binary files /dev/null and b/sound/vox_fem/different.ogg differ
diff --git a/sound/vox_fem/direct.ogg b/sound/vox_fem/direct.ogg
new file mode 100644
index 00000000000..f1268e34a09
Binary files /dev/null and b/sound/vox_fem/direct.ogg differ
diff --git a/sound/vox_fem/dont.ogg b/sound/vox_fem/dont.ogg
new file mode 100644
index 00000000000..20723c8687c
Binary files /dev/null and b/sound/vox_fem/dont.ogg differ
diff --git a/sound/vox_fem/double.ogg b/sound/vox_fem/double.ogg
new file mode 100644
index 00000000000..3c3eb3157bc
Binary files /dev/null and b/sound/vox_fem/double.ogg differ
diff --git a/sound/vox_fem/dress.ogg b/sound/vox_fem/dress.ogg
new file mode 100644
index 00000000000..84cc7fe79b9
Binary files /dev/null and b/sound/vox_fem/dress.ogg differ
diff --git a/sound/vox_fem/dressed.ogg b/sound/vox_fem/dressed.ogg
new file mode 100644
index 00000000000..4a9ef3515a6
Binary files /dev/null and b/sound/vox_fem/dressed.ogg differ
diff --git a/sound/vox_fem/dressing.ogg b/sound/vox_fem/dressing.ogg
new file mode 100644
index 00000000000..dded3edabb3
Binary files /dev/null and b/sound/vox_fem/dressing.ogg differ
diff --git a/sound/vox_fem/easily.ogg b/sound/vox_fem/easily.ogg
new file mode 100644
index 00000000000..cadf6f9291f
Binary files /dev/null and b/sound/vox_fem/easily.ogg differ
diff --git a/sound/vox_fem/eat.ogg b/sound/vox_fem/eat.ogg
new file mode 100644
index 00000000000..b33c5a35034
Binary files /dev/null and b/sound/vox_fem/eat.ogg differ
diff --git a/sound/vox_fem/eaten.ogg b/sound/vox_fem/eaten.ogg
new file mode 100644
index 00000000000..9005930d0f6
Binary files /dev/null and b/sound/vox_fem/eaten.ogg differ
diff --git a/sound/vox_fem/education.ogg b/sound/vox_fem/education.ogg
new file mode 100644
index 00000000000..9a7ed1d17d1
Binary files /dev/null and b/sound/vox_fem/education.ogg differ
diff --git a/sound/vox_fem/effects.ogg b/sound/vox_fem/effects.ogg
new file mode 100644
index 00000000000..59eb22c67fe
Binary files /dev/null and b/sound/vox_fem/effects.ogg differ
diff --git a/sound/vox_fem/electrical.ogg b/sound/vox_fem/electrical.ogg
new file mode 100644
index 00000000000..08e40b2367c
Binary files /dev/null and b/sound/vox_fem/electrical.ogg differ
diff --git a/sound/vox_fem/emitted.ogg b/sound/vox_fem/emitted.ogg
new file mode 100644
index 00000000000..d31b6307a3d
Binary files /dev/null and b/sound/vox_fem/emitted.ogg differ
diff --git a/sound/vox_fem/emitter.ogg b/sound/vox_fem/emitter.ogg
new file mode 100644
index 00000000000..6662ece8be4
Binary files /dev/null and b/sound/vox_fem/emitter.ogg differ
diff --git a/sound/vox_fem/emitting.ogg b/sound/vox_fem/emitting.ogg
new file mode 100644
index 00000000000..377f792c080
Binary files /dev/null and b/sound/vox_fem/emitting.ogg differ
diff --git a/sound/vox_fem/end.ogg b/sound/vox_fem/end.ogg
new file mode 100644
index 00000000000..95b430e8b64
Binary files /dev/null and b/sound/vox_fem/end.ogg differ
diff --git a/sound/vox_fem/ends.ogg b/sound/vox_fem/ends.ogg
new file mode 100644
index 00000000000..34e06feb39e
Binary files /dev/null and b/sound/vox_fem/ends.ogg differ
diff --git a/sound/vox_fem/enough.ogg b/sound/vox_fem/enough.ogg
new file mode 100644
index 00000000000..b7056903b95
Binary files /dev/null and b/sound/vox_fem/enough.ogg differ
diff --git a/sound/vox_fem/even.ogg b/sound/vox_fem/even.ogg
new file mode 100644
index 00000000000..75294ef4025
Binary files /dev/null and b/sound/vox_fem/even.ogg differ
diff --git a/sound/vox_fem/every.ogg b/sound/vox_fem/every.ogg
new file mode 100644
index 00000000000..6468158bd7d
Binary files /dev/null and b/sound/vox_fem/every.ogg differ
diff --git a/sound/vox_fem/everybody.ogg b/sound/vox_fem/everybody.ogg
new file mode 100644
index 00000000000..8d8cc974894
Binary files /dev/null and b/sound/vox_fem/everybody.ogg differ
diff --git a/sound/vox_fem/everyone.ogg b/sound/vox_fem/everyone.ogg
new file mode 100644
index 00000000000..19e137b9a0c
Binary files /dev/null and b/sound/vox_fem/everyone.ogg differ
diff --git a/sound/vox_fem/exploded.ogg b/sound/vox_fem/exploded.ogg
new file mode 100644
index 00000000000..1534b362892
Binary files /dev/null and b/sound/vox_fem/exploded.ogg differ
diff --git a/sound/vox_fem/exploding.ogg b/sound/vox_fem/exploding.ogg
new file mode 100644
index 00000000000..a6980e6b53b
Binary files /dev/null and b/sound/vox_fem/exploding.ogg differ
diff --git a/sound/vox_fem/external.ogg b/sound/vox_fem/external.ogg
new file mode 100644
index 00000000000..d6f39b2651e
Binary files /dev/null and b/sound/vox_fem/external.ogg differ
diff --git a/sound/vox_fem/extra.ogg b/sound/vox_fem/extra.ogg
new file mode 100644
index 00000000000..0b5b16c6957
Binary files /dev/null and b/sound/vox_fem/extra.ogg differ
diff --git a/sound/vox_fem/feature.ogg b/sound/vox_fem/feature.ogg
new file mode 100644
index 00000000000..59a3f68d456
Binary files /dev/null and b/sound/vox_fem/feature.ogg differ
diff --git a/sound/vox_fem/featured.ogg b/sound/vox_fem/featured.ogg
new file mode 100644
index 00000000000..e087b0d7cc6
Binary files /dev/null and b/sound/vox_fem/featured.ogg differ
diff --git a/sound/vox_fem/features.ogg b/sound/vox_fem/features.ogg
new file mode 100644
index 00000000000..745992b760a
Binary files /dev/null and b/sound/vox_fem/features.ogg differ
diff --git a/sound/vox_fem/featuring.ogg b/sound/vox_fem/featuring.ogg
new file mode 100644
index 00000000000..72578f2bb19
Binary files /dev/null and b/sound/vox_fem/featuring.ogg differ
diff --git a/sound/vox_fem/few.ogg b/sound/vox_fem/few.ogg
new file mode 100644
index 00000000000..5ba76619c0f
Binary files /dev/null and b/sound/vox_fem/few.ogg differ
diff --git a/sound/vox_fem/filter.ogg b/sound/vox_fem/filter.ogg
new file mode 100644
index 00000000000..3b2ae95e37f
Binary files /dev/null and b/sound/vox_fem/filter.ogg differ
diff --git a/sound/vox_fem/filters.ogg b/sound/vox_fem/filters.ogg
new file mode 100644
index 00000000000..204b82da198
Binary files /dev/null and b/sound/vox_fem/filters.ogg differ
diff --git a/sound/vox_fem/foolish.ogg b/sound/vox_fem/foolish.ogg
new file mode 100644
index 00000000000..214ed30f2c4
Binary files /dev/null and b/sound/vox_fem/foolish.ogg differ
diff --git a/sound/vox_fem/freon.ogg b/sound/vox_fem/freon.ogg
new file mode 100644
index 00000000000..82128fee241
Binary files /dev/null and b/sound/vox_fem/freon.ogg differ
diff --git a/sound/vox_fem/gases.ogg b/sound/vox_fem/gases.ogg
new file mode 100644
index 00000000000..0605a260556
Binary files /dev/null and b/sound/vox_fem/gases.ogg differ
diff --git a/sound/vox_fem/gave.ogg b/sound/vox_fem/gave.ogg
new file mode 100644
index 00000000000..beb35f1f421
Binary files /dev/null and b/sound/vox_fem/gave.ogg differ
diff --git a/sound/vox_fem/gear.ogg b/sound/vox_fem/gear.ogg
new file mode 100644
index 00000000000..629667e5df4
Binary files /dev/null and b/sound/vox_fem/gear.ogg differ
diff --git a/sound/vox_fem/geared.ogg b/sound/vox_fem/geared.ogg
new file mode 100644
index 00000000000..7188c657589
Binary files /dev/null and b/sound/vox_fem/geared.ogg differ
diff --git a/sound/vox_fem/gearing.ogg b/sound/vox_fem/gearing.ogg
new file mode 100644
index 00000000000..9e27a795694
Binary files /dev/null and b/sound/vox_fem/gearing.ogg differ
diff --git a/sound/vox_fem/generate.ogg b/sound/vox_fem/generate.ogg
new file mode 100644
index 00000000000..ceb09ec3b43
Binary files /dev/null and b/sound/vox_fem/generate.ogg differ
diff --git a/sound/vox_fem/generated.ogg b/sound/vox_fem/generated.ogg
new file mode 100644
index 00000000000..42a49e7d43e
Binary files /dev/null and b/sound/vox_fem/generated.ogg differ
diff --git a/sound/vox_fem/generating.ogg b/sound/vox_fem/generating.ogg
new file mode 100644
index 00000000000..a90e62d9dd7
Binary files /dev/null and b/sound/vox_fem/generating.ogg differ
diff --git a/sound/vox_fem/give.ogg b/sound/vox_fem/give.ogg
new file mode 100644
index 00000000000..8144ba82ccc
Binary files /dev/null and b/sound/vox_fem/give.ogg differ
diff --git a/sound/vox_fem/given.ogg b/sound/vox_fem/given.ogg
new file mode 100644
index 00000000000..63f86c4878f
Binary files /dev/null and b/sound/vox_fem/given.ogg differ
diff --git a/sound/vox_fem/halon.ogg b/sound/vox_fem/halon.ogg
new file mode 100644
index 00000000000..0cab7dee9a5
Binary files /dev/null and b/sound/vox_fem/halon.ogg differ
diff --git a/sound/vox_fem/hard.ogg b/sound/vox_fem/hard.ogg
new file mode 100644
index 00000000000..be1a80c3f09
Binary files /dev/null and b/sound/vox_fem/hard.ogg differ
diff --git a/sound/vox_fem/hardly.ogg b/sound/vox_fem/hardly.ogg
new file mode 100644
index 00000000000..bb4de217a17
Binary files /dev/null and b/sound/vox_fem/hardly.ogg differ
diff --git a/sound/vox_fem/harness.ogg b/sound/vox_fem/harness.ogg
new file mode 100644
index 00000000000..a81840955f4
Binary files /dev/null and b/sound/vox_fem/harness.ogg differ
diff --git a/sound/vox_fem/harnessed.ogg b/sound/vox_fem/harnessed.ogg
new file mode 100644
index 00000000000..d1f259c7bdf
Binary files /dev/null and b/sound/vox_fem/harnessed.ogg differ
diff --git a/sound/vox_fem/harnessing.ogg b/sound/vox_fem/harnessing.ogg
new file mode 100644
index 00000000000..3dbdd03af88
Binary files /dev/null and b/sound/vox_fem/harnessing.ogg differ
diff --git a/sound/vox_fem/heal.ogg b/sound/vox_fem/heal.ogg
new file mode 100644
index 00000000000..f31b53c1cba
Binary files /dev/null and b/sound/vox_fem/heal.ogg differ
diff --git a/sound/vox_fem/healed.ogg b/sound/vox_fem/healed.ogg
new file mode 100644
index 00000000000..80eea8068a5
Binary files /dev/null and b/sound/vox_fem/healed.ogg differ
diff --git a/sound/vox_fem/healing.ogg b/sound/vox_fem/healing.ogg
new file mode 100644
index 00000000000..6ebcc588294
Binary files /dev/null and b/sound/vox_fem/healing.ogg differ
diff --git a/sound/vox_fem/healium.ogg b/sound/vox_fem/healium.ogg
new file mode 100644
index 00000000000..79b4fd19de3
Binary files /dev/null and b/sound/vox_fem/healium.ogg differ
diff --git a/sound/vox_fem/heated.ogg b/sound/vox_fem/heated.ogg
new file mode 100644
index 00000000000..e3db67adee4
Binary files /dev/null and b/sound/vox_fem/heated.ogg differ
diff --git a/sound/vox_fem/heating.ogg b/sound/vox_fem/heating.ogg
new file mode 100644
index 00000000000..553b4d55766
Binary files /dev/null and b/sound/vox_fem/heating.ogg differ
diff --git a/sound/vox_fem/hydrogen.ogg b/sound/vox_fem/hydrogen.ogg
new file mode 100644
index 00000000000..050a3bc028c
Binary files /dev/null and b/sound/vox_fem/hydrogen.ogg differ
diff --git a/sound/vox_fem/hyper-noblium.ogg b/sound/vox_fem/hyper-noblium.ogg
new file mode 100644
index 00000000000..4fda6b40468
Binary files /dev/null and b/sound/vox_fem/hyper-noblium.ogg differ
diff --git a/sound/vox_fem/inert.ogg b/sound/vox_fem/inert.ogg
new file mode 100644
index 00000000000..00826c23b2a
Binary files /dev/null and b/sound/vox_fem/inert.ogg differ
diff --git a/sound/vox_fem/internal.ogg b/sound/vox_fem/internal.ogg
new file mode 100644
index 00000000000..0cc275bd471
Binary files /dev/null and b/sound/vox_fem/internal.ogg differ
diff --git a/sound/vox_fem/irradiate.ogg b/sound/vox_fem/irradiate.ogg
new file mode 100644
index 00000000000..d14b2961798
Binary files /dev/null and b/sound/vox_fem/irradiate.ogg differ
diff --git a/sound/vox_fem/its.ogg b/sound/vox_fem/its.ogg
new file mode 100644
index 00000000000..b5c2c22dd57
Binary files /dev/null and b/sound/vox_fem/its.ogg differ
diff --git a/sound/vox_fem/job.ogg b/sound/vox_fem/job.ogg
new file mode 100644
index 00000000000..5b8186d0f72
Binary files /dev/null and b/sound/vox_fem/job.ogg differ
diff --git a/sound/vox_fem/jobs.ogg b/sound/vox_fem/jobs.ogg
new file mode 100644
index 00000000000..3fd3529e06c
Binary files /dev/null and b/sound/vox_fem/jobs.ogg differ
diff --git a/sound/vox_fem/kelvin.ogg b/sound/vox_fem/kelvin.ogg
new file mode 100644
index 00000000000..dd759e02214
Binary files /dev/null and b/sound/vox_fem/kelvin.ogg differ
diff --git a/sound/vox_fem/limit.ogg b/sound/vox_fem/limit.ogg
new file mode 100644
index 00000000000..d88bd43e01d
Binary files /dev/null and b/sound/vox_fem/limit.ogg differ
diff --git a/sound/vox_fem/limited.ogg b/sound/vox_fem/limited.ogg
new file mode 100644
index 00000000000..df3d3b3ebe4
Binary files /dev/null and b/sound/vox_fem/limited.ogg differ
diff --git a/sound/vox_fem/list.ogg b/sound/vox_fem/list.ogg
new file mode 100644
index 00000000000..1c3d6439833
Binary files /dev/null and b/sound/vox_fem/list.ogg differ
diff --git a/sound/vox_fem/made.ogg b/sound/vox_fem/made.ogg
new file mode 100644
index 00000000000..3540661b5c1
Binary files /dev/null and b/sound/vox_fem/made.ogg differ
diff --git a/sound/vox_fem/making.ogg b/sound/vox_fem/making.ogg
new file mode 100644
index 00000000000..d2ee9ab93e3
Binary files /dev/null and b/sound/vox_fem/making.ogg differ
diff --git a/sound/vox_fem/mean.ogg b/sound/vox_fem/mean.ogg
new file mode 100644
index 00000000000..d53391b033f
Binary files /dev/null and b/sound/vox_fem/mean.ogg differ
diff --git a/sound/vox_fem/means.ogg b/sound/vox_fem/means.ogg
new file mode 100644
index 00000000000..068cb7bfd1a
Binary files /dev/null and b/sound/vox_fem/means.ogg differ
diff --git a/sound/vox_fem/medium.ogg b/sound/vox_fem/medium.ogg
new file mode 100644
index 00000000000..0bd964ffe35
Binary files /dev/null and b/sound/vox_fem/medium.ogg differ
diff --git a/sound/vox_fem/meson.ogg b/sound/vox_fem/meson.ogg
new file mode 100644
index 00000000000..5bd85d32920
Binary files /dev/null and b/sound/vox_fem/meson.ogg differ
diff --git a/sound/vox_fem/method.ogg b/sound/vox_fem/method.ogg
new file mode 100644
index 00000000000..4da8e60475f
Binary files /dev/null and b/sound/vox_fem/method.ogg differ
diff --git a/sound/vox_fem/miasma.ogg b/sound/vox_fem/miasma.ogg
new file mode 100644
index 00000000000..5956b203f87
Binary files /dev/null and b/sound/vox_fem/miasma.ogg differ
diff --git a/sound/vox_fem/minute.ogg b/sound/vox_fem/minute.ogg
new file mode 100644
index 00000000000..fe29d747120
Binary files /dev/null and b/sound/vox_fem/minute.ogg differ
diff --git a/sound/vox_fem/mixture.ogg b/sound/vox_fem/mixture.ogg
new file mode 100644
index 00000000000..63525110a7a
Binary files /dev/null and b/sound/vox_fem/mixture.ogg differ
diff --git a/sound/vox_fem/most.ogg b/sound/vox_fem/most.ogg
new file mode 100644
index 00000000000..472837fced8
Binary files /dev/null and b/sound/vox_fem/most.ogg differ
diff --git a/sound/vox_fem/moved.ogg b/sound/vox_fem/moved.ogg
new file mode 100644
index 00000000000..e8eb17787ef
Binary files /dev/null and b/sound/vox_fem/moved.ogg differ
diff --git a/sound/vox_fem/moving.ogg b/sound/vox_fem/moving.ogg
new file mode 100644
index 00000000000..258c38a7116
Binary files /dev/null and b/sound/vox_fem/moving.ogg differ
diff --git a/sound/vox_fem/near.ogg b/sound/vox_fem/near.ogg
new file mode 100644
index 00000000000..0c48f322133
Binary files /dev/null and b/sound/vox_fem/near.ogg differ
diff --git a/sound/vox_fem/nearly.ogg b/sound/vox_fem/nearly.ogg
new file mode 100644
index 00000000000..2b76f6323f8
Binary files /dev/null and b/sound/vox_fem/nearly.ogg differ
diff --git a/sound/vox_fem/none.ogg b/sound/vox_fem/none.ogg
new file mode 100644
index 00000000000..6c16899ce92
Binary files /dev/null and b/sound/vox_fem/none.ogg differ
diff --git a/sound/vox_fem/normal.ogg b/sound/vox_fem/normal.ogg
new file mode 100644
index 00000000000..cf68ed5ec5e
Binary files /dev/null and b/sound/vox_fem/normal.ogg differ
diff --git a/sound/vox_fem/normally.ogg b/sound/vox_fem/normally.ogg
new file mode 100644
index 00000000000..81d7c16d366
Binary files /dev/null and b/sound/vox_fem/normally.ogg differ
diff --git a/sound/vox_fem/notably.ogg b/sound/vox_fem/notably.ogg
new file mode 100644
index 00000000000..d6f60439050
Binary files /dev/null and b/sound/vox_fem/notably.ogg differ
diff --git a/sound/vox_fem/object.ogg b/sound/vox_fem/object.ogg
new file mode 100644
index 00000000000..091c4b6b190
Binary files /dev/null and b/sound/vox_fem/object.ogg differ
diff --git a/sound/vox_fem/ordered.ogg b/sound/vox_fem/ordered.ogg
new file mode 100644
index 00000000000..a483e355544
Binary files /dev/null and b/sound/vox_fem/ordered.ogg differ
diff --git a/sound/vox_fem/ordering.ogg b/sound/vox_fem/ordering.ogg
new file mode 100644
index 00000000000..e05a1534d3c
Binary files /dev/null and b/sound/vox_fem/ordering.ogg differ
diff --git a/sound/vox_fem/output.ogg b/sound/vox_fem/output.ogg
new file mode 100644
index 00000000000..fc3e5312c4e
Binary files /dev/null and b/sound/vox_fem/output.ogg differ
diff --git a/sound/vox_fem/own.ogg b/sound/vox_fem/own.ogg
new file mode 100644
index 00000000000..e978ba4a8da
Binary files /dev/null and b/sound/vox_fem/own.ogg differ
diff --git a/sound/vox_fem/pda.ogg b/sound/vox_fem/pda.ogg
new file mode 100644
index 00000000000..72407e2b0e3
Binary files /dev/null and b/sound/vox_fem/pda.ogg differ
diff --git a/sound/vox_fem/plating.ogg b/sound/vox_fem/plating.ogg
new file mode 100644
index 00000000000..d0783ca5e35
Binary files /dev/null and b/sound/vox_fem/plating.ogg differ
diff --git a/sound/vox_fem/pluoxium.ogg b/sound/vox_fem/pluoxium.ogg
new file mode 100644
index 00000000000..cb52c5ac298
Binary files /dev/null and b/sound/vox_fem/pluoxium.ogg differ
diff --git a/sound/vox_fem/portion.ogg b/sound/vox_fem/portion.ogg
new file mode 100644
index 00000000000..7f2f699da19
Binary files /dev/null and b/sound/vox_fem/portion.ogg differ
diff --git a/sound/vox_fem/powered.ogg b/sound/vox_fem/powered.ogg
new file mode 100644
index 00000000000..d03eb08d564
Binary files /dev/null and b/sound/vox_fem/powered.ogg differ
diff --git a/sound/vox_fem/powering.ogg b/sound/vox_fem/powering.ogg
new file mode 100644
index 00000000000..7c13b590846
Binary files /dev/null and b/sound/vox_fem/powering.ogg differ
diff --git a/sound/vox_fem/premature.ogg b/sound/vox_fem/premature.ogg
new file mode 100644
index 00000000000..19fb67b8f3b
Binary files /dev/null and b/sound/vox_fem/premature.ogg differ
diff --git a/sound/vox_fem/prematurely.ogg b/sound/vox_fem/prematurely.ogg
new file mode 100644
index 00000000000..b3422a535f6
Binary files /dev/null and b/sound/vox_fem/prematurely.ogg differ
diff --git a/sound/vox_fem/priority.ogg b/sound/vox_fem/priority.ogg
new file mode 100644
index 00000000000..5084584a44e
Binary files /dev/null and b/sound/vox_fem/priority.ogg differ
diff --git a/sound/vox_fem/projectile.ogg b/sound/vox_fem/projectile.ogg
new file mode 100644
index 00000000000..ec6d297926a
Binary files /dev/null and b/sound/vox_fem/projectile.ogg differ
diff --git a/sound/vox_fem/protect.ogg b/sound/vox_fem/protect.ogg
new file mode 100644
index 00000000000..512d7e30e27
Binary files /dev/null and b/sound/vox_fem/protect.ogg differ
diff --git a/sound/vox_fem/protected.ogg b/sound/vox_fem/protected.ogg
new file mode 100644
index 00000000000..7f6c8c0f6ef
Binary files /dev/null and b/sound/vox_fem/protected.ogg differ
diff --git a/sound/vox_fem/protection.ogg b/sound/vox_fem/protection.ogg
new file mode 100644
index 00000000000..90b6a508e2a
Binary files /dev/null and b/sound/vox_fem/protection.ogg differ
diff --git a/sound/vox_fem/proto-nitrate.ogg b/sound/vox_fem/proto-nitrate.ogg
new file mode 100644
index 00000000000..13f3aed7b45
Binary files /dev/null and b/sound/vox_fem/proto-nitrate.ogg differ
diff --git a/sound/vox_fem/pull.ogg b/sound/vox_fem/pull.ogg
new file mode 100644
index 00000000000..9ac8e2c399a
Binary files /dev/null and b/sound/vox_fem/pull.ogg differ
diff --git a/sound/vox_fem/pulled.ogg b/sound/vox_fem/pulled.ogg
new file mode 100644
index 00000000000..ef3388a25c9
Binary files /dev/null and b/sound/vox_fem/pulled.ogg differ
diff --git a/sound/vox_fem/pulling.ogg b/sound/vox_fem/pulling.ogg
new file mode 100644
index 00000000000..5a2b5532e5a
Binary files /dev/null and b/sound/vox_fem/pulling.ogg differ
diff --git a/sound/vox_fem/pump.ogg b/sound/vox_fem/pump.ogg
new file mode 100644
index 00000000000..07400cb2b8c
Binary files /dev/null and b/sound/vox_fem/pump.ogg differ
diff --git a/sound/vox_fem/pumps.ogg b/sound/vox_fem/pumps.ogg
new file mode 100644
index 00000000000..eba663d3963
Binary files /dev/null and b/sound/vox_fem/pumps.ogg differ
diff --git a/sound/vox_fem/release.ogg b/sound/vox_fem/release.ogg
new file mode 100644
index 00000000000..4a935016848
Binary files /dev/null and b/sound/vox_fem/release.ogg differ
diff --git a/sound/vox_fem/releasing.ogg b/sound/vox_fem/releasing.ogg
new file mode 100644
index 00000000000..3d2a5d0ed70
Binary files /dev/null and b/sound/vox_fem/releasing.ogg differ
diff --git a/sound/vox_fem/remove.ogg b/sound/vox_fem/remove.ogg
new file mode 100644
index 00000000000..e9b2cb3bc12
Binary files /dev/null and b/sound/vox_fem/remove.ogg differ
diff --git a/sound/vox_fem/removed.ogg b/sound/vox_fem/removed.ogg
new file mode 100644
index 00000000000..2a7d78e9280
Binary files /dev/null and b/sound/vox_fem/removed.ogg differ
diff --git a/sound/vox_fem/removing.ogg b/sound/vox_fem/removing.ogg
new file mode 100644
index 00000000000..638d20b0c35
Binary files /dev/null and b/sound/vox_fem/removing.ogg differ
diff --git a/sound/vox_fem/resistant.ogg b/sound/vox_fem/resistant.ogg
new file mode 100644
index 00000000000..44015199791
Binary files /dev/null and b/sound/vox_fem/resistant.ogg differ
diff --git a/sound/vox_fem/resisting.ogg b/sound/vox_fem/resisting.ogg
new file mode 100644
index 00000000000..c3c23d267fd
Binary files /dev/null and b/sound/vox_fem/resisting.ogg differ
diff --git a/sound/vox_fem/resonance.ogg b/sound/vox_fem/resonance.ogg
new file mode 100644
index 00000000000..0d569574c83
Binary files /dev/null and b/sound/vox_fem/resonance.ogg differ
diff --git a/sound/vox_fem/sabotaged.ogg b/sound/vox_fem/sabotaged.ogg
new file mode 100644
index 00000000000..9d883d12218
Binary files /dev/null and b/sound/vox_fem/sabotaged.ogg differ
diff --git a/sound/vox_fem/sabotaging.ogg b/sound/vox_fem/sabotaging.ogg
new file mode 100644
index 00000000000..a8b5d6680c3
Binary files /dev/null and b/sound/vox_fem/sabotaging.ogg differ
diff --git a/sound/vox_fem/same.ogg b/sound/vox_fem/same.ogg
new file mode 100644
index 00000000000..38adb22168b
Binary files /dev/null and b/sound/vox_fem/same.ogg differ
diff --git a/sound/vox_fem/saw.ogg b/sound/vox_fem/saw.ogg
new file mode 100644
index 00000000000..fa3c7b1994d
Binary files /dev/null and b/sound/vox_fem/saw.ogg differ
diff --git a/sound/vox_fem/scan.ogg b/sound/vox_fem/scan.ogg
new file mode 100644
index 00000000000..36026e8f8c4
Binary files /dev/null and b/sound/vox_fem/scan.ogg differ
diff --git a/sound/vox_fem/scanned.ogg b/sound/vox_fem/scanned.ogg
new file mode 100644
index 00000000000..bc9bb80ac4c
Binary files /dev/null and b/sound/vox_fem/scanned.ogg differ
diff --git a/sound/vox_fem/scanner.ogg b/sound/vox_fem/scanner.ogg
new file mode 100644
index 00000000000..2098a81a34c
Binary files /dev/null and b/sound/vox_fem/scanner.ogg differ
diff --git a/sound/vox_fem/scanners.ogg b/sound/vox_fem/scanners.ogg
new file mode 100644
index 00000000000..74bbdae300a
Binary files /dev/null and b/sound/vox_fem/scanners.ogg differ
diff --git a/sound/vox_fem/scanning.ogg b/sound/vox_fem/scanning.ogg
new file mode 100644
index 00000000000..84701910e99
Binary files /dev/null and b/sound/vox_fem/scanning.ogg differ
diff --git a/sound/vox_fem/screw.ogg b/sound/vox_fem/screw.ogg
new file mode 100644
index 00000000000..ae9325f69a6
Binary files /dev/null and b/sound/vox_fem/screw.ogg differ
diff --git a/sound/vox_fem/seen.ogg b/sound/vox_fem/seen.ogg
new file mode 100644
index 00000000000..7ad89a173ab
Binary files /dev/null and b/sound/vox_fem/seen.ogg differ
diff --git a/sound/vox_fem/set.ogg b/sound/vox_fem/set.ogg
new file mode 100644
index 00000000000..f2f8c19c278
Binary files /dev/null and b/sound/vox_fem/set.ogg differ
diff --git a/sound/vox_fem/sever.ogg b/sound/vox_fem/sever.ogg
new file mode 100644
index 00000000000..3bc2e2102f7
Binary files /dev/null and b/sound/vox_fem/sever.ogg differ
diff --git a/sound/vox_fem/severed.ogg b/sound/vox_fem/severed.ogg
new file mode 100644
index 00000000000..4f6f7e47bf0
Binary files /dev/null and b/sound/vox_fem/severed.ogg differ
diff --git a/sound/vox_fem/severing.ogg b/sound/vox_fem/severing.ogg
new file mode 100644
index 00000000000..1ddad6209ef
Binary files /dev/null and b/sound/vox_fem/severing.ogg differ
diff --git a/sound/vox_fem/shame.ogg b/sound/vox_fem/shame.ogg
new file mode 100644
index 00000000000..f90b1306da0
Binary files /dev/null and b/sound/vox_fem/shame.ogg differ
diff --git a/sound/vox_fem/shameful.ogg b/sound/vox_fem/shameful.ogg
new file mode 100644
index 00000000000..526b70909f6
Binary files /dev/null and b/sound/vox_fem/shameful.ogg differ
diff --git a/sound/vox_fem/shameless.ogg b/sound/vox_fem/shameless.ogg
new file mode 100644
index 00000000000..804a92e2961
Binary files /dev/null and b/sound/vox_fem/shameless.ogg differ
diff --git a/sound/vox_fem/shard.ogg b/sound/vox_fem/shard.ogg
new file mode 100644
index 00000000000..2a6b0c19580
Binary files /dev/null and b/sound/vox_fem/shard.ogg differ
diff --git a/sound/vox_fem/shift.ogg b/sound/vox_fem/shift.ogg
new file mode 100644
index 00000000000..70e7d3ca6d1
Binary files /dev/null and b/sound/vox_fem/shift.ogg differ
diff --git a/sound/vox_fem/shifts.ogg b/sound/vox_fem/shifts.ogg
new file mode 100644
index 00000000000..f2caa538efc
Binary files /dev/null and b/sound/vox_fem/shifts.ogg differ
diff --git a/sound/vox_fem/sides.ogg b/sound/vox_fem/sides.ogg
new file mode 100644
index 00000000000..cef8d153207
Binary files /dev/null and b/sound/vox_fem/sides.ogg differ
diff --git a/sound/vox_fem/single.ogg b/sound/vox_fem/single.ogg
new file mode 100644
index 00000000000..1d5912ecc57
Binary files /dev/null and b/sound/vox_fem/single.ogg differ
diff --git a/sound/vox_fem/siphon.ogg b/sound/vox_fem/siphon.ogg
new file mode 100644
index 00000000000..789653f5cbb
Binary files /dev/null and b/sound/vox_fem/siphon.ogg differ
diff --git a/sound/vox_fem/siphoning.ogg b/sound/vox_fem/siphoning.ogg
new file mode 100644
index 00000000000..163f2ed4734
Binary files /dev/null and b/sound/vox_fem/siphoning.ogg differ
diff --git a/sound/vox_fem/soft.ogg b/sound/vox_fem/soft.ogg
new file mode 100644
index 00000000000..ea9361ea8c9
Binary files /dev/null and b/sound/vox_fem/soft.ogg differ
diff --git a/sound/vox_fem/source.ogg b/sound/vox_fem/source.ogg
new file mode 100644
index 00000000000..824dd3f0a8c
Binary files /dev/null and b/sound/vox_fem/source.ogg differ
diff --git a/sound/vox_fem/special.ogg b/sound/vox_fem/special.ogg
new file mode 100644
index 00000000000..f0f219c1397
Binary files /dev/null and b/sound/vox_fem/special.ogg differ
diff --git a/sound/vox_fem/spew.ogg b/sound/vox_fem/spew.ogg
new file mode 100644
index 00000000000..b9d20048d26
Binary files /dev/null and b/sound/vox_fem/spew.ogg differ
diff --git a/sound/vox_fem/start.ogg b/sound/vox_fem/start.ogg
new file mode 100644
index 00000000000..b07dc5faf25
Binary files /dev/null and b/sound/vox_fem/start.ogg differ
diff --git a/sound/vox_fem/starts.ogg b/sound/vox_fem/starts.ogg
new file mode 100644
index 00000000000..fca5914927f
Binary files /dev/null and b/sound/vox_fem/starts.ogg differ
diff --git a/sound/vox_fem/stations.ogg b/sound/vox_fem/stations.ogg
new file mode 100644
index 00000000000..c0a49526369
Binary files /dev/null and b/sound/vox_fem/stations.ogg differ
diff --git a/sound/vox_fem/stationwide.ogg b/sound/vox_fem/stationwide.ogg
new file mode 100644
index 00000000000..b7b000b3b9f
Binary files /dev/null and b/sound/vox_fem/stationwide.ogg differ
diff --git a/sound/vox_fem/such.ogg b/sound/vox_fem/such.ogg
new file mode 100644
index 00000000000..a85d0642e6e
Binary files /dev/null and b/sound/vox_fem/such.ogg differ
diff --git a/sound/vox_fem/suited.ogg b/sound/vox_fem/suited.ogg
new file mode 100644
index 00000000000..36b2b45c527
Binary files /dev/null and b/sound/vox_fem/suited.ogg differ
diff --git a/sound/vox_fem/super.ogg b/sound/vox_fem/super.ogg
new file mode 100644
index 00000000000..6d110152fdc
Binary files /dev/null and b/sound/vox_fem/super.ogg differ
diff --git a/sound/vox_fem/tesla.ogg b/sound/vox_fem/tesla.ogg
new file mode 100644
index 00000000000..70cb9e4ca82
Binary files /dev/null and b/sound/vox_fem/tesla.ogg differ
diff --git a/sound/vox_fem/tick.ogg b/sound/vox_fem/tick.ogg
new file mode 100644
index 00000000000..bb986534a5b
Binary files /dev/null and b/sound/vox_fem/tick.ogg differ
diff --git a/sound/vox_fem/tile.ogg b/sound/vox_fem/tile.ogg
new file mode 100644
index 00000000000..2d794c26a12
Binary files /dev/null and b/sound/vox_fem/tile.ogg differ
diff --git a/sound/vox_fem/touched.ogg b/sound/vox_fem/touched.ogg
new file mode 100644
index 00000000000..e4cb940d71f
Binary files /dev/null and b/sound/vox_fem/touched.ogg differ
diff --git a/sound/vox_fem/touching.ogg b/sound/vox_fem/touching.ogg
new file mode 100644
index 00000000000..b1ef04d443a
Binary files /dev/null and b/sound/vox_fem/touching.ogg differ
diff --git a/sound/vox_fem/trigger.ogg b/sound/vox_fem/trigger.ogg
new file mode 100644
index 00000000000..22ed22d4890
Binary files /dev/null and b/sound/vox_fem/trigger.ogg differ
diff --git a/sound/vox_fem/triggered.ogg b/sound/vox_fem/triggered.ogg
new file mode 100644
index 00000000000..7fe30ad8046
Binary files /dev/null and b/sound/vox_fem/triggered.ogg differ
diff --git a/sound/vox_fem/triggering.ogg b/sound/vox_fem/triggering.ogg
new file mode 100644
index 00000000000..8cb47881556
Binary files /dev/null and b/sound/vox_fem/triggering.ogg differ
diff --git a/sound/vox_fem/triple.ogg b/sound/vox_fem/triple.ogg
new file mode 100644
index 00000000000..6b667925810
Binary files /dev/null and b/sound/vox_fem/triple.ogg differ
diff --git a/sound/vox_fem/tritium.ogg b/sound/vox_fem/tritium.ogg
new file mode 100644
index 00000000000..7e62eddb9c5
Binary files /dev/null and b/sound/vox_fem/tritium.ogg differ
diff --git a/sound/vox_fem/turned.ogg b/sound/vox_fem/turned.ogg
new file mode 100644
index 00000000000..2d9c59edfac
Binary files /dev/null and b/sound/vox_fem/turned.ogg differ
diff --git a/sound/vox_fem/unique.ogg b/sound/vox_fem/unique.ogg
new file mode 100644
index 00000000000..cb773507fcb
Binary files /dev/null and b/sound/vox_fem/unique.ogg differ
diff --git a/sound/vox_fem/unwrench.ogg b/sound/vox_fem/unwrench.ogg
new file mode 100644
index 00000000000..bd31681fe31
Binary files /dev/null and b/sound/vox_fem/unwrench.ogg differ
diff --git a/sound/vox_fem/unwrenching.ogg b/sound/vox_fem/unwrenching.ogg
new file mode 100644
index 00000000000..6f4d8b7de7e
Binary files /dev/null and b/sound/vox_fem/unwrenching.ogg differ
diff --git a/sound/vox_fem/useful.ogg b/sound/vox_fem/useful.ogg
new file mode 100644
index 00000000000..dfd8e6952a5
Binary files /dev/null and b/sound/vox_fem/useful.ogg differ
diff --git a/sound/vox_fem/useless.ogg b/sound/vox_fem/useless.ogg
new file mode 100644
index 00000000000..5b9bb7aa294
Binary files /dev/null and b/sound/vox_fem/useless.ogg differ
diff --git a/sound/vox_fem/way.ogg b/sound/vox_fem/way.ogg
new file mode 100644
index 00000000000..fdc121629c9
Binary files /dev/null and b/sound/vox_fem/way.ogg differ
diff --git a/sound/vox_fem/ways.ogg b/sound/vox_fem/ways.ogg
new file mode 100644
index 00000000000..8fa99a1c4b7
Binary files /dev/null and b/sound/vox_fem/ways.ogg differ
diff --git a/sound/vox_fem/weld.ogg b/sound/vox_fem/weld.ogg
new file mode 100644
index 00000000000..6273ba147a6
Binary files /dev/null and b/sound/vox_fem/weld.ogg differ
diff --git a/sound/vox_fem/which.ogg b/sound/vox_fem/which.ogg
new file mode 100644
index 00000000000..ae1486f816a
Binary files /dev/null and b/sound/vox_fem/which.ogg differ
diff --git a/sound/vox_fem/work.ogg b/sound/vox_fem/work.ogg
new file mode 100644
index 00000000000..8ddb06ef1d5
Binary files /dev/null and b/sound/vox_fem/work.ogg differ
diff --git a/sound/vox_fem/worked.ogg b/sound/vox_fem/worked.ogg
new file mode 100644
index 00000000000..7ea59ff0b2b
Binary files /dev/null and b/sound/vox_fem/worked.ogg differ
diff --git a/sound/vox_fem/working.ogg b/sound/vox_fem/working.ogg
new file mode 100644
index 00000000000..8e49779ee5c
Binary files /dev/null and b/sound/vox_fem/working.ogg differ
diff --git a/sound/vox_fem/works.ogg b/sound/vox_fem/works.ogg
new file mode 100644
index 00000000000..c35984171ab
Binary files /dev/null and b/sound/vox_fem/works.ogg differ
diff --git a/sound/vox_fem/would.ogg b/sound/vox_fem/would.ogg
new file mode 100644
index 00000000000..2df534769af
Binary files /dev/null and b/sound/vox_fem/would.ogg differ
diff --git a/sound/vox_fem/wouldnt.ogg b/sound/vox_fem/wouldnt.ogg
new file mode 100644
index 00000000000..0c3b01facee
Binary files /dev/null and b/sound/vox_fem/wouldnt.ogg differ
diff --git a/sound/vox_fem/wrench.ogg b/sound/vox_fem/wrench.ogg
new file mode 100644
index 00000000000..98cf94db09a
Binary files /dev/null and b/sound/vox_fem/wrench.ogg differ
diff --git a/sound/vox_fem/wrenching.ogg b/sound/vox_fem/wrenching.ogg
new file mode 100644
index 00000000000..b61b70fd363
Binary files /dev/null and b/sound/vox_fem/wrenching.ogg differ
diff --git a/sound/vox_fem/zap.ogg b/sound/vox_fem/zap.ogg
new file mode 100644
index 00000000000..233653de913
Binary files /dev/null and b/sound/vox_fem/zap.ogg differ
diff --git a/sound/vox_fem/zauker.ogg b/sound/vox_fem/zauker.ogg
new file mode 100644
index 00000000000..ed586c6c3f0
Binary files /dev/null and b/sound/vox_fem/zauker.ogg differ
diff --git a/sound/weapons/shove.ogg b/sound/weapons/shove.ogg
new file mode 100644
index 00000000000..eb10eabed26
Binary files /dev/null and b/sound/weapons/shove.ogg differ
diff --git a/sound/weapons/solarflare.ogg b/sound/weapons/solarflare.ogg
new file mode 100644
index 00000000000..48f6c5e8922
Binary files /dev/null and b/sound/weapons/solarflare.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index 637a1d674ca..466d81c03e4 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -565,7 +565,6 @@
#include "code\__HELPERS\radio.dm"
#include "code\__HELPERS\randoms.dm"
#include "code\__HELPERS\reagents.dm"
-#include "code\__HELPERS\records.dm"
#include "code\__HELPERS\ref.dm"
#include "code\__HELPERS\roundend.dm"
#include "code\__HELPERS\sanitize_values.dm"
@@ -894,6 +893,7 @@
#include "code\datums\dog_fashion.dm"
#include "code\datums\ductnet.dm"
#include "code\datums\eigenstate.dm"
+#include "code\datums\embed_data.dm"
#include "code\datums\emotes.dm"
#include "code\datums\ert.dm"
#include "code\datums\hailer_phrase.dm"
@@ -901,6 +901,7 @@
#include "code\datums\hotkeys_help.dm"
#include "code\datums\http.dm"
#include "code\datums\hud.dm"
+#include "code\datums\instability_meltdown.dm"
#include "code\datums\json_database.dm"
#include "code\datums\json_savefile.dm"
#include "code\datums\lazy_template.dm"
@@ -1133,6 +1134,7 @@
#include "code\datums\components\ai_has_target_timer.dm"
#include "code\datums\components\ai_listen_to_weather.dm"
#include "code\datums\components\ai_retaliate_advanced.dm"
+#include "code\datums\components\amputating_limbs.dm"
#include "code\datums\components\anti_magic.dm"
#include "code\datums\components\appearance_on_aggro.dm"
#include "code\datums\components\aquarium_content.dm"
@@ -1146,6 +1148,7 @@
#include "code\datums\components\basic_inhands.dm"
#include "code\datums\components\basic_mob_attack_telegraph.dm"
#include "code\datums\components\basic_ranged_ready_overlay.dm"
+#include "code\datums\components\bayonet_attachable.dm"
#include "code\datums\components\beetlejuice.dm"
#include "code\datums\components\blob_minion.dm"
#include "code\datums\components\blood_walk.dm"
@@ -1386,7 +1389,6 @@
#include "code\datums\components\crafting\misc.dm"
#include "code\datums\components\crafting\ranged_weapon.dm"
#include "code\datums\components\crafting\robot.dm"
-#include "code\datums\components\crafting\slapcrafting.dm"
#include "code\datums\components\crafting\structures.dm"
#include "code\datums\components\crafting\tailoring.dm"
#include "code\datums\components\crafting\tiles.dm"
@@ -1489,7 +1491,6 @@
#include "code\datums\elements\ai_retaliate.dm"
#include "code\datums\elements\ai_swap_combat_mode.dm"
#include "code\datums\elements\ai_target_damagesource.dm"
-#include "code\datums\elements\amputating_limbs.dm"
#include "code\datums\elements\animal_variety.dm"
#include "code\datums\elements\art.dm"
#include "code\datums\elements\atmos_requirements.dm"
@@ -1575,6 +1576,7 @@
#include "code\datums\elements\kneejerk.dm"
#include "code\datums\elements\knockback.dm"
#include "code\datums\elements\lazy_fishing_spot.dm"
+#include "code\datums\elements\leeching_walk.dm"
#include "code\datums\elements\lifesteal.dm"
#include "code\datums\elements\light_blocking.dm"
#include "code\datums\elements\light_eaten.dm"
@@ -1589,6 +1591,7 @@
#include "code\datums\elements\move_force_on_death.dm"
#include "code\datums\elements\movement_turf_changer.dm"
#include "code\datums\elements\movetype_handler.dm"
+#include "code\datums\elements\muffles_speech.dm"
#include "code\datums\elements\nerfed_pulling.dm"
#include "code\datums\elements\noisy_movement.dm"
#include "code\datums\elements\noticable_organ.dm"
@@ -1617,6 +1620,7 @@
#include "code\datums\elements\simple_flying.dm"
#include "code\datums\elements\skill_reward.dm"
#include "code\datums\elements\skittish.dm"
+#include "code\datums\elements\slapcrafting.dm"
#include "code\datums\elements\soft_landing.dm"
#include "code\datums\elements\spooky.dm"
#include "code\datums\elements\squish.dm"
@@ -1645,6 +1649,7 @@
#include "code\datums\elements\wall_walker.dm"
#include "code\datums\elements\watery_tile.dm"
#include "code\datums\elements\weapon_description.dm"
+#include "code\datums\elements\wearable_client_colour.dm"
#include "code\datums\elements\weather_listener.dm"
#include "code\datums\elements\web_walker.dm"
#include "code\datums\elements\wheel.dm"
@@ -1707,6 +1712,7 @@
#include "code\datums\keybinding\robot.dm"
#include "code\datums\looping_sounds\_looping_sound.dm"
#include "code\datums\looping_sounds\acid.dm"
+#include "code\datums\looping_sounds\breathing.dm"
#include "code\datums\looping_sounds\burning.dm"
#include "code\datums\looping_sounds\choking.dm"
#include "code\datums\looping_sounds\cyborg.dm"
@@ -3168,7 +3174,6 @@
#include "code\modules\antagonists\clown_ops\clownop.dm"
#include "code\modules\antagonists\clown_ops\outfits.dm"
#include "code\modules\antagonists\cult\blood_magic.dm"
-#include "code\modules\antagonists\cult\cult_bastard_sword.dm"
#include "code\modules\antagonists\cult\cult_comms.dm"
#include "code\modules\antagonists\cult\cult_items.dm"
#include "code\modules\antagonists\cult\cult_objectives.dm"
@@ -3181,6 +3186,7 @@
#include "code\modules\antagonists\cult\cult_turf_overlay.dm"
#include "code\modules\antagonists\cult\rune_spawn_action.dm"
#include "code\modules\antagonists\cult\runes.dm"
+#include "code\modules\antagonists\cult\sword_fling.dm"
#include "code\modules\antagonists\cult\datums\constructs.dm"
#include "code\modules\antagonists\cult\datums\cult_team.dm"
#include "code\modules\antagonists\cult\datums\cultist.dm"
@@ -3203,6 +3209,7 @@
#include "code\modules\antagonists\heretic\knife_effect.dm"
#include "code\modules\antagonists\heretic\moon_lunatic.dm"
#include "code\modules\antagonists\heretic\rust_effect.dm"
+#include "code\modules\antagonists\heretic\soultrapped_heretic.dm"
#include "code\modules\antagonists\heretic\transmutation_rune.dm"
#include "code\modules\antagonists\heretic\items\corrupted_organs.dm"
#include "code\modules\antagonists\heretic\items\eldritch_flask.dm"
@@ -3560,6 +3567,7 @@
#include "code\modules\atmospherics\machinery\pipes\heat_exchange\manifold4w.dm"
#include "code\modules\atmospherics\machinery\pipes\heat_exchange\simple.dm"
#include "code\modules\atmospherics\machinery\portable\canister.dm"
+#include "code\modules\atmospherics\machinery\portable\pipe_scrubber.dm"
#include "code\modules\atmospherics\machinery\portable\portable_atmospherics.dm"
#include "code\modules\atmospherics\machinery\portable\pump.dm"
#include "code\modules\atmospherics\machinery\portable\scrubber.dm"
@@ -3613,6 +3621,13 @@
#include "code\modules\bitrunning\components\netpod_healing.dm"
#include "code\modules\bitrunning\components\npc_friendly.dm"
#include "code\modules\bitrunning\components\virtual_entity.dm"
+#include "code\modules\bitrunning\netpod\_netpod.dm"
+#include "code\modules\bitrunning\netpod\container.dm"
+#include "code\modules\bitrunning\netpod\outfitting.dm"
+#include "code\modules\bitrunning\netpod\signals.dm"
+#include "code\modules\bitrunning\netpod\tools.dm"
+#include "code\modules\bitrunning\netpod\ui.dm"
+#include "code\modules\bitrunning\netpod\utils.dm"
#include "code\modules\bitrunning\objects\byteforge.dm"
#include "code\modules\bitrunning\objects\clothing.dm"
#include "code\modules\bitrunning\objects\debug.dm"
@@ -3622,7 +3637,6 @@
#include "code\modules\bitrunning\objects\landmarks.dm"
#include "code\modules\bitrunning\objects\loot_box.dm"
#include "code\modules\bitrunning\objects\loot_crate.dm"
-#include "code\modules\bitrunning\objects\netpod.dm"
#include "code\modules\bitrunning\objects\quantum_console.dm"
#include "code\modules\bitrunning\objects\vendor.dm"
#include "code\modules\bitrunning\orders\bepis.dm"
@@ -3652,6 +3666,7 @@
#include "code\modules\bitrunning\virtual_domain\domains\fredingtonfastingbear.dm"
#include "code\modules\bitrunning\virtual_domain\domains\gondola_asteroid.dm"
#include "code\modules\bitrunning\virtual_domain\domains\hierophant.dm"
+#include "code\modules\bitrunning\virtual_domain\domains\island_brawl.dm"
#include "code\modules\bitrunning\virtual_domain\domains\pipedream.dm"
#include "code\modules\bitrunning\virtual_domain\domains\pirates.dm"
#include "code\modules\bitrunning\virtual_domain\domains\psyker_shuffle.dm"
@@ -4597,6 +4612,7 @@
#include "code\modules\mapfluff\ruins\lavaland_ruin_code.dm"
#include "code\modules\mapfluff\ruins\icemoonruin_code\commsagent.dm"
#include "code\modules\mapfluff\ruins\icemoonruin_code\hotsprings.dm"
+#include "code\modules\mapfluff\ruins\icemoonruin_code\lavaland_incursion.dm"
#include "code\modules\mapfluff\ruins\icemoonruin_code\library.dm"
#include "code\modules\mapfluff\ruins\icemoonruin_code\mailroom.dm"
#include "code\modules\mapfluff\ruins\icemoonruin_code\mining_site.dm"
@@ -5459,10 +5475,10 @@
#include "code\modules\plumbing\plumbers\plumbing_buffer.dm"
#include "code\modules\plumbing\plumbers\pumps.dm"
#include "code\modules\plumbing\plumbers\reaction_chamber.dm"
+#include "code\modules\plumbing\plumbers\simple_machines.dm"
#include "code\modules\plumbing\plumbers\splitters.dm"
#include "code\modules\plumbing\plumbers\synthesizer.dm"
#include "code\modules\plumbing\plumbers\teleporter.dm"
-#include "code\modules\plumbing\plumbers\vatgrower.dm"
#include "code\modules\point\point.dm"
#include "code\modules\power\battery.dm"
#include "code\modules\power\cable.dm"
@@ -5637,6 +5653,7 @@
#include "code\modules\projectiles\projectile\energy\net_snare.dm"
#include "code\modules\projectiles\projectile\energy\ninja.dm"
#include "code\modules\projectiles\projectile\energy\nuclear_particle.dm"
+#include "code\modules\projectiles\projectile\energy\photon.dm"
#include "code\modules\projectiles\projectile\energy\radiation.dm"
#include "code\modules\projectiles\projectile\energy\stun.dm"
#include "code\modules\projectiles\projectile\energy\tesla.dm"
@@ -5855,6 +5872,7 @@
#include "code\modules\research\xenobiology\vatgrowing\microscope.dm"
#include "code\modules\research\xenobiology\vatgrowing\petri_dish.dm"
#include "code\modules\research\xenobiology\vatgrowing\swab.dm"
+#include "code\modules\research\xenobiology\vatgrowing\vatgrower.dm"
#include "code\modules\research\xenobiology\vatgrowing\samples\_micro_organism.dm"
#include "code\modules\research\xenobiology\vatgrowing\samples\_sample.dm"
#include "code\modules\research\xenobiology\vatgrowing\samples\cell_lines\common.dm"
@@ -6204,6 +6222,7 @@
#include "code\modules\vehicles\mecha\combat\durand.dm"
#include "code\modules\vehicles\mecha\combat\gygax.dm"
#include "code\modules\vehicles\mecha\combat\honker.dm"
+#include "code\modules\vehicles\mecha\combat\justice.dm"
#include "code\modules\vehicles\mecha\combat\marauder.dm"
#include "code\modules\vehicles\mecha\combat\phazon.dm"
#include "code\modules\vehicles\mecha\combat\reticence.dm"
@@ -6248,6 +6267,7 @@
#include "code\modules\vending\security.dm"
#include "code\modules\vending\snack.dm"
#include "code\modules\vending\sovietsoda.dm"
+#include "code\modules\vending\subtype.dm"
#include "code\modules\vending\sustenance.dm"
#include "code\modules\vending\toys.dm"
#include "code\modules\vending\wardrobes.dm"
@@ -6676,6 +6696,7 @@
#include "modular_skyrat\master_files\code\modules\jobs\job_types\warden.dm"
#include "modular_skyrat\master_files\code\modules\language\_language.dm"
#include "modular_skyrat\master_files\code\modules\language\language_holder.dm"
+#include "modular_skyrat\master_files\code\modules\loadout\category_rebalance.dm"
#include "modular_skyrat\master_files\code\modules\loadout\categories\accessories.dm"
#include "modular_skyrat\master_files\code\modules\loadout\categories\belts.dm"
#include "modular_skyrat\master_files\code\modules\loadout\categories\donator_personal.dm"
@@ -6765,6 +6786,7 @@
#include "modular_skyrat\master_files\code\modules\projectiles\guns\ballistic\bows\_bow.dm"
#include "modular_skyrat\master_files\code\modules\projectiles\guns\ballistic\bows\bow_quivers.dm"
#include "modular_skyrat\master_files\code\modules\projectiles\guns\ballistic\bows\bow_types.dm"
+#include "modular_skyrat\master_files\code\modules\projectiles\guns\energy\beam_rifle.dm"
#include "modular_skyrat\master_files\code\modules\reagents\bottle.dm"
#include "modular_skyrat\master_files\code\modules\reagents\reagent_containers.dm"
#include "modular_skyrat\master_files\code\modules\reagents\chemistry\colors.dm"
@@ -7729,6 +7751,7 @@
#include "modular_skyrat\modules\mapping\code\spaceship_items.dm"
#include "modular_skyrat\modules\mapping\code\spaceship_turfs.dm"
#include "modular_skyrat\modules\mapping\code\static_plaques.dm"
+#include "modular_skyrat\modules\mapping\code\telescreens.dm"
#include "modular_skyrat\modules\mapping\code\tools.dm"
#include "modular_skyrat\modules\mapping\code\turf.dm"
#include "modular_skyrat\modules\mapping\code\turf_decals.dm"
diff --git a/tgui/packages/tgui-panel/settings/SettingsGeneral.tsx b/tgui/packages/tgui-panel/settings/SettingsGeneral.tsx
new file mode 100644
index 00000000000..8203f031395
--- /dev/null
+++ b/tgui/packages/tgui-panel/settings/SettingsGeneral.tsx
@@ -0,0 +1,173 @@
+import { toFixed } from 'common/math';
+import { capitalize } from 'common/string';
+import { useState } from 'react';
+import { useDispatch, useSelector } from 'tgui/backend';
+import {
+ Button,
+ Collapsible,
+ Divider,
+ Input,
+ LabeledList,
+ NumberInput,
+ Section,
+ Stack,
+} from 'tgui/components';
+
+import { clearChat, saveChatToDisk } from '../chat/actions';
+import { THEMES } from '../themes';
+import { updateSettings } from './actions';
+import { FONTS } from './constants';
+import { selectSettings } from './selectors';
+
+export function SettingsGeneral(props) {
+ const { theme, fontFamily, fontSize, lineHeight } =
+ useSelector(selectSettings);
+ const dispatch = useDispatch();
+ const [freeFont, setFreeFont] = useState(false);
+
+ return (
+
+
+
+ {THEMES.map((THEME) => (
+
+ ))}
+
+
+
+ {!freeFont ? (
+ {
+ setFreeFont(!freeFont);
+ }}
+ >
+ Custom font
+
+ }
+ >
+ {FONTS.map((FONT) => (
+
+ ))}
+
+ ) : (
+
+
+ dispatch(
+ updateSettings({
+ fontFamily: value,
+ }),
+ )
+ }
+ />
+
+
+ )}
+
+
+
+ toFixed(value)}
+ onChange={(value) =>
+ dispatch(
+ updateSettings({
+ fontSize: value,
+ }),
+ )
+ }
+ />
+
+
+ toFixed(value, 2)}
+ onDrag={(value) =>
+ dispatch(
+ updateSettings({
+ lineHeight: value,
+ }),
+ )
+ }
+ />
+
+
+
+
+
+
+
+
+ dispatch(clearChat())}
+ >
+ Clear chat
+
+
+
+
+ );
+}
diff --git a/tgui/packages/tgui-panel/settings/SettingsPanel.jsx b/tgui/packages/tgui-panel/settings/SettingsPanel.jsx
deleted file mode 100644
index e5691037bba..00000000000
--- a/tgui/packages/tgui-panel/settings/SettingsPanel.jsx
+++ /dev/null
@@ -1,371 +0,0 @@
-/**
- * @file
- * @copyright 2020 Aleksej Komarov
- * @license MIT
- */
-
-import { toFixed } from 'common/math';
-import { capitalize } from 'common/string';
-import { useLocalState } from 'tgui/backend';
-import { useDispatch, useSelector } from 'tgui/backend';
-import {
- Box,
- Button,
- Collapsible,
- ColorBox,
- Divider,
- Input,
- LabeledList,
- NumberInput,
- Section,
- Stack,
- Tabs,
- TextArea,
-} from 'tgui/components';
-
-import { ChatPageSettings } from '../chat';
-import { clearChat, rebuildChat, saveChatToDisk } from '../chat/actions';
-import { THEMES } from '../themes';
-import {
- addHighlightSetting,
- changeSettingsTab,
- removeHighlightSetting,
- updateHighlightSetting,
- updateSettings,
-} from './actions';
-import { FONTS, MAX_HIGHLIGHT_SETTINGS, SETTINGS_TABS } from './constants';
-import {
- selectActiveTab,
- selectHighlightSettingById,
- selectHighlightSettings,
- selectSettings,
-} from './selectors';
-
-export const SettingsPanel = (props) => {
- const activeTab = useSelector(selectActiveTab);
- const dispatch = useDispatch();
- return (
-
-
-
-
- {SETTINGS_TABS.map((tab) => (
-
- dispatch(
- changeSettingsTab({
- tabId: tab.id,
- }),
- )
- }
- >
- {tab.name}
-
- ))}
-
-
-
-
- {activeTab === 'general' && }
- {activeTab === 'chatPage' && }
- {activeTab === 'textHighlight' && }
-
-
- );
-};
-
-export const SettingsGeneral = (props) => {
- const { theme, fontFamily, fontSize, lineHeight } =
- useSelector(selectSettings);
- const dispatch = useDispatch();
- const [freeFont, setFreeFont] = useLocalState('freeFont', false);
- return (
-
-
-
- {THEMES.map((THEME) => (
-
-
-
- {(!freeFont && (
- {
- setFreeFont(!freeFont);
- }}
- />
- }
- >
- {FONTS.map((FONT) => (
-
- )) || (
-
-
- dispatch(
- updateSettings({
- fontFamily: value,
- }),
- )
- }
- />
-
- )}
-
-
-
- toFixed(value)}
- onChange={(value) =>
- dispatch(
- updateSettings({
- fontSize: value,
- }),
- )
- }
- />
-
-
- toFixed(value, 2)}
- onDrag={(value) =>
- dispatch(
- updateSettings({
- lineHeight: value,
- }),
- )
- }
- />
-
-
-
-
-
-
-
- dispatch(clearChat())}
- />
-
-
-
- );
-};
-
-const TextHighlightSettings = (props) => {
- const highlightSettings = useSelector(selectHighlightSettings);
- const dispatch = useDispatch();
- return (
-
-
- {highlightSettings.map((id, i) => (
-
- ))}
- {highlightSettings.length < MAX_HIGHLIGHT_SETTINGS && (
-
-
- )}
-
-
-
-
-
- Can freeze the chat for a while.
-
-
-
- );
-};
-
-const TextHighlightSetting = (props) => {
- const { id, ...rest } = props;
- const highlightSettingById = useSelector(selectHighlightSettingById);
- const dispatch = useDispatch();
- const {
- highlightColor,
- highlightText,
- highlightWholeMessage,
- matchWord,
- matchCase,
- } = highlightSettingById[id];
- return (
-
-
-
-
-
-
- dispatch(
- updateHighlightSetting({
- id: id,
- highlightWholeMessage: !highlightWholeMessage,
- }),
- )
- }
- />
-
-
-
- dispatch(
- updateHighlightSetting({
- id: id,
- matchWord: !matchWord,
- }),
- )
- }
- />
-
-
-
- dispatch(
- updateHighlightSetting({
- id: id,
- matchCase: !matchCase,
- }),
- )
- }
- />
-
-
-
-
- dispatch(
- updateHighlightSetting({
- id: id,
- highlightColor: value,
- }),
- )
- }
- />
-
-
-
- );
-};
diff --git a/tgui/packages/tgui-panel/settings/SettingsPanel.tsx b/tgui/packages/tgui-panel/settings/SettingsPanel.tsx
new file mode 100644
index 00000000000..abb0ef86f63
--- /dev/null
+++ b/tgui/packages/tgui-panel/settings/SettingsPanel.tsx
@@ -0,0 +1,51 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
+import { useDispatch, useSelector } from 'tgui/backend';
+import { Section, Stack, Tabs } from 'tgui/components';
+
+import { ChatPageSettings } from '../chat';
+import { changeSettingsTab } from './actions';
+import { SETTINGS_TABS } from './constants';
+import { selectActiveTab } from './selectors';
+import { SettingsGeneral } from './SettingsGeneral';
+import { TextHighlightSettings } from './TextHighlight';
+
+export function SettingsPanel(props) {
+ const activeTab = useSelector(selectActiveTab);
+ const dispatch = useDispatch();
+
+ return (
+
+
+
+
+ {SETTINGS_TABS.map((tab) => (
+
+ dispatch(
+ changeSettingsTab({
+ tabId: tab.id,
+ }),
+ )
+ }
+ >
+ {tab.name}
+
+ ))}
+
+
+
+
+ {activeTab === 'general' && }
+ {activeTab === 'chatPage' && }
+ {activeTab === 'textHighlight' && }
+
+
+ );
+}
diff --git a/tgui/packages/tgui-panel/settings/TextHighlight.tsx b/tgui/packages/tgui-panel/settings/TextHighlight.tsx
new file mode 100644
index 00000000000..76512cf3770
--- /dev/null
+++ b/tgui/packages/tgui-panel/settings/TextHighlight.tsx
@@ -0,0 +1,178 @@
+import { useDispatch, useSelector } from 'tgui/backend';
+import {
+ Box,
+ Button,
+ ColorBox,
+ Divider,
+ Input,
+ Section,
+ Stack,
+ TextArea,
+} from 'tgui/components';
+
+import { rebuildChat } from '../chat/actions';
+import {
+ addHighlightSetting,
+ removeHighlightSetting,
+ updateHighlightSetting,
+} from './actions';
+import { MAX_HIGHLIGHT_SETTINGS } from './constants';
+import {
+ selectHighlightSettingById,
+ selectHighlightSettings,
+} from './selectors';
+
+export function TextHighlightSettings(props) {
+ const highlightSettings = useSelector(selectHighlightSettings);
+ const dispatch = useDispatch();
+
+ return (
+
+
+ {highlightSettings.map((id, i) => (
+
+ ))}
+ {highlightSettings.length < MAX_HIGHLIGHT_SETTINGS && (
+
+
+
+ )}
+
+
+
+
+
+ Can freeze the chat for a while.
+
+
+
+ );
+}
+
+function TextHighlightSetting(props) {
+ const { id, ...rest } = props;
+ const highlightSettingById = useSelector(selectHighlightSettingById);
+ const dispatch = useDispatch();
+ const {
+ highlightColor,
+ highlightText,
+ highlightWholeMessage,
+ matchWord,
+ matchCase,
+ } = highlightSettingById[id];
+
+ return (
+
+
+
+
+
+
+
+ dispatch(
+ updateHighlightSetting({
+ id: id,
+ highlightWholeMessage: !highlightWholeMessage,
+ }),
+ )
+ }
+ >
+ Whole Message
+
+
+
+
+ dispatch(
+ updateHighlightSetting({
+ id: id,
+ matchWord: !matchWord,
+ }),
+ )
+ }
+ >
+ Exact
+
+
+
+
+ dispatch(
+ updateHighlightSetting({
+ id: id,
+ matchCase: !matchCase,
+ }),
+ )
+ }
+ >
+ Case
+
+
+
+
+
+ dispatch(
+ updateHighlightSetting({
+ id: id,
+ highlightColor: value,
+ }),
+ )
+ }
+ />
+
+
+
+ );
+}
diff --git a/tgui/packages/tgui-panel/settings/middleware.js b/tgui/packages/tgui-panel/settings/middleware.js
deleted file mode 100644
index edb16a51c4c..00000000000
--- a/tgui/packages/tgui-panel/settings/middleware.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/**
- * @file
- * @copyright 2020 Aleksej Komarov
- * @license MIT
- */
-
-import { storage } from 'common/storage';
-
-import { setClientTheme } from '../themes';
-import {
- addHighlightSetting,
- loadSettings,
- removeHighlightSetting,
- updateHighlightSetting,
- updateSettings,
-} from './actions';
-import { FONTS_DISABLED } from './constants';
-import { selectSettings } from './selectors';
-
-let overrideRule = null;
-let overrideFontFamily = null;
-let overrideFontSize = null;
-
-const updateGlobalOverrideRule = () => {
- let fontFamily = '';
-
- if (overrideFontFamily !== null) {
- fontFamily = `font-family: ${overrideFontFamily} !important;`;
- }
-
- const constructedRule = `body * :not(.Icon) {
- ${fontFamily}
- }`;
-
- if (overrideRule === null) {
- overrideRule = document.createElement('style');
- document.querySelector('head').append(overrideRule);
- }
-
- // no other way to force a CSS refresh other than to update its innerText
- overrideRule.innerText = constructedRule;
-
- document.body.style.setProperty('font-size', overrideFontSize);
-};
-
-const setGlobalFontSize = (fontSize) => {
- overrideFontSize = `${fontSize}px`;
-};
-
-const setGlobalFontFamily = (fontFamily) => {
- if (fontFamily === FONTS_DISABLED) fontFamily = null;
- overrideFontFamily = fontFamily;
-};
-
-export const settingsMiddleware = (store) => {
- let initialized = false;
- return (next) => (action) => {
- const { type, payload } = action;
- if (!initialized) {
- initialized = true;
- storage.get('panel-settings').then((settings) => {
- store.dispatch(loadSettings(settings));
- });
- }
- if (
- type === updateSettings.type ||
- type === loadSettings.type ||
- type === addHighlightSetting.type ||
- type === removeHighlightSetting.type ||
- type === updateHighlightSetting.type
- ) {
- // Set client theme
- const theme = payload?.theme;
- if (theme) {
- setClientTheme(theme);
- }
- // Pass action to get an updated state
- next(action);
- const settings = selectSettings(store.getState());
- // Update global UI font size
- setGlobalFontSize(settings.fontSize);
- setGlobalFontFamily(settings.fontFamily);
- updateGlobalOverrideRule();
- // Save settings to the web storage
- storage.set('panel-settings', settings);
- return;
- }
- return next(action);
- };
-};
diff --git a/tgui/packages/tgui-panel/settings/middleware.ts b/tgui/packages/tgui-panel/settings/middleware.ts
new file mode 100644
index 00000000000..01ad8856190
--- /dev/null
+++ b/tgui/packages/tgui-panel/settings/middleware.ts
@@ -0,0 +1,98 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
+import { storage } from 'common/storage';
+
+import { setClientTheme } from '../themes';
+import {
+ addHighlightSetting,
+ loadSettings,
+ removeHighlightSetting,
+ updateHighlightSetting,
+ updateSettings,
+} from './actions';
+import { FONTS_DISABLED } from './constants';
+import { selectSettings } from './selectors';
+
+let overrideRule: HTMLStyleElement;
+let overrideFontFamily: string | undefined;
+let overrideFontSize: string;
+
+/** Updates the global CSS rule to override the font family and size. */
+function updateGlobalOverrideRule() {
+ let fontFamily = '';
+
+ if (overrideFontFamily !== undefined) {
+ fontFamily = `font-family: ${overrideFontFamily} !important;`;
+ }
+
+ const constructedRule = `body * :not(.Icon) {
+ ${fontFamily}
+ }`;
+
+ if (overrideRule === undefined) {
+ overrideRule = document.createElement('style');
+ document.querySelector('head')!.append(overrideRule);
+ }
+
+ // no other way to force a CSS refresh other than to update its innerText
+ overrideRule.innerText = constructedRule;
+
+ document.body.style.setProperty('font-size', overrideFontSize);
+}
+
+function setGlobalFontSize(fontSize: string) {
+ overrideFontSize = `${fontSize}px`;
+}
+
+function setGlobalFontFamily(fontFamily: string) {
+ overrideFontFamily = fontFamily === FONTS_DISABLED ? undefined : fontFamily;
+}
+
+export function settingsMiddleware(store) {
+ let initialized = false;
+
+ return (next) => (action) => {
+ const { type, payload } = action;
+
+ if (!initialized) {
+ initialized = true;
+ storage.get('panel-settings').then((settings) => {
+ store.dispatch(loadSettings(settings));
+ });
+ }
+ if (
+ type !== updateSettings.type &&
+ type !== loadSettings.type &&
+ type !== addHighlightSetting.type &&
+ type !== removeHighlightSetting.type &&
+ type !== updateHighlightSetting.type
+ ) {
+ return next(action);
+ }
+
+ // Set client theme
+ const theme = payload?.theme;
+ if (theme) {
+ setClientTheme(theme);
+ }
+
+ // Pass action to get an updated state
+ next(action);
+
+ const settings = selectSettings(store.getState());
+
+ // Update global UI font size
+ setGlobalFontSize(settings.fontSize);
+ setGlobalFontFamily(settings.fontFamily);
+ updateGlobalOverrideRule();
+
+ // Save settings to the web storage
+ storage.set('panel-settings', settings);
+
+ return;
+ };
+}
diff --git a/tgui/packages/tgui-panel/settings/reducer.js b/tgui/packages/tgui-panel/settings/reducer.js
deleted file mode 100644
index 1240198df8a..00000000000
--- a/tgui/packages/tgui-panel/settings/reducer.js
+++ /dev/null
@@ -1,184 +0,0 @@
-/**
- * @file
- * @copyright 2020 Aleksej Komarov
- * @license MIT
- */
-
-import {
- addHighlightSetting,
- changeSettingsTab,
- loadSettings,
- openChatSettings,
- removeHighlightSetting,
- toggleSettings,
- updateHighlightSetting,
- updateSettings,
-} from './actions';
-import { FONTS, MAX_HIGHLIGHT_SETTINGS, SETTINGS_TABS } from './constants';
-import { createDefaultHighlightSetting } from './model';
-
-const defaultHighlightSetting = createDefaultHighlightSetting();
-
-const initialState = {
- version: 1,
- fontSize: 13,
- fontFamily: FONTS[0],
- lineHeight: 1.2,
- theme: 'light',
- adminMusicVolume: 0.5,
- // Keep these two state vars for compatibility with other servers
- highlightText: '',
- highlightColor: '#ffdd44',
- // END compatibility state vars
- highlightSettings: [defaultHighlightSetting.id],
- highlightSettingById: {
- [defaultHighlightSetting.id]: defaultHighlightSetting,
- },
- view: {
- visible: false,
- activeTab: SETTINGS_TABS[0].id,
- },
-};
-
-export const settingsReducer = (state = initialState, action) => {
- const { type, payload } = action;
- if (type === updateSettings.type) {
- return {
- ...state,
- ...payload,
- };
- }
- if (type === loadSettings.type) {
- // Validate version and/or migrate state
- if (!payload?.version) {
- return state;
- }
-
- delete payload.view;
- const nextState = {
- ...state,
- ...payload,
- };
- // Lazy init the list for compatibility reasons
- if (!nextState.highlightSettings) {
- nextState.highlightSettings = [defaultHighlightSetting.id];
- nextState.highlightSettingById[defaultHighlightSetting.id] =
- defaultHighlightSetting;
- }
- // Compensating for mishandling of default highlight settings
- else if (!nextState.highlightSettingById[defaultHighlightSetting.id]) {
- nextState.highlightSettings = [
- defaultHighlightSetting.id,
- ...nextState.highlightSettings,
- ];
- nextState.highlightSettingById[defaultHighlightSetting.id] =
- defaultHighlightSetting;
- }
- // Update the highlight settings for default highlight
- // settings compatibility
- const highlightSetting =
- nextState.highlightSettingById[defaultHighlightSetting.id];
- highlightSetting.highlightColor = nextState.highlightColor;
- highlightSetting.highlightText = nextState.highlightText;
- return nextState;
- }
- if (type === toggleSettings.type) {
- return {
- ...state,
- view: {
- ...state.view,
- visible: !state.view.visible,
- },
- };
- }
- if (type === openChatSettings.type) {
- return {
- ...state,
- view: {
- ...state.view,
- visible: true,
- activeTab: 'chatPage',
- },
- };
- }
- if (type === changeSettingsTab.type) {
- const { tabId } = payload;
- return {
- ...state,
- view: {
- ...state.view,
- activeTab: tabId,
- },
- };
- }
- if (type === addHighlightSetting.type) {
- const highlightSetting = payload;
- if (state.highlightSettings.length >= MAX_HIGHLIGHT_SETTINGS) {
- return state;
- }
- return {
- ...state,
- highlightSettings: [...state.highlightSettings, highlightSetting.id],
- highlightSettingById: {
- ...state.highlightSettingById,
- [highlightSetting.id]: highlightSetting,
- },
- };
- }
- if (type === removeHighlightSetting.type) {
- const { id } = payload;
- const nextState = {
- ...state,
- highlightSettings: [...state.highlightSettings],
- highlightSettingById: {
- ...state.highlightSettingById,
- },
- };
- if (id === defaultHighlightSetting.id) {
- nextState.highlightSettings[defaultHighlightSetting.id] =
- defaultHighlightSetting;
- } else {
- delete nextState.highlightSettingById[id];
- nextState.highlightSettings = nextState.highlightSettings.filter(
- (sid) => sid !== id,
- );
- if (!nextState.highlightSettings.length) {
- nextState.highlightSettings.push(defaultHighlightSetting.id);
- nextState.highlightSettingById[defaultHighlightSetting.id] =
- defaultHighlightSetting;
- }
- }
- return nextState;
- }
- if (type === updateHighlightSetting.type) {
- const { id, ...settings } = payload;
- const nextState = {
- ...state,
- highlightSettings: [...state.highlightSettings],
- highlightSettingById: {
- ...state.highlightSettingById,
- },
- };
-
- // Transfer this data from the default highlight setting
- // so they carry over to other servers
- if (id === defaultHighlightSetting.id) {
- if (settings.highlightText) {
- nextState.highlightText = settings.highlightText;
- }
- if (settings.highlightColor) {
- nextState.highlightColor = settings.highlightColor;
- }
- }
-
- if (nextState.highlightSettingById[id]) {
- nextState.highlightSettingById[id] = {
- ...nextState.highlightSettingById[id],
- ...settings,
- };
- }
-
- return nextState;
- }
- return state;
-};
diff --git a/tgui/packages/tgui-panel/settings/reducer.ts b/tgui/packages/tgui-panel/settings/reducer.ts
new file mode 100644
index 00000000000..62d08986a14
--- /dev/null
+++ b/tgui/packages/tgui-panel/settings/reducer.ts
@@ -0,0 +1,208 @@
+/**
+ * @file
+ * @copyright 2020 Aleksej Komarov
+ * @license MIT
+ */
+
+import {
+ addHighlightSetting,
+ changeSettingsTab,
+ loadSettings,
+ openChatSettings,
+ removeHighlightSetting,
+ toggleSettings,
+ updateHighlightSetting,
+ updateSettings,
+} from './actions';
+import { FONTS, MAX_HIGHLIGHT_SETTINGS, SETTINGS_TABS } from './constants';
+import { createDefaultHighlightSetting } from './model';
+
+const defaultHighlightSetting = createDefaultHighlightSetting();
+
+const initialState = {
+ version: 1,
+ fontSize: 13,
+ fontFamily: FONTS[0],
+ lineHeight: 1.2,
+ theme: 'light',
+ adminMusicVolume: 0.5,
+ // Keep these two state vars for compatibility with other servers
+ highlightText: '',
+ highlightColor: '#ffdd44',
+ // END compatibility state vars
+ highlightSettings: [defaultHighlightSetting.id],
+ highlightSettingById: {
+ [defaultHighlightSetting.id]: defaultHighlightSetting,
+ },
+ view: {
+ visible: false,
+ activeTab: SETTINGS_TABS[0].id,
+ },
+} as const;
+
+export function settingsReducer(
+ state = initialState,
+ action: { type: string; payload: any },
+) {
+ const { type, payload } = action;
+
+ switch (type) {
+ case updateSettings.type:
+ return {
+ ...state,
+ ...payload,
+ };
+
+ case loadSettings.type: {
+ {
+ // Validate version and/or migrate state
+ if (!payload?.version) {
+ return state;
+ }
+
+ delete payload.view;
+ const nextState = {
+ ...state,
+ ...payload,
+ };
+ // Lazy init the list for compatibility reasons
+ if (!nextState.highlightSettings) {
+ nextState.highlightSettings = [defaultHighlightSetting.id];
+ nextState.highlightSettingById[defaultHighlightSetting.id] =
+ defaultHighlightSetting;
+ }
+ // Compensating for mishandling of default highlight settings
+ else if (!nextState.highlightSettingById[defaultHighlightSetting.id]) {
+ nextState.highlightSettings = [
+ defaultHighlightSetting.id,
+ ...nextState.highlightSettings,
+ ];
+ nextState.highlightSettingById[defaultHighlightSetting.id] =
+ defaultHighlightSetting;
+ }
+
+ // Update the highlight settings for default highlight
+ // settings compatibility
+ const highlightSetting =
+ nextState.highlightSettingById[defaultHighlightSetting.id];
+ highlightSetting.highlightColor = nextState.highlightColor;
+ highlightSetting.highlightText = nextState.highlightText;
+
+ return nextState;
+ }
+ }
+
+ case toggleSettings.type: {
+ return {
+ ...state,
+ view: {
+ ...state.view,
+ visible: !state.view.visible,
+ },
+ };
+ }
+
+ case openChatSettings.type: {
+ return {
+ ...state,
+ view: {
+ ...state.view,
+ visible: true,
+ activeTab: 'chatPage',
+ },
+ };
+ }
+
+ case changeSettingsTab.type: {
+ const { tabId } = payload;
+
+ return {
+ ...state,
+ view: {
+ ...state.view,
+ activeTab: tabId,
+ },
+ };
+ }
+
+ case addHighlightSetting.type: {
+ const highlightSetting = payload;
+
+ if (state.highlightSettings.length >= MAX_HIGHLIGHT_SETTINGS) {
+ return state;
+ }
+
+ return {
+ ...state,
+ highlightSettings: [...state.highlightSettings, highlightSetting.id],
+ highlightSettingById: {
+ ...state.highlightSettingById,
+ [highlightSetting.id]: highlightSetting,
+ },
+ };
+ }
+
+ case removeHighlightSetting.type: {
+ const { id } = payload;
+
+ const nextState = {
+ ...state,
+ highlightSettings: [...state.highlightSettings],
+ highlightSettingById: {
+ ...state.highlightSettingById,
+ },
+ };
+
+ if (id === defaultHighlightSetting.id) {
+ nextState.highlightSettings[defaultHighlightSetting.id] =
+ defaultHighlightSetting;
+ } else {
+ delete nextState.highlightSettingById[id];
+ nextState.highlightSettings = nextState.highlightSettings.filter(
+ (sid) => sid !== id,
+ );
+ if (!nextState.highlightSettings.length) {
+ nextState.highlightSettings.push(defaultHighlightSetting.id);
+ nextState.highlightSettingById[defaultHighlightSetting.id] =
+ defaultHighlightSetting;
+ }
+ }
+
+ return nextState;
+ }
+
+ case updateHighlightSetting.type: {
+ const { id, ...settings } = payload;
+
+ const nextState = {
+ ...state,
+ highlightSettings: [...state.highlightSettings],
+ highlightSettingById: {
+ ...state.highlightSettingById,
+ },
+ };
+
+ // Transfer this data from the default highlight setting
+ // so they carry over to other servers
+ if (id === defaultHighlightSetting.id) {
+ if (settings.highlightText) {
+ nextState.highlightText = settings.highlightText;
+ }
+ if (settings.highlightColor) {
+ nextState.highlightColor = settings.highlightColor;
+ }
+ }
+
+ if (nextState.highlightSettingById[id]) {
+ nextState.highlightSettingById[id] = {
+ ...nextState.highlightSettingById[id],
+ ...settings,
+ };
+ }
+
+ return nextState;
+ }
+ }
+
+ return state;
+}
diff --git a/tgui/packages/tgui/components/DmIcon.tsx b/tgui/packages/tgui/components/DmIcon.tsx
index fb6816576ac..bb785fc6e39 100644
--- a/tgui/packages/tgui/components/DmIcon.tsx
+++ b/tgui/packages/tgui/components/DmIcon.tsx
@@ -29,7 +29,7 @@ type Props = {
/** Frame number. Default is 1 */
frame: number;
/** Movement state. Default is false */
- movement: boolean;
+ movement: any;
}> &
BoxProps;
@@ -49,7 +49,7 @@ export function DmIcon(props: Props) {
const [iconRef, setIconRef] = useState('');
- const query = `${iconRef}?state=${icon_state}&dir=${direction}&movement=${movement}&frame=${frame}`;
+ const query = `${iconRef}?state=${icon_state}&dir=${direction}&movement=${!!movement}&frame=${frame}`;
useEffect(() => {
async function fetchRefMap() {
diff --git a/tgui/packages/tgui/interfaces/AbductorConsole.jsx b/tgui/packages/tgui/interfaces/AbductorConsole.jsx
index e870089a1bc..bb2f1dffccf 100644
--- a/tgui/packages/tgui/interfaces/AbductorConsole.jsx
+++ b/tgui/packages/tgui/interfaces/AbductorConsole.jsx
@@ -1,5 +1,12 @@
+import {
+ Button,
+ LabeledList,
+ NoticeBox,
+ Section,
+ Tabs,
+} from 'tgui-core/components';
+
import { useBackend, useSharedState } from '../backend';
-import { Button, LabeledList, NoticeBox, Section, Tabs } from '../components';
import { Window } from '../layouts';
import { GenericUplink } from './Uplink/GenericUplink';
diff --git a/tgui/packages/tgui/interfaces/AccountingConsole.tsx b/tgui/packages/tgui/interfaces/AccountingConsole.tsx
index 92124bc38ef..726abb78ece 100644
--- a/tgui/packages/tgui/interfaces/AccountingConsole.tsx
+++ b/tgui/packages/tgui/interfaces/AccountingConsole.tsx
@@ -1,4 +1,3 @@
-import { BooleanLike } from 'common/react';
import { useState } from 'react';
import {
BlockQuote,
@@ -8,6 +7,7 @@ import {
Stack,
Tabs,
} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
import { Window } from '../layouts';
diff --git a/tgui/packages/tgui/interfaces/Achievements.jsx b/tgui/packages/tgui/interfaces/Achievements.jsx
index a9b6b914b9e..ccb7c2807f6 100644
--- a/tgui/packages/tgui/interfaces/Achievements.jsx
+++ b/tgui/packages/tgui/interfaces/Achievements.jsx
@@ -1,7 +1,7 @@
import { useState } from 'react';
+import { Box, Flex, Icon, Table, Tabs, Tooltip } from 'tgui-core/components';
import { useBackend } from '../backend';
-import { Box, Flex, Icon, Table, Tabs, Tooltip } from '../components';
import { Window } from '../layouts';
export const Achievements = (props) => {
diff --git a/tgui/packages/tgui/interfaces/AdminFax.jsx b/tgui/packages/tgui/interfaces/AdminFax.jsx
index 3e6026d4ce4..37bdd523c41 100644
--- a/tgui/packages/tgui/interfaces/AdminFax.jsx
+++ b/tgui/packages/tgui/interfaces/AdminFax.jsx
@@ -1,6 +1,4 @@
import { useState } from 'react';
-
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -12,7 +10,9 @@ import {
Section,
TextArea,
Tooltip,
-} from '../components';
+} from 'tgui-core/components';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
export const AdminFax = (props) => {
diff --git a/tgui/packages/tgui/interfaces/AdminPDA.jsx b/tgui/packages/tgui/interfaces/AdminPDA.jsx
index fa83f200e52..da06e7d294d 100644
--- a/tgui/packages/tgui/interfaces/AdminPDA.jsx
+++ b/tgui/packages/tgui/interfaces/AdminPDA.jsx
@@ -1,5 +1,6 @@
+import { Box, Dropdown, Input, Section, TextArea } from 'tgui-core/components';
+
import { useBackend, useLocalState } from '../backend';
-import { Box, Dropdown, Input, Section, TextArea } from '../components';
import { Button } from '../components/Button';
import { Window } from '../layouts';
diff --git a/tgui/packages/tgui/interfaces/Adminhelp.tsx b/tgui/packages/tgui/interfaces/Adminhelp.tsx
index 7e29a807f1d..af1e273b685 100644
--- a/tgui/packages/tgui/interfaces/Adminhelp.tsx
+++ b/tgui/packages/tgui/interfaces/Adminhelp.tsx
@@ -1,8 +1,15 @@
-import { BooleanLike } from 'common/react';
import { useState } from 'react';
+import {
+ Box,
+ Button,
+ Input,
+ NoticeBox,
+ Stack,
+ TextArea,
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Box, Button, Input, NoticeBox, Stack, TextArea } from '../components';
import { Window } from '../layouts';
type AdminhelpData = {
diff --git a/tgui/packages/tgui/interfaces/AdventureBrowser.tsx b/tgui/packages/tgui/interfaces/AdventureBrowser.tsx
index cb43f6f319d..86e05231952 100644
--- a/tgui/packages/tgui/interfaces/AdventureBrowser.tsx
+++ b/tgui/packages/tgui/interfaces/AdventureBrowser.tsx
@@ -1,5 +1,6 @@
+import { Box, Button, NoticeBox, Section, Table } from 'tgui-core/components';
+
import { useBackend, useLocalState } from '../backend';
-import { Box, Button, NoticeBox, Section, Table } from '../components';
import { formatTime } from '../format';
import { Window } from '../layouts';
import { AdventureDataProvider, AdventureScreen } from './ExodroneConsole';
diff --git a/tgui/packages/tgui/interfaces/AiAirlock.jsx b/tgui/packages/tgui/interfaces/AiAirlock.jsx
index b74735d8e6a..bf9cdf4a7ff 100644
--- a/tgui/packages/tgui/interfaces/AiAirlock.jsx
+++ b/tgui/packages/tgui/interfaces/AiAirlock.jsx
@@ -1,5 +1,6 @@
+import { Button, LabeledList, Section } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Button, LabeledList, Section } from '../components';
import { Window } from '../layouts';
const dangerMap = {
diff --git a/tgui/packages/tgui/interfaces/AiRestorer.tsx b/tgui/packages/tgui/interfaces/AiRestorer.tsx
index 0de0224eaab..3fbac445683 100644
--- a/tgui/packages/tgui/interfaces/AiRestorer.tsx
+++ b/tgui/packages/tgui/interfaces/AiRestorer.tsx
@@ -1,6 +1,3 @@
-import { BooleanLike } from 'common/react';
-
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -8,7 +5,10 @@ import {
NoticeBox,
ProgressBar,
Section,
-} from '../components';
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/AiVoiceChanger.tsx b/tgui/packages/tgui/interfaces/AiVoiceChanger.tsx
index 09c1aa04e57..a9bd489c7b0 100644
--- a/tgui/packages/tgui/interfaces/AiVoiceChanger.tsx
+++ b/tgui/packages/tgui/interfaces/AiVoiceChanger.tsx
@@ -1,7 +1,13 @@
-import { BooleanLike } from 'common/react';
+import {
+ Button,
+ Dropdown,
+ Input,
+ LabeledList,
+ Section,
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Button, Dropdown, Input, LabeledList, Section } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/AirAlarm.tsx b/tgui/packages/tgui/interfaces/AirAlarm.tsx
index 448d233d368..f5d439915e9 100644
--- a/tgui/packages/tgui/interfaces/AirAlarm.tsx
+++ b/tgui/packages/tgui/interfaces/AirAlarm.tsx
@@ -1,4 +1,3 @@
-import { BooleanLike } from 'common/react';
import { Fragment } from 'react';
import {
Box,
@@ -10,6 +9,7 @@ import {
Table,
VirtualList,
} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend, useLocalState } from '../backend';
import { Window } from '../layouts';
diff --git a/tgui/packages/tgui/interfaces/AirlockButtonController.tsx b/tgui/packages/tgui/interfaces/AirlockButtonController.tsx
index 98a3f7c9113..83f98497489 100644
--- a/tgui/packages/tgui/interfaces/AirlockButtonController.tsx
+++ b/tgui/packages/tgui/interfaces/AirlockButtonController.tsx
@@ -1,7 +1,7 @@
-import { BooleanLike } from 'common/react';
+import { Button, NoticeBox, Section, Stack } from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Button, NoticeBox, Section, Stack } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/AirlockController.tsx b/tgui/packages/tgui/interfaces/AirlockController.tsx
index ca3551c3415..81a10f75bc5 100644
--- a/tgui/packages/tgui/interfaces/AirlockController.tsx
+++ b/tgui/packages/tgui/interfaces/AirlockController.tsx
@@ -1,5 +1,6 @@
+import { Box, Button, Icon, LabeledList, Section } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Box, Button, Icon, LabeledList, Section } from '../components';
import { Window } from '../layouts';
type AirlockControllerData = {
diff --git a/tgui/packages/tgui/interfaces/AirlockElectronics.tsx b/tgui/packages/tgui/interfaces/AirlockElectronics.tsx
index b90e8ac3b57..2a0e535ac88 100644
--- a/tgui/packages/tgui/interfaces/AirlockElectronics.tsx
+++ b/tgui/packages/tgui/interfaces/AirlockElectronics.tsx
@@ -1,7 +1,13 @@
-import { BooleanLike } from 'common/react';
+import {
+ Button,
+ Input,
+ LabeledList,
+ Section,
+ Stack,
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Button, Input, LabeledList, Section, Stack } from '../components';
import { Window } from '../layouts';
import { AccessConfig, Region } from './common/AccessConfig';
diff --git a/tgui/packages/tgui/interfaces/AlertModal.tsx b/tgui/packages/tgui/interfaces/AlertModal.tsx
index 5924dc7ae7c..8341d5dbd66 100644
--- a/tgui/packages/tgui/interfaces/AlertModal.tsx
+++ b/tgui/packages/tgui/interfaces/AlertModal.tsx
@@ -1,9 +1,9 @@
-import { isEscape, KEY } from 'common/keys';
-import { BooleanLike } from 'common/react';
import { KeyboardEvent, useState } from 'react';
+import { Autofocus, Box, Button, Section, Stack } from 'tgui-core/components';
+import { isEscape, KEY } from 'tgui-core/keys';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Autofocus, Box, Button, Section, Stack } from '../components';
import { Window } from '../layouts';
import { Loader } from './common/Loader';
diff --git a/tgui/packages/tgui/interfaces/AnomalyRefinery.jsx b/tgui/packages/tgui/interfaces/AnomalyRefinery.jsx
index 89f95e28c4a..870bb5c2646 100644
--- a/tgui/packages/tgui/interfaces/AnomalyRefinery.jsx
+++ b/tgui/packages/tgui/interfaces/AnomalyRefinery.jsx
@@ -1,4 +1,3 @@
-import { useBackend, useSharedState } from '../backend';
import {
Box,
Button,
@@ -7,7 +6,9 @@ import {
Modal,
Section,
Stack,
-} from '../components';
+} from 'tgui-core/components';
+
+import { useBackend, useSharedState } from '../backend';
import { Window } from '../layouts';
import { GasmixParser } from './common/GasmixParser';
diff --git a/tgui/packages/tgui/interfaces/AntagInfoBlob.tsx b/tgui/packages/tgui/interfaces/AntagInfoBlob.tsx
index 058c2fe2fbf..0fdb6016a77 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoBlob.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoBlob.tsx
@@ -1,4 +1,3 @@
-import { useBackend } from '../backend';
import {
Box,
Collapsible,
@@ -6,7 +5,9 @@ import {
LabeledList,
Section,
Stack,
-} from '../components';
+} from 'tgui-core/components';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
import { Rules } from './AntagInfoRules'; // SKYRAT EDIT ADDITION
import { Objective } from './common/Objectives';
diff --git a/tgui/packages/tgui/interfaces/AntagInfoBrainwashed.tsx b/tgui/packages/tgui/interfaces/AntagInfoBrainwashed.tsx
index a506171504a..dfa05895bd0 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoBrainwashed.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoBrainwashed.tsx
@@ -1,5 +1,6 @@
+import { Box, Icon, Section, Stack } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Box, Icon, Section, Stack } from '../components';
import { Window } from '../layouts';
import { Objective, ObjectivePrintout } from './common/Objectives';
diff --git a/tgui/packages/tgui/interfaces/AntagInfoBrother.tsx b/tgui/packages/tgui/interfaces/AntagInfoBrother.tsx
index 294fc101135..731e24e485f 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoBrother.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoBrother.tsx
@@ -1,5 +1,6 @@
+import { Section, Stack } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Section, Stack } from '../components';
import { Window } from '../layouts';
import { Objective, ObjectivePrintout } from './common/Objectives';
diff --git a/tgui/packages/tgui/interfaces/AntagInfoChangeling.tsx b/tgui/packages/tgui/interfaces/AntagInfoChangeling.tsx
index 3d313582f3a..48fecbbc2ab 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoChangeling.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoChangeling.tsx
@@ -1,7 +1,4 @@
-import { BooleanLike } from 'common/react';
import { useState } from 'react';
-
-import { useBackend } from '../backend';
import {
Button,
Dimmer,
@@ -9,7 +6,10 @@ import {
NoticeBox,
Section,
Stack,
-} from '../components';
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
import { Rules } from './AntagInfoRules'; // SKYRAT EDIT ADDITION
import {
diff --git a/tgui/packages/tgui/interfaces/AntagInfoDemon.tsx b/tgui/packages/tgui/interfaces/AntagInfoDemon.tsx
index 1dceb6a60a6..aaa02521167 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoDemon.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoDemon.tsx
@@ -1,7 +1,7 @@
-import { BooleanLike } from 'common/react';
+import { Box, Section, Stack } from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Box, Section, Stack } from '../components';
import { Window } from '../layouts';
import { Objective, ObjectivePrintout } from './common/Objectives';
diff --git a/tgui/packages/tgui/interfaces/AntagInfoGeneric.tsx b/tgui/packages/tgui/interfaces/AntagInfoGeneric.tsx
index ff6eb51c913..b540b42552a 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoGeneric.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoGeneric.tsx
@@ -1,5 +1,6 @@
+import { Section, Stack } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Section, Stack } from '../components';
import { Window } from '../layouts';
import { Rules } from './AntagInfoRules'; // SKYRAT EDIT ADDITION
import { Objective, ObjectivePrintout } from './common/Objectives';
diff --git a/tgui/packages/tgui/interfaces/AntagInfoGlitch.tsx b/tgui/packages/tgui/interfaces/AntagInfoGlitch.tsx
index 0fd7d18a47c..469215a7f45 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoGlitch.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoGlitch.tsx
@@ -1,5 +1,6 @@
+import { Divider, Section, Stack } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Divider, Section, Stack } from '../components';
import { Window } from '../layouts';
import { Objective } from './common/Objectives';
diff --git a/tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx b/tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx
index ebdea394748..389fecb5770 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx
@@ -1,9 +1,16 @@
-import { BooleanLike } from 'common/react';
import { useState } from 'react';
+import {
+ Box,
+ Button,
+ DmIcon,
+ Flex,
+ Section,
+ Stack,
+ Tabs,
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { BlockQuote, Box, Button, Section, Stack, Tabs } from '../components';
-import { CssColor } from '../constants';
import { Window } from '../layouts';
import { Rules } from './AntagInfoRules'; // SKYRAT EDIT ADDITION
import {
@@ -36,20 +43,33 @@ const hereticYellow = {
color: 'yellow',
};
+type IconParams = {
+ icon: string;
+ state: string;
+ frame: number;
+ dir: number;
+ moving: BooleanLike;
+};
+
type Knowledge = {
path: string;
+ icon_params: IconParams;
name: string;
desc: string;
gainFlavor: string;
cost: number;
- disabled: boolean;
- hereticPath: string;
- color: CssColor;
+ bgr: string;
+ disabled: BooleanLike;
+ finished: BooleanLike;
+ ascension: BooleanLike;
};
type KnowledgeInfo = {
- learnableKnowledge: Knowledge[];
- learnedKnowledge: Knowledge[];
+ knowledge_tiers: KnowledgeTier[];
+};
+
+type KnowledgeTier = {
+ nodes: Knowledge[];
};
type Info = {
@@ -227,63 +247,96 @@ const InformationSection = (props) => {
);
};
-const ResearchedKnowledge = (props) => {
- const { data } = useBackend();
- const { learnedKnowledge } = data;
+const KnowledgeTree = (props) => {
+ const { data, act } = useBackend();
+ const { knowledge_tiers } = data;
return (
-
-
-
- {(!learnedKnowledge.length && 'None!') ||
- learnedKnowledge.map((learned) => (
-
-
+
+
+ DAWN
+
+
+ {knowledge_tiers.length === 0
+ ? 'None!'
+ : knowledge_tiers.map((tier, i) => (
+
+
+ {tier.nodes.map((node) => (
+
+
+ {!!node.ascension && (
+
+ DUSK
+
+ )}
+
+ ))}
+
+
))}
-
-
-
- );
-};
-
-const KnowledgeShop = (props) => {
- const { data, act } = useBackend();
- const { learnableKnowledge } = data;
-
- return (
-
-
- {(!learnableKnowledge.length && 'None!') ||
- learnableKnowledge.map((toLearn) => (
-
-
- ))}
-
-
+
+
);
};
@@ -292,23 +345,16 @@ const ResearchInfo = (props) => {
const { charges } = data;
return (
-
+
+
+ You have {charges || 0}
+
+ knowledge point{charges !== 1 ? 's' : ''}
+ {' '}
+ to spend.
+
-
-
- You have {charges || 0}
-
- knowledge point{charges !== 1 ? 's' : ''}
- {' '}
- to spend.
-
-
-
-
-
-
-
-
+
);
diff --git a/tgui/packages/tgui/interfaces/AntagInfoMalf.tsx b/tgui/packages/tgui/interfaces/AntagInfoMalf.tsx
index 8036a5f7f9f..7b799e96552 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoMalf.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoMalf.tsx
@@ -1,8 +1,8 @@
-import { BooleanLike } from 'common/react';
import { useState } from 'react';
+import { BlockQuote, Button, Section, Stack, Tabs } from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { BlockQuote, Button, Section, Stack, Tabs } from '../components';
import { Window } from '../layouts';
import { Rules } from './AntagInfoRules'; // SKYRAT EDIT ADDITION
import {
diff --git a/tgui/packages/tgui/interfaces/AntagInfoMorph.tsx b/tgui/packages/tgui/interfaces/AntagInfoMorph.tsx
index 1a1321c6c28..97782ffab9c 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoMorph.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoMorph.tsx
@@ -1,4 +1,5 @@
-import { BlockQuote, Stack } from '../components';
+import { BlockQuote, Stack } from 'tgui-core/components';
+
import { Window } from '../layouts';
import { Rules } from './AntagInfoRules'; // SKYRAT EDIT ADDITION
diff --git a/tgui/packages/tgui/interfaces/AntagInfoNightmare.tsx b/tgui/packages/tgui/interfaces/AntagInfoNightmare.tsx
index 75c14be8439..a1243ff5fe6 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoNightmare.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoNightmare.tsx
@@ -1,4 +1,5 @@
-import { BlockQuote, LabeledList, Section, Stack } from '../components';
+import { BlockQuote, LabeledList, Section, Stack } from 'tgui-core/components';
+
import { Window } from '../layouts';
import { Rules } from './AntagInfoRules'; // SKYRAT EDIT ADDITION
diff --git a/tgui/packages/tgui/interfaces/AntagInfoNinja.tsx b/tgui/packages/tgui/interfaces/AntagInfoNinja.tsx
index b8a7ca96987..fd2a2d921ce 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoNinja.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoNinja.tsx
@@ -1,7 +1,7 @@
-import { BooleanLike } from 'common/react';
+import { Icon, Section, Stack } from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Icon, Section, Stack } from '../components';
import { Window } from '../layouts';
import { Rules } from './AntagInfoRules'; // SKYRAT EDIT ADDITION
import {
diff --git a/tgui/packages/tgui/interfaces/AntagInfoSentient.jsx b/tgui/packages/tgui/interfaces/AntagInfoSentient.jsx
index d3d939b50c6..5d3c901c9fc 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoSentient.jsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoSentient.jsx
@@ -1,5 +1,6 @@
+import { BlockQuote, Section, Stack } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { BlockQuote, Section, Stack } from '../components';
import { Window } from '../layouts';
export const AntagInfoSentient = (props) => {
diff --git a/tgui/packages/tgui/interfaces/AntagInfoSeparatist.tsx b/tgui/packages/tgui/interfaces/AntagInfoSeparatist.tsx
index 1a14c6203c6..2999024659d 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoSeparatist.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoSeparatist.tsx
@@ -1,5 +1,6 @@
+import { Icon, Section, Stack } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Icon, Section, Stack } from '../components';
import { Window } from '../layouts';
import { Objective, ObjectivePrintout } from './common/Objectives';
diff --git a/tgui/packages/tgui/interfaces/AntagInfoShade.jsx b/tgui/packages/tgui/interfaces/AntagInfoShade.jsx
index 4a44a323f7d..3929d0ca68a 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoShade.jsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoShade.jsx
@@ -1,5 +1,6 @@
+import { Icon, Section, Stack } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Icon, Section, Stack } from '../components';
import { Window } from '../layouts';
export const AntagInfoShade = (props) => {
diff --git a/tgui/packages/tgui/interfaces/AntagInfoSpy.tsx b/tgui/packages/tgui/interfaces/AntagInfoSpy.tsx
index a26266bceb4..98ed0ae72d2 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoSpy.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoSpy.tsx
@@ -1,5 +1,6 @@
+import { Section, Stack } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Section, Stack } from '../components';
import { Window } from '../layouts';
import { Objective, ObjectivePrintout } from './common/Objectives';
diff --git a/tgui/packages/tgui/interfaces/AntagInfoTraitor.tsx b/tgui/packages/tgui/interfaces/AntagInfoTraitor.tsx
index b96c7b0b5e3..0c1f1d1b7c2 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoTraitor.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoTraitor.tsx
@@ -1,7 +1,13 @@
-import { BooleanLike } from 'common/react';
+import {
+ BlockQuote,
+ Button,
+ Dimmer,
+ Section,
+ Stack,
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { BlockQuote, Button, Dimmer, Section, Stack } from '../components';
import { Window } from '../layouts';
import { Rules } from './AntagInfoRules'; // SKYRAT EDIT ADDITION
import { Objective, ObjectivePrintout } from './common/Objectives';
diff --git a/tgui/packages/tgui/interfaces/AntagInfoWizard.tsx b/tgui/packages/tgui/interfaces/AntagInfoWizard.tsx
index 1eee2fd37c7..e316de69c13 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoWizard.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoWizard.tsx
@@ -1,7 +1,7 @@
-import { BooleanLike } from 'common/react';
+import { Box, Section, Stack } from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Box, Section, Stack } from '../components';
import { Window } from '../layouts';
import { Rules } from './AntagInfoRules'; // SKYRAT EDIT ADDITION
import {
diff --git a/tgui/packages/tgui/interfaces/Apc.jsx b/tgui/packages/tgui/interfaces/Apc.jsx
index 13a8984f9d4..e4f3ebe94b9 100644
--- a/tgui/packages/tgui/interfaces/Apc.jsx
+++ b/tgui/packages/tgui/interfaces/Apc.jsx
@@ -1,4 +1,3 @@
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -6,7 +5,9 @@ import {
NoticeBox,
ProgressBar,
Section,
-} from '../components';
+} from 'tgui-core/components';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox';
diff --git a/tgui/packages/tgui/interfaces/ApcControl.jsx b/tgui/packages/tgui/interfaces/ApcControl.jsx
index fb29c78032d..caeb5ee1bed 100644
--- a/tgui/packages/tgui/interfaces/ApcControl.jsx
+++ b/tgui/packages/tgui/interfaces/ApcControl.jsx
@@ -1,8 +1,6 @@
import { map, sortBy } from 'common/collections';
import { flow } from 'common/fp';
import { useState } from 'react';
-
-import { useBackend, useLocalState } from '../backend';
import {
Box,
Button,
@@ -12,7 +10,9 @@ import {
Stack,
Table,
Tabs,
-} from '../components';
+} from 'tgui-core/components';
+
+import { useBackend, useLocalState } from '../backend';
import { Window } from '../layouts';
import { AreaCharge, powerRank } from './PowerMonitor';
diff --git a/tgui/packages/tgui/interfaces/ApprenticeContract.tsx b/tgui/packages/tgui/interfaces/ApprenticeContract.tsx
index b61f9ea5984..ca89a36e1d3 100644
--- a/tgui/packages/tgui/interfaces/ApprenticeContract.tsx
+++ b/tgui/packages/tgui/interfaces/ApprenticeContract.tsx
@@ -1,6 +1,14 @@
+import {
+ BlockQuote,
+ Box,
+ Button,
+ Icon,
+ Section,
+ Stack,
+} from 'tgui-core/components';
+
import { resolveAsset } from '../assets';
import { useBackend } from '../backend';
-import { BlockQuote, Box, Button, Icon, Section, Stack } from '../components';
import { Window } from '../layouts';
export const ApprenticeContract = (props) => {
diff --git a/tgui/packages/tgui/interfaces/Aquarium.tsx b/tgui/packages/tgui/interfaces/Aquarium.tsx
index 259844d58a8..caa0c65d75f 100644
--- a/tgui/packages/tgui/interfaces/Aquarium.tsx
+++ b/tgui/packages/tgui/interfaces/Aquarium.tsx
@@ -1,6 +1,3 @@
-import { BooleanLike } from 'common/react';
-
-import { useBackend } from '../backend';
import {
Button,
Flex,
@@ -8,7 +5,10 @@ import {
LabeledControls,
NumberInput,
Section,
-} from '../components';
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/AtmosAlertConsole.tsx b/tgui/packages/tgui/interfaces/AtmosAlertConsole.tsx
index 1202c5d73bd..e27ddaa1396 100644
--- a/tgui/packages/tgui/interfaces/AtmosAlertConsole.tsx
+++ b/tgui/packages/tgui/interfaces/AtmosAlertConsole.tsx
@@ -1,5 +1,6 @@
+import { Button, Section } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Button, Section } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/AtmosControlConsole.tsx b/tgui/packages/tgui/interfaces/AtmosControlConsole.tsx
index 52852e908e2..0fb88d386f9 100644
--- a/tgui/packages/tgui/interfaces/AtmosControlConsole.tsx
+++ b/tgui/packages/tgui/interfaces/AtmosControlConsole.tsx
@@ -1,6 +1,4 @@
import { useState } from 'react';
-
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -9,7 +7,9 @@ import {
NumberInput,
Section,
Stack,
-} from '../components';
+} from 'tgui-core/components';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
import {
AtmosHandbookContent,
diff --git a/tgui/packages/tgui/interfaces/AtmosControlPanel.jsx b/tgui/packages/tgui/interfaces/AtmosControlPanel.jsx
index c39778c410f..36d0b40e0eb 100644
--- a/tgui/packages/tgui/interfaces/AtmosControlPanel.jsx
+++ b/tgui/packages/tgui/interfaces/AtmosControlPanel.jsx
@@ -1,7 +1,7 @@
import { map, sortBy } from 'common/collections';
+import { Box, Button, Flex, Section, Table } from 'tgui-core/components';
import { useBackend } from '../backend';
-import { Box, Button, Flex, Section, Table } from '../components';
import { Window } from '../layouts';
export const AtmosControlPanel = (props) => {
diff --git a/tgui/packages/tgui/interfaces/AtmosFilter.tsx b/tgui/packages/tgui/interfaces/AtmosFilter.tsx
index e99c09c6695..1a4134e615d 100644
--- a/tgui/packages/tgui/interfaces/AtmosFilter.tsx
+++ b/tgui/packages/tgui/interfaces/AtmosFilter.tsx
@@ -1,7 +1,12 @@
-import { BooleanLike } from 'common/react';
+import {
+ Button,
+ LabeledList,
+ NumberInput,
+ Section,
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Button, LabeledList, NumberInput, Section } from '../components';
import { getGasLabel } from '../constants';
import { Window } from '../layouts';
diff --git a/tgui/packages/tgui/interfaces/AtmosMixer.tsx b/tgui/packages/tgui/interfaces/AtmosMixer.tsx
index 5f1578a69e7..87643c4dd59 100644
--- a/tgui/packages/tgui/interfaces/AtmosMixer.tsx
+++ b/tgui/packages/tgui/interfaces/AtmosMixer.tsx
@@ -1,7 +1,12 @@
-import { BooleanLike } from 'common/react';
+import {
+ Button,
+ LabeledList,
+ NumberInput,
+ Section,
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Button, LabeledList, NumberInput, Section } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/AtmosPump.tsx b/tgui/packages/tgui/interfaces/AtmosPump.tsx
index 002af582fb5..5cdd8764a6c 100644
--- a/tgui/packages/tgui/interfaces/AtmosPump.tsx
+++ b/tgui/packages/tgui/interfaces/AtmosPump.tsx
@@ -1,7 +1,12 @@
-import { BooleanLike } from 'common/react';
+import {
+ Button,
+ LabeledList,
+ NumberInput,
+ Section,
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Button, LabeledList, NumberInput, Section } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/AtmosTempGate.tsx b/tgui/packages/tgui/interfaces/AtmosTempGate.tsx
index 11c1356b7cc..049adbdc49a 100644
--- a/tgui/packages/tgui/interfaces/AtmosTempGate.tsx
+++ b/tgui/packages/tgui/interfaces/AtmosTempGate.tsx
@@ -1,7 +1,12 @@
-import { BooleanLike } from 'common/react';
+import {
+ Button,
+ LabeledList,
+ NumberInput,
+ Section,
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Button, LabeledList, NumberInput, Section } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/AtmosTempPump.tsx b/tgui/packages/tgui/interfaces/AtmosTempPump.tsx
index 48d7234fd87..f398f9969cc 100644
--- a/tgui/packages/tgui/interfaces/AtmosTempPump.tsx
+++ b/tgui/packages/tgui/interfaces/AtmosTempPump.tsx
@@ -1,7 +1,12 @@
-import { BooleanLike } from 'common/react';
+import {
+ Button,
+ LabeledList,
+ NumberInput,
+ Section,
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Button, LabeledList, NumberInput, Section } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/Autolathe.tsx b/tgui/packages/tgui/interfaces/Autolathe.tsx
index 67f707476fe..26b52540726 100644
--- a/tgui/packages/tgui/interfaces/Autolathe.tsx
+++ b/tgui/packages/tgui/interfaces/Autolathe.tsx
@@ -1,5 +1,3 @@
-import { BooleanLike, classes } from 'common/react';
-import { capitalize } from 'common/string';
import {
Box,
Button,
@@ -11,6 +9,8 @@ import {
Stack,
Tooltip,
} from 'tgui-core/components';
+import { BooleanLike, classes } from 'tgui-core/react';
+import { capitalize } from 'tgui-core/string';
import { useBackend } from '../backend';
import { Window } from '../layouts';
diff --git a/tgui/packages/tgui/interfaces/AutomatedAnnouncement.tsx b/tgui/packages/tgui/interfaces/AutomatedAnnouncement.tsx
index 6446ac5f6de..f08ee83fd4f 100644
--- a/tgui/packages/tgui/interfaces/AutomatedAnnouncement.tsx
+++ b/tgui/packages/tgui/interfaces/AutomatedAnnouncement.tsx
@@ -1,7 +1,7 @@
-import { BooleanLike } from 'common/react';
+import { Button, Input, LabeledList, Section } from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Button, Input, LabeledList, Section } from '../components';
import { Window } from '../layouts';
const TOOLTIP_TEXT = `
diff --git a/tgui/packages/tgui/interfaces/AuxBaseConsole.tsx b/tgui/packages/tgui/interfaces/AuxBaseConsole.tsx
index c1b966674a7..baae00676f5 100644
--- a/tgui/packages/tgui/interfaces/AuxBaseConsole.tsx
+++ b/tgui/packages/tgui/interfaces/AuxBaseConsole.tsx
@@ -1,8 +1,8 @@
-import { BooleanLike } from 'common/react';
import { useState } from 'react';
+import { Button, NoticeBox, Section, Table, Tabs } from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Button, NoticeBox, Section, Table, Tabs } from '../components';
import { Window } from '../layouts';
import { ShuttleConsoleContent } from './ShuttleConsole';
diff --git a/tgui/packages/tgui/interfaces/AvatarHelp.tsx b/tgui/packages/tgui/interfaces/AvatarHelp.tsx
index d25beb6aa36..0a57d9cc8f0 100644
--- a/tgui/packages/tgui/interfaces/AvatarHelp.tsx
+++ b/tgui/packages/tgui/interfaces/AvatarHelp.tsx
@@ -1,5 +1,6 @@
+import { Box, Icon, Section, Stack } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Box, Icon, Section, Stack } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/BankMachine.tsx b/tgui/packages/tgui/interfaces/BankMachine.tsx
index c45cc424fa6..005b94da0aa 100644
--- a/tgui/packages/tgui/interfaces/BankMachine.tsx
+++ b/tgui/packages/tgui/interfaces/BankMachine.tsx
@@ -1,13 +1,13 @@
-import { BooleanLike } from 'common/react';
-
-import { useBackend } from '../backend';
import {
AnimatedNumber,
Button,
LabeledList,
NoticeBox,
Section,
-} from '../components';
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
import { formatMoney } from '../format';
import { Window } from '../layouts';
diff --git a/tgui/packages/tgui/interfaces/BasketballPanel.tsx b/tgui/packages/tgui/interfaces/BasketballPanel.tsx
index 6de429e9234..d5d2865a81a 100644
--- a/tgui/packages/tgui/interfaces/BasketballPanel.tsx
+++ b/tgui/packages/tgui/interfaces/BasketballPanel.tsx
@@ -1,5 +1,6 @@
+import { Button, NoticeBox, Section, Stack } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Button, NoticeBox, Section, Stack } from '../components';
import { Window } from '../layouts';
type BasketballPanelData = {
diff --git a/tgui/packages/tgui/interfaces/BattleArcade.tsx b/tgui/packages/tgui/interfaces/BattleArcade.tsx
index a400628575f..d455bfe8e52 100644
--- a/tgui/packages/tgui/interfaces/BattleArcade.tsx
+++ b/tgui/packages/tgui/interfaces/BattleArcade.tsx
@@ -1,6 +1,7 @@
+import { Box, Button, Image, Section } from 'tgui-core/components';
+
import { resolveAsset } from '../assets';
import { useBackend } from '../backend';
-import { Box, Button, Image, Section } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/Biogenerator.tsx b/tgui/packages/tgui/interfaces/Biogenerator.tsx
index 24597404396..f87eb73d903 100644
--- a/tgui/packages/tgui/interfaces/Biogenerator.tsx
+++ b/tgui/packages/tgui/interfaces/Biogenerator.tsx
@@ -1,8 +1,4 @@
-import { BooleanLike } from 'common/react';
-import { classes } from 'common/react';
import { useState } from 'react';
-
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -15,7 +11,11 @@ import {
Stack,
Table,
Tabs,
-} from '../components';
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
+import { classes } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/BlackMarketUplink.tsx b/tgui/packages/tgui/interfaces/BlackMarketUplink.tsx
index 0f2915f5a0d..51d397d500c 100644
--- a/tgui/packages/tgui/interfaces/BlackMarketUplink.tsx
+++ b/tgui/packages/tgui/interfaces/BlackMarketUplink.tsx
@@ -1,4 +1,3 @@
-import { useBackend } from '../backend';
import {
AnimatedNumber,
Box,
@@ -7,7 +6,9 @@ import {
Section,
Stack,
Tabs,
-} from '../components';
+} from 'tgui-core/components';
+
+import { useBackend } from '../backend';
import { formatMoney } from '../format';
import { Window } from '../layouts';
diff --git a/tgui/packages/tgui/interfaces/BloodFilter.tsx b/tgui/packages/tgui/interfaces/BloodFilter.tsx
index 4e1d553edfd..57ac2794287 100644
--- a/tgui/packages/tgui/interfaces/BloodFilter.tsx
+++ b/tgui/packages/tgui/interfaces/BloodFilter.tsx
@@ -1,5 +1,6 @@
+import { Stack } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Stack } from '../components';
import { Window } from '../layouts';
import { ChemFilterPane } from './ChemFilter';
diff --git a/tgui/packages/tgui/interfaces/Blueprints.tsx b/tgui/packages/tgui/interfaces/Blueprints.tsx
index 644412a9a77..2a2369188c4 100644
--- a/tgui/packages/tgui/interfaces/Blueprints.tsx
+++ b/tgui/packages/tgui/interfaces/Blueprints.tsx
@@ -1,7 +1,7 @@
-import { BooleanLike } from 'common/react';
+import { Box, Button, Divider, Section, Stack } from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Box, Button, Divider, Section, Stack } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/BluespaceArtillery.tsx b/tgui/packages/tgui/interfaces/BluespaceArtillery.tsx
index dbaf0caa0b2..004407ed30c 100644
--- a/tgui/packages/tgui/interfaces/BluespaceArtillery.tsx
+++ b/tgui/packages/tgui/interfaces/BluespaceArtillery.tsx
@@ -1,7 +1,13 @@
-import { BooleanLike } from 'common/react';
+import {
+ Box,
+ Button,
+ LabeledList,
+ NoticeBox,
+ Section,
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Box, Button, LabeledList, NoticeBox, Section } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/BluespaceLocator.tsx b/tgui/packages/tgui/interfaces/BluespaceLocator.tsx
index 9b08accd341..5d8f177b4ad 100644
--- a/tgui/packages/tgui/interfaces/BluespaceLocator.tsx
+++ b/tgui/packages/tgui/interfaces/BluespaceLocator.tsx
@@ -1,7 +1,7 @@
import { useState } from 'react';
+import { Icon, ProgressBar, Tabs } from 'tgui-core/components';
import { useBackend } from '../backend';
-import { Icon, ProgressBar, Tabs } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/BluespaceSender.tsx b/tgui/packages/tgui/interfaces/BluespaceSender.tsx
index 2f94af4cfaa..823090eeb4e 100644
--- a/tgui/packages/tgui/interfaces/BluespaceSender.tsx
+++ b/tgui/packages/tgui/interfaces/BluespaceSender.tsx
@@ -1,8 +1,4 @@
import { filter, sortBy } from 'common/collections';
-import { toFixed } from 'common/math';
-import { BooleanLike } from 'common/react';
-
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -12,7 +8,11 @@ import {
ProgressBar,
Section,
Stack,
-} from '../components';
+} from 'tgui-core/components';
+import { toFixed } from 'tgui-core/math';
+import { BooleanLike } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
import { getGasColor } from '../constants';
import { Window } from '../layouts';
diff --git a/tgui/packages/tgui/interfaces/BluespaceVendor.tsx b/tgui/packages/tgui/interfaces/BluespaceVendor.tsx
index 7b9619de26e..76f6594e455 100644
--- a/tgui/packages/tgui/interfaces/BluespaceVendor.tsx
+++ b/tgui/packages/tgui/interfaces/BluespaceVendor.tsx
@@ -1,15 +1,15 @@
import { filter, sortBy } from 'common/collections';
-import { toFixed } from 'common/math';
-import { BooleanLike } from 'common/react';
-
-import { useBackend } from '../backend';
import {
Button,
NumberInput,
ProgressBar,
Section,
Stack,
-} from '../components';
+} from 'tgui-core/components';
+import { toFixed } from 'tgui-core/math';
+import { BooleanLike } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
import { Table, TableCell, TableRow } from '../components/Table';
import { getGasColor } from '../constants';
import { Window } from '../layouts';
diff --git a/tgui/packages/tgui/interfaces/BorgHypo.tsx b/tgui/packages/tgui/interfaces/BorgHypo.tsx
index 8fa8d2c8131..bafa2fc302c 100644
--- a/tgui/packages/tgui/interfaces/BorgHypo.tsx
+++ b/tgui/packages/tgui/interfaces/BorgHypo.tsx
@@ -1,7 +1,13 @@
-import { toFixed } from 'common/math';
+import {
+ Button,
+ Flex,
+ NoticeBox,
+ ProgressBar,
+ Section,
+} from 'tgui-core/components';
+import { toFixed } from 'tgui-core/math';
import { useBackend } from '../backend';
-import { Button, Flex, NoticeBox, ProgressBar, Section } from '../components';
import { Window } from '../layouts';
type BorgHypoContext = {
diff --git a/tgui/packages/tgui/interfaces/BorgPanel.jsx b/tgui/packages/tgui/interfaces/BorgPanel.jsx
index 96b536af0aa..b9ef7ed45c5 100644
--- a/tgui/packages/tgui/interfaces/BorgPanel.jsx
+++ b/tgui/packages/tgui/interfaces/BorgPanel.jsx
@@ -1,5 +1,12 @@
+import {
+ Box,
+ Button,
+ LabeledList,
+ ProgressBar,
+ Section,
+} from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Box, Button, LabeledList, ProgressBar, Section } from '../components';
import { Window } from '../layouts';
export const BorgPanel = (props) => {
diff --git a/tgui/packages/tgui/interfaces/BorgShaker.tsx b/tgui/packages/tgui/interfaces/BorgShaker.tsx
index c77bf32ea8c..b16a68a3918 100644
--- a/tgui/packages/tgui/interfaces/BorgShaker.tsx
+++ b/tgui/packages/tgui/interfaces/BorgShaker.tsx
@@ -1,5 +1,6 @@
+import { Button, NoticeBox, Section } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Button, NoticeBox, Section } from '../components';
import { Window } from '../layouts';
type BorgShakerContext = {
diff --git a/tgui/packages/tgui/interfaces/BotAnnouncement.tsx b/tgui/packages/tgui/interfaces/BotAnnouncement.tsx
index 3292e81a0e7..4064df6ffd6 100644
--- a/tgui/packages/tgui/interfaces/BotAnnouncement.tsx
+++ b/tgui/packages/tgui/interfaces/BotAnnouncement.tsx
@@ -1,7 +1,4 @@
-import { createSearch } from 'common/string';
import { useState } from 'react';
-
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -11,7 +8,10 @@ import {
Section,
Stack,
Tabs,
-} from '../components';
+} from 'tgui-core/components';
+import { createSearch } from 'tgui-core/string';
+
+import { useBackend } from '../backend';
import { RADIO_CHANNELS } from '../constants';
import { Window } from '../layouts';
diff --git a/tgui/packages/tgui/interfaces/BountyBoard.tsx b/tgui/packages/tgui/interfaces/BountyBoard.tsx
index 2f271bfb4f9..9b892b3ca70 100644
--- a/tgui/packages/tgui/interfaces/BountyBoard.tsx
+++ b/tgui/packages/tgui/interfaces/BountyBoard.tsx
@@ -1,4 +1,3 @@
-import { useBackend } from '../backend';
import {
BlockQuote,
Box,
@@ -9,7 +8,9 @@ import {
Section,
Stack,
TextArea,
-} from '../components';
+} from 'tgui-core/components';
+
+import { useBackend } from '../backend';
import { formatMoney } from '../format';
import { Window } from '../layouts';
import { UserDetails } from './Vending';
diff --git a/tgui/packages/tgui/interfaces/BrigTimer.tsx b/tgui/packages/tgui/interfaces/BrigTimer.tsx
index 18527b9aab4..2069b5a8626 100644
--- a/tgui/packages/tgui/interfaces/BrigTimer.tsx
+++ b/tgui/packages/tgui/interfaces/BrigTimer.tsx
@@ -1,7 +1,7 @@
-import { BooleanLike } from 'common/react';
+import { Button, Section } from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Button, Section } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/CTFPanel.tsx b/tgui/packages/tgui/interfaces/CTFPanel.tsx
index b4760bb619d..7dbfa752964 100644
--- a/tgui/packages/tgui/interfaces/CTFPanel.tsx
+++ b/tgui/packages/tgui/interfaces/CTFPanel.tsx
@@ -1,6 +1,7 @@
+import { Box, Button, Flex, Section, Stack } from 'tgui-core/components';
+
import { BooleanLike } from '../../common/react';
import { useBackend } from '../backend';
-import { Box, Button, Flex, Section, Stack } from '../components';
import { Window } from '../layouts';
type CTFPanelData =
diff --git a/tgui/packages/tgui/interfaces/CameraConsole.tsx b/tgui/packages/tgui/interfaces/CameraConsole.tsx
index f28c308f1b5..b47f043e3eb 100644
--- a/tgui/packages/tgui/interfaces/CameraConsole.tsx
+++ b/tgui/packages/tgui/interfaces/CameraConsole.tsx
@@ -1,6 +1,4 @@
import { filter, sort } from 'common/collections';
-import { BooleanLike, classes } from 'common/react';
-import { createSearch } from 'common/string';
import { useState } from 'react';
import {
Button,
@@ -10,6 +8,8 @@ import {
Section,
Stack,
} from 'tgui-core/components';
+import { BooleanLike, classes } from 'tgui-core/react';
+import { createSearch } from 'tgui-core/string';
import { useBackend } from '../backend';
import { Window } from '../layouts';
diff --git a/tgui/packages/tgui/interfaces/Canister.tsx b/tgui/packages/tgui/interfaces/Canister.tsx
index f650085df46..7a2d63f7345 100644
--- a/tgui/packages/tgui/interfaces/Canister.tsx
+++ b/tgui/packages/tgui/interfaces/Canister.tsx
@@ -1,7 +1,3 @@
-import { toFixed } from 'common/math';
-import { BooleanLike } from 'common/react';
-
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -13,7 +9,11 @@ import {
RoundGauge,
Section,
Tooltip,
-} from '../components';
+} from 'tgui-core/components';
+import { toFixed } from 'tgui-core/math';
+import { BooleanLike } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
import { formatSiUnit } from '../format';
import { Window } from '../layouts';
diff --git a/tgui/packages/tgui/interfaces/Canvas.tsx b/tgui/packages/tgui/interfaces/Canvas.tsx
index 648418e7abd..3621c12eb3c 100644
--- a/tgui/packages/tgui/interfaces/Canvas.tsx
+++ b/tgui/packages/tgui/interfaces/Canvas.tsx
@@ -1,9 +1,9 @@
-import { Color } from 'common/color';
-import { decodeHtmlEntities } from 'common/string';
import { Component, createRef, RefObject } from 'react';
+import { Color } from 'tgui-core/color';
+import { Box, Button, Flex, Icon, Tooltip } from 'tgui-core/components';
+import { decodeHtmlEntities } from 'tgui-core/string';
import { useBackend } from '../backend';
-import { Box, Button, Flex, Icon, Tooltip } from '../components';
import { Window } from '../layouts';
const LEFT_CLICK = 0;
diff --git a/tgui/packages/tgui/interfaces/CargoExpress.tsx b/tgui/packages/tgui/interfaces/CargoExpress.tsx
index 83947b5adc4..91156956455 100644
--- a/tgui/packages/tgui/interfaces/CargoExpress.tsx
+++ b/tgui/packages/tgui/interfaces/CargoExpress.tsx
@@ -1,13 +1,13 @@
-import { BooleanLike } from 'common/react';
-
-import { useBackend } from '../backend';
import {
AnimatedNumber,
Box,
Button,
LabeledList,
Section,
-} from '../components';
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
import { CargoCatalog } from './Cargo/CargoCatalog';
import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox';
diff --git a/tgui/packages/tgui/interfaces/CargoHoldTerminal.tsx b/tgui/packages/tgui/interfaces/CargoHoldTerminal.tsx
index 6a392a38402..818706d57e7 100644
--- a/tgui/packages/tgui/interfaces/CargoHoldTerminal.tsx
+++ b/tgui/packages/tgui/interfaces/CargoHoldTerminal.tsx
@@ -1,13 +1,13 @@
-import { BooleanLike } from 'common/react';
-
-import { useBackend } from '../backend';
import {
AnimatedNumber,
Box,
Button,
LabeledList,
Section,
-} from '../components';
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/CellularEmporium.tsx b/tgui/packages/tgui/interfaces/CellularEmporium.tsx
index 77c3943cddf..a766bcae3e5 100644
--- a/tgui/packages/tgui/interfaces/CellularEmporium.tsx
+++ b/tgui/packages/tgui/interfaces/CellularEmporium.tsx
@@ -1,7 +1,4 @@
import { useState } from 'react';
-
-import { BooleanLike } from '../../common/react';
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -11,7 +8,10 @@ import {
NoticeBox,
Section,
Stack,
-} from '../components';
+} from 'tgui-core/components';
+
+import { BooleanLike } from '../../common/react';
+import { useBackend } from '../backend';
import { Window } from '../layouts';
type typePath = string;
diff --git a/tgui/packages/tgui/interfaces/Changelog.jsx b/tgui/packages/tgui/interfaces/Changelog.jsx
index 75fbd98ad2d..ccedd342df1 100644
--- a/tgui/packages/tgui/interfaces/Changelog.jsx
+++ b/tgui/packages/tgui/interfaces/Changelog.jsx
@@ -1,10 +1,6 @@
-import { classes } from 'common/react';
import dateformat from 'dateformat';
import yaml from 'js-yaml';
import { Component, Fragment } from 'react';
-
-import { resolveAsset } from '../assets';
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -13,7 +9,11 @@ import {
Section,
Stack,
Table,
-} from '../components';
+} from 'tgui-core/components';
+import { classes } from 'tgui-core/react';
+
+import { resolveAsset } from '../assets';
+import { useBackend } from '../backend';
import { Window } from '../layouts';
const icons = {
diff --git a/tgui/packages/tgui/interfaces/CheckboxInput.tsx b/tgui/packages/tgui/interfaces/CheckboxInput.tsx
index ab98a8a1f02..6ff49847df4 100644
--- a/tgui/packages/tgui/interfaces/CheckboxInput.tsx
+++ b/tgui/packages/tgui/interfaces/CheckboxInput.tsx
@@ -1,7 +1,4 @@
-import { createSearch, decodeHtmlEntities } from 'common/string';
import { useState } from 'react';
-
-import { useBackend } from '../backend';
import {
Button,
Icon,
@@ -11,7 +8,10 @@ import {
Stack,
Table,
Tooltip,
-} from '../components';
+} from 'tgui-core/components';
+import { createSearch, decodeHtmlEntities } from 'tgui-core/string';
+
+import { useBackend } from '../backend';
import { TableCell, TableRow } from '../components/Table';
import { Window } from '../layouts';
import { InputButtons } from './common/InputButtons';
diff --git a/tgui/packages/tgui/interfaces/ChemAcclimator.tsx b/tgui/packages/tgui/interfaces/ChemAcclimator.tsx
index 622b7055ddf..f400030bf23 100644
--- a/tgui/packages/tgui/interfaces/ChemAcclimator.tsx
+++ b/tgui/packages/tgui/interfaces/ChemAcclimator.tsx
@@ -1,7 +1,12 @@
-import { BooleanLike } from 'common/react';
+import {
+ Button,
+ LabeledList,
+ NumberInput,
+ Section,
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Button, LabeledList, NumberInput, Section } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/ChemDebugSynthesizer.tsx b/tgui/packages/tgui/interfaces/ChemDebugSynthesizer.tsx
index 06af6c1727f..b0a5b7a7681 100644
--- a/tgui/packages/tgui/interfaces/ChemDebugSynthesizer.tsx
+++ b/tgui/packages/tgui/interfaces/ChemDebugSynthesizer.tsx
@@ -1,5 +1,6 @@
+import { Button, NumberInput, Section } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Button, NumberInput, Section } from '../components';
import { Window } from '../layouts';
import { Beaker, BeakerDisplay } from './common/BeakerDisplay';
diff --git a/tgui/packages/tgui/interfaces/ChemDispenser.tsx b/tgui/packages/tgui/interfaces/ChemDispenser.tsx
index 850104e90f6..754229871f2 100644
--- a/tgui/packages/tgui/interfaces/ChemDispenser.tsx
+++ b/tgui/packages/tgui/interfaces/ChemDispenser.tsx
@@ -1,5 +1,3 @@
-import { BooleanLike } from 'common/react';
-import { toTitleCase } from 'common/string';
import { useState } from 'react';
import {
Box,
@@ -9,6 +7,8 @@ import {
ProgressBar,
Section,
} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
+import { toTitleCase } from 'tgui-core/string';
import { useBackend } from '../backend';
import { Window } from '../layouts';
diff --git a/tgui/packages/tgui/interfaces/ChemFilter.tsx b/tgui/packages/tgui/interfaces/ChemFilter.tsx
index 7615ec7de7b..f9ccb78fcb9 100644
--- a/tgui/packages/tgui/interfaces/ChemFilter.tsx
+++ b/tgui/packages/tgui/interfaces/ChemFilter.tsx
@@ -1,7 +1,7 @@
import { Fragment } from 'react';
+import { Button, Section, Stack } from 'tgui-core/components';
import { useBackend } from '../backend';
-import { Button, Section, Stack } from '../components';
import { CssColor } from '../constants';
import { Window } from '../layouts';
diff --git a/tgui/packages/tgui/interfaces/ChemHeater.tsx b/tgui/packages/tgui/interfaces/ChemHeater.tsx
index adf8020ba21..08df90ad4bd 100644
--- a/tgui/packages/tgui/interfaces/ChemHeater.tsx
+++ b/tgui/packages/tgui/interfaces/ChemHeater.tsx
@@ -1,6 +1,3 @@
-import { round, toFixed } from '../../common/math';
-import { BooleanLike } from '../../common/react';
-import { useBackend } from '../backend';
import {
AnimatedNumber,
Box,
@@ -12,7 +9,11 @@ import {
RoundGauge,
Section,
Table,
-} from '../components';
+} from 'tgui-core/components';
+
+import { round, toFixed } from '../../common/math';
+import { BooleanLike } from '../../common/react';
+import { useBackend } from '../backend';
import { COLORS } from '../constants';
import { Window } from '../layouts';
import { Beaker, BeakerSectionDisplay } from './common/BeakerDisplay';
diff --git a/tgui/packages/tgui/interfaces/ChemMaster.tsx b/tgui/packages/tgui/interfaces/ChemMaster.tsx
index c23ca29f8d9..62692588c65 100644
--- a/tgui/packages/tgui/interfaces/ChemMaster.tsx
+++ b/tgui/packages/tgui/interfaces/ChemMaster.tsx
@@ -1,8 +1,4 @@
-import { BooleanLike, classes } from 'common/react';
-import { capitalize } from 'common/string';
import { useState } from 'react';
-
-import { useBackend } from '../backend';
import {
AnimatedNumber,
Box,
@@ -16,7 +12,11 @@ import {
Stack,
Table,
Tooltip,
-} from '../components';
+} from 'tgui-core/components';
+import { BooleanLike, classes } from 'tgui-core/react';
+import { capitalize } from 'tgui-core/string';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
import { Beaker, BeakerReagent } from './common/BeakerDisplay';
diff --git a/tgui/packages/tgui/interfaces/ChemMixingChamber.tsx b/tgui/packages/tgui/interfaces/ChemMixingChamber.tsx
index a82fad9d465..765862ab4b8 100644
--- a/tgui/packages/tgui/interfaces/ChemMixingChamber.tsx
+++ b/tgui/packages/tgui/interfaces/ChemMixingChamber.tsx
@@ -1,8 +1,4 @@
-import { round, toFixed } from 'common/math';
-import { BooleanLike } from 'common/react';
import { useState } from 'react';
-
-import { useBackend } from '../backend';
import {
AnimatedNumber,
Box,
@@ -10,7 +6,11 @@ import {
NumberInput,
Section,
Stack,
-} from '../components';
+} from 'tgui-core/components';
+import { round, toFixed } from 'tgui-core/math';
+import { BooleanLike } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
type Reagent = {
diff --git a/tgui/packages/tgui/interfaces/ChemPress.tsx b/tgui/packages/tgui/interfaces/ChemPress.tsx
index 58e578f8027..ce54f04c8a5 100644
--- a/tgui/packages/tgui/interfaces/ChemPress.tsx
+++ b/tgui/packages/tgui/interfaces/ChemPress.tsx
@@ -1,7 +1,4 @@
-import { capitalizeAll } from 'common/string';
import { useState } from 'react';
-
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -9,7 +6,10 @@ import {
LabeledList,
NumberInput,
Section,
-} from '../components';
+} from 'tgui-core/components';
+import { capitalizeAll } from 'tgui-core/string';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
type Product = {
diff --git a/tgui/packages/tgui/interfaces/ChemReactionChamber.tsx b/tgui/packages/tgui/interfaces/ChemReactionChamber.tsx
index 8104065c88f..cdc2b6fac97 100644
--- a/tgui/packages/tgui/interfaces/ChemReactionChamber.tsx
+++ b/tgui/packages/tgui/interfaces/ChemReactionChamber.tsx
@@ -1,7 +1,4 @@
-import { round, toFixed } from 'common/math';
import { useState } from 'react';
-
-import { useBackend } from '../backend';
import {
AnimatedNumber,
Box,
@@ -11,7 +8,10 @@ import {
RoundGauge,
Section,
Stack,
-} from '../components';
+} from 'tgui-core/components';
+import { round, toFixed } from 'tgui-core/math';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
import { MixingData } from './ChemMixingChamber';
diff --git a/tgui/packages/tgui/interfaces/ChemRecipeDebug.tsx b/tgui/packages/tgui/interfaces/ChemRecipeDebug.tsx
index 5ebdd7d1e08..4fe2340757e 100644
--- a/tgui/packages/tgui/interfaces/ChemRecipeDebug.tsx
+++ b/tgui/packages/tgui/interfaces/ChemRecipeDebug.tsx
@@ -1,7 +1,4 @@
-import { BooleanLike } from 'common/react';
import { useState } from 'react';
-
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -11,7 +8,10 @@ import {
Section,
Stack,
Tabs,
-} from '../components';
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
import { ActiveReaction, ReactionDisplay } from './ChemHeater';
import { Beaker, BeakerSectionDisplay } from './common/BeakerDisplay';
diff --git a/tgui/packages/tgui/interfaces/ChemSeparator.tsx b/tgui/packages/tgui/interfaces/ChemSeparator.tsx
index e6be430466f..6a6a83a771b 100644
--- a/tgui/packages/tgui/interfaces/ChemSeparator.tsx
+++ b/tgui/packages/tgui/interfaces/ChemSeparator.tsx
@@ -1,6 +1,3 @@
-import { BooleanLike } from 'common/react';
-
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -8,7 +5,10 @@ import {
LabeledList,
ProgressBar,
Stack,
-} from '../components';
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
type RegHolderData = {
diff --git a/tgui/packages/tgui/interfaces/ChemSplitter.tsx b/tgui/packages/tgui/interfaces/ChemSplitter.tsx
index 75089e9a961..688cfa28020 100644
--- a/tgui/packages/tgui/interfaces/ChemSplitter.tsx
+++ b/tgui/packages/tgui/interfaces/ChemSplitter.tsx
@@ -1,7 +1,7 @@
-import { toFixed } from 'common/math';
+import { LabeledList, NumberInput, Section } from 'tgui-core/components';
+import { toFixed } from 'tgui-core/math';
import { useBackend } from '../backend';
-import { LabeledList, NumberInput, Section } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/ChemSynthesizer.tsx b/tgui/packages/tgui/interfaces/ChemSynthesizer.tsx
index 898ac07b292..440460c5d1c 100644
--- a/tgui/packages/tgui/interfaces/ChemSynthesizer.tsx
+++ b/tgui/packages/tgui/interfaces/ChemSynthesizer.tsx
@@ -1,7 +1,7 @@
-import { toFixed } from 'common/math';
+import { Box, Button, Section } from 'tgui-core/components';
+import { toFixed } from 'tgui-core/math';
import { useBackend } from '../backend';
-import { Box, Button, Section } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/CircuitAccessChecker.tsx b/tgui/packages/tgui/interfaces/CircuitAccessChecker.tsx
index f37414a86f5..baddb0ea65e 100644
--- a/tgui/packages/tgui/interfaces/CircuitAccessChecker.tsx
+++ b/tgui/packages/tgui/interfaces/CircuitAccessChecker.tsx
@@ -1,7 +1,7 @@
-import { BooleanLike } from 'common/react';
+import { Button, LabeledList } from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Button, LabeledList } from '../components';
import { Window } from '../layouts';
import { AccessConfig, Region } from './common/AccessConfig';
diff --git a/tgui/packages/tgui/interfaces/CircuitAdminPanel.tsx b/tgui/packages/tgui/interfaces/CircuitAdminPanel.tsx
index e95c7714dcd..8b4628bb900 100644
--- a/tgui/packages/tgui/interfaces/CircuitAdminPanel.tsx
+++ b/tgui/packages/tgui/interfaces/CircuitAdminPanel.tsx
@@ -1,7 +1,7 @@
-import { BooleanLike } from 'common/react';
+import { Button, Stack, Table } from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Button, Stack, Table } from '../components';
import { Window } from '../layouts';
type CircuitAdminPanelData = {
diff --git a/tgui/packages/tgui/interfaces/CircuitModule.jsx b/tgui/packages/tgui/interfaces/CircuitModule.jsx
index bbb323e5e5a..9aa3a616644 100644
--- a/tgui/packages/tgui/interfaces/CircuitModule.jsx
+++ b/tgui/packages/tgui/interfaces/CircuitModule.jsx
@@ -1,5 +1,6 @@
+import { Button, Dropdown, Input, Section, Stack } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Button, Dropdown, Input, Section, Stack } from '../components';
import { Window } from '../layouts';
export const CircuitModule = (props) => {
diff --git a/tgui/packages/tgui/interfaces/CircuitSignalHandler.tsx b/tgui/packages/tgui/interfaces/CircuitSignalHandler.tsx
index 66d04644391..43e4895d06b 100644
--- a/tgui/packages/tgui/interfaces/CircuitSignalHandler.tsx
+++ b/tgui/packages/tgui/interfaces/CircuitSignalHandler.tsx
@@ -1,8 +1,15 @@
-import { BooleanLike } from 'common/react';
import { Component, KeyboardEvent, MouseEvent } from 'react';
+import {
+ Box,
+ Button,
+ Dropdown,
+ Input,
+ Section,
+ Stack,
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Box, Button, Dropdown, Input, Section, Stack } from '../components';
import { Window } from '../layouts';
type Response = {
diff --git a/tgui/packages/tgui/interfaces/CivCargoHoldTerminal.jsx b/tgui/packages/tgui/interfaces/CivCargoHoldTerminal.jsx
index 438030e3947..a1cceecc7d7 100644
--- a/tgui/packages/tgui/interfaces/CivCargoHoldTerminal.jsx
+++ b/tgui/packages/tgui/interfaces/CivCargoHoldTerminal.jsx
@@ -1,4 +1,3 @@
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -6,7 +5,9 @@ import {
LabeledList,
NoticeBox,
Section,
-} from '../components';
+} from 'tgui-core/components';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
export const CivCargoHoldTerminal = (props) => {
diff --git a/tgui/packages/tgui/interfaces/Clipboard.tsx b/tgui/packages/tgui/interfaces/Clipboard.tsx
index 67cb86a1a98..119b0e80042 100644
--- a/tgui/packages/tgui/interfaces/Clipboard.tsx
+++ b/tgui/packages/tgui/interfaces/Clipboard.tsx
@@ -1,6 +1,3 @@
-import { BooleanLike } from 'common/react';
-
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -8,7 +5,10 @@ import {
Flex,
LabeledList,
Section,
-} from '../components';
+} from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/CodexGigas.tsx b/tgui/packages/tgui/interfaces/CodexGigas.tsx
index cd0f3dc55e0..581341fb67a 100644
--- a/tgui/packages/tgui/interfaces/CodexGigas.tsx
+++ b/tgui/packages/tgui/interfaces/CodexGigas.tsx
@@ -1,5 +1,6 @@
+import { Button, LabeledList, Section } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Button, LabeledList, Section } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/ColorBlindTester.tsx b/tgui/packages/tgui/interfaces/ColorBlindTester.tsx
index 3f33edcedc7..be0b89cd0e6 100644
--- a/tgui/packages/tgui/interfaces/ColorBlindTester.tsx
+++ b/tgui/packages/tgui/interfaces/ColorBlindTester.tsx
@@ -1,5 +1,6 @@
+import { Box, Button, NoticeBox, Section } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Box, Button, NoticeBox, Section } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/ColorMatrixEditor.tsx b/tgui/packages/tgui/interfaces/ColorMatrixEditor.tsx
index edf75dcf161..c0d515b4d27 100644
--- a/tgui/packages/tgui/interfaces/ColorMatrixEditor.tsx
+++ b/tgui/packages/tgui/interfaces/ColorMatrixEditor.tsx
@@ -1,6 +1,3 @@
-import { toFixed } from 'common/math';
-
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -8,7 +5,10 @@ import {
NumberInput,
Section,
Stack,
-} from '../components';
+} from 'tgui-core/components';
+import { toFixed } from 'tgui-core/math';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/CommandReport.tsx b/tgui/packages/tgui/interfaces/CommandReport.tsx
index 9185bebb5b4..8153ab81c72 100644
--- a/tgui/packages/tgui/interfaces/CommandReport.tsx
+++ b/tgui/packages/tgui/interfaces/CommandReport.tsx
@@ -1,6 +1,4 @@
import { useState } from 'react';
-
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -9,7 +7,9 @@ import {
Section,
Stack,
TextArea,
-} from '../components';
+} from 'tgui-core/components';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx b/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx
index 0a1306a19d6..459dbfbe855 100644
--- a/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx
+++ b/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx
@@ -1,8 +1,5 @@
import { sortBy } from 'common/collections';
-import { capitalize } from 'common/string';
import { useState } from 'react';
-
-import { useBackend, useLocalState } from '../backend';
import {
Blink,
Box,
@@ -13,7 +10,10 @@ import {
Modal,
Section,
TextArea,
-} from '../components';
+} from 'tgui-core/components';
+import { capitalize } from 'tgui-core/string';
+
+import { useBackend, useLocalState } from '../backend';
import { Window } from '../layouts';
import { sanitizeText } from '../sanitize';
import { StatusDisplayControls } from './common/StatusDisplayControls';
diff --git a/tgui/packages/tgui/interfaces/ComponentPrinter.tsx b/tgui/packages/tgui/interfaces/ComponentPrinter.tsx
index 7c8f16a8c53..9baf102e951 100644
--- a/tgui/packages/tgui/interfaces/ComponentPrinter.tsx
+++ b/tgui/packages/tgui/interfaces/ComponentPrinter.tsx
@@ -1,7 +1,7 @@
-import { classes } from 'common/react';
+import { Box, Icon, Section, Stack, Tooltip } from 'tgui-core/components';
+import { classes } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Box, Icon, Section, Stack, Tooltip } from '../components';
import { Window } from '../layouts';
import { DesignBrowser } from './Fabrication/DesignBrowser';
import { MaterialAccessBar } from './Fabrication/MaterialAccessBar';
diff --git a/tgui/packages/tgui/interfaces/Crayon.tsx b/tgui/packages/tgui/interfaces/Crayon.tsx
index 27a36f745cd..333f02f47cd 100644
--- a/tgui/packages/tgui/interfaces/Crayon.tsx
+++ b/tgui/packages/tgui/interfaces/Crayon.tsx
@@ -1,7 +1,7 @@
-import { BooleanLike } from 'common/react';
+import { Button, LabeledList, Section } from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Button, LabeledList, Section } from '../components';
import { Window } from '../layouts';
import { ColorItem } from './RapidPipeDispenser';
diff --git a/tgui/packages/tgui/interfaces/CrewConsole.tsx b/tgui/packages/tgui/interfaces/CrewConsole.tsx
index 7394d9964fd..9c15a551bb0 100644
--- a/tgui/packages/tgui/interfaces/CrewConsole.tsx
+++ b/tgui/packages/tgui/interfaces/CrewConsole.tsx
@@ -1,7 +1,7 @@
-import { BooleanLike } from 'common/react';
-import { createSearch } from 'common/string';
import { useState } from 'react';
import { Box, Button, Icon, Input, Section, Table } from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
+import { createSearch } from 'tgui-core/string';
import { useBackend } from '../backend';
import { COLORS } from '../constants';
@@ -26,7 +26,7 @@ const SORT_NAMES = {
const STAT_LIVING = 0;
const STAT_DEAD = 4;
-const SORT_OPTIONS = ['ijob', 'name', 'area', 'health'];
+const SORT_OPTIONS = ['health', 'ijob', 'name', 'area'];
const jobIsHead = (jobId: number) => jobId % 10 === 0;
@@ -69,10 +69,10 @@ const statToIcon = (life_status: number) => {
};
const healthSort = (a: CrewSensor, b: CrewSensor) => {
- if (a.life_status < b.life_status) return -1;
- if (a.life_status > b.life_status) return 1;
- if (a.health > b.health) return -1;
- if (a.health < b.health) return 1;
+ if (a.life_status > b.life_status) return -1;
+ if (a.life_status < b.life_status) return 1;
+ if (a.health < b.health) return -1;
+ if (a.health > b.health) return 1;
return 0;
};
diff --git a/tgui/packages/tgui/interfaces/CrewManifest.jsx b/tgui/packages/tgui/interfaces/CrewManifest.jsx
index 73d67bf5158..5fe3741f62c 100644
--- a/tgui/packages/tgui/interfaces/CrewManifest.jsx
+++ b/tgui/packages/tgui/interfaces/CrewManifest.jsx
@@ -1,7 +1,7 @@
-import { classes } from 'common/react';
+import { Icon, Section, Table, Tooltip } from 'tgui-core/components';
+import { classes } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { Icon, Section, Table, Tooltip } from '../components';
import { Window } from '../layouts';
const commandJobs = [
diff --git a/tgui/packages/tgui/interfaces/CrossingSignal.tsx b/tgui/packages/tgui/interfaces/CrossingSignal.tsx
index e0585318649..ecdc52f6a1d 100644
--- a/tgui/packages/tgui/interfaces/CrossingSignal.tsx
+++ b/tgui/packages/tgui/interfaces/CrossingSignal.tsx
@@ -1,7 +1,7 @@
-import { BooleanLike } from 'common/react';
+import { LabeledList, Section } from 'tgui-core/components';
+import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
-import { LabeledList, Section } from '../components';
import { Window } from '../layouts';
type Data = {
diff --git a/tgui/packages/tgui/interfaces/Cryo.tsx b/tgui/packages/tgui/interfaces/Cryo.tsx
index 2999f4c04d8..a4c2d368017 100644
--- a/tgui/packages/tgui/interfaces/Cryo.tsx
+++ b/tgui/packages/tgui/interfaces/Cryo.tsx
@@ -1,14 +1,14 @@
-import { round } from 'common/math';
-import { BooleanLike } from 'common/react';
-
-import { useBackend } from '../backend';
import {
AnimatedNumber,
Button,
LabeledList,
ProgressBar,
Section,
-} from '../components';
+} from 'tgui-core/components';
+import { round } from 'tgui-core/math';
+import { BooleanLike } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
import { Window } from '../layouts';
import { Beaker, BeakerSectionDisplay } from './common/BeakerDisplay';
diff --git a/tgui/packages/tgui/interfaces/Crystallizer.tsx b/tgui/packages/tgui/interfaces/Crystallizer.tsx
index 6cdd0bb86a5..213fef342a6 100644
--- a/tgui/packages/tgui/interfaces/Crystallizer.tsx
+++ b/tgui/packages/tgui/interfaces/Crystallizer.tsx
@@ -1,7 +1,3 @@
-import { toFixed } from 'common/math';
-import { BooleanLike } from 'common/react';
-
-import { useBackend } from '../backend';
import {
Box,
Button,
@@ -9,7 +5,11 @@ import {
NumberInput,
ProgressBar,
Section,
-} from '../components';
+} from 'tgui-core/components';
+import { toFixed } from 'tgui-core/math';
+import { BooleanLike } from 'tgui-core/react';
+
+import { useBackend } from '../backend';
import { getGasColor } from '../constants';
import { Window } from '../layouts';
diff --git a/tgui/packages/tgui/interfaces/CyborgBootDebug.jsx b/tgui/packages/tgui/interfaces/CyborgBootDebug.jsx
index 914d4373ea0..d023240c7e1 100644
--- a/tgui/packages/tgui/interfaces/CyborgBootDebug.jsx
+++ b/tgui/packages/tgui/interfaces/CyborgBootDebug.jsx
@@ -1,5 +1,6 @@
+import { Button, Input, LabeledList, Section } from 'tgui-core/components';
+
import { useBackend } from '../backend';
-import { Button, Input, LabeledList, Section } from '../components';
import { Window } from '../layouts';
const TOOLTIP_NAME = `
diff --git a/tgui/packages/tgui/interfaces/FishCatalog.tsx b/tgui/packages/tgui/interfaces/FishCatalog.tsx
index 7bb85054fb0..34ee7a629b2 100644
--- a/tgui/packages/tgui/interfaces/FishCatalog.tsx
+++ b/tgui/packages/tgui/interfaces/FishCatalog.tsx
@@ -27,6 +27,7 @@ type FishInfo = {
weight: string;
size: string;
icon: string;
+ beauty: string;
};
type FishCatalogData = {
@@ -93,6 +94,9 @@ export const FishCatalog = (props) => {
{currentFish.weight} g
+
+ {currentFish.beauty}
+
diff --git a/tgui/packages/tgui/interfaces/LoadoutManager.tsx b/tgui/packages/tgui/interfaces/LoadoutManager.tsx
deleted file mode 100644
index 3a151e4abf6..00000000000
--- a/tgui/packages/tgui/interfaces/LoadoutManager.tsx
+++ /dev/null
@@ -1,243 +0,0 @@
-// THIS IS A SKYRAT UI FILE
-import { BooleanLike } from 'common/react';
-import { createSearch } from 'common/string';
-import { useState } from 'react';
-
-import { useBackend } from '../backend';
-import { Box, Button, Dropdown, Input, Section, Stack } from '../components';
-import { Window } from '../layouts';
-
-type LoadoutTabData = {
- loadout_tabs: LoadoutTab[];
- selected_loadout: string[];
- user_is_donator: BooleanLike;
-};
-
-type LoadoutTab = {
- name: string;
- title: string;
- contents: LoadoutTabItem[];
-};
-
-type LoadoutTabItem = {
- name: string;
- path: string;
- is_greyscale: BooleanLike;
- is_renameable: BooleanLike;
- is_job_restricted: BooleanLike;
- is_job_blacklisted: BooleanLike;
- is_species_restricted: BooleanLike;
- is_donator_only: BooleanLike;
- is_ckey_whitelisted: BooleanLike;
- tooltip_text: string;
-};
-
-export const LoadoutManager = (props) => {
- const { act, data } = useBackend();
- const { selected_loadout, loadout_tabs, user_is_donator } = data;
- const [selectedTabName, setSelectedTab] = useState(loadout_tabs[0]?.name);
- const selectedTab = loadout_tabs.find((curTab) => {
- return curTab.name === selectedTabName;
- });
- const [searchItem, setSearchItem] = useState('');
- const search = createSearch(
- searchItem,
- (loadoutTabItem: LoadoutTabItem) => loadoutTabItem.name,
- );
- const loadout_items_filtered =
- searchItem.length > 0
- ? selectedTab?.contents.filter((loadoutTabItem) => search(loadoutTabItem))
- : selectedTab?.contents;
-
- return (
-
-
-
-
-
- curTab.name)}
- onSelected={(curTab) => setSelectedTab(curTab)}
- />
-
-
-
-
- {
- setSearchItem(value);
- }}
- fluid
- />
-
-
-
-
-
-
-
-
-
-
- {selectedTab && selectedTab.contents ? (
- act('clear_all_items')}
- />
- }
- >
-
- {loadout_items_filtered?.map((item) => (
-
-
-
- {item.name}
-
- {!!item.is_greyscale && (
-
-
- )}
- {!!item.is_renameable && (
-
-
- )}
- {!!item.is_job_restricted && (
-
-
- )}
- {!!item.is_job_blacklisted && (
-
-
- )}
- {!!item.is_species_restricted && (
-
-
- )}
- {!!item.is_donator_only && (
-
-
- )}
- {!!item.is_ckey_whitelisted && (
-
-
- )}
-
-
- act('select_item', {
- path: item.path,
- deselect: selected_loadout.includes(
- item.path,
- ),
- })
- }
- />
-
-
-
- ))}
-
-
- ) : (
-
- No contents for selected tab.
-
- )}
-
-
-
-
-
-
- );
-};
diff --git a/tgui/packages/tgui/interfaces/Orbit/JobIcon.tsx b/tgui/packages/tgui/interfaces/Orbit/JobIcon.tsx
index f102927e9bc..2d584bfc6b1 100644
--- a/tgui/packages/tgui/interfaces/Orbit/JobIcon.tsx
+++ b/tgui/packages/tgui/interfaces/Orbit/JobIcon.tsx
@@ -37,20 +37,15 @@ export function JobIcon(props: Props) {
return (