Welcome agent, congratulations on your new position as contractor. On top of your already assigned objectives,
this kit will provide you contracts to take on for TC payments.
-
Provided within, we give your specialist contractor space suit. It's even more compact, being able to fit into a pocket, and faster than the
Syndicate space suit available to you on the uplink. We also provide your chameleon jumpsuit and mask, both of which can be changed
to any form you need for the moment. The cigarettes are a special blend - it'll heal your injuries slowly overtime.
-
Your standard issue contractor baton hits harder than the ones you might be used to, and likely be your go to weapon for kidnapping your
targets. The three additional items have been randomly selected from what we had available. We hope they're useful to you for your mission.
-
The contractor hub, available at the top right of the uplink, will provide you unique items and abilities. These are bought using Contractor Rep,
with two Rep being provided each time you complete a contract.
-
Using the tablet
Open the Syndicate Contract Uplink program.
@@ -312,7 +308,6 @@
Contracts are completed by bringing the target to designated dropoff, calling for extraction, and putting them
inside the pod.
-
Be careful when accepting a contract. While you'll be able to see the location of the dropoff point, cancelling will make it
unavailable to take on again.
The tablet can also be recharged at any cell charger.
@@ -327,7 +322,6 @@
We need your target for our own reasons, but we ransom them back to your mission area once their use is served. They will return back
from where you sent them off from in several minutes time. Don't worry, we give you a cut of what we get paid. We pay this into whatever
ID card you have equipped, on top of the TC payment we give.
-
Good luck agent. You can burn this document with the supplied lighter.
"}
return ..()
@@ -532,6 +526,22 @@
new /obj/item/food/croissant/throwing(src)
new /obj/item/book/granter/crafting_recipe/combat_baking(src)
+/obj/item/storage/box/syndie_kit/laser_arm/PopulateContents()
+ new /obj/item/autosurgeon/organ/cyberlink_syndicate(src)
+ new /obj/item/autosurgeon/syndicate/laser_arm (src)
+
+/obj/item/storage/box/syndie_kit/nodrop/PopulateContents()
+ new /obj/item/autosurgeon/organ/cyberlink_nt_high(src)
+ new /obj/item/autosurgeon/syndicate/nodrop(src)
+
+/obj/item/storage/box/syndie_kit/anti_stun/PopulateContents()
+ new /obj/item/autosurgeon/organ/cyberlink_nt_high(src)
+ new /obj/item/autosurgeon/syndicate/anti_stun(src)
+
+/obj/item/storage/box/syndie_kit/reviver/PopulateContents()
+ new /obj/item/autosurgeon/organ/cyberlink_nt_high(src)
+ new /obj/item/autosurgeon/syndicate/reviver(src)
+
/obj/item/storage/box/syndie_kit/centcom_costume/PopulateContents()
new /obj/item/clothing/under/rank/centcom/officer(src)
new /obj/item/clothing/shoes/sneakers/black(src)
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 728e1504531b..e9d319803b7b 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -813,7 +813,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
return COMSIG_CARBON_SHOVE_HANDLED
/// Signal proc for [COMSIG_ATOM_MAGICALLY_UNLOCKED]. Unlock and open up when we get knock casted.
-/obj/structure/closet/proc/on_magic_unlock(datum/source, datum/action/cooldown/spell/aoe/knock/spell, mob/living/caster)
+/obj/structure/closet/proc/on_magic_unlock(datum/source, datum/action/cooldown/spell/aoe/knock/spell, atom/caster)
SIGNAL_HANDLER
locked = FALSE
diff --git a/code/game/objects/structures/lavaland/geyser.dm b/code/game/objects/structures/lavaland/geyser.dm
index fdf4cd550dcb..2928a20da191 100644
--- a/code/game/objects/structures/lavaland/geyser.dm
+++ b/code/game/objects/structures/lavaland/geyser.dm
@@ -4,6 +4,7 @@
///A lavaland geyser that spawns chems and can be mining scanned for points. Made to work with the plumbing pump to extract that sweet rare nectar
/obj/structure/geyser
name = "geyser"
+ desc = "A geyser that contains some form of chemicals inside. A liquid pump can be used to extract it's chemicals." //monkestation addition
icon = 'icons/obj/lavaland/terrain.dmi'
icon_state = "geyser"
anchored = TRUE
@@ -107,7 +108,7 @@
/obj/structure/geyser/random
point_value = 500
true_name = "strange geyser"
- discovery_message = "It's a strange geyser! How does any of this even work?" //it doesnt
+ discovery_message = "It's a strange geyser! Who knows what chemicals are inside?" //it doesnt //monkestation edit: more accurate description
/obj/structure/geyser/random/Initialize(mapload)
reagent_id = get_random_reagent_id()
diff --git a/code/game/turfs/open/floor/misc_floor.dm b/code/game/turfs/open/floor/misc_floor.dm
index 4e83c9acb054..81249fe0ace6 100644
--- a/code/game/turfs/open/floor/misc_floor.dm
+++ b/code/game/turfs/open/floor/misc_floor.dm
@@ -155,6 +155,14 @@
/turf/open/floor/noslip/MakeSlippery(wet_setting, min_wet_time, wet_time_to_add, max_wet_time, permanent)
return
+/turf/open/floor/noslip/tram/Initialize(mapload)
+ . = ..()
+ var/current_holiday_color = request_holiday_colors(src, PATTERN_VERTICAL_STRIPE)
+ if(current_holiday_color)
+ color = current_holiday_color
+ else
+ color = "#EFB341"
+
/turf/open/floor/oldshuttle
icon = 'icons/turf/shuttleold.dmi'
icon_state = "floor"
diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm
index 1ba428e0f931..81f5cf73bee8 100644
--- a/code/game/turfs/open/lava.dm
+++ b/code/game/turfs/open/lava.dm
@@ -138,6 +138,7 @@
initial_gas_mix = AIRLESS_ATMOS
/turf/open/lava/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
+ . = ..()
if(burn_stuff(arrived))
START_PROCESSING(SSobj, src)
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 6e84fae33a9b..4386545f07d3 100755
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -652,6 +652,11 @@ GLOBAL_LIST_EMPTY(station_turfs)
/turf/AllowDrop()
return TRUE
+// monkestation start: fix runtime from turf.drop_location()
+/turf/drop_location()
+ return src
+// monkestation end
+
/turf/proc/add_vomit_floor(mob/living/M, toxvomit = NONE, purge_ratio = 0.1)
var/obj/effect/decal/cleanable/vomit/V = new /obj/effect/decal/cleanable/vomit(src, M.get_static_viruses())
@@ -743,16 +748,16 @@ GLOBAL_LIST_EMPTY(station_turfs)
* Returns adjacent turfs to this turf that are reachable, in all cardinal directions
*
* Arguments:
- * * caller: The movable, if one exists, being used for mobility checks to see what tiles it can reach
+ * * source: The movable, if one exists, being used for mobility checks to see what tiles it can reach
* * access: A list that decides if we can gain access to doors that would otherwise block a turf
* * simulated_only: Do we only worry about turfs with simulated atmos, most notably things that aren't space?
* * no_id: When true, doors with public access will count as impassible
*/
-/turf/proc/reachableAdjacentTurfs(atom/movable/caller, list/access, simulated_only, no_id = FALSE)
+/turf/proc/reachableAdjacentTurfs(atom/movable/source, list/access, simulated_only, no_id = FALSE)
var/static/space_type_cache = typecacheof(/turf/open/space)
. = list()
- var/datum/can_pass_info/pass_info = new(caller, access, no_id)
+ var/datum/can_pass_info/pass_info = new(source, access, no_id)
for(var/iter_dir in GLOB.cardinals)
var/turf/turf_to_check = get_step(src,iter_dir)
if(!turf_to_check || (simulated_only && space_type_cache[turf_to_check.type]))
diff --git a/code/modules/admin/permissionedit.dm b/code/modules/admin/permissionedit.dm
index 5544f90fb748..0db57f74efe6 100644
--- a/code/modules/admin/permissionedit.dm
+++ b/code/modules/admin/permissionedit.dm
@@ -222,7 +222,7 @@
. = ckey(admin_key)
if(!.)
return FALSE
- if(!admin_ckey && (. in GLOB.admin_datums+GLOB.deadmins))
+ if(!admin_ckey && (. in (GLOB.admin_datums+GLOB.deadmins)))
to_chat(usr, span_danger("[admin_key] is already an admin."), confidential = TRUE)
return FALSE
if(use_db)
diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm
index b13363f4545b..4c00bdec7493 100644
--- a/code/modules/admin/verbs/playsound.dm
+++ b/code/modules/admin/verbs/playsound.dm
@@ -107,7 +107,8 @@
if (duration > 10 MINUTES)
if((tgui_alert(user, "This song is over 10 minutes long. Are you sure you want to play it?", "Length Warning!", list("No", "Yes", "Cancel")) != "Yes"))
return
- var/res = tgui_alert(user, "Show the title of and link to this song to the players?\n[title]", "Show Info?", list("Yes", "No", "Cancel"))
+ // MONKESTATION EDIT ORIGINAL: var/res = tgui_alert(user, "Show the title of and link to this song to the players?\n[title]", "Show Info?", list("Yes", "No", "Cancel"))
+ var/res = tgui_input_list(user, "Show the title of and link to this song to the players?\n[title]", "Show Info?", list("Yes", "No", "Custom Title", "Cancel")) // MONKESTATION EDIT - Custom title
switch(res)
if("Yes")
music_extra_data["title"] = data["title"]
@@ -117,6 +118,14 @@
music_extra_data["artist"] = "Song Artist Hidden"
music_extra_data["upload_date"] = "Song Upload Date Hidden"
music_extra_data["album"] = "Song Album Hidden"
+ // MONKESTATION EDIT START - Custom title
+ if("Custom Title")
+ var/custom_title = tgui_input_text(user, "Enter the title to show to players", "Custom sound info", null)
+ if (!length(custom_title))
+ tgui_alert(user, "No title specified, using default.", "Custom sound info", list("Okay"))
+ else
+ music_extra_data["title"] = custom_title
+ // MONKESTATION EDIT END
if("Cancel", null)
return
var/anon = tgui_alert(user, "Display who played the song?", "Credit Yourself?", list("Yes", "No", "Cancel"))
diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm
index 2adf60b77cbd..a172f3d84542 100644
--- a/code/modules/admin/verbs/secrets.dm
+++ b/code/modules/admin/verbs/secrets.dm
@@ -1,4 +1,4 @@
-GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller)
+GLOBAL_DATUM(everyone_an_antag, /datum/everyone_is_an_antag_controller)
/client/proc/secrets() //Creates a verb for admins to open up the ui
set name = "Secrets"
@@ -453,24 +453,35 @@ GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller)
for(var/i in GLOB.human_list)
var/mob/living/carbon/human/H = i
INVOKE_ASYNC(H, TYPE_PROC_REF(/mob/living/carbon, monkeyize))
- if("traitor_all")
+ if("antag_all")
if(!is_funmin)
return
if(!SSticker.HasRoundStarted())
tgui_alert(usr,"The game hasn't started yet!")
return
- if(GLOB.everyone_a_traitor)
- tgui_alert(usr, "The everyone is a traitor secret has already been triggered")
+ if(GLOB.everyone_an_antag)
+ var/are_we_antagstacking = tgui_alert(usr, "The everyone is antag secret has already been triggered. Do you want to stack antags?", "DANGER ZONE. Are you sure about this?", list("Confirm", "Abort"))
+ if(are_we_antagstacking != "Confirm")
+ return
+
+ var/chosen_antag = tgui_input_list(usr, "Choose antag", "Chose antag", list(ROLE_TRAITOR, ROLE_CHANGELING, ROLE_HERETIC, ROLE_CULTIST, ROLE_NINJA, ROLE_WIZARD, ROLE_NIGHTMARE))
+ if(!chosen_antag)
return
- var/objective = tgui_input_text(holder, "Enter an objective", "Objective")
+ var/objective = tgui_input_text(usr, "Enter an objective", "Objective")
if(!objective)
return
- GLOB.everyone_a_traitor = new /datum/everyone_is_a_traitor_controller(objective)
- SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Traitor All", "[objective]"))
+ var/confirmation = tgui_alert(usr, "Make everyone in to [chosen_antag] with objective: [objective]", "Are you sure about this?", list("Confirm", "Abort"))
+ if(confirmation != "Confirm")
+ return
+ var/keep_generic_objecives = tgui_alert(usr, "Generate normal objectives?", "Give default objectives?", list("Yes", "No"))
+ keep_generic_objecives = (keep_generic_objecives != "Yes") ? FALSE : TRUE
+
+ GLOB.everyone_an_antag = new /datum/everyone_is_an_antag_controller(chosen_antag, objective, keep_generic_objecives)
+ SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("[chosen_antag] All", "[objective]"))
for(var/mob/living/player in GLOB.player_list)
- GLOB.everyone_a_traitor.make_traitor(null, player)
- message_admins(span_adminnotice("[key_name_admin(holder)] used everyone is a traitor secret. Objective is [objective]"))
- log_admin("[key_name(holder)] used everyone is a traitor secret. Objective is [objective]")
+ GLOB.everyone_an_antag.make_antag(null, player)
+ message_admins(span_adminnotice("[key_name_admin(holder)] used everyone is antag secret. Antag is [chosen_antag]. Objective is [objective]. Generate default objectives: [keep_generic_objecives]"))
+ log_admin("[key_name(holder)] used everyone is antag secret: [chosen_antag] . Objective is [objective]. Generate default objectives: [keep_generic_objecives]. ")
if("massbraindamage")
if(!is_funmin)
return
@@ -623,38 +634,79 @@ GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller)
T.flick_overlay_static(portal_appearance[GET_TURF_PLANE_OFFSET(T) + 1], 15)
playsound(T, 'sound/magic/lightningbolt.ogg', rand(80, 100), TRUE)
-///Makes sure latejoining crewmembers also become traitors.
-/datum/everyone_is_a_traitor_controller
+/datum/everyone_is_an_antag_controller
+ var/chosen_antag = ""
var/objective = ""
+ var/keep_generic_objecives
-/datum/everyone_is_a_traitor_controller/New(objective)
+/datum/everyone_is_an_antag_controller/New(chosen_antag, objective, keep_generic_objecives)
+ . = ..()
+ src.chosen_antag = chosen_antag
src.objective = objective
- RegisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED, PROC_REF(make_traitor))
+ src.keep_generic_objecives = keep_generic_objecives
+ RegisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED, PROC_REF(make_antag_delay))
-/datum/everyone_is_a_traitor_controller/Destroy()
+/datum/everyone_is_an_antag_controller/Destroy()
UnregisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED)
return ..()
-/datum/everyone_is_a_traitor_controller/proc/make_traitor(datum/source, mob/living/player)
+/datum/everyone_is_an_antag_controller/proc/assign_admin_objective_and_antag(mob/living/player, datum/antagonist/antag_datum)
+ var/datum/objective/new_objective = new(objective)
+ new_objective.team = player
+ new_objective.team_explanation_text = objective
+ antag_datum.objectives += new_objective
+ player.mind.add_antag_datum(antag_datum)
+
+/datum/everyone_is_an_antag_controller/proc/make_antag_delay(datum/source, mob/living/player)
SIGNAL_HANDLER
+ INVOKE_ASYNC(src, PROC_REF(make_antag), source, player)
+
+
+/datum/everyone_is_an_antag_controller/proc/make_antag(datum/source, mob/living/player)
if(player.stat == DEAD || !player.mind)
return
- if(is_special_character(player))
- return
+ sleep(1)
if(ishuman(player))
- var/datum/antagonist/traitor/traitor_datum = new(give_objectives = FALSE)
- var/datum/objective/new_objective = new
- new_objective.owner = player
- new_objective.explanation_text = objective
- traitor_datum.objectives += new_objective
- player.mind.add_antag_datum(traitor_datum)
- var/datum/uplink_handler/uplink = traitor_datum.uplink_handler
- uplink.has_progression = FALSE
- uplink.has_objectives = FALSE
+ switch(chosen_antag)
+ if(ROLE_TRAITOR)
+ var/datum/antagonist/traitor/antag_datum = new(give_objectives = keep_generic_objecives)
+ assign_admin_objective_and_antag(player, antag_datum)
+ var/datum/uplink_handler/uplink = antag_datum.uplink_handler
+ uplink.has_progression = FALSE
+ uplink.has_objectives = FALSE
+ if(ROLE_CHANGELING)
+ var/datum/antagonist/changeling/antag_datum = new
+ antag_datum.give_objectives = keep_generic_objecives
+ assign_admin_objective_and_antag(player, antag_datum)
+ if(ROLE_HERETIC)
+ var/datum/antagonist/heretic/antag_datum = new
+ antag_datum.give_objectives = keep_generic_objecives
+ assign_admin_objective_and_antag(player, antag_datum)
+ if(ROLE_CULTIST)
+ var/datum/antagonist/cult/antag_datum = new
+ assign_admin_objective_and_antag(player, antag_datum)
+ if(ROLE_NINJA)
+ var/datum/antagonist/ninja/antag_datum = new
+ antag_datum.give_objectives = keep_generic_objecives
+ for(var/obj/item/item_to_drop in player)
+ if(!istype(item_to_drop, /obj/item/implant)) //avoid removing implanted uplinks
+ player.dropItemToGround(item_to_drop, FALSE)
+ assign_admin_objective_and_antag(player, antag_datum)
+ if(ROLE_WIZARD)
+ var/datum/antagonist/wizard/antag_datum = new
+ antag_datum.give_objectives = keep_generic_objecives
+ antag_datum.move_to_lair = FALSE
+ for(var/obj/item/item_to_drop in player) //avoid deleting player's items
+ if(!istype(item_to_drop, /obj/item/implant))
+ player.dropItemToGround(item_to_drop, FALSE)
+ assign_admin_objective_and_antag(player, antag_datum)
+ if(ROLE_NIGHTMARE)
+ var/datum/antagonist/nightmare/antag_datum = new
+ assign_admin_objective_and_antag(player, antag_datum)
+ player.set_species(/datum/species/shadow/nightmare)
+
else if(isAI(player))
- var/datum/antagonist/malf_ai/malfunction_datum = new(give_objectives = FALSE)
- var/datum/objective/new_objective = new
- new_objective.owner = player
- new_objective.explanation_text = objective
- malfunction_datum.objectives += new_objective
- player.mind.add_antag_datum(malfunction_datum)
+ var/datum/antagonist/malf_ai/antag_datum = new
+ antag_datum.give_objectives = keep_generic_objecives
+ assign_admin_objective_and_antag(player, antag_datum)
+
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 19a346e3654c..9ea41eca4aba 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -54,7 +54,7 @@ GLOBAL_LIST_EMPTY(antagonists)
/// The typepath for the outfit to show in the preview for the preferences menu.
var/preview_outfit
/// Flags for antags to turn on or off and check!
- var/antag_flags = NONE
+ var/antag_flags = FLAG_CAN_SEE_EXPOITABLE_INFO // monkestation edit: allow antags to see exploitable info.
/// If true, this antagonist can assign themself a new objective
var/can_assign_self_objectives = FALSE
/// Default to fill in when entering a custom objective.
diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm
index d3d5bf24b66c..ed32ca368d5a 100644
--- a/code/modules/antagonists/abductor/abductor.dm
+++ b/code/modules/antagonists/abductor/abductor.dm
@@ -7,6 +7,7 @@
show_in_antagpanel = FALSE //should only show subtypes
show_to_ghosts = TRUE
suicide_cry = "FOR THE MOTHERSHIP!!" // They can't even talk but y'know
+ antag_flags = parent_type::antag_flags | FLAG_ANTAG_CAP_TEAM // monkestation addition
var/datum/team/abductor_team/team
var/sub_role
var/outfit
diff --git a/code/modules/antagonists/blob/blob_minion.dm b/code/modules/antagonists/blob/blob_minion.dm
index 9bf37e961d5d..3e3f7647bcfd 100644
--- a/code/modules/antagonists/blob/blob_minion.dm
+++ b/code/modules/antagonists/blob/blob_minion.dm
@@ -4,6 +4,7 @@
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
show_in_antagpanel = FALSE
+ antag_flags = FLAG_ANTAG_CAP_IGNORE // monkestation addition
/// The blob core that this minion is attached to
var/datum/weakref/overmind
diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm
index af4b9b466930..732f26945dbd 100644
--- a/code/modules/antagonists/blob/overmind.dm
+++ b/code/modules/antagonists/blob/overmind.dm
@@ -195,7 +195,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
if(isnull(guy_turf) || !is_station_level(guy_turf.z))
continue
- if(live_guy in GLOB.overminds || (live_guy.pass_flags & PASSBLOB))
+ if((live_guy in GLOB.overminds) || (live_guy.pass_flags & PASSBLOB))
continue
var/area/blob_area = get_area(guy_turf)
diff --git a/code/modules/antagonists/brainwashing/brainwashing.dm b/code/modules/antagonists/brainwashing/brainwashing.dm
index e74e7b32f7f9..7f60de3a1436 100644
--- a/code/modules/antagonists/brainwashing/brainwashing.dm
+++ b/code/modules/antagonists/brainwashing/brainwashing.dm
@@ -35,7 +35,7 @@
roundend_category = "brainwashed victims"
show_in_antagpanel = TRUE
antag_hud_name = "brainwashed"
- antagpanel_category = "Other"
+ antagpanel_category = ANTAG_GROUP_CREW
show_name_in_check_antagonists = TRUE
count_against_dynamic_roll_chance = FALSE
ui_name = "AntagInfoBrainwashed"
diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm
index 8218544a305e..4aef0a99229f 100644
--- a/code/modules/antagonists/brother/brother.dm
+++ b/code/modules/antagonists/brother/brother.dm
@@ -8,6 +8,7 @@
ui_name = "AntagInfoBrother"
suicide_cry = "FOR MY BROTHER!!"
antag_moodlet = /datum/mood_event/focused
+ antag_flags = parent_type::antag_flags | FLAG_ANTAG_CAP_TEAM // monkestation addition
VAR_PRIVATE
datum/team/brother_team/team
@@ -38,6 +39,7 @@
if (!is_first_brother)
to_chat(carbon_owner, span_boldwarning("The Syndicate have higher expectations from you than others. They have granted you an extra flash to convert one other person."))
+ carbon_owner.balloon_alert(carbon_owner, "extra flash granted!")
return ..()
@@ -79,7 +81,7 @@
flashed.balloon_alert(source, "[flashed.p_theyre()] loyal to someone else!")
return
- if (HAS_TRAIT(flashed, TRAIT_MINDSHIELD) || HAS_MIND_TRAIT(flashed, TRAIT_UNCONVERTABLE) || (flashed.mind.assigned_role?.departments_bitflags & DEPARTMENT_BITFLAG_SECURITY)) // monkestation edit: TRAIT_UNCONVERTABLE
+ if (HAS_TRAIT(flashed, TRAIT_MINDSHIELD) || HAS_MIND_TRAIT(flashed, TRAIT_UNCONVERTABLE)) // monkestation edit: TRAIT_UNCONVERTABLE and remove hardcoded security check
flashed.balloon_alert(source, "[flashed.p_they()] resist!")
return
diff --git a/code/modules/antagonists/changeling/fallen_changeling.dm b/code/modules/antagonists/changeling/fallen_changeling.dm
index c44c1b66cd3d..ba5d3c3976ba 100644
--- a/code/modules/antagonists/changeling/fallen_changeling.dm
+++ b/code/modules/antagonists/changeling/fallen_changeling.dm
@@ -6,6 +6,7 @@
job_rank = ROLE_CHANGELING
antag_moodlet = /datum/mood_event/fallen_changeling
antag_hud_name = "changeling"
+ antag_flags = parent_type::antag_flags | FLAG_ANTAG_CAP_IGNORE // monkestation addition
/datum/mood_event/fallen_changeling
description = "My powers! Where are my powers?!"
diff --git a/code/modules/antagonists/changeling/powers/fakedeath.dm b/code/modules/antagonists/changeling/powers/fakedeath.dm
index ba29f7cf5cb7..e39880796518 100644
--- a/code/modules/antagonists/changeling/powers/fakedeath.dm
+++ b/code/modules/antagonists/changeling/powers/fakedeath.dm
@@ -121,7 +121,7 @@
return
var/datum/antagonist/changeling/ling = user.mind?.has_antag_datum(/datum/antagonist/changeling)
- if(QDELETED(ling) || !(src in ling.innate_powers + ling.purchased_powers)) // checking both innate and purchased for full coverage
+ if(QDELETED(ling) || !(src in (ling.innate_powers + ling.purchased_powers))) // checking both innate and purchased for full coverage
return
if(!HAS_TRAIT_FROM(user, TRAIT_DEATHCOMA, CHANGELING_TRAIT))
return
diff --git a/code/modules/antagonists/changeling/powers/spiders.dm b/code/modules/antagonists/changeling/powers/spiders.dm
index ce23fc0eb1b5..ff2ae524dc6f 100644
--- a/code/modules/antagonists/changeling/powers/spiders.dm
+++ b/code/modules/antagonists/changeling/powers/spiders.dm
@@ -1,3 +1,4 @@
+/* monkestation removal: replaced by teratomas in [monkestation\code\modules\antagonists\changeling\powers\teratomas.dm]
/datum/action/changeling/spiders
name = "Spread Infestation"
desc = "Our form divides, creating a cluster of eggs which will grow into a deadly arachnid. Costs 45 chemicals."
@@ -12,3 +13,4 @@
..()
new /obj/effect/mob_spawn/ghost_role/spider/bloody(user.loc)
return TRUE
+*/
diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm
index 1acb4fff68a0..2f72953f4a05 100644
--- a/code/modules/antagonists/cult/blood_magic.dm
+++ b/code/modules/antagonists/cult/blood_magic.dm
@@ -226,12 +226,12 @@
enable_text = span_cult("You prepare to horrify a target...")
disable_text = span_cult("You dispel the magic...")
-/datum/action/innate/cult/blood_spell/horror/InterceptClickOn(mob/living/caller, params, atom/clicked_on)
- var/turf/caller_turf = get_turf(caller)
+/datum/action/innate/cult/blood_spell/horror/InterceptClickOn(mob/living/user, params, atom/clicked_on)
+ var/turf/caller_turf = get_turf(user)
if(!isturf(caller_turf))
return FALSE
- if(!ishuman(clicked_on) || get_dist(caller, clicked_on) > 7)
+ if(!ishuman(clicked_on) || get_dist(user, clicked_on) > 7)
return FALSE
var/mob/living/carbon/human/human_clicked = clicked_on
@@ -240,23 +240,23 @@
return ..()
-/datum/action/innate/cult/blood_spell/horror/do_ability(mob/living/caller, mob/living/carbon/human/clicked_on)
+/datum/action/innate/cult/blood_spell/horror/do_ability(mob/living/user, mob/living/carbon/human/clicked_on)
clicked_on.set_hallucinations_if_lower(240 SECONDS)
- SEND_SOUND(caller, sound('sound/effects/ghost.ogg', FALSE, TRUE, 50))
+ SEND_SOUND(user, sound('sound/effects/ghost.ogg', FALSE, TRUE, 50))
var/image/sparkle_image = image('icons/effects/cult/effects.dmi', clicked_on, "bloodsparkles", ABOVE_MOB_LAYER)
clicked_on.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/cult, "cult_apoc", sparkle_image, NONE)
addtimer(CALLBACK(clicked_on, TYPE_PROC_REF(/atom/, remove_alt_appearance), "cult_apoc", TRUE), 4 MINUTES, TIMER_OVERRIDE|TIMER_UNIQUE)
- to_chat(caller, span_cultbold("[clicked_on] has been cursed with living nightmares!"))
+ to_chat(user, span_cultbold("[clicked_on] has been cursed with living nightmares!"))
charges--
desc = base_desc
desc += " Has [charges] use\s remaining."
build_all_button_icons()
if(charges <= 0)
- to_chat(caller, span_cult("You have exhausted the spell's power!"))
+ to_chat(user, span_cult("You have exhausted the spell's power!"))
qdel(src)
return TRUE
diff --git a/code/modules/antagonists/cult/cult_comms.dm b/code/modules/antagonists/cult/cult_comms.dm
index 2aeb662aa38e..19716589e1af 100644
--- a/code/modules/antagonists/cult/cult_comms.dm
+++ b/code/modules/antagonists/cult/cult_comms.dm
@@ -284,8 +284,8 @@
/datum/action/innate/cult/master/cultmark/IsAvailable(feedback = FALSE)
return ..() && COOLDOWN_FINISHED(src, cult_mark_cooldown)
-/datum/action/innate/cult/master/cultmark/InterceptClickOn(mob/caller, params, atom/clicked_on)
- var/turf/caller_turf = get_turf(caller)
+/datum/action/innate/cult/master/cultmark/InterceptClickOn(mob/user, params, atom/clicked_on)
+ var/turf/caller_turf = get_turf(user)
if(!isturf(caller_turf))
return FALSE
@@ -294,8 +294,8 @@
return ..()
-/datum/action/innate/cult/master/cultmark/do_ability(mob/living/caller, atom/clicked_on)
- var/datum/antagonist/cult/cultist = caller.mind.has_antag_datum(/datum/antagonist/cult, TRUE)
+/datum/action/innate/cult/master/cultmark/do_ability(mob/living/user, atom/clicked_on)
+ var/datum/antagonist/cult/cultist = user.mind.has_antag_datum(/datum/antagonist/cult, TRUE)
if(!cultist)
CRASH("[type] was casted by someone without a cult antag datum.")
@@ -304,17 +304,17 @@
CRASH("[type] was casted by a cultist without a cult team datum.")
if(cult_team.blood_target)
- to_chat(caller, span_cult("The cult has already designated a target!"))
+ to_chat(user, span_cult("The cult has already designated a target!"))
return FALSE
- if(cult_team.set_blood_target(clicked_on, caller, cult_mark_duration))
- unset_ranged_ability(caller, span_cult("The marking rite is complete! It will last for [DisplayTimeText(cult_mark_duration)] seconds."))
+ if(cult_team.set_blood_target(clicked_on, user, cult_mark_duration))
+ unset_ranged_ability(user, span_cult("The marking rite is complete! It will last for [DisplayTimeText(cult_mark_duration)] seconds."))
COOLDOWN_START(src, cult_mark_cooldown, cult_mark_cooldown_duration)
build_all_button_icons()
addtimer(CALLBACK(src, PROC_REF(build_all_button_icons)), cult_mark_cooldown_duration + 1)
return TRUE
- unset_ranged_ability(caller, span_cult("The marking rite failed!"))
+ unset_ranged_ability(user, span_cult("The marking rite failed!"))
return TRUE
/datum/action/innate/cult/ghostmark //Ghost version
@@ -414,44 +414,44 @@
/datum/action/innate/cult/master/pulse/IsAvailable(feedback = FALSE)
return ..() && COOLDOWN_FINISHED(src, pulse_cooldown)
-/datum/action/innate/cult/master/pulse/InterceptClickOn(mob/living/caller, params, atom/clicked_on)
- var/turf/caller_turf = get_turf(caller)
+/datum/action/innate/cult/master/pulse/InterceptClickOn(mob/living/user, params, atom/clicked_on)
+ var/turf/caller_turf = get_turf(user)
if(!isturf(caller_turf))
return FALSE
if(!(clicked_on in view(7, caller_turf)))
return FALSE
- if(clicked_on == caller)
+ if(clicked_on == user)
return FALSE
return ..()
-/datum/action/innate/cult/master/pulse/do_ability(mob/living/caller, atom/clicked_on)
+/datum/action/innate/cult/master/pulse/do_ability(mob/living/user, atom/clicked_on)
var/atom/throwee = throwee_ref?.resolve()
if(QDELETED(throwee))
- to_chat(caller, span_cult("You lost your target!"))
+ to_chat(user, span_cult("You lost your target!"))
throwee = null
throwee_ref = null
return FALSE
if(throwee)
if(get_dist(throwee, clicked_on) >= 16)
- to_chat(caller, span_cult("You can't teleport [clicked_on.p_them()] that far!"))
+ to_chat(user, span_cult("You can't teleport [clicked_on.p_them()] that far!"))
return FALSE
var/turf/throwee_turf = get_turf(throwee)
playsound(throwee_turf, 'sound/magic/exit_blood.ogg')
- new /obj/effect/temp_visual/cult/sparks(throwee_turf, caller.dir)
+ new /obj/effect/temp_visual/cult/sparks(throwee_turf, user.dir)
throwee.visible_message(
span_warning("A pulse of magic whisks [throwee] away!"),
span_cult("A pulse of blood magic whisks you away..."),
)
if(!do_teleport(throwee, clicked_on, channel = TELEPORT_CHANNEL_CULT))
- to_chat(caller, span_cult("The teleport fails!"))
+ to_chat(user, span_cult("The teleport fails!"))
throwee.visible_message(
span_warning("...Except they don't go very far"),
span_cult("...Except you don't appear to have moved very far."),
@@ -459,15 +459,15 @@
return FALSE
throwee_turf.Beam(clicked_on, icon_state = "sendbeam", time = 0.4 SECONDS)
- new /obj/effect/temp_visual/cult/sparks(get_turf(clicked_on), caller.dir)
+ new /obj/effect/temp_visual/cult/sparks(get_turf(clicked_on), user.dir)
throwee.visible_message(
span_warning("[throwee] appears suddenly in a pulse of magic!"),
span_cult("...And you appear elsewhere."),
)
COOLDOWN_START(src, pulse_cooldown, pulse_cooldown_duration)
- to_chat(caller, span_cult("A pulse of blood magic surges through you as you shift [throwee] through time and space."))
- caller.click_intercept = null
+ to_chat(user, span_cult("A pulse of blood magic surges through you as you shift [throwee] through time and space."))
+ user.click_intercept = null
throwee_ref = null
build_all_button_icons()
addtimer(CALLBACK(src, PROC_REF(build_all_button_icons)), pulse_cooldown_duration + 1)
@@ -479,13 +479,13 @@
var/mob/living/living_clicked = clicked_on
if(!IS_CULTIST(living_clicked))
return FALSE
- SEND_SOUND(caller, sound('sound/weapons/thudswoosh.ogg'))
- to_chat(caller, span_cultbold("You reach through the veil with your mind's eye and seize [clicked_on]! Click anywhere nearby to teleport [clicked_on.p_them()]!"))
+ SEND_SOUND(user, sound('sound/weapons/thudswoosh.ogg'))
+ to_chat(user, span_cultbold("You reach through the veil with your mind's eye and seize [clicked_on]! Click anywhere nearby to teleport [clicked_on.p_them()]!"))
throwee_ref = WEAKREF(clicked_on)
return TRUE
if(istype(clicked_on, /obj/structure/destructible/cult))
- to_chat(caller, span_cultbold("You reach through the veil with your mind's eye and lift [clicked_on]! Click anywhere nearby to teleport it!"))
+ to_chat(user, span_cultbold("You reach through the veil with your mind's eye and lift [clicked_on]! Click anywhere nearby to teleport it!"))
throwee_ref = WEAKREF(clicked_on)
return TRUE
diff --git a/code/modules/antagonists/fugitive/fugitive.dm b/code/modules/antagonists/fugitive/fugitive.dm
index 1ef9989dc607..7f2cc4a9f3d5 100644
--- a/code/modules/antagonists/fugitive/fugitive.dm
+++ b/code/modules/antagonists/fugitive/fugitive.dm
@@ -5,6 +5,8 @@
job_rank = ROLE_FUGITIVE
silent = TRUE //greet called by the event
show_in_antagpanel = FALSE
+ show_to_ghosts = TRUE
+ antagpanel_category = ANTAG_GROUP_FUGITIVES
prevent_roundtype_conversion = FALSE
antag_hud_name = "fugitive"
suicide_cry = "FOR FREEDOM!!"
diff --git a/code/modules/antagonists/fugitive/hunters/hunter.dm b/code/modules/antagonists/fugitive/hunters/hunter.dm
index 32d18998e748..84f26a142811 100644
--- a/code/modules/antagonists/fugitive/hunters/hunter.dm
+++ b/code/modules/antagonists/fugitive/hunters/hunter.dm
@@ -4,6 +4,8 @@
roundend_category = "Fugitive"
silent = TRUE //greet called by the spawn
show_in_antagpanel = FALSE
+ show_to_ghosts = TRUE
+ antagpanel_category = ANTAG_GROUP_HUNTERS
prevent_roundtype_conversion = FALSE
antag_hud_name = "fugitive_hunter"
suicide_cry = "FOR GLORY!!"
@@ -122,7 +124,7 @@
else//from here on out, hunters lost because they did not capture any fugitive dead or alive. there are different levels of getting beat though:
if(!fugitives_dead)//all fugitives survived
return FUGITIVE_RESULT_MAJOR_FUGITIVE
- else if(fugitives_dead < fugitives_counted)//at least ANY fugitive lived
+ else if(length(fugitives_dead) < length(fugitives_counted))//at least ANY fugitive lived
return FUGITIVE_RESULT_FUGITIVE_VICTORY
else if(!hunters_dead)//all fugitives died, but none were taken in by the hunters. minor win
return FUGITIVE_RESULT_MINOR_FUGITIVE
diff --git a/code/modules/antagonists/fugitive/hunters/hunter_gear.dm b/code/modules/antagonists/fugitive/hunters/hunter_gear.dm
index 596420ba8f4c..47dbcbb21b25 100644
--- a/code/modules/antagonists/fugitive/hunters/hunter_gear.dm
+++ b/code/modules/antagonists/fugitive/hunters/hunter_gear.dm
@@ -138,6 +138,19 @@
if(gored)
name = gored.real_name
+ AddComponent(
+ /datum/component/blood_walk,\
+ blood_type = /obj/effect/decal/cleanable/blood,\
+ blood_spawn_chance = 66.6,\
+ max_blood = INFINITY,\
+ )
+
+ AddComponent(/datum/component/bloody_spreader,\
+ blood_left = INFINITY,\
+ blood_dna = list("meaty DNA" = "MT-"),\
+ diseases = null,\
+ )
+
/obj/structure/bouncy_castle/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
switch(damage_type)
if(BRUTE)
diff --git a/code/modules/antagonists/heretic/heretic_monsters.dm b/code/modules/antagonists/heretic/heretic_monsters.dm
index 4e76b11c90c6..db220e93bd5d 100644
--- a/code/modules/antagonists/heretic/heretic_monsters.dm
+++ b/code/modules/antagonists/heretic/heretic_monsters.dm
@@ -8,6 +8,7 @@
antag_hud_name = "heretic_beast"
suicide_cry = "MY MASTER SMILES UPON ME!!"
show_in_antagpanel = FALSE
+ antag_flags = parent_type::antag_flags | FLAG_ANTAG_CAP_IGNORE // monkestation addition
/// Our master (a heretic)'s mind.
var/datum/mind/master
diff --git a/code/modules/antagonists/heretic/items/heretic_blades.dm b/code/modules/antagonists/heretic/items/heretic_blades.dm
index c83fde235797..ac2a6400ae56 100644
--- a/code/modules/antagonists/heretic/items/heretic_blades.dm
+++ b/code/modules/antagonists/heretic/items/heretic_blades.dm
@@ -90,6 +90,23 @@
inhand_icon_state = "flesh_blade"
after_use_message = "The Marshal hears your call..."
+/obj/item/melee/sickly_blade/flesh/Initialize(mapload)
+ . = ..()
+
+ AddComponent(
+ /datum/component/blood_walk,\
+ blood_type = /obj/effect/decal/cleanable/blood,\
+ blood_spawn_chance = 66.6,\
+ max_blood = INFINITY,\
+ )
+
+ AddComponent(
+ /datum/component/bloody_spreader,\
+ blood_left = INFINITY,\
+ blood_dna = list("Unknown DNA" = "X*"),\
+ diseases = null,\
+ )
+
// Path of Void's blade
/obj/item/melee/sickly_blade/void
name = "\improper void blade"
diff --git a/code/modules/antagonists/heretic/knowledge/moon_lore.dm b/code/modules/antagonists/heretic/knowledge/moon_lore.dm
index 05195b9eb00a..b8eb5849076e 100644
--- a/code/modules/antagonists/heretic/knowledge/moon_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/moon_lore.dm
@@ -162,7 +162,7 @@
"upgraded path of moon blades", \
)
target.emote(pick("giggle", "laugh"))
- target.mob_mood.set_sanity(target.mob_mood.sanity - 10)
+ target.mob_mood?.set_sanity(target.mob_mood.sanity - 10)
/datum/heretic_knowledge/spell/moon_ringleader
name = "Ringleaders Rise"
diff --git a/code/modules/antagonists/heretic/knowledge/rust_lore.dm b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
index 63d503a8ed38..dbe9e4d77178 100644
--- a/code/modules/antagonists/heretic/knowledge/rust_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
@@ -378,7 +378,7 @@
max_dist = max(max_dist, get_dist(found_turf, centre) + 1)
for(var/turf/nearby_turf as anything in spiral_range_turfs(max_dist, centre, FALSE))
- if(nearby_turf in rusted_turfs || is_type_in_typecache(nearby_turf, blacklisted_turfs))
+ if((nearby_turf in rusted_turfs) || is_type_in_typecache(nearby_turf, blacklisted_turfs))
continue
for(var/turf/line_turf as anything in get_line(nearby_turf, centre))
diff --git a/code/modules/antagonists/heretic/knowledge/starting_lore.dm b/code/modules/antagonists/heretic/knowledge/starting_lore.dm
index e0a153d93b6b..c36faa8a2cff 100644
--- a/code/modules/antagonists/heretic/knowledge/starting_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/starting_lore.dm
@@ -106,27 +106,24 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
/datum/heretic_knowledge/living_heart/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
var/datum/antagonist/heretic/our_heretic = IS_HERETIC(user)
var/obj/item/organ/our_living_heart = user.get_organ_slot(our_heretic.living_heart_organ_slot)
- // Obviously you need a heart in your chest to do a ritual on your... heart
- if(!our_living_heart)
- loc.balloon_alert(user, "ritual failed, you have no [our_heretic.living_heart_organ_slot]!") // "you have no heart!"
- return FALSE
- // For sanity's sake, check if they've got a heart -
+ // For sanity's sake, check if they've got a living heart -
// even though it's not invokable if you already have one,
// they may have gained one unexpectantly in between now and then
- if(HAS_TRAIT(our_living_heart, TRAIT_LIVING_HEART))
- loc.balloon_alert(user, "ritual failed, already have a living heart!")
- return FALSE
-
- // By this point they are making a new heart
- // If their current heart is organic / not synthetic, we can continue the ritual as normal
- if(is_valid_heart(our_living_heart))
- return TRUE
+ if(!QDELETED(our_living_heart))
+ if(HAS_TRAIT(our_living_heart, TRAIT_LIVING_HEART))
+ loc.balloon_alert(user, "ritual failed, already have a living heart!")
+ return FALSE
+
+ // By this point they are making a new heart
+ // If their current heart is organic / not synthetic, we can continue the ritual as normal
+ if(is_valid_heart(our_living_heart))
+ return TRUE
- // If their current heart is not organic / is synthetic, they need an organic replacement
- // ...But if our organ-to-be-replaced is unremovable, we're screwed
- if(our_living_heart.organ_flags & ORGAN_UNREMOVABLE)
- loc.balloon_alert(user, "ritual failed, [our_heretic.living_heart_organ_slot] unremovable!") // "heart unremovable!"
- return FALSE
+ // If their current heart is not organic / is synthetic, they need an organic replacement
+ // ...But if our organ-to-be-replaced is unremovable, we're screwed
+ if(our_living_heart.organ_flags & ORGAN_UNREMOVABLE)
+ loc.balloon_alert(user, "ritual failed, [our_heretic.living_heart_organ_slot] unremovable!") // "heart unremovable!"
+ return FALSE
// Otherwise, seek out a replacement in our atoms
for(var/obj/item/organ/nearby_organ in atoms)
@@ -148,17 +145,21 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
// Our heart is robotic or synthetic - we need to replace it, and we fortunately should have one by here
if(!is_valid_heart(our_new_heart))
var/obj/item/organ/our_replacement_heart = locate(required_organ_type) in selected_atoms
- if(our_replacement_heart)
+ if(!our_replacement_heart)
+ CRASH("[type] required a replacement organic heart in on_finished_recipe, but did not find one.")
+ // Repair the organic heart, if needed, to just below the high threshold
+ if(our_replacement_heart.damage >= our_replacement_heart.high_threshold)
+ our_replacement_heart.set_organ_damage(our_replacement_heart.high_threshold - 1)
+ // And now, put our organic heart in its place
+ our_replacement_heart.Insert(user, TRUE, TRUE)
+ if(our_new_heart)
// Throw our current heart out of our chest, violently
user.visible_message(span_boldwarning("[user]'s [our_new_heart.name] bursts suddenly out of [user.p_their()] chest!"))
INVOKE_ASYNC(user, TYPE_PROC_REF(/mob, emote), "scream")
user.apply_damage(20, BRUTE, BODY_ZONE_CHEST)
- // And put our organic heart in its place
- our_replacement_heart.Insert(user, TRUE, TRUE)
+ selected_atoms -= our_new_heart // so we don't delete our old heart while we dramatically toss is out
our_new_heart.throw_at(get_edge_target_turf(user, pick(GLOB.alldirs)), 2, 2)
- our_new_heart = our_replacement_heart
- else
- CRASH("[type] required a replacement organic heart in on_finished_recipe, but did not find one.")
+ our_new_heart = our_replacement_heart
if(!our_new_heart)
CRASH("[type] somehow made it to on_finished_recipe without a heart. What?")
@@ -179,7 +180,7 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
/// Checks if the passed heart is a valid heart to become a living heart
/datum/heretic_knowledge/living_heart/proc/is_valid_heart(obj/item/organ/new_heart)
- if(!new_heart)
+ if(QDELETED(new_heart))
return FALSE
if(!new_heart.useable)
return FALSE
diff --git a/code/modules/antagonists/heretic/magic/aggressive_spread.dm b/code/modules/antagonists/heretic/magic/aggressive_spread.dm
index c15ae041a47a..de1233382f64 100644
--- a/code/modules/antagonists/heretic/magic/aggressive_spread.dm
+++ b/code/modules/antagonists/heretic/magic/aggressive_spread.dm
@@ -17,11 +17,7 @@
aoe_radius = 3
/datum/action/cooldown/spell/aoe/rust_conversion/get_things_to_cast_on(atom/center)
- var/list/things = list()
- for(var/turf/nearby_turf in range(aoe_radius, center))
- things += nearby_turf
-
- return things
+ return RANGE_TURFS(aoe_radius, center)
/datum/action/cooldown/spell/aoe/rust_conversion/cast_on_thing_in_aoe(turf/victim, atom/caster)
// We have less chance of rusting stuff that's further
diff --git a/code/modules/antagonists/heretic/magic/ash_ascension.dm b/code/modules/antagonists/heretic/magic/ash_ascension.dm
index 0de92c49c22e..c9b6b2ce0458 100644
--- a/code/modules/antagonists/heretic/magic/ash_ascension.dm
+++ b/code/modules/antagonists/heretic/magic/ash_ascension.dm
@@ -53,7 +53,8 @@
return
for(var/turf/nearby_turf as anything in RANGE_TURFS(1, owner))
- new /obj/effect/hotspot(nearby_turf)
+ var/obj/effect/hotspot/flame_tile = locate(nearby_turf) || new(nearby_turf)
+ flame_tile.alpha = 125
nearby_turf.hotspot_expose(750, 25 * seconds_per_tick, 1)
for(var/mob/living/fried_living in nearby_turf.contents - owner)
fried_living.apply_damage(2.5 * seconds_per_tick, BURN)
@@ -86,7 +87,8 @@
/datum/action/cooldown/spell/fire_cascade/proc/fire_cascade(atom/centre, flame_radius = 1)
for(var/i in 0 to flame_radius)
for(var/turf/nearby_turf as anything in spiral_range_turfs(i + 1, centre))
- new /obj/effect/hotspot(nearby_turf)
+ var/obj/effect/hotspot/flame_tile = locate(nearby_turf) || new(nearby_turf)
+ flame_tile.alpha = 125
nearby_turf.hotspot_expose(750, 50, 1)
for(var/mob/living/fried_living in nearby_turf.contents - owner)
fried_living.apply_damage(5, BURN)
@@ -148,7 +150,7 @@
if(L.can_block_magic())
L.visible_message(span_danger("The spell bounces off of [L]!"), span_danger("The spell bounces off of you!"))
continue
- if(L in hit_list || L == source)
+ if((L in hit_list) || L == source)
continue
hit_list += L
L.adjustFireLoss(20)
diff --git a/code/modules/antagonists/heretic/magic/furious_steel.dm b/code/modules/antagonists/heretic/magic/furious_steel.dm
index 5899ca53fd44..da54c1904b15 100644
--- a/code/modules/antagonists/heretic/magic/furious_steel.dm
+++ b/code/modules/antagonists/heretic/magic/furious_steel.dm
@@ -43,12 +43,12 @@
unset_click_ability(source, refund_cooldown = TRUE)
-/datum/action/cooldown/spell/pointed/projectile/furious_steel/InterceptClickOn(mob/living/caller, params, atom/target)
+/datum/action/cooldown/spell/pointed/projectile/furious_steel/InterceptClickOn(mob/living/user, params, atom/target)
// Let the caster prioritize using items like guns over blade casts
- if(caller.get_active_held_item())
+ if(user.get_active_held_item())
return FALSE
// Let the caster prioritize melee attacks like punches and shoves over blade casts
- if(get_dist(caller, target) <= 1)
+ if(get_dist(user, target) <= 1)
return FALSE
return ..()
diff --git a/code/modules/antagonists/heretic/magic/moon_parade.dm b/code/modules/antagonists/heretic/magic/moon_parade.dm
index 409e55bf9261..b2bfa13d48a2 100644
--- a/code/modules/antagonists/heretic/magic/moon_parade.dm
+++ b/code/modules/antagonists/heretic/magic/moon_parade.dm
@@ -86,7 +86,7 @@
victim.add_mood_event("Moon Insanity", /datum/mood_event/moon_insanity)
victim.cause_hallucination(/datum/hallucination/delusion/preset/moon, name)
- victim.mob_mood.set_sanity(victim.mob_mood.sanity - 20)
+ victim.mob_mood?.set_sanity(victim.mob_mood.sanity - 20)
/obj/projectile/moon_parade/Destroy()
for(var/mob/living/leftover_mob as anything in mobs_hit)
diff --git a/code/modules/antagonists/heretic/magic/rust_charge.dm b/code/modules/antagonists/heretic/magic/rust_charge.dm
index 0d693b0de86e..e944b827582b 100644
--- a/code/modules/antagonists/heretic/magic/rust_charge.dm
+++ b/code/modules/antagonists/heretic/magic/rust_charge.dm
@@ -46,4 +46,4 @@
SSexplosions.med_mov_atom += target
INVOKE_ASYNC(src, PROC_REF(DestroySurroundings), source)
- hit_target(source, target, charge_damage)
+ try_hit_target(source, target) // monkestation edit: fix runtime error
diff --git a/code/modules/antagonists/heretic/status_effects/mark_effects.dm b/code/modules/antagonists/heretic/status_effects/mark_effects.dm
index 1771d7079e8d..b810dc8e7dd5 100644
--- a/code/modules/antagonists/heretic/status_effects/mark_effects.dm
+++ b/code/modules/antagonists/heretic/status_effects/mark_effects.dm
@@ -107,15 +107,16 @@
ORGAN_SLOT_HEART,
)
- // Roughly 75% of their organs will take a bit of damage
+ // Roughly 25% of their organs will take a bit of damage
for(var/organ_slot in organs_to_damage)
- if(prob(75))
+ if(prob(25)) //monkestation edit begin : Changes rust to not be busted as shit
carbon_owner.adjustOrganLoss(organ_slot, 20)
- // And roughly 75% of their items will take a smack, too
+ // And roughly 50% of their items will take a smack, too
for(var/obj/item/thing in carbon_owner.get_all_gear())
- if(!QDELETED(thing) && prob(75))
- thing.take_damage(100)
+ if(!QDELETED(thing) && prob(50))
+ if(!istype(thing, /obj/item/card/id))
+ thing.take_damage(50) //monkestation edit end
return ..()
@@ -235,6 +236,7 @@
return ..()
/datum/status_effect/eldritch/cosmic/on_effect()
+ owner.adjust_confusion(7 SECONDS) //monkestation edit
new teleport_effect(get_turf(owner))
new /obj/effect/forcefield/cosmic_field(get_turf(owner))
do_teleport(
diff --git a/code/modules/antagonists/hypnotized/hypnotized.dm b/code/modules/antagonists/hypnotized/hypnotized.dm
index 2ee17b671aaa..4f1f49aa3be7 100644
--- a/code/modules/antagonists/hypnotized/hypnotized.dm
+++ b/code/modules/antagonists/hypnotized/hypnotized.dm
@@ -6,7 +6,7 @@
antag_hud_name = "brainwashed"
ui_name = "AntagInfoBrainwashed"
show_in_antagpanel = TRUE
- antagpanel_category = "Other"
+ antagpanel_category = ANTAG_GROUP_CREW
show_name_in_check_antagonists = TRUE
count_against_dynamic_roll_chance = FALSE
diff --git a/code/modules/antagonists/magic_servant/servant.dm b/code/modules/antagonists/magic_servant/servant.dm
index f6ecaf80cf6c..8851776213fe 100644
--- a/code/modules/antagonists/magic_servant/servant.dm
+++ b/code/modules/antagonists/magic_servant/servant.dm
@@ -3,6 +3,7 @@
show_in_roundend = FALSE
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
+ antag_flags = parent_type::antag_flags | FLAG_ANTAG_CAP_IGNORE // monkestation addition
/datum/antagonist/magic_servant/proc/setup_master(mob/M)
var/datum/objective/O = new("Serve [M.real_name].")
diff --git a/code/modules/antagonists/malf_ai/malf_ai.dm b/code/modules/antagonists/malf_ai/malf_ai.dm
index 863b32e28d8f..6adcf901aaa3 100644
--- a/code/modules/antagonists/malf_ai/malf_ai.dm
+++ b/code/modules/antagonists/malf_ai/malf_ai.dm
@@ -57,8 +57,6 @@
/// Generates a complete set of malf AI objectives up to the traitor objective limit.
/datum/antagonist/malf_ai/proc/forge_ai_objectives()
- objectives.Cut()
-
if(prob(PROB_SPECIAL))
forge_special_objective()
diff --git a/code/modules/antagonists/malf_ai/malf_ai_modules.dm b/code/modules/antagonists/malf_ai/malf_ai_modules.dm
index 4fef406a5fbe..9b48f836afef 100644
--- a/code/modules/antagonists/malf_ai/malf_ai_modules.dm
+++ b/code/modules/antagonists/malf_ai/malf_ai_modules.dm
@@ -442,19 +442,19 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
. = ..()
desc = "[desc] It has [uses] use\s remaining."
-/datum/action/innate/ai/ranged/override_machine/do_ability(mob/living/caller, atom/clicked_on)
- if(caller.incapacitated())
- unset_ranged_ability(caller)
+/datum/action/innate/ai/ranged/override_machine/do_ability(mob/living/user, atom/clicked_on)
+ if(user.incapacitated())
+ unset_ranged_ability(user)
return FALSE
if(!ismachinery(clicked_on))
- to_chat(caller, span_warning("You can only animate machines!"))
+ to_chat(user, span_warning("You can only animate machines!"))
return FALSE
var/obj/machinery/clicked_machine = clicked_on
if(!clicked_machine.can_be_overridden() || is_type_in_typecache(clicked_machine, GLOB.blacklisted_malf_machines))
- to_chat(caller, span_warning("That machine can't be overridden!"))
+ to_chat(user, span_warning("That machine can't be overridden!"))
return FALSE
- caller.playsound_local(caller, 'sound/misc/interference.ogg', 50, FALSE, use_reverb = FALSE)
+ user.playsound_local(user, 'sound/misc/interference.ogg', 50, FALSE, use_reverb = FALSE)
adjust_uses(-1)
if(uses)
@@ -462,15 +462,15 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
build_all_button_icons()
clicked_machine.audible_message(span_userdanger("You hear a loud electrical buzzing sound coming from [clicked_machine]!"))
- addtimer(CALLBACK(src, PROC_REF(animate_machine), caller, clicked_machine), 5 SECONDS) //kabeep!
- unset_ranged_ability(caller, span_danger("Sending override signal..."))
+ addtimer(CALLBACK(src, PROC_REF(animate_machine), user, clicked_machine), 5 SECONDS) //kabeep!
+ unset_ranged_ability(user, span_danger("Sending override signal..."))
return TRUE
-/datum/action/innate/ai/ranged/override_machine/proc/animate_machine(mob/living/caller, obj/machinery/to_animate)
+/datum/action/innate/ai/ranged/override_machine/proc/animate_machine(mob/living/user, obj/machinery/to_animate)
if(QDELETED(to_animate))
return
- new /mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(to_animate), to_animate, caller, TRUE)
+ new /mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(to_animate), to_animate, user, TRUE)
/// Destroy RCDs: Detonates all non-cyborg RCDs on the station.
/datum/ai_module/destructive/destroy_rcd
@@ -519,38 +519,38 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
..()
desc = "[desc] It has [uses] use\s remaining."
-/datum/action/innate/ai/ranged/overload_machine/proc/detonate_machine(mob/living/caller, obj/machinery/to_explode)
+/datum/action/innate/ai/ranged/overload_machine/proc/detonate_machine(mob/living/user, obj/machinery/to_explode)
if(QDELETED(to_explode))
return
var/turf/machine_turf = get_turf(to_explode)
- message_admins("[ADMIN_LOOKUPFLW(caller)] overloaded [to_explode.name] ([to_explode.type]) at [ADMIN_VERBOSEJMP(machine_turf)].")
- caller.log_message("overloaded [to_explode.name] ([to_explode.type])", LOG_ATTACK)
+ message_admins("[ADMIN_LOOKUPFLW(user)] overloaded [to_explode.name] ([to_explode.type]) at [ADMIN_VERBOSEJMP(machine_turf)].")
+ user.log_message("overloaded [to_explode.name] ([to_explode.type])", LOG_ATTACK)
explosion(to_explode, heavy_impact_range = 2, light_impact_range = 3)
if(!QDELETED(to_explode)) //to check if the explosion killed it before we try to delete it
qdel(to_explode)
-/datum/action/innate/ai/ranged/overload_machine/do_ability(mob/living/caller, atom/clicked_on)
- if(caller.incapacitated())
- unset_ranged_ability(caller)
+/datum/action/innate/ai/ranged/overload_machine/do_ability(mob/living/user, atom/clicked_on)
+ if(user.incapacitated())
+ unset_ranged_ability(user)
return FALSE
if(!ismachinery(clicked_on))
- to_chat(caller, span_warning("You can only overload machines!"))
+ to_chat(user, span_warning("You can only overload machines!"))
return FALSE
var/obj/machinery/clicked_machine = clicked_on
if(is_type_in_typecache(clicked_machine, GLOB.blacklisted_malf_machines))
- to_chat(caller, span_warning("You cannot overload that device!"))
+ to_chat(user, span_warning("You cannot overload that device!"))
return FALSE
- caller.playsound_local(caller, SFX_SPARKS, 50, 0)
+ user.playsound_local(user, SFX_SPARKS, 50, 0)
adjust_uses(-1)
if(uses)
desc = "[initial(desc)] It has [uses] use\s remaining."
build_all_button_icons()
clicked_machine.audible_message(span_userdanger("You hear a loud electrical buzzing sound coming from [clicked_machine]!"))
- addtimer(CALLBACK(src, PROC_REF(detonate_machine), caller, clicked_machine), 5 SECONDS) //kaboom!
- unset_ranged_ability(caller, span_danger("Overcharging machine..."))
+ addtimer(CALLBACK(src, PROC_REF(detonate_machine), user, clicked_machine), 5 SECONDS) //kaboom!
+ unset_ranged_ability(user, span_danger("Overcharging machine..."))
return TRUE
/// Blackout: Overloads a random number of lights across the station. Three uses.
@@ -1050,7 +1050,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
. = ..()
desc = "[desc] It has [uses] use\s remaining."
-/datum/action/innate/ai/ranged/emag/do_ability(mob/living/caller, atom/clicked_on)
+/datum/action/innate/ai/ranged/emag/do_ability(mob/living/user, atom/clicked_on)
// Only things with of or subtyped of any of these types may be remotely emagged
var/static/list/compatable_typepaths = list(
@@ -1062,13 +1062,13 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
/mob/living/silicon,
)
- if (!isAI(caller))
+ if (!isAI(user))
return FALSE
- var/mob/living/silicon/ai/ai_caller = caller
+ var/mob/living/silicon/ai/ai_caller = user
if(ai_caller.incapacitated())
- unset_ranged_ability(caller)
+ unset_ranged_ability(user)
return FALSE
if (!ai_caller.can_see(clicked_on))
@@ -1147,15 +1147,15 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
. = ..()
desc = "[desc] It has [uses] use\s remaining."
-/datum/action/innate/ai/ranged/core_tilt/do_ability(mob/living/caller, atom/clicked_on)
+/datum/action/innate/ai/ranged/core_tilt/do_ability(mob/living/user, atom/clicked_on)
if (!COOLDOWN_FINISHED(src, time_til_next_tilt))
- caller.balloon_alert(caller, "on cooldown!")
+ user.balloon_alert(user, "on cooldown!")
return FALSE
- if (!isAI(caller))
+ if (!isAI(user))
return FALSE
- var/mob/living/silicon/ai/ai_caller = caller
+ var/mob/living/silicon/ai/ai_caller = user
if (ai_caller.incapacitated() || !isturf(ai_caller.loc))
return FALSE
@@ -1198,8 +1198,8 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
return ai_caller.fall_and_crush(target, MALF_AI_ROLL_DAMAGE, MALF_AI_ROLL_CRIT_CHANCE, null, paralyze_time, picked_dir, rotation = get_rotation_from_dir(picked_dir))
/// Used in our radial menu, state-checking proc after the radial menu sleeps
-/datum/action/innate/ai/ranged/core_tilt/proc/radial_check(mob/living/silicon/ai/caller)
- if (QDELETED(caller) || caller.incapacitated() || caller.stat == DEAD)
+/datum/action/innate/ai/ranged/core_tilt/proc/radial_check(mob/living/silicon/ai/user)
+ if (QDELETED(user) || user.incapacitated() || user.stat == DEAD)
return FALSE
if (uses <= 0)
@@ -1240,14 +1240,14 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
. = ..()
desc = "[desc] It has [uses] use\s remaining."
-/datum/action/innate/ai/ranged/remote_vendor_tilt/do_ability(mob/living/caller, atom/clicked_on)
+/datum/action/innate/ai/ranged/remote_vendor_tilt/do_ability(mob/living/user, atom/clicked_on)
- if (!isAI(caller))
+ if (!isAI(user))
return FALSE
- var/mob/living/silicon/ai/ai_caller = caller
+ var/mob/living/silicon/ai/ai_caller = user
if(ai_caller.incapacitated())
- unset_ranged_ability(caller)
+ unset_ranged_ability(user)
return FALSE
if(!isvendor(clicked_on))
@@ -1268,7 +1268,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
clicked_vendor.balloon_alert(ai_caller, "inoperable!")
return FALSE
- var/picked_dir_string = show_radial_menu(ai_caller, clicked_vendor, GLOB.all_radial_directions, custom_check = CALLBACK(src, PROC_REF(radial_check), caller, clicked_vendor))
+ var/picked_dir_string = show_radial_menu(ai_caller, clicked_vendor, GLOB.all_radial_directions, custom_check = CALLBACK(src, PROC_REF(radial_check), user, clicked_vendor))
if (isnull(picked_dir_string))
return FALSE
var/picked_dir = text2dir(picked_dir_string)
@@ -1288,7 +1288,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
desc = "[initial(desc)] It has [uses] use\s remaining."
build_all_button_icons()
- unset_ranged_ability(caller, span_danger("Tilting..."))
+ unset_ranged_ability(user, span_danger("Tilting..."))
return TRUE
/datum/action/innate/ai/ranged/remote_vendor_tilt/proc/do_vendor_tilt(obj/machinery/vending/vendor, turf/target)
@@ -1301,8 +1301,8 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
vendor.tilt(target, MALF_VENDOR_TIPPING_CRIT_CHANCE)
/// Used in our radial menu, state-checking proc after the radial menu sleeps
-/datum/action/innate/ai/ranged/remote_vendor_tilt/proc/radial_check(mob/living/silicon/ai/caller, obj/machinery/vending/clicked_vendor)
- if (QDELETED(caller) || caller.incapacitated() || caller.stat == DEAD)
+/datum/action/innate/ai/ranged/remote_vendor_tilt/proc/radial_check(mob/living/silicon/ai/user, obj/machinery/vending/clicked_vendor)
+ if (QDELETED(user) || user.incapacitated() || user.stat == DEAD)
return FALSE
if (QDELETED(clicked_vendor))
@@ -1311,8 +1311,8 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
if (uses <= 0)
return FALSE
- if (!caller.can_see(clicked_vendor))
- to_chat(caller, span_warning("Lost sight of [clicked_vendor]!"))
+ if (!user.can_see(clicked_vendor))
+ to_chat(user, span_warning("Lost sight of [clicked_vendor]!"))
return FALSE
return TRUE
diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm
index 82d59ba02925..a94821f71f86 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm
@@ -618,6 +618,9 @@ GLOBAL_VAR(station_nuke_source)
* Helper proc that handles gibbing someone who has been nuked.
*/
/proc/nuke_gib(mob/living/gibbed, atom/source)
+ if(HAS_TRAIT(gibbed, TRAIT_NUKEIMMUNE))
+ return FALSE
+
if(istype(gibbed.loc, /obj/structure/closet/secure_closet/freezer))
var/obj/structure/closet/secure_closet/freezer/freezer = gibbed.loc
if(!freezer.jones)
diff --git a/code/modules/antagonists/obsessed/obsessed.dm b/code/modules/antagonists/obsessed/obsessed.dm
index 585867d5767b..a079f95cfa0e 100644
--- a/code/modules/antagonists/obsessed/obsessed.dm
+++ b/code/modules/antagonists/obsessed/obsessed.dm
@@ -1,8 +1,9 @@
/datum/antagonist/obsessed
name = "Obsessed"
show_in_antagpanel = TRUE
- antagpanel_category = "Other"
+ antagpanel_category = ANTAG_GROUP_CREW
job_rank = ROLE_OBSESSED
+ show_to_ghosts = TRUE
antag_hud_name = "obsessed"
show_name_in_check_antagonists = TRUE
roundend_category = "obsessed"
diff --git a/code/modules/antagonists/paradox_clone/paradox_clone.dm b/code/modules/antagonists/paradox_clone/paradox_clone.dm
index 8ec0a75e8c9f..0773d949d25c 100644
--- a/code/modules/antagonists/paradox_clone/paradox_clone.dm
+++ b/code/modules/antagonists/paradox_clone/paradox_clone.dm
@@ -2,7 +2,9 @@
name = "\improper Paradox Clone"
roundend_category = "Paradox Clone"
job_rank = ROLE_PARADOX_CLONE
+ antagpanel_category = ANTAG_GROUP_PARADOX
antag_hud_name = "paradox_clone"
+ show_to_ghosts = TRUE
suicide_cry = "THERE CAN BE ONLY ONE!!"
preview_outfit = /datum/outfit/paradox_clone
diff --git a/code/modules/antagonists/pirate/pirate.dm b/code/modules/antagonists/pirate/pirate.dm
index d499a8034d80..8812de960ddd 100644
--- a/code/modules/antagonists/pirate/pirate.dm
+++ b/code/modules/antagonists/pirate/pirate.dm
@@ -7,6 +7,7 @@
show_to_ghosts = TRUE
suicide_cry = "FOR ME MATEYS!!"
hijack_speed = 2 // That is without doubt the worst pirate I have ever seen.
+ antag_flags = parent_type::antag_flags | FLAG_ANTAG_CAP_TEAM // monkestation addition
var/datum/team/pirate/crew
/datum/antagonist/pirate/greet()
diff --git a/code/modules/antagonists/pyro_slime/pyro_slime.dm b/code/modules/antagonists/pyro_slime/pyro_slime.dm
index aed278d261d9..7f63d0f5482f 100644
--- a/code/modules/antagonists/pyro_slime/pyro_slime.dm
+++ b/code/modules/antagonists/pyro_slime/pyro_slime.dm
@@ -5,6 +5,7 @@
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
+ antag_flags = FLAG_ANTAG_CAP_IGNORE // monkestation addition
/datum/antagonist/pyro_slime/on_gain()
forge_objectives()
diff --git a/code/modules/antagonists/revolution/enemy_of_the_revolution.dm b/code/modules/antagonists/revolution/enemy_of_the_revolution.dm
index 93a205a02cd2..0e20463d4898 100644
--- a/code/modules/antagonists/revolution/enemy_of_the_revolution.dm
+++ b/code/modules/antagonists/revolution/enemy_of_the_revolution.dm
@@ -7,6 +7,7 @@
name = "\improper Enemy of the Revolution"
show_in_antagpanel = FALSE
suicide_cry = "FOR NANOTRASEN, NOW AND FOREVER!!"
+ antag_flags = parent_type::antag_flags | FLAG_ANTAG_CAP_IGNORE // monkestation addition
/datum/antagonist/enemy_of_the_revolution/forge_objectives()
var/datum/objective/survive/survive = new
diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm
index 85c7df3a7a3e..f43549932b46 100644
--- a/code/modules/antagonists/revolution/revolution.dm
+++ b/code/modules/antagonists/revolution/revolution.dm
@@ -541,7 +541,7 @@
if (!(player_mind.assigned_role.departments_bitflags & (DEPARTMENT_BITFLAG_SECURITY|DEPARTMENT_BITFLAG_COMMAND)))
continue
- if (player_mind in ex_revs + ex_headrevs)
+ if (player_mind in (ex_revs + ex_headrevs))
continue
if (!istype(player))
diff --git a/code/modules/antagonists/sentient_creature/sentient_creature.dm b/code/modules/antagonists/sentient_creature/sentient_creature.dm
index 4b68a288be12..000fa8b51aff 100644
--- a/code/modules/antagonists/sentient_creature/sentient_creature.dm
+++ b/code/modules/antagonists/sentient_creature/sentient_creature.dm
@@ -4,6 +4,7 @@
show_in_roundend = FALSE
count_against_dynamic_roll_chance = FALSE
ui_name = "AntagInfoSentient"
+ antag_flags = FLAG_ANTAG_CAP_IGNORE // monkestation addition
/datum/antagonist/sentient_creature/get_preview_icon()
var/icon/final_icon = icon('icons/mob/simple/pets.dmi', "corgi")
diff --git a/code/modules/antagonists/space_dragon/carp_rift.dm b/code/modules/antagonists/space_dragon/carp_rift.dm
index 88df5c03a197..a2a6eb12ad0b 100644
--- a/code/modules/antagonists/space_dragon/carp_rift.dm
+++ b/code/modules/antagonists/space_dragon/carp_rift.dm
@@ -18,13 +18,13 @@
if(!dragon)
return
var/area/rift_location = get_area(owner)
- if(!(rift_location.area_flags & VALID_TERRITORY))
- to_chat(owner, span_warning("You can't summon a rift here! Try summoning somewhere secure within the station!"))
+ if(!(rift_location in dragon.chosen_rift_areas))
+ owner.balloon_alert(owner, "can't summon a rift here!")
return
for(var/obj/structure/carp_rift/rift as anything in dragon.rift_list)
var/area/used_location = get_area(rift)
if(used_location == rift_location)
- to_chat(owner, span_warning("You've already summoned a rift in this area! You have to summon again somewhere else!"))
+ owner.balloon_alert(owner, "already summoned a rift here!")
return
var/turf/rift_spawn_turf = get_turf(dragon)
if(isopenspaceturf(rift_spawn_turf))
@@ -70,7 +70,7 @@
light_color = LIGHT_COLOR_PURPLE
light_outer_range = 10
anchored = TRUE
- density = FALSE
+ density = TRUE
plane = MASSIVE_OBJ_PLANE
/// The amount of time the rift has charged for.
var/time_charged = 0
@@ -83,11 +83,13 @@
/// Current charge state of the rift.
var/charge_state = CHARGE_ONGOING
/// The interval for adding additional space carp spawns to the rift.
- var/carp_interval = 60
+ var/carp_interval = 45
/// The time since an extra carp was added to the ghost role spawning pool.
var/last_carp_inc = 0
/// A list of all the ckeys which have used this carp rift to spawn in as carps.
var/list/ckey_list = list()
+ /// Gravity aura for the rift, makes all turfs nearby forced grav.
+ var/datum/proximity_monitor/advanced/gravity/warns_on_entrance/gravity_aura
/datum/armor/structure_carp_rift
energy = 100
@@ -101,14 +103,25 @@
AddComponent( \
/datum/component/aura_healing, \
- range = 0, \
+ range = 1, \
simple_heal = 5, \
limit_to_trait = TRAIT_HEALS_FROM_CARP_RIFTS, \
healing_color = COLOR_BLUE, \
)
+ gravity_aura = new(
+ /* host = */src,
+ /* range = */15,
+ /* ignore_if_not_on_turf = */TRUE,
+ /* gravity = */1,
+ )
+
START_PROCESSING(SSobj, src)
+/obj/structure/carp_rift/Destroy()
+ QDEL_NULL(gravity_aura)
+ return ..()
+
// Carp rifts always take heavy explosion damage. Discourages the use of maxcaps
// and favours more weaker explosives to destroy the portal
// as they have the same effect on the portal.
@@ -239,12 +252,13 @@
to_chat(user, span_warning("The rift already summoned enough carp!"))
return FALSE
- if(!dragon)
+ if(isnull(dragon))
return
var/mob/living/newcarp = new dragon.minion_to_spawn(loc)
newcarp.faction = dragon.owner.current.faction
newcarp.AddElement(/datum/element/nerfed_pulling, GLOB.typecache_general_bad_things_to_easily_move)
newcarp.AddElement(/datum/element/prevent_attacking_of_types, GLOB.typecache_general_bad_hostile_attack_targets, "this tastes awful!")
+ dragon.wavespeak?.link_mob(newcarp)
if(!is_listed)
ckey_list += user.ckey
diff --git a/code/modules/antagonists/space_dragon/space_carp.dm b/code/modules/antagonists/space_dragon/space_carp.dm
index 0d06ea3991d9..ad84d0ee4337 100644
--- a/code/modules/antagonists/space_dragon/space_carp.dm
+++ b/code/modules/antagonists/space_dragon/space_carp.dm
@@ -5,6 +5,7 @@
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
+ antag_flags = FLAG_ANTAG_CAP_IGNORE // monkestation addition
/// The rift to protect
var/datum/weakref/rift
diff --git a/code/modules/antagonists/space_dragon/space_dragon.dm b/code/modules/antagonists/space_dragon/space_dragon.dm
index 66d1e13461aa..3338c62612b1 100644
--- a/code/modules/antagonists/space_dragon/space_dragon.dm
+++ b/code/modules/antagonists/space_dragon/space_dragon.dm
@@ -21,9 +21,13 @@
/// Whether or not Space Dragon has completed their objective, and thus triggered the ending sequence.
var/objective_complete = FALSE
/// What mob to spawn from ghosts using this dragon's rifts
- var/minion_to_spawn = /mob/living/basic/carp
+ var/minion_to_spawn = /mob/living/basic/carp/advanced
/// What AI mobs to spawn from this dragon's rifts
var/ai_to_spawn = /mob/living/basic/carp
+ /// Wavespeak mind linker, to allow telepathy between dragon and carps
+ var/datum/component/mind_linker/wavespeak
+ /// What areas are we allowed to place rifts in?
+ var/list/chosen_rift_areas = list()
/datum/antagonist/space_dragon/greet()
. = ..()
@@ -33,16 +37,42 @@
Today, we will snuff out one of those lights.")
to_chat(owner, span_boldwarning("You have five minutes to find a safe location to place down the first rift. If you take longer than five minutes to place a rift, you will be returned from whence you came."))
owner.announce_objectives()
- SEND_SOUND(owner.current, sound('sound/magic/demon_attack1.ogg'))
+ owner.current.playsound_local(get_turf(owner.current), 'sound/magic/demon_attack1.ogg', 80)
/datum/antagonist/space_dragon/forge_objectives()
+ var/static/list/area/allowed_areas
+ if(!allowed_areas)
+ // Areas that will prove a challeng for the dragon and are provocative to the crew.
+ allowed_areas = typecacheof(list(
+ /area/station/command,
+ /area/station/engineering,
+ /area/station/science,
+ /area/station/security,
+ ))
+
+ var/list/possible_areas = typecache_filter_list(get_sorted_areas(), allowed_areas)
+ for(var/area/possible_area as anything in possible_areas)
+ if(initial(possible_area.outdoors) || !(possible_area.area_flags & VALID_TERRITORY))
+ possible_areas -= possible_area
+
+ for(var/i in 1 to 5)
+ chosen_rift_areas += pick_n_take(possible_areas)
+
var/datum/objective/summon_carp/summon = new
- summon.dragon = src
objectives += summon
+ summon.owner = owner
+ summon.update_explanation_text()
/datum/antagonist/space_dragon/on_gain()
forge_objectives()
rift_ability = new()
+ owner.special_role = ROLE_SPACE_DRAGON
+ owner.set_assigned_role(SSjob.GetJobType(/datum/job/space_dragon))
+ return ..()
+
+/datum/antagonist/space_dragon/on_removal()
+ owner.special_role = null
+ owner.set_assigned_role(SSjob.GetJobType(/datum/job/unassigned))
return ..()
/datum/antagonist/space_dragon/apply_innate_effects(mob/living/mob_override)
@@ -52,6 +82,15 @@
antag.faction |= FACTION_CARP
// Give the ability over if we have one
rift_ability?.Grant(antag)
+ wavespeak = antag.AddComponent( \
+ /datum/component/mind_linker, \
+ network_name = "Wavespeak", \
+ chat_color = "#635BAF", \
+ signals_which_destroy_us = list(COMSIG_LIVING_DEATH), \
+ speech_action_icon = 'icons/mob/actions/actions_space_dragon.dmi', \
+ speech_action_icon_state = "wavespeak", \
+ )
+ RegisterSignal(wavespeak, COMSIG_QDELETING, PROC_REF(clear_wavespeak))
/datum/antagonist/space_dragon/remove_innate_effects(mob/living/mob_override)
var/mob/living/antag = mob_override || owner.current
@@ -59,11 +98,14 @@
UnregisterSignal(antag, COMSIG_LIVING_DEATH)
antag.faction -= FACTION_CARP
rift_ability?.Remove(antag)
+ QDEL_NULL(wavespeak)
/datum/antagonist/space_dragon/Destroy()
rift_list = null
carp = null
QDEL_NULL(rift_ability)
+ QDEL_NULL(wavespeak)
+ chosen_rift_areas.Cut()
return ..()
/datum/antagonist/space_dragon/get_preview_icon()
@@ -77,6 +119,10 @@
return icon
+/datum/antagonist/space_dragon/proc/clear_wavespeak()
+ SIGNAL_HANDLER
+ wavespeak = null
+
/**
* Checks to see if we need to do anything with the current state of the dragon's rifts.
*
@@ -133,8 +179,7 @@
objective_complete = TRUE
permanant_empower()
var/datum/objective/summon_carp/main_objective = locate() in objectives
- if(main_objective)
- main_objective.completed = TRUE
+ main_objective?.completed = TRUE
priority_announce("A large amount of lifeforms have been detected approaching [station_name()] at extreme speeds. \
Remaining crew are advised to evacuate as soon as possible.", "[command_name()] Wildlife Observations", has_important_message = TRUE)
sound_to_playing_players('sound/creatures/space_dragon_roar.ogg')
@@ -178,8 +223,19 @@
owner.current.remove_movespeed_modifier(/datum/movespeed_modifier/dragon_rage)
/datum/objective/summon_carp
- var/datum/antagonist/space_dragon/dragon
- explanation_text = "Summon and protect the rifts to flood the station with carp."
+ explanation_text = "Summon 3 rifts in order to flood the station with carp."
+
+/datum/objective/summon_carp/update_explanation_text()
+ var/datum/antagonist/space_dragon/dragon_owner = owner.has_antag_datum(/datum/antagonist/space_dragon)
+ if(isnull(dragon_owner))
+ return
+
+ var/list/converted_names = list()
+ for(var/area/possible_area as anything in dragon_owner.chosen_rift_areas)
+ converted_names += possible_area.get_original_area_name()
+
+ explanation_text = initial(explanation_text)
+ explanation_text += " Your possible rift locations are: [english_list(converted_names)]"
/datum/antagonist/space_dragon/roundend_report()
var/list/parts = list()
@@ -198,7 +254,17 @@
parts += "The [name] was successful!"
else
parts += "The [name] has failed!"
- if(carp.len)
- parts += "The [name] was assisted by:"
- parts += printplayerlist(carp)
+
+ if(length(carp))
+ parts += " The [name] was assisted by:"
+ parts += "
"
+ var/list/players_to_carp_taken = list()
+ for(var/datum/mind/carpy as anything in carp)
+ players_to_carp_taken[carpy.key] += 1
+ var/list = ""
+ for(var/carp_user in players_to_carp_taken)
+ list += "
[carp_user], who played [players_to_carp_taken[carp_user]] space carps.
"
+ parts += list
+ parts += "
"
+
return "
[parts.Join(" ")]
"
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index 2aade03ae3fb..84a2f92ab9a6 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -205,7 +205,6 @@
/// Generates a complete set of traitor objectives up to the traitor objective limit, including non-generic objectives such as martyr and hijack.
/datum/antagonist/traitor/proc/forge_traitor_objectives()
- objectives.Cut()
var/objective_count = 0
if((GLOB.joined_player_list.len >= HIJACK_MIN_PLAYERS) && prob(HIJACK_PROB))
diff --git a/code/modules/antagonists/venus_human_trap/venus_human_trap.dm b/code/modules/antagonists/venus_human_trap/venus_human_trap.dm
index c84f20d05965..04ffc1dcd51c 100644
--- a/code/modules/antagonists/venus_human_trap/venus_human_trap.dm
+++ b/code/modules/antagonists/venus_human_trap/venus_human_trap.dm
@@ -5,6 +5,7 @@
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
+ antag_flags = FLAG_ANTAG_CAP_IGNORE // monkestation addition
/datum/antagonist/venus_human_trap/on_gain()
forge_objectives()
diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm
index bfac673535af..d27ad3df8a10 100644
--- a/code/modules/antagonists/wishgranter/wishgranter.dm
+++ b/code/modules/antagonists/wishgranter/wishgranter.dm
@@ -4,6 +4,7 @@
show_name_in_check_antagonists = TRUE
hijack_speed = 2 //You literally are here to do nothing else. Might as well be fast about it.
suicide_cry = "HAHAHAHAHA!!"
+ antag_flags = parent_type::antag_flags | FLAG_ANTAG_CAP_IGNORE // monkestation addition
/datum/antagonist/wishgranter/forge_objectives()
var/datum/objective/hijack/hijack = new
diff --git a/code/modules/antagonists/wizard/equipment/enchanted_clown_suit.dm b/code/modules/antagonists/wizard/equipment/enchanted_clown_suit.dm
index 3222deecd84c..623971276937 100644
--- a/code/modules/antagonists/wizard/equipment/enchanted_clown_suit.dm
+++ b/code/modules/antagonists/wizard/equipment/enchanted_clown_suit.dm
@@ -9,6 +9,7 @@
cooldown_time = 30 SECONDS
cooldown_reduction_per_rank = 2 SECONDS
delete_old = FALSE
+ delete_on_failure = FALSE
/// Amount of time it takes you to rummage around in there
var/cast_time = 3 SECONDS
/// True while currently casting the spell
@@ -56,7 +57,7 @@
return . | SPELL_CANCEL_CAST
casting = FALSE
-/datum/action/cooldown/spell/conjure_item/clown_pockets/make_item()
+/datum/action/cooldown/spell/conjure_item/clown_pockets/make_item(atom/caster)
item_type = pick_weight(clown_items)
return ..()
diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/assistance.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/assistance.dm
index e576d7738c3d..03b38e10f56a 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook_entries/assistance.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/assistance.dm
@@ -1,44 +1,45 @@
+#define SPELLBOOK_CATEGORY_ASSISTANCE "Assistance"
// Wizard spells that assist the caster in some way
/datum/spellbook_entry/summonitem
name = "Summon Item"
desc = "Recalls a previously marked item to your hand from anywhere in the universe."
spell_type = /datum/action/cooldown/spell/summonitem
- category = "Assistance"
+ category = SPELLBOOK_CATEGORY_ASSISTANCE
cost = 1
/datum/spellbook_entry/charge
name = "Charge"
desc = "This spell can be used to recharge a variety of things in your hands, from magical artifacts to electrical components. A creative wizard can even use it to grant magical power to a fellow magic user."
spell_type = /datum/action/cooldown/spell/charge
- category = "Assistance"
+ category = SPELLBOOK_CATEGORY_ASSISTANCE
cost = 1
/datum/spellbook_entry/shapeshift
name = "Wild Shapeshift"
desc = "Take on the shape of another for a time to use their natural abilities. Once you've made your choice it cannot be changed."
spell_type = /datum/action/cooldown/spell/shapeshift/wizard
- category = "Assistance"
+ category = SPELLBOOK_CATEGORY_ASSISTANCE
cost = 1
/datum/spellbook_entry/tap
name = "Soul Tap"
desc = "Fuel your spells using your own soul!"
spell_type = /datum/action/cooldown/spell/tap
- category = "Assistance"
+ category = SPELLBOOK_CATEGORY_ASSISTANCE
cost = 1
/datum/spellbook_entry/item/staffanimation
name = "Staff of Animation"
desc = "An arcane staff capable of shooting bolts of eldritch energy which cause inanimate objects to come to life. This magic doesn't affect machines."
item_path = /obj/item/gun/magic/staff/animate
- category = "Assistance"
+ category = SPELLBOOK_CATEGORY_ASSISTANCE
/datum/spellbook_entry/item/soulstones
name = "Soulstone Shard Kit"
desc = "Soul Stone Shards are ancient tools capable of capturing and harnessing the spirits of the dead and dying. \
The spell Artificer allows you to create arcane machines for the captured souls to pilot."
item_path = /obj/item/storage/belt/soulstone/full
- category = "Assistance"
+ category = SPELLBOOK_CATEGORY_ASSISTANCE
/datum/spellbook_entry/item/soulstones/try_equip_item(mob/living/carbon/human/user, obj/item/to_equip)
var/was_equipped = user.equip_to_slot_if_possible(to_equip, ITEM_SLOT_BELT, disable_warning = TRUE)
@@ -56,13 +57,13 @@
name = "A Necromantic Stone"
desc = "A Necromantic stone is able to resurrect three dead individuals as skeletal thralls for you to command."
item_path = /obj/item/necromantic_stone
- category = "Assistance"
+ category = SPELLBOOK_CATEGORY_ASSISTANCE
/datum/spellbook_entry/item/contract
name = "Contract of Apprenticeship"
desc = "A magical contract binding an apprentice wizard to your service, using it will summon them to your side."
item_path = /obj/item/antag_spawner/contract
- category = "Assistance"
+ category = SPELLBOOK_CATEGORY_ASSISTANCE
refundable = TRUE
/datum/spellbook_entry/item/guardian
@@ -70,7 +71,7 @@
desc = "A deck of guardian tarot cards, capable of binding a personal guardian to your body. There are multiple types of guardian available, but all of them will transfer some amount of damage to you. \
It would be wise to avoid buying these with anything capable of causing you to swap bodies with others."
item_path = /obj/item/guardian_creator/wizard
- category = "Assistance"
+ category = SPELLBOOK_CATEGORY_ASSISTANCE
/datum/spellbook_entry/item/bloodbottle
name = "Bottle of Blood"
@@ -80,7 +81,7 @@
in their killing, and you yourself may become a victim."
item_path = /obj/item/antag_spawner/slaughter_demon
limit = 3
- category = "Assistance"
+ category = SPELLBOOK_CATEGORY_ASSISTANCE
refundable = TRUE
/datum/spellbook_entry/item/hugbottle
@@ -95,5 +96,7 @@
item_path = /obj/item/antag_spawner/slaughter_demon/laughter
cost = 1 //non-destructive; it's just a jape, sibling!
limit = 3
- category = "Assistance"
+ category = SPELLBOOK_CATEGORY_ASSISTANCE
refundable = TRUE
+
+#undef SPELLBOOK_CATEGORY_ASSISTANCE
diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm
index 46f78dfc6c4e..d03dc224e4e7 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm
@@ -1,49 +1,50 @@
+#define SPELLBOOK_CATEGORY_DEFENSIVE "Defensive"
// Defensive wizard spells
/datum/spellbook_entry/magicm
name = "Magic Missile"
desc = "Fires several, slow moving, magic projectiles at nearby targets."
spell_type = /datum/action/cooldown/spell/aoe/magic_missile
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
/datum/spellbook_entry/disabletech
name = "Disable Tech"
desc = "Disables all weapons, cameras and most other technology in range."
spell_type = /datum/action/cooldown/spell/emp/disable_tech
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
cost = 1
/datum/spellbook_entry/repulse
name = "Repulse"
desc = "Throws everything around the user away."
spell_type = /datum/action/cooldown/spell/aoe/repulse/wizard
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
/datum/spellbook_entry/lightning_packet
name = "Thrown Lightning"
desc = "Forged from eldrich energies, a packet of pure power, \
known as a spell packet will appear in your hand, that when thrown will stun the target."
spell_type = /datum/action/cooldown/spell/conjure_item/spellpacket
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
/datum/spellbook_entry/timestop
name = "Time Stop"
desc = "Stops time for everyone except for you, allowing you to move freely \
while your enemies and even projectiles are frozen."
spell_type = /datum/action/cooldown/spell/timestop
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
/datum/spellbook_entry/smoke
name = "Smoke"
desc = "Spawns a cloud of choking smoke at your location."
spell_type = /datum/action/cooldown/spell/smoke
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
cost = 1
/datum/spellbook_entry/forcewall
name = "Force Wall"
desc = "Create a magical barrier that only you can pass through."
spell_type = /datum/action/cooldown/spell/forcewall
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
cost = 1
/datum/spellbook_entry/lichdom
@@ -60,21 +61,21 @@
name = "Chuuni Invocations"
desc = "Makes all your spells shout invocations, and the invocations become... stupid. You heal slightly after casting a spell."
spell_type = /datum/action/cooldown/spell/chuuni_invocations
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
/datum/spellbook_entry/spacetime_dist
name = "Spacetime Distortion"
desc = "Entangle the strings of space-time in an area around you, \
randomizing the layout and making proper movement impossible. The strings vibrate..."
spell_type = /datum/action/cooldown/spell/spacetime_dist
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
cost = 1
/datum/spellbook_entry/the_traps
name = "The Traps!"
desc = "Summon a number of traps around you. They will damage and enrage any enemies that step on them."
spell_type = /datum/action/cooldown/spell/conjure/the_traps
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
cost = 1
/datum/spellbook_entry/bees
@@ -82,7 +83,7 @@
desc = "This spell magically kicks a transdimensional beehive, \
instantly summoning a swarm of bees to your location. These bees are NOT friendly to anyone."
spell_type = /datum/action/cooldown/spell/conjure/bee
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
/datum/spellbook_entry/duffelbag
name = "Bestow Cursed Duffel Bag"
@@ -91,7 +92,7 @@
if it is not fed regularly, and regardless of whether or not it's been fed, \
it will slow the person wearing it down significantly."
spell_type = /datum/action/cooldown/spell/touch/duffelbag
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
cost = 1
/datum/spellbook_entry/item/staffhealing
@@ -99,26 +100,26 @@
desc = "An altruistic staff that can heal the lame and raise the dead."
item_path = /obj/item/gun/magic/staff/healing
cost = 1
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
/datum/spellbook_entry/item/lockerstaff
name = "Staff of the Locker"
desc = "A staff that shoots lockers. It eats anyone it hits on its way, leaving a welded locker with your victims behind."
item_path = /obj/item/gun/magic/staff/locker
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
/datum/spellbook_entry/item/scryingorb
name = "Scrying Orb"
desc = "An incandescent orb of crackling energy. Using it will allow you to release your ghost while alive, allowing you to spy upon the station and talk to the deceased. In addition, buying it will permanently grant you X-ray vision."
item_path = /obj/item/scrying
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
/datum/spellbook_entry/item/wands
name = "Wand Assortment"
desc = "A collection of wands that allow for a wide variety of utility. \
Wands have a limited number of charges, so be conservative with their use. Comes in a handy belt."
item_path = /obj/item/storage/belt/wands/full
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
cost = 3 //monkestation edit
/datum/spellbook_entry/item/wands/try_equip_item(mob/living/carbon/human/user, obj/item/to_equip)
@@ -131,7 +132,7 @@
while providing more protection against attacks and the void of space. \
Also grants a battlemage shield."
item_path = /obj/item/mod/control/pre_equipped/enchanted
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
/datum/spellbook_entry/item/armor/try_equip_item(mob/living/carbon/human/user, obj/item/to_equip)
var/obj/item/mod/control/mod = to_equip
@@ -150,7 +151,9 @@
/datum/spellbook_entry/item/battlemage_charge
name = "Battlemage Armour Charges"
- desc = "A powerful defensive rune, it will grant twelve additional charges to a battlemage shield." //monkestation edit: reaplced eight with twelve
+ desc = "A powerful defensive rune, it will grant a battlemage shield the ability to absorb 50 more damage." //monkestation edited
item_path = /obj/item/wizard_armour_charge
- category = "Defensive"
+ category = SPELLBOOK_CATEGORY_DEFENSIVE
cost = 1
+
+#undef SPELLBOOK_CATEGORY_DEFENSIVE
diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/mobility.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/mobility.dm
index d5112a59611b..2893bf5614e4 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook_entries/mobility.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/mobility.dm
@@ -1,47 +1,48 @@
+#define SPELLBOOK_CATEGORY_MOBILITY "Mobility"
// Wizard spells that aid mobiilty(or stealth?)
/datum/spellbook_entry/mindswap
name = "Mindswap"
desc = "Allows you to switch bodies with a target next to you. You will both fall asleep when this happens, and it will be quite obvious that you are the target's body if someone watches you do it."
spell_type = /datum/action/cooldown/spell/pointed/mind_transfer
- category = "Mobility"
+ category = SPELLBOOK_CATEGORY_MOBILITY
/datum/spellbook_entry/knock
name = "Knock"
desc = "Opens nearby doors and closets."
spell_type = /datum/action/cooldown/spell/aoe/knock
- category = "Mobility"
+ category = SPELLBOOK_CATEGORY_MOBILITY
cost = 1
/datum/spellbook_entry/blink
name = "Blink"
desc = "Randomly teleports you a short distance."
spell_type = /datum/action/cooldown/spell/teleport/radius_turf/blink
- category = "Mobility"
+ category = SPELLBOOK_CATEGORY_MOBILITY
/datum/spellbook_entry/teleport
name = "Teleport"
desc = "Teleports you to an area of your selection."
spell_type = /datum/action/cooldown/spell/teleport/area_teleport/wizard
- category = "Mobility"
+ category = SPELLBOOK_CATEGORY_MOBILITY
/datum/spellbook_entry/jaunt
name = "Ethereal Jaunt"
desc = "Turns your form ethereal, temporarily making you invisible and able to pass through walls."
spell_type = /datum/action/cooldown/spell/jaunt/ethereal_jaunt
- category = "Mobility"
+ category = SPELLBOOK_CATEGORY_MOBILITY
/datum/spellbook_entry/swap
name = "Swap"
desc = "Switch places with any living target within nine tiles. Right click to mark a secondary target. You will always swap to your primary target."
spell_type = /datum/action/cooldown/spell/pointed/swap
- category = "Mobility"
+ category = SPELLBOOK_CATEGORY_MOBILITY
cost = 1
/datum/spellbook_entry/item/warpwhistle
name = "Warp Whistle"
desc = "A strange whistle that will transport you to a distant safe place on the station. There is a window of vulnerability at the beginning of every use."
item_path = /obj/item/warp_whistle
- category = "Mobility"
+ category = SPELLBOOK_CATEGORY_MOBILITY
cost = 1
/datum/spellbook_entry/item/staffdoor
@@ -49,4 +50,6 @@
desc = "A particular staff that can mold solid walls into ornate doors. Useful for getting around in the absence of other transportation. Does not work on glass."
item_path = /obj/item/gun/magic/staff/door
cost = 1
- category = "Mobility"
+ category = SPELLBOOK_CATEGORY_MOBILITY
+
+#undef SPELLBOOK_CATEGORY_MOBILITY
diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/offensive.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/offensive.dm
index 57aa2ad7af18..c30ca31915b5 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook_entries/offensive.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/offensive.dm
@@ -1,27 +1,28 @@
+#define SPELLBOOK_CATEGORY_OFFENSIVE "Offensive"
// Offensive wizard spells
/datum/spellbook_entry/fireball
- name = "Fireball"
- desc = "Fires an explosive fireball at a target. Considered a classic among all wizards."
- spell_type = /datum/action/cooldown/spell/pointed/projectile/fireball
- category = "Offensive"
+ name = "Fire Ball" //monkestation edit: added the space
+ desc = "Fires a fire ball at a target. The Wizard Federation got hit hard by the budget cuts." //monkestation edit: edited to reflect the new version of the given spell
+ spell_type = /datum/action/cooldown/spell/pointed/projectile/fireball/bouncy //monkestation edit: adds the bouncy subtype
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
/datum/spellbook_entry/spell_cards
name = "Spell Cards"
desc = "Blazing hot rapid-fire homing cards. Send your foes to the shadow realm with their mystical power!"
spell_type = /datum/action/cooldown/spell/pointed/projectile/spell_cards
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
/datum/spellbook_entry/rod_form
name = "Rod Form"
desc = "Take on the form of an immovable rod, destroying all in your path. Purchasing this spell multiple times will also increase the rod's damage and travel range."
spell_type = /datum/action/cooldown/spell/rod_form
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
/*/datum/spellbook_entry/disintegrate //monkestation edit: replaced with a pointed version with a different effect
name = "Smite"
desc = "Charges your hand with an unholy energy that can be used to cause a touched victim to violently explode."
spell_type = /datum/action/cooldown/spell/touch/smite
- category = "Offensive"*/
+ category = SPELLBOOK_CATEGORY_OFFENSIVE*/
/datum/spellbook_entry/summon_simians
name = "Summon Simians"
@@ -29,45 +30,52 @@
summons primal monkeys and lesser gorillas that will promptly flip out and attack everything in sight. Fun! \
Their lesser, easily manipulable minds will be convinced you are one of their allies, but only for a minute. Unless you also are a monkey."
spell_type = /datum/action/cooldown/spell/conjure/simian
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
/datum/spellbook_entry/blind
name = "Blind"
desc = "Temporarily blinds a single target."
spell_type = /datum/action/cooldown/spell/pointed/blind
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
+ cost = 1
+
+/datum/spellbook_entry/tie_shoes
+ name = "Tie Shoes"
+ desc = "This unassuming spell first unties, then knots the target's shoes. While weak at first glance, each upgrade quietens the spell, allowing it to untie laceless footwear and even summon shoes to knot!"
+ spell_type = /datum/action/cooldown/spell/pointed/untie_shoes
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
cost = 1
/datum/spellbook_entry/mutate
name = "Mutate"
desc = "Causes you to turn into a hulk and gain laser vision for a short while."
spell_type = /datum/action/cooldown/spell/apply_mutations/mutate
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
/datum/spellbook_entry/fleshtostone
name = "Flesh to Stone"
desc = "Charges your hand with the power to turn victims into inert statues for a long period of time."
spell_type = /datum/action/cooldown/spell/touch/flesh_to_stone
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
/datum/spellbook_entry/teslablast
name = "Tesla Blast"
desc = "Charge up a tesla arc and release it at a random nearby target! You can move freely while it charges. The arc jumps between targets and can knock them down."
spell_type = /datum/action/cooldown/spell/charged/beam/tesla
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
/datum/spellbook_entry/lightningbolt
name = "Lightning Bolt"
desc = "Fire a lightning bolt at your foes! It will jump between targets, but can't knock them down."
spell_type = /datum/action/cooldown/spell/pointed/projectile/lightningbolt
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
cost = 1
/datum/spellbook_entry/infinite_guns
name = "Lesser Summon Guns"
desc = "Why reload when you have infinite guns? Summons an unending stream of bolt action rifles that deal little damage, but will knock targets down. Requires both hands free to use. Learning this spell makes you unable to learn Arcane Barrage."
spell_type = /datum/action/cooldown/spell/conjure_item/infinite_guns/gun
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
cost = 3
no_coexistance_typecache = list(/datum/action/cooldown/spell/conjure_item/infinite_guns/arcane_barrage)
@@ -75,7 +83,7 @@
name = "Arcane Barrage"
desc = "Fire a torrent of arcane energy at your foes with this (powerful) spell. Deals much more damage than Lesser Summon Guns, but won't knock targets down. Requires both hands free to use. Learning this spell makes you unable to learn Lesser Summon Gun."
spell_type = /datum/action/cooldown/spell/conjure_item/infinite_guns/arcane_barrage
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
cost = 3
no_coexistance_typecache = list(/datum/action/cooldown/spell/conjure_item/infinite_guns/gun)
@@ -83,7 +91,7 @@
name = "Barnyard Curse"
desc = "This spell dooms an unlucky soul to possess the speech and facial attributes of a barnyard animal."
spell_type = /datum/action/cooldown/spell/pointed/barnyardcurse
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
cost = 1 //monkestation edit
/datum/spellbook_entry/splattercasting
@@ -91,55 +99,64 @@
desc = "Dramatically lowers the cooldown on all spells, but each one will cost blood, as well as it naturally \
draining from you over time. You can replenish it from your victims, specifically their necks."
spell_type = /datum/action/cooldown/spell/splattercasting
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
no_coexistance_typecache = list(/datum/action/cooldown/spell/lichdom)
/datum/spellbook_entry/sanguine_strike
name = "Exsanguinating Strike"
desc = "Sanguine spell that enchants your next weapon strike to deal more damage, heal you for damage dealt, and refill blood."
spell_type = /datum/action/cooldown/spell/sanguine_strike
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
/datum/spellbook_entry/scream_for_me
name = "Scream For Me"
desc = "Sadistic sanguine spell that inflicts numerous severe blood wounds all over the victim's body."
spell_type = /datum/action/cooldown/spell/touch/scream_for_me
cost = 1
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
/datum/spellbook_entry/item/staffchaos
name = "Staff of Chaos"
desc = "A caprious tool that can fire all sorts of magic without any rhyme or reason. Using it on people you care about is not recommended."
item_path = /obj/item/gun/magic/staff/chaos
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
/datum/spellbook_entry/item/staffchange
name = "Staff of Change"
desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself."
item_path = /obj/item/gun/magic/staff/change
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
/*/datum/spellbook_entry/item/mjolnir //monkestation edit: replaced with the spell summon mjollnir
name = "Mjolnir"
desc = "A mighty hammer on loan from Thor, God of Thunder. It crackles with barely contained power."
item_path = /obj/item/mjollnir
- category = "Offensive"*/
+ category = SPELLBOOK_CATEGORY_OFFENSIVE*/
/datum/spellbook_entry/item/singularity_hammer
name = "Singularity Hammer"
desc = "A hammer that creates an intensely powerful field of gravity where it strikes, pulling everything nearby to the point of impact."
item_path = /obj/item/singularityhammer
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
/datum/spellbook_entry/item/spellblade
name = "Spellblade"
desc = "A sword capable of firing blasts of energy which rip targets limb from limb."
item_path = /obj/item/gun/magic/staff/spellblade
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
/datum/spellbook_entry/item/highfrequencyblade
name = "High Frequency Blade"
desc = "An incredibly swift enchanted blade resonating at a frequency high enough to be able to slice through anything."
item_path = /obj/item/highfrequencyblade/wizard
- category = "Offensive"
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
cost = 3
+
+/datum/spellbook_entry/item/staffshrink
+ name = "Staff of Shrinking"
+ desc = "An artefact that can shrink anything for a reasonable duration. Small structures can be walked over, and small people are very vulnerable (often because their armour no longer fits)."
+ item_path = /obj/item/gun/magic/staff/shrink
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
+
+
+#undef SPELLBOOK_CATEGORY_OFFENSIVE
diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm b/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm
index 942a3d656ac3..3e71dbcbe8d1 100644
--- a/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm
@@ -12,7 +12,7 @@
YOU WILL NOT SURVIVE THIS."
icon = 'icons/hud/screen_alert.dmi'
icon_state = "wounded"
- minimum_time = 90 MINUTES // This will probably immediately end the round if it gets finished.
+ minimum_time = 80 MINUTES // This will probably immediately end the round if it gets finished. //monkestation edit: from 90 to 80 minutes
ritual_invoke_time = 60 SECONDS // Really give the crew some time to interfere with this one.
dire_warning = TRUE
glow_colour = "#be000048"
diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm b/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm
index 4caf8b87dca6..c4f33543878c 100644
--- a/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm
@@ -9,7 +9,16 @@
/datum/grand_finale/clown/trigger(mob/living/carbon/human/invoker)
for(var/mob/living/carbon/human/victim as anything in GLOB.human_list)
victim.Unconscious(3 SECONDS)
- if (!victim.mind || IS_HUMAN_INVADER(victim) || victim == invoker)
+ if (victim == invoker)
+ if(locate(/datum/action/cooldown/spell/pointed/untie_shoes) in invoker.actions)
+ continue
+ var/datum/action/cooldown/spell/pointed/untie_shoes/newer_spell = new(invoker)
+ newer_spell.Grant(invoker)
+ for(var/i in 1 to newer_spell.spell_max_level)
+ newer_spell.level_spell()
+ newer_spell.invocation_type = INVOCATION_SHOUT
+ continue
+ if (!victim.mind || IS_HUMAN_INVADER(victim))
continue
if (HAS_TRAIT(victim, TRAIT_CLOWN_ENJOYER))
victim.add_mood_event("clown_world", /datum/mood_event/clown_world)
@@ -23,6 +32,8 @@
if (is_clown_job(victim.mind.assigned_role))
var/datum/action/cooldown/spell/conjure_item/clown_pockets/new_spell = new(victim)
new_spell.Grant(victim)
+ var/datum/action/cooldown/spell/pointed/untie_shoes/newer_spell = new(victim)
+ newer_spell.Grant(victim)
continue
if (!ismonkey(victim)) // Monkeys cannot yet wear clothes
dress_as_magic_clown(victim)
diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/grand_ritual_finale.dm b/code/modules/antagonists/wizard/grand_ritual/finales/grand_ritual_finale.dm
index bfb0e01201b7..b1e5fae978f6 100644
--- a/code/modules/antagonists/wizard/grand_ritual/finales/grand_ritual_finale.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/finales/grand_ritual_finale.dm
@@ -71,7 +71,7 @@
name = "\improper Wizard Prank Victim"
roundend_category = "wizard prank victims"
show_in_antagpanel = FALSE
- antagpanel_category = "Other"
+ antagpanel_category = ANTAG_GROUP_CREW
show_name_in_check_antagonists = TRUE
count_against_dynamic_roll_chance = FALSE
silent = TRUE
diff --git a/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm b/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm
index 305171cca73b..4daa3d77f45f 100644
--- a/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm
@@ -18,7 +18,7 @@
pixel_x = -28
pixel_y = -33
anchored = TRUE
- interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND
+ interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_ATTACK_PAW
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
layer = SIGIL_LAYER
/// How many prior grand rituals have been completed?
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index 87fe2f3e4325..f3f6c810baa9 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -34,10 +34,11 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
/datum/antagonist/wizard_minion
name = "Wizard Minion"
- antagpanel_category = "Wizard Federation"
+ antagpanel_category = ANTAG_GROUP_WIZARDS
antag_hud_name = "apprentice"
show_in_roundend = FALSE
show_name_in_check_antagonists = TRUE
+ antag_flags = parent_type::antag_flags | FLAG_ANTAG_CAP_IGNORE // monkestation addition
/// The wizard team this wizard minion is part of.
var/datum/team/wizard/wiz_team
@@ -113,7 +114,7 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
/// Initialises the grand ritual action for this mob
/datum/antagonist/wizard/proc/assign_ritual()
- ritual = new(owner.current)
+ ritual = new(owner) //monkestation edit: adds directly to owner instead of owner.current
RegisterSignal(ritual, COMSIG_GRAND_RITUAL_FINAL_COMPLETE, PROC_REF(on_ritual_complete))
/datum/antagonist/wizard/proc/send_to_lair()
@@ -255,6 +256,7 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
name = "Wizard Apprentice"
antag_hud_name = "apprentice"
can_assign_self_objectives = FALSE
+ move_to_lair = FALSE
var/datum/mind/master
var/school = APPRENTICE_DESTRUCTION
outfit_type = /datum/outfit/wizard/apprentice
@@ -279,7 +281,7 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
if(APPRENTICE_DESTRUCTION)
spells_to_grant = list(
/datum/action/cooldown/spell/aoe/magic_missile,
- /datum/action/cooldown/spell/pointed/projectile/fireball,
+ /datum/action/cooldown/spell/pointed/projectile/fireball/bouncy, //monkestation edit: adds the bouncy subtype
)
to_chat(owner, span_bold("Your service has not gone unrewarded, however. \
Studying under [master.current.real_name], you have learned powerful, \
diff --git a/code/modules/antagonists/xeno/xeno.dm b/code/modules/antagonists/xeno/xeno.dm
index 115e40ca595c..eff0866999cf 100644
--- a/code/modules/antagonists/xeno/xeno.dm
+++ b/code/modules/antagonists/xeno/xeno.dm
@@ -66,6 +66,7 @@
return
captive_team = new
captive_team.progenitor = owner
+ antag_flags |= FLAG_ANTAG_CAP_IGNORE // monkestation edit: first captive xeno does not count against cap
else
if(!istype(new_team))
CRASH("Wrong xeno team type provided to create_team")
diff --git a/code/modules/assembly/flash.dm b/code/modules/assembly/flash.dm
index d45528c2d3ed..e80f45ae8dee 100644
--- a/code/modules/assembly/flash.dm
+++ b/code/modules/assembly/flash.dm
@@ -164,6 +164,9 @@
if(deviation == DEVIATION_FULL)
return
+ if(CAN_BYPASS_INNATE_FLASH_RESISTANCE(user)) // MONKESTATION EDIT: Make IPCs not resistant to bb and rev conversions.
+ ADD_TRAIT(flashed, TRAIT_CONVERSION_FLASHED, TRAIT_GENERIC)
+
if(targeted)
if(flashed.flash_act(1, 1))
flashed.set_confusion_if_lower(confusion_duration * CONFUSION_STACK_MAX_MULTIPLIER)
@@ -185,6 +188,8 @@
if(flashed.flash_act())
flashed.set_confusion_if_lower(confusion_duration * CONFUSION_STACK_MAX_MULTIPLIER)
+ REMOVE_TRAIT(flashed, TRAIT_CONVERSION_FLASHED, TRAIT_GENERIC) // MONKESTATION EDIT: Make IPCs not resistant to bb and rev conversions.
+
/**
* Handles the directionality of the attack
*
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
index 579c3fa62cec..af1f78552796 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
@@ -298,6 +298,15 @@
radio.talk_into(src, msg, radio_channel)
return
+ // monkestation start: kick no-healers out
+ if(HAS_TRAIT(mob_occupant, TRAIT_NO_HEALS))
+ playsound(src, 'sound/machines/cryo_warning.ogg', volume)
+ radio.talk_into(src, "Patient is unable to be healed, ejecting.", radio_channel)
+ set_on(FALSE)
+ open_machine()
+ return
+ // monkerstation end
+
patient_dead = FALSE
if(mob_occupant.get_organic_health() >= mob_occupant.getMaxHealth()) // Don't bother with fully healed people.
diff --git a/code/modules/basketball/referee.dm b/code/modules/basketball/referee.dm
index 666ff628682b..b9e2a4a8988d 100644
--- a/code/modules/basketball/referee.dm
+++ b/code/modules/basketball/referee.dm
@@ -14,33 +14,33 @@
disable_text = span_cult("You decide it was a bad call...")
COOLDOWN_DECLARE(whistle_cooldown_minigame)
-/datum/action/innate/timeout/InterceptClickOn(mob/living/caller, params, atom/clicked_on)
- var/turf/caller_turf = get_turf(caller)
+/datum/action/innate/timeout/InterceptClickOn(mob/living/user, params, atom/clicked_on)
+ var/turf/caller_turf = get_turf(user)
if(!isturf(caller_turf))
return FALSE
- if(!ishuman(clicked_on) || get_dist(caller, clicked_on) > 7)
+ if(!ishuman(clicked_on) || get_dist(user, clicked_on) > 7)
return FALSE
- if(clicked_on == caller) // can't call a foul on yourself
+ if(clicked_on == user) // can't call a foul on yourself
return FALSE
if(!COOLDOWN_FINISHED(src, whistle_cooldown_minigame))
- caller.balloon_alert(caller, "cant cast for [COOLDOWN_TIMELEFT(src, whistle_cooldown_minigame) *0.1] seconds!")
- unset_ranged_ability(caller)
+ user.balloon_alert(user, "cant cast for [COOLDOWN_TIMELEFT(src, whistle_cooldown_minigame) *0.1] seconds!")
+ unset_ranged_ability(user)
return FALSE
return ..()
-/datum/action/innate/timeout/do_ability(mob/living/caller, mob/living/carbon/human/target)
- caller.say("FOUL BY [target]!", forced = "whistle")
- playsound(caller, 'sound/misc/whistle.ogg', 30, FALSE, 4)
+/datum/action/innate/timeout/do_ability(mob/living/user, mob/living/carbon/human/target)
+ user.say("FOUL BY [target]!", forced = "whistle")
+ playsound(user, 'sound/misc/whistle.ogg', 30, FALSE, 4)
- new /obj/effect/timestop(get_turf(target), 0, 5 SECONDS, list(caller), TRUE, TRUE)
+ new /obj/effect/timestop(get_turf(target), 0, 5 SECONDS, list(user), TRUE, TRUE)
COOLDOWN_START(src, whistle_cooldown_minigame, 1 MINUTES)
- unset_ranged_ability(caller)
+ unset_ranged_ability(user)
- to_chat(target, span_bold("[caller] has given you a timeout for a foul!"))
- to_chat(caller, span_bold("You put [target] in a timeout!"))
+ to_chat(target, span_bold("[user] has given you a timeout for a foul!"))
+ to_chat(user, span_bold("You put [target] in a timeout!"))
return TRUE
diff --git a/code/modules/bitrunning/antagonists/cyber_police.dm b/code/modules/bitrunning/antagonists/cyber_police.dm
index 73ff96ff7c8c..9d99520dec76 100644
--- a/code/modules/bitrunning/antagonists/cyber_police.dm
+++ b/code/modules/bitrunning/antagonists/cyber_police.dm
@@ -10,6 +10,7 @@
show_to_ghosts = TRUE
suicide_cry = "ALT F4!"
ui_name = "AntagInfoCyberAuth"
+ antag_flags = FLAG_ANTAG_CAP_IGNORE // monkestation addition
/datum/antagonist/cyber_police/greet()
. = ..()
diff --git a/code/modules/cargo/packs/engineering.dm b/code/modules/cargo/packs/engineering.dm
index 4acfa1e0d969..ffb7b1e37b26 100644
--- a/code/modules/cargo/packs/engineering.dm
+++ b/code/modules/cargo/packs/engineering.dm
@@ -287,8 +287,6 @@
crate_type = /obj/structure/closet/crate/secure/engineering
dangerous = TRUE
-
-
/datum/supply_pack/engine/collector
name = "Radiation Collector Crate"
desc = "Contains three radiation collectors. Useful for collecting energy off nearby Supermatter Crystals, Singularities or Teslas!"
@@ -298,3 +296,11 @@
/obj/machinery/power/rad_collector)
crate_name = "collector crate"
+/datum/supply_pack/engineering/portagrav
+ name = "Portable Gravity Unit Crate"
+ desc = "Contains a portable gravity unit, to make the clown float into the ceiling."
+ cost = CARGO_CRATE_VALUE * 4
+ access_view = ACCESS_ENGINEERING
+ contains = list(/obj/machinery/power/portagrav = 1)
+ crate_name = "portable gravity unit crate"
+ crate_type = /obj/structure/closet/crate/engineering
diff --git a/code/modules/cargo/packs/livestock.dm b/code/modules/cargo/packs/livestock.dm
index f9eb781bfa2f..af6924a53b32 100644
--- a/code/modules/cargo/packs/livestock.dm
+++ b/code/modules/cargo/packs/livestock.dm
@@ -92,6 +92,14 @@
contains = list(/mob/living/basic/pig)
crate_name = "pig crate"
+/datum/supply_pack/critter/pony
+ name = "Pony Crate"
+ desc = "Ponies, yay! (Just the one.)"
+ cost = CARGO_CRATE_VALUE * 6
+ access_view = ACCESS_SERVICE
+ contains = list(/mob/living/basic/pony)
+ crate_name = "pony crate"
+
/datum/supply_pack/critter/crab
name = "Crab Rocket"
desc = "CRAAAAAAB ROCKET. CRAB ROCKET. CRAB ROCKET. CRAB CRAB CRAB CRAB CRAB CRAB CRAB \
diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm
index 9208c229e5b7..bc2b683882f2 100644
--- a/code/modules/client/verbs/ooc.dm
+++ b/code/modules/client/verbs/ooc.dm
@@ -55,7 +55,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
msg = emoji_parse(msg)
- if(SSticker.HasRoundStarted() && (msg[1] in list(".",";",":","#") || findtext_char(msg, "say", 1, 5)))
+ if(SSticker.HasRoundStarted() && ((msg[1] in list(".",";",":","#")) || findtext_char(msg, "say", 1, 5)))
if(tgui_alert(usr,"Your message \"[raw_msg]\" looks like it was meant for in game communication, say it in OOC?", "Meant for OOC?", list("Yes", "No")) != "Yes")
return
diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm
index 7e984c659e5e..2b304c631a76 100644
--- a/code/modules/clothing/head/helmet.dm
+++ b/code/modules/clothing/head/helmet.dm
@@ -144,6 +144,12 @@
inhand_icon_state = "blueshift_helmet"
custom_premium_price = PAYCHECK_COMMAND
+/obj/item/clothing/head/helmet/guardmanhelmet
+ name = "guardman's helmet"
+ desc = "Keeps your brain intact when fighting heretics"
+ icon = 'monkestation/icons/obj/clothing/hats.dmi'
+ worn_icon = 'monkestation/icons/mob/clothing/head.dmi'
+ icon_state = "guardman_helmet"
/obj/item/clothing/head/helmet/toggleable
dog_fashion = null
diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm
index c4e8f9133175..d69460e6a1ce 100644
--- a/code/modules/clothing/shoes/_shoes.dm
+++ b/code/modules/clothing/shoes/_shoes.dm
@@ -114,9 +114,10 @@
* *
* * state: SHOES_UNTIED, SHOES_TIED, or SHOES_KNOTTED, depending on what you want them to become
* * user: used to check to see if we're the ones unknotting our own laces
+ * * force_lacing: boolean. if TRUE, ignores can_be_tied
*/
-/obj/item/clothing/shoes/proc/adjust_laces(state, mob/user)
- if(!can_be_tied)
+/obj/item/clothing/shoes/proc/adjust_laces(state, mob/user, force_lacing = FALSE)
+ if(!can_be_tied && !force_lacing)
return
var/mob/living/carbon/human/our_guy
diff --git a/code/modules/clothing/shoes/sneakers.dm b/code/modules/clothing/shoes/sneakers.dm
index f07ea00c5089..954925ffad5b 100644
--- a/code/modules/clothing/shoes/sneakers.dm
+++ b/code/modules/clothing/shoes/sneakers.dm
@@ -12,6 +12,11 @@
greyscale_config_inhand_right = /datum/greyscale_config/sneakers_inhand_right
flags_1 = IS_PLAYER_COLORABLE_1
+/obj/item/clothing/shoes/sneakers/random/Initialize(mapload)
+ . = ..()
+ greyscale_colors = "#" + random_color() + "#" + random_color()
+ update_greyscale()
+
/obj/item/clothing/shoes/sneakers/black
name = "black shoes"
desc = "A pair of black shoes."
diff --git a/code/modules/clothing/suits/bio.dm b/code/modules/clothing/suits/bio.dm
index 4543014ae7a8..150af82f26e0 100644
--- a/code/modules/clothing/suits/bio.dm
+++ b/code/modules/clothing/suits/bio.dm
@@ -13,10 +13,11 @@
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH | PEPPERPROOF
supports_variations_flags = CLOTHING_SNOUTED_VARIATION
-/obj/item/clothing/head/bio_hood/Initialize(mapload)
- . = ..()
- if(flags_inv & HIDEFACE)
- AddComponent(/datum/component/clothing_fov_visor, FOV_90_DEGREES)
+// MONKESTATION REMOVAL
+//obj/item/clothing/head/bio_hood/Initialize(mapload)
+ //. = ..()
+ //if(flags_inv & HIDEFACE)
+ // AddComponent(/datum/component/clothing_fov_visor, FOV_90_DEGREES)
/datum/armor/head_bio_hood
bio = 100
diff --git a/code/modules/events/_event.dm b/code/modules/events/_event.dm
index 0efede18f782..03a0cbd0718b 100644
--- a/code/modules/events/_event.dm
+++ b/code/modules/events/_event.dm
@@ -71,6 +71,12 @@
for(var/admin_setup_type in admin_setup_types)
admin_setup += new admin_setup_type(src)
+// monkestation start: fix some hard deletes
+/datum/round_event_control/Destroy(force)
+ QDEL_LIST(admin_setup)
+ return ..()
+// monkestation end
+
/datum/round_event_control/wizard
category = EVENT_CATEGORY_WIZARD
wizardevent = TRUE
@@ -96,8 +102,6 @@
return FALSE
if(roundstart && ((SSticker.round_start_time && (world.time - SSticker.round_start_time) >= 2 MINUTES) || (SSgamemode.ran_roundstart && !fake_check)))
return FALSE
- if(istype(src, /datum/round_event_control/antagonist/solo/from_ghosts) && (SSautotransfer.starttime + 85 MINUTES <= world.time))
- return TRUE // we allow all ghost roles to run at this point and dont care about other checks
// monkestation end
if(occurrences >= max_occurrences)
return FALSE
diff --git a/code/modules/events/_event_admin_setup.dm b/code/modules/events/_event_admin_setup.dm
index d4774d75869d..40051b719a8f 100644
--- a/code/modules/events/_event_admin_setup.dm
+++ b/code/modules/events/_event_admin_setup.dm
@@ -7,6 +7,12 @@
/datum/event_admin_setup/New(event_control)
src.event_control = event_control
+// monkestation start: fix some hard deletes
+/datum/event_admin_setup/Destroy(force)
+ event_control = null
+ return ..()
+// monkestation end
+
/datum/event_admin_setup/proc/prompt_admins()
SHOULD_CALL_PARENT(FALSE)
CRASH("Unimplemented prompt_admins() on [event_control]'s admin setup.")
diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm
index 58ed378be403..44a27e28ec34 100644
--- a/code/modules/events/brand_intelligence.dm
+++ b/code/modules/events/brand_intelligence.dm
@@ -1,3 +1,4 @@
+/* monkestation edit: overwritten in [code\modules\events\brand_intelligence.dm]
/datum/round_event_control/brand_intelligence
name = "Brand Intelligence"
typepath = /datum/round_event/brand_intelligence
@@ -93,3 +94,4 @@
/datum/event_admin_setup/listed_options/brand_intelligence/apply_to_event(datum/round_event/brand_intelligence/event)
event.chosen_vendor_type = chosen
+*/
diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm
index 821a01446406..a380750ff67e 100644
--- a/code/modules/events/carp_migration.dm
+++ b/code/modules/events/carp_migration.dm
@@ -4,7 +4,7 @@
weight = 15
min_players = 20 //monkie edit: 12 to 20
earliest_start = 40 MINUTES //monkie edit: 10 to 40
- max_occurrences = 2 //monkie edit: 6 to 2
+ max_occurrences = 3 //monkie edit: 6 to 2
category = EVENT_CATEGORY_ENTITIES
description = "Summons a school of space carp."
min_wizard_trigger_potency = 0
diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm
index da8c6409c43e..bdd60f16050f 100644
--- a/code/modules/events/disease_outbreak.dm
+++ b/code/modules/events/disease_outbreak.dm
@@ -22,10 +22,10 @@
/datum/round_event_control/disease_outbreak
name = "Disease Outbreak: Classic"
typepath = /datum/round_event/disease_outbreak
- max_occurrences = 1
+ max_occurrences = 2
min_players = 10
weight = 0
- track = EVENT_TRACK_MAJOR
+ track = EVENT_TRACK_MAJOR //monkie edit
earliest_start = 55 MINUTES
category = EVENT_CATEGORY_HEALTH
description = "A 'classic' virus will infect some members of the crew."
@@ -53,7 +53,7 @@
/datum/round_event_control/disease_outbreak/proc/generate_candidates()
disease_candidates.Cut() //We clear the list and rebuild it again.
for(var/mob/living/carbon/human/candidate in shuffle(GLOB.player_list)) //Player list is much more up to date and requires less checks(?)
- if(!(candidate.mind.assigned_role.job_flags & JOB_CREW_MEMBER) || candidate.stat == DEAD)
+ if(!(candidate.mind?.assigned_role?.job_flags & JOB_CREW_MEMBER) || candidate.stat == DEAD)
continue
if(HAS_TRAIT(candidate, TRAIT_VIRUSIMMUNE)) //Don't pick someone who's virus immune, only for it to not do anything.
continue
diff --git a/code/modules/events/ghost_role/alien_infestation.dm b/code/modules/events/ghost_role/alien_infestation.dm
index 9b35b2145dcf..db3fd898e9ae 100644
--- a/code/modules/events/ghost_role/alien_infestation.dm
+++ b/code/modules/events/ghost_role/alien_infestation.dm
@@ -87,25 +87,26 @@
if(temp_vent_parent.other_atmos_machines.len > 20)
vents += temp_vent
- if(!vents.len)
+ if(!length(vents))
message_admins("An event attempted to spawn an alien but no suitable vents were found. Shutting down.")
return MAP_ERROR
- for(var/i in 1 to antag_count)
- if(!length(candidates))
- break
-
- var/client/mob_client = pick_n_take_weighted(weighted_candidates)
- var/mob/candidate = mob_client.mob
- if(candidate.client) //I hate this
- candidate.client.prefs.reset_antag_rep()
+ var/selected_count = 0
+ while(length(weighted_candidates) && selected_count < antag_count)
+ var/client/candidate_ckey = pick_n_take_weighted(weighted_candidates)
+ var/client/candidate_client = GLOB.directory[candidate_ckey]
+ if(QDELETED(candidate_client) || QDELETED(candidate_client.mob))
+ continue
+ var/mob/candidate = candidate_client.mob
+ candidate_client.prefs?.reset_antag_rep()
if(!candidate.mind)
candidate.mind = new /datum/mind(candidate.key)
var/obj/vent = pick_n_take(vents)
var/mob/living/carbon/alien/larva/new_xeno = new(vent.loc)
- new_xeno.key = candidate.key
+ new_xeno.ckey = candidate_ckey
new_xeno.move_into_vent(vent)
+ selected_count++
message_admins("[ADMIN_LOOKUPFLW(new_xeno)] has been made into an alien by an event.")
new_xeno.log_message("was spawned as an alien by an event.", LOG_GAME)
diff --git a/code/modules/events/ghost_role/blob.dm b/code/modules/events/ghost_role/blob.dm
index 2cdf8174d63a..99836ba2b585 100644
--- a/code/modules/events/ghost_role/blob.dm
+++ b/code/modules/events/ghost_role/blob.dm
@@ -3,9 +3,8 @@
typepath = /datum/round_event/ghost_role/blob
weight = 5 //monkie edit: 10 to 5
max_occurrences = 1
-
min_players = 35 //monkie edit: 20 to 35
- earliest_start = 60 MINUTES //monkie edit: 20 to 90
+ earliest_start = 80 MINUTES //monkie edit: 20 to 90
//dynamic_should_hijack = TRUE
category = EVENT_CATEGORY_ENTITIES
description = "Spawns a new blob overmind."
diff --git a/code/modules/events/ghost_role/space_dragon.dm b/code/modules/events/ghost_role/space_dragon.dm
index cfcd2df52dcf..8d09fee4f4f8 100644
--- a/code/modules/events/ghost_role/space_dragon.dm
+++ b/code/modules/events/ghost_role/space_dragon.dm
@@ -33,8 +33,6 @@
var/mob/living/basic/space_dragon/dragon = new (spawn_location)
dragon.key = key
- dragon.mind.set_assigned_role(SSjob.GetJobType(/datum/job/space_dragon))
- dragon.mind.special_role = ROLE_SPACE_DRAGON
dragon.mind.add_antag_datum(/datum/antagonist/space_dragon)
playsound(dragon, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
message_admins("[ADMIN_LOOKUPFLW(dragon)] has been made into a Space Dragon by an event.")
diff --git a/code/modules/events/heart_attack.dm b/code/modules/events/heart_attack.dm
index 0cdadfbc499f..5a06238e5746 100644
--- a/code/modules/events/heart_attack.dm
+++ b/code/modules/events/heart_attack.dm
@@ -32,7 +32,7 @@
for(var/mob/living/carbon/human/candidate in shuffle(GLOB.player_list))
if(candidate.stat == DEAD || HAS_TRAIT(candidate, TRAIT_CRITICAL_CONDITION) || !candidate.can_heartattack() || candidate.undergoing_cardiac_arrest())
continue
- if(!(candidate.mind.assigned_role.job_flags & JOB_CREW_MEMBER))//only crewmembers can get one, a bit unfair for some ghost roles and it wastes the event
+ if(!(candidate.mind?.assigned_role?.job_flags & JOB_CREW_MEMBER))//only crewmembers can get one, a bit unfair for some ghost roles and it wastes the event
continue
if(candidate.satiety <= -60 && !candidate.has_status_effect(/datum/status_effect/exercised)) //Multiple junk food items recently //No foodmaxxing for the achievement
heart_attack_candidates[candidate] = 3
diff --git a/code/modules/events/meteors/stray_meteor_event.dm b/code/modules/events/meteors/stray_meteor_event.dm
index 53daff1ac8a1..09b91b36a1a2 100644
--- a/code/modules/events/meteors/stray_meteor_event.dm
+++ b/code/modules/events/meteors/stray_meteor_event.dm
@@ -1,9 +1,9 @@
/datum/round_event_control/stray_meteor
name = "Stray Meteor"
typepath = /datum/round_event/stray_meteor
- weight = 15 //Number subject to change based on how often meteors actually collide with the station
+ weight = 18 //Number subject to change based on how often meteors actually collide with the station
min_players = 15
- max_occurrences = 3
+ max_occurrences = 6
earliest_start = 20 MINUTES
category = EVENT_CATEGORY_SPACE
description = "Throw a random meteor somewhere near the station."
diff --git a/code/modules/events/portal_storm.dm b/code/modules/events/portal_storm.dm
index 5296814dfadd..55e26bc191bd 100644
--- a/code/modules/events/portal_storm.dm
+++ b/code/modules/events/portal_storm.dm
@@ -32,6 +32,32 @@
/mob/living/basic/construct/wraith/hostile = 6,
)
+//begin monkestation edit
+/datum/round_event_control/portal_storm_monkey
+ name = "Portal Storm: Monkeys"
+ typepath = /datum/round_event/portal_storm/portal_storm_monkey
+ weight = 4
+ max_occurrences = 2
+ earliest_start = 20 MINUTES
+ category = EVENT_CATEGORY_ENTITIES
+ track = EVENT_TRACK_MAJOR
+ description = "Angry monkies pour out of portals."
+
+/datum/round_event/portal_storm/portal_storm_monkey
+ boss_types = list(/mob/living/basic/gorilla/lesser = 1)
+ hostile_types = list(
+ /mob/living/carbon/human/species/monkey/angry = 10,
+ )
+
+/datum/round_event/portal_storm/portal_storm_monkey/announce(fake)
+ set waitfor = 0
+ sound_to_playing_players('sound/magic/lightning_chargeup.ogg')
+ sleep(8 SECONDS)
+ priority_announce("Massive bluespace anomaly detected en route to [station_name()]. Brace for impact.")
+ sleep(2 SECONDS)
+ sound_to_playing_players('monkestation/sound/misc/monkeystorm.ogg')
+
+//end monkestation edit
/datum/round_event/portal_storm
start_when = 7
end_when = 999
diff --git a/code/modules/events/scrubber_overflow.dm b/code/modules/events/scrubber_overflow.dm
index baa07d7e15cb..e9a4f80368fd 100644
--- a/code/modules/events/scrubber_overflow.dm
+++ b/code/modules/events/scrubber_overflow.dm
@@ -16,7 +16,7 @@
/// Amount of reagents ejected from each scrubber
var/reagents_amount = 50
/// Probability of an individual scrubber overflowing
- var/overflow_probability = 50
+ var/overflow_probability = 20 //monkestation edit: 20 down from 50
/// Specific reagent to force all scrubbers to use, null for random reagent choice
var/datum/reagent/forced_reagent_type
/// A list of scrubbers that will have reagents ejected from them
diff --git a/code/modules/events/spider_infestation.dm b/code/modules/events/spider_infestation.dm
index 63d965a523c6..bc740e5ba84b 100644
--- a/code/modules/events/spider_infestation.dm
+++ b/code/modules/events/spider_infestation.dm
@@ -1,7 +1,7 @@
/datum/round_event_control/spider_infestation
name = "Spider Infestation"
typepath = /datum/round_event/spider_infestation
- weight = 6 //monkestation edit: from 10 to 6
+ weight = 7 //monkestation edit: from 10 to 6
max_occurrences = 1
min_players = 35 //monkie edit: 20 to 35
earliest_start = 60 MINUTES //monke edit: 20 to 60
diff --git a/code/modules/events/wizard/curseditems.dm b/code/modules/events/wizard/curseditems.dm
index cc63c15cda5d..68e74ac30143 100644
--- a/code/modules/events/wizard/curseditems.dm
+++ b/code/modules/events/wizard/curseditems.dm
@@ -1,3 +1,16 @@
+/// Turns them into a psuedo-wizard costume.
+#define WIZARD_MIMICRY "wizardmimic"
+/// Gives them a cursed sword.
+#define CURSED_SWORDS "swords"
+/// Gives them a blunt that they need to smoke
+#define BIG_FAT_DOOBIE "bigfatdoobie"
+/// Gives them boxing gloves and a luchador mask
+#define BOXING "boxing"
+/// Gives them a chameleon mask
+#define VOICE_MODULATORS "voicemodulators"
+/// Gives them kitty ears and also modifies their gender to FEMALE
+#define CATGIRLS_2015 "catgirls2015"
+
/datum/round_event_control/wizard/cursed_items //fashion disasters
name = "Cursed Items"
weight = 3
@@ -11,51 +24,72 @@
//item you want to equip to the hand, and set its slots_flags = null. Only items equiped to hands need do this.
/datum/round_event/wizard/cursed_items/start()
- var/item_set = pick("wizardmimic", "swords", "bigfatdoobie", "boxing", "voicemodulators", "catgirls2015")
+ var/item_set = pick(
+ BIG_FAT_DOOBIE,
+ BOXING,
+ CATGIRLS_2015,
+ CURSED_SWORDS,
+ VOICE_MODULATORS,
+ WIZARD_MIMICRY,
+ )
var/list/loadout[SLOTS_AMT]
- var/ruins_spaceworthiness
- var/ruins_wizard_loadout
+ var/ruins_spaceworthiness = FALSE
+ var/ruins_wizard_loadout = FALSE
switch(item_set)
- if("wizardmimic")
- loadout[ITEM_SLOT_OCLOTHING] = /obj/item/clothing/suit/wizrobe
- loadout[ITEM_SLOT_FEET] = /obj/item/clothing/shoes/sandal/magic
- loadout[ITEM_SLOT_HEAD] = /obj/item/clothing/head/wizard
- ruins_spaceworthiness = 1
- if("swords")
- loadout[ITEM_SLOT_HANDS] = /obj/item/katana/cursed
- if("bigfatdoobie")
+ if(BIG_FAT_DOOBIE)
loadout[ITEM_SLOT_MASK] = /obj/item/clothing/mask/cigarette/rollie/trippy
- ruins_spaceworthiness = 1
- if("boxing")
+ ruins_spaceworthiness = TRUE
+ if(BOXING)
loadout[ITEM_SLOT_MASK] = /obj/item/clothing/mask/luchador
loadout[ITEM_SLOT_GLOVES] = /obj/item/clothing/gloves/boxing
- ruins_spaceworthiness = 1
- if("voicemodulators")
- loadout[ITEM_SLOT_MASK] = /obj/item/clothing/mask/chameleon
- if("catgirls2015")
+ ruins_spaceworthiness = TRUE
+ if(CATGIRLS_2015)
loadout[ITEM_SLOT_HEAD] = /obj/item/clothing/head/costume/kitty
- ruins_spaceworthiness = 1
- ruins_wizard_loadout = 1
+ ruins_spaceworthiness = TRUE
+ ruins_wizard_loadout = TRUE
+ if(CURSED_SWORDS)
+ loadout[ITEM_SLOT_HANDS] = /obj/item/katana/cursed
+ if(VOICE_MODULATORS)
+ loadout[ITEM_SLOT_MASK] = /obj/item/clothing/mask/chameleon
+ if(WIZARD_MIMICRY)
+ loadout[ITEM_SLOT_OCLOTHING] = /obj/item/clothing/suit/wizrobe
+ loadout[ITEM_SLOT_FEET] = /obj/item/clothing/shoes/sandal/magic
+ loadout[ITEM_SLOT_HEAD] = /obj/item/clothing/head/wizard
+ ruins_spaceworthiness = TRUE
- for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
- if(ruins_spaceworthiness && !is_station_level(H.z) || isspaceturf(H.loc) || isplasmaman(H))
+ var/list/mob/living/carbon/human/victims = list()
+
+ for(var/mob/living/carbon/human/target in GLOB.alive_mob_list)
+ if(isspaceturf(target.loc) || !isnull(target.dna.species.outfit_important_for_life) || (ruins_spaceworthiness && !is_station_level(target.z)))
continue //#savetheminers
- if(ruins_wizard_loadout && IS_WIZARD(H))
+ if(ruins_wizard_loadout && IS_WIZARD(target))
continue
- if(item_set == "catgirls2015") //Wizard code means never having to say you're sorry
- H.gender = FEMALE
- for(var/i in 1 to loadout.len)
- if(loadout[i])
- var/obj/item/J = loadout[i]
- var/obj/item/I = new J //dumb but required because of byond throwing a fit anytime new gets too close to a list
- H.dropItemToGround(H.get_item_by_slot(i), TRUE)
- H.equip_to_slot_or_del(I, i)
- ADD_TRAIT(I, TRAIT_NODROP, CURSED_ITEM_TRAIT(I))
- I.item_flags |= DROPDEL
- I.name = "cursed " + I.name
-
- for(var/mob/living/carbon/human/victim in GLOB.alive_mob_list)
+ if(item_set == CATGIRLS_2015) //Wizard code means never having to say you're sorry
+ target.gender = FEMALE
+ for(var/iterable in 1 to loadout.len)
+ if(!loadout[iterable])
+ continue
+
+ var/obj/item/item_type = loadout[iterable]
+ var/obj/item/thing = new item_type //dumb but required because of byond throwing a fit anytime new gets too close to a list
+
+ target.dropItemToGround(target.get_item_by_slot(iterable), TRUE)
+ target.equip_to_slot_or_del(thing, iterable)
+ ADD_TRAIT(thing, TRAIT_NODROP, CURSED_ITEM_TRAIT(thing))
+ thing.item_flags |= DROPDEL
+ thing.name = "cursed " + thing.name
+
+ victims += target
+
+ for(var/mob/living/carbon/human/victim as anything in victims)
var/datum/effect_system/fluid_spread/smoke/smoke = new
smoke.set_up(0, holder = victim, location = victim.loc)
smoke.start()
+
+#undef BIG_FAT_DOOBIE
+#undef BOXING
+#undef CATGIRLS_2015
+#undef CURSED_SWORDS
+#undef VOICE_MODULATORS
+#undef WIZARD_MIMICRY
diff --git a/code/modules/food_and_drinks/machinery/processor.dm b/code/modules/food_and_drinks/machinery/processor.dm
index 7ff1e07db07d..627ff85fca0f 100644
--- a/code/modules/food_and_drinks/machinery/processor.dm
+++ b/code/modules/food_and_drinks/machinery/processor.dm
@@ -153,9 +153,12 @@
log_admin("DEBUG: [movable_input] in processor doesn't have a suitable recipe. How did it get in there? Please report it immediately!!!")
continue
total_time += recipe.time
- var/offset = prob(50) ? -2 : 2
- animate(src, pixel_x = pixel_x + offset, time = 0.2, loop = (total_time / rating_speed)*5) //start shaking
- sleep(total_time / rating_speed)
+
+ var/duration = (total_time / rating_speed)
+ INVOKE_ASYNC(src, TYPE_PROC_REF(/atom, Shake), 1, 0, duration)
+ addtimer(CALLBACK(src, PROC_REF(complete_processing)), duration)
+
+/obj/machinery/processor/proc/complete_processing()
for(var/atom/movable/content_item in processor_contents)
var/datum/food_processor_process/recipe = PROCESSOR_SELECT_RECIPE(content_item)
if (!recipe)
diff --git a/code/modules/food_and_drinks/recipes/soup_mixtures.dm b/code/modules/food_and_drinks/recipes/soup_mixtures.dm
index 90a9e82d9f90..8a9c092d2b4f 100644
--- a/code/modules/food_and_drinks/recipes/soup_mixtures.dm
+++ b/code/modules/food_and_drinks/recipes/soup_mixtures.dm
@@ -194,6 +194,7 @@
transfer_ingredient_reagents(ingredient, holder)
// Delete, it's done
qdel(ingredient)
+ continue
// Everything else will just get fried
ingredient.AddElement(/datum/element/fried_item, 30)
@@ -238,7 +239,7 @@
ingredient_pool.remove_reagent(/datum/reagent/consumable/nutriment, amount * percentage_of_nutriment_converted)
ingredient_pool.remove_reagent(/datum/reagent/consumable/nutriment/vitamin, amount * percentage_of_nutriment_converted)
// The other half of the nutriment, and the rest of the reagents, will get put directly into the pot
- ingredient_pool.trans_to(holder, amount, ingredient_reagent_multiplier, no_react = TRUE)
+ ingredient_pool.trans_to(holder, amount, ingredient_reagent_multiplier, preserve_data = FALSE, no_react = TRUE)
/// Called whenever the soup pot overfills with reagent.
/datum/chemical_reaction/food/soup/proc/boil_over(datum/reagents/holder)
diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm
index 21545dc49b65..8749875221aa 100644
--- a/code/modules/holiday/holidays.dm
+++ b/code/modules/holiday/holidays.dm
@@ -18,13 +18,17 @@
var/year_offset = 0
///Timezones this holiday is celebrated in (defaults to three timezones spanning a 50 hour window covering all timezones)
var/list/timezones = list(TIMEZONE_LINT, TIMEZONE_UTC, TIMEZONE_ANYWHERE_ON_EARTH)
- ///If this is defined, drones without a default hat will spawn with this one during the holiday; check drones_as_items.dm to see this used
- var/obj/item/drone_hat
+ ///If this is defined, drones/assistants without a default hat will spawn with this item in their head clothing slot.
+ var/obj/item/holiday_hat
///When this holiday is active, does this prevent mail from arriving to cargo? Try not to use this for longer holidays.
var/mail_holiday = FALSE
var/poster_name = "generic celebration poster"
var/poster_desc = "A poster for celebrating some holiday. Unfortunately, its unfinished, so you can't see what the holiday is."
var/poster_icon = "holiday_unfinished"
+ /// Color scheme for this holiday
+ var/list/holiday_colors
+ /// The default pattern of the holiday, if the requested pattern is null.
+ var/holiday_pattern = PATTERN_DEFAULT
// This proc gets run before the game starts when the holiday is activated. Do festive shit here.
/datum/holiday/proc/celebrate()
@@ -75,6 +79,31 @@
return FALSE
+/// Procs to return holiday themed colors for recoloring atoms
+/datum/holiday/proc/get_holiday_colors(atom/thing_to_color, pattern = holiday_pattern)
+ if(!holiday_colors)
+ return
+ switch(pattern)
+ if(PATTERN_DEFAULT)
+ return holiday_colors[(thing_to_color.y % holiday_colors.len) + 1]
+ if(PATTERN_VERTICAL_STRIPE)
+ return holiday_colors[(thing_to_color.x % holiday_colors.len) + 1]
+
+/proc/request_holiday_colors(atom/thing_to_color, pattern)
+ switch(pattern)
+ if(PATTERN_RANDOM)
+ return "#[random_short_color()]"
+ if(PATTERN_RAINBOW)
+ var/datum/holiday/pride_week/rainbow_datum = new()
+ return rainbow_datum.get_holiday_colors(thing_to_color, PATTERN_DEFAULT)
+ if(!length(GLOB.holidays))
+ return
+ for(var/holiday_key in GLOB.holidays)
+ var/datum/holiday/holiday_real = GLOB.holidays[holiday_key]
+ if(!holiday_real.holiday_colors)
+ continue
+ return holiday_real.get_holiday_colors(thing_to_color, pattern || holiday_real.holiday_pattern)
+
// The actual holidays
// JANUARY
@@ -84,6 +113,7 @@
name = "Fleet Day"
begin_month = JANUARY
begin_day = 19
+ holiday_hat = /obj/item/clothing/head/mothcap
/datum/holiday/fleet_day/greet()
return "This day commemorates another year of successful survival aboard the Mothic Grand Nomad Fleet. Moths galaxywide are encouraged to eat, drink, and be merry."
@@ -106,6 +136,12 @@
timezones = list(TIMEZONE_NZDT, TIMEZONE_CHADT)
begin_day = 6
begin_month = FEBRUARY
+ holiday_colors = list(
+ COLOR_UNION_JACK_BLUE,
+ COLOR_WHITE,
+ COLOR_UNION_JACK_RED,
+ COLOR_WHITE,
+ )
/datum/holiday/nz/getStationPrefix()
return pick("Aotearoa","Kiwi","Fish 'n' Chips","Kākāpō","Southern Cross")
@@ -130,7 +166,7 @@
name = "Birthday of Space Station 13"
begin_day = 16
begin_month = FEBRUARY
- drone_hat = /obj/item/clothing/head/costume/festive
+ holiday_hat = /obj/item/clothing/head/costume/festive
poster_name = "station birthday poster"
poster_desc = "A poster celebrating another year of the station's operation. Why anyone would be happy to be here is byond you."
poster_icon = "holiday_cake" // is a lie
@@ -195,7 +231,13 @@
name = "St. Patrick's Day"
begin_day = 17
begin_month = MARCH
- drone_hat = /obj/item/clothing/head/soft/green
+ holiday_hat = /obj/item/clothing/head/soft/green
+ holiday_colors = list(
+ COLOR_IRISH_GREEN,
+ COLOR_WHITE,
+ COLOR_IRISH_ORANGE,
+ )
+ holiday_pattern = PATTERN_VERTICAL_STRIPE
/datum/holiday/no_this_is_patrick/getStationPrefix()
return pick("Blarney","Green","Leprechaun","Booze")
@@ -210,6 +252,7 @@
begin_month = APRIL
begin_day = 1
end_day = 2
+ holiday_hat = /obj/item/clothing/head/chameleon/broken
/datum/holiday/april_fools/celebrate()
. = ..()
@@ -220,13 +263,19 @@
var/mob/dead/new_player/P = i
if(P.client)
P.client.playtitlemusic()
- */
+ */ //monkestation removal end
+
+/datum/holiday/april_fools/get_holiday_colors(atom/thing_to_color)
+ return "#[random_short_color()]"
+
+/datum/holiday/april_fools/get_holiday_colors(atom/thing_to_color)
+ return "#[random_short_color()]"
/datum/holiday/spess
name = "Cosmonautics Day"
begin_day = 12
begin_month = APRIL
- drone_hat = /obj/item/clothing/head/syndicatefake
+ holiday_hat = /obj/item/clothing/head/syndicatefake
/datum/holiday/spess/greet()
return "On this day over 600 years ago, Comrade Yuri Gagarin first ventured into space!"
@@ -235,6 +284,12 @@
name = "Four-Twenty"
begin_day = 20
begin_month = APRIL
+ holiday_hat = /obj/item/clothing/head/rasta
+ holiday_colors = list(
+ COLOR_ETHIOPIA_GREEN,
+ COLOR_ETHIOPIA_YELLOW,
+ COLOR_ETHIOPIA_RED,
+ )
/datum/holiday/fourtwenty/getStationPrefix()
return pick("Snoop","Blunt","Toke","Dank","Cheech","Chong")
@@ -257,7 +312,7 @@
timezones = list(TIMEZONE_TKT, TIMEZONE_TOT, TIMEZONE_NZST, TIMEZONE_NFT, TIMEZONE_LHST, TIMEZONE_AEST, TIMEZONE_ACST, TIMEZONE_ACWST, TIMEZONE_AWST, TIMEZONE_CXT, TIMEZONE_CCT, TIMEZONE_CKT, TIMEZONE_NUT)
begin_day = 25
begin_month = APRIL
- drone_hat = /obj/item/food/grown/poppy
+ holiday_hat = /obj/item/food/grown/poppy
/datum/holiday/anz/getStationPrefix()
return pick("Australian","New Zealand","Poppy", "Southern Cross")
@@ -268,7 +323,7 @@
name = "Labor Day"
begin_day = 1
begin_month = MAY
- drone_hat = /obj/item/clothing/head/utility/hardhat
+ holiday_hat = /obj/item/clothing/head/utility/hardhat
mail_holiday = TRUE
//Draconic Day is celebrated on May 3rd, the date on which the Draconic language was merged (#26780)
@@ -287,7 +342,7 @@
name = "Firefighter's Day"
begin_day = 4
begin_month = MAY
- drone_hat = /obj/item/clothing/head/utility/hardhat/red
+ holiday_hat = /obj/item/clothing/head/utility/hardhat/red
/datum/holiday/firefighter/getStationPrefix()
return pick("Burning","Blazing","Plasma","Fire")
@@ -296,7 +351,6 @@
name = "Bee Day"
begin_day = 20
begin_month = MAY
- drone_hat = /obj/item/clothing/mask/animal/small/bee
/datum/holiday/bee/getStationPrefix()
return pick("Bee","Honey","Hive","Africanized","Mead","Buzz")
@@ -329,6 +383,22 @@
name = "Summer Solstice"
begin_day = 21
begin_month = JUNE
+ holiday_hat = /obj/item/clothing/head/costume/garland
+
+/datum/holiday/pride_week
+ name = PRIDE_WEEK
+ begin_month = JUNE
+ // Stonewall was June 28th, this captures its week.
+ begin_day = 23
+ end_day = 29
+ holiday_colors = list(
+ COLOR_PRIDE_PURPLE,
+ COLOR_PRIDE_BLUE,
+ COLOR_PRIDE_GREEN,
+ COLOR_PRIDE_YELLOW,
+ COLOR_PRIDE_ORANGE,
+ COLOR_PRIDE_RED,
+ )
// JULY
@@ -336,13 +406,13 @@
name = "Doctor's Day"
begin_day = 1
begin_month = JULY
- drone_hat = /obj/item/clothing/head/costume/nursehat
+ holiday_hat = /obj/item/clothing/head/costume/nursehat
/datum/holiday/ufo
name = "UFO Day"
begin_day = 2
begin_month = JULY
- drone_hat = /obj/item/clothing/mask/facehugger/dead
+ holiday_hat = /obj/item/clothing/head/collectable/xenom
/datum/holiday/ufo/getStationPrefix() //Is such a thing even possible?
return pick("Ayy","Truth","Tsoukalos","Mulder","Scully") //Yes it is!
@@ -353,6 +423,15 @@
begin_day = 4
begin_month = JULY
mail_holiday = TRUE
+ holiday_hat = /obj/item/clothing/head/cowboy/brown
+ holiday_colors = list(
+ COLOR_OLD_GLORY_BLUE,
+ COLOR_OLD_GLORY_RED,
+ COLOR_WHITE,
+ COLOR_OLD_GLORY_RED,
+ COLOR_WHITE,
+ )
+
/datum/holiday/usa/getStationPrefix()
return pick("Independent","American","Burger","Bald Eagle","Star-Spangled", "Fireworks")
@@ -367,11 +446,17 @@
timezones = list(TIMEZONE_CEST)
begin_day = 14
begin_month = JULY
- drone_hat = /obj/item/clothing/head/beret
+ holiday_hat = /obj/item/clothing/head/beret
mail_holiday = TRUE
+ holiday_colors = list(
+ COLOR_FRENCH_BLUE,
+ COLOR_WHITE,
+ COLOR_FRENCH_RED
+ )
+ holiday_pattern = PATTERN_VERTICAL_STRIPE
/datum/holiday/france/getStationPrefix()
- return pick("Francais","Fromage", "Zut", "Merde")
+ return pick("Francais", "Fromage", "Zut", "Merde", "Sacrebleu")
/datum/holiday/france/greet()
return "Do you hear the people sing?"
@@ -389,7 +474,7 @@
name = "Wizard's Day"
begin_month = JULY
begin_day = 27
- drone_hat = /obj/item/clothing/head/wizard
+ holiday_hat = /obj/item/clothing/head/wizard
/datum/holiday/wizards_day/getStationPrefix()
return pick("Dungeon", "Elf", "Magic", "D20", "Edition")
@@ -421,6 +506,7 @@
name = "Tiziran Unification Day"
begin_month = SEPTEMBER
begin_day = 1
+ holiday_hat = /obj/item/clothing/head/costume/lizard
/datum/holiday/tiziran_unification/greet()
return "On this day over 400 years ago, Lizardkind first united under a single banner, ready to face the stars as one unified people."
@@ -444,7 +530,7 @@
name = "Talk-Like-a-Pirate Day"
begin_day = 19
begin_month = SEPTEMBER
- drone_hat = /obj/item/clothing/head/costume/pirate
+ holiday_hat = /obj/item/clothing/head/costume/pirate
/datum/holiday/pirate/greet()
return "Ye be talkin' like a pirate today or else ye'r walkin' tha plank, matey!"
@@ -474,13 +560,13 @@
name = "Smiling Day"
begin_day = 7
begin_month = OCTOBER
- drone_hat = /obj/item/clothing/head/costume/papersack/smiley
+ holiday_hat = /obj/item/clothing/head/costume/papersack/smiley
/datum/holiday/boss
name = "Boss' Day"
begin_day = 16
begin_month = OCTOBER
- drone_hat = /obj/item/clothing/head/hats/tophat
+ holiday_hat = /obj/item/clothing/head/hats/tophat
/datum/holiday/un_day
name = "Anniversary of the Foundation of the United Nations"
@@ -516,12 +602,25 @@
/datum/holiday/vegan/getStationPrefix()
return pick("Tofu", "Tempeh", "Seitan", "Tofurkey")
+/datum/holiday/october_revolution
+ name = "October Revolution"
+ begin_day = 6
+ begin_month = NOVEMBER
+ end_day = 7
+ holiday_colors = list(
+ COLOR_MEDIUM_DARK_RED,
+ COLOR_GOLD,
+ COLOR_MEDIUM_DARK_RED,
+ )
+
+/datum/holiday/october_revolution/getStationPrefix()
+ return pick("Communist", "Soviet", "Bolshevik", "Socialist", "Red", "Workers'")
/datum/holiday/remembrance_day
name = "Remembrance Day"
begin_month = NOVEMBER
begin_day = 11
- drone_hat = /obj/item/food/grown/poppy
+ holiday_hat = /obj/item/food/grown/poppy
/datum/holiday/remembrance_day/getStationPrefix()
return pick("Peace", "Armistice", "Poppy")
@@ -543,7 +642,7 @@
name = "Flowers Day"
begin_day = 19
begin_month = NOVEMBER
- drone_hat = /obj/item/food/grown/moonflower
+ holiday_hat = /obj/item/food/grown/moonflower
/datum/holiday/hello
name = "Saying-'Hello' Day"
@@ -572,7 +671,7 @@
begin_day = 1
begin_month = DECEMBER
end_day = 31
- drone_hat = /obj/item/clothing/head/costume/santa
+ holiday_hat = /obj/item/clothing/head/costume/santa
/datum/holiday/festive_season/greet()
return "Have a nice festive season!"
@@ -586,21 +685,23 @@
name = MONKEYDAY
begin_day = 14
begin_month = DECEMBER
- drone_hat = /obj/item/clothing/mask/gas/monkeymask
/datum/holiday/doomsday
name = "Mayan Doomsday Anniversary"
begin_day = 21
begin_month = DECEMBER
- drone_hat = /obj/item/clothing/mask/animal/small/tribal
/datum/holiday/xmas
name = CHRISTMAS
begin_day = 23
begin_month = DECEMBER
end_day = 27
- drone_hat = /obj/item/clothing/head/costume/santa
+ holiday_hat = /obj/item/clothing/head/costume/santa
mail_holiday = TRUE
+ holiday_colors = list(
+ COLOR_CHRISTMAS_GREEN,
+ COLOR_CHRISTMAS_RED,
+ )
/datum/holiday/xmas/greet()
return "Have a merry Christmas!"
@@ -616,7 +717,7 @@
begin_month = DECEMBER
end_day = 2
end_month = JANUARY
- drone_hat = /obj/item/clothing/head/costume/festive
+ holiday_hat = /obj/item/clothing/head/costume/festive
mail_holiday = TRUE
/datum/holiday/new_year/getStationPrefix()
@@ -706,7 +807,6 @@
/datum/holiday/hebrew/passover/getStationPrefix()
return pick("Matzah", "Moses", "Red Sea")
-
// HOLIDAY ADDONS
/datum/holiday/xmas/celebrate()
@@ -732,7 +832,7 @@
/datum/holiday/easter
name = EASTER
- drone_hat = /obj/item/clothing/head/costume/rabbitears
+ holiday_hat = /obj/item/clothing/head/costume/rabbitears
var/const/days_early = 1 //to make editing the holiday easier
var/const/days_extra = 1
diff --git a/code/modules/holiday/nth_week.dm b/code/modules/holiday/nth_week.dm
index 55cfec74be6e..ef4815de0664 100644
--- a/code/modules/holiday/nth_week.dm
+++ b/code/modules/holiday/nth_week.dm
@@ -35,7 +35,7 @@
begin_week = 4
begin_month = NOVEMBER
begin_weekday = THURSDAY
- drone_hat = /obj/item/clothing/head/hats/tophat //This is the closest we can get to a pilgrim's hat
+ holiday_hat = /obj/item/clothing/head/hats/tophat //This is the closest we can get to a pilgrim's hat
/datum/holiday/nth_week/thanksgiving/canada
name = "Thanksgiving in Canada"
diff --git a/code/modules/hydroponics/gene_modder.dm b/code/modules/hydroponics/gene_modder.dm
index 70fae57a0bb8..fad5063ec7e7 100644
--- a/code/modules/hydroponics/gene_modder.dm
+++ b/code/modules/hydroponics/gene_modder.dm
@@ -426,6 +426,10 @@
var/read_only = 0 //Well, it's still a floppy disk
obj_flags = UNIQUE_RENAME
+/obj/item/disk/plantgene/syndicate // Monkestation item
+ gene = new /datum/plant_gene/trait/noreact
+ read_only = 1
+
/obj/item/disk/plantgene/Initialize(mapload)
. = ..()
add_overlay("datadisk_gene")
diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm
index 274a88418671..b014ecf116e8 100644
--- a/code/modules/hydroponics/grown.dm
+++ b/code/modules/hydroponics/grown.dm
@@ -42,6 +42,8 @@
var/alt_icon
/// Should we pixel offset ourselves at init? for mapping
var/offset_at_init = TRUE
+ //This volume can be altered by densified chemicals trait
+ var/volume_rate = 1 //Monkestation Edit
/obj/item/food/grown/Initialize(mapload, obj/item/seeds/new_seed)
if(!tastes)
@@ -65,16 +67,26 @@
make_dryable()
// Go through all traits in their genes and call on_new_plant from them.
+ //TODO: We need a priority queue for traits,some of them need to be called first
for(var/datum/plant_gene/trait/trait in seed.genes)
trait.on_new_plant(src, loc)
+ //Needs to be run after traits are called because some of them alter max_volume and volume_rate
+ //Since traits do not know in which order they were run we need to do it here
+ max_volume *= volume_rate //Monkestation Edit
+
// Set our default bitesize: bite size = 1 + (potency * 0.05) * (max_volume * 0.01) * modifier
// A 100 potency, non-densified plant = 1 + (5 * 1 * modifier) = 6u bite size
// For reference, your average 100 potency tomato has 14u of reagents - So, with no modifier it is eaten in 3 bites
bite_consumption = 1 + round(max((seed.potency * BITE_SIZE_POTENCY_MULTIPLIER), 1) * (max_volume * BITE_SIZE_VOLUME_MULTIPLIER) * bite_consumption_mod)
. = ..() //Only call it here because we want all the genes and shit to be applied before we add edibility. God this code is a mess.
-
+ //Monkestation Edit Begin
+ //We want this trait to run after reagents component is added to the plant
+ var/datum/plant_gene/trait/trait_noreact = seed.get_gene(/datum/plant_gene/trait/noreact)
+ if(trait_noreact)
+ trait_noreact.on_new_plant(src, loc)
+ //Monkestation Edit End
seed.prepare_result(src)
transform *= TRANSFORM_USING_VARIABLE(seed.potency, 100) + 0.5 //Makes the resulting produce's sprite larger or smaller based on potency!
diff --git a/code/modules/hydroponics/grown/mushrooms.dm b/code/modules/hydroponics/grown/mushrooms.dm
index 8d88dba452f6..f00b980b793e 100644
--- a/code/modules/hydroponics/grown/mushrooms.dm
+++ b/code/modules/hydroponics/grown/mushrooms.dm
@@ -232,7 +232,7 @@
potency = 30 //-> brightness
growthstages = 4
rarity = 20
- genes = list(/datum/plant_gene/trait/glow, /datum/plant_gene/trait/plant_type/fungal_metabolism, /datum/plant_gene/trait/potencylimit)
+ genes = list(/datum/plant_gene/trait/glow, /datum/plant_gene/trait/plant_type/fungal_metabolism)
growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi'
possible_mutations = list(/datum/hydroponics/plant_mutation/glow_cap, /datum/hydroponics/plant_mutation/shadow_shroom)
reagents_add = list(/datum/reagent/uranium/radium = 0.1, /datum/reagent/phosphorus = 0.1, /datum/reagent/consumable/nutriment = 0.04)
diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm
index 9007644983fe..7a45f27d23a8 100644
--- a/code/modules/hydroponics/hydroponics.dm
+++ b/code/modules/hydroponics/hydroponics.dm
@@ -794,16 +794,16 @@
/obj/machinery/hydroponics/attackby(obj/item/O, mob/user, params)
//Called when mob user "attacks" it with object O
- if(istype(O, /obj/item/bio_cube))
+ if(istype(O, /obj/item/stack/biocube))
if(bio_boosted)
to_chat(user, span_notice("This tray is already bio-boosted please wait until its no longer bio-boosted to apply it again"))
return
- var/obj/item/bio_cube/attacked_cube = O
+ var/obj/item/stack/biocube/attacked_cube = O
bio_boosted = TRUE
- addtimer(CALLBACK(src, PROC_REF(end_boost)), attacked_cube.total_duration)
- to_chat(user, span_notice("The [attacked_cube.name] dissolves boosting the growth of plants for [attacked_cube.total_duration * 0.1] seconds."))
+ var/boost_time = attacked_cube.boost_time()
+ addtimer(CALLBACK(src, PROC_REF(end_boost)), boost_time)
+ to_chat(user, span_notice("\The [attacked_cube] dissolves boosting the growth of plants for [DisplayTimeText(boost_time)]."))
qdel(attacked_cube)
-
if(IS_EDIBLE(O) || is_reagent_container(O)) // Syringe stuff (and other reagent containers now too)
var/obj/item/reagent_containers/reagent_source = O
diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm
index 4f92ba56807e..875de6a3a7b1 100644
--- a/code/modules/hydroponics/plant_genes.dm
+++ b/code/modules/hydroponics/plant_genes.dm
@@ -3,7 +3,7 @@
/// The name of the gene.
var/name
/// The font awesome icon name representing the gene in the seed extractor UI
- var/icon = "dna"
+ var/icon = FA_ICON_DNA
/// Flags that determine if a gene can be modified.
var/mutability_flags
@@ -271,7 +271,7 @@
/// Allows the plant to be squashed when thrown or slipped on, leaving a colored mess and trash type item behind.
/datum/plant_gene/trait/squash
name = "Liquid Contents"
- icon = "droplet"
+ icon = FA_ICON_DROPLET
description = "It may burst open from the internal pressure on impact."
trait_ids = THROW_IMPACT_ID | REAGENT_TRANSFER_ID | ATTACK_SELF_ID
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
@@ -327,7 +327,7 @@
/datum/plant_gene/trait/slip
name = "Slippery Skin"
description = "Watch your step around this."
- icon = "person-falling"
+ icon = FA_ICON_PERSON_FALLING
rate = 1.6
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
@@ -361,7 +361,7 @@
/datum/plant_gene/trait/cell_charge
name = "Electrical Activity"
description = "It can electrocute on interaction or recharge batteries when eaten."
- icon = "bolt"
+ icon = FA_ICON_BOLT
rate = 0.2
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
var/prob_mult = 2
@@ -430,7 +430,7 @@
*/
/datum/plant_gene/trait/glow
name = "Bioluminescence"
- icon = "lightbulb"
+ icon = FA_ICON_LIGHTBULB
rate = 0.03
description = "It emits a soft glow."
trait_ids = GLOW_ID
@@ -459,7 +459,7 @@
*/
/datum/plant_gene/trait/glow/shadow
name = "Shadow Emission"
- icon = "lightbulb-o"
+ icon = FA_ICON_MOON
rate = 0.04
glow_color = "#AAD84B"
@@ -510,7 +510,7 @@
/datum/plant_gene/trait/teleport
name = "Bluespace Activity"
description = "It causes people to teleport on interaction."
- icon = "right-left"
+ icon = FA_ICON_RIGHT_LEFT
rate = 0.1
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
@@ -575,7 +575,7 @@
/datum/plant_gene/trait/maxchem
name = "Densified Chemicals"
description = "The reagent volume is doubled, halving the plant yield instead."
- icon = "flask-vial"
+ icon = FA_ICON_FLASK_VIAL
rate = 2
trait_flags = TRAIT_HALVES_YIELD
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
@@ -587,8 +587,7 @@
var/obj/item/food/grown/grown_plant = our_plant
if(istype(grown_plant, /obj/item/food/grown))
- //Grown foods use the edible component so we need to change their max_volume var
- grown_plant.max_volume *= rate
+ grown_plant.volume_rate = rate //Monkestation Edit
else
//Grown inedibles however just use a reagents holder, so.
our_plant.reagents?.maximum_volume *= rate
@@ -597,7 +596,7 @@
/datum/plant_gene/trait/repeated_harvest
name = "Perennial Growth"
description = "It may be harvested multiple times from the same plant."
- icon = "cubes-stacked"
+ icon = FA_ICON_CUBES_STACKED
/// Don't allow replica pods to be multi harvested, please.
seed_blacklist = list(/obj/item/seeds/replicapod)
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
@@ -610,7 +609,7 @@
/datum/plant_gene/trait/battery
name = "Capacitive Cell Production"
description = "It can work like a power cell when wired properly."
- icon = "car-battery"
+ icon = FA_ICON_CAR_BATTERY
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
/// The number of cables needed to make a battery.
var/cables_needed_per_battery = 5
@@ -690,7 +689,7 @@
/datum/plant_gene/trait/stinging
name = "Hypodermic Prickles"
description = "It stings, passing some reagents in the process."
- icon = "syringe"
+ icon = FA_ICON_SYRINGE
trait_ids = REAGENT_TRANSFER_ID
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
@@ -729,7 +728,7 @@
/datum/plant_gene/trait/smoke
name = "Gaseous Decomposition"
description = "It can be smashed to turn its Liquid Contents into smoke."
- icon = "cloud"
+ icon = FA_ICON_CLOUD
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
/datum/plant_gene/trait/smoke/on_new_plant(obj/item/our_plant, newloc)
@@ -762,7 +761,7 @@
/datum/plant_gene/trait/fire_resistance
name = "Fire Resistance"
description = "Makes the seeds, plant and produce fireproof."
- icon = "fire"
+ icon = FA_ICON_FIRE
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
/datum/plant_gene/trait/fire_resistance/on_new_seed(obj/item/seeds/new_seed)
@@ -785,7 +784,7 @@
/datum/plant_gene/trait/invasive
name = "Invasive Spreading"
description = "It attempts to spread around if not contained."
- icon = "virus"
+ icon = FA_ICON_VIRUS
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
/datum/plant_gene/trait/invasive/on_new_seed(obj/item/seeds/new_seed)
@@ -851,7 +850,7 @@
/datum/plant_gene/trait/brewing
name = "Auto-Distilling Composition"
description = "Its nutriments undergo fermentation."
- icon = "wine-glass"
+ icon = FA_ICON_WINE_GLASS
trait_ids = CONTENTS_CHANGE_ID
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
@@ -863,7 +862,7 @@
/datum/plant_gene/trait/juicing
name = "Auto-Juicing Composition"
description = "Its nutriments turn into juice."
- icon = "glass-water"
+ icon = FA_ICON_GLASS_WATER
trait_ids = CONTENTS_CHANGE_ID
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
@@ -875,7 +874,7 @@
/datum/plant_gene/trait/plant_laughter
name = "Hallucinatory Feedback"
description = "Makes sounds when people slip on it."
- icon = "face-laugh-squint"
+ icon = FA_ICON_FACE_LAUGH_SQUINT
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
/// Sounds that play when this trait triggers
var/list/sounds = list('sound/items/SitcomLaugh1.ogg', 'sound/items/SitcomLaugh2.ogg', 'sound/items/SitcomLaugh3.ogg')
@@ -911,7 +910,7 @@
/datum/plant_gene/trait/eyes
name = "Oculary Mimicry"
description = "It will watch after you."
- icon = "eye"
+ icon = FA_ICON_EYE
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
/// Our googly eyes appearance.
var/mutable_appearance/googly
@@ -929,7 +928,7 @@
/datum/plant_gene/trait/sticky
name = "Prickly Adhesion"
description = "It sticks to people when thrown, also passing reagents if stingy."
- icon = "bandage"
+ icon = FA_ICON_BANDAGE
trait_ids = THROW_IMPACT_ID
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
@@ -953,7 +952,7 @@
/datum/plant_gene/trait/chem_heating
name = "Exothermic Activity"
description = "It consumes nutriments to heat up other reagents, halving the yield."
- icon = "temperatyre-arrow-up"
+ icon = FA_ICON_TEMPERATURE_ARROW_UP
trait_ids = TEMP_CHANGE_ID
trait_flags = TRAIT_HALVES_YIELD
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
@@ -965,7 +964,7 @@
/datum/plant_gene/trait/chem_cooling
name = "Endothermic Activity"
description = "It consumes nutriments to cool down other reagents, halving the yield."
- icon = "temperature-arrow-down"
+ icon = FA_ICON_TEMPERATURE_ARROW_DOWN
trait_ids = TEMP_CHANGE_ID
trait_flags = TRAIT_HALVES_YIELD
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
@@ -986,7 +985,7 @@
/datum/plant_gene/trait/preserved
name = "Natural Insecticide"
description = "It does not attract ants or decompose."
- icon = "bug-slash"
+ icon = FA_ICON_BUG_SLASH
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
/datum/plant_gene/trait/preserved/on_new_plant(obj/item/our_plant, newloc)
@@ -1001,7 +1000,7 @@
/datum/plant_gene/trait/carnivory
name = "Obligate Carnivory"
description = "Pests have positive effect on the plant health."
- icon = "spider"
+ icon = FA_ICON_SPIDER
/// Plant type traits. Incompatible with one another.
/datum/plant_gene/trait/plant_type
@@ -1013,20 +1012,15 @@
/datum/plant_gene/trait/plant_type/weed_hardy
name = "Weed Adaptation"
description = "It is a weed that needs no nutrients and doesn't suffer from other weeds."
- icon = "seedling"
+ icon = FA_ICON_SEEDLING
/// Mushrooms need less light and have a minimum yield.
/datum/plant_gene/trait/plant_type/fungal_metabolism
name = "Fungal Vitality"
description = "It is a mushroom that needs no water, less light and can't be overtaken by weeds."
- icon = "droplet-slash"
+ icon = FA_ICON_DROPLET_SLASH
/// Currently unused and does nothing. Appears in strange seeds.
/datum/plant_gene/trait/plant_type/alien_properties
- name ="?????"
- icon = "reddit-alien"
-
-/datum/plant_gene/trait/seedless
- name = "Seedless"
- description = "The plant is unable to produce seeds"
- mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
+ name = "?????"
+ icon = FA_ICON_QUESTION
diff --git a/code/modules/hydroponics/seed_extractor.dm b/code/modules/hydroponics/seed_extractor.dm
index 72c96788fe55..45e66000da71 100644
--- a/code/modules/hydroponics/seed_extractor.dm
+++ b/code/modules/hydroponics/seed_extractor.dm
@@ -109,14 +109,15 @@
if(istype(attacking_item, /obj/item/storage/bag/plants))
var/loaded = 0
for(var/obj/item/seeds/to_store in attacking_item.contents)
- if(contents.len >= max_seeds)
+ if(length(contents) >= max_seeds)
to_chat(user, span_warning("[src] is full."))
break
if(!add_seed(to_store, attacking_item))
continue
- loaded += 1
+ loaded = TRUE
if(loaded)
+ INVOKE_ASYNC(src, TYPE_PROC_REF(/datum, update_static_data_for_all_viewers)) // monkestation edit: lagfixing
to_chat(user, span_notice("You put as many seeds from [attacking_item] into [src] as you can."))
else
to_chat(user, span_warning("There are no seeds in [attacking_item]."))
@@ -127,23 +128,26 @@
if(!isnull(generated_seeds))
if((user.istate & ISTATE_SECONDARY))
//find all seeds lying on the turf and add them to the machine
+ var/loaded = FALSE
for(var/obj/item/seeds/seed as anything in generated_seeds)
//machine is full
- if(contents.len >= max_seeds)
+ if(length(contents) >= max_seeds)
to_chat(user, span_warning("[src] is full."))
break
//add seed to machine. second argument is null which means just force move into the machine
- add_seed(seed)
+ if(add_seed(seed))
+ loaded = TRUE
+ if(loaded)
to_chat(user, span_notice("You extract some seeds."))
+ INVOKE_ASYNC(src, TYPE_PROC_REF(/datum, update_static_data_for_all_viewers)) // monkestation edit: lagfixing
return TRUE
else if(istype(attacking_item, /obj/item/seeds))
- if(contents.len >= max_seeds)
+ if(length(contents) >= max_seeds)
to_chat(user, span_warning("[src] is full."))
-
else if(add_seed(attacking_item, user))
to_chat(user, span_notice("You add [attacking_item] to [src]."))
-
+ INVOKE_ASYNC(src, TYPE_PROC_REF(/datum, update_static_data_for_all_viewers)) // monkestation edit: lagfixing
else
to_chat(user, span_warning("You can't seem to add [attacking_item] to [src]."))
return TRUE
@@ -222,6 +226,7 @@
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "SeedExtractor", name)
+ ui.set_autoupdate(FALSE) // monkestation edit: lagfixing
ui.open()
/obj/machinery/seed_extractor/ui_data()
@@ -283,6 +288,7 @@
else
found_seed.forceMove(drop_location())
visible_message(span_notice("[found_seed] falls onto the floor."), null, span_hear("You hear a soft clatter."), COMBAT_MESSAGE_RANGE)
+ INVOKE_ASYNC(src, TYPE_PROC_REF(/datum, update_static_data_for_all_viewers)) // monkestation edit: lagfixing
. = TRUE
/obj/machinery/seed_extractor/ui_assets(mob/user)
diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm
index e5f2a15b6f81..0da6d6a811a4 100644
--- a/code/modules/hydroponics/seeds.dm
+++ b/code/modules/hydroponics/seeds.dm
@@ -429,11 +429,6 @@
return
var/max_potency = MAX_PLANT_YIELD
- for(var/datum/plant_gene/trait/trait in genes)
- if(trait.trait_flags & TRAIT_LIMIT_POTENCY)
- max_potency = 100
- break
-
potency = clamp(potency + adjustamt, 0, max_potency)
var/datum/plant_gene/core/C = get_gene(/datum/plant_gene/core/potency)
if(C)
diff --git a/code/modules/hydroponics/unique_plant_genes.dm b/code/modules/hydroponics/unique_plant_genes.dm
index 61bde105bc9f..938ecadb78d8 100644
--- a/code/modules/hydroponics/unique_plant_genes.dm
+++ b/code/modules/hydroponics/unique_plant_genes.dm
@@ -499,7 +499,6 @@
. = ..()
if(!.)
return
-
var/obj/item/food/grown/grown_plant = our_plant
if(istype(grown_plant))
grown_plant.max_volume = new_capcity
diff --git a/code/modules/industrial_lift/tram/tram_floors.dm b/code/modules/industrial_lift/tram/tram_floors.dm
index 83f82e2d131d..3d4cff43ccbd 100644
--- a/code/modules/industrial_lift/tram/tram_floors.dm
+++ b/code/modules/industrial_lift/tram/tram_floors.dm
@@ -1,8 +1,15 @@
+/turf/open/floor/noslip/tram
+ name = "high-traction platform"
+ icon_state = "noslip_tram"
+ base_icon_state = "noslip_tram"
+ floor_tile = /obj/item/stack/tile/noslip/tram
+
/turf/open/floor/noslip/tram_plate
name = "linear induction plate"
desc = "The linear induction plate that powers the tram."
icon_state = "tram_plate"
base_icon_state = "tram_plate"
+ floor_tile = /obj/item/stack/tile/noslip/tram_plate
slowdown = 0
flags_1 = NONE
@@ -15,9 +22,9 @@
/turf/open/floor/noslip/tram_platform
name = "tram platform"
- desc = "A sturdy looking tram platform."
icon_state = "tram_platform"
base_icon_state = "tram_platform"
+ floor_tile = /obj/item/stack/tile/noslip/tram_platform
slowdown = 0
/turf/open/floor/noslip/tram_plate/broken_states()
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index a39f45555cb9..6960ab4b8a94 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -103,7 +103,7 @@
/// List of family heirlooms this job can get with the family heirloom quirk. List of types.
var/list/family_heirlooms
- /// All values = (JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN)
+ /// All values = (JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN | JOB_CANNOT_OPEN_SLOTS)
var/job_flags = NONE
/// Multiplier for general usage of the voice of god.
diff --git a/code/modules/jobs/job_types/ai.dm b/code/modules/jobs/job_types/ai.dm
index 22763cee110d..8c813fa156a9 100644
--- a/code/modules/jobs/job_types/ai.dm
+++ b/code/modules/jobs/job_types/ai.dm
@@ -19,7 +19,7 @@
/datum/job_department/silicon,
)
random_spawns_possible = FALSE
- job_flags = JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK | JOB_BOLD_SELECT_TEXT
+ job_flags = JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
var/do_special_check = TRUE
config_tag = "AI"
diff --git a/code/modules/jobs/job_types/assistant.dm b/code/modules/jobs/job_types/assistant.dm
index f009bd938856..037b86f03fe4 100644
--- a/code/modules/jobs/job_types/assistant.dm
+++ b/code/modules/jobs/job_types/assistant.dm
@@ -31,7 +31,7 @@ Assistant
/obj/item/crowbar/large = 1
)
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
rpg_title = "Lout"
config_tag = "ASSISTANT"
@@ -43,6 +43,12 @@ Assistant
/datum/outfit/job/assistant/pre_equip(mob/living/carbon/human/target)
..()
+ for(var/holidayname in GLOB.holidays)
+ var/datum/holiday/holiday_today = GLOB.holidays[holidayname]
+ var/obj/item/special_hat = holiday_today.holiday_hat
+ if(prob(HOLIDAY_HAT_CHANCE) && !isnull(special_hat) && isnull(head))
+ head = special_hat
+
give_jumpsuit(target)
/datum/outfit/job/assistant/proc/give_jumpsuit(mob/living/carbon/human/target)
diff --git a/code/modules/jobs/job_types/atmospheric_technician.dm b/code/modules/jobs/job_types/atmospheric_technician.dm
index b5c38f4fe589..77f52fd8ab60 100644
--- a/code/modules/jobs/job_types/atmospheric_technician.dm
+++ b/code/modules/jobs/job_types/atmospheric_technician.dm
@@ -35,7 +35,7 @@
/obj/item/grenade/gas_crystal/nitrous_oxide_crystal = 5,
)
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
rpg_title = "Aeromancer"
/datum/outfit/job/atmos
diff --git a/code/modules/jobs/job_types/bartender.dm b/code/modules/jobs/job_types/bartender.dm
index ec526e10d915..0d33c968e563 100644
--- a/code/modules/jobs/job_types/bartender.dm
+++ b/code/modules/jobs/job_types/bartender.dm
@@ -29,7 +29,7 @@
/obj/item/stack/sheet/mineral/uranium = 10,
)
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
rpg_title = "Tavernkeeper"
/datum/job/bartender/award_service(client/winner, award)
diff --git a/code/modules/jobs/job_types/botanist.dm b/code/modules/jobs/job_types/botanist.dm
index 9486cc6e4c30..d977b221b474 100644
--- a/code/modules/jobs/job_types/botanist.dm
+++ b/code/modules/jobs/job_types/botanist.dm
@@ -36,7 +36,7 @@
/obj/item/food/monkeycube/bee = 2
)
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
rpg_title = "Gardener"
/datum/outfit/job/botanist
diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm
index d4e41d63b364..48f0d569f5e1 100755
--- a/code/modules/jobs/job_types/captain.dm
+++ b/code/modules/jobs/job_types/captain.dm
@@ -42,7 +42,7 @@
/obj/item/skillchip/sabrage = 5,
)
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
rpg_title = "Star Duke"
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/cargo_technician.dm b/code/modules/jobs/job_types/cargo_technician.dm
index b277200c03f3..27c9723af780 100644
--- a/code/modules/jobs/job_types/cargo_technician.dm
+++ b/code/modules/jobs/job_types/cargo_technician.dm
@@ -33,7 +33,7 @@
/obj/item/gun/ballistic/automatic/wt550 = 1,
)
rpg_title = "Merchantman"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
/datum/outfit/job/cargo_tech
diff --git a/code/modules/jobs/job_types/chaplain/chaplain.dm b/code/modules/jobs/job_types/chaplain/chaplain.dm
index 803aa0019f95..bb5cfcd67538 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain.dm
@@ -31,7 +31,7 @@
/obj/item/toy/plush/ratplush = 1
)
rpg_title = "Paladin"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
voice_of_god_power = 2 //Chaplains are very good at speaking with the voice of god
diff --git a/code/modules/jobs/job_types/chemist.dm b/code/modules/jobs/job_types/chemist.dm
index 4393066100a7..e4e545553b32 100644
--- a/code/modules/jobs/job_types/chemist.dm
+++ b/code/modules/jobs/job_types/chemist.dm
@@ -35,7 +35,7 @@
/obj/item/paper/secretrecipe = 1
)
rpg_title = "Alchemist"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
/datum/outfit/job/chemist
diff --git a/code/modules/jobs/job_types/chief_engineer.dm b/code/modules/jobs/job_types/chief_engineer.dm
index 9ef561013e1b..ce4d6c9e4745 100644
--- a/code/modules/jobs/job_types/chief_engineer.dm
+++ b/code/modules/jobs/job_types/chief_engineer.dm
@@ -43,7 +43,7 @@
/obj/effect/spawner/random/engineering/tool_advanced = 3
)
rpg_title = "Head Crystallomancer"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm
index daebcfa9d845..a89172e44e66 100644
--- a/code/modules/jobs/job_types/chief_medical_officer.dm
+++ b/code/modules/jobs/job_types/chief_medical_officer.dm
@@ -40,7 +40,7 @@
)
family_heirlooms = list(/obj/item/storage/medkit/ancient/heirloom, /obj/item/scalpel, /obj/item/hemostat, /obj/item/circular_saw, /obj/item/retractor, /obj/item/cautery)
rpg_title = "High Cleric"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/clown.dm b/code/modules/jobs/job_types/clown.dm
index 0bd6ad84a1b1..b535d0f2b783 100644
--- a/code/modules/jobs/job_types/clown.dm
+++ b/code/modules/jobs/job_types/clown.dm
@@ -33,7 +33,7 @@
family_heirlooms = list(/obj/item/bikehorn/golden)
rpg_title = "Jester"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
job_tone = "honk"
@@ -113,4 +113,3 @@
var/datum/atom_hud/fan = GLOB.huds[DATA_HUD_FAN]
fan.show_to(H)
H.faction |= FACTION_CLOWN
-
diff --git a/code/modules/jobs/job_types/cook.dm b/code/modules/jobs/job_types/cook.dm
index edbc4f625bdb..a7544a1a5eb9 100644
--- a/code/modules/jobs/job_types/cook.dm
+++ b/code/modules/jobs/job_types/cook.dm
@@ -46,7 +46,7 @@
)
rpg_title = "Tavern Chef"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
/datum/job/cook/award_service(client/winner, award)
winner.give_award(award, winner.mob)
diff --git a/code/modules/jobs/job_types/curator.dm b/code/modules/jobs/job_types/curator.dm
index dd2ca0563808..41d989ed6b9f 100644
--- a/code/modules/jobs/job_types/curator.dm
+++ b/code/modules/jobs/job_types/curator.dm
@@ -30,7 +30,7 @@
family_heirlooms = list(/obj/item/pen/fountain, /obj/item/storage/dice)
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
voice_of_god_silence_power = 3
rpg_title = "Veteran Adventurer"
diff --git a/code/modules/jobs/job_types/cyborg.dm b/code/modules/jobs/job_types/cyborg.dm
index 43885a875e4e..c51dc10a1eea 100644
--- a/code/modules/jobs/job_types/cyborg.dm
+++ b/code/modules/jobs/job_types/cyborg.dm
@@ -19,7 +19,7 @@
/datum/job_department/silicon,
)
random_spawns_possible = FALSE
- job_flags = JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK
+ job_flags = JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK | JOB_CANNOT_OPEN_SLOTS
/datum/job/cyborg/after_spawn(mob/living/spawned, client/player_client)
diff --git a/code/modules/jobs/job_types/detective.dm b/code/modules/jobs/job_types/detective.dm
index 4b44a3a4ae24..b6984e24b422 100644
--- a/code/modules/jobs/job_types/detective.dm
+++ b/code/modules/jobs/job_types/detective.dm
@@ -40,7 +40,7 @@
family_heirlooms = list(/obj/item/reagent_containers/cup/glass/bottle/whiskey)
rpg_title = "Thiefcatcher" //I guess they caught them all rip thief...
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
job_tone = "objection"
diff --git a/code/modules/jobs/job_types/geneticist.dm b/code/modules/jobs/job_types/geneticist.dm
index be1af0f60edc..7966128f02c5 100644
--- a/code/modules/jobs/job_types/geneticist.dm
+++ b/code/modules/jobs/job_types/geneticist.dm
@@ -29,7 +29,7 @@
family_heirlooms = list(/obj/item/clothing/under/shorts/purple)
rpg_title = "Genemancer"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
/datum/outfit/job/geneticist
diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm
index cda5e183e811..2ad677fc97c1 100644
--- a/code/modules/jobs/job_types/head_of_personnel.dm
+++ b/code/modules/jobs/job_types/head_of_personnel.dm
@@ -40,7 +40,7 @@
family_heirlooms = list(/obj/item/reagent_containers/cup/glass/trophy/silver_cup)
rpg_title = "Guild Questgiver"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm
index 259c828f47c0..c3372bacfc67 100644
--- a/code/modules/jobs/job_types/head_of_security.dm
+++ b/code/modules/jobs/job_types/head_of_security.dm
@@ -34,7 +34,7 @@
family_heirlooms = list(/obj/item/book/manual/wiki/security_space_law)
rpg_title = "Guard Leader"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/janitor.dm b/code/modules/jobs/job_types/janitor.dm
index e4bc41d2eb06..13a3496c609d 100644
--- a/code/modules/jobs/job_types/janitor.dm
+++ b/code/modules/jobs/job_types/janitor.dm
@@ -28,7 +28,7 @@
/obj/item/lightreplacer = 10
)
rpg_title = "Groundskeeper"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
job_tone = "slip"
diff --git a/code/modules/jobs/job_types/lawyer.dm b/code/modules/jobs/job_types/lawyer.dm
index 38d55e85c285..a25a1d86d3ad 100644
--- a/code/modules/jobs/job_types/lawyer.dm
+++ b/code/modules/jobs/job_types/lawyer.dm
@@ -25,7 +25,7 @@
rpg_title = "Magistrate"
family_heirlooms = list(/obj/item/gavelhammer, /obj/item/book/manual/wiki/security_space_law)
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
job_tone = "objection"
diff --git a/code/modules/jobs/job_types/medical_doctor.dm b/code/modules/jobs/job_types/medical_doctor.dm
index e8f064fd5456..049ca7489578 100644
--- a/code/modules/jobs/job_types/medical_doctor.dm
+++ b/code/modules/jobs/job_types/medical_doctor.dm
@@ -36,7 +36,7 @@
/obj/effect/spawner/random/medical/memeorgans = 1
)
rpg_title = "Cleric"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
/datum/outfit/job/doctor
diff --git a/code/modules/jobs/job_types/mime.dm b/code/modules/jobs/job_types/mime.dm
index 9be0af70b651..97a84511a2ad 100644
--- a/code/modules/jobs/job_types/mime.dm
+++ b/code/modules/jobs/job_types/mime.dm
@@ -29,7 +29,7 @@
/obj/item/book/mimery = 1,
)
rpg_title = "Fool"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
voice_of_god_power = 0.5 //Why are you speaking
voice_of_god_silence_power = 3
diff --git a/code/modules/jobs/job_types/paramedic.dm b/code/modules/jobs/job_types/paramedic.dm
index c9dad2aa0a66..76c6e10dabf4 100644
--- a/code/modules/jobs/job_types/paramedic.dm
+++ b/code/modules/jobs/job_types/paramedic.dm
@@ -35,7 +35,7 @@
/obj/item/reagent_containers/hypospray/medipen/survival/luxury = 5
)
rpg_title = "Corpse Runner"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
/datum/outfit/job/paramedic
diff --git a/code/modules/jobs/job_types/prisoner.dm b/code/modules/jobs/job_types/prisoner.dm
index 2737b405802f..e931a34f5ae6 100644
--- a/code/modules/jobs/job_types/prisoner.dm
+++ b/code/modules/jobs/job_types/prisoner.dm
@@ -27,7 +27,7 @@
family_heirlooms = list(/obj/item/pen/blue)
rpg_title = "Defeated Miniboss"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN | JOB_CANNOT_OPEN_SLOTS
/datum/job/prisoner/New()
. = ..()
diff --git a/code/modules/jobs/job_types/psychologist.dm b/code/modules/jobs/job_types/psychologist.dm
index b39babc00450..440adbda9d0c 100644
--- a/code/modules/jobs/job_types/psychologist.dm
+++ b/code/modules/jobs/job_types/psychologist.dm
@@ -31,7 +31,7 @@
/obj/item/gun/syringe = 1
)
rpg_title = "Snake Oil Salesman"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
/datum/outfit/job/psychologist
diff --git a/code/modules/jobs/job_types/quartermaster.dm b/code/modules/jobs/job_types/quartermaster.dm
index 49d26f09d62a..7088b76c35e7 100644
--- a/code/modules/jobs/job_types/quartermaster.dm
+++ b/code/modules/jobs/job_types/quartermaster.dm
@@ -32,7 +32,7 @@
/obj/item/circuitboard/machine/emitter = 3
)
rpg_title = "Steward"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
ignore_human_authority = TRUE
/datum/outfit/job/quartermaster
diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm
index d0d711411061..b8ba0f237606 100644
--- a/code/modules/jobs/job_types/research_director.dm
+++ b/code/modules/jobs/job_types/research_director.dm
@@ -41,7 +41,7 @@
family_heirlooms = list(/obj/item/toy/plush/slimeplushie)
rpg_title = "Archmagister"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_CANNOT_OPEN_SLOTS
voice_of_god_power = 1.4 //Command staff has authority
diff --git a/code/modules/jobs/job_types/roboticist.dm b/code/modules/jobs/job_types/roboticist.dm
index 83ea82c129dd..becff790c07a 100644
--- a/code/modules/jobs/job_types/roboticist.dm
+++ b/code/modules/jobs/job_types/roboticist.dm
@@ -31,7 +31,7 @@
family_heirlooms = list(/obj/item/toy/plush/pkplush)
rpg_title = "Necromancer"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
/datum/job/roboticist/New()
diff --git a/code/modules/jobs/job_types/scientist.dm b/code/modules/jobs/job_types/scientist.dm
index 5d4cc75b9b8b..06be18a02b6e 100644
--- a/code/modules/jobs/job_types/scientist.dm
+++ b/code/modules/jobs/job_types/scientist.dm
@@ -32,7 +32,7 @@
/obj/item/disk/design_disk/bepis = 2,
)
rpg_title = "Thaumaturgist"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
job_tone = "boom"
diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm
index c80bccb55d94..f299c690577f 100644
--- a/code/modules/jobs/job_types/security_officer.dm
+++ b/code/modules/jobs/job_types/security_officer.dm
@@ -38,7 +38,7 @@
/obj/item/melee/baton/security/boomerang/loaded = 1
)
rpg_title = "Guard"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, SEC_DEPT_SCIENCE, SEC_DEPT_SUPPLY))
@@ -121,13 +121,11 @@ GLOBAL_LIST_EMPTY(security_officer_distribution)
qdel(spawning.ears)
spawning.equip_to_slot_or_del(new ears(spawning),ITEM_SLOT_EARS)
- //monkestation edit start: add dept sec outfits
+ //monkestation edit start: add dept sec outfitsif(suit)
if(suit)
- for(var/obj/item/gun/ballistic/automatic/pistol/paco/no_mag/stored in spawning.contents)
- if(spawning.wear_suit)
- qdel(spawning.wear_suit)
- spawning.equip_to_slot_or_del(new suit(spawning),ITEM_SLOT_OCLOTHING)
- spawning.equip_to_slot_or_del(stored,ITEM_SLOT_SUITSTORE)
+ if(spawning.wear_suit)
+ qdel(spawning.wear_suit)
+ spawning.equip_to_slot_or_del(new suit(spawning),ITEM_SLOT_OCLOTHING)
if(head)
if(spawning.head && !isplasmaman(spawning))
qdel(spawning.head)
@@ -224,10 +222,10 @@ GLOBAL_LIST_EMPTY(security_officer_distribution)
uniform = /obj/item/clothing/under/rank/security/officer
head = /obj/item/clothing/head/helmet/hat/cowboy //monkestation edit: cowboy sec
suit = /obj/item/clothing/suit/armor/secduster //monkestation edit: cowboy sec
- suit_store = /obj/item/gun/ballistic/automatic/pistol/paco/no_mag //monkestation edit: Paco sec
backpack_contents = list(
/obj/item/evidencebag = 1,
- /obj/item/ammo_box/magazine/m35/rubber = 2, //monkestation edit: Paco sec
+ /obj/item/ammo_box/magazine/m35/rubber = 2,
+ /obj/item/gun/ballistic/automatic/pistol/paco/no_mag, //monkestation edit: Paco sec
)
belt = /obj/item/modular_computer/pda/security
ears = /obj/item/radio/headset/headset_sec/alt
diff --git a/code/modules/jobs/job_types/shaft_miner.dm b/code/modules/jobs/job_types/shaft_miner.dm
index 4e4e55496d0d..db7fff237cdd 100644
--- a/code/modules/jobs/job_types/shaft_miner.dm
+++ b/code/modules/jobs/job_types/shaft_miner.dm
@@ -26,7 +26,7 @@
family_heirlooms = list(/obj/item/pickaxe/mini, /obj/item/shovel)
rpg_title = "Adventurer"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
/datum/outfit/job/miner
diff --git a/code/modules/jobs/job_types/spawner/lavaland_syndicate.dm b/code/modules/jobs/job_types/spawner/lavaland_syndicate.dm
index a22e798274af..984c87b42cea 100644
--- a/code/modules/jobs/job_types/spawner/lavaland_syndicate.dm
+++ b/code/modules/jobs/job_types/spawner/lavaland_syndicate.dm
@@ -1,3 +1,4 @@
/datum/job/lavaland_syndicate
title = ROLE_LAVALAND_SYNDICATE
policy_index = ROLE_LAVALAND_SYNDICATE
+
diff --git a/code/modules/jobs/job_types/station_engineer.dm b/code/modules/jobs/job_types/station_engineer.dm
index 343752ea2fb3..f636058677ec 100644
--- a/code/modules/jobs/job_types/station_engineer.dm
+++ b/code/modules/jobs/job_types/station_engineer.dm
@@ -36,7 +36,7 @@
/obj/item/clothing/head/utility/hardhat/red/upgraded = 1
)
rpg_title = "Crystallomancer"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
/datum/outfit/job/engineer
diff --git a/code/modules/jobs/job_types/virologist.dm b/code/modules/jobs/job_types/virologist.dm
index b8b68b478b74..5ea786330b18 100644
--- a/code/modules/jobs/job_types/virologist.dm
+++ b/code/modules/jobs/job_types/virologist.dm
@@ -44,7 +44,7 @@
// End Monkestation Addition
)
rpg_title = "Plague Doctor"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
+ job_flags = STATION_JOB_FLAGS
/datum/outfit/job/virologist
diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm
index 2179908c57d7..62e7dc9e2058 100644
--- a/code/modules/jobs/job_types/warden.dm
+++ b/code/modules/jobs/job_types/warden.dm
@@ -40,8 +40,7 @@
/obj/item/storage/box/lethalshot = 5
)
rpg_title = "Jailor"
- job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_BOLD_SELECT_TEXT | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
-
+ job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT
/datum/outfit/job/warden
name = "Warden"
diff --git a/code/modules/library/bibles.dm b/code/modules/library/bibles.dm
index 564104baa9a3..fd8a7842a36e 100644
--- a/code/modules/library/bibles.dm
+++ b/code/modules/library/bibles.dm
@@ -319,6 +319,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list(
name = "Syndicate Tome"
desc = "A very ominous tome resembling a bible."
icon_state ="ebook"
+ slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKETS
item_flags = NO_BLOOD_ON_ITEM
throw_speed = 2
throw_range = 7
diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm
index 3abfd151153f..7477c8296c11 100644
--- a/code/modules/lighting/lighting_source.dm
+++ b/code/modules/lighting/lighting_source.dm
@@ -88,6 +88,7 @@
//yes, we register the signal to the top atom too, this is intentional and ensures contained lighting updates properly
if(ismovable(new_atom_host) && new_atom_host == source_atom)
RegisterSignal(new_atom_host, COMSIG_MOVABLE_MOVED, PROC_REF(update_host_lights))
+ RegisterSignal(new_atom_host, COMSIG_TURF_NO_LONGER_BLOCK_LIGHT, PROC_REF(force_update))
return TRUE
///remove this light source from old_atom_host's light_sources list, unsetting movement registrations
@@ -98,6 +99,7 @@
LAZYREMOVE(old_atom_host.light_sources, src)
if(ismovable(old_atom_host) && old_atom_host == source_atom)
UnregisterSignal(old_atom_host, COMSIG_MOVABLE_MOVED)
+ UnregisterSignal(old_atom_host, COMSIG_TURF_NO_LONGER_BLOCK_LIGHT)
return TRUE
// Yes this doesn't align correctly on anything other than 4 width tabs.
diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm
index d2400097c343..56e64dada77f 100644
--- a/code/modules/lighting/lighting_turf.dm
+++ b/code/modules/lighting/lighting_turf.dm
@@ -101,6 +101,9 @@
else //If fulltile and opaque, then the whole tile blocks view, no need to continue checking.
directional_opacity = ALL_CARDINALS
break
+ else
+ for(var/atom/movable/content as anything in contents)
+ SEND_SIGNAL(content, COMSIG_TURF_NO_LONGER_BLOCK_LIGHT)
if(. != directional_opacity && (. == ALL_CARDINALS || directional_opacity == ALL_CARDINALS))
reconsider_lights() //The lighting system only cares whether the tile is fully concealed from all directions or not.
reconsider_sunlight() //monkestation addition
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/caravanambush.dm b/code/modules/mapfluff/ruins/spaceruin_code/caravanambush.dm
index d6b2d9ffaa75..c6d4a8beb4b8 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/caravanambush.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/caravanambush.dm
@@ -106,7 +106,7 @@
req_access = list(ACCESS_SYNDICATE)
circuit = /obj/item/circuitboard/computer/caravan/syndicate1
shuttleId = "caravansyndicate1"
- possible_destinations = "caravansyndicate1_custom;caravansyndicate1_ambush;caravansyndicate1_listeningpost"
+ possible_destinations = "caravansyndicate1_custom;caravansyndicate1_ambush;syndicate_listening_post" //monkestation edit: new listening post
/obj/machinery/computer/camera_advanced/shuttle_docker/caravan/syndicate1
name = "Syndicate Fighter Navigation Computer"
@@ -116,7 +116,7 @@
shuttleId = "caravansyndicate1"
lock_override = NONE
shuttlePortId = "caravansyndicate1_custom"
- jump_to_ports = list("caravansyndicate1_ambush" = 1, "caravansyndicate1_listeningpost" = 1)
+ jump_to_ports = list("caravansyndicate1_ambush" = 1, "syndicate_listening_post" = 1) //monkestation edit: new listening post
view_range = 0
x_offset = 2
y_offset = 0
@@ -130,7 +130,7 @@
light_color = COLOR_SOFT_RED
circuit = /obj/item/circuitboard/computer/caravan/syndicate2
shuttleId = "caravansyndicate2"
- possible_destinations = "caravansyndicate2_custom;caravansyndicate2_ambush;caravansyndicate1_listeningpost"
+ possible_destinations = "caravansyndicate2_custom;caravansyndicate2_ambush;syndicate_listening_post" //monkestation edit: new listening post
/obj/machinery/computer/camera_advanced/shuttle_docker/caravan/syndicate2
name = "Syndicate Fighter Navigation Computer"
@@ -140,7 +140,7 @@
shuttleId = "caravansyndicate2"
lock_override = NONE
shuttlePortId = "caravansyndicate2_custom"
- jump_to_ports = list("caravansyndicate2_ambush" = 1, "caravansyndicate1_listeningpost" = 1)
+ jump_to_ports = list("caravansyndicate2_ambush" = 1, "syndicate_listening_post" = 1) //monkestation edit: new listening post
view_range = 0
x_offset = 0
y_offset = 2
@@ -154,7 +154,7 @@
light_color = COLOR_SOFT_RED
circuit = /obj/item/circuitboard/computer/caravan/syndicate3
shuttleId = "caravansyndicate3"
- possible_destinations = "caravansyndicate3_custom;caravansyndicate3_ambush;caravansyndicate3_listeningpost"
+ possible_destinations = "caravansyndicate3_custom;caravansyndicate3_ambush;syndicate_listening_post" //monkestation edit: new listening post
/obj/machinery/computer/camera_advanced/shuttle_docker/caravan/syndicate3
name = "Syndicate Drop Ship Navigation Computer"
@@ -164,7 +164,7 @@
shuttleId = "caravansyndicate3"
lock_override = NONE
shuttlePortId = "caravansyndicate3_custom"
- jump_to_ports = list("caravansyndicate3_ambush" = 1, "caravansyndicate3_listeningpost" = 1)
+ jump_to_ports = list("caravansyndicate3_ambush" = 1, "syndicate_listening_post" = 1) //monkestation edit: new listening post
view_range = 2.5
x_offset = -1
y_offset = -3
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/listeningstation.dm b/code/modules/mapfluff/ruins/spaceruin_code/listeningstation.dm
index 1c44703f10af..54d36dd3eea5 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/listeningstation.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/listeningstation.dm
@@ -36,11 +36,19 @@
It's not like anyone but me reads these, why else should I talk about my base upkeep? Today: the "lobby".
"}
+//monkestation edit: goliath
+/obj/item/paper/fluff/ruins/listeningstation/reports/june/goliath
+ name = "hastily-written report"
+ default_raw_text = {"Heard some strange noises in the lobby, leaving this in case something happens.
+ Some sorta loud banging behind one of the walls. I'm going to go take a look.
+ Got my gun just in case, hopefully it isn't anything I need to use it for.
+"}
+//monkey business end
// "Anderson" starts writing here
/obj/item/paper/fluff/ruins/listeningstation/reports/july
name = "july report"
- default_raw_text = {"Hey, old guy got a transfer, and I was next in line. I'll show them how we do it over at the Gorlex Marauders! Let's monitor some stuff. This will be fun.
+ default_raw_text = {"Hey, old guy went missing, and I was next in line. I'll show them how we do it over at the Gorlex Marauders! Let's monitor some stuff. This will be fun.
It seems "old guy" did some upkeep around the base, and I will admit: it's nice. The lobby is shoddy for some reason. Not sure why that is.
I read some of the older reports, and it seems like interesting stuff. No idea where June is. Ah well, maybe he got out in May?
Odd sleeper, the frost covered it up. They were telling me about this on the way here, that it's meant to be a replacement to ensure "seamless" operation of this base. Okay?
@@ -60,7 +68,7 @@
/obj/item/paper/fluff/ruins/listeningstation/reports/september
name = "september report"
default_raw_text = {"i'm... not doing good. i'm doing so bad. the sleeper is still there. my friend died in a sleeper malfunction. it overheated.
- i don't want to unplug it, i don't want to wake them up. i don't go in the bedroom anymore, i have a small cot in the lobby area. i'm lonely.
+ i don't want to unplug it, i don't want to wake them up. i don't go near the backup anymore. i'm lonely.
blowing myself up is out of the question. it'll kill them too. i've killed many people in my life, but i think ending another that way
will probably send me to hell. if hell is anything like this, i'd rather try and salvage as much as i can before i pass. death awaits us all.
@@ -115,4 +123,17 @@
You may view intelligence reports from your predecessors in the filing cabinet in your office.
"}
-
+// MONKESTATION EDIT: monkestation new listening post. im not making a seperate file for modularising some goddamn fluff on the other side of a file. this is why git exists
+/obj/item/paper/fluff/ruins/listeningstation/engineer
+ name = "message from syndicate engineering"
+ default_raw_text = {"This outpost's pretty messy compared to other models, so here's the basic rundown of every room and whatnot:
+ As is to be expected, you've probably already found your way around the lounge area: it's where you'll spend most of your day, given the frequent dark periods on Nanotrasen stations.
+ You also have a self-destruct issued for emergencies - probably don't wanna touch that.
+ There's also your office, equipped with everything needed to communicate with agents and decieve (or just annoy) Nanotrasen crew - given this is being faxed, you're probably here.
+ Service areas should be behind the yellow airlock - that's the laundry, engineering area, and life support: the relay is also through here.
+ Entrance's opposite - it's reconfigured by different post crews for different uses: some use it as a lobby, some use it for cargo handling, others just cut it up for parts and leave it in shoddy condition.
+ Infirmary's below - it's got a proper sleeper modified to allow you to use it without external assistance, as well as some supplies.
+ There's also a pile of garden equipment in the excavation near medical - have fun with that.
+
+ Enjoy your assignment."}
+// end monkey business
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm b/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm
index bea6c8cce77f..8dfd57199e6e 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm
@@ -44,6 +44,14 @@
icon = 'icons/mob/simple/meteor_heart.dmi'
anchored = TRUE
+/obj/structure/meateor_fluff/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/bloody_spreader,\
+ blood_left = INFINITY,\
+ blood_dna = list("meaty DNA" = "MT-"),\
+ diseases = null,\
+ )
+
/obj/structure/meateor_fluff/play_attack_sound(damage_amount, damage_type, damage_flag)
switch(damage_type)
if(BRUTE)
diff --git a/code/modules/meteors/meteor_types.dm b/code/modules/meteors/meteor_types.dm
index 9f598f9809e9..94e1f3dfe4eb 100644
--- a/code/modules/meteors/meteor_types.dm
+++ b/code/modules/meteors/meteor_types.dm
@@ -327,8 +327,8 @@
signature = "bluespace flux"
/obj/effect/meteor/bluespace/Bump()
- ..()
- if(prob(35))
+ . = ..()
+ if(!QDELETED(src) && prob(35)) // monkestation edit: runtime fix
do_teleport(src, get_turf(src), 6, asoundin = 'sound/effects/phasein.ogg', channel = TELEPORT_CHANNEL_BLUESPACE)
/obj/effect/meteor/banana
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 849468a93b4a..862a269ebbfb 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -37,7 +37,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
var/health_scan = FALSE //Are health scans currently enabled?
var/chem_scan = FALSE //Are chem scans currently enabled?
var/gas_scan = FALSE //Are gas scans currently enabled?
- var/list/datahuds = list(DATA_HUD_SECURITY_ADVANCED, DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC_ADVANCED) //list of data HUDs shown to ghosts.
+ var/list/datahuds = list(DATA_HUD_SECURITY_ADVANCED, DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC_ADVANCED, DATA_HUD_MOOD) //list of data HUDs shown to ghosts. monkestation addition here
var/ghost_orbit = GHOST_ORBIT_CIRCLE
//These variables store hair data if the ghost originates from a species with head and/or facial hair.
@@ -770,9 +770,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
var/datum/atom_hud/data_hud = GLOB.huds[hudtype]
data_hud.hide_from(src)
+//MONKESTATION
/mob/dead/observer/verb/toggle_data_huds()
- set name = "Toggle Sec/Med/Diag HUD"
- set desc = "Toggles whether you see medical/security/diagnostic HUDs"
+ set name = "Toggle Sec/Med/Diag/Mood HUD"
+ set desc = "Toggles whether you see medical/security/diagnostic/mood HUDs"
set category = "Ghost"
if(data_huds_on) //remove old huds
diff --git a/code/modules/mob/living/basic/bots/_bots.dm b/code/modules/mob/living/basic/bots/_bots.dm
index e5584574ee3e..26cf415c11d7 100644
--- a/code/modules/mob/living/basic/bots/_bots.dm
+++ b/code/modules/mob/living/basic/bots/_bots.dm
@@ -793,11 +793,11 @@ GLOBAL_LIST_INIT(command_strings, list(
initial_access = access_card.access.Copy()
-/mob/living/basic/bot/proc/summon_bot(atom/caller, turf/turf_destination, user_access = list(), grant_all_access = FALSE)
- if(isAI(caller) && !set_ai_caller(caller))
+/mob/living/basic/bot/proc/summon_bot(atom/user, turf/turf_destination, user_access = list(), grant_all_access = FALSE)
+ if(isAI(user) && !set_ai_caller(user))
return FALSE
- bot_reset(bypass_ai_reset = isAI(caller))
- var/turf/destination = turf_destination ? turf_destination : get_turf(caller)
+ bot_reset(bypass_ai_reset = isAI(user))
+ var/turf/destination = turf_destination ? turf_destination : get_turf(user)
ai_controller?.set_blackboard_key(BB_BOT_SUMMON_TARGET, destination)
var/list/access_to_grant = grant_all_access ? REGION_ACCESS_ALL_STATION : user_access + initial_access
access_card.set_access(access_to_grant)
@@ -805,11 +805,11 @@ GLOBAL_LIST_INIT(command_strings, list(
update_bot_mode(new_mode = BOT_SUMMON)
return TRUE
-/mob/living/basic/bot/proc/set_ai_caller(mob/living/caller)
+/mob/living/basic/bot/proc/set_ai_caller(mob/living/user)
var/atom/calling_ai = calling_ai_ref?.resolve()
if(!isnull(calling_ai) && calling_ai != src)
return FALSE
- calling_ai_ref = WEAKREF(caller)
+ calling_ai_ref = WEAKREF(user)
return TRUE
/mob/living/basic/bot/proc/update_bot_mode(new_mode, update_hud = TRUE)
diff --git a/code/modules/mob/living/basic/bots/medbot/medbot.dm b/code/modules/mob/living/basic/bots/medbot/medbot.dm
index 88e843cc814d..2e7a78d2b679 100644
--- a/code/modules/mob/living/basic/bots/medbot/medbot.dm
+++ b/code/modules/mob/living/basic/bots/medbot/medbot.dm
@@ -163,7 +163,7 @@
icon_state = "[base_icon_state]a"
return
if(mode == BOT_HEALING)
- icon_state = "[base_icon_state]s[medical_mode_flags & MEDBOT_STATIONARY_MODE]"
+ icon_state = "[base_icon_state]s[medical_mode_flags & MEDBOT_STATIONARY_MODE ? 1 : 0]"
return
icon_state = "[base_icon_state][medical_mode_flags & MEDBOT_STATIONARY_MODE ? 2 : 1]" //Bot has yellow light to indicate stationary mode.
diff --git a/code/modules/mob/living/basic/drone/_drone.dm b/code/modules/mob/living/basic/drone/_drone.dm
index cf0e71553abe..27c328617c4d 100644
--- a/code/modules/mob/living/basic/drone/_drone.dm
+++ b/code/modules/mob/living/basic/drone/_drone.dm
@@ -187,7 +187,8 @@
equip_to_slot_or_del(storage, ITEM_SLOT_DEX_STORAGE)
for(var/holiday_name in GLOB.holidays)
- var/obj/item/potential_hat
+ var/datum/holiday/holiday_today = GLOB.holidays[holiday_name]
+ var/obj/item/potential_hat = holiday_today.holiday_hat
if(!isnull(potential_hat) && isnull(default_headwear)) //If our drone type doesn't start with a hat, we take the holiday one.
default_headwear = potential_hat
diff --git a/code/modules/mob/living/basic/farm_animals/pony.dm b/code/modules/mob/living/basic/farm_animals/pony.dm
new file mode 100644
index 000000000000..4c200de45c1d
--- /dev/null
+++ b/code/modules/mob/living/basic/farm_animals/pony.dm
@@ -0,0 +1,88 @@
+/mob/living/basic/pony
+ name = "pony"
+ desc = "Look at my horse, my horse is amazing!"
+ icon_state = "pony"
+ icon_living = "pony"
+ icon_dead = "pony_dead"
+ gender = MALE
+ mob_biotypes = MOB_ORGANIC | MOB_BEAST
+ speak_emote = list("neighs", "winnies")
+ response_help_continuous = "pets"
+ response_help_simple = "pet"
+ response_disarm_continuous = "gently pushes aside"
+ response_disarm_simple = "gently push aside"
+ response_harm_continuous = "kicks"
+ response_harm_simple = "kick"
+ attack_verb_continuous = "kicks"
+ attack_verb_simple = "kick"
+ attack_sound = 'sound/weapons/punch1.ogg'
+ attack_vis_effect = ATTACK_EFFECT_KICK
+ melee_damage_lower = 5
+ melee_damage_upper = 10
+ health = 50
+ maxHealth = 50
+ gold_core_spawnable = FRIENDLY_SPAWN
+ blood_volume = BLOOD_VOLUME_NORMAL
+ ai_controller = /datum/ai_controller/basic_controller/pony
+
+/mob/living/basic/pony/Initialize(mapload)
+ . = ..()
+
+ AddElement(/datum/element/pet_bonus, "whickers.")
+ AddElement(/datum/element/ai_retaliate)
+ AddElement(/datum/element/ai_flee_while_injured)
+ AddElement(/datum/element/waddling)
+ AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/apple), tame_chance = 25, bonus_tame_chance = 15, after_tame = CALLBACK(src, PROC_REF(tamed)))
+
+/mob/living/basic/pony/proc/tamed(mob/living/tamer)
+ can_buckle = TRUE
+ buckle_lying = 0
+ playsound(src, 'sound/creatures/pony/snort.ogg', 50)
+ AddElement(/datum/element/ridable, /datum/component/riding/creature/pony)
+ visible_message(span_notice("[src] snorts happily."))
+
+ ai_controller.replace_planning_subtrees(list(
+ /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee,
+ /datum/ai_planning_subtree/flee_target,
+ /datum/ai_planning_subtree/random_speech/pony/tamed
+ ))
+
+/mob/living/basic/pony/proc/whinny_angrily()
+ manual_emote("whinnies ANGRILY!")
+
+ playsound(src, pick(list(
+ 'sound/creatures/pony/whinny01.ogg',
+ 'sound/creatures/pony/whinny02.ogg',
+ 'sound/creatures/pony/whinny03.ogg'
+ )), 50)
+
+/mob/living/basic/pony/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration)
+ . = ..()
+
+ if (prob(33))
+ whinny_angrily()
+
+/mob/living/basic/pony/melee_attack(atom/target, list/modifiers, ignore_cooldown = FALSE)
+ . = ..()
+
+ if (!.)
+ return
+
+ whinny_angrily()
+
+/datum/ai_controller/basic_controller/pony
+ blackboard = list(
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
+ )
+
+ ai_traits = STOP_MOVING_WHEN_PULLED
+ ai_movement = /datum/ai_movement/basic_avoidance
+ idle_behavior = /datum/idle_behavior/idle_random_walk
+
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee,
+ /datum/ai_planning_subtree/flee_target,
+ /datum/ai_planning_subtree/target_retaliate,
+ /datum/ai_planning_subtree/basic_melee_attack_subtree,
+ /datum/ai_planning_subtree/random_speech/pony
+ )
diff --git a/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm b/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm
index 47aaa3beb227..c527317863be 100644
--- a/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm
+++ b/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm
@@ -145,7 +145,7 @@
if(isnull(can))
return
- if(locate(/datum/reagent/water) in can.reagents.reagent_list)
+ if(can.reagents?.has_reagent(/datum/reagent/water)) // monkestation edit: just use has_reagent instead of locate()
return
return ..()
@@ -153,7 +153,7 @@
/datum/ai_behavior/find_hunt_target/suitable_dispenser
/datum/ai_behavior/find_hunt_target/suitable_dispenser/valid_dinner(mob/living/source, obj/structure/water_source, radius)
- if(!(locate(/datum/reagent/water) in water_source.reagents.reagent_list))
+ if(!water_source.reagents?.has_reagent(/datum/reagent/water)) // monkestation edit: just use has_reagent instead of locate()
return FALSE
return can_see(source, water_source, radius)
diff --git a/code/modules/mob/living/basic/jungle/venus_human_trap.dm b/code/modules/mob/living/basic/jungle/venus_human_trap.dm
index b8959ace2d06..c679153ba7f2 100644
--- a/code/modules/mob/living/basic/jungle/venus_human_trap.dm
+++ b/code/modules/mob/living/basic/jungle/venus_human_trap.dm
@@ -13,10 +13,9 @@
/obj/structure/alien/resin/flower_bud //inheriting basic attack/damage stuff from alien structures
name = "flower bud"
desc = "A large pulsating plant..."
- icon = 'icons/effects/spacevines.dmi'
+ icon = 'icons/mob/spacevines.dmi'
icon_state = "bud0"
layer = SPACEVINE_MOB_LAYER
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
opacity = FALSE
canSmoothWith = null
smoothing_flags = NONE
@@ -130,12 +129,11 @@
/mob/living/basic/venus_human_trap
name = "venus human trap"
desc = "Now you know how the fly feels."
- icon = 'icons/effects/spacevines.dmi'
+ icon = 'icons/mob/spacevines.dmi'
icon_state = "venus_human_trap"
health_doll_icon = "venus_human_trap"
mob_biotypes = MOB_ORGANIC | MOB_PLANT
layer = SPACEVINE_MOB_LAYER
- plane = GAME_PLANE_UPPER_FOV_HIDDEN
health = 100
maxHealth = 100
obj_damage = 60
@@ -173,9 +171,11 @@
/mob/living/basic/venus_human_trap/Initialize(mapload)
. = ..()
- var/datum/action/cooldown/vine_tangle/tangle = new(src)
- tangle.Grant(src)
- ai_controller.set_blackboard_key(BB_TARGETED_ACTION, tangle)
+ AddElement(/datum/element/lifesteal, 5)
+ var/static/list/innate_actions = list(
+ /datum/action/cooldown/mob_cooldown/projectile_attack/vine_tangle = BB_TARGETED_ACTION,
+ )
+ grant_actions_by_list(innate_actions)
/mob/living/basic/venus_human_trap/RangedAttack(atom/victim)
if(!(istate & ISTATE_HARM))
@@ -199,14 +199,12 @@
adjustBruteLoss(vines_in_range ? -weed_heal : no_weed_damage) //every life tick take 20 damage if not near vines or heal 10 if near vines, 5 times out of weeds = u ded
-/datum/action/cooldown/vine_tangle
+/datum/action/cooldown/mob_cooldown/projectile_attack/vine_tangle
name = "Tangle"
button_icon = 'icons/mob/spacevines.dmi'
button_icon_state = "Light1"
desc = "Grabs a target with a sticky vine, allowing you to pull it alongside you."
cooldown_time = 8 SECONDS
- ///how many vines can we handle
- var/max_vines = 2
/// An assoc list of all the plant's vines (beam = leash)
var/list/datum/beam/vines = list()
/// How far away a plant can attach a vine to something
@@ -214,17 +212,19 @@
/// how long does a vine attached to something last (and its leash) (lasts twice as long on nonliving things)
var/vine_duration = 2 SECONDS
-/datum/action/cooldown/vine_tangle/Remove(mob/remove_from)
+/datum/action/cooldown/mob_cooldown/projectile_attack/vine_tangle/Remove(mob/remove_from)
QDEL_LIST(vines)
return ..()
-/datum/action/cooldown/vine_tangle/Activate(atom/target_atom)
+/datum/action/cooldown/mob_cooldown/projectile_attack/vine_tangle/Activate(atom/target_atom)
if(isturf(target_atom) || istype(target_atom, /obj/structure/spacevine))
return
- if(length(vines) >= max_vines || get_dist(owner, target_atom) > vine_grab_distance)
+ if(get_dist(owner, target_atom) > vine_grab_distance)
+ owner.balloon_alert(owner, "too far!")
return
for(var/turf/blockage in get_line(owner, target_atom))
if(blockage.is_blocked_turf(exclude_mobs = TRUE))
+ owner.balloon_alert(owner, "something's in the way!")
return
var/datum/beam/new_vine = owner.Beam(target_atom, icon_state = "vine", time = vine_duration * (ismob(target_atom) ? 1 : 2), beam_type = /obj/effect/ebeam/vine, emissive = FALSE)
@@ -245,7 +245,7 @@
* Arguments:
* * datum/beam/vine - The vine to be removed from the list.
*/
-/datum/action/cooldown/vine_tangle/proc/remove_vine(datum/beam/vine)
+/datum/action/cooldown/mob_cooldown/projectile_attack/vine_tangle/proc/remove_vine(datum/beam/vine)
SIGNAL_HANDLER
qdel(vines[vine])
diff --git a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm
index 0012aff294d4..3e0558f89af0 100644
--- a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm
+++ b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm
@@ -31,7 +31,7 @@
var/datum/action/cooldown/ability = controller.blackboard[BB_TARGETED_ACTION]
if(QDELETED(target) || QDELETED(controller.pawn) || !ability?.IsAvailable())
return
- ability.InterceptClickOn(caller = controller.pawn, target = target)
+ ability.InterceptClickOn(user = controller.pawn, target = target)
/datum/ai_planning_subtree/targeted_mob_ability/brimbeam
use_ability_behaviour = /datum/ai_behavior/targeted_mob_ability/brimbeam
diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp.dm b/code/modules/mob/living/basic/space_fauna/carp/carp.dm
index 24fb0b582642..b889305f811e 100644
--- a/code/modules/mob/living/basic/space_fauna/carp/carp.dm
+++ b/code/modules/mob/living/basic/space_fauna/carp/carp.dm
@@ -269,6 +269,10 @@
disk_overlay = mutable_appearance('icons/mob/simple/carp.dmi', "disk_overlay")
new_overlays += disk_overlay
+/mob/living/basic/carp/advanced
+ health = 40
+ obj_damage = 15
+
#undef RARE_CAYENNE_CHANCE
///Wild carp that just vibe ya know
diff --git a/code/modules/mob/living/basic/space_fauna/morph.dm b/code/modules/mob/living/basic/space_fauna/morph.dm
index 0cce48ba377e..40d9a8802219 100644
--- a/code/modules/mob/living/basic/space_fauna/morph.dm
+++ b/code/modules/mob/living/basic/space_fauna/morph.dm
@@ -147,7 +147,7 @@
SIGNAL_HANDLER
// linters hate this if it's not async for some reason even though nothing blocks
- INVOKE_ASYNC(disguise_ability, TYPE_PROC_REF(/datum/action/cooldown, InterceptClickOn), caller = source, target = target)
+ INVOKE_ASYNC(disguise_ability, TYPE_PROC_REF(/datum/action/cooldown, InterceptClickOn), user = source, target = target)
return COMSIG_MOB_CANCEL_CLICKON
/// Handles the logic for attacking anything.
diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm
index 0d218d73ec14..15cc2b7747b0 100644
--- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm
+++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm
@@ -65,11 +65,7 @@
return TRUE
/datum/action/cooldown/spell/aoe/revenant/get_things_to_cast_on(atom/center)
- var/list/things = list()
- for(var/turf/nearby_turf in range(aoe_radius, center))
- things += nearby_turf
-
- return things
+ return RANGE_TURFS(aoe_radius, center)
/datum/action/cooldown/spell/aoe/revenant/before_cast(mob/living/basic/revenant/cast_on)
. = ..()
diff --git a/code/modules/mob/living/basic/vermin/cockroach.dm b/code/modules/mob/living/basic/vermin/cockroach.dm
index c55b94cd0182..51652ae179ae 100644
--- a/code/modules/mob/living/basic/vermin/cockroach.dm
+++ b/code/modules/mob/living/basic/vermin/cockroach.dm
@@ -2,13 +2,15 @@
name = "cockroach"
desc = "This station is just crawling with bugs."
icon_state = "cockroach"
- icon_dead = "cockroach" //Make this work
+ icon_dead = "cockroach_no_animation"
density = FALSE
mob_biotypes = MOB_ORGANIC|MOB_BUG
mob_size = MOB_SIZE_TINY
+ held_w_class = WEIGHT_CLASS_TINY
health = 1
maxHealth = 1
speed = 1.25
+ can_be_held = TRUE
gold_core_spawnable = FRIENDLY_SPAWN
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
@@ -38,17 +40,22 @@
var/static/list/roach_drops = list(/obj/effect/decal/cleanable/insectguts)
AddElement(/datum/element/death_drops, roach_drops)
AddElement(/datum/element/swabable, cockroach_cell_line, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 7)
- AddComponent(/datum/component/squashable, squash_chance = 50, squash_damage = 1)
+ AddComponent( \
+ /datum/component/squashable, \
+ squash_chance = 50, \
+ squash_damage = 1, \
+ squash_flags = SQUASHED_SHOULD_BE_GIBBED|SQUASHED_ALWAYS_IF_DEAD|SQUASHED_DONT_SQUASH_IN_CONTENTS, \
+ )
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
-
-/mob/living/basic/cockroach/death(gibbed)
- if(GLOB.station_was_nuked) //If the nuke is going off, then cockroaches are invincible. Keeps the nuke from killing them, cause cockroaches are immune to nukes.
- return
- ..()
+ ADD_TRAIT(src, TRAIT_NUKEIMMUNE, INNATE_TRAIT)
+ ADD_TRAIT(src, TRAIT_RADIMMUNE, INNATE_TRAIT)
/mob/living/basic/cockroach/ex_act() //Explosions are a terrible way to handle a cockroach.
return FALSE
+// Roach goop is the gibs to drop
+/mob/living/basic/cockroach/spawn_gibs()
+ return
/datum/ai_controller/basic_controller/cockroach
blackboard = list(
@@ -137,7 +144,13 @@
/mob/living/basic/cockroach/hauberoach/Initialize(mapload)
. = ..()
AddComponent(/datum/component/caltrop, min_damage = 10, max_damage = 15, flags = (CALTROP_BYPASS_SHOES | CALTROP_SILENT))
- AddComponent(/datum/component/squashable, squash_chance = 100, squash_damage = 1, squash_callback = TYPE_PROC_REF(/mob/living/basic/cockroach/hauberoach, on_squish))
+ AddComponent( \
+ /datum/component/squashable, \
+ squash_chance = 100, \
+ squash_damage = 1, \
+ squash_flags = SQUASHED_SHOULD_BE_GIBBED|SQUASHED_ALWAYS_IF_DEAD|SQUASHED_DONT_SQUASH_IN_CONTENTS, \
+ squash_callback = TYPE_PROC_REF(/mob/living/basic/cockroach/hauberoach, on_squish), \
+ )
///Proc used to override the squashing behavior of the normal cockroach.
/mob/living/basic/cockroach/hauberoach/proc/on_squish(mob/living/cockroach, mob/living/living_target)
@@ -148,6 +161,7 @@
return TRUE
living_target.visible_message(span_notice("[living_target] squashes [cockroach], not even noticing its spike."), span_notice("You squashed [cockroach], not even noticing its spike."))
return FALSE
+
/datum/ai_controller/basic_controller/cockroach/hauberoach
planning_subtrees = list(
/datum/ai_planning_subtree/pet_planning,
diff --git a/code/modules/mob/living/basic/vermin/mouse.dm b/code/modules/mob/living/basic/vermin/mouse.dm
index bbf09efc3449..880f27750dd5 100644
--- a/code/modules/mob/living/basic/vermin/mouse.dm
+++ b/code/modules/mob/living/basic/vermin/mouse.dm
@@ -36,8 +36,6 @@
var/contributes_to_ratcap = TRUE
/// Probability that, if we successfully bite a shocked cable, that we will die to it.
var/cable_zap_prob = 85
- /// responsible for disease stuff
- var/list/ratdisease = list()
var/chooses_bodycolor = TRUE
@@ -112,7 +110,6 @@
// On death, remove the mouse from the ratcap, and turn it into an item if applicable
/mob/living/basic/mouse/death(gibbed)
- var/list/data = list("viruses" = ratdisease)
SSmobs.cheeserats -= src
// Rats with a mind will not turn into a lizard snack on death
if(mind)
@@ -125,8 +122,15 @@
var/obj/item/food/deadmouse/mouse = new(loc)
mouse.name = name
mouse.icon_state = icon_dead
+ //MONKESTATION EDIT START
+ var/list/data = list("viruses"=list(),"blood_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"immunity"=list())
+ for(var/datum/disease/D as anything in diseases)
+ var/datum/disease/DA = D.Copy()
+ DA.spread_flags = DISEASE_SPREAD_BLOOD //please stop killing the station with the black death from eating rats
+ data["viruses"] += DA
+ data["immunity"] = immune_system.GetImmunity()
+ //MONKESTATION EDIT END
mouse.reagents.add_reagent(/datum/reagent/blood, 2, data)
- mouse.ratdisease = src.ratdisease
if(HAS_TRAIT(src, TRAIT_BEING_SHOCKED))
mouse.desc = "They're toast."
mouse.add_atom_colour("#3A3A3A", FIXED_COLOUR_PRIORITY)
@@ -307,8 +311,6 @@
decomp_req_handle = TRUE
ant_attracting = FALSE
decomp_type = /obj/item/food/deadmouse/moldy
- ///responsible for holding diseases for dead rat
- var/list/ratdisease = list()
var/body_color = "gray"
var/critter_type = /mob/living/basic/mouse
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 4c9635ef9742..4cccc6c6ba63 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -421,7 +421,7 @@
* Helper proc for throwing blood particles around, similar to the spray_blood proc.
*/
/mob/living/proc/blood_particles(amount = rand(1, 3), angle = rand(0,360), min_deviation = -30, max_deviation = 30, min_pixel_z = 0, max_pixel_z = 6)
- if(!isturf(loc) || !blood_volume ||HAS_TRAIT(src, TRAIT_NOBLOOD))
+ if(QDELETED(src) || QDELETED(loc) || !isturf(loc) || !blood_volume || HAS_TRAIT(src, TRAIT_NOBLOOD))
return
for(var/i in 1 to amount)
var/obj/effect/decal/cleanable/blood/particle/droplet = new(loc)
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 e26406a68288..ec6efde78d56 100644
--- a/code/modules/mob/living/carbon/alien/adult/alien_powers.dm
+++ b/code/modules/mob/living/carbon/alien/adult/alien_powers.dm
@@ -277,22 +277,22 @@ Doesn't work on other aliens/AI.*/
// We do this in InterceptClickOn() instead of Activate()
// because we use the click parameters for aiming the projectile
// (or something like that)
-/datum/action/cooldown/alien/acid/neurotoxin/InterceptClickOn(mob/living/caller, params, atom/target)
+/datum/action/cooldown/alien/acid/neurotoxin/InterceptClickOn(mob/living/user, params, atom/target)
. = ..()
if(!.)
- unset_click_ability(caller, refund_cooldown = FALSE)
+ unset_click_ability(user, refund_cooldown = FALSE)
return FALSE
var/modifiers = params2list(params)
- caller.visible_message(
- span_danger("[caller] spits neurotoxin!"),
+ user.visible_message(
+ span_danger("[user] spits neurotoxin!"),
span_alertalien("You spit neurotoxin."),
)
- var/obj/projectile/neurotoxin/neurotoxin = new /obj/projectile/neurotoxin(caller.loc)
- neurotoxin.preparePixelProjectile(target, caller, modifiers)
- neurotoxin.firer = caller
+ var/obj/projectile/neurotoxin/neurotoxin = new /obj/projectile/neurotoxin(user.loc)
+ neurotoxin.preparePixelProjectile(target, user, modifiers)
+ neurotoxin.firer = user
neurotoxin.fire()
- caller.newtonian_move(get_dir(target, caller))
+ user.newtonian_move(get_dir(target, user))
return TRUE
// Has to return TRUE, otherwise is skipped.
diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
index 91821aa049a5..727f17c455e8 100644
--- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
+++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
@@ -65,9 +65,9 @@
if(stage < 6)
INVOKE_ASYNC(src, PROC_REF(RefreshInfectionImage))
var/slowdown = 1
- if(ishuman(owner))
- var/mob/living/carbon/human/baby_momma = owner
- slowdown = baby_momma.reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin) ? 2 : 1 // spaceacillin doubles the time it takes to grow
+ if(!isnull(owner)) // it gestates out of bodies.
+ if(HAS_TRAIT(owner, TRAIT_VIRUS_RESISTANCE))
+ slowdown *= 2 // spaceacillin doubles the time it takes to grow
if(owner.has_status_effect(/datum/status_effect/nest_sustenance))
slowdown *= 0.80 //egg gestates 20% faster if you're trapped in a nest
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 8ed56358f71e..0dcd336ffe1e 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -651,7 +651,7 @@
//this handles hud updates
/mob/living/carbon/update_damage_hud()
-
+ ..() // monkestation edit
if(!client)
return
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 73dad7af793c..bbd4b644412c 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -6,7 +6,8 @@
return INFINITY //For all my homies that can not see in the world
var/obj/item/organ/internal/eyes/eyes = get_organ_slot(ORGAN_SLOT_EYES)
if(eyes)
- . += eyes.flash_protect
+ if(!HAS_TRAIT(src, TRAIT_CONVERSION_FLASHED) || !(eyes.organ_flags & ORGAN_DOESNT_PROTECT_AGAINST_CONVERSION)) // MONKESTATION EDIT: Make IPCs not immune to rev and bb conversions.
+ . += eyes.flash_protect
else
return INFINITY //Can't get flashed without eyes
if(isclothing(head)) //Adds head protection
@@ -519,12 +520,12 @@
add_mood_event("hug", /datum/mood_event/bad_touch_bear_hug)
// Let people know if they hugged someone really warm or really cold
- if(helper.bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT)
+ if(helper.bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT || helper.has_status_effect(/datum/status_effect/bloodsucker_sol)) // monkestation edit: bloodsucker sol
to_chat(src, span_warning("It feels like [helper] is over heating as [helper.p_they()] hug[helper.p_s()] you."))
else if(helper.bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT)
to_chat(src, span_warning("It feels like [helper] is freezing as [helper.p_they()] hug[helper.p_s()] you."))
- if(bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT)
+ if(bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT || has_status_effect(/datum/status_effect/bloodsucker_sol)) // monkestation edit: bloodsucker sol
to_chat(helper, span_warning("It feels like [src] is over heating as you hug [p_them()]."))
else if(bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT)
to_chat(helper, span_warning("It feels like [src] is freezing as you hug [p_them()]."))
diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm
index 3e884ae3b476..af129ce55802 100644
--- a/code/modules/mob/living/carbon/carbon_defines.dm
+++ b/code/modules/mob/living/carbon/carbon_defines.dm
@@ -2,7 +2,7 @@
blood_volume = BLOOD_VOLUME_NORMAL
gender = MALE
pressure_resistance = 15
- hud_possible = list(HEALTH_HUD,STATUS_HUD,ANTAG_HUD,GLAND_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD,SENSOR_HUD)
+ hud_possible = list(HEALTH_HUD,STATUS_HUD,ANTAG_HUD,GLAND_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD,SENSOR_HUD,MOOD_HUD)
has_limbs = TRUE
held_items = list(null, null)
num_legs = 0 //Populated on init through list/bodyparts
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index 9c5a1a90bbb0..13db05913a0e 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -5,7 +5,7 @@
icon = 'icons/mob/species/human/human.dmi'
icon_state = "human_basic"
appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE
- hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPCHEM_HUD,IMPTRACK_HUD,ANTAG_HUD,GLAND_HUD,SENTIENT_DISEASE_HUD,FAN_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD,PERMIT_HUD,SENSOR_HUD)
+ hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPCHEM_HUD,IMPTRACK_HUD,ANTAG_HUD,GLAND_HUD,SENTIENT_DISEASE_HUD,FAN_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD,PERMIT_HUD,SENSOR_HUD,MOOD_HUD)
hud_type = /datum/hud/human
pressure_resistance = 25
can_buckle = TRUE
diff --git a/code/modules/mob/living/carbon/human/physiology.dm b/code/modules/mob/living/carbon/human/physiology.dm
index 3d52aab08928..6975ece08ca5 100644
--- a/code/modules/mob/living/carbon/human/physiology.dm
+++ b/code/modules/mob/living/carbon/human/physiology.dm
@@ -32,7 +32,11 @@
var/siemens_coeff = 1 // resistance to shocks
- var/stun_mod = 1 // % stun modifier
+ /// Multiplier applied to all incapacitating stuns (knockdown, stun, paralyze, immobilize)
+ var/stun_mod = 1
+ /// Multiplied aplpied to just knockdowns, stacks with above multiplicatively
+ var/knockdown_mod = 1
+
var/bleed_mod = 1 // % bleeding modifier
var/datum/armor/armor // internal armor datum
diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
index fc2719f8f7b5..3fb5d55cf93a 100644
--- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
@@ -682,10 +682,11 @@
project_action = new(src)
project_action.Grant(grant_to)
- grant_to.AddComponent(/datum/component/mind_linker, \
+ grant_to.AddComponent( \
+ /datum/component/mind_linker/active_linking, \
network_name = "Slime Link", \
- linker_action_path = /datum/action/innate/link_minds, \
signals_which_destroy_us = list(COMSIG_SPECIES_LOSS), \
+ linker_action_path = /datum/action/innate/link_minds, \
)
//Species datums don't normally implement destroy, but JELLIES SUCK ASS OUT OF A STEEL STRAW
diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
index a122cbb6f292..117ff8257afd 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -122,6 +122,7 @@ Lizard subspecies: ASHWALKERS
id = SPECIES_LIZARD_ASH
mutantlungs = /obj/item/organ/internal/lungs/lavaland
mutantbrain = /obj/item/organ/internal/brain/primitive
+ wing_types = list(/obj/item/organ/external/wings/functional/dragon)
species_traits = list(
MUTCOLORS,
MUTCOLORS_SECONDARY,
@@ -130,7 +131,8 @@ Lizard subspecies: ASHWALKERS
inherent_traits = list(
//TRAIT_LITERATE,
TRAIT_VIRUSIMMUNE,
- TRAIT_HARD_SOLES //MONKESTATION ADDITION
+ TRAIT_HARD_SOLES, //MONKESTATION ADDITION
+ TRAIT_CAN_USE_FLIGHT_POTION,
)
species_language_holder = /datum/language_holder/lizard/ash
/*digitigrade_customization = DIGITIGRADE_FORCED*/ //MONKESTATION REMOVAL: not needed
diff --git a/code/modules/mob/living/carbon/human/species_types/mothmen.dm b/code/modules/mob/living/carbon/human/species_types/mothmen.dm
index 2d41b333f5d7..90cd1774596c 100644
--- a/code/modules/mob/living/carbon/human/species_types/mothmen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/mothmen.dm
@@ -65,7 +65,6 @@
if(istype(attacking_item, /obj/item/melee/flyswatter))
damage_mods += 10 // Yes, a 10x damage modifier
-
/datum/species/moth/randomize_features(mob/living/carbon/human/human_mob)
human_mob.dna.features["moth_markings"] = pick(GLOB.moth_markings_list)
randomize_external_organs(human_mob)
diff --git a/code/modules/mob/living/carbon/human/status_procs.dm b/code/modules/mob/living/carbon/human/status_procs.dm
index 5eb42e042246..acbb3c528c6f 100644
--- a/code/modules/mob/living/carbon/human/status_procs.dm
+++ b/code/modules/mob/living/carbon/human/status_procs.dm
@@ -1,10 +1,10 @@
/mob/living/carbon/human/Stun(amount, ignore_canstun = FALSE)
- amount = dna.species.spec_stun(src,amount)
+ amount = dna.species.spec_stun(src, amount)
return ..()
/mob/living/carbon/human/Knockdown(amount, ignore_canstun = FALSE)
- amount = dna.species.spec_stun(src,amount)
+ amount = dna.species.spec_stun(src, amount) * physiology.knockdown_mod
return ..()
/mob/living/carbon/human/Paralyze(amount, ignore_canstun = FALSE)
@@ -16,7 +16,7 @@
return ..()
/mob/living/carbon/human/Unconscious(amount, ignore_canstun = FALSE)
- amount = dna.species.spec_stun(src,amount)
+ amount = dna.species.spec_stun(src, amount)
if(HAS_TRAIT(src, TRAIT_HEAVY_SLEEPER))
amount *= (rand(125, 130) * 0.01)
return ..()
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index c3842847a11a..44b962a3d5e4 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -330,7 +330,7 @@
to_chat(usr, span_alert("[can_evac_or_fail_reason]"))
return
- var/reason = tgui_input_text(src, "What is the nature of your emergency? ([CALL_SHUTTLE_REASON_LENGTH] characters required.)", "Confirm Shuttle Call")
+ var/reason = tgui_input_text(src, "What is the nature of your emergency? ([CALL_SHUTTLE_REASON_LENGTH] characters required.)", "Confirm Shuttle Call", encode = FALSE) // monkestation edit: fix double-encoded ai shuttle call reasons
if(incapacitated())
return
@@ -779,6 +779,7 @@
if (!camera_light_on)
to_chat(src, "Camera lights deactivated.")
+ list_clear_nulls(lit_cameras)
for (var/obj/machinery/camera/C in lit_cameras)
C.set_light(0)
lit_cameras = list()
@@ -797,8 +798,12 @@
var/list/obj/machinery/camera/visible = list()
for (var/datum/camerachunk/chunk as anything in eyeobj.visibleCameraChunks)
for (var/z_key in chunk.cameras)
- for(var/obj/machinery/camera/camera as anything in chunk.cameras[z_key])
- if (!camera.can_use() || get_dist(camera, eyeobj) > 7 || !camera.internal_light)
+ var/list/z_cameras = chunk.cameras[z_key]
+ list_clear_nulls(z_cameras)
+ for(var/obj/machinery/camera/camera as anything in z_cameras)
+ if(QDELETED(camera))
+ continue
+ if(!camera.can_use() || get_dist(camera, eyeobj) > 7 || !camera.internal_light)
continue
visible |= camera
diff --git a/code/modules/mob/living/silicon/robot/robot_model.dm b/code/modules/mob/living/silicon/robot/robot_model.dm
index 64c4261040c5..4b6363f4c847 100644
--- a/code/modules/mob/living/silicon/robot/robot_model.dm
+++ b/code/modules/mob/living/silicon/robot/robot_model.dm
@@ -760,7 +760,7 @@
/obj/item/robot_model/peacekeeper/do_transform_animation()
..()
- to_chat(loc, "Under ASIMOV, you are an enforcer of the PEACE and preventer of HUMAN HARM. \
+ to_chat(loc, "You are an Enforcer and Upholder of your active lawset. \
You are not a security member and you are expected to follow orders and prevent harm above all else. Space law means nothing to you.")
/obj/item/robot_model/security
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index 26f3ea537144..9101a2ec6049 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -661,8 +661,8 @@ Pass a positive integer as an argument to override a bot's default speed.
if(mode != BOT_SUMMON && mode != BOT_RESPONDING)
access_card.set_access(prev_access)
-/mob/living/simple_animal/bot/proc/call_bot(caller, turf/waypoint, message = TRUE)
- if(isAI(caller) && calling_ai && calling_ai != src) //Prevents an override if another AI is controlling this bot.
+/mob/living/simple_animal/bot/proc/call_bot(user, turf/waypoint, message = TRUE)
+ if(isAI(user) && calling_ai && calling_ai != src) //Prevents an override if another AI is controlling this bot.
return FALSE
bot_reset() //Reset a bot before setting it to call mode.
@@ -671,7 +671,7 @@ Pass a positive integer as an argument to override a bot's default speed.
//Easier then building the list ourselves. I'm sorry.
var/static/obj/item/card/id/all_access = new /obj/item/card/id/advanced/gold/captains_spare()
set_path(get_path_to(src, waypoint, max_distance=200, access = all_access.GetAccess()))
- calling_ai = caller //Link the AI to the bot!
+ calling_ai = user //Link the AI to the bot!
ai_waypoint = waypoint
if(path?.len) //Ensures that a valid path is calculated!
@@ -681,7 +681,7 @@ Pass a positive integer as an argument to override a bot's default speed.
access_card.set_access(REGION_ACCESS_ALL_STATION) //Give the bot all-access while under the AI's command.
if(client)
reset_access_timer_id = addtimer(CALLBACK (src, PROC_REF(bot_reset)), 60 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE) //if the bot is player controlled, they get the extra access for a limited time
- to_chat(src, span_notice("[span_big("Priority waypoint set by [icon2html(calling_ai, src)] [caller]. Proceed to [end_area].")] [path.len-1] meters to destination. You have been granted additional door access for 60 seconds."))
+ to_chat(src, span_notice("[span_big("Priority waypoint set by [icon2html(calling_ai, src)] [user]. Proceed to [end_area].")] [path.len-1] meters to destination. You have been granted additional door access for 60 seconds."))
if(message)
to_chat(calling_ai, span_notice("[icon2html(src, calling_ai)] [name] called to [end_area]. [path.len-1] meters to destination."))
pathset = TRUE
diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm
index 0990edfcc8f9..6b51db93bdf6 100644
--- a/code/modules/mob/living/simple_animal/bot/secbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/secbot.dm
@@ -98,6 +98,19 @@
bot_mode_flags = BOT_MODE_ON
bot_cover_flags = BOT_COVER_LOCKED | BOT_COVER_EMAGGED
+//monkestation edit begin
+/mob/living/simple_animal/bot/secbot/beepsky/big
+ name = "Officer Bigsky"
+ desc = "It's Commander Beep O'sky's massive, just-as aggressive cousin, Bigsky."
+ health = 150
+ bot_mode_flags = BOT_MODE_ON | BOT_MODE_AUTOPATROL | BOT_MODE_REMOTE_ENABLED
+ commissioned = FALSE
+
+/mob/living/simple_animal/bot/secbot/beepsky/jr/Initialize(mapload)
+ . = ..()
+ update_transform(1.2)
+//monkestation edit end
+
/mob/living/simple_animal/bot/secbot/beepsky/explode()
var/atom/Tsec = drop_location()
new /obj/item/stock_parts/cell/potato(Tsec)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
index 34851c30a65a..0e8a8251e6f0 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
@@ -591,6 +591,7 @@
START_PROCESSING(SSobj, src)
/obj/structure/closet/stasis/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
+ . = ..()
if(isliving(arrived) && holder_animal)
var/mob/living/possessor = arrived
possessor.add_traits(list(TRAIT_UNDENSE, TRAIT_NO_TRANSFORM), STASIS_MUTE)
diff --git a/code/modules/mob/living/simple_animal/hostile/ooze.dm b/code/modules/mob/living/simple_animal/hostile/ooze.dm
index 190db5f19ac8..99a94a1197a8 100644
--- a/code/modules/mob/living/simple_animal/hostile/ooze.dm
+++ b/code/modules/mob/living/simple_animal/hostile/ooze.dm
@@ -335,7 +335,7 @@
return TRUE
-/datum/action/cooldown/globules/InterceptClickOn(mob/living/caller, params, atom/target)
+/datum/action/cooldown/globules/InterceptClickOn(mob/living/user, params, atom/target)
. = ..()
if(!.)
return FALSE
@@ -344,19 +344,19 @@
// Well, we need to use the params of the click intercept
// for passing into preparePixelProjectile, so we'll handle it here instead.
// We just need to make sure Pre-activate and Activate return TRUE so we make it this far
- caller.visible_message(
- span_nicegreen("[caller] launches a mending globule!"),
+ user.visible_message(
+ span_nicegreen("[user] launches a mending globule!"),
span_notice("You launch a mending globule."),
)
- var/mob/living/simple_animal/hostile/ooze/oozy = caller
+ var/mob/living/simple_animal/hostile/ooze/oozy = user
if(istype(oozy))
oozy.adjust_ooze_nutrition(-5)
var/modifiers = params2list(params)
- var/obj/projectile/globule/globule = new(caller.loc)
- globule.preparePixelProjectile(target, caller, modifiers)
- globule.def_zone = caller.zone_selected
+ var/obj/projectile/globule/globule = new(user.loc)
+ globule.preparePixelProjectile(target, user, modifiers)
+ globule.def_zone = user.zone_selected
globule.fire()
StartCooldown()
diff --git a/code/modules/mob/living/simple_animal/hostile/vatbeast.dm b/code/modules/mob/living/simple_animal/hostile/vatbeast.dm
index 7c6edeb88da4..48918ec2c483 100644
--- a/code/modules/mob/living/simple_animal/hostile/vatbeast.dm
+++ b/code/modules/mob/living/simple_animal/hostile/vatbeast.dm
@@ -81,13 +81,13 @@
if(refund_cooldown)
to_chat(on_who, span_notice("You stop preparing your [on_who == owner ? "":"steed's "]pimp-tentacle."))
-/datum/action/cooldown/tentacle_slap/InterceptClickOn(mob/living/caller, params, atom/target)
+/datum/action/cooldown/tentacle_slap/InterceptClickOn(mob/living/user, params, atom/target)
// Check if we can slap
if(!isliving(target) || target == owner)
return FALSE
if(!owner.Adjacent(target))
- owner.balloon_alert(caller, "too far!")
+ owner.balloon_alert(user, "too far!")
return FALSE
// Do the slap
@@ -97,8 +97,8 @@
// Give feedback from the slap.
// Additional feedback for if a rider did it
- if(caller != owner)
- to_chat(caller, span_notice("You command [owner] to slap [target] with its tentacles."))
+ if(user != owner)
+ to_chat(user, span_notice("You command [owner] to slap [target] with its tentacles."))
return TRUE
diff --git a/code/modules/mob_spawn/corpses/mob_corpses.dm b/code/modules/mob_spawn/corpses/mob_corpses.dm
index 0ad9d0bcd8ce..56ff8591d9d2 100644
--- a/code/modules/mob_spawn/corpses/mob_corpses.dm
+++ b/code/modules/mob_spawn/corpses/mob_corpses.dm
@@ -48,6 +48,18 @@
id = null
id_trim = null
+//monkestation edit begin
+/obj/effect/mob_spawn/corpse/human/syndicatecommando/lessenedgear/listeningpost
+ outfit = /datum/outfit/syndicatecommandocorpse/lessenedgear/listeningpost
+
+/datum/outfit/syndicatecommandocorpse/lessenedgear/listeningpost
+ name = "Forgotten Comms Agent Corpse"
+ back = /obj/item/storage/backpack
+ uniform = /obj/item/clothing/under/syndicate/sniper //corpse drip
+ neck = /obj/item/clothing/neck/large_scarf/syndie
+ head = /obj/item/clothing/head/soft/black
+//monke business ends
+
/obj/effect/mob_spawn/corpse/human/syndicatestormtrooper
name = "Syndicate Stormtrooper"
hairstyle = "Bald"
diff --git a/code/modules/mob_spawn/ghost_roles/space_roles.dm b/code/modules/mob_spawn/ghost_roles/space_roles.dm
index cc97da88536e..f64984017f5e 100644
--- a/code/modules/mob_spawn/ghost_roles/space_roles.dm
+++ b/code/modules/mob_spawn/ghost_roles/space_roles.dm
@@ -87,19 +87,6 @@
shoes = /obj/item/clothing/shoes/laceup
l_pocket = /obj/item/stack/medical/bruise_pack
-///asteroid comms agent
-
-/obj/effect/mob_spawn/ghost_role/human/lavaland_syndicate/comms/space
- you_are_text = "You are a syndicate agent, assigned to a small listening post station situated near your hated enemy's top secret research facility: Space Station 13."
- flavour_text = "Monitor enemy activity as best you can, and try to keep a low profile. Monitor enemy activity as best you can, and try to keep a low profile. Use the communication equipment to provide support to any field agents, and sow disinformation to throw Nanotrasen off your trail. Do not let the base fall into enemy hands!"
- important_text = "DO NOT abandon the base."
-
-/obj/effect/mob_spawn/ghost_role/human/lavaland_syndicate/comms/space/Initialize(mapload)
- . = ..()
- if(prob(85)) //only has a 15% chance of existing, otherwise it'll just be a NPC syndie.
- new /mob/living/basic/trooper/syndicate/ranged(get_turf(src))
- return INITIALIZE_HINT_QDEL
-
///battlecruiser stuff
/obj/effect/mob_spawn/ghost_role/human/syndicate/battlecruiser
diff --git a/code/modules/mod/mod_activation.dm b/code/modules/mod/mod_activation.dm
index 36563bbd96cb..2745033c2a59 100644
--- a/code/modules/mod/mod_activation.dm
+++ b/code/modules/mod/mod_activation.dm
@@ -234,7 +234,8 @@
module.on_suit_deactivation()
STOP_PROCESSING(SSobj, src)
update_speed()
- update_icon_state()
+ if(!(locate(/obj/item/mod/module/chameleon) in modules)) // monkestation edit: janky bugfix for chameleon modules
+ update_icon_state()
wearer.update_clothing(slot_flags)
/// Quickly deploys all the suit parts and if successful, seals them and turns on the suit. Intended mostly for outfits.
diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm
index 2c69dac697ca..f23fdd549c00 100644
--- a/code/modules/mod/modules/modules_antag.dm
+++ b/code/modules/mod/modules/modules_antag.dm
@@ -148,9 +148,9 @@
icon_state = "battlemage_shield"
idle_power_cost = DEFAULT_CHARGE_DRAIN * 0 //magic
use_power_cost = DEFAULT_CHARGE_DRAIN * 0 //magic too
- max_charges = 15
- recharge_start_delay = 0 SECONDS
- charge_recovery = 12 //monkestation edit: from 8 to 12
+ max_charges = 25 //monkestation edit: from 15 to 25
+ recharge_start_delay = 1 MINUTES //monkestation edit: from 0 SECONDS to 1 MINUTES
+ charge_recovery = 25 //monkestation edit: from 8 to 25
shield_icon_file = 'icons/effects/magic.dmi'
shield_icon = "mageshield"
recharge_path = /obj/item/wizard_armour_charge
@@ -354,6 +354,7 @@
return
mod.wearer.do_attack_animation(target, ATTACK_EFFECT_SMASH)
+/* monkestation removal: overwritten in [monkestation\code\modules\mod\modules\modules_antag.dm], to fix bugs
///Chameleon - lets the suit disguise as any item that would fit on that slot.
/obj/item/mod/module/chameleon
name = "MOD chameleon module"
@@ -425,6 +426,7 @@
mod.wearer.update_clothing(mod.slot_flags)
current_disguise = null
UnregisterSignal(mod, COMSIG_MOD_ACTIVATE)
+monkestation end */
///Plate Compression - Compresses the suit to normal size
/obj/item/mod/module/plate_compression
diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm
index ea8e4409ee1b..5dd178c409d7 100644
--- a/code/modules/mod/modules/modules_general.dm
+++ b/code/modules/mod/modules/modules_general.dm
@@ -208,7 +208,7 @@
for(var/datum/disease/virus as anything in mod.wearer.diseases)
var/list/virus_data = list()
virus_data["name"] = virus.name
- virus_data["type"] = virus.spread_text
+ virus_data["type"] = virus.get_spread_string()
virus_data["stage"] = virus.stage
virus_data["maxstage"] = virus.max_stages
virus_data["cure"] = virus.cure_text
diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm
index 1e3d2a4898a6..61985dd13f42 100644
--- a/code/modules/modular_computers/computers/item/computer.dm
+++ b/code/modules/modular_computers/computers/item/computer.dm
@@ -497,11 +497,11 @@
* The program calling this proc.
* The message that the program wishes to display.
*/
-/obj/item/modular_computer/proc/alert_call(datum/computer_file/program/caller, alerttext, sound = 'sound/machines/twobeep_high.ogg')
- if(!caller || !caller.alert_able || caller.alert_silenced || !alerttext) //Yeah, we're checking alert_able. No, you don't get to make alerts that the user can't silence.
+/obj/item/modular_computer/proc/alert_call(datum/computer_file/program/origin, alerttext, sound = 'sound/machines/twobeep_high.ogg')
+ if(!origin || !origin.alert_able || origin.alert_silenced || !alerttext) //Yeah, we're checking alert_able. No, you don't get to make alerts that the user can't silence.
return FALSE
playsound(src, sound, 50, TRUE)
- loc.visible_message(span_notice("[icon2html(src)] [span_notice("The [src] displays a [caller.filedesc] notification: [alerttext]")]"))
+ loc.visible_message(span_notice("[icon2html(src)] [span_notice("The [src] displays a [origin.filedesc] notification: [alerttext]")]"))
/obj/item/modular_computer/proc/ring(ringtone) // bring bring
if(HAS_TRAIT(SSstation, STATION_TRAIT_PDA_GLITCHED))
diff --git a/code/modules/modular_computers/file_system/programs/jobmanagement.dm b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
index cb059cf9663e..b394b0a2ad9f 100644
--- a/code/modules/modular_computers/file_system/programs/jobmanagement.dm
+++ b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
@@ -14,19 +14,6 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
program_icon = "address-book"
var/change_position_cooldown = 30
- ///Jobs blacklisted from having their slots edited.
- var/static/list/blacklisted = list(
- JOB_CAPTAIN,
- JOB_HEAD_OF_PERSONNEL,
- JOB_HEAD_OF_SECURITY,
- JOB_RESEARCH_DIRECTOR,
- JOB_CHIEF_ENGINEER,
- JOB_CHIEF_MEDICAL_OFFICER,
- JOB_QUARTERMASTER,
- JOB_AI,
- JOB_CYBORG,
- JOB_ASSISTANT,
- )
//The scaling factor of max total positions in relation to the total amount of people on board the station in %
var/max_relative_positions = 30 //30%: Seems reasonable, limit of 6 @ 20 players
@@ -41,14 +28,16 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
/datum/computer_file/program/job_management/proc/can_edit_job(datum/job/job)
- if(!job || !(job.job_flags & JOB_CREW_MEMBER) || (job.title in blacklisted))
+ if(!istype(job))
+ return FALSE
+ if(!(job.job_flags & JOB_CREW_MEMBER))
+ return FALSE
+ if(job.job_flags & JOB_CANNOT_OPEN_SLOTS)
return FALSE
return TRUE
/datum/computer_file/program/job_management/proc/can_open_job(datum/job/job)
- if(!can_edit_job(job))
- return FALSE
if((job.total_positions <= length(GLOB.player_list) * (max_relative_positions / 100)))
var/delta = (world.time / 10) - GLOB.time_last_changed_position
if((change_position_cooldown < delta) || (opened_positions[job.title] < 0))
@@ -57,8 +46,6 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
/datum/computer_file/program/job_management/proc/can_close_job(datum/job/job)
- if(!can_edit_job(job))
- return FALSE
if(job.total_positions > length(GLOB.player_list) * (max_relative_positions / 100))
var/delta = (world.time / 10) - GLOB.time_last_changed_position
if((change_position_cooldown < delta) || (opened_positions[job.title] > 0))
@@ -75,7 +62,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if("PRG_open_job")
var/edit_job_target = params["target"]
var/datum/job/j = SSjob.GetJob(edit_job_target)
- if(!j || !can_open_job(j))
+ if(!can_edit_job(j) || !can_open_job(j))
return TRUE
if(opened_positions[edit_job_target] >= 0)
GLOB.time_last_changed_position = world.time / 10
@@ -87,7 +74,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if("PRG_close_job")
var/edit_job_target = params["target"]
var/datum/job/j = SSjob.GetJob(edit_job_target)
- if(!j || !can_close_job(j))
+ if(!can_edit_job(j) || !can_close_job(j))
return TRUE
//Allow instant closing without cooldown if a position has been opened before
if(opened_positions[edit_job_target] <= 0)
@@ -100,7 +87,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if("PRG_priority")
var/priority_target = params["target"]
var/datum/job/j = SSjob.GetJob(priority_target)
- if(!j || !can_edit_job(j))
+ if(!can_edit_job(j))
return TRUE
if(j.total_positions <= j.current_positions)
return TRUE
@@ -128,7 +115,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
var/list/pos = list()
var/list/priority = list()
for(var/datum/job/job as anything in SSjob.joinable_occupations)
- if(job.title in blacklisted)
+ if(!can_edit_job(job))
continue
if(job in SSjob.prioritized_jobs)
priority += job.title
@@ -145,4 +132,3 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
var/delta = round(change_position_cooldown - ((world.time / 10) - GLOB.time_last_changed_position), 1)
data["cooldown"] = delta < 0 ? 0 : delta
return data
-
diff --git a/code/modules/movespeed/modifiers/mobs.dm b/code/modules/movespeed/modifiers/mobs.dm
index c2778d19471b..892b928db9ee 100644
--- a/code/modules/movespeed/modifiers/mobs.dm
+++ b/code/modules/movespeed/modifiers/mobs.dm
@@ -172,3 +172,6 @@
/datum/movespeed_modifier/basilisk_overheat
multiplicative_slowdown = -18
+
+/datum/movespeed_modifier/magic_ties
+ multiplicative_slowdown = 0.5
diff --git a/code/modules/pai/card.dm b/code/modules/pai/card.dm
index 1c42beac3412..3e11c0b7bcb8 100644
--- a/code/modules/pai/card.dm
+++ b/code/modules/pai/card.dm
@@ -68,6 +68,7 @@
. = ..()
update_appearance()
SSpai.pai_card_list += src
+ ADD_TRAIT(src, TRAIT_CASTABLE_LOC, INNATE_TRAIT)
/obj/item/pai_card/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] is staring sadly at [src]! [user.p_they()] can't keep living without real human intimacy!"))
diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm
index e8387182fad7..12c9bda7fd5c 100644
--- a/code/modules/power/gravitygenerator.dm
+++ b/code/modules/power/gravitygenerator.dm
@@ -212,6 +212,20 @@ GLOBAL_LIST_EMPTY(gravity_generators)
// Interaction
+/obj/machinery/gravity_generator/main/examine(mob/user)
+ . = ..()
+ if(!(machine_stat & BROKEN))
+ return
+ switch(broken_state)
+ if(GRAV_NEEDS_SCREWDRIVER)
+ . += span_notice("The entire frame is barely holding together, the screws need to be refastened.")
+ if(GRAV_NEEDS_WELDING)
+ . += span_notice("There's lots of broken seals on the framework, it could use some welding.")
+ if(GRAV_NEEDS_PLASTEEL)
+ . += span_notice("Some of this damaged plating needs full replacement. 10 plasteel> should be enough.")
+ if(GRAV_NEEDS_WRENCH)
+ . += span_notice("The new plating just needs to be bolted into place now.")
+
// Fixing the gravity generator.
/obj/machinery/gravity_generator/main/attackby(obj/item/weapon, mob/user, params)
if(machine_stat & BROKEN)
diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm
index afdaa342f2ad..4d3625b7a5ad 100644
--- a/code/modules/power/singularity/singularity.dm
+++ b/code/modules/power/singularity/singularity.dm
@@ -341,7 +341,7 @@
if(STAGE_ONE)
steps = 1
if(STAGE_TWO)
- steps = 3//Yes this is right
+ steps = 2//Now THIS is right
if(STAGE_THREE)
steps = 3
if(STAGE_FOUR)
diff --git a/code/modules/projectiles/ammunition/ballistic/revolver.dm b/code/modules/projectiles/ammunition/ballistic/revolver.dm
index 8784bdeece3c..c27d3557647b 100644
--- a/code/modules/projectiles/ammunition/ballistic/revolver.dm
+++ b/code/modules/projectiles/ammunition/ballistic/revolver.dm
@@ -56,3 +56,17 @@
name = ".38 Iceblox bullet casing"
desc = "A .38 Iceblox bullet casing."
projectile_type = /obj/projectile/bullet/c38/iceblox
+
+// .45 (Lever Rifle and Long Revolver)
+
+/obj/item/ammo_casing/g45l
+ name = ".45 Long bullet casing "
+ desc = "A .45 Long bullet casing."
+ caliber = CALIBER_45L
+ projectile_type = /obj/projectile/bullet/g45l
+
+/obj/item/ammo_casing/g45l/rubber
+ name = ".45 Long rubber bullet casing"
+ desc = "A .45 Long rubber bullet casing."
+ caliber = CALIBER_45L
+ projectile_type = /obj/projectile/bullet/g45l/rubber
diff --git a/code/modules/projectiles/ammunition/energy/special.dm b/code/modules/projectiles/ammunition/energy/special.dm
index 47f36877f053..684f18e77efa 100644
--- a/code/modules/projectiles/ammunition/energy/special.dm
+++ b/code/modules/projectiles/ammunition/energy/special.dm
@@ -65,7 +65,7 @@
projectile_type = /obj/projectile/energy/tesla_cannon
/obj/item/ammo_casing/energy/shrink
- projectile_type = /obj/projectile/beam/shrink
+ projectile_type = /obj/projectile/magic/shrink/alien
select_name = "shrink ray"
e_cost = 200
diff --git a/code/modules/projectiles/ammunition/special/magic.dm b/code/modules/projectiles/ammunition/special/magic.dm
index 0965aae2fddd..70afbc169256 100644
--- a/code/modules/projectiles/ammunition/special/magic.dm
+++ b/code/modules/projectiles/ammunition/special/magic.dm
@@ -83,3 +83,9 @@
/obj/item/ammo_casing/magic/nothing
projectile_type = /obj/projectile/magic/nothing
harmful = FALSE
+
+/obj/item/ammo_casing/magic/shrink
+ projectile_type = /obj/projectile/magic/shrink
+
+/obj/item/ammo_casing/magic/shrink/wand
+ projectile_type = /obj/projectile/magic/shrink/wand
diff --git a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm
index 2ff028e19909..1d345cca5d44 100644
--- a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm
+++ b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm
@@ -55,6 +55,20 @@
desc = "Designed to quickly reload revolvers. Iceblox bullets contain a cryogenic payload."
ammo_type = /obj/item/ammo_casing/c38/iceblox
+/obj/item/ammo_box/g45l
+ name = "ammo box (.45 Long Lethal)"
+ desc = "This box contains .45 Long lethal cartridges."
+ ammo_type = /obj/item/ammo_casing/g45l
+ icon_state = "45box"
+ max_ammo = 24
+
+/obj/item/ammo_box/g45l/rubber
+ name = "ammo box (.45 Long Rubber)"
+ desc = "Brought to you at great expense,this box contains .45 Long rubber cartridges."
+ icon_state = "45box"
+ ammo_type = /obj/item/ammo_casing/g45l/rubber
+ max_ammo = 24
+
/obj/item/ammo_box/c9mm
name = "ammo box (9mm)"
icon_state = "9mmbox"
diff --git a/code/modules/projectiles/boxes_magazines/internal/revolver.dm b/code/modules/projectiles/boxes_magazines/internal/revolver.dm
index 7d881a11c4dc..c742117ccbaf 100644
--- a/code/modules/projectiles/boxes_magazines/internal/revolver.dm
+++ b/code/modules/projectiles/boxes_magazines/internal/revolver.dm
@@ -20,3 +20,9 @@
/obj/item/ammo_box/magazine/internal/rus357/Initialize(mapload)
stored_ammo += new ammo_type(src)
. = ..()
+
+/obj/item/ammo_box/magazine/internal/cylinder/rev45l
+ name = ".45 Long revolver cylinder"
+ ammo_type = /obj/item/ammo_casing/g45l
+ caliber = CALIBER_45L
+ max_ammo = 6
diff --git a/code/modules/projectiles/boxes_magazines/internal/shotgun.dm b/code/modules/projectiles/boxes_magazines/internal/shotgun.dm
index d5e234034d6f..32ad20799d73 100644
--- a/code/modules/projectiles/boxes_magazines/internal/shotgun.dm
+++ b/code/modules/projectiles/boxes_magazines/internal/shotgun.dm
@@ -42,3 +42,9 @@
name = "triple-barrel shotgun internal magazine"
ammo_type = /obj/item/ammo_casing/shotgun/incapacitate
max_ammo = 3
+
+/obj/item/ammo_box/magazine/internal/shot/levergun
+ name = "brush gun internal magazine"
+ ammo_type = /obj/item/ammo_casing/g45l
+ caliber = CALIBER_45L
+ max_ammo = 6
diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm
index 6d2e9d9c3c9f..7afb1ab9c49a 100644
--- a/code/modules/projectiles/guns/ballistic/revolver.dm
+++ b/code/modules/projectiles/guns/ballistic/revolver.dm
@@ -157,6 +157,17 @@
accepted_magazine_type = /obj/item/ammo_box/magazine/internal/cylinder/rev762
+/obj/item/gun/ballistic/revolver/r45l
+ name = "\improper .45 Long Revolver"
+ desc = "A cheap .45 Long Revolver. Pray the timing keeps."
+ icon_state = "45revolver"
+ accepted_magazine_type = /obj/item/ammo_box/magazine/internal/cylinder/rev45l
+ obj_flags = UNIQUE_RENAME
+
+ unique_reskin = list("Default" = "45revolver",
+ "Cowboy" = "357colt",
+ "Lucky" = "lucky" //Well do ya?
+ )
// A gun to play Russian Roulette!
// You can spin the chamber to randomize the position of the bullet.
@@ -286,3 +297,5 @@
user.emote("scream")
user.drop_all_held_items()
user.Paralyze(80)
+
+
diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm
index 32d98eceb6b8..f9871819731e 100644
--- a/code/modules/projectiles/guns/ballistic/shotgun.dm
+++ b/code/modules/projectiles/guns/ballistic/shotgun.dm
@@ -334,3 +334,23 @@
/obj/item/gun/ballistic/shotgun/hook/afterattack_secondary(atom/target, mob/user, proximity_flag, click_parameters)
hook.afterattack(target, user, proximity_flag, click_parameters)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+// Lever gun
+/obj/item/gun/ballistic/shotgun/leveraction
+ name = "brush gun"
+ desc = "While lever-actions have been horribly out of date for hundreds of years now, \
+ putting a nicely sized hole in a man-sized target with a .45 Long round has stayed relatively timeless."
+ icon_state = "brushgun"
+ bolt_wording = "Lever"
+ cartridge_wording = "bullet"
+ accepted_magazine_type = /obj/item/ammo_box/magazine/internal/shot/levergun
+ projectile_wound_bonus = 10
+ projectile_damage_multiplier = 1.1
+ w_class = WEIGHT_CLASS_BULKY
+ force = 10
+ flags_1 = CONDUCT_1
+ semi_auto = FALSE
+ internal_magazine = TRUE
+ casing_ejector = FALSE
+ weapon_weight = WEAPON_HEAVY
+
diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm
index e23a1e9121a4..dc729ab3a1f9 100644
--- a/code/modules/projectiles/guns/magic/staff.dm
+++ b/code/modules/projectiles/guns/magic/staff.dm
@@ -148,6 +148,7 @@
/obj/projectile/magic/teleport,
/obj/projectile/magic/wipe,
/obj/projectile/temp/chill,
+ /obj/projectile/magic/shrink
)
/obj/item/gun/magic/staff/chaos/unrestricted
@@ -318,3 +319,17 @@
inhand_icon_state = "pharoah_sceptre"
worn_icon_state = "wipestaff"
school = SCHOOL_FORBIDDEN //arguably the worst staff in the entire game effect wise
+
+/obj/item/gun/magic/staff/shrink
+ name = "staff of shrinking"
+ desc = "An artefact that spits bolts of tiny magic that makes things small. It's easily mistaken for a wand."
+ fire_sound = 'sound/magic/staff_shrink.ogg'
+ ammo_type = /obj/item/ammo_casing/magic/shrink
+ icon_state = "shrinkstaff"
+ inhand_icon_state = "staff"
+ max_charges = 10 // slightly more/faster charges since this will be used on walls and such
+ recharge_rate = 5
+ no_den_usage = TRUE
+ school = SCHOOL_TRANSMUTATION
+ slot_flags = NONE //too small to wear on your back
+ w_class = WEIGHT_CLASS_NORMAL //but small enough for a bag
diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm
index a078c4ae00b0..82b78a4859ed 100644
--- a/code/modules/projectiles/guns/magic/wand.dm
+++ b/code/modules/projectiles/guns/magic/wand.dm
@@ -256,3 +256,25 @@
name = "wand of nothing"
desc = "It's not just a stick, it's a MAGIC stick?"
ammo_type = /obj/item/ammo_casing/magic/nothing
+
+
+/////////////////////////////////////
+//WAND OF SHRINKING
+/////////////////////////////////////
+
+/obj/item/gun/magic/wand/shrink
+ name = "wand of shrinking"
+ desc = "Feel the tiny eldritch terror of an itty... bitty... head!"
+ ammo_type = /obj/item/ammo_casing/magic/shrink/wand
+ icon_state = "shrinkwand"
+ base_icon_state = "shrinkwand"
+ fire_sound = 'sound/magic/staff_shrink.ogg'
+ max_charges = 10 //10, 5, 5, 4
+ no_den_usage = TRUE
+ w_class = WEIGHT_CLASS_TINY
+
+/obj/item/gun/magic/wand/shrink/zap_self(mob/living/user)
+ to_chat(user, span_notice("The world grows large..."))
+ charges--
+ user.AddComponent(/datum/component/shrink, -1) // small forever
+ return ..()
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 28166514b241..35ca181378e7 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -519,8 +519,10 @@
return process_hit(T, select_target(T, target, bumped), bumped, hit_something) // try to hit something else
// at this point we are going to hit the thing
// in which case send signal to it
- if (SEND_SIGNAL(target, COMSIG_PROJECTILE_PREHIT, args, src) & PROJECTILE_INTERRUPT_HIT)
- qdel(src)
+ var/signal_bitfield = SEND_SIGNAL(target, COMSIG_PROJECTILE_PREHIT, args, src) //monkestation edit
+ if (signal_bitfield & PROJECTILE_INTERRUPT_HIT)
+ if(!(signal_bitfield & PROJECTILE_INTERRUPT_BLOCK_QDEL)) //monkestation edit
+ qdel(src)
return BULLET_ACT_BLOCK
if(mode == PROJECTILE_PIERCE_HIT)
++pierces
@@ -919,7 +921,7 @@
process_homing()
var/forcemoved = FALSE
for(var/i in 1 to SSprojectiles.global_iterations_per_move)
- if(QDELETED(src))
+ if(QDELETED(src) || !trajectory) //monkestation edit: adds the trajectory check
return
trajectory.increment(trajectory_multiplier)
var/turf/T = trajectory.return_turf()
diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm
index 8d5d22da3cdc..96c99fca0298 100644
--- a/code/modules/projectiles/projectile/beams.dm
+++ b/code/modules/projectiles/projectile/beams.dm
@@ -226,23 +226,6 @@
/obj/projectile/beam/lasertag/bluetag/hitscan
hitscan = TRUE
-//a shrink ray that shrinks stuff, which grows back after a short while.
-/obj/projectile/beam/shrink
- name = "shrink ray"
- icon_state = "blue_laser"
- hitsound = 'sound/weapons/shrink_hit.ogg'
- damage = 0
- damage_type = STAMINA
- armor_flag = ENERGY
- impact_effect_type = /obj/effect/temp_visual/impact_effect/shrink
- light_color = LIGHT_COLOR_BLUE
- var/shrink_time = 90
-
-/obj/projectile/beam/shrink/on_hit(atom/target, blocked = 0, pierce_hit)
- . = ..()
- if(isopenturf(target) || isindestructiblewall(target))//shrunk floors wouldnt do anything except look weird, i-walls shouldn't be bypassable
- return
- target.AddComponent(/datum/component/shrink, shrink_time)
-
-/obj/projectile/beam/shrink/is_hostile_projectile()
- return TRUE
+/obj/projectile/magic/shrink/alien
+ antimagic_flags = NONE
+ shrink_time = 9 SECONDS
diff --git a/code/modules/projectiles/projectile/bullets/revolver.dm b/code/modules/projectiles/projectile/bullets/revolver.dm
index 7436ad2fa482..7028cc22446d 100644
--- a/code/modules/projectiles/projectile/bullets/revolver.dm
+++ b/code/modules/projectiles/projectile/bullets/revolver.dm
@@ -126,3 +126,20 @@
ricochet_auto_aim_range = 6
ricochet_incidence_leeway = 80
ricochet_decay_chance = 1
+
+// .45 (Lever Rifle and Long Revolver)
+
+/obj/projectile/bullet/g45l/rubber
+ name = ".45 Long rubber bullet"
+ damage = 5
+ stamina = 35
+ weak_against_armour = TRUE
+ sharpness = NONE
+ embedding = null
+
+/obj/projectile/bullet/g45l
+ name = ".45 Long bullet"
+ damage = 35
+ wound_bonus = -5
+ sharpness = SHARP_EDGED
+ 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)
diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm
index 3b0104ac55c7..ea38aedb0021 100644
--- a/code/modules/projectiles/projectile/magic.dm
+++ b/code/modules/projectiles/projectile/magic.dm
@@ -611,3 +611,31 @@
damage_type = BURN
damage = 2
antimagic_charge_cost = 0 // since the cards gets spammed like a shotgun
+
+//a shrink ray that shrinks stuff, which grows back after a short while.
+/obj/projectile/magic/shrink
+ name = "shrink ray"
+ icon_state = "blue_laser"
+ hitsound = 'sound/weapons/shrink_hit.ogg'
+ damage = 0
+ damage_type = STAMINA
+ armor_flag = ENERGY
+ impact_effect_type = /obj/effect/temp_visual/impact_effect/shrink
+ light_color = LIGHT_COLOR_BLUE
+ var/shrink_time = -1
+
+/obj/projectile/magic/shrink/on_hit(atom/target, blocked = 0, pierce_hit)
+ . = ..()
+ if(isopenturf(target) || isindestructiblewall(target))//shrunk floors wouldnt do anything except look weird, i-walls shouldn't be bypassable
+ return
+ target.AddComponent(/datum/component/shrink, shrink_time)
+
+/obj/projectile/magic/shrink/is_hostile_projectile()
+ return TRUE
+
+/obj/projectile/magic/shrink/wand
+ shrink_time = 90 SECONDS
+
+/obj/projectile/magic/shrink/wand/on_hit(atom/target, blocked = 0, pierce_hit)
+ shrink_time = rand(60 SECONDS, 90 SECONDS)
+ return ..()
diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm
index d67449a7fb91..348838f7ec67 100644
--- a/code/modules/reagents/chemistry/machinery/pandemic.dm
+++ b/code/modules/reagents/chemistry/machinery/pandemic.dm
@@ -307,7 +307,7 @@
traits["description"] = disease.desc || "none"
traits["index"] = index++
traits["name"] = disease.name
- traits["spread"] = disease.spread_text || "none"
+ traits["spread"] = disease.get_spread_string() || "none"
if(istype(disease, /datum/disease/advance)) // Advanced diseases get more info
var/datum/disease/advance/adv_disease = disease
var/disease_name = SSdisease.get_disease_name(adv_disease.GetDiseaseID())
diff --git a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm
index 79d9a191e845..d91620353710 100644
--- a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm
@@ -2015,17 +2015,18 @@
taste_description = "the pain of ten thousand slain mosquitos"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
-/datum/reagent/consumable/ethanol/bug_spray/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, times_fired)
- //Bugs should not drink Bug spray.
- if(ismoth(drinker) || isflyperson(drinker))
- drinker.adjustToxLoss(1 * REM * seconds_per_tick, FALSE, required_biotype = affected_biotype)
- return ..()
-
-/datum/reagent/consumable/ethanol/bug_spray/on_mob_metabolize(mob/living/carbon/drinker)
+/datum/reagent/consumable/ethanol/bug_spray/on_new(data)
+ . = ..()
+ AddElement(/datum/element/bugkiller_reagent)
- if(ismoth(drinker) || isflyperson(drinker))
+/datum/reagent/consumable/ethanol/bug_spray/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, times_fired)
+ // Does some damage to bug biotypes
+ var/did_damage = drinker.adjustToxLoss(1 * REM * seconds_per_tick, updating_health = FALSE, required_biotype = MOB_BUG)
+ // Random chance of causing a screm if we did some damage
+ if(did_damage && SPT_PROB(2, seconds_per_tick))
drinker.emote("scream")
- return ..()
+
+ return ..() || did_damage
/datum/reagent/consumable/ethanol/applejack
name = "Applejack"
diff --git a/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm
index b4d869631672..8dccaf47f6f5 100644
--- a/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm
@@ -118,7 +118,7 @@ Basically, we fill the time between now and 2s from now with hands based off the
hands++
for(var/id in timer_ids) // So that we can be certain that all timers are deleted at the end.
deltimer(id)
- timer_ids.Cut()
+ timer_ids?.Cut()
return ..()
/datum/reagent/inverse/helgrasp/heretic
diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
index f7dd0808abba..d1bfbf5a97df 100644
--- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
@@ -241,6 +241,14 @@
ph = 8.1
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
+/datum/reagent/medicine/spaceacillin/on_mob_metabolize(mob/living/L)
+ . = ..()
+ ADD_TRAIT(L, TRAIT_VIRUS_RESISTANCE, type)
+
+/datum/reagent/medicine/spaceacillin/on_mob_end_metabolize(mob/living/L)
+ . = ..()
+ REMOVE_TRAIT(L, TRAIT_VIRUS_RESISTANCE, type)
+
//Goon Chems. Ported mainly from Goonstation. Easily mixable (or not so easily) and provide a variety of effects.
/datum/reagent/medicine/oxandrolone
diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
index d7d309fe5e9d..54e6461a701f 100644
--- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
@@ -389,11 +389,13 @@
ph = 3.2
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
-/datum/reagent/toxin/pestkiller/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume)
+/datum/reagent/toxin/pestkiller/on_new(data)
. = ..()
- if(exposed_mob.mob_biotypes & MOB_BUG)
- var/damage = min(round(0.4*reac_volume, 0.1),10)
- exposed_mob.adjustToxLoss(damage, required_biotype = affected_biotype)
+ AddElement(/datum/element/bugkiller_reagent)
+
+/datum/reagent/toxin/pestkiller/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired)
+ . = affected_mob.adjustToxLoss(2 * toxpwr * REM * seconds_per_tick, updating_health = FALSE, required_biotype = MOB_BUG)
+ return ..() || .
/datum/reagent/toxin/pestkiller/organic
name = "Natural Pest Killer"
diff --git a/code/modules/religion/sparring/sparring_contract.dm b/code/modules/religion/sparring/sparring_contract.dm
index 4f1f17e6837e..9238af61a397 100644
--- a/code/modules/religion/sparring/sparring_contract.dm
+++ b/code/modules/religion/sparring/sparring_contract.dm
@@ -84,7 +84,7 @@
if(!isnull(resolved))
resolved_opponents += resolved
- if(user in resolved_opponents && params["stakes"] == STAKES_HOLY_MATCH)
+ if((user in resolved_opponents) && params["stakes"] == STAKES_HOLY_MATCH)
to_chat(user, span_warning("This contract refuses to be signed up for a holy match by a previous holy match loser. Pick a different stake!"))
//any updating of the terms should update the UI to display new terms
diff --git a/code/modules/research/designs/autolathe/security_designs.dm b/code/modules/research/designs/autolathe/security_designs.dm
index def8847a1743..6c6449873126 100644
--- a/code/modules/research/designs/autolathe/security_designs.dm
+++ b/code/modules/research/designs/autolathe/security_designs.dm
@@ -234,6 +234,30 @@
)
departmental_flags = DEPARTMENT_BITFLAG_SECURITY
+/datum/design/g45l
+ name = "Ammo Box (.45 Long) (Rubber)"
+ id = "g45l"
+ build_type = AUTOLATHE
+ materials = list(/datum/material/iron = 30000)
+ build_path = /obj/item/ammo_box/g45l/rubber
+ category = list(
+ RND_CATEGORY_HACKED,
+ RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_AMMO,
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_SECURITY
+
+/datum/design/g45lethal
+ name = "Ammo Box (.45 Long) (Lethal)"
+ id = "g45lethal"
+ build_type = AUTOLATHE
+ materials = list(/datum/material/iron = 35000)
+ build_path = /obj/item/ammo_box/g45l
+ category = list(
+ RND_CATEGORY_HACKED,
+ RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_AMMO,
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_SECURITY
+
/datum/design/c9mm
name = "Ammo Box (9mm) (Lethal)"
id = "c9mm"
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 6367a99ecb59..423afdaeb938 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -685,11 +685,24 @@
design_ids = list(
"anomaly_neutralizer",
"reactive_armour",
+ //"artifact_heater", //MONKESTATION EDIT REMOVAL
+ //"artifact_xray", //MONKESTATION EDIT REMOVAL
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
+//MONKEYSTATION ADDITION START
+/datum/techweb_node/artifact
+ id = "artifact_research"
+ display_name = "Artifact Research"
+ description = "Properly concuct research on the various artifacts found around."
+ prereq_ids = list("base")
+ design_ids = list(
"artifact_heater",
"artifact_xray",
+ "disk_artifact",
+ "artifact_wand"
)
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
-
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
+//MONKESTATION ADDITION END
/datum/techweb_node/high_efficiency
id = "high_efficiency"
display_name = "High Efficiency Parts"
diff --git a/code/modules/research/xenobiology/crossbreeding/_clothing.dm b/code/modules/research/xenobiology/crossbreeding/_clothing.dm
index 4290f7a7a746..24439dc1b4f4 100644
--- a/code/modules/research/xenobiology/crossbreeding/_clothing.dm
+++ b/code/modules/research/xenobiology/crossbreeding/_clothing.dm
@@ -145,7 +145,7 @@ Slimecrossing Armor
var/hit_reflect_chance = 40
/obj/item/clothing/suit/armor/heavy/adamantine/IsReflect(def_zone)
- if(def_zone in list(BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) && prob(hit_reflect_chance))
+ if((def_zone in list(BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)) && prob(hit_reflect_chance))
return TRUE
else
return FALSE
diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm
index 43838df140bd..7b05e2f6b56f 100644
--- a/code/modules/spells/spell.dm
+++ b/code/modules/spells/spell.dm
@@ -141,7 +141,9 @@
/datum/action/cooldown/spell/PreActivate(atom/target)
if(SEND_SIGNAL(owner, COMSIG_MOB_ABILITY_STARTED, src) & COMPONENT_BLOCK_ABILITY_START)
return FALSE
- if(!is_valid_target(target))
+ if(target == owner)
+ target = get_caster_from_target(target)
+ if(isnull(target) || !is_valid_target(target))
return FALSE
return Activate(target)
@@ -213,10 +215,6 @@
to_chat(owner, span_warning("[src] can't be cast in this state!"))
return FALSE
- // Being put into a card form breaks a lot of spells, so we'll just forbid them in these states
- if(ispAI(owner) || (isAI(owner) && istype(owner.loc, /obj/item/aicard)))
- return FALSE
-
return TRUE
/**
@@ -229,6 +227,28 @@
/datum/action/cooldown/spell/proc/is_valid_target(atom/cast_on)
return TRUE
+/**
+ * Used to get the cast_on atom if a self cast spell is being cast.
+ *
+ * Allows for some atoms to be used as casting sources if a spell caster is located within.
+ */
+/datum/action/cooldown/spell/proc/get_caster_from_target(atom/target)
+ var/atom/cast_loc = target.loc
+ if(isnull(cast_loc))
+ return null // No magic in nullspace
+
+ if(isturf(cast_loc))
+ return target // They're just standing around, proceed as normal
+
+ if(HAS_TRAIT(cast_loc, TRAIT_CASTABLE_LOC))
+ /*if(HAS_TRAIT(cast_loc, TRAIT_SPELLS_TRANSFER_TO_LOC) && ismob(cast_loc.loc))
+ return cast_loc.loc
+ else*/ //monkestation temp removal
+ return cast_loc
+ // They're in an atom which allows casting, so redirect the caster to loc
+
+ return null
+
// The actual cast chain occurs here, in Activate().
// You should generally not be overriding or extending Activate() for spells.
// Defer to any of the cast chain procs instead.
diff --git a/code/modules/spells/spell_types/aoe_spell/_aoe_spell.dm b/code/modules/spells/spell_types/aoe_spell/_aoe_spell.dm
index 4d16ae379714..92442bd0e40a 100644
--- a/code/modules/spells/spell_types/aoe_spell/_aoe_spell.dm
+++ b/code/modules/spells/spell_types/aoe_spell/_aoe_spell.dm
@@ -12,6 +12,9 @@
/// The radius of the aoe.
var/aoe_radius = 7
+/datum/action/cooldown/spell/aoe/is_valid_target(atom/cast_on)
+ return isturf(cast_on.loc)
+
// At this point, cast_on == owner. Either works.
// Don't extend this for your spell! Look at cast_on_thing_in_aoe.
/datum/action/cooldown/spell/aoe/cast(atom/cast_on)
diff --git a/code/modules/spells/spell_types/aoe_spell/area_conversion.dm b/code/modules/spells/spell_types/aoe_spell/area_conversion.dm
index f75c39586852..a03b4c9ab21a 100644
--- a/code/modules/spells/spell_types/aoe_spell/area_conversion.dm
+++ b/code/modules/spells/spell_types/aoe_spell/area_conversion.dm
@@ -16,11 +16,7 @@
aoe_radius = 2
/datum/action/cooldown/spell/aoe/area_conversion/get_things_to_cast_on(atom/center)
- var/list/things = list()
- for(var/turf/nearby_turf in range(aoe_radius, center))
- things += nearby_turf
-
- return things
+ return RANGE_TURFS(aoe_radius, center)
/datum/action/cooldown/spell/aoe/area_conversion/cast_on_thing_in_aoe(turf/victim, atom/caster)
playsound(victim, 'sound/items/welder.ogg', 75, TRUE)
diff --git a/code/modules/spells/spell_types/aoe_spell/knock.dm b/code/modules/spells/spell_types/aoe_spell/knock.dm
index fd9e4503de8f..ede1462633b0 100644
--- a/code/modules/spells/spell_types/aoe_spell/knock.dm
+++ b/code/modules/spells/spell_types/aoe_spell/knock.dm
@@ -13,6 +13,23 @@
spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC
aoe_radius = 3
+/datum/action/cooldown/spell/aoe/knock/get_caster_from_target(atom/target)
+ if(istype(target.loc, /obj/structure/closet))
+ return target
+
+ return ..()
+
+/datum/action/cooldown/spell/aoe/knock/is_valid_target(atom/cast_on)
+ return ..() || istype(cast_on.loc, /obj/structure/closet)
+
+/datum/action/cooldown/spell/aoe/knock/cast(atom/cast_on)
+ if(istype(cast_on.loc, /obj/structure/closet))
+ var/obj/structure/closet/open_closet = cast_on.loc
+ open_closet.locked = FALSE
+ open_closet.open()
+
+ return ..()
+
/datum/action/cooldown/spell/aoe/knock/get_things_to_cast_on(atom/center)
return RANGE_TURFS(aoe_radius, center)
diff --git a/code/modules/spells/spell_types/aoe_spell/repulse.dm b/code/modules/spells/spell_types/aoe_spell/repulse.dm
index d0074a9f3866..259f20ee9ba0 100644
--- a/code/modules/spells/spell_types/aoe_spell/repulse.dm
+++ b/code/modules/spells/spell_types/aoe_spell/repulse.dm
@@ -6,6 +6,23 @@
/// The moveforce of the throw done by the repulsion.
var/repulse_force = MOVE_FORCE_EXTREMELY_STRONG
+/datum/action/cooldown/spell/aoe/repulse/get_caster_from_target(atom/target)
+ if(istype(target.loc, /obj/structure/closet))
+ return target
+
+ return ..()
+
+/datum/action/cooldown/spell/aoe/repulse/is_valid_target(atom/cast_on)
+ return ..() || istype(cast_on.loc, /obj/structure/closet)
+
+/datum/action/cooldown/spell/aoe/repulse/cast(atom/cast_on)
+ if(istype(cast_on.loc, /obj/structure/closet))
+ var/obj/structure/closet/open_closet = cast_on.loc
+ open_closet.open(force = TRUE)
+ open_closet.visible_message(span_warning("[open_closet] suddenly flies open!"))
+
+ return ..()
+
/datum/action/cooldown/spell/aoe/repulse/get_things_to_cast_on(atom/center)
var/list/things = list()
for(var/atom/movable/nearby_movable in view(aoe_radius, center))
@@ -44,7 +61,13 @@
to_chat(victim, span_userdanger("You're thrown back by [caster]!"))
// So stuff gets tossed around at the same time.
- victim.safe_throw_at(throwtarget, ((clamp((max_throw - (clamp(dist_from_caster - 2, 0, dist_from_caster))), 3, max_throw))), 1, caster, force = repulse_force)
+ victim.safe_throw_at(
+ target = throwtarget,
+ range = clamp((max_throw - (clamp(dist_from_caster - 2, 0, dist_from_caster))), 3, max_throw),
+ speed = 1,
+ thrower = ismob(caster) ? caster : null,
+ force = repulse_force,
+ )
/datum/action/cooldown/spell/aoe/repulse/wizard
name = "Repulse"
diff --git a/code/modules/spells/spell_types/conjure/_conjure.dm b/code/modules/spells/spell_types/conjure/_conjure.dm
index ffab08871118..3afe7c525575 100644
--- a/code/modules/spells/spell_types/conjure/_conjure.dm
+++ b/code/modules/spells/spell_types/conjure/_conjure.dm
@@ -16,6 +16,9 @@
/// If TRUE, no two summons can be spawned in the same turf.
var/summon_respects_prev_spawn_points = TRUE
+/datum/action/cooldown/spell/conjure/is_valid_target(atom/cast_on)
+ return isturf(cast_on.loc)
+
/datum/action/cooldown/spell/conjure/cast(atom/cast_on)
. = ..()
var/list/to_summon_in = list()
diff --git a/code/modules/spells/spell_types/conjure_item/_conjure_item.dm b/code/modules/spells/spell_types/conjure_item/_conjure_item.dm
index 45b4ff1a11bb..181fd118871e 100644
--- a/code/modules/spells/spell_types/conjure_item/_conjure_item.dm
+++ b/code/modules/spells/spell_types/conjure_item/_conjure_item.dm
@@ -8,6 +8,10 @@
var/delete_old = TRUE
/// List of weakrefs to items summoned
var/list/datum/weakref/item_refs
+ /// If TRUE, deletes the item if no mob picks it up on cast
+ var/delete_on_failure = TRUE
+ /// If TRUE, requires the caster be able to pick it up afterwards
+ var/requires_hands = FALSE
/datum/action/cooldown/spell/conjure_item/Destroy()
// If we delete_old, clean up all of our items on delete
@@ -20,29 +24,56 @@
return ..()
+/datum/action/cooldown/spell/conjure_item/can_cast_spell(feedback)
+ . = ..()
+ if(!.)
+ return FALSE
+
+ if(!requires_hands)
+ return TRUE
+
+ if(!isliving(owner))
+ return FALSE
+
+ var/mob/living/living_owner = owner
+ if(living_owner.usable_hands < 1)
+ if(feedback)
+ owner.balloon_alert(owner, "no free hands!")
+ return FALSE
+
+ return TRUE
+
/datum/action/cooldown/spell/conjure_item/is_valid_target(atom/cast_on)
- return iscarbon(cast_on)
+ if(!requires_hands)
+ return TRUE
+ if(!isliving(cast_on))
+ return FALSE
+ var/mob/living/living_cast_on = cast_on
+ return living_cast_on.usable_hands >= 1
-/datum/action/cooldown/spell/conjure_item/cast(mob/living/carbon/cast_on)
+/datum/action/cooldown/spell/conjure_item/cast(atom/cast_on)
if(delete_old && LAZYLEN(item_refs))
QDEL_LAZYLIST(item_refs)
- var/obj/item/existing_item = cast_on.get_active_held_item()
- if(existing_item)
- cast_on.dropItemToGround(existing_item)
+ var/mob/mob_caster = cast_on
+ if(istype(mob_caster))
+ var/obj/item/existing_item = mob_caster.get_active_held_item()
+ if(existing_item)
+ mob_caster.dropItemToGround(existing_item)
- var/obj/item/created = make_item()
+ var/obj/item/created = make_item(cast_on)
if(QDELETED(created))
CRASH("[type] tried to create an item, but failed. It's item type is [item_type].")
- cast_on.put_in_hands(created, del_on_fail = TRUE)
+ if(istype(mob_caster))
+ mob_caster.put_in_hands(created, del_on_fail = delete_on_failure)
post_created(cast_on, created) //monkestation edit: im just gonna call this here
return ..()
/// Instantiates the item we're conjuring and returns it.
-/// Item is made in nullspace and moved out in cast().
-/datum/action/cooldown/spell/conjure_item/proc/make_item()
- var/obj/item/made_item = new item_type()
+/// Item is made in at the caster's.
+/datum/action/cooldown/spell/conjure_item/proc/make_item(atom/caster)
+ var/obj/item/made_item = new item_type(caster.loc)
LAZYADD(item_refs, WEAKREF(made_item))
return made_item
diff --git a/code/modules/spells/spell_types/conjure_item/infinite_guns.dm b/code/modules/spells/spell_types/conjure_item/infinite_guns.dm
index 98921da4879d..35d83a9987f6 100644
--- a/code/modules/spells/spell_types/conjure_item/infinite_guns.dm
+++ b/code/modules/spells/spell_types/conjure_item/infinite_guns.dm
@@ -8,6 +8,7 @@
item_type = /obj/item/gun/ballistic/rifle
// Enchanted guns self delete / do wacky stuff, anyways
delete_old = FALSE
+ requires_hands = TRUE
/datum/action/cooldown/spell/conjure_item/infinite_guns/Remove(mob/living/remove_from)
var/obj/item/existing = remove_from.is_holding_item_of_type(item_type)
@@ -18,8 +19,8 @@
// Because enchanted guns self-delete and regenerate themselves,
// override make_item here and let's not bother with tracking their weakrefs.
-/datum/action/cooldown/spell/conjure_item/infinite_guns/make_item()
- return new item_type()
+/datum/action/cooldown/spell/conjure_item/infinite_guns/make_item(atom/caster)
+ return new item_type(caster.loc)
/datum/action/cooldown/spell/conjure_item/infinite_guns/gun
name = "Lesser Summon Guns"
diff --git a/code/modules/spells/spell_types/conjure_item/invisible_box.dm b/code/modules/spells/spell_types/conjure_item/invisible_box.dm
index af6d5586af25..42da02121d3c 100644
--- a/code/modules/spells/spell_types/conjure_item/invisible_box.dm
+++ b/code/modules/spells/spell_types/conjure_item/invisible_box.dm
@@ -30,7 +30,7 @@
. = ..()
invocation = span_notice("[cast_on] moves [cast_on.p_their()] hands in the shape of a cube, pressing a box out of the air.")
-/datum/action/cooldown/spell/conjure_item/invisible_box/make_item()
+/datum/action/cooldown/spell/conjure_item/invisible_box/make_item(atom/caster)
. = ..()
var/obj/item/made_box = .
made_box.alpha = 255
diff --git a/code/modules/spells/spell_types/conjure_item/lighting_packet.dm b/code/modules/spells/spell_types/conjure_item/lighting_packet.dm
index 2badfdd46dff..2df0c85f470e 100644
--- a/code/modules/spells/spell_types/conjure_item/lighting_packet.dm
+++ b/code/modules/spells/spell_types/conjure_item/lighting_packet.dm
@@ -9,6 +9,7 @@
spell_max_level = 1
item_type = /obj/item/spellpacket/lightningbolt
+ requires_hands = TRUE
/datum/action/cooldown/spell/conjure_item/spellpacket/cast(mob/living/carbon/cast_on)
. = ..()
diff --git a/code/modules/spells/spell_types/conjure_item/snowball.dm b/code/modules/spells/spell_types/conjure_item/snowball.dm
index ffd6d8d5e549..80b4aad9361c 100644
--- a/code/modules/spells/spell_types/conjure_item/snowball.dm
+++ b/code/modules/spells/spell_types/conjure_item/snowball.dm
@@ -8,3 +8,4 @@
antimagic_flags = NONE
cooldown_time = 1.5 SECONDS
item_type = /obj/item/toy/snowball
+ requires_hands = TRUE
diff --git a/code/modules/spells/spell_types/jaunt/_jaunt.dm b/code/modules/spells/spell_types/jaunt/_jaunt.dm
index e0463c02a10f..0378a5efee45 100644
--- a/code/modules/spells/spell_types/jaunt/_jaunt.dm
+++ b/code/modules/spells/spell_types/jaunt/_jaunt.dm
@@ -19,6 +19,12 @@
/// What dummy mob type do we put jaunters in on jaunt?
var/jaunt_type = /obj/effect/dummy/phased_mob
+/datum/action/cooldown/spell/jaunt/get_caster_from_target(atom/target)
+ if(istype(target.loc, jaunt_type))
+ return target
+
+ return ..()
+
/datum/action/cooldown/spell/jaunt/before_cast(atom/cast_on)
return ..() | SPELL_NO_FEEDBACK // Don't do the feedback until after we're jaunting
diff --git a/code/modules/spells/spell_types/jaunt/shadow_walk.dm b/code/modules/spells/spell_types/jaunt/shadow_walk.dm
index 29bb80633673..de03f8e15e02 100644
--- a/code/modules/spells/spell_types/jaunt/shadow_walk.dm
+++ b/code/modules/spells/spell_types/jaunt/shadow_walk.dm
@@ -9,6 +9,9 @@
spell_requirements = NONE
jaunt_type = /obj/effect/dummy/phased_mob/shadow
+ /// The max amount of lumens on a turf allowed before we can no longer enter jaunt with this
+ var/light_threshold = SHADOW_SPECIES_LIGHT_THRESHOLD
+
/datum/action/cooldown/spell/jaunt/shadow_walk/Grant(mob/grant_to)
. = ..()
RegisterSignal(grant_to, COMSIG_MOVABLE_MOVED, PROC_REF(update_status_on_signal))
@@ -17,6 +20,12 @@
. = ..()
UnregisterSignal(remove_from, COMSIG_MOVABLE_MOVED)
+/datum/action/cooldown/spell/jaunt/shadow_walk/enter_jaunt(mob/living/jaunter, turf/loc_override)
+ var/obj/effect/dummy/phased_mob/shadow/shadow = ..()
+ if(istype(shadow))
+ shadow.light_max = light_threshold
+ return shadow
+
/datum/action/cooldown/spell/jaunt/shadow_walk/can_cast_spell(feedback = TRUE)
. = ..()
if(!.)
@@ -24,7 +33,7 @@
if(is_jaunting(owner))
return TRUE
var/turf/cast_turf = get_turf(owner)
- if(cast_turf.get_lumcount() >= SHADOW_SPECIES_LIGHT_THRESHOLD)
+ if(cast_turf.get_lumcount() >= light_threshold)
if(feedback)
to_chat(owner, span_warning("It isn't dark enough here!"))
return FALSE
@@ -44,6 +53,8 @@
/obj/effect/dummy/phased_mob/shadow
name = "shadows"
+ /// Max amount of light permitted before being kicked out
+ var/light_max = SHADOW_SPECIES_LIGHT_THRESHOLD
/// The amount that shadow heals us per SSobj tick (times seconds_per_tick)
var/healing_rate = 1.5
/// When cooldown is active, you are prevented from moving into tiles that would eject you from your jaunt
@@ -109,11 +120,9 @@
* * location_to_check - The location to have its light level checked.
*/
-/obj/effect/dummy/phased_mob/shadow/proc/check_light_level(location_to_check)
- var/turf/T = get_turf(location_to_check)
- var/light_amount = T.get_lumcount()
- if(light_amount > 0.2) // jaunt ends
- return TRUE
+/obj/effect/dummy/phased_mob/shadow/proc/check_light_level(atom/location_to_check)
+ var/turf/light_turf = get_turf(location_to_check)
+ return light_turf.get_lumcount() > light_max // jaunt ends on TRUE
/**
* Checks if the user should recieve a warning that they're moving into light.
diff --git a/code/modules/spells/spell_types/pointed/_pointed.dm b/code/modules/spells/spell_types/pointed/_pointed.dm
index 976e18e2731e..c220f5573b35 100644
--- a/code/modules/spells/spell_types/pointed/_pointed.dm
+++ b/code/modules/spells/spell_types/pointed/_pointed.dm
@@ -64,7 +64,7 @@
build_all_button_icons()
return TRUE
-/datum/action/cooldown/spell/pointed/InterceptClickOn(mob/living/caller, params, atom/target)
+/datum/action/cooldown/spell/pointed/InterceptClickOn(mob/living/user, params, atom/target)
var/atom/aim_assist_target
if(aim_assist && isturf(target))
@@ -74,7 +74,7 @@
// If we didn't find a human, we settle for any living at all
aim_assist_target = locate(/mob/living) in target
- return ..(caller, params, aim_assist_target || target)
+ return ..(user, params, aim_assist_target || target)
/datum/action/cooldown/spell/pointed/is_valid_target(atom/cast_on)
if(cast_on == owner)
@@ -133,10 +133,11 @@
// cast_on is a turf, or atom target, that we clicked on to fire at.
/datum/action/cooldown/spell/pointed/projectile/cast(atom/cast_on)
. = ..()
- if(!isturf(owner.loc))
+ var/atom/caster = get_caster_from_target(owner)
+ if(!isturf(caster.loc))
return FALSE
- var/turf/caster_turf = get_turf(owner)
+ var/turf/caster_turf = caster.loc
// Get the tile infront of the caster, based on their direction
var/turf/caster_front_turf = get_step(owner, owner.dir)
diff --git a/code/modules/spells/spell_types/pointed/spell_cards.dm b/code/modules/spells/spell_types/pointed/spell_cards.dm
index 47700d2c09a8..d2f12092ff59 100644
--- a/code/modules/spells/spell_types/pointed/spell_cards.dm
+++ b/code/modules/spells/spell_types/pointed/spell_cards.dm
@@ -7,7 +7,8 @@
school = SCHOOL_EVOCATION
cooldown_time = 5 SECONDS
- cooldown_reduction_per_rank = 1 SECONDS
+ cooldown_reduction_per_rank = 2 SECONDS
+ spell_max_level = 3
invocation = "Sigi'lu M'Fan 'Tasia!"
invocation_type = INVOCATION_SHOUT
@@ -21,7 +22,7 @@
/// A weakref to the mob we're currently targeting with the lockon component.
var/datum/weakref/current_target_weakref
/// The turn rate of the spell cards in flight. (They track onto locked on targets)
- var/projectile_turnrate = 10
+ var/projectile_turnrate = 15 //monkestation edit: from 10 to 15
/// The homing spread of the spell cards in flight.
var/projectile_pixel_homing_spread = 32
/// The initial spread of the spell cards when fired.
@@ -47,6 +48,7 @@
target_typecache = GLOB.typecache_living, \
lock_amount = 1, \
on_lock = CALLBACK(src, PROC_REF(on_lockon_component)), \
+ catcher_default_click = FALSE, /*monkestation edit*/ \
)
/datum/action/cooldown/spell/pointed/projectile/spell_cards/proc/on_lockon_component(list/locked_weakrefs)
diff --git a/code/modules/spells/spell_types/pointed/swap.dm b/code/modules/spells/spell_types/pointed/swap.dm
index 904c2d36c6ee..8c1b152127f9 100644
--- a/code/modules/spells/spell_types/pointed/swap.dm
+++ b/code/modules/spells/spell_types/pointed/swap.dm
@@ -8,11 +8,11 @@
active_overlay_icon_state = "bg_spell_border_active_blue"
school = SCHOOL_TRANSLOCATION
- cooldown_time = 30 SECONDS
- cooldown_reduction_per_rank = 6 SECONDS
+ cooldown_time = 25 SECONDS
+ cooldown_reduction_per_rank = 10 SECONDS
+ spell_max_level = 3
cast_range = 9
- invocation = "FRO' BRT'TRO, DA!"
- invocation_type = INVOCATION_SHOUT
+ invocation_type = INVOCATION_NONE
spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC|SPELL_REQUIRES_STATION
active_msg = "You prepare to swap locations with a target..."
@@ -35,8 +35,8 @@
return FALSE
return TRUE
-/datum/action/cooldown/spell/pointed/swap/InterceptClickOn(mob/living/caller, params, atom/target)
- if((caller.istate & ISTATE_SECONDARY))
+/datum/action/cooldown/spell/pointed/swap/InterceptClickOn(mob/living/user, params, atom/target)
+ if((user.istate & ISTATE_SECONDARY))
if(!IsAvailable(feedback = TRUE))
return FALSE
if(!target)
diff --git a/code/modules/spells/spell_types/pointed/tie_shoes.dm b/code/modules/spells/spell_types/pointed/tie_shoes.dm
new file mode 100644
index 000000000000..5783717d0e72
--- /dev/null
+++ b/code/modules/spells/spell_types/pointed/tie_shoes.dm
@@ -0,0 +1,137 @@
+
+/datum/action/cooldown/spell/pointed/untie_shoes
+ name = "Untie Shoes"
+ desc = "This unassuming spell unties and then knots the target's shoes."
+ ranged_mousepointer = 'icons/effects/mouse_pointers/lace.dmi'
+ button_icon_state = "lace"
+
+ school = SCHOOL_CONJURATION
+ cooldown_time = 3 SECONDS
+ cooldown_reduction_per_rank = 0.2 SECONDS
+
+ spell_max_level = 4
+ invocation = "Acetato!"
+ invocation_type = INVOCATION_SHOUT
+ spell_requirements = NONE
+ antimagic_flags = MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY
+
+ cast_range = INFINITY
+ active_msg = "You prepare to tie your target's shoes!"
+
+ /// Ignores inability to tie laces, such as jackboots, magboots, or sandals.
+ var/bypass_tie_status = FALSE
+ /// Summons shoes to untie if the target has none.
+ var/summons_shoes = FALSE
+
+/datum/action/cooldown/spell/pointed/untie_shoes/New(Target)
+ . = ..()
+ // tgs first spell with multiple invocations!!!!!!
+ invocation = pick("Acetato!", "Agaletto!")
+
+/datum/action/cooldown/spell/pointed/untie_shoes/level_spell(bypass_cap)
+ . = ..()
+ if(spell_level == 2)
+ bypass_tie_status = TRUE
+ to_chat(owner, span_notice("You will now summon laces on laceless shoes, such as jackboots."))
+
+ if(spell_level == 3)
+ summons_shoes = TRUE
+ to_chat(owner, span_notice("You will now summon shoes if your target has none."))
+
+ if(spell_level == 4)
+ invocation_type = INVOCATION_NONE
+ to_chat(owner, span_boldnotice("Your invocations are now silent!"))
+
+/datum/action/cooldown/spell/pointed/untie_shoes/is_valid_target(atom/cast_on)
+ return isliving(cast_on)
+
+// We need to override this, as trying to change next_use_time in cast() will just result in it being overridden.
+/datum/action/cooldown/spell/touch/before_cast(atom/cast_on)
+ return ..() | SPELL_NO_IMMEDIATE_COOLDOWN
+
+/datum/action/cooldown/spell/pointed/untie_shoes/cast(mob/living/carbon/cast_on)
+ . = ..()
+ if(cast_on.can_block_magic(antimagic_flags))
+ to_chat(owner, span_warning("The spell had no effect!"))
+ return FALSE
+
+ if(isanimal_or_basicmob(cast_on))
+ cast_on.add_movespeed_modifier(/datum/movespeed_modifier/magic_ties)
+ addtimer(CALLBACK(cast_on, TYPE_PROC_REF(/mob/living, remove_movespeed_modifier), /datum/movespeed_modifier/magic_ties), 3 SECONDS * spell_level, TIMER_UNIQUE|TIMER_OVERRIDE)
+ to_chat(owner, span_warning("You tie [cast_on] with weak, magic laces!"))
+ if(invocation_type != INVOCATION_NONE) // extra feedback since it's weird for them
+ cast_on.balloon_alert_to_viewers("magically tied!")
+ else
+ cast_on.balloon_alert(owner, "magically tied!")
+ playsound(cast_on, 'sound/magic/summonitems_generic.ogg', 50, TRUE)
+ return TRUE
+
+ var/shoe_to_cast = /obj/item/clothing/shoes/sneakers/random
+
+ if(HAS_TRAIT(owner, TRAIT_CHUUNIBYOU))
+ shoe_to_cast = /obj/item/clothing/shoes/sneakers/marisa
+ if(HAS_TRAIT(owner, TRAIT_SPLATTERCASTER))
+ shoe_to_cast = /obj/item/clothing/shoes/laceup
+
+ var/obj/item/clothing/shoes/shoes_to_tie = cast_on.shoes
+
+ if(isnull(shoes_to_tie))
+ if(!summons_shoes)
+ to_chat(owner, span_warning("[cast_on] isn't wearing any shoes!"))
+ return FALSE
+
+ shoes_to_tie = new shoe_to_cast(cast_on)
+ if(!cast_on.equip_to_slot_or_del(shoes_to_tie, ITEM_SLOT_FEET))
+ to_chat(owner, span_warning("Couldn't equip shoes on [cast_on]!"))
+ return FALSE
+
+ if(invocation_type != INVOCATION_NONE)
+ playsound(cast_on, 'sound/magic/summonitems_generic.ogg', 50, TRUE)
+
+ switch(shoes_to_tie.tied)
+ if(SHOES_TIED)
+ if(!shoes_to_tie.can_be_tied)
+ if(bypass_tie_status)
+ to_chat(owner, span_warning("You magically grant laces to [cast_on]'s shoes!"))
+ cast_on.balloon_alert(owner, "laced!")
+ shoes_to_tie.can_be_tied = TRUE
+ if(invocation_type != INVOCATION_NONE)
+ playsound(cast_on, 'sound/magic/summonitems_generic.ogg', 50, TRUE)
+ return TRUE
+ else
+ to_chat(owner, span_warning("[cast_on] is wearing laceless shoes!"))
+ cast_on.balloon_alert(owner, "laceless!")
+ return FALSE
+
+ to_chat(owner, span_warning("You untie [cast_on]'s shoes!"))
+ cast_on.balloon_alert(owner, "untied!")
+ shoes_to_tie.adjust_laces(SHOES_UNTIED, force_lacing = TRUE)
+ if(SHOES_UNTIED)
+ to_chat(owner, span_warning("You knot [cast_on]'s laces!"))
+ cast_on.balloon_alert(owner, "knotted!")
+ shoes_to_tie.adjust_laces(SHOES_KNOTTED, force_lacing = TRUE)
+ if(SHOES_KNOTTED)
+ to_chat(owner, span_warning("[cast_on]'s laces are already knotted!"))
+ return FALSE
+
+// We need to override this, as trying to change next_use_time in cast() will just result in it being overridden.
+/datum/action/cooldown/spell/pointed/untie_shoes/after_cast(atom/cast_on)
+ . = ..()
+ var/extra_time = 0 SECONDS
+ if((cast_on.z != owner.z) || get_dist(cast_on, owner) > 7)
+ extra_time += cooldown_time * 10 // :)
+
+ StartCooldown(cooldown_time + extra_time)
+
+/datum/action/cooldown/spell/pointed/untie_shoes/get_spell_title()
+ switch(spell_level)
+ if(2)
+ return "Laceless "
+ if(3)
+ return "Prankster's "
+ if(4)
+ return "Sneakerly "
+ if(5)
+ return "Clown's Own "
+
+ return ""
diff --git a/code/modules/spells/spell_types/self/basic_heal.dm b/code/modules/spells/spell_types/self/basic_heal.dm
index a4acba2d8845..135b80942062 100644
--- a/code/modules/spells/spell_types/self/basic_heal.dm
+++ b/code/modules/spells/spell_types/self/basic_heal.dm
@@ -17,6 +17,9 @@
/// Amount of burn to heal to the spell caster on cast
var/burn_to_heal = 10
+/datum/action/cooldown/spell/basic_heal/is_valid_target(atom/cast_on)
+ return isliving(cast_on)
+
/datum/action/cooldown/spell/basic_heal/cast(mob/living/cast_on)
. = ..()
cast_on.visible_message(
diff --git a/code/modules/spells/spell_types/self/mutate.dm b/code/modules/spells/spell_types/self/mutate.dm
index 9f4bf3db6c8e..59f8f6ddc37f 100644
--- a/code/modules/spells/spell_types/self/mutate.dm
+++ b/code/modules/spells/spell_types/self/mutate.dm
@@ -38,9 +38,10 @@
/datum/action/cooldown/spell/apply_mutations/mutate
name = "Mutate"
- desc = "This spell causes you to turn into a gigantic hulk and gain laser vision for a short while. Unlike the lesser nonmagical version, it works on non-humans and mantains hand dexterity as well!"
- cooldown_time = 60 SECONDS //monkestation edit: from 40 to 60 seconds
- cooldown_reduction_per_rank = 5 SECONDS //monkestation edit: from 2.5 to 5 seconds
+ desc = "This spell causes you to turn into a hulk and gain laser vision for a short while."
+ cooldown_time = 40 SECONDS
+ cooldown_reduction_per_rank = 5 SECONDS
+ spell_max_level = 3
invocation = "BIRUZ BENNAR"
invocation_type = INVOCATION_SHOUT
@@ -56,7 +57,7 @@
cast_on.add_atom_colour("#00FF00", TEMPORARY_COLOUR_PRIORITY)
/datum/action/cooldown/spell/apply_mutations/mutate/remove_mutations(mob/living/carbon/human/cast_on)
- if(QDELETED(cast_on) || !is_valid_target(cast_on))
+ if(QDELETED(cast_on) || !is_valid_target(cast_on)) // Not 100% sure if this check is still needed, leaving it just in case
return
-
+ ..()
cast_on.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY)
diff --git a/code/modules/spells/spell_types/self/personality_commune.dm b/code/modules/spells/spell_types/self/personality_commune.dm
deleted file mode 100644
index cd10c2b7736a..000000000000
--- a/code/modules/spells/spell_types/self/personality_commune.dm
+++ /dev/null
@@ -1,56 +0,0 @@
-// This can probably be changed to use mind linker at some point
-/datum/action/cooldown/spell/personality_commune
- name = "Personality Commune"
- desc = "Sends thoughts to your alternate consciousness."
- button_icon_state = "telepathy"
- cooldown_time = 0 SECONDS
- spell_requirements = NONE
-
- /// Fluff text shown when a message is sent to the pair
- var/fluff_text = span_boldnotice("You hear an echoing voice in the back of your head...")
- /// The message to send to the corresponding person on cast
- var/to_send
-
-/datum/action/cooldown/spell/personality_commune/New(Target)
- . = ..()
- if(!istype(target, /datum/brain_trauma/severe/split_personality))
- stack_trace("[type] was created on a target that isn't a /datum/brain_trauma/severe/split_personality, this doesn't work.")
- qdel(src)
-
-/datum/action/cooldown/spell/personality_commune/is_valid_target(atom/cast_on)
- return isliving(cast_on)
-
-/datum/action/cooldown/spell/personality_commune/before_cast(atom/cast_on)
- . = ..()
- if(. & SPELL_CANCEL_CAST)
- return
-
- var/datum/brain_trauma/severe/split_personality/trauma = target
- if(!istype(trauma)) // hypothetically impossible but you never know
- return . | SPELL_CANCEL_CAST
-
- to_send = tgui_input_text(cast_on, "What would you like to tell your other self?", "Commune")
- if(QDELETED(src) || QDELETED(trauma) || QDELETED(cast_on) || QDELETED(trauma.owner) || !can_cast_spell())
- return . | SPELL_CANCEL_CAST
- if(!to_send)
- reset_cooldown()
- return . | SPELL_CANCEL_CAST
-
-// Pillaged and adapted from telepathy code
-/datum/action/cooldown/spell/personality_commune/cast(mob/living/cast_on)
- . = ..()
- var/datum/brain_trauma/severe/split_personality/trauma = target
-
- var/user_message = span_boldnotice("You concentrate and send thoughts to your other self:")
- var/user_message_body = span_notice("[to_send]")
-
- to_chat(cast_on, "[user_message] [user_message_body]")
-
- trauma.owner.balloon_alert(trauma.owner, "you hear a voice")
- to_chat(trauma.owner, "[fluff_text] [user_message_body]")
-
- log_directed_talk(cast_on, trauma.owner, to_send, LOG_SAY, "[name]")
- for(var/dead_mob in GLOB.dead_mob_list)
- if(!isobserver(dead_mob))
- continue
- to_chat(dead_mob, "[FOLLOW_LINK(dead_mob, cast_on)] [span_boldnotice("[cast_on] [name]:")] [span_notice("\"[to_send]\" to")] [span_name("[trauma]")]")
diff --git a/code/modules/spells/spell_types/self/soultap.dm b/code/modules/spells/spell_types/self/soultap.dm
index 0c114575c4be..cb611de0a21a 100644
--- a/code/modules/spells/spell_types/self/soultap.dm
+++ b/code/modules/spells/spell_types/self/soultap.dm
@@ -45,6 +45,8 @@
cast_on.health = min(cast_on.health, cast_on.maxHealth)
for(var/datum/action/cooldown/spell/spell in cast_on.actions)
+ if(istype(spell, /datum/action/cooldown/spell/pointed/mind_transfer)) //monkestation edit
+ continue //monkestation edit
spell.reset_spell_cooldown()
// If the tap took all of our life, we die and lose our soul!
diff --git a/code/modules/spells/spell_types/shapeshift/shapechange.dm b/code/modules/spells/spell_types/shapeshift/shapechange.dm
index d7ff71e0425d..dd2597d00970 100644
--- a/code/modules/spells/spell_types/shapeshift/shapechange.dm
+++ b/code/modules/spells/spell_types/shapeshift/shapechange.dm
@@ -4,7 +4,8 @@
Once you've made your choice, it cannot be changed."
cooldown_time = 20 SECONDS
- cooldown_reduction_per_rank = 3.75 SECONDS
+ cooldown_reduction_per_rank = 8 SECONDS
+ spell_max_level = 3
invocation = "RAC'WA NO!"
invocation_type = INVOCATION_SHOUT
diff --git a/code/modules/spells/spell_types/teleport/teleport.dm b/code/modules/spells/spell_types/teleport/teleport.dm
index 1dfcf6f2608e..d48615720428 100644
--- a/code/modules/spells/spell_types/teleport/teleport.dm
+++ b/code/modules/spells/spell_types/teleport/teleport.dm
@@ -7,7 +7,8 @@
school = SCHOOL_TRANSLOCATION
cooldown_time = 1 MINUTES
- cooldown_reduction_per_rank = 10 SECONDS
+ cooldown_reduction_per_rank = 20 SECONDS
+ spell_max_level = 3
invocation = "SCYAR NILA"
invocation_type = INVOCATION_SHOUT
diff --git a/code/modules/surgery/organs/liver.dm b/code/modules/surgery/organs/liver.dm
old mode 100644
new mode 100755
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index fa2bc18ae8c8..0d373b931c25 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -210,6 +210,7 @@
#include "species_unique_id.dm"
#include "species_whitelists.dm"
#include "spell_invocations.dm"
+#include "spell_jaunt.dm"
#include "spell_mindswap.dm"
#include "spell_names.dm"
#include "spell_shapeshift.dm"
diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_teratoma.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_teratoma.png
new file mode 100644
index 000000000000..ffb7cd7284fd
Binary files /dev/null and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_teratoma.png differ
diff --git a/code/modules/unit_tests/spell_jaunt.dm b/code/modules/unit_tests/spell_jaunt.dm
new file mode 100644
index 000000000000..41446b71a591
--- /dev/null
+++ b/code/modules/unit_tests/spell_jaunt.dm
@@ -0,0 +1,21 @@
+/// Tests Shadow Walk can be entered and exited
+/datum/unit_test/shadow_jaunt
+
+/datum/unit_test/shadow_jaunt/Run()
+ var/mob/living/carbon/human/jaunter = allocate(/mob/living/carbon/human/consistent)
+ var/datum/action/cooldown/spell/jaunt/shadow_walk/walk = allocate(/datum/action/cooldown/spell/jaunt/shadow_walk, jaunter)
+ walk.Grant(jaunter)
+
+ var/turf/jaunt_turf = jaunter.loc
+ TEST_ASSERT(istype(jaunt_turf), "Jaunter was not allocated to a turf, instead to [jaunt_turf || "nullspace"].")
+ TEST_ASSERT(walk.IsAvailable(), "Unit test room is not suitable to test [walk].")
+
+ walk.Trigger()
+
+ TEST_ASSERT_NOTEQUAL(jaunter.loc, jaunt_turf, "Jaunter's loc did not change on casting [walk].")
+ TEST_ASSERT(istype(jaunter.loc, walk.jaunt_type), "Jaunter failed to enter jaunt on casting [walk].")
+
+ walk.next_use_time = -1
+ walk.Trigger()
+
+ TEST_ASSERT_EQUAL(jaunter.loc, jaunt_turf, "Jaunter failed to exit jaunt on exiting [walk].")
diff --git a/code/modules/uplink/uplink_items/job.dm b/code/modules/uplink/uplink_items/job.dm
index fad2f28193c3..27e838f6cc79 100644
--- a/code/modules/uplink/uplink_items/job.dm
+++ b/code/modules/uplink/uplink_items/job.dm
@@ -207,7 +207,7 @@
desc = "An implant that grants you a recharging laser gun inside your arm. Weak to EMPs. Comes with a syndicate autosurgeon for immediate self-application."
progression_minimum = 20 MINUTES
cost = 10
- item = /obj/item/autosurgeon/syndicate/laser_arm
+ item = /obj/item/storage/box/syndie_kit/laser_arm
restricted_roles = list(JOB_ROBOTICIST, JOB_RESEARCH_DIRECTOR)
surplus = 20
diff --git a/code/modules/uplink/uplink_items/nukeops.dm b/code/modules/uplink/uplink_items/nukeops.dm
index b4dc2d8dc4de..777ebca1b0b4 100644
--- a/code/modules/uplink/uplink_items/nukeops.dm
+++ b/code/modules/uplink/uplink_items/nukeops.dm
@@ -709,7 +709,7 @@
/datum/uplink_item/implants/antistun
name = "CNS Rebooter Implant"
desc = "This implant will help you get back up on your feet faster after being stunned. Comes with an autosurgeon."
- item = /obj/item/autosurgeon/syndicate/anti_stun
+ item = /obj/item/storage/box/syndie_kit/anti_stun
cost = 12
surplus = 40 //monkestation edit: from 0 to 40
purchasable_from = UPLINK_NUKE_OPS
@@ -735,7 +735,7 @@
/datum/uplink_item/implants/reviver
name = "Reviver Implant"
desc = "This implant will attempt to revive and heal you if you lose consciousness. Comes with an autosurgeon."
- item = /obj/item/autosurgeon/syndicate/reviver
+ item = /obj/item/storage/box/syndie_kit/reviver
cost = 8
surplus = 30 //monkestation edit: from 0 to 30
purchasable_from = UPLINK_NUKE_OPS
diff --git a/code/modules/vehicles/_vehicle.dm b/code/modules/vehicles/_vehicle.dm
index 03554ac081d4..ca666292a3f5 100644
--- a/code/modules/vehicles/_vehicle.dm
+++ b/code/modules/vehicles/_vehicle.dm
@@ -51,6 +51,7 @@
autogrant_actions_controller = list()
occupant_actions = list()
generate_actions()
+ ADD_TRAIT(src, TRAIT_CASTABLE_LOC, INNATE_TRAIT)
/obj/vehicle/Destroy(force)
QDEL_NULL(trailer)
diff --git a/code/modules/vending/security.dm b/code/modules/vending/security.dm
index 477295f390c4..baf524c01ffc 100644
--- a/code/modules/vending/security.dm
+++ b/code/modules/vending/security.dm
@@ -35,7 +35,7 @@
/obj/item/storage/belt/holster/energy = 4,
/obj/item/clothing/head/helmet/civilprotection_helmet = 1, //monkestation edit
/obj/item/clothing/suit/armor/civilprotection_vest = 1, //monkestation edit
- /obj/item/clothing/head/guardmanhelmet = 1, //monkestation edit: Guardman
+ /obj/item/clothing/head/helmet/guardmanhelmet = 1, //monkestation edit: Guardman
/obj/item/clothing/under/guardmanuniform = 1, //monkestation edit: Guardman
/obj/item/clothing/suit/armor/guardmanvest = 1, //monkestation edit: Guardman
/obj/item/citationinator = 3 // monkestation edit: security assistants
diff --git a/code/modules/zombie/items.dm b/code/modules/zombie/items.dm
index 376bef8fbfce..464bf9f3740c 100644
--- a/code/modules/zombie/items.dm
+++ b/code/modules/zombie/items.dm
@@ -35,7 +35,7 @@
return
// spaceacillin has a 75% chance to block infection
- if(istype(target) && target.reagents.has_reagent(/datum/reagent/medicine/antipathogenic/spaceacillin) && prob(75))
+ if(HAS_TRAIT(target, TRAIT_VIRUS_RESISTANCE) && prob(75))
return
var/obj/item/bodypart/actual_limb = target.get_bodypart(def_zone)
diff --git a/config/game_options.txt b/config/game_options.txt
index 8e2d62c329b5..66448be8ea59 100644
--- a/config/game_options.txt
+++ b/config/game_options.txt
@@ -548,6 +548,14 @@ METACURRENCY_NAME Metacoin
TWITCH_KEY mrhouse
+## How long (in minutes) after roundstart should the server start an automatic crew transfer vote?
+## Defaults to 90 minutes. Set to 0 to completely disable automatic crew transfer votes.
+TRANSFER_VOTE_TIME 90
+
+## If players vote against a crew transfer, how long (in minutes) until another automatic transfer vote will be initiated?
+## Defaults to 30 minutes. Set to 0 to completely disable subsequent crew transfer votes.
+SUBSEQUENT_TRANSFER_VOTE_TIME 30
+
## Gamemode configurations
## Multipliers for points gained over time for event tracks.
diff --git a/config/maps.txt b/config/maps.txt
index 79f8b38ad18e..8ca6982fe8a7 100644
--- a/config/maps.txt
+++ b/config/maps.txt
@@ -34,16 +34,21 @@ map icebox
votable
endmap
-map tramstation
- minplayers 35
+map theseus
+ minplayers 5
votable
endmap
-map northstar
- minplayers 50
+map tramstation
+ minplayers 35
votable
endmap
+#map northstar
+# minplayers 50
+# votable
+#endmap
+
map runtimestation
endmap
diff --git a/config/monkestation/antag-tokens.toml b/config/monkestation/antag-tokens.toml
index a6b25fbfd528..267c6c32c904 100644
--- a/config/monkestation/antag-tokens.toml
+++ b/config/monkestation/antag-tokens.toml
@@ -1,3 +1,3 @@
-low = ["traitor", "florida_man", "paradox_clone"]
-medium = ["heretic", "bloodsucker"]
+low = ["traitor", "florida_man", "paradox_clone", "cortical_borer"]
+medium = ["heretic", "bloodsucker", "cortical_borer/hivemind"]
high = ["cult", "rev/head", "wizard", "clock_cultist", "ninja"]
diff --git a/html/changelogs/archive/2024-09.yml b/html/changelogs/archive/2024-09.yml
index f8843d3ef95b..c3b69b6f4d85 100644
--- a/html/changelogs/archive/2024-09.yml
+++ b/html/changelogs/archive/2024-09.yml
@@ -114,9 +114,7 @@
- qol: Medical cyborg can use their health analyzer when in the dark or blind.
- bugfix: Fixes fugitive hunter victory end round text.
- bugfix: Restored old HOS trenchcoat sprite. Winter trenchcoat sprite also fixed.
- - bugfix: 'Plush refill canister is actually in maint loot now.
-
- :cl:'
+ - bugfix: Plush refill canister is actually in maint loot now.
RikuTheKiller:
- rscadd: You can now permanently remove NIFs through a surgery on the head called
"NIF debonding", but it requires OOC consent.
@@ -287,3 +285,367 @@
- rscdel: Old Asteroid Generation
- qol: Astroid Magnet now gives a general compass direction along with AZIMUTH
- code_imp: Asteroid Magnet now pulls from DMM files instead of being a generator.
+2024-09-13:
+ Absolucy:
+ - bugfix: Fixed Gary being invisible when dead.
+ - rscdel: Remove slurry and brine from maintpill pool.
+ - balance: Reworked the Brand Intelligence event.
+ - balance: Killing the "origin" vendor will put a stop to the entire event, including
+ already animated vendors.
+ - balance: Brand Intelligence only infects vendors within a randomized range of
+ the origin vendor.
+ - balance: Brand Intelligence doesn't last forever, ending automatically after around
+ ~15 minutes or so.
+ - balance: Brand Intelligence won't affect prison wing vendors, as the prisoners
+ likely have no means of deconstructing the vendors.
+ - balance: Tweaked the weights of target departments for Brand Intelligence.
+ - qol: Ghosts can now examine vendors to see if they're affected by Brand Intelligence
+ or not.
+ - balance: Examining vendors closely will give hints as to their rebellion status.
+ Silicons and anyone with a diagnostic HUD will see even more detail.
+ - refactor: Heavily refactored and cleaned up code relating to Brand Intelligence.
+ - balance: Sol has been reworked for Bloodsuckers. Instead of burning during Sol,
+ they'll instead get a status effect that completely disables their passive healing,
+ slower movement and action speed, they will take 50% more damage (of any type),
+ and some powers will either have a much higher blood cost, or be completely
+ unavailable.
+ - bugfix: Fixed various jank regarding oozeling bloodsuckers.
+ - rscadd: Added various botany circuit boards to Tech Storage.
+ - bugfix: Fix runtime error spam relating to AI camera lighting.
+ - bugfix: The Opposing Force panel now properly sets the max length of its text
+ inputs, rather than silently cutting the input off afterwards.
+ - qol: Changed the maximum opfor objective title length from 40 to 64.
+ - qol: Changed the maximum opfor admin chat length to 1024 - the usual maximum message
+ length.
+ Absolucy, carlarctg:
+ - qol: Ash Ascension spells have high transparency
+ Absolucy, vinylspiders, SmArtKar:
+ - bugfix: (vinylspiders) Fixed a hard delete with soapboxes.
+ - bugfix: (SmArtKar) Fixed soapbox component sometimes runtiming roundstart.
+ Gw0sty:
+ - rscadd: Adds R.D.S.P.L.X. skill chip to the science skill chip box.
+ KnigTheThrasher:
+ - rscadd: Kilo docking bay has been moved somewhere else
+ - rscadd: Improved Kilo xenobiology piping and disposals
+ MomoBerri:
+ - bugfix: sandstone stools now deconstruct properly into sandstone bricks.
+ - bugfix: adds a normal floor tile underneath a certain airlock in a certain lavaland
+ ruin.
+ - bugfix: fixes contractor guide formatting.
+ PotatoTomahto:
+ - rscadd: Adds "Cryptozoologist" and "Paranormal Investigator" alt titles.
+ RandomDudeFromTheRim:
+ - bugfix: Split Personas can speak with owner and vice versa now once again
+ RikuTheKiller:
+ - bugfix: Varients work now.
+ Shoddd:
+ - bugfix: Makes robo tot laser arm usable again
+ Wisemonster:
+ - balance: The Bogseo now uses .27-54 Cesarzowa rounds, instead of .585 trappiste
+ rounds
+ - balance: The Bogseo smg gunset for the Blueshield now comes with 1 box of .27-54
+ Cesarzowa piercing bullets, and 2 boxes of the rubber variant.
+ - balance: The trappiste ammo boxes now contain 32 rounds
+2024-09-14:
+ Absolucy:
+ - balance: The confusion symptom's (Topographical Cretinism) confusion no longer
+ infinitely stacks - it will no longer COMPLETELY randomize your movement.
+ - balance: The confusion symptom can now only activate every 1 to 5 seconds.
+ Addust:
+ - rscadd: We've been picking up radio traffic from a nearby asteroid in the area.
+ It appears the Syndicate has commissioned a new listening station.
+ - rscadd: We've also seen reports of a second agent operating from the outpost.
+ Gw0sty:
+ - rscadd: 'Accelerator Cybernetic for Security Assistants and S.M.A.R.T. Ammo Counters
+ for Blueshields during the Cybernetic Revolution Station Trait.
+
+ :cl:'
+ - qol: 'Brought IPC surgery steps more in line with normal surgery steps
+
+ :cl:'
+ - bugfix: 'Made Peacekeeper Cyborg transformation message more broad.
+
+ :cl:'
+ MomoBerri:
+ - bugfix: adds a bluespace sender and a disposals dispenser to boxstation atmos.
+ PotatoTomahto:
+ - rscadd: Ghosts can now see mood changes.
+ RandomDudeFromTheRim:
+ - bugfix: made gorilla transformation when affected by excess amounts of genetic
+ damage work
+ Shoddd:
+ - bugfix: fixed secoffs not getting paco with non standard suit
+ - bugfix: Security's Guardman Helmet now armored
+ - bugfix: Makes Aphid hive exit indestructible
+ SirNightKnight:
+ - qol: Removed the FOV restriction from bio hoods.
+ The-Black-Screen:
+ - rscadd: added a new random engine map for boxstation, added more fire extinguishers,
+ miscellaneous things.
+ - rscdel: removed a pipe gun, removed the burned clown skeleton, miscellaneous things.
+ - qol: added a direct entrance from the exam room to cryogenics, added a northern
+ entrance to all random bar maps.
+ - bugfix: tried to fix some lights being weird.
+ - code_imp: added the new engine to boxstation's engines file.
+2024-09-15:
+ Absolucy:
+ - bugfix: Fixed various runtimes and hard deletes.
+ ? Comxy, Fikou, lessthnthree, Rhials, Ghommie, MrMelbert, IndieanaJones, san7890,
+ Holoo-1, PKPenguin321, larentoun, Das15, carlarctg, Jacquerel, JohnFulpWillard
+ : - rscadd: Wizards can now purchase mirror shields
+ - rscadd: The Fireball spell in wizards spellbooks has been replaced with Fire
+ Ball
+ - balance: The magical chem sprayed now comes with a bio suit and wizard combat
+ gloves
+ - balance: The wizard MODsuit shield has been reworked and now costs 1
+ - bugfix: spell cards work again
+ PotatoTomahto:
+ - balance: Wrenching and unwrenching the liquid pump takes 2 seconds instead of
+ 4.
+ - bugfix: Mood hud should no longer constantly appear when retoggling ghost hud.
+ RikuTheKiller:
+ - rscadd: Borers are now available in the antag token menu. (regular and neutered)
+ - bugfix: Borers now spawn in vents, rather than on top of them.
+ - spellcheck: Fixed some minor typos and whatnot.
+ Shoddd:
+ - bugfix: Ash walkers can now use flight potions
+2024-09-16:
+ Absolucy:
+ - bugfix: Fixed the Goldeneye shuttle console becoming a nukie shuttle console when
+ reconstructed.
+ - bugfix: Cleaning stickers off the floor now properly drops the sticker instead
+ of runtiming and permanently sending it to nullspace.
+ KnigTheThrasher:
+ - bugfix: Fixed bot pathfinding in Kilostation
+ MomoBerri:
+ - bugfix: Icebox's lavaland bar has been renovated to fit properly.
+ - bugfix: several fixes to voidraptor
+ PotatoTomahto:
+ - bugfix: Bot launch pad fixed and renamed to "orbital bot pad".
+ - bugfix: Stationary medbot healing sprite fixed.
+ RandomDudeFromTheRim:
+ - bugfix: Stage 2 singularities should no longer escape containment
+ The-Black-Screen:
+ - rscadd: added more dirt to maintenance, added more fire extinguishers, miscellaneous
+ things.
+ - rscdel: removed the construction area, miscellaneous things.
+ - qol: added a northern entrance to all random bar maps, changed a lot of stuff
+ in a lot of areas, removed airlocks from specific maintenance area paths.
+ - bugfix: added a lot of the changes that were supposed to be in PR 2213.
+2024-09-17:
+ Shoddd:
+ - bugfix: Apid hive exits are now anchored
+ SirNightKnight:
+ - qol: Utility jumpsuits can now be unzipped.
+2024-09-18:
+ Absolucy:
+ - qol: Looms will now "refill" your stack while you're spinning if you have more
+ stacks adjacent to you on the floor.
+ - bugfix: You can now actually see what Craig the Potted Plant is holding!
+ - bugfix: Craig's mini sprite now properly shows up when examining them.
+ - qol: Craig will now automatically pick up an adjacent watering can when loading,
+ if there is one.
+ - qol: You can manually hand Craig a watering can now. You can also swap out a normal
+ can for an advanced one.
+ - bugfix: Fixed Craig's name showing up as "the craig the potted plant" in examines
+ and such.
+ - bugfix: Plant trays no longer have doubled info about contained reagents when
+ examined.
+ - qol: Dexterous basic mobs with inhand sprites will now show what they're holding
+ when examined.
+ MrBagHead:
+ - qol: The Xenobiology/Science Bag can now hold slime crossbreed extracts, allowing
+ for easier management of all slime extracts.
+ Wisemonster:
+ - rscadd: Added the medipen refiller to all in-rotation medbay lobbies
+ - balance: ' Moved the medipen reffiler on voidraptor to the medbay lobby'
+2024-09-19:
+ Absolucy:
+ - refactor: Biocubes are now proper stacks, and behave as such.
+ - image: Made the biocube sprite slightly smaller.
+2024-09-20:
+ Absolucy:
+ - bugfix: Fix constant errors related to pathology symptom varients.
+ - qol: Health Analyzers now tell if someone is incapable of being healed.
+ - qol: Cryotubes will now automatically eject people who are incapable of being
+ healed.
+ - qol: Wound tending surgeries can no longer be started on patients who are incapable
+ of being healed.
+ - bugfix: Hopefully fix bloodsuckers getting stuck in their coffin after torpor.
+ - bugfix: Abandoned areas in maint (i.e gambling den, garden, etc) are now properly
+ protected from radstorms.
+ - rscadd: Radstorm-proofed some more areas.
+ - bugfix: MOD chameleon modules actually work properly - they disguise when the
+ suit is deactivated, undisguise when its activated, and properly handle modsuits
+ that can be equipped to different slots.
+ Ivniinvi:
+ - spellcheck: Fixed some typos in the cassette system
+ PotatoTomahto:
+ - rscadd: Cooking removes diseases from all reagents.
+ - bugfix: Mice properly transfer their disease to their dead carcass.
+ RafRoq:
+ - rscadd: 'New plant trait: Catalytic Inhibitor Serum.'
+ - rscadd: 'New restricted traitor uplink item: plant data disk with Catalytic Inhibitor
+ Serum gene for 20 TC.'
+ - bugfix: Fixed Dank Vesicles and Densified Chemicals interaction.
+ - rscdel: Removed the Potency Limiter gene.
+ ThePooba:
+ - rscadd: added maints between supply and top centcom
+ - bugfix: fixed retirement air
+ - rscadd: Warden requires 10 sec hours now
+ - rscadd: added security bot "Bigsky" (he big!)
+ - balance: Rust heretics mark doesnt delete 75% of your items now and instead does
+ 50 damage to your items
+ - balance: cosmic mark makes you confused for 7 seconds
+ Wisemonster:
+ - balance: Heavily slowed the progression of heart worms
+ - qol: Geysers now have a description explaining how to extract chemicals from them.
+2024-09-21:
+ ThePooba:
+ - balance: changed florida men anmes to be character names and not set to florida
+ man
+2024-09-22:
+ Absolucy:
+ - bugfix: Fixed some major lag that'd occur related to alt-clicking.
+ - server: Added a failsafe to immediately restart the MC during certain conditions
+ where a subsystem would loop infinitely while queuing.
+ - balance: The storyteller now actually still continues to matter after the 85 minute
+ mark.
+ - rscadd: Added an automatic crew transfer vote - which will first start at the
+ 90 minute mark, and repeat every 30 minutes if it doesn't pass. If it passes,
+ the shuttle will be called (and cannot be recalled). Players cannot manually
+ start transfer votes - only admins or the server itself can.
+ - qol: Made the vote start/end messages in chat look better and easier to see.
+ C0ld0n02:
+ - rscadd: Adds Power gloves as a new 8 tc engineering traitor item.
+ Tractor Mann:
+ - rscadd: Added a new quirk, Tunnel Vision! (-2) using it will add FOV, preventing
+ you from seeing behind you! You can pick a range of 90-180-270 Degrees to not
+ see people in.
+ - rscdel: Removed herobrine.
+2024-09-23:
+ Absolucy:
+ - admin: '"Get Server Logs" can now jump to a specific round ID.'
+ - admin: '"Get Current/Server Logs (Debug)" can now download profiler logs, as users
+ with the permission to use that can use the profiler anyways.'
+2024-09-24:
+ Absolucy:
+ - bugfix: Seedless now works for spliced plants.
+ - bugfix: Made the seed extractor UI a bit less laggy.
+ - bugfix: Fixed the Living Heart ritual deleting your old heart when replacing it
+ instead of having it dramatically burst out of your chest like it should.
+ - qol: The Living Heart ritual will now work if you don't have one at all for some
+ reason, in the same way that'd you use an organic heart in the ritual to replace
+ a cybernetic heart.
+ - qol: The Living Heart ritual, when putting a new heart into your chest, will now
+ heal the heart enough to be just under the "severe damage" threshold, if needed.
+ ancient-engineer:
+ - bugfix: fixed power gloves dusting you if the power was below 10 GWs instead of
+ greater then or equal to 10 GWs
+ flleeppyy:
+ - rscdel: qc some jukebox songs
+2024-09-25:
+ Absolucy, Kashargul:
+ - qol: Hides the delete button on the main chat tab.
+ - qol: You can now reorder chat tabs.
+ CannibalHunter:
+ - rscadd: Added new gravestone decor for mappers
+ - rscadd: Added several admin plushies to the monkeshop!
+ Gw0sty:
+ - rscadd: Adds syndicate boxes for revivers, anti-stun, and anti-drop cybernetics
+ with a cyberlink also.
+ - bugfix: Fixed the Nuclear and Assault Operatives cybernetics by giving them syndicate
+ box bundles instead
+ Shoddd:
+ - bugfix: Vine Tangle for Venus Human traps works again.
+ - rscadd: Vine Tangle heals Venus Human Traps.
+ TTNT789:
+ - rscadd: Eeble Plushie
+ Wisemonster:
+ - balance: Replaced the blueshield's security belt with a sligtly larger, empty
+ one that cannot hold batons. Moved their pepperspray to their locker.
+ - balance: rReduced the amount of scrubbers affected by the overflow event down
+ from 50% to 20%
+2024-09-26:
+ Absolucy:
+ - bugfix: Fixed some minor errors and bugs with bloodsuckers.
+ - rscdel: Changelings can no longer make flesh spiders.
+ - rscadd: Instead, changelings can now birth teratomas, monkeylike menaces with
+ the goal of fucking everything up!
+ - bugfix: Ensured some of the plant genes had proper icons in the Seed Extractor
+ UI.
+ AlbertNanotracen:
+ - bugfix: self respiration messages should no longer spam chat as often
+ Shoddd:
+ - rscadd: Ponys are now orderable from cargo
+ - rscadd: The ranger poncho is now purchable in the loadout menu for 5000 monkecoins
+ - rscadd: New Revolver and Lever action Rifle now orderable from cargo
+ TTNT789:
+ - bugfix: Eeble plushie now adds to loudout when bought (im stupid i know)
+ ThePooba:
+ - bugfix: gary doesnt get aa anymore
+ Wisemonster:
+ - bugfix: Fixed the doubled blueshields lockers on blueshift
+ - balance: Blueshields are now taken into account for spawning various storyteller
+ antag events.
+ flleeppyy:
+ - rscadd: If you are the chaplain, or have the Spiritual trait, you will be divinely
+ warned that it is almost time to go.
+2024-09-27:
+ Absolucy:
+ - bugfix: Fixed malkavian bloodsuckers not getting an objective when another bloodsucker
+ breaks the masquerade.
+ - bugfix: Fixed some logic relating to antag selection (roundstart, midround, and
+ ghost roles), hopefully reducing hard deletes and potential issues.
+ Gw0sty:
+ - bugfix: Fix Delta's hydroponics railings to be on a lower layer than the machines
+ - rscadd: Added spawners for Science security officers, Research Directors, robotists,
+ and scientists on Tram Station.
+ KnigTheThrasher:
+ - rscadd: Added layer adapters to atmos
+ - bugfix: Added cmo spawner to kilo
+ - bugfix: Connected rest of areas to distro
+ PotatoTomahto:
+ - rscadd: Portable gravity machine added as circuit and cargo order.
+ - bugfix: Fields, such as gravity, work now.
+ Shoddd:
+ - bugfix: Long Revolver and Levergun are now loadable
+ ThePooba:
+ - rscadd: Bomb Actualizer machine, available from illegal tech, pop in a ttv and
+ watch it explode bigger and bettert than before
+ Wisemonster:
+ - balance: Added a telescopic baton to the blueshield's locker
+2024-09-28:
+ Absolucy:
+ - server: Profiler logs now sort by overtime and have a proper timestamp in the
+ filename.
+ Absolucy, Kocma-san:
+ - bugfix: Fixed lights stopping emitting light in some situations.
+ RikuTheKiller:
+ - balance: Blood Brothers can now convert members of security if their mindshields
+ are removed.
+ - balance: IPCs are no longer immune to being converted by revolutionaries and blood
+ brothers.
+ - qol: You now get a balloon alert if you get a second flash as a Blood Brother.
+2024-09-29:
+ CannibalHunter:
+ - rscadd: Added several more admin plushies to the monkeshop
+ ThePooba:
+ - rscadd: monkeystorm
+ - bugfix: loneop less
+2024-09-30:
+ Gw0sty:
+ - bugfix: Removed the second windoor on delta botany.
+ - rscadd: adds missing medipen refiller to Kilo
+ - qol: Ensures all surgical tools in robotics are in a container of some sort.
+ - rscadd: Added disk compartmentalizers to most stations virology and hydroponics.
+ - qol: Made Bio bags able to hold GNA disks. (Disease disks)
+ - qol: Moves botany and chicken lexicons into a single bookshelf
+ - rscadd: Added an auto lathe, morgue, and plasteel to Blueshift's robotics
+ KnigTheThrasher:
+ - rscadd: Added a shower to kilo engi
+ - rscadd: Added a windoor to medbay storage on kilo
+ Shoddd:
+ - balance: syndicate bible can now provide anti-magic from pocket and belt
+ t-toasted:
+ - rscadd: Theseus
diff --git a/html/changelogs/archive/2024-10.yml b/html/changelogs/archive/2024-10.yml
new file mode 100644
index 000000000000..eadec1c276e4
--- /dev/null
+++ b/html/changelogs/archive/2024-10.yml
@@ -0,0 +1,3 @@
+2024-10-01:
+ flleeppyy:
+ - admin: '"Play Internet Sound" now has a "Custom Title" option'
diff --git a/icons/effects/mouse_pointers/lace.dmi b/icons/effects/mouse_pointers/lace.dmi
new file mode 100644
index 000000000000..68aad755c627
Binary files /dev/null and b/icons/effects/mouse_pointers/lace.dmi differ
diff --git a/icons/mob/actions/actions_space_dragon.dmi b/icons/mob/actions/actions_space_dragon.dmi
index a4e33eef1eb2..48d73fd6f47a 100644
Binary files a/icons/mob/actions/actions_space_dragon.dmi and b/icons/mob/actions/actions_space_dragon.dmi differ
diff --git a/icons/mob/actions/actions_spells.dmi b/icons/mob/actions/actions_spells.dmi
index 39ea58104173..d66c2e22011a 100644
Binary files a/icons/mob/actions/actions_spells.dmi and b/icons/mob/actions/actions_spells.dmi differ
diff --git a/icons/mob/clothing/back.dmi b/icons/mob/clothing/back.dmi
index 36b259334cce..065bf09fccbf 100644
Binary files a/icons/mob/clothing/back.dmi and b/icons/mob/clothing/back.dmi differ
diff --git a/icons/mob/huds/hud.dmi b/icons/mob/huds/hud.dmi
index 5473ba0f1390..5d9ab7cdfd19 100644
Binary files a/icons/mob/huds/hud.dmi and b/icons/mob/huds/hud.dmi differ
diff --git a/icons/mob/simple/animal.dmi b/icons/mob/simple/animal.dmi
index 602648205a26..3de6e06a2484 100644
Binary files a/icons/mob/simple/animal.dmi and b/icons/mob/simple/animal.dmi differ
diff --git a/icons/mob/species/misc/bodypart_overlay_simple.dmi b/icons/mob/species/misc/bodypart_overlay_simple.dmi
index 2bc1dda5663a..2c1739fd1687 100644
Binary files a/icons/mob/species/misc/bodypart_overlay_simple.dmi and b/icons/mob/species/misc/bodypart_overlay_simple.dmi differ
diff --git a/icons/obj/machines/artifact_machines.dmi b/icons/obj/machines/artifact_machines.dmi
index 004e7539d4e5..8cbd0b5ff0ba 100644
Binary files a/icons/obj/machines/artifact_machines.dmi and b/icons/obj/machines/artifact_machines.dmi differ
diff --git a/icons/obj/machines/gravity_generator.dmi b/icons/obj/machines/gravity_generator.dmi
index 69395034dc84..fd3fec903c67 100644
Binary files a/icons/obj/machines/gravity_generator.dmi and b/icons/obj/machines/gravity_generator.dmi differ
diff --git a/icons/obj/machines/research.dmi b/icons/obj/machines/research.dmi
index 02d848eb4e9d..77721bb9bbb3 100644
Binary files a/icons/obj/machines/research.dmi and b/icons/obj/machines/research.dmi differ
diff --git a/icons/obj/medical/organs/infuser_organs.dmi b/icons/obj/medical/organs/infuser_organs.dmi
index 49ac2751aae2..c2551b41f666 100644
Binary files a/icons/obj/medical/organs/infuser_organs.dmi and b/icons/obj/medical/organs/infuser_organs.dmi differ
diff --git a/icons/obj/smooth_structures/grav_field.dmi b/icons/obj/smooth_structures/grav_field.dmi
new file mode 100644
index 000000000000..4f51707938e9
Binary files /dev/null and b/icons/obj/smooth_structures/grav_field.dmi differ
diff --git a/icons/obj/smooth_structures/grav_field.png b/icons/obj/smooth_structures/grav_field.png
new file mode 100644
index 000000000000..5177e5772404
Binary files /dev/null and b/icons/obj/smooth_structures/grav_field.png differ
diff --git a/icons/obj/smooth_structures/grav_field.png.toml b/icons/obj/smooth_structures/grav_field.png.toml
new file mode 100644
index 000000000000..49f4000c213d
--- /dev/null
+++ b/icons/obj/smooth_structures/grav_field.png.toml
@@ -0,0 +1,5 @@
+output_name = "grav_field"
+template = "bitmask/diagonal_32x32.toml"
+
+[animation]
+delays = [1, 1, 1]
diff --git a/icons/obj/smooth_structures/grav_field_emissive.dmi b/icons/obj/smooth_structures/grav_field_emissive.dmi
new file mode 100644
index 000000000000..a2db63f082a5
Binary files /dev/null and b/icons/obj/smooth_structures/grav_field_emissive.dmi differ
diff --git a/icons/obj/smooth_structures/grav_field_emissive.png b/icons/obj/smooth_structures/grav_field_emissive.png
new file mode 100644
index 000000000000..d3746ac5b63a
Binary files /dev/null and b/icons/obj/smooth_structures/grav_field_emissive.png differ
diff --git a/icons/obj/smooth_structures/grav_field_emissive.png.toml b/icons/obj/smooth_structures/grav_field_emissive.png.toml
new file mode 100644
index 000000000000..ba6a7f73aaf7
--- /dev/null
+++ b/icons/obj/smooth_structures/grav_field_emissive.png.toml
@@ -0,0 +1,5 @@
+output_name = "grav_field_emissive"
+template = "bitmask/diagonal_32x32.toml"
+
+[animation]
+delays = [1, 1, 1]
diff --git a/icons/obj/tiles.dmi b/icons/obj/tiles.dmi
index 4ba733b29dcc..fdddb793362a 100644
Binary files a/icons/obj/tiles.dmi and b/icons/obj/tiles.dmi differ
diff --git a/icons/obj/weapons/guns/ballistic.dmi b/icons/obj/weapons/guns/ballistic.dmi
index aacb9457de0c..0f2ca2698644 100644
Binary files a/icons/obj/weapons/guns/ballistic.dmi and b/icons/obj/weapons/guns/ballistic.dmi differ
diff --git a/icons/obj/weapons/guns/magic.dmi b/icons/obj/weapons/guns/magic.dmi
index 92210a4e0542..0434b5b6d852 100644
Binary files a/icons/obj/weapons/guns/magic.dmi and b/icons/obj/weapons/guns/magic.dmi differ
diff --git a/icons/turf/decals.dmi b/icons/turf/decals.dmi
index 278db3ad3cd1..c4a9d8858c5a 100644
Binary files a/icons/turf/decals.dmi and b/icons/turf/decals.dmi differ
diff --git a/icons/turf/floors.dmi b/icons/turf/floors.dmi
index 580a53fcb3ad..153fe582544f 100644
Binary files a/icons/turf/floors.dmi and b/icons/turf/floors.dmi differ
diff --git a/monkestation/code/__DEFINES/antag_defines.dm b/monkestation/code/__DEFINES/antag_defines.dm
deleted file mode 100644
index 33358561c55a..000000000000
--- a/monkestation/code/__DEFINES/antag_defines.dm
+++ /dev/null
@@ -1,2 +0,0 @@
-/// Whether the antagonist can see exploitable info on people they examine.
-#define FLAG_CAN_SEE_EXPOITABLE_INFO (1<<1)
diff --git a/monkestation/code/__HELPERS/_lists.dm b/monkestation/code/__HELPERS/_lists.dm
index c13af294d408..1b997b61f862 100644
--- a/monkestation/code/__HELPERS/_lists.dm
+++ b/monkestation/code/__HELPERS/_lists.dm
@@ -5,3 +5,10 @@
for(var/i=1, iError: browse_files(): File not found/Invalid file([path]).")
return
diff --git a/monkestation/code/area/areas/ruins.dm b/monkestation/code/area/areas/ruins.dm
new file mode 100644
index 000000000000..89889cd7eab2
--- /dev/null
+++ b/monkestation/code/area/areas/ruins.dm
@@ -0,0 +1,25 @@
+//SYNDICATE LISTENING POST STATION
+
+/area/ruin/space/has_grav/listeningstation
+ name = "\improper Listening Post"
+
+/area/ruin/space/has_grav/listeningstation/dorms
+ name = "Listening Post Dormitories"
+
+/area/ruin/space/has_grav/listeningstation/lobby
+ name = "Listening Post Lobby"
+
+/area/ruin/space/has_grav/listeningstation/lounge
+ name = "Listening Post Lounge"
+
+/area/ruin/space/has_grav/listeningstation/support
+ name = "Listening Post Support Facilities"
+
+/area/ruin/space/has_grav/listeningstation/asteroid
+ name = "Suspicious Asteroid"
+
+/area/ruin/space/has_grav/listeningstation/comms
+ name = "Listening Post Communications"
+
+/area/ruin/space/has_grav/listeningstation/medical
+ name = "Listening Post Infirmary"
diff --git a/monkestation/code/controllers/subsystem/autotransfer.dm b/monkestation/code/controllers/subsystem/autotransfer.dm
new file mode 100644
index 000000000000..e136b3aca303
--- /dev/null
+++ b/monkestation/code/controllers/subsystem/autotransfer.dm
@@ -0,0 +1,57 @@
+SUBSYSTEM_DEF(autotransfer)
+ name = "Autotransfer Vote"
+ flags = SS_KEEP_TIMING | SS_BACKGROUND
+ wait = 1 MINUTES
+ runlevels = RUNLEVEL_GAME
+
+ var/doing_transfer_vote = FALSE
+ COOLDOWN_DECLARE(next_transfer_vote)
+
+/datum/controller/subsystem/autotransfer/Initialize(timeofday)
+ if(!CONFIG_GET(number/transfer_vote_time))
+ flags |= SS_NO_FIRE
+ return SS_INIT_NO_NEED
+ SSticker.OnRoundstart(CALLBACK(src, PROC_REF(crew_transfer_setup)))
+ return SS_INIT_SUCCESS
+
+/datum/controller/subsystem/autotransfer/fire()
+ if(can_run_transfer_vote())
+ SSvote.initiate_vote(/datum/vote/shuttle_call, "automatic shuttle vote", forced = TRUE)
+
+/datum/controller/subsystem/autotransfer/proc/can_run_transfer_vote()
+ . = TRUE
+ if(doing_transfer_vote)
+ return FALSE
+ if(!CONFIG_GET(number/transfer_vote_time))
+ return FALSE
+ if(!next_transfer_vote || !COOLDOWN_FINISHED(src, next_transfer_vote))
+ return FALSE
+ if(SSvote.current_vote)
+ return FALSE
+ if(EMERGENCY_PAST_POINT_OF_NO_RETURN)
+ return FALSE
+ if(SSshuttle.supermatter_cascade)
+ return FALSE
+
+/datum/controller/subsystem/autotransfer/proc/crew_transfer_setup()
+ COOLDOWN_START(src, next_transfer_vote, CONFIG_GET(number/transfer_vote_time))
+
+/datum/controller/subsystem/autotransfer/proc/crew_transfer_passed()
+ if(!SSticker.IsRoundInProgress())
+ CRASH("Somehow tried to initiate crew transfer, even tho there is not ongoing round!")
+ SSshuttle.admin_emergency_no_recall = TRUE
+ if(SSshuttle.emergency?.mode == SHUTTLE_DISABLED || EMERGENCY_PAST_POINT_OF_NO_RETURN)
+ return
+ if(EMERGENCY_IDLE_OR_RECALLED)
+ SSshuttle.emergency.request(
+ red_alert = (SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED)
+ )
+ crew_transfer_continue() // safety measure
+
+/datum/controller/subsystem/autotransfer/proc/crew_transfer_continue()
+ SSgamemode.point_gain_multipliers[EVENT_TRACK_ROLESET]++
+ var/subsequent_transfer_vote_time = CONFIG_GET(number/subsequent_transfer_vote_time)
+ if(!subsequent_transfer_vote_time)
+ next_transfer_vote = 0
+ return
+ COOLDOWN_START(src, next_transfer_vote, subsequent_transfer_vote_time)
diff --git a/monkestation/code/controllers/subsystem/init_profiler.dm b/monkestation/code/controllers/subsystem/init_profiler.dm
new file mode 100644
index 000000000000..a17025be41a2
--- /dev/null
+++ b/monkestation/code/controllers/subsystem/init_profiler.dm
@@ -0,0 +1,28 @@
+#define INIT_PROFILE_NAME "init_profiler.json"
+
+///Subsystem exists so we can separately log init time costs from the costs of general operation
+///Hopefully this makes sorting out what causes problems when easier
+SUBSYSTEM_DEF(init_profiler)
+ name = "Init Profiler"
+ init_order = INIT_ORDER_INIT_PROFILER
+ init_stage = INITSTAGE_MAX
+ flags = SS_NO_FIRE
+
+/datum/controller/subsystem/init_profiler/Initialize()
+ if(CONFIG_GET(flag/auto_profile))
+ write_init_profile()
+ return SS_INIT_SUCCESS
+ return SS_INIT_NO_NEED
+
+/datum/controller/subsystem/init_profiler/proc/write_init_profile()
+ var/list/current_profile_data = world.Profile(PROFILE_REFRESH, format = "json")
+ current_profile_data = json_decode(current_profile_data) // yes this is stupid but this gets us a list in a non-awful format
+ CHECK_TICK
+ sortTim(current_profile_data, GLOBAL_PROC_REF(sort_overtime_dsc))
+
+ if(!length(current_profile_data)) //Would be nice to have explicit proc to check this
+ stack_trace("Warning, profiling stopped manually before dump.")
+ rustg_file_write(json_encode(current_profile_data), "[GLOB.log_directory]/[INIT_PROFILE_NAME]")
+ world.Profile(PROFILE_CLEAR) //Now that we're written this data out, dump it. We don't want it getting mixed up with our current round data
+
+#undef INIT_PROFILE_NAME
diff --git a/monkestation/code/controllers/subsystem/job.dm b/monkestation/code/controllers/subsystem/job.dm
index cbe28992efda..d3ca62b5b386 100644
--- a/monkestation/code/controllers/subsystem/job.dm
+++ b/monkestation/code/controllers/subsystem/job.dm
@@ -65,9 +65,10 @@
mass_adjust_antag_rep(cliented_list, 1)
var/list/weighted_candidates = return_antag_rep_weight(candidates)
-
var/antag_selection_loops = SSgamemode.current_roundstart_event.get_antag_amount()
- for(var/i in 1 to antag_selection_loops)
+ var/iter = 0
+ while(iter < antag_selection_loops)
+ iter++
if(antag_selection_loops >= 100)
log_storyteller("h_r_a failed, antag_selection_loops went over 100")
return FALSE
@@ -78,7 +79,11 @@
log_storyteller("h_r_a failed, below required candidates for selected roundstart event")
return FALSE
break
- var/client/dead_client = pick_n_take_weighted(weighted_candidates)
+ var/candidate_ckey = pick_n_take_weighted(weighted_candidates)
+ var/client/dead_client = GLOB.directory[candidate_ckey]
+ if(QDELETED(dead_client))
+ antag_selection_loops++
+ continue
var/mob/dead/new_player/candidate = dead_client.mob
if(!candidate.mind || !istype(candidate))
antag_selection_loops++
@@ -105,12 +110,16 @@
continue
var/mob/dead/new_player/candidate
var/sanity = 0
- while(!candidate && length(weighted_candidates) && !sanity >= 100)
+ while(QDELETED(candidate) && length(weighted_candidates) && sanity < 100)
sanity++
- candidate = pick_n_take_weighted(weighted_candidates)
- if(!candidate.mind || !istype(candidate))
+ var/candidate_ckey = pick_n_take_weighted(weighted_candidates)
+ var/client/candidate_client = GLOB.directory[candidate_ckey]
+ if(QDELETED(candidate_client))
+ continue
+ candidate = candidate_client.mob
+ if(!isnewplayer(candidate) || QDELING(candidate) || QDELETED(candidate.mind))
candidate = null
- if(!candidate)
+ if(QDELETED(candidate))
if(length(SSgamemode.roundstart_antag_minds) < SSgamemode.current_roundstart_event.base_antags)
log_storyteller("h_r_a failed, removing unassigned antag player put us below current event minimum candidates and we were unable to find a replacement")
return FALSE
diff --git a/monkestation/code/controllers/subsystem/profiler.dm b/monkestation/code/controllers/subsystem/profiler.dm
new file mode 100644
index 000000000000..f3445536588d
--- /dev/null
+++ b/monkestation/code/controllers/subsystem/profiler.dm
@@ -0,0 +1,42 @@
+/datum/controller/subsystem/profiler
+ var/sort_cost = 0
+
+/datum/controller/subsystem/profiler/stat_entry(msg)
+ msg += "F:[round(fetch_cost, 1)]ms"
+ msg += "|S:[round(sort_cost, 1)]ms"
+ msg += "|W:[round(write_cost, 1)]ms"
+ return msg
+
+/datum/controller/subsystem/profiler/proc/DumpFile()
+ var/timer = TICK_USAGE_REAL
+ var/list/current_profile_data = world.Profile(PROFILE_REFRESH, format = "json")
+ current_profile_data = json_decode(current_profile_data) // yes this is stupid but this gets us a list in a non-awful format
+ var/current_sendmaps_data = world.Profile(PROFILE_REFRESH, type = "sendmaps", format = "json")
+ fetch_cost = MC_AVERAGE(fetch_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+ CHECK_TICK
+
+ if(!length(current_profile_data)) //Would be nice to have explicit proc to check this
+ stack_trace("Warning, profiling stopped manually before dump.")
+
+ timer = TICK_USAGE_REAL
+ sortTim(current_profile_data, GLOBAL_PROC_REF(sort_overtime_dsc))
+ sort_cost = MC_AVERAGE(sort_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+
+ var/timestamp = time2text(world.timeofday, "YYYY-MM-DD_hh-mm-ss")
+ var/prof_file = "[GLOB.log_directory]/profiler/profiler-[timestamp].json"
+ if(!length(current_sendmaps_data)) //Would be nice to have explicit proc to check this
+ stack_trace("Warning, sendmaps profiling stopped manually before dump.")
+ var/sendmaps_file = "[GLOB.log_directory]/profiler/sendmaps-[timestamp].json"
+
+ timer = TICK_USAGE_REAL
+ rustg_file_write(json_encode(current_profile_data), prof_file)
+ rustg_file_write(current_sendmaps_data, sendmaps_file)
+ write_cost = MC_AVERAGE(write_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+
+/datum/controller/subsystem/profiler/get_metrics()
+ . = ..()
+ .["custom"] = list(
+ "fetch_cost" = fetch_cost,
+ "sort_cost" = sort_cost,
+ "write_cost" = write_cost,
+ )
diff --git a/monkestation/code/datums/components/basic_inhands.dm b/monkestation/code/datums/components/basic_inhands.dm
new file mode 100644
index 000000000000..fdf16c5b8e66
--- /dev/null
+++ b/monkestation/code/datums/components/basic_inhands.dm
@@ -0,0 +1,15 @@
+/datum/component/basic_inhands/RegisterWithParent()
+ . = ..()
+ RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
+
+/datum/component/basic_inhands/UnregisterFromParent()
+ . = ..()
+ UnregisterSignal(parent, COMSIG_ATOM_EXAMINE)
+
+/datum/component/basic_inhands/proc/on_examine(datum/source, mob/user, list/examine_info)
+ SIGNAL_HANDLER
+ var/mob/living/parent = src.parent
+ for(var/obj/item/held_thing in parent.held_items)
+ if(held_thing.item_flags & (ABSTRACT | EXAMINE_SKIP | HAND_ITEM))
+ continue
+ examine_info += span_info("[parent.p_They()] [parent.p_are()] holding [held_thing.get_examine_string(user)] in [parent.p_their()] [parent.get_held_index_name(parent.get_held_index_of_item(held_thing))].")
diff --git a/monkestation/code/datums/components/charge_adjuster.dm b/monkestation/code/datums/components/charge_adjuster.dm
new file mode 100644
index 000000000000..2fa3c1d3b097
--- /dev/null
+++ b/monkestation/code/datums/components/charge_adjuster.dm
@@ -0,0 +1,37 @@
+/datum/component/charge_adjuster
+ ///The typepath of atom to give charges to
+ var/type_to_charge_to
+ ///How many charges to give
+ var/charges_given = 1
+ ///TYPE_PROC_REF() to call on the hit item if its type_to_charge_to, proc MUST take amount to get adjusted by as first arg
+ var/called_proc_name
+
+/datum/component/charge_adjuster/Initialize(type_to_charge_to, charges_given = 1, called_proc_name)
+ if(!isitem(parent) || !type_to_charge_to || !called_proc_name)
+ return COMPONENT_INCOMPATIBLE
+
+ src.type_to_charge_to = type_to_charge_to
+ src.charges_given = charges_given
+ src.called_proc_name = called_proc_name
+
+/datum/component/charge_adjuster/Destroy(force, silent)
+ called_proc_name = null
+ return ..()
+
+/datum/component/charge_adjuster/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, PROC_REF(check_hit_atom))
+
+/datum/component/charge_adjuster/UnregisterFromParent()
+ UnregisterSignal(parent, COMSIG_ITEM_AFTERATTACK)
+
+/datum/component/charge_adjuster/proc/check_hit_atom(obj/item/source, atom/target, mob/user, proximity_flag)
+ SIGNAL_HANDLER
+ if(!proximity_flag || !istype(target, type_to_charge_to))
+ return
+
+ if(!call(target, called_proc_name)(charges_given))
+ return
+
+ to_chat(user, span_notice("You insert \the [source] in \the [target]."))
+ qdel(parent)
+ return COMPONENT_CANCEL_ATTACK_CHAIN
diff --git a/monkestation/code/datums/components/lock_on_cursor.dm b/monkestation/code/datums/components/lock_on_cursor.dm
new file mode 100644
index 000000000000..99d6fa447604
--- /dev/null
+++ b/monkestation/code/datums/components/lock_on_cursor.dm
@@ -0,0 +1,12 @@
+/datum/component/lock_on_cursor/Initialize(lock_cursor_range,
+ lock_amount,
+ list/target_typecache,
+ list/immune,
+ icon,
+ icon_state,
+ datum/callback/on_click_callback,
+ datum/callback/on_lock,
+ datum/callback/can_target_callback,
+ catcher_default_click)
+ . = ..()
+ mouse_tracker.default_click = catcher_default_click
diff --git a/monkestation/code/datums/elements/loomable.dm b/monkestation/code/datums/elements/loomable.dm
new file mode 100644
index 000000000000..9534fd1ade2b
--- /dev/null
+++ b/monkestation/code/datums/elements/loomable.dm
@@ -0,0 +1,6 @@
+/datum/element/loomable/proc/combine_nearby_stacks(atom/target, obj/item/stack/our_stack)
+ for(var/obj/item/stack/nearby_stack as anything in view(1, get_turf(target)))
+ if(our_stack.amount >= our_stack.max_amount)
+ break
+ if(our_stack.can_merge(nearby_stack, inhand = TRUE))
+ nearby_stack.merge(our_stack)
diff --git a/monkestation/code/datums/quirks/negative_quirks.dm b/monkestation/code/datums/quirks/negative_quirks.dm
index 63f898111838..aa4eeb29a9a7 100644
--- a/monkestation/code/datums/quirks/negative_quirks.dm
+++ b/monkestation/code/datums/quirks/negative_quirks.dm
@@ -214,3 +214,29 @@
if(isipc(quirk_holder)) //monkestation addition
to_chat(quirk_holder, span_boldnotice("Your chassis feels frail."))
+/datum/quirk/tunnel_vision
+ name = "Tunnel Vision"
+ desc = "You spent too long scoped in. You cant see behind you!"
+ value = -2
+ icon = FA_ICON_QUESTION
+ gain_text = span_notice("You have trouble focusing on what you left behind.")
+ lose_text = span_notice("You feel paranoid, constantly checking your back...")
+ medical_record_text = "Patient had trouble noticing people walking up from behind during the examination."
+
+/datum/quirk/tunnel_vision/add_unique(client/client_source)
+ var/range_name = client_source?.prefs.read_preference(/datum/preference/choiced/tunnel_vision_fov) || "Minor (90 Degrees)"
+ var/fov_range
+ switch(range_name)
+ if ("Severe (270 Degrees)")
+ fov_range = FOV_270_DEGREES
+ if ("Moderate (180 Degrees)")
+ fov_range = FOV_180_DEGREES
+ else
+ fov_range = FOV_90_DEGREES
+ quirk_holder.add_fov_trait("tunnel vision quirk", fov_range)
+/*
+/datum/quirk/tunnel_vision/add()
+ quirk_holder.add_fov_trait("tunnel vision quirk", fov_range)
+*/
+/datum/quirk/tunnel_vision/remove()
+ quirk_holder.remove_fov_trait("tunnel vision quirk")
diff --git a/monkestation/code/datums/votes/_vote_datum.dm b/monkestation/code/datums/votes/_vote_datum.dm
new file mode 100644
index 000000000000..e19428a64d16
--- /dev/null
+++ b/monkestation/code/datums/votes/_vote_datum.dm
@@ -0,0 +1,2 @@
+/datum/vote/proc/can_vote(mob/voter)
+ return TRUE
diff --git a/monkestation/code/datums/votes/transfer_vote.dm b/monkestation/code/datums/votes/transfer_vote.dm
new file mode 100644
index 000000000000..5baa6218e1a5
--- /dev/null
+++ b/monkestation/code/datums/votes/transfer_vote.dm
@@ -0,0 +1,50 @@
+#define CHOICE_CALL "Call Shuttle"
+#define CHOICE_CONTINUE "Continue Round"
+
+/// If a map vote is called before the emergency shuttle leaves the station, the players can call another vote to re-run the vote on the shuttle leaving.
+/datum/vote/shuttle_call
+ name = "Call Shuttle"
+ message = "Should we go home?"
+ default_choices = list(
+ CHOICE_CALL,
+ CHOICE_CONTINUE,
+ )
+ player_startable = FALSE
+
+/datum/vote/shuttle_call/reset()
+ . = ..()
+ SSautotransfer.doing_transfer_vote = FALSE
+
+/datum/vote/shuttle_call/can_be_initiated(mob/by_who, forced = FALSE)
+ . = ..()
+ if(!.)
+ return FALSE
+ if(!SSticker.IsRoundInProgress())
+ return FALSE
+ if(EMERGENCY_PAST_POINT_OF_NO_RETURN)
+ return FALSE
+ if(SSautotransfer.doing_transfer_vote)
+ return FALSE
+
+/datum/vote/shuttle_call/initiate_vote(initiator, duration)
+ . = ..()
+ SSautotransfer.doing_transfer_vote = TRUE
+
+/datum/vote/shuttle_call/finalize_vote(winning_option)
+ switch(winning_option)
+ if(CHOICE_CALL)
+ SSautotransfer.crew_transfer_passed()
+ if(CHOICE_CONTINUE)
+ SSautotransfer.crew_transfer_continue()
+ else
+ CRASH("[type] wasn't passed a valid winning choice. (Got: [winning_option || "null"])")
+
+/datum/vote/shuttle_call/can_vote(mob/voter = usr)
+ . = TRUE
+ if(voter.client?.holder)
+ return TRUE
+ if(isobserver(voter) || voter.stat == DEAD || !(voter.ckey in GLOB.joined_player_list)) // only living crew gets to vote
+ return FALSE
+
+#undef CHOICE_CONTINUE
+#undef CHOICE_CALL
diff --git a/monkestation/code/datums/weather/weather_types/radiation_storm.dm b/monkestation/code/datums/weather/weather_types/radiation_storm.dm
new file mode 100644
index 000000000000..4d6d2466c9bb
--- /dev/null
+++ b/monkestation/code/datums/weather/weather_types/radiation_storm.dm
@@ -0,0 +1,15 @@
+/datum/weather/rad_storm
+ protected_areas = list(/area/station/maintenance,
+ /area/station/ai_monitored/turret_protected/ai_upload, /area/station/ai_monitored/turret_protected/ai_upload_foyer,
+ /area/station/ai_monitored/turret_protected/aisat/maint, /area/station/ai_monitored/command/storage/satellite,
+ /area/station/ai_monitored/turret_protected/ai, /area/station/commons/storage/emergency/starboard,
+ /area/station/commons/storage/emergency/port, /area/shuttle, /area/station/common/cryopods, /area/station/security/prison/safe,
+ /area/station/security/prison/toilet, /area/icemoon/underground, /area/station/service/hydroponics/garden/abandoned,
+ /area/station/security/detectives_office/private_investigators_office, /area/station/security/bitden,
+ /area/station/service/library/abandoned, /area/station/service/theater/abandoned, /area/station/service/abandoned_gambling_den,
+ /area/station/service/electronic_marketing_den, /area/station/service/kitchen/abandoned, /area/station/medical/abandoned,
+ /area/station/science/research/abandoned, /area/station/asteroid, /area/misc/asteroid, /area/station/command/secure_bunker,
+ /area/station/cargo/power_station, /area/station/engineering/power_station, /area/station/science/power_station,
+ /area/station/science/power_station, /area/station/security/power_station, /area/station/service/power_station,
+ /area/station/medical/aslyum, /area/station/medical/virology/isolation,
+ )
diff --git a/monkestation/code/game/machinery/bomb_actualizer.dm b/monkestation/code/game/machinery/bomb_actualizer.dm
new file mode 100644
index 000000000000..e90f12c35c41
--- /dev/null
+++ b/monkestation/code/game/machinery/bomb_actualizer.dm
@@ -0,0 +1,260 @@
+/obj/machinery/bomb_actualizer
+ name = "Bomb Actualizer"
+ desc = "An advanced machine capable of releasing the normally bluespace-inhibited destructive potential of a bomb assembly... or so the sticker says"
+ circuit = /obj/item/circuitboard/machine/bomb_actualizer
+ icon = 'icons/obj/machines/research.dmi'
+ base_icon_state = "bomb_actualizer"
+ icon_state = "bomb_actualizer"
+ density = TRUE
+ max_integrity = 600
+ use_power = NO_POWER_USE
+ resistance_flags = FIRE_PROOF | ACID_PROOF
+ processing_flags = START_PROCESSING_MANUALLY
+ subsystem_type = /datum/controller/subsystem/processing/fastprocess
+
+ /**
+ * Stages for countdown warnings and alerts.
+ * 0 = no major alerts,
+ * 1 = the timer has reached 60 seconds,
+ * 2 = the timer has reached 10 seconds (it will reset to zero immediately after)
+ */
+ var/stage = 0
+ //location to call out on priority message
+ var/alerthere = ""
+ /// The TTV inserted in the machine.
+ var/obj/item/transfer_valve/inserted_bomb
+ //combined gasmix to determine the simulation to reality
+ var/datum/gas_mixture/combined_gasmix
+ //Timer till detonation in seconds
+ var/default_time = 420 SECONDS
+ //used to track current world time
+ var/timer = null
+ /// The countdown that'll show up to ghosts regarding the bomb's timer.
+ var/obj/effect/countdown/bomb_actualizer/countdown
+ //Can examine the Countdown
+ var/examinable_countdown = TRUE
+ //So the ttv transfers gas properly
+ var/obj/item/tank/tank_to_target
+ //For managing if the tank exploded so it doesnt explode twice
+ var/active = FALSE
+ var/exploded = FALSE
+ //When to beep (every second)
+ var/next_beep = null
+ //sounds for scaryness
+ var/beepsound = 'sound/items/timer.ogg'
+ var/scarywarning = 'sound/misc/bloblarm.ogg'
+ var/verybadsound = 'sound/machines/engine_alert1.ogg'
+ var/youdied = 'sound/misc/guitarreverb.ogg'
+ /// The countdown that'll show up to ghosts regarding the bomb's timer.
+ ///Our internal radio
+ var/obj/item/radio/radio
+ ///The key our internal radio uses
+ var/radio_key = /obj/item/encryptionkey/headset_sci
+ ///The common channel
+ var/common_channel = null
+ //Cooldown for pressing button
+ var/COOLDOWN_BOMB_BUTTON
+
+/obj/machinery/bomb_actualizer/Initialize(mapload)
+ . = ..()
+ RegisterSignal(src, COMSIG_ATOM_INTERNAL_EXPLOSION, PROC_REF(modify_explosion))
+ radio = new(src)
+ radio.keyslot = new radio_key
+ radio.set_listening(FALSE)
+ radio.recalculateChannels()
+ countdown = new(src)
+ update_appearance()
+
+//For when the machine is destroyed
+/obj/machinery/bomb_actualizer/Destroy()
+ inserted_bomb = null
+ radio = null
+ combined_gasmix = null
+ QDEL_NULL(countdown)
+ end_processing()
+ return ..()
+
+/obj/machinery/bomb_actualizer/attackby(obj/item/tool, mob/living/user, params)
+ if(active && istype(tool, /obj/item/transfer_valve))
+ to_chat(user, span_warning("You can't insert [tool] into [src] while [p_theyre()] currently active."))
+ return
+ if(istype(tool, /obj/item/transfer_valve))
+ if(inserted_bomb)
+ to_chat(user, span_warning("There is already a bomb in [src]."))
+ return
+ var/obj/item/transfer_valve/valve = tool
+ if(!valve.ready())
+ to_chat(user, span_warning("[valve] is incomplete."))
+ return
+ if(!user.transferItemToLoc(tool, src))
+ to_chat(user, span_warning("[tool] is stuck to your hand."))
+ return
+ inserted_bomb = tool
+ tank_to_target = inserted_bomb.tank_two
+ to_chat(user, span_notice("You insert [tool] into [src]"))
+ return
+ update_appearance()
+ return ..()
+
+/obj/machinery/bomb_actualizer/wrench_act(mob/living/user, obj/item/tool)
+ . = ..()
+ if(!active)
+ default_unfasten_wrench(user, tool)
+ return TOOL_ACT_TOOLTYPE_SUCCESS
+ return FALSE
+
+/obj/machinery/bomb_actualizer/screwdriver_act(mob/living/user, obj/item/tool)
+ if(!active)
+ if(!default_deconstruction_screwdriver(user, "[base_icon_state]-off", "[base_icon_state]", tool))
+ return FALSE
+ update_appearance()
+ return TRUE
+
+/obj/machinery/bomb_actualizer/crowbar_act(mob/living/user, obj/item/tool)
+ if(!default_deconstruction_crowbar(tool) && !active)
+ return FALSE
+ return TRUE
+
+/**
+ * Starts the Detonation Sequence
+ */
+/obj/machinery/bomb_actualizer/proc/start_detonation()
+ if(!TIMER_COOLDOWN_CHECK(src, COOLDOWN_BOMB_BUTTON))
+
+ if(active)
+ say("ERROR: The countdown has aready begun!!!")
+ TIMER_COOLDOWN_START(src, COOLDOWN_BOMB_BUTTON, 3 SECONDS)
+ return
+
+ else if(!istype(inserted_bomb))
+ say("ERROR: No Bomb Inserted")
+ TIMER_COOLDOWN_START(src, COOLDOWN_BOMB_BUTTON, 3 SECONDS)
+ return
+
+
+ else if(!on_reebe(src))
+ say("Beginning detonation sequence. Countdown starting.")
+ alerthere = get_area(src)
+ countdown.start()
+ active = TRUE
+ next_beep = world.time + 1 SECONDS
+ timer = world.time + (default_time)
+ begin_processing()
+ priority_announce("DANGER - Tampering of bluespace ordinance dampeners detected, Resulting explosion may be catastrophic to station integrity. \
+ Remove the tampering device within 7 Minutes or evacuate the localized areas. \
+ Location: [alerthere].", "Doppler Array Detection - DANGER", 'sound/misc/notice3.ogg')
+ TIMER_COOLDOWN_START(src, COOLDOWN_BOMB_BUTTON, 3 SECONDS)
+ return
+ say("UNKNOWN ERROR: Nice try nerd. ")
+ TIMER_COOLDOWN_START(src, COOLDOWN_BOMB_BUTTON, 3 SECONDS)
+ return
+
+//Process for handling the bombs timer
+/obj/machinery/bomb_actualizer/process()
+ var/volume = 40
+ if(!active)
+ end_processing()
+ timer = null
+ next_beep = null
+ countdown.stop()
+ stage = 0
+ return
+ if(!isnull(next_beep) && (next_beep <= world.time))
+
+ playsound(loc, beepsound, volume, FALSE)
+ next_beep = world.time +10
+ if(seconds_remaining() <= 60)
+ if(stage == 0)
+ radio.talk_into(src, "WARNING: DETONATION IN ONE MINUTE.", common_channel)
+ playsound(loc, scarywarning, volume, FALSE)
+ stage++
+ if(seconds_remaining() <= 10)
+ if(stage == 1)
+ radio.talk_into(src, "FAILSAFE DISENGAGED, DETONATION IMMINENT", common_channel)
+ playsound(loc, verybadsound, 80, FALSE)
+ stage++
+
+ if(active && (timer <= world.time))
+ playsound(loc, youdied, 100, FALSE, 45)
+ active = FALSE
+ stage = 0
+ update_appearance()
+ inserted_bomb.toggle_valve(tank_to_target)
+
+
+/obj/machinery/bomb_actualizer/proc/seconds_remaining()
+ if(active)
+ . = max(0, round(timer - world.time) / 10)
+
+ else
+ . = 420
+
+
+
+ /**
+ * catches the parameters of the TTV's explosion as it happens internally,
+ * cancels the explosion and then re-triggers it to happen with modified perameters (such as maxcap = false)
+ * while REDUCING the theoretical size by actualizer multiplier
+ * (actualizer_multiplier 0.25 would mean the 200 size theoretical bomb is only 12 + (200*0.25) in size )
+ */
+/obj/machinery/bomb_actualizer/proc/modify_explosion(atom/source, list/arguments)
+ SIGNAL_HANDLER
+ if(!exploded)
+ var/heavy = arguments[EXARG_KEY_DEV_RANGE]
+ var/medium = arguments[EXARG_KEY_HEAVY_RANGE]
+ var/light = arguments[EXARG_KEY_LIGHT_RANGE]
+ var/flame = 0
+ var/flash = 0
+ var/turf/location = get_turf(src)
+ var/actualizer_multiplier = 0.5
+ var/capped_heavy
+ var/capped_medium
+ var/capped_light
+
+ if(heavy > 12)
+ capped_heavy = (GLOB.MAX_EX_DEVESTATION_RANGE + (heavy * actualizer_multiplier))
+
+ if(medium > 12)
+ capped_medium = (GLOB.MAX_EX_HEAVY_RANGE + (medium * actualizer_multiplier))
+
+ if(light > 12)
+ capped_light = (GLOB.MAX_EX_LIGHT_RANGE + (light * actualizer_multiplier))
+
+ if(capped_light > 200)
+ capped_light = 200
+
+ if(capped_medium > 120)
+ capped_medium = 120
+
+ if(capped_heavy > 60)
+ capped_heavy = 60
+
+ SSexplosions.explode(location, capped_heavy, capped_medium, capped_light, flame, flash, TRUE, TRUE, FALSE, FALSE)
+ exploded = TRUE
+ return COMSIG_CANCEL_EXPLOSION
+ return COMSIG_ATOM_EXPLODE
+
+/obj/machinery/bomb_actualizer/examine(mob/user)
+ . = ..()
+ . += "Big bomb."
+ if(examinable_countdown)
+ . += span_notice("A digital display on it reads \"[seconds_remaining()]\".")
+ if(active)
+ balloon_alert(user, "[seconds_remaining()]")
+ else
+ . += span_notice({"The digital display on it is inactive."})
+
+/obj/machinery/bomb_actualizer/ui_interact(mob/user, datum/tgui/ui)
+ .=..()
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "BombActualizer", name)
+ ui.open()
+
+/obj/machinery/bomb_actualizer/ui_act(action, params)
+ . = ..()
+ if (.)
+ return
+ if(action == "start_timer" && !active)
+ start_detonation()
+
diff --git a/monkestation/code/game/objects/effects/countdown.dm b/monkestation/code/game/objects/effects/countdown.dm
index 4bff884f31ac..f0b805002e7b 100644
--- a/monkestation/code/game/objects/effects/countdown.dm
+++ b/monkestation/code/game/objects/effects/countdown.dm
@@ -11,3 +11,14 @@
var/completion = round(C.get_completion())
return completion
+
+
+/obj/effect/countdown/bomb_actualizer
+ name = "bomb actualizer countdown"
+ color = "#ff9100"
+/obj/effect/countdown/bomb_actualizer/get_value()
+ var/obj/machinery/bomb_actualizer/Bomb_Countdown = attached_to
+ if(!istype(Bomb_Countdown))
+ return
+ else if(Bomb_Countdown.active)
+ return Bomb_Countdown.seconds_remaining()
diff --git a/monkestation/code/game/objects/items/admin_plushies.dm b/monkestation/code/game/objects/items/admin_plushies.dm
new file mode 100644
index 000000000000..61155eeb5b7d
--- /dev/null
+++ b/monkestation/code/game/objects/items/admin_plushies.dm
@@ -0,0 +1,281 @@
+// This is where all of the MonkeStation Admin Plushies SHOULD be stored
+
+// Plushies
+/obj/item/toy/plush/admin
+ name = "admin plushie"
+ desc = "if you're seeing this there's an issue."
+ icon = 'monkestation/icons/obj/admin_plushies.dmi'
+ icon_state = ""
+ /// A string of text that is optionaly added to the objects desc, it SHOULD be the admin's CKEY.
+ var/adminCKey = null
+
+/obj/item/toy/plush/admin/Initialize(mapload)
+ . = ..()
+ if(adminCKey)
+ desc = "[desc]" + " " + "(A member of our beloved admin team- ''[adminCKey]'')"
+ else
+ desc = "[desc]" + " " + "(A member of our beloved admin team)"
+
+/obj/item/toy/plush/admin/ben_mothman
+ name = "ben mothman"
+ desc = "HAH this guy is short! Laugh at him.. this is an order!"
+ icon_state = "ben"
+/datum/loadout_item/toys/ben_mothman
+ name = "Ben Mothman plush"
+ item_path = /obj/item/toy/plush/admin/ben_mothman
+/datum/store_item/toys/ben_mothman
+ name = "Ben Mothman Plush"
+ item_path = /obj/item/toy/plush/admin/ben_mothman
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/abraxis
+ name = "abraxis"
+ desc = "This feller is always up to something.. he's even got that huge company I forgot the name of..."
+ icon_state = "abraxis"
+/datum/loadout_item/toys/abraxis
+ name = "Abraxis Plush"
+ item_path = /obj/item/toy/plush/admin/abraxis
+/datum/store_item/toys/abraxis
+ name = "Abraxis Plush"
+ item_path = /obj/item/toy/plush/admin/abraxis
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/brad
+ name = "brad"
+ desc = "Woah.. they're BLUE, and they've also got a cane! How fancy dancy."
+ icon_state = "brad"
+/datum/loadout_item/toys/brad
+ name = "Brad Plush"
+ item_path = /obj/item/toy/plush/admin/brad
+/datum/store_item/toys/brad
+ name = "Brad Plush"
+ item_path = /obj/item/toy/plush/admin/brad
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/andrea
+ name = "andrea"
+ desc = "Best combat medic around.. if your legs are blown off and you see this fellah comming around- you're lucky."
+ icon_state = "andrea"
+/datum/loadout_item/toys/andrea
+ name = "Andrea Plush"
+ item_path = /obj/item/toy/plush/admin/andrea
+/datum/store_item/toys/andrea
+ name = "Andrea Plush"
+ item_path = /obj/item/toy/plush/admin/andrea
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/pippi
+ name = "pippi"
+ desc = "..."
+ icon_state = "pippi"
+/datum/loadout_item/toys/pippi
+ name = "Pippi Plush"
+ item_path = /obj/item/toy/plush/admin/pippi
+/datum/store_item/toys/pippi
+ name = "Pippi Plush"
+ item_path = /obj/item/toy/plush/admin/pippi
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/syndi_kate
+ name = "syndi-kate"
+ desc = "''GLORY TO THE SYNDICATE!''"
+ icon_state = "syndi_kate"
+/datum/loadout_item/toys/syndi_kate
+ name = "Syndi-Kate Plush"
+ item_path = /obj/item/toy/plush/admin/syndi_kate
+/datum/store_item/toys/syndi_kate
+ name = "Syndi-Kate Plush"
+ item_path = /obj/item/toy/plush/admin/syndi_kate
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/jace
+ name = "jace"
+ desc = "It's Jace!"
+ icon_state = "jace"
+/datum/loadout_item/toys/jace
+ name = "Jace Plush"
+ item_path = /obj/item/toy/plush/admin/jace
+/datum/store_item/toys/jace
+ name = "Jace Plush"
+ item_path = /obj/item/toy/plush/admin/jace
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/lavender
+ name = "lavender"
+ desc = "It's Lavender!"
+ icon_state = "lavender"
+/datum/loadout_item/toys/lavender
+ name = "Lavender Plush"
+ item_path = /obj/item/toy/plush/admin/lavender
+/datum/store_item/toys/lavender
+ name = "Lavender Plush"
+ item_path = /obj/item/toy/plush/admin/lavender
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/waffles
+ name = "waffles"
+ desc = "It's Waffles! What a wierdo!"
+ icon_state = "waffles"
+/datum/loadout_item/toys/waffles
+ name = "Waffles Plush"
+ item_path = /obj/item/toy/plush/admin/waffles
+/datum/store_item/toys/waffles
+ name = "Waffles Plush"
+ item_path = /obj/item/toy/plush/admin/waffles
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/vicky
+ name = "vicky"
+ desc = "It's Vicky!"
+ icon_state = "vicky"
+/datum/loadout_item/toys/vicky
+ name = "Vicky Plush"
+ item_path = /obj/item/toy/plush/admin/vicky
+/datum/store_item/toys/vicky
+ name = "Vicky Plush"
+ item_path = /obj/item/toy/plush/admin/vicky
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/richard_deckard
+ name = "richard deckard"
+ desc = "It's Richard Deckard!"
+ icon_state = "richard_deckard"
+/datum/loadout_item/toys/richard_deckard
+ name = "Richard Deckard Plush"
+ item_path = /obj/item/toy/plush/admin/richard_deckard
+/datum/store_item/toys/richard_deckard
+ name = "Richard Deckard Plush"
+ item_path = /obj/item/toy/plush/admin/richard_deckard
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/marisa
+ name = "marisa"
+ desc = "It's Marisa! THE GOOBER- LOOK AT HER!"
+ icon_state = "marisa"
+/datum/loadout_item/toys/marisa
+ name = "Marisa Plush"
+ item_path = /obj/item/toy/plush/admin/marisa
+/datum/store_item/toys/marisa
+ name = "Marisa Plush"
+ item_path = /obj/item/toy/plush/admin/marisa
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/raziel
+ name = "raziel"
+ desc = "It's Raziel! He smells of bubblegum, and looks like he'll commit arson if you dont watch em."
+ icon_state = "raziel"
+/datum/loadout_item/toys/raziel
+ name = "Raziel Plush"
+ item_path = /obj/item/toy/plush/admin/raziel
+/datum/store_item/toys/raziel
+ name = "Raziel Plush"
+ item_path = /obj/item/toy/plush/admin/raziel
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/gabbie
+ name = "gabbie"
+ desc = "It's Gabbie!"
+ icon_state = "gabbie"
+/datum/loadout_item/toys/gabbie
+ name = "Gabbie Plush"
+ item_path = /obj/item/toy/plush/admin/gabbie
+/datum/store_item/toys/gabbie
+ name = "Gabbie Plush"
+ item_path = /obj/item/toy/plush/admin/gabbie
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/amunsethep
+ name = "amun set hep"
+ desc = "CURSE OF THE SANDS BE UPON YOU!!!"
+ icon_state = "amunsethep"
+/datum/loadout_item/toys/amunsethep
+ name = "Amun Set Hep Plush"
+ item_path = /obj/item/toy/plush/admin/amunsethep
+/datum/store_item/toys/gabbie
+ name = "Amun Set Hep Plush"
+ item_path = /obj/item/toy/plush/admin/amunsethep
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/tendsthefire
+ name = "tends-the-fire"
+ desc = "It's Tends-The-Fire!, what a lovable little lizard!"
+ icon_state = "tendsthefire"
+/datum/loadout_item/toys/tendsthefire
+ name = "Tends-The-Fire Plush"
+ item_path = /obj/item/toy/plush/admin/tendsthefire
+/datum/store_item/toys/tendsthefire
+ name = "Tends-The-Fire Plush"
+ item_path = /obj/item/toy/plush/admin/tendsthefire
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/haileyspire
+ name = "hailey spire"
+ desc = "It's Hailey Spire! They've got a BIG WRENCH- WATCH OUT!!!"
+ icon_state = "haileyspire"
+/datum/loadout_item/toys/haileyspire
+ name = "Hailey Spire Plush"
+ item_path = /obj/item/toy/plush/admin/haileyspire
+/datum/store_item/toys/haileyspire
+ name = "Hailey Spire Plush"
+ item_path = /obj/item/toy/plush/admin/haileyspire
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/haileyspire
+ name = "hailey spire"
+ desc = "It's Hailey Spire! They've got a BIG WRENCH- WATCH OUT!!!"
+ icon_state = "haileyspire"
+/datum/loadout_item/toys/haileyspire
+ name = "Hailey Spire Plush"
+ item_path = /obj/item/toy/plush/admin/haileyspire
+/datum/store_item/toys/haileyspire
+ name = "Hailey Spire Plush"
+ item_path = /obj/item/toy/plush/admin/haileyspire
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/sydneysahrin
+ name = "sydney sahrin"
+ desc = "It's Sydney Sahrin! Shortest plantmin!"
+ icon_state = "sydneysahrin"
+/datum/loadout_item/toys/sydneysahrin
+ name = "Sydney Sahrin Plush"
+ item_path = /obj/item/toy/plush/admin/sydneysahrin
+/datum/store_item/toys/sydneysahrin
+ name = "Sydney Sahrin Plush"
+ item_path = /obj/item/toy/plush/admin/sydneysahrin
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/veth
+ name = "veth"
+ desc = "It's Veth! Suprisingly not upside down!"
+ icon_state = "veth"
+/datum/loadout_item/toys/veth
+ name = "Veth Plush"
+ item_path = /obj/item/toy/plush/admin/veth
+/datum/store_item/toys/veth
+ name = "Veth Plush"
+ item_path = /obj/item/toy/plush/admin/veth
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/cassielpip
+ name = "cassiel pip"
+ desc = "Smelly Rat."
+ icon_state = "cassielpip"
+/datum/loadout_item/toys/cassielpip
+ name = "Cassiel Pip Plush"
+ item_path = /obj/item/toy/plush/admin/cassielpip
+/datum/store_item/toys/cassielpip
+ name = "Cassiel Pip Plush"
+ item_path = /obj/item/toy/plush/admin/cassielpip
+ item_cost = 7500
+
+/obj/item/toy/plush/admin/fortune
+ name = "fortune"
+ desc = "It's Fortune- the Felinid!"
+ icon_state = "fortune"
+/datum/loadout_item/toys/fortune
+ name = "Fortune Plush"
+ item_path = /obj/item/toy/plush/admin/fortune
+/datum/store_item/toys/fortune
+ name = "Fortune Plush"
+ item_path = /obj/item/toy/plush/admin/fortune
+ item_cost = 7500
diff --git a/monkestation/code/game/objects/items/circuitboards/computer_circuitboards.dm b/monkestation/code/game/objects/items/circuitboards/computer_circuitboards.dm
index 305ef9fc449e..02122087ab4e 100644
--- a/monkestation/code/game/objects/items/circuitboards/computer_circuitboards.dm
+++ b/monkestation/code/game/objects/items/circuitboards/computer_circuitboards.dm
@@ -17,3 +17,13 @@
name = "Nanite Cloud Control (Computer Board)"
greyscale_colors = CIRCUIT_COLOR_SCIENCE
build_path = /obj/machinery/computer/nanite_cloud_controller
+
+/obj/item/circuitboard/computer/goldeneye_helm
+ name = "goldeneye cruiser helm"
+ greyscale_colors = CIRCUIT_COLOR_GENERIC
+ build_path = /obj/machinery/computer/shuttle/goldeneye_cruiser
+
+/obj/item/circuitboard/computer/goldeneye_recall
+ name = "goldeneye cruiser recall"
+ greyscale_colors = CIRCUIT_COLOR_GENERIC
+ build_path = /obj/machinery/computer/shuttle/goldeneye_cruiser/recall
diff --git a/monkestation/code/game/objects/items/circuitboards/machine_circuitboards.dm b/monkestation/code/game/objects/items/circuitboards/machine_circuitboards.dm
index f92f715cb3f9..ffc5fe574103 100644
--- a/monkestation/code/game/objects/items/circuitboards/machine_circuitboards.dm
+++ b/monkestation/code/game/objects/items/circuitboards/machine_circuitboards.dm
@@ -84,6 +84,15 @@
. = ..()
. += "Cloud ID is currently set to [cloud_id]."
+/obj/item/circuitboard/machine/bomb_actualizer
+ name = "Bomb Actualizer (Machine Board)"
+ greyscale_colors = CIRCUIT_COLOR_SCIENCE
+ build_path = /obj/machinery/bomb_actualizer
+ req_components = list(
+ /datum/stock_part/manipulator = 1,
+ /datum/stock_part/scanning_module = 1,
+ /datum/stock_part/matter_bin = 5)
+
/obj/item/circuitboard/machine/composters
name = "NT-Brand Auto Composter (Machine Board)"
greyscale_colors = CIRCUIT_COLOR_ENGINEERING
diff --git a/monkestation/code/game/objects/items/plushies.dm b/monkestation/code/game/objects/items/plushies.dm
index 35b82665cc4e..2ceb89b5135c 100644
--- a/monkestation/code/game/objects/items/plushies.dm
+++ b/monkestation/code/game/objects/items/plushies.dm
@@ -61,3 +61,11 @@
icon = 'monkestation/icons/obj/plushes.dmi'
icon_state = "durrcell"
squeak_override = list('monkestation/sound/voice/durrcell-squeak.ogg'=1)
+
+/obj/item/toy/plush/Eeble
+ name = "Eeble plushie"
+ attack_verb_simple = list("Eeble","prybar")
+ attack_verb_continuous = list("Eebles","prybars")
+ desc = "Eeble is Eeble"
+ icon = 'monkestation/icons/obj/plushes.dmi'
+ icon_state = "eeble"
diff --git a/monkestation/code/game/objects/structures/beds_chairs/chair.dm b/monkestation/code/game/objects/structures/beds_chairs/chair.dm
index 83cc49eb7282..e15ba069b2cc 100644
--- a/monkestation/code/game/objects/structures/beds_chairs/chair.dm
+++ b/monkestation/code/game/objects/structures/beds_chairs/chair.dm
@@ -6,6 +6,7 @@
icon_state = "stool"
resistance_flags = FIRE_PROOF
can_buckle = FALSE
+ buildstacktype = /obj/item/stack/sheet/mineral/sandstone
buildstackamount = 1
item_chair = /obj/item/chair/stool/sandstone
diff --git a/monkestation/code/game/objects/structures/gravestones.dm b/monkestation/code/game/objects/structures/gravestones.dm
new file mode 100644
index 000000000000..94a263873e75
--- /dev/null
+++ b/monkestation/code/game/objects/structures/gravestones.dm
@@ -0,0 +1,111 @@
+//These are gravestones for mappers, they are decorations. Nothing more
+/obj/structure/gravestone
+ name = "gravestone"
+ desc = "Rest in piss..."
+ icon = 'monkestation/icons/obj/structures/gravestones.dmi'
+ icon_state = "gravestone"
+ resistance_flags = FIRE_PROOF | ACID_PROOF
+ anchored = TRUE
+ receive_ricochet_chance_mod = 1 // Bullets CANNOT harm the dead
+ max_integrity = 30
+ density = 1
+ var/gravestoneHasRandDesc = TRUE
+ var/list/gravestoneRandList = list(
+ "Got romantic with the supermatter crystal...",
+ "Suffocated in their own piss cube...",
+ "Was wrastled by Hulk Hogan...",
+ "Was turned into a felinid...",
+ "Tripped on their shoelaces setting up the singularity...",
+ "Rode the whip straight into a vending machine...",
+ "Tried to bodyblock the escape shuttle...",
+ "Was a victim of a mild clown prank...",
+ "Was ordered to bite the curb by the Head of Security...",
+ "Gored by a hog...",
+ "Romanced a goliath...",
+ "Tried eating twelve hotdogs at once...",
+ "Stole the captain's spare ID...",
+ "Tied the HoS' shoes together one too many times...",
+ "Hated father, divorced thrice...",
+ "Slipped on a bluespace banana peel, ended up in space...",
+ "Didn't understand what the 'lusty xenomorph maid' was...",
+ "Failed self surgery...",
+ "Let the clown do surgery on them...",
+ "Ate too many maint pills...",
+ "Got locked in the shitter...",
+ "Tried to build a jetpack with a plasma tank...",
+ "Won a drinking contest...",
+ "Didn't realize what 'mass driver' meant...",
+ "Got their head crushed by a blast door...",
+ "Face tanked an RPG...",
+ "Asked the nice man in a red space suit for cash for the vending machine...",
+ "Rocked the vending machine too hard...",
+ "Forgot to eat one apple a day...",
+ "Didn't eat enough fiber...",
+ "Drank the entire contents of the pool...",
+ "Caught a space carp...",
+ "Failed to climb on a table, smashing their head on on the leg...",
+ "Didn't realize 'DANGER:RADIOACTIVE' isn't the name of a band...",
+ "Face-farted the captain in front of the blueshield...",
+ "Dropped a deuce in the captain's bathroom...",
+ "Lubed outside the warden's office...",
+ "Picked a fight with the wrong chicken...",
+ "Thought the crematorium was a tanning booth...",
+ "Suffocated in mime jail...",
+ "Stuck his face over the air supply pipe...",
+ "Got ran over by the MULE bot...",
+ "Lost a boxing match with an APLU mech...",
+ "Put a single toe into cargo...",
+ "Jumped the table into the kitchen...",
+ "Stole from gary...",
+ "Shoved a crayon up their nose...",
+ "Joined a cult...",
+ "Drowned in a piss cube...",
+ "Pissed off the bartender...",
+ "Was devoured by a plushie...",
+ "Was eaten by a grue...",
+ "Didn't know what they wanted before going up to the hopdesk...",
+ "Didn't take a ticket...",
+ "Pulled the tag off a mattress...",
+ "Got shoved in a washing machine...",
+ "Learned what 'ordnance' meant...",
+ "Tried making a meth factory...",
+ "Successfully made a meth factory...",
+ "Made fun of the detective's hat...",
+ "Slipped on a banana peel...",
+ "Played chicken with the escape shuttle...",
+ "Fed the deep fryer a 20 pound bag of ice...",
+ "Had their eyes pecked out by a chicken...",
+ "Killed Poly...",
+ "Was used as target practice by security...",
+ "Stubbed their toe...",
+ "Took a nap in a coffin, was fed into the incinerator...",
+ "Wasn't read their last rites...",
+ "Took too long of a shower...",
+ "Slipped and smashed their head into the bathroom sink...",
+ "Ate all the communal donk pockets...",
+ "Got trapped in the morgue tray...",
+ "Got spooked...",
+ "Became friends with the clown...",
+ "Met jeff...",
+ "Ignored the OSHA warnings...",
+ "Is the reason safety standards were written...",
+ "Wrote most of these lines...",
+ "HELP IM STUCK IN THE HEADSTONE FACTORY!!!"
+ )
+
+/obj/structure/gravestone/Initialize(mapload)
+ . = ..()
+ if(gravestoneHasRandDesc==TRUE)
+ var/deadGender = pick("male","female")
+ if(deadGender == "male")
+ desc = pick(GLOB.first_names_male) + " " + capitalize(pick(GLOB.last_names) + " : " + pick(gravestoneRandList))
+ else
+ desc = pick(GLOB.first_names_female) + " " + capitalize(pick(GLOB.last_names) + " : " + pick(gravestoneRandList))
+
+/obj/structure/gravestone/one
+ icon_state = "gravestone1"
+ desc = "Mai waife..."
+
+/obj/structure/gravestone/two
+ icon_state = "gravestone2"
+ desc = "It's Pee Puddle Pendrick! The infamous bandit..."
diff --git a/monkestation/code/game/world.dm b/monkestation/code/game/world.dm
new file mode 100644
index 000000000000..adea6c02f0a7
--- /dev/null
+++ b/monkestation/code/game/world.dm
@@ -0,0 +1,39 @@
+/world/SetupLogs()
+ . = ..()
+ set_db_log_directory()
+
+/proc/set_db_log_directory()
+ set waitfor = FALSE
+ if(!GLOB.round_id || !SSdbcore.IsConnected())
+ return
+ var/datum/db_query/set_log_directory = SSdbcore.NewQuery({"
+ UPDATE [format_table_name("round")]
+ SET
+ `log_directory` = :log_directory
+ WHERE
+ `id` = :round_id
+ "}, list("log_directory" = GLOB.log_directory, "round_id" = GLOB.round_id))
+ set_log_directory.Execute()
+ QDEL_NULL(set_log_directory)
+
+/proc/get_log_directory_by_round_id(round_id)
+ if(!isnum(round_id) || round_id <= 0 || !SSdbcore.IsConnected())
+ return
+ var/datum/db_query/query_log_directory = SSdbcore.NewQuery({"
+ SELECT `log_directory`
+ FROM
+ [format_table_name("round")]
+ WHERE
+ `id` = :round_id
+ "}, list("round_id" = round_id))
+ if(!query_log_directory.warn_execute())
+ qdel(query_log_directory)
+ return
+ if(!query_log_directory.NextRow())
+ qdel(query_log_directory)
+ CRASH("Failed to get log directory for round [round_id]")
+ var/log_directory = query_log_directory.item[1]
+ QDEL_NULL(query_log_directory)
+ if(!rustg_file_exists(log_directory))
+ CRASH("Log directory '[log_directory]' for round ID [round_id] doesn't exist!")
+ return log_directory
diff --git a/monkestation/code/modules/antagonists/_common/antag_datum.dm b/monkestation/code/modules/antagonists/_common/antag_datum.dm
index 5995594cf332..177b48b11b54 100644
--- a/monkestation/code/modules/antagonists/_common/antag_datum.dm
+++ b/monkestation/code/modules/antagonists/_common/antag_datum.dm
@@ -1,6 +1,4 @@
/datum/antagonist
- /// Allows antags to check exploitable info
- antag_flags = FLAG_CAN_SEE_EXPOITABLE_INFO
///The list of keys that are valid to see our antag hud/of huds we can see
var/list/hud_keys
/// If this antagonist should be removed from the crew manifest upon gain.
diff --git a/monkestation/code/modules/antagonists/borers/code/antagonist_stuff/antagonist_datum.dm b/monkestation/code/modules/antagonists/borers/code/antagonist_stuff/antagonist_datum.dm
index e103f421d89a..d328bd1f0654 100644
--- a/monkestation/code/modules/antagonists/borers/code/antagonist_stuff/antagonist_datum.dm
+++ b/monkestation/code/modules/antagonists/borers/code/antagonist_stuff/antagonist_datum.dm
@@ -7,15 +7,15 @@
count_against_dynamic_roll_chance = FALSE // there are thousands of them, we do not need them to be the only antagonist
prevent_roundtype_conversion = FALSE
show_to_ghosts = TRUE
+
/// Our linked borer, used for the antagonist panel TGUI
var/mob/living/basic/cortical_borer/cortical_owner
+ /// Borer mob type, used for antag token spawns.
+ var/borer_mob_type = /mob/living/basic/cortical_borer/neutered
+
/datum/antagonist/cortical_borer/antag_token(datum/mind/hosts_mind, mob/spender)
var/list/vents = list()
- if(isliving(spender) && hosts_mind)
- hosts_mind.current.unequip_everything()
- new /obj/effect/holy(hosts_mind.current.loc)
- QDEL_IN(hosts_mind.current, 20)
for(var/obj/machinery/atmospherics/components/unary/vent_pump/temp_vent as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/atmospherics/components/unary/vent_pump))
if(QDELETED(temp_vent))
continue
@@ -29,14 +29,20 @@
vents += temp_vent
if(!length(vents))
- message_admins("Spawning in as a borer failed!")
+ message_admins(span_adminnotice("[spender] ([ckey(spender.key)]) tried spawning in as a borer, but no suitable vents were found!"))
return MAP_ERROR
+ if(isliving(spender) && hosts_mind)
+ hosts_mind.current.unequip_everything()
+ new /obj/effect/holy(hosts_mind.current.loc)
+ QDEL_IN(hosts_mind.current, 1 SECOND)
+
var/mob/dead/observer/new_borer = spender
- var/turf/vent_turf = get_turf(pick(vents))
- var/mob/living/basic/cortical_borer/spawned_cb = new(vent_turf)
+ var/vent = pick(vents)
+ var/mob/living/basic/cortical_borer/spawned_cb = new borer_mob_type(get_turf(vent))
+ spawned_cb.move_into_vent(vent)
spawned_cb.ckey = new_borer.ckey
- spawned_cb.mind.add_antag_datum(/datum/antagonist/cortical_borer/hivemind)
+ spawned_cb.mind.add_antag_datum(type)
notify_ghosts(
"Someone has become a borer due to spending an antag token ([spawned_cb])!",
source = spawned_cb,
@@ -56,6 +62,8 @@
/datum/antagonist/cortical_borer/hivemind
roundend_category = "cortical borers"
+ borer_mob_type = /mob/living/basic/cortical_borer
+
/// The team of borers
var/datum/team/cortical_borers/borers
diff --git a/monkestation/code/modules/antagonists/borers/code/antagonist_stuff/midround_event.dm b/monkestation/code/modules/antagonists/borers/code/antagonist_stuff/midround_event.dm
index 8ac51f258a50..33dcd8813e06 100644
--- a/monkestation/code/modules/antagonists/borers/code/antagonist_stuff/midround_event.dm
+++ b/monkestation/code/modules/antagonists/borers/code/antagonist_stuff/midround_event.dm
@@ -68,8 +68,9 @@
for(var/repeating_code in 1 to choosing_number)
var/mob/dead/observer/new_borer = pick(candidates)
candidates -= new_borer
- var/turf/vent_turf = get_turf(pick(vents))
- var/mob/living/basic/cortical_borer/spawned_cb = new /mob/living/basic/cortical_borer(vent_turf)
+ var/vent = pick(vents)
+ var/mob/living/basic/cortical_borer/spawned_cb = new /mob/living/basic/cortical_borer(get_turf(vent))
+ spawned_cb.move_into_vent(vent)
spawned_cb.ckey = new_borer.ckey
spawned_cb.mind.add_antag_datum(/datum/antagonist/cortical_borer/hivemind)
announce_to_ghosts(spawned_cb)
diff --git a/monkestation/code/modules/antagonists/borers/code/items/borer_spawner.dm b/monkestation/code/modules/antagonists/borers/code/items/borer_spawner.dm
index 8aeaf618a23f..5c49fadb2ce9 100644
--- a/monkestation/code/modules/antagonists/borers/code/items/borer_spawner.dm
+++ b/monkestation/code/modules/antagonists/borers/code/items/borer_spawner.dm
@@ -89,8 +89,8 @@
action = NOTIFY_ORBIT,
header = "Someone just got a new friend!"
)
- message_admins("[ADMIN_LOOKUPFLW(new_mob)] has been made into a borer via a traitor item used by [user]")
- log_game("[key_name(new_mob)] was spawned as a borer by [key_name(user)]")
+ message_admins("[ADMIN_LOOKUPFLW(new_mob)] has been made into a borer via a traitor item used by [user].")
+ log_game("[key_name(new_mob)] was spawned as a borer by [key_name(user)].")
visible_message("A borer wriggles out of the [src]!")
var/obj/item/cortical_cage/empty_cage = new(drop_location())
diff --git a/monkestation/code/modules/antagonists/brother/gear/misc.dm b/monkestation/code/modules/antagonists/brother/gear/misc.dm
new file mode 100644
index 000000000000..a937231b73d6
--- /dev/null
+++ b/monkestation/code/modules/antagonists/brother/gear/misc.dm
@@ -0,0 +1,5 @@
+/datum/bb_gear/money_tree_seeds
+ name = "Money Tree Seeds"
+ desc = "Contains a pack of seeds of the rare money tree."
+ spawn_path = /obj/item/seeds/tree/money
+ preview_path = /obj/item/seeds/tree/money
diff --git a/monkestation/code/modules/antagonists/brother/gear/recipes.dm b/monkestation/code/modules/antagonists/brother/gear/recipes.dm
index a9693a5ef6e2..4d5f035eff0c 100644
--- a/monkestation/code/modules/antagonists/brother/gear/recipes.dm
+++ b/monkestation/code/modules/antagonists/brother/gear/recipes.dm
@@ -41,6 +41,6 @@
/datum/bb_gear/granter/elance
name = "Recipe: Explosive Lance (Grenade)"
desc = "Contains a recipe book, allowing you to learn the knowledge to build an explosive lance (grenade)."
- spawn_path = /obj/item/spear/explosive
- preview_path = /obj/item/book/granter/crafting_recipe/maint_gun/explosive_lance
+ spawn_path = /obj/item/book/granter/crafting_recipe/maint_gun/explosive_lance
+ preview_path = /obj/item/spear/explosive
diff --git a/monkestation/code/modules/antagonists/changeling/powers/teratomas.dm b/monkestation/code/modules/antagonists/changeling/powers/teratomas.dm
new file mode 100644
index 000000000000..013f2445a236
--- /dev/null
+++ b/monkestation/code/modules/antagonists/changeling/powers/teratomas.dm
@@ -0,0 +1,49 @@
+/datum/action/changeling/teratoma
+ name = "Birth Teratoma"
+ desc = "Our form divides, creating an egg that will soon hatch into a living tumor, fixated on causing mayhem."
+ helptext = "The tumor will not be loyal to us or our cause. Requires 3 changeling absorptions."
+ button_icon_state = "spread_infestation"
+ chemical_cost = 60
+ dna_cost = 2
+ req_absorbs = 3
+
+/datum/action/changeling/teratoma/sting_action(mob/living/user)
+ ..()
+ if(create_teratoma(user))
+ playsound(user.loc, 'sound/effects/blobattack.ogg', 50, 1)
+ user.spawn_gibs()
+ user.visible_message(span_danger("Something horrible bursts out of [user]'s chest!"), \
+ span_danger("Living teratoma bursts out of your chest!"), \
+ span_hear("You hear flesh tearing!"), COMBAT_MESSAGE_RANGE)
+ return FALSE //create_teratoma() handles the chemicals anyway so there is no reason to take them again
+
+/datum/action/changeling/teratoma/proc/create_teratoma(mob/living/user)
+ var/datum/antagonist/changeling/ling = user?.mind?.has_antag_datum(/datum/antagonist/changeling)
+ if(!ling)
+ return FALSE
+ ling.adjust_chemicals(-chemical_cost)
+ var/list/candidates = SSpolling.poll_ghost_candidates(
+ "Do you want to play as a living teratoma?",
+ poll_time = 7.5 SECONDS,
+ ignore_category = POLL_IGNORE_TERATOMA,
+ alert_pic = /datum/antagonist/teratoma,
+ role_name_text = "living teratoma",
+ chat_text_border_icon = /datum/antagonist/teratoma,
+ )
+ if(!length(candidates)) //if we got at least one candidate, they're teratoma now
+ to_chat(user, span_warning("You fail at creating a tumor. Perhaps you should try again later?"))
+ ling.adjust_chemicals(chemical_cost)
+ return FALSE
+ var/mob/dead/observer/candidate = pick(candidates)
+ if(QDELETED(candidate))
+ to_chat(user, span_warning("You fail at creating a tumor. Perhaps you should try again later?"))
+ ling.adjust_chemicals(chemical_cost)
+ return FALSE
+ var/mob/living/carbon/human/species/teratoma/goober = new(user.drop_location())
+ goober.key = candidate.key
+ if(!goober.mind)
+ goober.mind_initialize()
+ goober.mind.add_antag_datum(/datum/antagonist/teratoma)
+ to_chat(goober, span_notice("You burst out from [user]'s chest!"))
+ SEND_SOUND(goober, sound('sound/effects/blobattack.ogg'))
+ return TRUE
diff --git a/monkestation/code/modules/antagonists/clock_cult/antag_datums/clock_cultist.dm b/monkestation/code/modules/antagonists/clock_cult/antag_datums/clock_cultist.dm
index 3c4ff85f2aa8..22beb4158965 100644
--- a/monkestation/code/modules/antagonists/clock_cult/antag_datums/clock_cultist.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/antag_datums/clock_cultist.dm
@@ -203,6 +203,7 @@
/datum/antagonist/clock_cultist/eminence
name = "Eminence"
+ antag_flags = parent_type::antag_flags | FLAG_ANTAG_CAP_IGNORE
give_slab = FALSE
antag_moodlet = null
communicate = null
diff --git a/monkestation/code/modules/antagonists/clock_cult/scriptures/_scripture.dm b/monkestation/code/modules/antagonists/clock_cult/scriptures/_scripture.dm
index e0baa183f696..2c351d6d4837 100644
--- a/monkestation/code/modules/antagonists/clock_cult/scriptures/_scripture.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/scriptures/_scripture.dm
@@ -404,7 +404,7 @@ GLOBAL_LIST_EMPTY(clock_scriptures_by_type)
return ..()
-/datum/action/cooldown/spell/pointed/slab/InterceptClickOn(mob/living/caller, params, atom/target)
+/datum/action/cooldown/spell/pointed/slab/InterceptClickOn(mob/living/user, params, atom/target)
parent_scripture?.click_on(target)
/// Generate all scriptures in a global assoc of name:ref. Only needs to be done once
diff --git a/monkestation/code/modules/antagonists/evil_clone/evil_clone.dm b/monkestation/code/modules/antagonists/evil_clone/evil_clone.dm
index a3b4bc9591db..f9616c0fa198 100644
--- a/monkestation/code/modules/antagonists/evil_clone/evil_clone.dm
+++ b/monkestation/code/modules/antagonists/evil_clone/evil_clone.dm
@@ -5,6 +5,7 @@
antagpanel_category = "Evil Clones"
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
+ antag_flags = parent_type::antag_flags | FLAG_ANTAG_CAP_IGNORE
/datum/antagonist/evil_clone/greet()
. = ..()
diff --git a/monkestation/code/modules/antagonists/florida_man/_florida_man.dm b/monkestation/code/modules/antagonists/florida_man/_florida_man.dm
index 50cc0f23803d..de4b5df4888b 100644
--- a/monkestation/code/modules/antagonists/florida_man/_florida_man.dm
+++ b/monkestation/code/modules/antagonists/florida_man/_florida_man.dm
@@ -52,9 +52,7 @@
owner.current.playsound_local(get_turf(owner.current), 'monkestation/sound/ambience/antag/floridaman.ogg',100,0, use_reverb = FALSE)
to_chat(owner, "You are THE Florida Man!\nYou're not quite sure how you got out here in space, but you don't generally bother thinking about things.\n\nYou love methamphetamine!\nYou love wrestling lizards!\nYou love getting drunk!\nYou love sticking it to THE MAN!\nYou don't act with any coherent plan or objective.\nYou don't outright want to destroy the station or murder people, as you have no home to return to.\n\nGo forth, son of Space Florida, and sow chaos!")
owner.announce_objectives()
- if(!prob(1)) // 1% chance to be Tony Brony...because meme references to streams are good!
- floridan.fully_replace_character_name(newname = "Florida Man")
- else
+ if(prob(1)) // 1% chance to be Tony Brony...because meme references to streams are good!
floridan.fully_replace_character_name(newname = "Tony Brony")
/datum/antagonist/florida_man/antag_token(datum/mind/hosts_mind, mob/spender)
diff --git a/monkestation/code/modules/antagonists/teratoma/teratoma.dm b/monkestation/code/modules/antagonists/teratoma/teratoma.dm
new file mode 100644
index 000000000000..653b291c8461
--- /dev/null
+++ b/monkestation/code/modules/antagonists/teratoma/teratoma.dm
@@ -0,0 +1,54 @@
+/datum/team/teratoma
+ name = "Teratomas"
+ member_name = "teratoma"
+
+/datum/antagonist/teratoma
+ name = "\improper Teratoma"
+ show_in_antagpanel = TRUE
+ antagpanel_category = ANTAG_GROUP_ABOMINATIONS
+ show_to_ghosts = TRUE
+ var/datum/team/teratoma/team
+
+/datum/antagonist/teratoma/greet()
+ var/list/parts = list()
+ parts += span_big("You are a living teratoma!")
+ parts += span_changeling("By all means, you should not exist. Your very existence is a sin against nature itself.")
+ parts += span_changeling("You are loyal to nobody, except the forces of chaos itself.")
+ parts += span_info("You are able to easily vault tables and ventcrawl, however you cannot use many things like guns, batons, and you are also illiterate and quite fragile.")
+ parts += span_hypnophrase("Spread misery and chaos upon the station.")
+ to_chat(owner.current, examine_block(parts.Join("\n")), type = MESSAGE_TYPE_INFO)
+
+/datum/antagonist/teratoma/can_be_owned(datum/mind/new_owner)
+ if(!isteratoma(new_owner.current))
+ return FALSE
+ return ..()
+
+/datum/antagonist/teratoma/create_team(datum/team/teratoma/new_team)
+ var/static/datum/team/teratoma/main_teratoma_team
+ if(!new_team)
+ if(!main_teratoma_team)
+ main_teratoma_team = new
+ main_teratoma_team.add_objective(new /datum/objective/teratoma)
+ new_team = main_teratoma_team
+ if(!istype(new_team))
+ stack_trace("Wrong team type passed to [type] initialization.")
+ team = new_team
+ objectives |= team.objectives
+
+/datum/antagonist/teratoma/get_team()
+ return team
+
+/datum/antagonist/teratoma/get_base_preview_icon()
+ RETURN_TYPE(/icon)
+ var/static/icon/teratoma_icon
+ if(!teratoma_icon)
+ var/mob/living/carbon/human/species/teratoma/teratoma = new
+ teratoma.setDir(SOUTH)
+ teratoma_icon = getFlatIcon(teratoma, no_anim = TRUE)
+ QDEL_NULL(teratoma)
+ return teratoma_icon
+
+/datum/objective/teratoma
+ name = "Spread misery and chaos"
+ explanation_text = "Spread misery and chaos upon the station."
+ completed = TRUE
diff --git a/monkestation/code/modules/antagonists/traitor/objectives/kidnapping.dm b/monkestation/code/modules/antagonists/traitor/objectives/kidnapping.dm
index 436ce5f3c9da..7b716141997c 100644
--- a/monkestation/code/modules/antagonists/traitor/objectives/kidnapping.dm
+++ b/monkestation/code/modules/antagonists/traitor/objectives/kidnapping.dm
@@ -21,7 +21,7 @@
var/list/target_belongings = list()
/datum/traitor_objective/target_player/kidnapping/common
- progression_reward = list(2 MINUTES, 4 MINUTES)
+ progression_reward = list(10 MINUTES, 15 MINUTES)
telecrystal_reward = list(2, 3)
target_jobs = list(
// Cargo
@@ -57,7 +57,7 @@
telecrystal_reward = 3 //go bully the assistants
/datum/traitor_objective/target_player/kidnapping/uncommon //Hard to fish out targets
- progression_reward = list(4 MINUTES, 8 MINUTES)
+ progression_reward = list(15 MINUTES, 20 MINUTES)
telecrystal_reward = list(3, 4)
given_contractor_rep = 2
@@ -75,7 +75,7 @@
alive_bonus = 4
/datum/traitor_objective/target_player/kidnapping/rare
- progression_reward = list(8 MINUTES, 12 MINUTES)
+ progression_reward = list(20 MINUTES, 25 MINUTES)
telecrystal_reward = list(4, 5)
given_contractor_rep = 3
@@ -93,7 +93,7 @@
alive_bonus = 5
/datum/traitor_objective/target_player/kidnapping/captain
- progression_reward = list(12 MINUTES, 16 MINUTES)
+ progression_reward = list(25 MINUTES, 30 MINUTES)
telecrystal_reward = list(5, 6)
given_contractor_rep = 4
diff --git a/monkestation/code/modules/antagonists/wizard/equipment/artefact.dm b/monkestation/code/modules/antagonists/wizard/equipment/artefact.dm
index 6d984316c65d..47f0a3cafbec 100644
--- a/monkestation/code/modules/antagonists/wizard/equipment/artefact.dm
+++ b/monkestation/code/modules/antagonists/wizard/equipment/artefact.dm
@@ -27,6 +27,33 @@
reagents.add_reagent_list(list_reagents)
return
+//wizard bio suit
+/obj/item/clothing/head/wizard/bio_suit
+ name = "gem encrusted bio hood"
+ desc = "A hood that protects the head and face from biological contaminants. It's covered in small gemstones."
+ icon = 'monkestation/icons/obj/clothing/head/bio.dmi'
+ icon_state = "bio_wizard"
+ worn_icon = 'monkestation/icons/mob/clothing/head/bio.dmi'
+ worn_icon_state = "bio_wizard"
+ inhand_icon_state = "bio_hood"
+ clothing_flags = THICKMATERIAL | BLOCK_GAS_SMOKE_EFFECT | SNUG_FIT | PLASMAMAN_HELMET_EXEMPT | HEADINTERNALS | CASTING_CLOTHES
+ flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEHAIR|HIDEFACIALHAIR|HIDEFACE|HIDESNOUT
+ flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH | PEPPERPROOF
+
+/obj/item/clothing/suit/wizrobe/bio_suit
+ name = "gem encrusted bio suit"
+ desc = "A suit that protects against biological contamination. It's covered in small gemstones."
+ icon = 'monkestation/icons/obj/clothing/suits/bio.dmi'
+ icon_state = "bio_wizard"
+ worn_icon = 'monkestation/icons/mob/clothing/suits/bio.dmi'
+ worn_icon_state = "bio_wizard"
+ inhand_icon_state = "bio_suit"
+ clothing_flags = THICKMATERIAL | CASTING_CLOTHES
+ body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
+ flags_inv = HIDEGLOVES|HIDEJUMPSUIT
+ strip_delay = 7 SECONDS
+ equip_delay_other = 7 SECONDS
+
//reactive talisman
#define REACTION_COOLDOWN_DURATION 10 SECONDS
/obj/item/clothing/neck/neckless/wizard_reactive //reactive armor for wizards that casts a spell when it reacts
@@ -125,11 +152,39 @@
desc = "An artifact that when inserted into a spellbook increases its power by 100."
value = 100
-/obj/item/spellbook_charge/afterattack(obj/item/spellbook/book, mob/user)
+/obj/item/spellbook_charge/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/charge_adjuster, type_to_charge_to = /obj/item/spellbook, charges_given = value, called_proc_name = TYPE_PROC_REF(/obj/item/spellbook, adjust_charge))
+
+//wizard shield charges
+#define ADDED_MAX_CHARGE 50
+#define MAX_CHARGES_ABSORBED 3
+/obj/item/wizard_armour_charge/Initialize(mapload)
. = ..()
- if(!istype(book))
- to_chat(user, "The charge can only increase the power of spellbooks!")
+ AddComponent(/datum/component/charge_adjuster, type_to_charge_to = /obj/item/spellbook, charges_given = 1, called_proc_name = TYPE_PROC_REF(/obj/item/spellbook, adjust_charge))
+
+/obj/item/wizard_armour_charge/pre_attack(atom/A, mob/living/user, params)
+ . = ..()
+ if(.)
return
- book.uses += value
- to_chat(user, "You increase the power of the spellbook by [value] points.")
- qdel(src)
+
+ var/obj/item/mod/module/energy_shield/wizard/shield = istype(A, /obj/item/mod/module/energy_shield/wizard) || locate(/obj/item/mod/module/energy_shield/wizard) in A.contents
+ if(shield)
+ if(isnum(shield))
+ shield = A
+ if(shield.max_charges >= (initial(shield.max_charges) + (ADDED_MAX_CHARGE * MAX_CHARGES_ABSORBED)))
+ balloon_alert(user, "\The [shield] cannot take more charges, you can put this back into your spellbook to refund it.")
+ return TRUE
+
+ shield.max_charges += ADDED_MAX_CHARGE
+ var/datum/component/shielded/shield_comp = shield.mod?.GetComponent(/datum/component/shielded)
+ if(shield_comp)
+ shield_comp.max_charges += ADDED_MAX_CHARGE
+ shield_comp.current_charges += (ADDED_MAX_CHARGE - initial(shield_comp.charge_recovery))
+ qdel(src) //should still be able to finish the attack chain
+
+#undef ADDED_MAX_CHARGE
+#undef MAX_CHARGES_ABSORBED
+
+/obj/item/mod/module/energy_shield/wizard
+ lose_multiple_charges = TRUE //I dont think we have anything else that uses this var, so all the numbers for this are subject to change
diff --git a/monkestation/code/modules/antagonists/wizard/equipment/mirror_shield.dm b/monkestation/code/modules/antagonists/wizard/equipment/mirror_shield.dm
new file mode 100644
index 000000000000..59cee2c7e692
--- /dev/null
+++ b/monkestation/code/modules/antagonists/wizard/equipment/mirror_shield.dm
@@ -0,0 +1,124 @@
+#define PROJECTILE_HIT_EFFECT_CHANCE 80
+#define NORMAL_BLOCK_CHANCE 30
+#define REACTION_MODE_ABSORB 0
+#define REACTION_MODE_REFLECT 1
+
+//a "shield" that can absorb projectiles and then shoot them back at attackers
+/obj/item/gun/magic/mirror_shield
+ name = "mirror shield"
+ desc = "A strange mirror adorned with various gemstones. If you look close enough it almost seems as if the surface is... rippling?"
+ icon = 'monkestation/icons/obj/weapons/shields.dmi'
+ icon_state = "wizard_mirror_shield"
+ inhand_icon_state = "wizard_mirror_shield"
+ lefthand_file = 'monkestation/icons/mob/inhands/equipment/shields_lefthand.dmi'
+ righthand_file = 'monkestation/icons/mob/inhands/equipment/shields_righthand.dmi'
+ worn_icon = 'monkestation/icons/mob/clothing/back.dmi'
+ worn_icon_state = "wizard_mirror_shield"
+ force = 16
+ slot_flags = ITEM_SLOT_BACK
+ w_class = WEIGHT_CLASS_BULKY
+ attack_verb_continuous = list("bumps", "prods")
+ attack_verb_simple = list("bump", "prod")
+ hitsound = 'sound/weapons/smash.ogg'
+ fire_sound = 'sound/magic/cosmic_expansion.ogg'
+ ammo_type = /obj/item/ammo_casing/mirror_shield_dummy
+ can_charge = TRUE
+ ///Up to how many projectiles can we "have stored"
+ var/max_stored_projectiles = 10
+ ///Do we absorb or reflect projectiles when hit
+ var/reaction_mode = REACTION_MODE_ABSORB
+ ///The list of projectiles we have stored ready to fire
+ var/list/stored_projectiles = list()
+ ///Cannot absorb projectile types in here
+ var/static/list/blacklisted_projectile_types = list()
+
+/obj/item/gun/magic/mirror_shield/Initialize(mapload)
+ . = ..()
+ STOP_PROCESSING(SSobj, src) //we want can_charge set to TRUE but dont actually use the processing it gives so just disable it
+
+/obj/item/gun/magic/mirror_shield/Destroy()
+ for(var/projectile in stored_projectiles)
+ qdel(projectile) //could also have them shoot off in random directions
+ stored_projectiles -= projectile
+ return ..()
+
+/obj/item/gun/magic/mirror_shield/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
+ . = ..()
+ if(ismob(old_loc))
+ UnregisterSignal(old_loc, COMSIG_PROJECTILE_PREHIT)
+
+ if(ismob(loc))
+ RegisterSignal(loc, COMSIG_PROJECTILE_PREHIT, PROC_REF(handle_hit))
+
+/obj/item/gun/magic/mirror_shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text, final_block_chance, damage, attack_type)
+ if(attack_type != PROJECTILE_ATTACK && prob(NORMAL_BLOCK_CHANCE))
+ return TRUE
+
+/obj/item/gun/magic/mirror_shield/attack_self(mob/user, modifiers)
+ . = ..()
+ reaction_mode = !reaction_mode
+ balloon_alert(user, "you hold \the [src] in such a way as to [reaction_mode == REACTION_MODE_ABSORB ? "absorb" : "reflect"] projectiles.")
+
+/obj/item/gun/magic/mirror_shield/examine(mob/user)
+ . = ..()
+ if(HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED))
+ . += " It currently contains: [english_list(stored_projectiles, comma_text = ", ")]."
+
+/obj/item/gun/magic/mirror_shield/recharge_newshot()
+ if(!chambered.loaded_projectile && length(stored_projectiles))
+ var/obj/projectile/loaded = stored_projectiles[1]
+ loaded.forceMove(chambered)
+ chambered.loaded_projectile = loaded
+ stored_projectiles -= loaded
+
+/obj/item/gun/magic/mirror_shield/can_shoot()
+ return chambered.loaded_projectile
+
+/obj/item/gun/magic/mirror_shield/handle_chamber(mob/living/user, empty_chamber, from_firing, chamber_next_round)
+ recharge_newshot()
+
+/obj/item/gun/magic/mirror_shield/proc/absorb_projectile(obj/projectile/absorbed)
+ STOP_PROCESSING(SSprojectiles, absorbed)
+ absorbed.fired = FALSE
+ QDEL_NULL(absorbed.trajectory)
+ if(!chambered.loaded_projectile)
+ absorbed.forceMove(chambered)
+ chambered.loaded_projectile = absorbed
+ else
+ absorbed.forceMove(src)
+ stored_projectiles += absorbed
+ absorbed.update_appearance()
+ visible_message(span_notice("\The [src] absorbs [absorbed]!"))
+
+/obj/item/gun/magic/mirror_shield/proc/handle_hit(mob/held_by, list/projectile_args, obj/projectile/hit_by)
+ SIGNAL_HANDLER
+ if(!prob((src in held_by.held_items) ? PROJECTILE_HIT_EFFECT_CHANCE : NORMAL_BLOCK_CHANCE)) //turns out its harder to block with something when your not holding it
+ return
+
+ hit_by.impacted = list()
+ var/turf/firer_turf = get_turf(hit_by.firer)
+ if(hit_by.firer && get_dist(firer_turf, get_turf(src)) <= 1) //this is due to some jank I cant figure out, if you want to go ahead
+ hit_by.process_hit(firer_turf, hit_by.firer)
+ else if(reaction_mode == REACTION_MODE_ABSORB && length(stored_projectiles) <= max_stored_projectiles && !(hit_by.type in blacklisted_projectile_types))
+ absorb_projectile(hit_by)
+ else
+ hit_by.set_angle_centered(get_angle(held_by, hit_by.firer))
+ hit_by.firer = held_by
+ hit_by.speed *= 0.8
+ hit_by.damage *= 1.15
+
+ playsound(src, 'sound/magic/cosmic_expansion.ogg', vol = 120, channel = CHANNEL_SOUND_EFFECTS)
+ return PROJECTILE_INTERRUPT_HIT | PROJECTILE_INTERRUPT_BLOCK_QDEL
+
+//a dummy casing type to get filled with absorbed projectiles
+/obj/item/ammo_casing/mirror_shield_dummy
+ loaded_projectile = null
+ firing_effect_type = null
+
+/obj/item/ammo_casing/mirror_shield_dummy/newshot()
+ return
+
+#undef PROJECTILE_HIT_EFFECT_CHANCE
+#undef NORMAL_BLOCK_CHANCE
+#undef REACTION_MODE_ABSORB
+#undef REACTION_MODE_REFLECT
diff --git a/monkestation/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm b/monkestation/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm
index 4199e85c6e73..f6db030d5c46 100644
--- a/monkestation/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm
+++ b/monkestation/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm
@@ -1,13 +1,34 @@
/datum/spellbook_entry/item/magical_chemsprayer
name = "Magic Chem Sprayer"
- desc = "A magic chemical sprayer that will fill itself with unlimited random chemicals."
+ desc = "A magic chemical sprayer that will fill itself with unlimited random chemicals. Now with protective gear!"
item_path = /obj/item/reagent_containers/spray/chemsprayer/magical
category = "Defensive"
cost = 1
+//if still too weak I could also give galoshes
+/datum/spellbook_entry/item/magical_chemsprayer/try_equip_item(mob/living/carbon/human/user, obj/item/to_equip)
+ . = ..()
+ var/turf/user_turf = get_turf(user)
+ if(!user_turf)
+ return
+
+ for(var/obj/item/thing as anything in list(/obj/item/clothing/gloves/combat/wizard, /obj/item/clothing/head/wizard/bio_suit, /obj/item/clothing/suit/wizrobe/bio_suit))
+ thing = new thing(user_turf)
+ user.equip_to_appropriate_slot(thing)
+
/datum/spellbook_entry/item/reactive_talisman
name = "Reactive Talisman"
desc = "An enchanted talisman that has a chance to cast a spell if it's wearer is hit."
item_path = /obj/item/clothing/neck/neckless/wizard_reactive
category = "Defensive"
cost = 1
+
+/datum/spellbook_entry/item/mirror_shield
+ name = "Mirror Shield"
+ desc = "A mirror that will absorb projectiles shot into it to later be shot back out at your convenience."
+ item_path = /obj/item/gun/magic/mirror_shield
+ category = "Defensive"
+ cost = 2
+
+/datum/spellbook_entry/item/armor
+ cost = 1
diff --git a/monkestation/code/modules/antagonists/wizard/equipment/wizard_spellbook.dm b/monkestation/code/modules/antagonists/wizard/equipment/wizard_spellbook.dm
new file mode 100644
index 000000000000..bc0348f39c1e
--- /dev/null
+++ b/monkestation/code/modules/antagonists/wizard/equipment/wizard_spellbook.dm
@@ -0,0 +1,4 @@
+/obj/item/spellbook/proc/adjust_charge(adjust_by)
+ log_spellbook("[src] charges adjusted by [adjust_by]. [usr ? "user: [usr]." : ""]")
+ uses += adjust_by
+ return TRUE
diff --git a/monkestation/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm b/monkestation/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm
index c0da0a38941d..10685cd7c179 100644
--- a/monkestation/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm
+++ b/monkestation/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm
@@ -6,4 +6,12 @@
///How many times this rune needs to be invoked to complete
var/invokes_needed = GRAND_RUNE_INVOKES_TO_COMPLETE
+/obj/effect/grand_rune/add_channel_effect(mob/living/user)
+ . = ..()
+ ADD_TRAIT(user, TRAIT_MOVE_FLYING, REF(src))
+
+/obj/effect/grand_rune/remove_channel_effect(mob/living/user)
+ . = ..()
+ REMOVE_TRAIT(user, TRAIT_MOVE_FLYING, REF(src))
+
#undef GRAND_RUNE_INVOKES_TO_COMPLETE
diff --git a/monkestation/code/modules/art_sci_overrides/activators/_base.dm b/monkestation/code/modules/art_sci_overrides/activators/_base.dm
index 57c25345fc73..34f1ecd81594 100644
--- a/monkestation/code/modules/art_sci_overrides/activators/_base.dm
+++ b/monkestation/code/modules/art_sci_overrides/activators/_base.dm
@@ -5,12 +5,16 @@
var/required_stimuli = NONE
/// our baseline amount needed to even think about triggering (do this in setup otherwise its gonna be static)
var/base_trigger_amount = 0
- ///the highest number our trigger can be
+ ///the highest number our trigger can be
var/highest_trigger_amount = 0
///the end goal of the amount we need set by setup below
var/amount = 0
///the hint we want to pass into the componenet for when we hit hint triggers
- var/list/hint_texts = list("emits a faint noise..")
+ var/list/hint_texts = list("Emits a faint noise..")
+ ///what it says on inspect when discovered
+ var/discovered_text = "Activated by ... coderbus"
+ ///Research value when discovered
+ var/research_value = 0
/datum/artifact_activator/proc/setup(potency)
amount = round(max(base_trigger_amount, base_trigger_amount + (highest_trigger_amount - base_trigger_amount) * (potency/100)))
diff --git a/monkestation/code/modules/art_sci_overrides/activators/range.dm b/monkestation/code/modules/art_sci_overrides/activators/range.dm
index dd946af80fbe..11a8c7237d48 100644
--- a/monkestation/code/modules/art_sci_overrides/activators/range.dm
+++ b/monkestation/code/modules/art_sci_overrides/activators/range.dm
@@ -3,7 +3,7 @@
/datum/artifact_activator/range
name = "Generic Range Trigger"
- //the upper range of the weapon basically between amount, and upper_range
+
var/upper_range = 0
///Hint range goes like amount - hint_range to upper_range + hint_range
var/hint_range = 0
@@ -12,40 +12,46 @@
/datum/artifact_activator/range/setup(potency)
. = ..()
- upper_range = amount + (hint_range * 2)
/datum/artifact_activator/range/force
name = "Physical Trauma"
required_stimuli = STIMULUS_FORCE
- highest_trigger_amount = 30 //any higher than this and its gonna be practically impossible to trigger
+ highest_trigger_amount = 10 //*meaty thwack* *both chuckle*
hint_prob = 50
- hint_range = 10
- hint_texts = list("you almost want to start hitting things.", "a good whack might fix this.")
+ hint_range = 5
+ hint_texts = list("You almost want to start hitting things.", "A good whack might fix this.")
+ discovered_text = "Activated by Kinetic Energy"
+
+/datum/artifact_activator/range/force/New()
+ base_trigger_amount = rand(2,highest_trigger_amount)
/datum/artifact_activator/range/heat
name = "Heat Sensisty"
required_stimuli = STIMULUS_HEAT
hint_range = 20
- highest_trigger_amount = 15000
- hint_texts = list("it feels like someone messed with the thermostat.", "it feels unpleasent being near")
+ highest_trigger_amount = 750
+ hint_texts = list("It feels like someone messed with the thermostat.", "It feels unpleasent being near")
+ discovered_text = "Activated by Thermal Energy"
/datum/artifact_activator/range/heat/New()
- base_trigger_amount = rand(350, 1000)
+ base_trigger_amount = rand(350, highest_trigger_amount)
/datum/artifact_activator/range/shock
name = "Electrical Charged"
required_stimuli = STIMULUS_SHOCK
- highest_trigger_amount = 10000 // requires atleast t2 parts to trigger a max roll one
+ highest_trigger_amount = 1200
hint_range = 500
- hint_texts = list("you can feel the static in the air", "your hairs stand on their ends")
+ hint_texts = list("You can feel the static in the air", "Your hairs stand on their ends")
+ discovered_text = "Activated by Electrical Energy"
/datum/artifact_activator/range/shock/New()
- base_trigger_amount = rand(400, 1200)
+ base_trigger_amount = rand(400, highest_trigger_amount)
/datum/artifact_activator/range/radiation
name = "Radioactivity"
required_stimuli = STIMULUS_RADIATION
- highest_trigger_amount = 10
+ highest_trigger_amount = 5
hint_range = 2
base_trigger_amount = 1 //x-ray machine goes from 1-10
- hint_texts = list("emits a hum that resembles the Super Matter", "you could swear you saw your bones for a second")
+ hint_texts = list("Emits a hum that resembles the Super Matter", "You could swear you saw your bones for a second")
+ discovered_text = "Activated by Radiation"
diff --git a/monkestation/code/modules/art_sci_overrides/activators/touch.dm b/monkestation/code/modules/art_sci_overrides/activators/touch.dm
index 4cdee177fd24..7d365b6f94b8 100644
--- a/monkestation/code/modules/art_sci_overrides/activators/touch.dm
+++ b/monkestation/code/modules/art_sci_overrides/activators/touch.dm
@@ -8,13 +8,16 @@
name = "Data"
required_stimuli = STIMULUS_DATA
hint_texts = list("It yearns for information")
+ discovered_text = "Activated by Information"
/datum/artifact_activator/touch/carbon
name = "Carbon Touch"
required_stimuli = STIMULUS_CARBON_TOUCH
hint_texts = list("You swear you hear the artifact saying it yearns for flesh.", "One touch couldn't hurt could it?")
+ discovered_text = "Activated by Organic Contact"
/datum/artifact_activator/touch/silicon
name = "Silicon Touch"
required_stimuli = STIMULUS_SILICON_TOUCH
hint_texts = list("It feels like it's malfunctioning")
+ discovered_text = "Activated by Silicon Contact"
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component.dm b/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component.dm
index 1e0520de7d1d..5a8af396b0c3 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component.dm
@@ -1,13 +1,11 @@
#define BASE_MAX_ACTIVATORS 2
+#define BASE_MAX_EFFECTS 2
+#define STIMULI_CD_TIME 5 SECONDS
/datum/component/artifact
dupe_mode = COMPONENT_DUPE_UNIQUE
- ///object related to this datum for spawning
- var/obj/associated_object
- ///actual specific object for this instance
+ //The object we are attached to
var/obj/holder
- ///list weight for picking this artifact datum (0 = never)
- var/weight = 0
///size class for visuals (ARTIFACT_SIZE_TINY,ARTIFACT_SIZE_SMALL,ARTIFACT_SIZE_LARGE)
var/artifact_size = ARTIFACT_SIZE_LARGE
///type name for displaying on analysis forms
@@ -21,7 +19,7 @@
///activators that activate the artifact
var/list/datum/artifact_activator/activators = list()
var/max_activators = BASE_MAX_ACTIVATORS
- ///Valid activators to pick
+ ///Valid activators to pick,mostly legacy code.
var/list/valid_activators = list(
/datum/artifact_activator/touch/carbon,
/datum/artifact_activator/touch/silicon,
@@ -32,71 +30,82 @@
/datum/artifact_activator/range/shock,
/datum/artifact_activator/range/radiation,
)
- ///valid list of faults with their weights [10 is base]
- var/list/valid_faults = list(
- /datum/artifact_fault/ignite = 10,
- /datum/artifact_fault/warp = 10,
- /datum/artifact_fault/reagent/poison = 10,
- /datum/artifact_fault/death = 2,
- /datum/artifact_fault/tesla_zap = 5,
- /datum/artifact_fault/shrink = 10,
- /datum/artifact_fault/explosion = 2,
- )
- ///origin datum
+ ///this artifacts origin
var/datum/artifact_origin/artifact_origin
- ///origin datums to pick
+ ///Just any effect that is real and can be added to an artifact. Mostly legacy
var/list/valid_origins = list(
/datum/artifact_origin/narsie,
/datum/artifact_origin/wizard,
/datum/artifact_origin/silicon,
/datum/artifact_origin/precursor,
- /datum/artifact_origin/martian,
+ /datum/artifact_origin/martian
)
var/activation_message
var/activation_sound
var/deactivation_message
var/deactivation_sound
- var/hint_text = "emits a faint noise.."
- var/examine_hint
var/mutable_appearance/act_effect
- /// Potency in percentage, used for making more strong artifacts need more stimulus. (1% - 100%) 100 is strongest.
- var/potency = 1
+ ///Have we been xray scanned at least once?
+ var/researched = FALSE
- ///structure description from x-ray machines
- var/xray_result = "NONE"
///we store our analysis form var here
var/obj/item/sticker/analysis_form/analysis
var/mutable_appearance/extra_effect
- ///the fault we picked from the listed ones
+ ///the fault we picked from the listed ones. Can be null!
var/datum/artifact_fault/chosen_fault
- ///the amount of freebies we get
+ ///the amount of times an artifact WONT do something bad, even though it should have
var/freebies = 3
///if we have a special examine IE borgers
var/explict_examine
+ ///Fault = weight
+ var/static/list/datum/artifact_fault/fault_weight_list
+
+ ///The activators we have discovered.
+ var/list/datum/artifact_activator/discovered_activators = list()
+ ///Have we discovered what the bad is?
+ var/fault_discovered = FALSE
+ ///A list of effects the artifact has
+ var/list/datum/artifact_effect/artifact_effects = list()
+ ///A list of effects that have been discovered
+ var/list/datum/artifact_effect/discovered_effects = list()
+
+ COOLDOWN_DECLARE(reactivate_cooldown)
-/datum/component/artifact/Initialize(forced_origin = null)
- . = ..()
+/datum/component/artifact/Initialize(forced_origin,forced_effect,forced_size)
if(!isobj(parent))
return COMPONENT_INCOMPATIBLE
holder = parent
GLOB.running_artifact_list[holder] = src
+ if(forced_size != null)
+ artifact_size = forced_size
+
+ if(!length(fault_weight_list))
+ var/list/datum/artifact_fault/valid_faults_pre = typecacheof(/datum/artifact_fault,ignore_root_path = TRUE)
+ var/list/datum/artifact_fault/valid_faults = list()
+ for(var/datum/artifact_fault/fault as anything in valid_faults_pre)
+ valid_faults += fault
+ valid_faults[fault] = fault.weight
+ fault_weight_list = valid_faults
if(forced_origin)
valid_origins = list(forced_origin)
- var/picked_origin = pick(valid_origins)
+ var/datum/artifact_origin/picked_origin = pick(valid_origins)
artifact_origin = new picked_origin
fake_name = "[pick(artifact_origin.name_vars["adjectives"])] [pick(isitem(holder) ? artifact_origin.name_vars["small-nouns"] : artifact_origin.name_vars["large-nouns"])]"
- var/picked_fault = pick_weight(valid_faults)
- chosen_fault = new picked_fault
+ if(prob(95))
+ var/picked_fault = pick_weight(fault_weight_list)
+ chosen_fault = new picked_fault
+ chosen_fault.our_artifact = src
+ chosen_fault.on_added(src)
generated_name = artifact_origin.generate_name()
if(!generated_name)
generated_name = "[pick(artifact_origin.name_vars["adjectives"])] [pick(isitem(holder) ? artifact_origin.name_vars["small-nouns"] : artifact_origin.name_vars["large-nouns"])]"
holder.name = fake_name
- holder.desc = "You have absolutely no clue what this thing is or how it got here."
+ holder.desc = "Some sort of artifact from a time long past."
var/dat_icon
switch(artifact_size)
@@ -124,20 +133,44 @@
activation_sound = pick(artifact_origin.activation_sounds)
if(LAZYLEN(artifact_origin.deactivation_sounds))
deactivation_sound = pick(artifact_origin.deactivation_sounds)
-
+ setup()
var/activator_amount = rand(1,max_activators)
- while(activator_amount>0)
+ for(var/i in 1 to activator_amount)
var/selection = pick(valid_activators)
valid_activators -= selection
- activators += new selection()
- activator_amount--
-
- ADD_TRAIT(holder, TRAIT_HIDDEN_EXPORT_VALUE, INNATE_TRAIT)
- setup()
- potency = clamp(potency, 0, 100)
- for(var/datum/artifact_activator/activator in activators)
+ var/datum/artifact_activator/activator = new selection()
+ activators += activator
+ var/potency = rand(0,100)
activator.setup(potency)
- hint_text = activator.grab_hint()
+ if(forced_effect)
+ var/datum/artifact_effect/added_boogaloo = new forced_effect
+ artifact_effects += added_boogaloo
+ added_boogaloo.our_artifact = src
+ added_boogaloo.setup()
+ if(!length(GLOB.artifact_effect_rarity["all"]))
+ build_weighted_rarities()
+ var/list/datum/artifact_effect/dont_touch = GLOB.artifact_effect_rarity["all"] //Dont touch because below.
+ var/list/datum/artifact_effect/all_possible_effects = dont_touch.Copy() //If you touch it, it actually edits the list, we need a copy. We cant call copy directly because its not a static type list.
+ var/effects_amount = rand(1,BASE_MAX_EFFECTS)
+
+ while(effects_amount > 0)
+ if(effects_amount <= 0)
+ logger.Log(LOG_CATEGORY_ARTIFACT, "[src] has ran out of possible artifact effects! It may not have any at all!")
+ break
+ var/datum/artifact_effect/effect = pick_weight(all_possible_effects)
+ all_possible_effects -= effect
+ if(try_add_effect(effect))
+ effects_amount--
+
+/datum/component/artifact/Destroy(force)
+ QDEL_NULL(artifact_origin)
+ QDEL_NULL(analysis)
+ QDEL_NULL(chosen_fault)
+ QDEL_LIST(activators)
+ QDEL_LIST(artifact_effects)
+ discovered_activators.Cut()
+ discovered_effects.Cut()
+ return ..()
/datum/component/artifact/RegisterWithParent()
RegisterSignals(parent, list(COMSIG_ATOM_DESTRUCTION, COMSIG_QDELETING), PROC_REF(on_destroy))
@@ -168,10 +201,122 @@
COMSIG_ATOM_NO_LONGER_PULLED,
COMSIG_ATOM_PULLED,
))
+///This just clears all the effects,activators,and faults of the artifact, so we can add new ones with a proc.
+/datum/component/artifact/proc/clear_out()
+ QDEL_LIST(activators)
+ QDEL_NULL(chosen_fault)
+ QDEL_LIST(artifact_effects)
+ fault_discovered = FALSE
+ discovered_effects = list()
+ return
+///Adds an activator, returns TRUE/FALSE based on success.
+/datum/component/artifact/proc/add_activator(datum/artifact_activator/new_activator,forced_potency = 0)
+ if(!new_activator)
+ return FALSE
+ if(length(activators) >= BASE_MAX_ACTIVATORS)
+ return FALSE
+ var/datum/artifact_activator/created
+ if(ispath(new_activator))
+ created = new new_activator()
+ else
+ created = new new_activator.type
+ activators += created
+ if(forced_potency > 0 )
+ created.setup(forced_potency)
+ else
+ created.setup(rand(1,100))
+ return TRUE
+///changes the fault of the artifact, returns TRUE/FALSE based on success.
+/datum/component/artifact/proc/change_fault(datum/artifact_fault/new_fault)
+ if(new_fault)
+ return force_replace_fault(new_fault.type)
+ else
+ qdel(chosen_fault)
+ chosen_fault = new new_fault
+ return TRUE
+
+/*
+* Long function, but basically:
+* For given effect:
+* If it has valid types, check to make sure its of the right type path. So you cant roll something that requires a structure on an item.
+* If it has valid origins, and the artifact isnt that origin, return FALSE.
+* If it has valid activators, and the artifact has none of them, return FALSE.
+* If it has a valid size, and the artifact isnt that size, return FALSE.
+* Then, if all is well, slam it on the artifact, call setup() on the effect, return TRUE
+*/
+/datum/component/artifact/proc/try_add_effect(datum/artifact_effect/effect)
+ var/datum/artifact_effect/added
+ if(ispath(effect))
+ added = new effect //We need it now, becasue for some reason we cant read the lists from just the raw datum.
+ else
+ added = new effect.type //Skip the checks, just add it.
+ artifact_effects += added
+ added.our_artifact = src
+ added.setup()
+ return TRUE
+ if(length(added.valid_type_paths))
+ var/bad_path = FALSE
+ for(var/path in added.valid_type_paths)
+ if(!istype(holder,path))
+ bad_path = TRUE
+ break
+ if(bad_path)
+ QDEL_NULL(added)
+ return FALSE
+ if(length(added.valid_origins))
+ if(!(artifact_origin.type_name in added.valid_origins))
+ QDEL_NULL(added)
+ return FALSE
+ if(length(added.valid_activators))
+ var/good_activators = FALSE
+ for(var/datum/artifact_activator/activator as anything in activators) //Only need one to be correct.
+ if(activator.type in added.valid_activators)
+ good_activators = TRUE
+ break
+ if(!good_activators)
+ QDEL_NULL(added)
+ return FALSE
+ if(added.artifact_size)
+ if(artifact_size != added.artifact_size)
+ QDEL_NULL(added)
+ return FALSE
+ artifact_effects += added
+ added.our_artifact = src
+ added.setup()
+ return TRUE
+///Kinda a legacy proc, but if you need something super special I guess.
/datum/component/artifact/proc/setup()
return
+///Replaces the fault on the artifact with a new one.
+/datum/component/artifact/proc/force_replace_fault(new_fault)
+ if(new_fault)
+ qdel(chosen_fault)
+ if(ispath(new_fault))
+ chosen_fault = new new_fault
+ chosen_fault.our_artifact = src
+ chosen_fault.on_added(src)
+ else
+ chosen_fault = new_fault
+ chosen_fault.our_artifact = src
+ chosen_fault.on_added(src)
+ return TRUE
+ return FALSE
+
+///Adds a new artifact effect to the artifact. Ignores all normal checks. Admin Proc. Not called.
+/datum/component/artifact/proc/force_add_effect(new_effect_path,effect_power = null)
+ if(new_effect_path && ispath(new_effect_path,/datum/artifact_effect))
+ var/datum/artifact_effect/added_boogaloo = new new_effect_path
+ artifact_effects += added_boogaloo
+ added_boogaloo.our_artifact = src
+ if(effect_power)
+ added_boogaloo.potency = effect_power
+ added_boogaloo.setup()
+ return TRUE
+ return FALSE
+
+///Activates the artifact.
/datum/component/artifact/proc/artifact_activate(silent)
if(active) //dont activate activated objects
return FALSE
@@ -183,10 +328,12 @@
active = TRUE
holder.add_overlay(act_effect)
logger.Log(LOG_CATEGORY_ARTIFACT, "[parent] has been activated")
- effect_activate(silent)
+ for(var/datum/artifact_effect/effect in artifact_effects)
+ effect.effect_activate(silent)
return TRUE
-/datum/component/artifact/proc/artifact_deactivate(silent)
+///The opposite of activates the artifact
+/datum/component/artifact/proc/artifact_deactivate(silent = FALSE)
if(!active)
return
if(deactivation_sound && !silent)
@@ -196,14 +343,22 @@
active = FALSE
holder.cut_overlay(act_effect)
logger.Log(LOG_CATEGORY_ARTIFACT, "[parent] has been deactivated")
- effect_deactivate(silent)
+ for(var/datum/artifact_effect/effect in artifact_effects)
+ effect.effect_deactivate(silent)
+/datum/component/artifact/effect_touched(mob/living/user)
+ for(var/datum/artifact_effect/effect in artifact_effects)
+ effect.effect_touched(user)
+ return
+
+///Called when the artifact gets something that may activate it. Skips re-activation of artifacts, but passes their triggers to faults.
/datum/component/artifact/proc/process_stimuli(stimuli, stimuli_value, triggers_faults = TRUE)
- if(!stimuli || active) // if called without a stimuli dont bother, if active we dont wanna reactivate
+ if(!stimuli)
return
var/checked_fault = FALSE
+ var/correct_trigger = FALSE
for(var/datum/artifact_activator/listed_activator in activators)
- if(!(listed_activator.required_stimuli & stimuli))
+ if(!(listed_activator.required_stimuli & stimuli) && chosen_fault)
if(!triggers_faults)
continue
if(freebies >= 1)
@@ -219,17 +374,16 @@
holder.visible_message("[holder] [chosen_fault.visible_message]")
continue
checked_fault = TRUE
- if(istype(listed_activator, /datum/artifact_activator/range))
- var/datum/artifact_activator/range/ranged_activator = listed_activator
- //if we fail the range check check if we are in hint range to send out the hint
- if(!ISINRANGE(stimuli_value, ranged_activator.amount, ranged_activator.upper_range))
- if(hint_text && !ISINRANGE(stimuli_value, ranged_activator.amount - ranged_activator.hint_range, ranged_activator.upper_range + ranged_activator.hint_range))
- continue
- if(!prob(ranged_activator.hint_prob))
- continue
- holder.visible_message(span_notice("[hint_text]"))
+ if((listed_activator.required_stimuli & stimuli) && istype(listed_activator, /datum/artifact_activator/range))
+ if(stimuli_value < listed_activator.amount)
continue
+ correct_trigger = TRUE
+ break
+ if(active || !correct_trigger)
+ return
+ if(COOLDOWN_FINISHED(src,reactivate_cooldown))
artifact_activate()
+ COOLDOWN_START(src,reactivate_cooldown,STIMULI_CD_TIME)
/datum/component/artifact/proc/stimulate_from_turf_heat(turf/target)
if(!QDELETED(target))
@@ -239,3 +393,5 @@
process_stimuli(STIMULUS_RADIATION, intensity)
#undef BASE_MAX_ACTIVATORS
+#undef BASE_MAX_EFFECTS
+#undef STIMULI_CD_TIME
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component_signal_procs.dm b/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component_signal_procs.dm
index 03874852ae2a..9b89fd631f0f 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component_signal_procs.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component_signal_procs.dm
@@ -4,15 +4,29 @@
if(!QDELETED(holder))
holder.loc.visible_message(span_warning("[holder] [artifact_origin.destroy_message]"))
artifact_deactivate(TRUE)
+ for(var/datum/artifact_effect/effect in artifact_effects)
+ effect.on_destroy(source)
if(!QDELETED(holder))
qdel(holder)
/datum/component/artifact/proc/on_examine(atom/source, mob/user, list/examine_list)
SIGNAL_HANDLER
- if(examine_hint)
- examine_list += examine_hint
if(explict_examine)
examine_list += explict_examine
+ for(var/datum/artifact_effect/effect in artifact_effects)
+ if(discovered_effects.Find(effect.type) && effect.examine_discovered)
+ examine_list += span_info(effect.examine_discovered)
+ else if (effect.examine_hint)
+ examine_list += span_info(effect.examine_hint)
+
+ for(var/datum/artifact_activator/act in activators)
+ if(discovered_activators.Find(act.type) && act.discovered_text)
+ examine_list += span_info(act.discovered_text)
+ else if(length(act.hint_texts))
+ examine_list += span_info(pick(act.hint_texts))
+
+ if(chosen_fault && chosen_fault.inspect_warning)
+ examine_list += span_warning(pick(chosen_fault.inspect_warning))
/datum/component/artifact/proc/on_sticker(atom/source, obj/item/sticker/sticker, mob/user)
SIGNAL_HANDLER
@@ -75,7 +89,8 @@
logger.Log(LOG_CATEGORY_ARTIFACT, "[user] has touched [parent]")
if(active)
- effect_touched(user)
+ for(var/datum/artifact_effect/effect in artifact_effects)
+ effect.effect_touched(user)
return
if(LAZYLEN(artifact_origin.touch_descriptors))
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), user, span_notice("[pick(artifact_origin.touch_descriptors)]")), 0.5 SECONDS)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/bomb.dm b/monkestation/code/modules/art_sci_overrides/artifact_components/bomb.dm
deleted file mode 100644
index 476af7185a04..000000000000
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/bomb.dm
+++ /dev/null
@@ -1,152 +0,0 @@
-/datum/component/artifact/bomb
- examine_hint = span_warning("It is covered in very conspicuous markings.")
- valid_activators = list(
- /datum/artifact_activator/range/force,
- /datum/artifact_activator/range/heat,
- /datum/artifact_activator/range/shock,
- /datum/artifact_activator/range/radiation
- )
- deactivation_message = "sputters a bit, and falls silent once more."
- xray_result = "COMPLEX"
- var/dud = FALSE
- var/dud_message = "sputters, failing to activate! Its a dud!"
- var/initial_warning = "begins overloading, rattling violenty!"
- var/explode_delay = 1 MINUTES // also delayed by finale_delay for fluff
- var/explode_cooldown_time = 1 MINUTES
- var/finale_delay = 6 SECONDS //delay before we actually deliver the payload for fluff
- var/final_message = "reaches a catastrophic overload, cracks forming at its surface!"
- var/sound/active_alarm = 'sound/effects/alert.ogg' // plays every alarm_cooldown_time when active
- var/alarm_cooldown_time = 3 SECONDS
- var/sound/final_sound = 'sound/misc/bloblarm.ogg'
- COOLDOWN_DECLARE(activation_cooldown)
- COOLDOWN_DECLARE(alarm_cooldown)
- var/timer_id
- var/do_alert = FALSE //do we send an announcement on activation
-
-/datum/component/artifact/bomb/setup()
- if(prob(20))
- dud = TRUE
-
-/datum/component/artifact/bomb/effect_activate()
- if(!COOLDOWN_FINISHED(src,explode_cooldown_time))
- holder.visible_message(span_warning("[holder] [deactivation_message]")) //rekt
- addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/component/artifact, artifact_deactivate)), 1 SECONDS)
- return
- holder.visible_message(span_bolddanger("[holder] [initial_warning]"))
- COOLDOWN_START(src,activation_cooldown,explode_cooldown_time)
- timer_id = addtimer(CALLBACK(src, PROC_REF(finale)), explode_delay, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE)
- if(do_alert && is_station_level(holder.z))
- priority_announce("A highly unstable object of type [type_name] has been activated at [get_area(holder)]. It has been marked on GPS, The crew is advised to get rid of it IMMEDIATELY.", null, SSstation.announcer.get_rand_report_sound(), has_important_message = TRUE)
- holder.AddComponent(/datum/component/gps, "Unstable Object")
-
-/datum/component/artifact/bomb/effect_deactivate()
- deltimer(timer_id)
-
-/datum/component/artifact/bomb/effect_process()
- . = ..()
- if(active && COOLDOWN_FINISHED(src,alarm_cooldown) && (COOLDOWN_TIMELEFT(src,alarm_cooldown) <= finale_delay))
- playsound(holder, active_alarm, 30, 1)
- holder.Shake(duration = 1 SECONDS, shake_interval = 0.08 SECONDS)
- COOLDOWN_START(src,alarm_cooldown, alarm_cooldown_time)
-
-/datum/component/artifact/bomb/proc/finale()
- if(final_sound)
- playsound(holder.loc, final_sound, 100, 1, -1)
- if(finale_delay)
- holder.visible_message(span_bolddanger("[holder] [final_message]"))
- addtimer(CALLBACK(src, PROC_REF(payload)), finale_delay)
- else
- payload()
-
-/datum/component/artifact/bomb/on_destroy(/datum/source)
- . = ..()
- if(active)
- payload()
- deltimer(timer_id)
-
-/datum/component/artifact/bomb/proc/payload()
- . = TRUE
- if(dud || !active)
- holder.visible_message(span_notice("[holder] [dud_message]"))
- artifact_deactivate(TRUE)
- return FALSE
-
-/// EXPLOSIVE BOMB
-
-/datum/component/artifact/bomb/explosive
- associated_object = /obj/structure/artifact/bomb
- type_name = "Bomb (explosive)"
- weight = ARTIFACT_RARE
- var/devast
- var/heavy
- var/light
-
-/datum/component/artifact/bomb/explosive/New()
- . = ..()
- devast = rand(1,3)
- heavy = rand(2,4)
- light = rand(3,10)
- potency = (light + heavy + devast) * 2
-
-/datum/component/artifact/bomb/explosive/payload()
- if(!..())
- return FALSE
- explosion(holder, devast,heavy,light,light*1.5)
- on_destroy()
-
-/// DEVESTATING BOMB
-
-/datum/component/artifact/bomb/explosive/devastating
- associated_object = /obj/structure/artifact/bomb/devastating
- type_name = "Bomb (explosive, devastating)"
- do_alert = TRUE
- weight = ARTIFACT_VERYRARE
- xray_result = "DENSE"
- explode_delay = 2 MINUTES
-
-/datum/component/artifact/bomb/explosive/devastating/New()
- ..()
- devast = rand(3,7)
- heavy = rand(7,12)
- light = rand(10,25)
- potency = (devast + heavy + light) * 2.25 // get real
-
-/// GAS BOMB
-
-/datum/component/artifact/bomb/gas
- associated_object = /obj/structure/artifact/bomb/gas
- type_name = "Bomb (gas)"
- weight = ARTIFACT_RARE
- xray_result = "POROUS"
- initial_warning = "begins rattling violenty!"
- final_message = "reaches a critical pressure, cracks forming at its surface!"
- var/datum/gas/payload_gas
- var/list/weighted_gas = list(
- /datum/gas/plasma = 5,
- /datum/gas/carbon_dioxide = 10,
- /datum/gas/nitrous_oxide = 10,
- /datum/gas/tritium = 5,
- /datum/gas/hydrogen = 5,
- /datum/gas/zauker = 2,
- )
-
-/datum/component/artifact/bomb/gas/setup()
- . = ..()
- payload_gas = pick_weight(weighted_gas)
-
-/datum/component/artifact/bomb/gas/payload()
- if(!..())
- artifact_deactivate()
- return FALSE
- var/turf/open/O = get_turf(holder)
- if(!isopenturf(O))
- artifact_deactivate()
- return FALSE
- var/datum/gas_mixture/merger = new
- merger.assert_gas(payload_gas)
- merger.assert_gas(/datum/gas/oxygen)
- merger.gases[payload_gas][MOLES] = rand(150,2000)
- merger.gases[/datum/gas/oxygen][MOLES] = 350
- merger.temperature = rand(200,3000)
- O.assume_air(merger)
- qdel(holder)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/bonk.dm b/monkestation/code/modules/art_sci_overrides/artifact_components/bonk.dm
deleted file mode 100644
index 39b6136e894d..000000000000
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/bonk.dm
+++ /dev/null
@@ -1,38 +0,0 @@
-/datum/component/artifact/bonk
- associated_object = /obj/structure/artifact/bonk
- weight = ARTIFACT_UNCOMMON
- type_name = "Slammer"
- activation_message = "opens up!"
- deactivation_message = "closes up."
- valid_activators = list(
- /datum/artifact_activator/touch/carbon,
- /datum/artifact_activator/touch/silicon
- )
- ///force of the hit
- var/hit_power = 1
- COOLDOWN_DECLARE(bonk_cooldown)
-
-/datum/component/artifact/bonk/setup()
- hit_power = rand(0,35)
- potency += hit_power
-
-/datum/component/artifact/bonk/effect_touched(mob/living/user)
- if(!COOLDOWN_FINISHED(src, bonk_cooldown))
- return
-
- if(iscarbon(user))
- var/mob/living/carbon/carbon = user
- if(!carbon.get_bodypart(BODY_ZONE_HEAD))
- holder.say("My condolences to your missing head.") //they can speak uhh galactic common because alien tech idk
- holder.visible_message(span_notice("[holder] shakes [user][p_s()] hands with an apparatus."))
- playsound(get_turf(holder), 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
- artifact_deactivate()
- return
- else
- carbon.apply_damage(hit_power, BRUTE, BODY_ZONE_HEAD, carbon.run_armor_check(BODY_ZONE_HEAD, MELEE))
- holder.visible_message(span_danger("[holder] hits [carbon] over the head!"))
- else
- holder.visible_message(span_danger("[holder] slams [user]!"))
- user.adjustBruteLoss(hit_power)
- playsound(get_turf(holder), 'sound/misc/bonk.ogg', 80, FALSE)
- COOLDOWN_START(src, bonk_cooldown, 1.5 SECONDS)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/cell.dm b/monkestation/code/modules/art_sci_overrides/artifact_components/cell.dm
deleted file mode 100644
index 2c3ee53a1886..000000000000
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/cell.dm
+++ /dev/null
@@ -1,37 +0,0 @@
-/datum/component/artifact/cell
- associated_object = /obj/item/stock_parts/cell/artifact
- artifact_size = ARTIFACT_SIZE_TINY
- type_name = "Power Cell"
- weight = ARTIFACT_UNCOMMON
- xray_result = "SEGMENTED"
- valid_activators = list(
- /datum/artifact_activator/range/heat,
- /datum/artifact_activator/range/shock,
- /datum/artifact_activator/range/radiation
- )
- valid_faults = list(
- /datum/artifact_fault/ignite = 10,
- /datum/artifact_fault/warp = 10,
- /datum/artifact_fault/reagent/poison = 10,
- /datum/artifact_fault/death = 2,
- /datum/artifact_fault/tesla_zap = 5,
- /datum/artifact_fault/grow = 10,
- /datum/artifact_fault/explosion = 2,
- )
-
-/datum/component/artifact/cell/setup()
- var/obj/item/stock_parts/cell/artifact/cell = holder
- cell.corrupted = prob(10) //trolled
- cell.maxcharge = rand(5 KW, 500 MW) // the heavenly battery
- cell.charge = cell.maxcharge / 2
- cell.chargerate = rand(5000, round(cell.maxcharge * 0.4))
- potency += cell.maxcharge / 900
- potency += cell.chargerate / 4000
-
-/datum/component/artifact/cell/effect_activate()
- var/obj/item/stock_parts/cell/artifact/cell = holder
- cell.ratingdesc = TRUE
-
-/datum/component/artifact/cell/effect_deactivate()
- var/obj/item/stock_parts/cell/artifact/cell = holder
- cell.ratingdesc = FALSE
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/ghost_object.dm b/monkestation/code/modules/art_sci_overrides/artifact_components/ghost_object.dm
new file mode 100644
index 000000000000..38c66f0e7863
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_components/ghost_object.dm
@@ -0,0 +1,118 @@
+///This is straight up an object version of the spirit plade one
+/datum/component/ghost_object_control
+ var/attempting_awakening = FALSE
+ ///mob contained in the item,null untill controlled!
+ var/mob/living/basic/shade/bound_spirit
+ ///do we make a callback to retry untill someone posesses it?
+ var/repolling= FALSE
+ ///How often can this thing move in seconds
+ var/speed= 1.25
+ COOLDOWN_DECLARE(move_cooldown)
+/datum/component/ghost_object_control/Initialize(repoll = FALSE,move_speed = null)
+ if(!ismovable(parent)) //you may apply this to mobs, I take no responsibility for how that works out
+ return COMPONENT_INCOMPATIBLE
+ if(move_speed)
+ speed = move_speed
+
+/datum/component/ghost_object_control/Destroy(force, silent)
+ . = ..()
+ if(bound_spirit)
+ QDEL_NULL(bound_spirit)
+
+/datum/component/ghost_object_control/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
+ RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(on_destroy))
+ RegisterSignal(parent, COMSIG_ATOM_RELAYMOVE, TYPE_PROC_REF(/datum/component/ghost_object_control,move_host))
+ //RegisterSignal(parent, COMSIG_RIDDEN_DRIVER_MOVE, TYPE_PROC_REF(/datum/component/ghost_object_control,move_host))
+
+/datum/component/ghost_object_control/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_ATOM_EXAMINE, COMSIG_QDELETING, COMSIG_ATOM_RELAYMOVE))
+
+///Moves the object. Yippee!
+/datum/component/ghost_object_control/proc/move_host(atom/movable/movable_parent,mob/buckled_mob,dir_to_move)
+ SIGNAL_HANDLER
+
+ if(!COOLDOWN_FINISHED(src, move_cooldown))
+ return COMSIG_BLOCK_RELAYMOVE
+ var/turf/next = get_step(movable_parent, dir_to_move)
+ var/turf/current = get_turf(movable_parent)
+ if(!istype(next) || !istype(current))
+ return COMSIG_BLOCK_RELAYMOVE
+ if(next.density)
+ return COMSIG_BLOCK_RELAYMOVE
+ if(!isturf(movable_parent.loc))
+ return COMSIG_BLOCK_RELAYMOVE
+
+ step(movable_parent, dir_to_move)
+ var/last_move_diagonal = ((dir_to_move & (dir_to_move - 1)) && (movable_parent.loc == next))
+ COOLDOWN_START(src, move_cooldown, ((last_move_diagonal ? 2 : 1) * speed) SECOND)
+
+ if(QDELETED(src))
+ return COMSIG_BLOCK_RELAYMOVE
+ return TRUE
+
+///signal fired on examining the parent
+/datum/component/ghost_object_control/proc/on_examine(datum/source, mob/user, list/examine_list)
+ SIGNAL_HANDLER
+ if(!bound_spirit)
+ return
+ examine_list += span_notice("[parent] is moving somehow?")
+
+///Call to poll for ghost role
+/datum/component/ghost_object_control/proc/request_control(movement_speed)
+ if(attempting_awakening)
+ return
+ if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE))
+ return
+
+ attempting_awakening = TRUE
+
+ var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates(
+ "Do you want to play as [parent]?",
+ check_jobban = ROLE_SENTIENCE,
+ poll_time = 10 SECONDS,
+ ignore_category = POLL_IGNORE_SENTIENCE_POTION,
+ alert_pic = parent,
+ role_name_text = "[parent]",
+ )
+ if(!LAZYLEN(candidates))
+ if(repolling)
+ addtimer(CALLBACK(src,PROC_REF(request_control),2.5 MINUTE))
+ attempting_awakening = FALSE
+ return
+
+ var/mob/dead/observer/chosen_spirit = pick(candidates)
+ bound_spirit = new(parent)
+ bound_spirit.ckey = chosen_spirit.ckey
+ bound_spirit.fully_replace_character_name(null, "[parent]")
+ bound_spirit.status_flags |= GODMODE
+ bound_spirit.grant_all_languages(FALSE, FALSE, TRUE) //Grants omnitongue
+ bound_spirit.update_atom_languages()
+ speed = movement_speed
+
+ //Add new signals for parent and stop attempting to awaken
+
+ // Now that all of the important things are in place for our spirit, it's time for them to choose their name.
+ var/valid_input_name = custom_name(bound_spirit)
+ if(valid_input_name)
+ bound_spirit.fully_replace_character_name(null, "[valid_input_name]")
+
+ attempting_awakening = FALSE
+
+/**
+ * custom_name : Simply sends a tgui input text box to the blade asking what name they want to be called, and retries it if the input is invalid.
+ *
+ * Arguments:
+ * * awakener: user who interacted with the blade
+ */
+/datum/component/ghost_object_control/proc/custom_name(mob/subject)
+ var/chosen_name = sanitize_name(tgui_input_text(bound_spirit, "What are you named?", "Spectral Nomenclature", max_length = MAX_NAME_LEN))
+ if(!chosen_name) // with the way that sanitize_name works, it'll actually send the error message to the awakener as well.
+ return custom_name(subject) //YOU WILL PICK A NAME.
+ return chosen_name
+
+///signal fired from parent being destroyed
+/datum/component/ghost_object_control/proc/on_destroy(datum/source)
+ SIGNAL_HANDLER
+ to_chat(bound_spirit, span_userdanger("You were destroyed!"))
+ QDEL_NULL(bound_spirit)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/lamp.dm b/monkestation/code/modules/art_sci_overrides/artifact_components/lamp.dm
deleted file mode 100644
index c62124f20c62..000000000000
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/lamp.dm
+++ /dev/null
@@ -1,34 +0,0 @@
-
-/datum/component/artifact/lamp
- associated_object = /obj/structure/artifact/lamp
- weight = ARTIFACT_COMMON
- type_name = "Lamp"
- activation_message = "starts shining!"
- deactivation_message = "stops shining."
-
-/datum/component/artifact/lamp/setup()
- var/power
- var/color = pick(COLOR_RED, COLOR_BLUE, COLOR_YELLOW, COLOR_GREEN, COLOR_PURPLE, COLOR_ORANGE)
- var/range
- switch(rand(1,100))
- if(1 to 75)
- power = rand(2,5)
- range = rand(2,5)
- if(76 to 100)
- range = rand(4,10)
- power = rand(2,10) // the sun
-
- if(artifact_origin.type_name == ORIGIN_NARSIE && prob(40))
- color = COLOR_BLACK
- holder.set_light_range_power_color(range, power, color)
- potency += (range + power) * 2
-
-/datum/component/artifact/lamp/effect_touched(mob/user)
- holder.set_light_on(!holder.light_on) //toggle
- to_chat(user, span_hear("[holder] clicks."))
-
-/datum/component/artifact/lamp/effect_activate()
- holder.set_light_on(TRUE)
-
-/datum/component/artifact/lamp/effect_deactivate()
- holder.set_light_on(FALSE)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/repulsor.dm b/monkestation/code/modules/art_sci_overrides/artifact_components/repulsor.dm
deleted file mode 100644
index 412b52123822..000000000000
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/repulsor.dm
+++ /dev/null
@@ -1,59 +0,0 @@
-
-/datum/component/artifact/repulsor
- associated_object = /obj/structure/artifact/repulsor
- weight = ARTIFACT_COMMON
- type_name = "Repulsor/Impulsor"
- activation_message = "opens up, a weird aura starts emitting from it!"
- deactivation_message = "closes up."
- xray_result = "SEGMENTED"
- var/attract = FALSE //if FALSE, repulse, otherwise, attract
- var/strength
- var/range
- var/cooldown_time
- COOLDOWN_DECLARE(cooldown)
-
-/datum/component/artifact/repulsor/setup()
- attract = prob(40)
- range = rand(1,3)
- cooldown_time = rand(3,5) SECONDS
- strength = rand(MOVE_FORCE_DEFAULT,MOVE_FORCE_OVERPOWERING)
- potency += cooldown_time / 4 + strength / 3000
- addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/component/artifact, artifact_deactivate)), round(20 * (potency * 10) SECONDS))
-
-/datum/component/artifact/repulsor/effect_touched(mob/user)
- if(!COOLDOWN_FINISHED(src,cooldown))
- return
- pulse()
- COOLDOWN_START(src,cooldown,cooldown_time)
-
-/datum/component/artifact/repulsor/effect_process()
- . = ..()
- if(prob(100 - potency))
- return
- pulse()
-
-/datum/component/artifact/repulsor/RegisterWithParent()
- . = ..()
- RegisterSignal(parent, COMSIG_ATOM_HITBY, PROC_REF(pulse))
-
-/datum/component/artifact/repulsor/UnregisterFromParent()
- . = ..()
- UnregisterSignal(parent, COMSIG_ATOM_HITBY)
-
-/datum/component/artifact/repulsor/proc/pulse(datum/source,atom/movable/thrown, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
- SIGNAL_HANDLER
- if(!active)
- return
- holder.visible_message(span_warning("[holder] emits a pulse of energy, throwing things [attract ? "towards it!" : "away from it!"]"))
- var/owner_turf = get_turf(holder)
- if(isnull(thrown))
- for(var/atom/movable/throwee in oview(range,holder))
- if(throwee.anchored)
- continue
- if(attract)
- throwee.safe_throw_at(holder, strength / 3000, 1, force = strength)
- else
- var/throwtarget = get_edge_target_turf(get_turf(throwee), get_dir(owner_turf, get_step_away(throwee, owner_turf)))
- throwee.safe_throw_at(throwtarget, strength / 3000, 1, force = strength)
- else if(throwingdatum?.thrower)
- thrown.safe_throw_at(throwingdatum.thrower, get_dist(holder, throwingdatum.thrower), 1, force = strength)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_effects/_artifact_effect.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/_artifact_effect.dm
new file mode 100644
index 000000000000..b16d05989c82
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/_artifact_effect.dm
@@ -0,0 +1,72 @@
+/datum/artifact_effect
+ ///string added to artifact desc, if not discovered.
+ var/examine_hint
+ ///string added to artifact desc, if the effect has been discovered
+ var/examine_discovered
+ ///When you discover this, how many credits does it add to the sell price?
+ var/discovered_credits = CARGO_CRATE_VALUE*0.75
+ ///how likely is it that this effect is added to an artifact?
+ var/weight = ARTIFACT_COMMON
+ ///if defined, artifact must be this size to roll
+ var/artifact_size
+ ///how strong is this effect,1-100
+ var/potency
+ ///If the artifact doesnt have the right activator, cant be put on. If null, assume any.
+ var/list/valid_activators
+ ///If the artifact doesnt have this origin, cant be put on. If null, assume any.
+ var/list/valid_origins
+ ///sent on activation
+ var/activation_message
+ ///played on activation
+ var/activation_sound
+ ///sent on deactivation
+ var/deactivation_message
+ ///played on deactivation
+ var/deactivation_sound
+ ///list of paths the artifacts holder is allowed to be, if null, may be on any artifact datum holder.
+ var/list/valid_type_paths
+ ///Does this show up on the artifact fourm?
+ var/super_secret = FALSE
+
+ ///Research value when discovered For reference,5000 is one node
+ var/research_value = 100
+ ///The artifact we're on.
+ var/datum/component/artifact/our_artifact
+ ///Type of effect, shows up in Xray Machine
+ var/type_name = "Generic Artifact Effect"
+
+/datum/artifact_effect/New()
+ . = ..()
+ potency = rand(1, 100)
+
+/datum/artifact_effect/Destroy(force)
+ our_artifact = null
+ return ..()
+
+///Called when the artifact has been created
+/datum/artifact_effect/proc/setup()
+ return
+///Called when the artifact has been activated
+/datum/artifact_effect/proc/effect_activate(silent)
+ return
+///Called when the artifact has been de-activated
+/datum/artifact_effect/proc/effect_deactivate(silent)
+ return
+///Called when the artifact has been touched by a living mob,does NOT call faults or activate artifact unless it has the correct touch component!
+/datum/artifact_effect/proc/effect_touched(mob/living/user)
+ return
+///Called on process() IF the artifact is active.
+/datum/artifact_effect/proc/effect_process()
+ return
+///Called when the artifact/effect is destroyed is destroyed
+/datum/artifact_effect/proc/on_destroy(atom/source)
+ return
+///Util, can be called to activate, then de-activate the artifact as a whole swiftly. Wont Re activate already active artifacts.
+/datum/artifact_effect/proc/flick_active(silent)
+ if(!our_artifact.active)
+ our_artifact.artifact_activate(silent)
+ our_artifact.artifact_deactivate(silent)
+ return
+///Util, can be called to swap the artifacts active status quickly.
+/datum/artifact_effect/proc/toggle_active(silent)
+ our_artifact.active ? our_artifact.artifact_deactivate(silent) : our_artifact.artifact_activate(silent)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_effects/bomb.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/bomb.dm
new file mode 100644
index 000000000000..56ec1504836b
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/bomb.dm
@@ -0,0 +1,155 @@
+/datum/artifact_effect/bomb
+ examine_hint = span_warning("It is covered in very conspicuous markings.")
+ valid_activators = list(
+ /datum/artifact_activator/range/force,
+ /datum/artifact_activator/range/heat,
+ /datum/artifact_activator/range/shock,
+ /datum/artifact_activator/range/radiation
+ )
+ research_value = 1000
+
+ type_name = "Explosive Effect"
+ examine_discovered = span_warning("It appears to explode.")
+
+
+ var/dud = FALSE
+ var/dud_message = "sputters, failing to activate! Its a dud!"
+ var/initial_warning = "begins overloading, rattling violenty!"
+ var/explode_delay = 1 MINUTES // also delayed by finale_delay for fluff
+ var/explode_cooldown_time = 1 MINUTES
+ var/finale_delay = 6 SECONDS //delay before we actually deliver the payload for fluff
+ var/final_message = "reaches a catastrophic overload, cracks forming at its surface!"
+ var/sound/active_alarm = 'sound/effects/alert.ogg' // plays every alarm_cooldown_time when active
+ var/alarm_cooldown_time = 3 SECONDS
+ var/sound/final_sound = 'sound/misc/bloblarm.ogg'
+ COOLDOWN_DECLARE(activation_cooldown)
+ COOLDOWN_DECLARE(alarm_cooldown)
+ var/timer_id
+ var/do_alert = FALSE //do we send an announcement on activation
+
+/datum/artifact_effect/bomb/setup()
+ if(prob(20))
+ dud = TRUE
+
+/datum/artifact_effect/bomb/effect_activate()
+ if(!our_artifact || !our_artifact.holder)
+ return
+ if(!COOLDOWN_FINISHED(src,activation_cooldown))
+ our_artifact.holder.visible_message(span_warning("[our_artifact.holder] [deactivation_message]")) //rekt
+ addtimer(CALLBACK(src.our_artifact, TYPE_PROC_REF(/datum/component/artifact, artifact_deactivate)), 1 SECONDS)
+ return
+ our_artifact.holder.visible_message(span_bolddanger("[our_artifact.holder] [initial_warning]"))
+ COOLDOWN_START(src,activation_cooldown,explode_cooldown_time)
+ timer_id = addtimer(CALLBACK(src, PROC_REF(finale)), explode_delay, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE)
+ if(do_alert && is_station_level(our_artifact.holder.z))
+ priority_announce("A highly unstable object has been activated at [get_area(our_artifact.holder)]. It has been marked on GPS, The crew is advised to get rid of it IMMEDIATELY.", null, SSstation.announcer.get_rand_report_sound(), has_important_message = TRUE)
+ our_artifact.holder.AddComponent(/datum/component/gps, "Unstable Object")
+
+/datum/artifact_effect/bomb/effect_deactivate()
+ deltimer(timer_id)
+
+/datum/artifact_effect/bomb/effect_process()
+ . = ..()
+ if(our_artifact.active && (COOLDOWN_FINISHED(src,alarm_cooldown) || (COOLDOWN_TIMELEFT(src,alarm_cooldown) <= finale_delay)))
+ playsound(our_artifact, active_alarm, 30, 1)
+ our_artifact.holder.Shake(duration = 1 SECONDS, shake_interval = 0.08 SECONDS)
+ COOLDOWN_START(src,alarm_cooldown, alarm_cooldown_time)
+
+/datum/artifact_effect/bomb/proc/finale()
+ if(final_sound)
+ playsound(our_artifact.holder.loc, final_sound, 100, 1, -1)
+ if(finale_delay)
+ our_artifact.holder.visible_message(span_bolddanger("[our_artifact.holder] [final_message]"))
+ addtimer(CALLBACK(src, PROC_REF(payload)), finale_delay)
+ else
+ payload()
+
+/datum/artifact_effect/bomb/on_destroy(datum/source)
+ . = ..()
+ if(our_artifact.active && source != src)//Prevents infinite bakoom.
+ payload()
+ deltimer(timer_id)
+
+/datum/artifact_effect/bomb/proc/payload()
+ . = TRUE
+ if(dud || !our_artifact.active)
+ our_artifact.holder.visible_message(span_notice("[our_artifact.holder] [dud_message]"))
+ our_artifact.artifact_deactivate(TRUE)
+ return FALSE
+
+/// EXPLOSIVE BOMB
+
+/datum/artifact_effect/bomb/explosive
+ type_name = "Bomb"
+ weight = ARTIFACT_RARE
+ var/devast
+ var/heavy
+ var/light
+ examine_discovered = span_danger("It appears to explode in a large radius!")
+
+/datum/artifact_effect/bomb/explosive/New()
+ . = ..()
+ devast = rand(1,3)
+ heavy = rand(2,4)
+ light = rand(3,10)
+ potency = (light + heavy + devast) * 2
+
+/datum/artifact_effect/bomb/explosive/payload()
+ if(!..())
+ return FALSE
+ explosion(our_artifact.holder, devast,heavy,light,light*1.5)
+ on_destroy(src)
+
+/// DEVESTATING BOMB
+
+/datum/artifact_effect/bomb/explosive/devastating
+ type_name = "Large Bomb"
+ do_alert = TRUE
+ weight = ARTIFACT_VERYRARE
+ explode_delay = 2 MINUTES
+
+/datum/artifact_effect/bomb/explosive/devastating/New()
+ ..()
+ devast = rand(2,4)
+ heavy = rand(4,6)
+ light = rand(6,16)
+ potency = (devast + heavy + light) * 2.25 // get real
+
+/// GAS BOMB
+
+/datum/artifact_effect/bomb/gas
+ type_name = "Atmospheric Bomb"
+ weight = ARTIFACT_RARE
+ examine_discovered = span_warning("It appears to explode, leaving gasses in its wake!")
+ initial_warning = "begins rattling violenty!"
+ final_message = "reaches a critical pressure, cracks forming at its surface!"
+ var/datum/gas/payload_gas
+ var/list/weighted_gas = list(
+ /datum/gas/plasma = 5,
+ /datum/gas/carbon_dioxide = 10,
+ /datum/gas/nitrous_oxide = 10,
+ /datum/gas/tritium = 5,
+ /datum/gas/hydrogen = 5,
+ /datum/gas/zauker = 2,
+ )
+
+/datum/artifact_effect/bomb/gas/setup()
+ . = ..()
+ payload_gas = pick_weight(weighted_gas)
+
+/datum/artifact_effect/bomb/gas/payload()
+ if(!..())
+ our_artifact.artifact_deactivate()
+ return FALSE
+ var/turf/open/O = get_turf(our_artifact.holder)
+ if(!isopenturf(O))
+ our_artifact.artifact_deactivate()
+ return FALSE
+ var/datum/gas_mixture/merger = new
+ merger.assert_gas(payload_gas)
+ merger.assert_gas(/datum/gas/oxygen)
+ merger.gases[payload_gas][MOLES] = rand(150,2000)
+ merger.gases[/datum/gas/oxygen][MOLES] = 350
+ merger.temperature = rand(200,3000)
+ O.assume_air(merger)
+ qdel(our_artifact)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_effects/bonk.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/bonk.dm
new file mode 100644
index 000000000000..b2fc28a51026
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/bonk.dm
@@ -0,0 +1,40 @@
+/datum/artifact_effect/bonk
+ weight = ARTIFACT_UNCOMMON
+ type_name = "Slammer Effect"
+ activation_message = "opens up!"
+ deactivation_message = "closes up."
+ valid_activators = list(
+ /datum/artifact_activator/touch/carbon,
+ /datum/artifact_activator/touch/silicon
+ )
+ ///force of the hit
+ var/hit_power = 1
+ artifact_size = ARTIFACT_SIZE_LARGE
+ COOLDOWN_DECLARE(bonk_cooldown)
+ research_value = 500
+ examine_discovered = span_warning("It appears to bonk those with heads on said head.")
+
+/datum/artifact_effect/bonk/setup()
+ hit_power = rand(0,35)
+ potency += hit_power
+
+/datum/artifact_effect/bonk/effect_touched(mob/living/user)
+ if(!COOLDOWN_FINISHED(src, bonk_cooldown))
+ return
+
+ if(iscarbon(user))
+ var/mob/living/carbon/carbon = user
+ if(!carbon.get_bodypart(BODY_ZONE_HEAD))
+ our_artifact.holder.say("My condolences to your missing head.") //they can speak uhh galactic common because alien tech idk
+ our_artifact.holder.visible_message(span_notice("[our_artifact.holder] shakes [user][p_s()] hands with an apparatus."))
+ playsound(get_turf(our_artifact.holder), 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+ our_artifact.artifact_deactivate()
+ return
+ else
+ carbon.apply_damage(hit_power, BRUTE, BODY_ZONE_HEAD, carbon.run_armor_check(BODY_ZONE_HEAD, MELEE))
+ our_artifact.holder.visible_message(span_danger("[our_artifact.holder] hits [carbon] over the head!"))
+ else
+ our_artifact.holder.visible_message(span_danger("[our_artifact.holder] slams [user]!"))
+ user.adjustBruteLoss(hit_power)
+ playsound(get_turf(our_artifact.holder), 'sound/misc/bonk.ogg', 80, FALSE)
+ COOLDOWN_START(src, bonk_cooldown, 1.5 SECONDS)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/borger.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/borger.dm
similarity index 73%
rename from monkestation/code/modules/art_sci_overrides/artifact_components/borger.dm
rename to monkestation/code/modules/art_sci_overrides/artifact_effects/borger.dm
index e87a58fbe398..ef3e1ded89aa 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/borger.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/borger.dm
@@ -1,14 +1,15 @@
-/datum/component/artifact/borger
- associated_object = /obj/structure/artifact/borger
- weight = ARTIFACT_UNCOMMON
- type_name = "Borger"
+/datum/artifact_effect/borger
+ weight = ARTIFACT_RARE
+ type_name = "Borger Effect"
activation_message = "opens up!"
deactivation_message = "closes up."
valid_activators = list(
/datum/artifact_activator/touch/carbon,
/datum/artifact_activator/touch/silicon
)
- explict_examine = span_bolddanger("It is vaguely forboding, touching this might be a bad idea...")
+ research_value = 2500
+ examine_hint = span_bolddanger("It is vaguely forboding, touching this might be a bad idea...")
+ examine_discovered = span_bolddanger("It will turn a random limb robotic if touched, touching this might be a bad idea...")
/// The time between each limb replacement
var/limb_replace_time = 1 SECONDS
/// People who've already touched it once. Touching it again will cause it to react.
@@ -16,9 +17,9 @@
/// The cooldown between borgings.
COOLDOWN_DECLARE(borg_cooldown)
-/datum/component/artifact/borger/effect_touched(mob/living/user)
+/datum/artifact_effect/borger/effect_touched(mob/living/user)
if(!iscarbon(user) || !COOLDOWN_FINISHED(src, borg_cooldown) || QDELETED(user.client) || did_robot_touch(user))
- holder.visible_message(span_smallnoticeital("[holder] does not react to [user]."))
+ our_artifact.holder.visible_message(span_smallnoticeital("[our_artifact.holder] does not react to [user]."))
return
if(!LAZYACCESS(first_touched, user))
@@ -54,14 +55,14 @@
addtimer(CALLBACK(carbon_target, TYPE_PROC_REF(/mob/, Robotize)), timer + 5)
COOLDOWN_START(src, borg_cooldown, 10 SECONDS)
-/datum/component/artifact/borger/proc/eat_limb(mob/living/carbon/victim)
+/datum/artifact_effect/borger/proc/eat_limb(mob/living/carbon/victim)
var/arm_name = victim.get_held_index_name(victim.active_hand_index)
- victim.visible_message(span_warning("[holder] lashes out and clamps down on [victim], rapidly transmuting [victim.p_their()] [arm_name]!"), \
- span_userdanger("[holder] lashes out and clamps down onto your [arm_name], rapidly transmuting it into cold metal!"))
+ victim.visible_message(span_warning("[our_artifact.holder] lashes out and clamps down on [victim], rapidly transmuting [victim.p_their()] [arm_name]!"), \
+ span_userdanger("[our_artifact.holder] lashes out and clamps down onto your [arm_name], rapidly transmuting it into cold metal!"))
var/new_arm_type = (victim.active_hand_index % 2) ? /obj/item/bodypart/arm/left/robot : /obj/item/bodypart/arm/right/robot
victim.del_and_replace_bodypart(new new_arm_type)
victim.emote("scream")
-/datum/component/artifact/borger/proc/did_robot_touch(mob/living/carbon/user)
+/datum/artifact_effect/borger/proc/did_robot_touch(mob/living/carbon/user)
var/obj/item/bodypart/arm/active_arm = user.get_active_hand()
return istype(active_arm) && (active_arm.bodytype & BODYTYPE_ROBOTIC)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_effects/bread.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/bread.dm
new file mode 100644
index 000000000000..47e9791fbd5b
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/bread.dm
@@ -0,0 +1,52 @@
+/datum/artifact_effect/bread
+ examine_discovered = "Summons bread?"
+ weight = ARTIFACT_UNCOMMON
+ discovered_credits = CARGO_CRATE_VALUE * 2 //Mmm bread
+ activation_message = "begins baking some fresh eldritch brand bread!"
+ deactivation_message = "runs out of bread!"
+ research_value = 500
+ type_name = "Bread Teleportation Effect"
+
+ COOLDOWN_DECLARE(bread_cd)
+
+ var/bread_counter = 0
+
+ ///We sell BREAD. We sell LOAF. TOASTED, ROASTED...
+ var/static/list/obj/item/food/validbread = list(
+ /obj/item/food/bread/plain = 20,
+ /obj/item/food/bread/meat = 15,
+ /obj/item/food/bread/banana = 15,
+ /obj/item/food/bread/tofu = 10,
+ /obj/item/food/croissant = 10,
+ /obj/item/food/baguette = 10,
+ /obj/item/food/garlicbread = 10,
+ /obj/item/food/bread/creamcheese = 10,
+ /obj/item/food/frenchtoast = 8,
+ /obj/item/food/breadstick = 8,
+ /obj/item/food/butterbiscuit = 5,
+ /obj/item/food/bread/mimana = 5,
+ /obj/item/food/bread/sausage = 5,
+ /obj/item/food/bread/xenomeat = 1,
+ /obj/item/food/bread/spidermeat = 1
+ )
+
+/datum/artifact_effect/bread/effect_process()
+ if(!COOLDOWN_FINISHED(src,bread_cd))
+ return
+ var/center_turf = get_turf(our_artifact.parent)
+ var/list/turf/valid_turfs = list()
+ if(!center_turf)
+ CRASH("[src] had attempted to trigger, but failed to find the center turf!")
+ for(var/turf/boi in range(3,center_turf))
+ if(boi.density)
+ continue
+ valid_turfs += boi
+ var/obj/item/food/pickedbread = pick_weight(validbread)
+ new pickedbread(pick(valid_turfs))
+ bread_counter++
+ if(bread_counter > round(potency/10))
+ bread_counter = 0
+ toggle_active(FALSE)
+ return
+ COOLDOWN_START(src,bread_cd,(7.5 SECONDS))
+
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_effects/cell.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/cell.dm
new file mode 100644
index 000000000000..2da2af3b00ff
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/cell.dm
@@ -0,0 +1,28 @@
+/datum/artifact_effect/cell
+ type_name = "Power Cell Effect"
+ weight = ARTIFACT_UNCOMMON
+ artifact_size = ARTIFACT_SIZE_TINY
+ valid_activators = list(
+ /datum/artifact_activator/range/heat,
+ /datum/artifact_activator/range/shock,
+ /datum/artifact_activator/range/radiation
+ )
+ valid_type_paths = list(/obj/item/stock_parts/cell/artifact)
+ research_value = 500
+ examine_discovered = span_warning("It appears to hold power")
+
+/datum/artifact_effect/cell/setup()
+ var/obj/item/stock_parts/cell/artifact/cell = our_artifact.holder
+ cell.maxcharge = rand(5 KW, 500 MW) // the heavenly battery
+ cell.charge = cell.maxcharge / 2
+ cell.chargerate = rand(5000, round(cell.maxcharge * 0.4))
+ potency += cell.maxcharge / 900
+ potency += cell.chargerate / 4000
+
+/datum/artifact_effect/cell/effect_activate()
+ var/obj/item/stock_parts/cell/artifact/cell = our_artifact.holder
+ cell.ratingdesc = TRUE
+
+/datum/artifact_effect/cell/effect_deactivate()
+ var/obj/item/stock_parts/cell/artifact/cell = our_artifact.holder
+ cell.ratingdesc = FALSE
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_effects/cleaning.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/cleaning.dm
new file mode 100644
index 000000000000..1b61aad86c44
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/cleaning.dm
@@ -0,0 +1,17 @@
+/datum/artifact_effect/soap
+ examine_hint = "Smells nice."
+ examine_discovered = "Seems to clean things."
+ artifact_size = ARTIFACT_SIZE_TINY
+ type_name = "Cleaning Effect"
+
+/datum/artifact_effect/soap/setup()
+ our_artifact.holder.AddComponent(/datum/component/slippery, 80)
+ our_artifact.holder.AddComponent(/datum/component/cleaner, 2 SECOND, 0.1, pre_clean_callback=CALLBACK(src, PROC_REF(should_clean)), on_cleaned_callback=CALLBACK(src, TYPE_PROC_REF(/datum/artifact_effect/soap,sorry_nothing)))
+
+/datum/artifact_effect/soap/proc/should_clean(datum/cleaning_source, atom/atom_to_clean, mob/living/cleaner)
+ if(isitem(our_artifact.holder))
+ var/obj/item/yep_its_an_item = our_artifact.holder
+ return yep_its_an_item.check_allowed_items(atom_to_clean)
+ return FALSE
+/datum/artifact_effect/soap/proc/sorry_nothing(datum/source, atom/target, mob/living/user, clean_succeeded)
+ return
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/emoter.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/emoter.dm
similarity index 60%
rename from monkestation/code/modules/art_sci_overrides/artifact_components/emoter.dm
rename to monkestation/code/modules/art_sci_overrides/artifact_effects/emoter.dm
index 88c88d374bb0..772bd97c0ad5 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/emoter.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/emoter.dm
@@ -1,7 +1,6 @@
-/datum/component/artifact/emotegen
- associated_object = /obj/structure/artifact/emotegen
+/datum/artifact_effect/emotegen
weight = ARTIFACT_UNCOMMON
- type_name = "Emote Forcefield"
+ type_name = "Emote Forcefield Effect"
activation_message = "springs to life and starts emitting a forcefield!"
deactivation_message = "shuts down, its forcefields shutting down with it."
valid_activators = list(
@@ -9,6 +8,9 @@
/datum/artifact_activator/touch/silicon,
/datum/artifact_activator/range/force
)
+
+ research_value = 150
+
var/cooldown_time //cooldown AFTER the shield lowers
var/radius
var/shield_time
@@ -25,10 +27,13 @@
"snore",
"cry",
)
+
+ examine_discovered = span_warning("It appears to radiate an emotional field")
+
var/list/picked_emotes = list()
COOLDOWN_DECLARE(cooldown)
-/datum/component/artifact/emotegen/setup()
+/datum/artifact_effect/emotegen/setup()
for(var/i = 1 to rand(3,4))
picked_emotes += pick(all_emotes)
@@ -39,24 +44,24 @@
cooldown_time = shield_time / 3
potency += radius * 3 + shield_time / 30
-/datum/component/artifact/emotegen/effect_activate(silent)
+/datum/artifact_effect/emotegen/effect_activate(silent)
if(!COOLDOWN_FINISHED(src,cooldown))
- holder.visible_message(span_notice("[holder] wheezes, shutting down."))
- artifact_deactivate(TRUE)
+ our_artifact.holder.visible_message(span_notice("[our_artifact.holder] wheezes, shutting down."))
+ our_artifact.artifact_deactivate(TRUE)
return
- addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/component/artifact, artifact_deactivate)), shield_time)
+ addtimer(CALLBACK(our_artifact, TYPE_PROC_REF(/datum/component/artifact, artifact_deactivate)), shield_time)
COOLDOWN_START(src,cooldown,shield_time + cooldown_time)
-/datum/component/artifact/emotegen/effect_process()
+/datum/artifact_effect/emotegen/effect_process()
var/current_emote = pick(picked_emotes)
- holder.anchored = TRUE
- var/turf/our_turf = get_turf(holder)
- for(var/turf/open/floor in range(radius,holder))
+ our_artifact.holder.anchored = TRUE
+ var/turf/our_turf = get_turf(our_artifact.holder)
+ for(var/turf/open/floor in range(radius,our_artifact.holder))
if(floor == our_turf)
continue
for(var/mob/living/living in floor)
living.emote(current_emote, intentional = FALSE)
-/datum/component/artifact/emotegen/effect_deactivate()
- holder.anchored = FALSE
+/datum/artifact_effect/emotegen/effect_deactivate()
+ our_artifact.holder.anchored = FALSE
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_effects/false_rod.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/false_rod.dm
new file mode 100644
index 000000000000..27030a251101
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/false_rod.dm
@@ -0,0 +1,93 @@
+/datum/artifact_effect/false_rod
+ examine_hint = span_warning("You feel a binding aura connected to this.")
+ examine_discovered = span_warning("Will bind it self to the wearer, forcing upon them an oath to heal!")
+ weight = ARTIFACT_UNCOMMON
+
+ artifact_size = ARTIFACT_SIZE_SMALL
+ type_name = "Oath-Bearing Rod Effect"
+
+ var/list/first_touched
+
+ var/mob/living/our_victim
+
+ COOLDOWN_DECLARE(touch_cooldown)
+
+/datum/artifact_effect/false_rod/setup()
+ RegisterSignal(our_artifact.holder,COMSIG_ITEM_POST_EQUIPPED,TYPE_PROC_REF(/datum/artifact_effect/false_rod,on_pickup))
+
+/datum/artifact_effect/false_rod/proc/on_pickup(obj/item/the_item,mob/taker,slot)
+ SIGNAL_HANDLER
+ if(!isliving(taker))
+ return COMPONENT_EQUIPPED_FAILED
+ var/mob/living/user = taker
+ if(!(isitem(our_artifact.holder)) || !COOLDOWN_FINISHED(src,touch_cooldown))
+ return COMPONENT_EQUIPPED_FAILED
+ if(!LAZYACCESS(first_touched, user))
+ to_chat(user,span_bolddanger("You hesitate before touching [our_artifact.holder], feeling it will do something that cannot be un-done easily!"))
+ LAZYSET(first_touched, user, TRUE)
+ COOLDOWN_START(src, touch_cooldown, 7.5 SECONDS) // so you don't get fucked over by spam-clicking it
+ return COMPONENT_EQUIPPED_FAILED
+ addtimer(CALLBACK(src,PROC_REF(post_pickup),user),(0.2 SECOND))
+ return
+
+/datum/artifact_effect/false_rod/on_destroy(atom/source)
+ our_victim?.remove_status_effect(/datum/status_effect/forced_oath)
+ . = ..()
+
+/datum/artifact_effect/false_rod/proc/post_pickup(mob/living/user)
+ to_chat(user,span_danger("[our_artifact.holder] forcefully melds with you, and a healing aura surrounds you!"))
+ ADD_TRAIT(our_artifact.holder,TRAIT_NODROP,CURSED_ITEM_TRAIT(our_artifact.holder.type))
+ user.apply_status_effect(/datum/status_effect/forced_oath)
+ our_victim = user
+ return
+
+/datum/status_effect/forced_oath
+ id = "Forced Oath"
+ status_type = STATUS_EFFECT_UNIQUE
+ duration = -1
+ tick_interval = 25
+ alert_type = null
+ var/datum/component/aura_healing/our_aura
+
+/datum/status_effect/forced_oath/on_apply()
+ var/static/list/organ_healing = list(
+ ORGAN_SLOT_BRAIN = 0.7,
+ )
+ //This is literally shitty Rod of Ascep.
+ our_aura = owner.AddComponent( \
+ /datum/component/aura_healing, \
+ range = 5, \
+ brute_heal = 1, \
+ burn_heal = 1, \
+ toxin_heal = 1, \
+ suffocation_heal = 1, \
+ stamina_heal = 1, \
+ clone_heal = 0.2, \
+ simple_heal = 1, \
+ organ_healing = organ_healing, \
+ healing_color = "#375637", \
+ )
+
+ var/datum/atom_hud/med_hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
+ med_hud.show_to(owner)
+ return TRUE
+/datum/status_effect/forced_oath/on_remove()
+ QDEL_NULL(our_aura)
+ var/datum/atom_hud/med_hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
+ med_hud.hide_from(owner)
+/datum/status_effect/forced_oath/get_examine_text()
+ return span_notice("[owner.p_they(TRUE)] seem[owner.p_s()] to have an aura of healing around [owner.p_them()].")
+/datum/status_effect/forced_oath/tick()
+ if(owner.stat ==DEAD)
+ return
+ else
+ if(iscarbon(owner))
+ if(owner.health < owner.maxHealth)
+ new /obj/effect/temp_visual/heal(get_turf(owner), "#375637")
+ owner.adjustBruteLoss(-1)
+ owner.adjustFireLoss(-1)
+ owner.adjustToxLoss(-1, forced = TRUE) //Because Slime People are people too
+ owner.adjustOxyLoss(-1, forced = TRUE)
+ owner.stamina.adjust(1)
+ owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1)
+ owner.adjustCloneLoss(-0.25) //Becasue apparently clone damage is the bastion of all health
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/forcegen.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/forcegen.dm
similarity index 68%
rename from monkestation/code/modules/art_sci_overrides/artifact_components/forcegen.dm
rename to monkestation/code/modules/art_sci_overrides/artifact_effects/forcegen.dm
index 95be5fbb3305..04608b5b8764 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/forcegen.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/forcegen.dm
@@ -11,13 +11,13 @@
resistance_flags = INDESTRUCTIBLE
can_atmos_pass = ATMOS_PASS_DENSITY
+
/obj/structure/artifact_forcefield/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
playsound(loc, 'sound/weapons/egloves.ogg', 80, TRUE)
-/datum/component/artifact/forcegen
- associated_object = /obj/structure/artifact/forcegen
+/datum/artifact_effect/forcegen
weight = ARTIFACT_UNCOMMON
- type_name = "Forcefield Generator"
+ type_name = "Forcefield Generator Effect"
activation_message = "springs to life and starts emitting a forcefield!"
deactivation_message = "shuts down, its forcefields shutting down with it."
valid_activators = list(
@@ -25,6 +25,11 @@
/datum/artifact_activator/touch/silicon,
/datum/artifact_activator/range/force
)
+
+ research_value = 5000
+
+ examine_discovered = span_warning("It appears to generate some kind of forcefield")
+
var/cooldown_time //cooldown AFTER the shield lowers
var/shield_iconstate
var/list/projected_forcefields = list()
@@ -32,26 +37,25 @@
var/shield_time
COOLDOWN_DECLARE(cooldown)
-/datum/component/artifact/forcegen/setup()
+/datum/artifact_effect/forcegen/setup()
shield_iconstate = pick("shieldsparkles","empdisable","shield2","shield-old","shield-red","shield-green","shield-yellow")
activation_sound = pick('sound/mecha/mech_shield_drop.ogg')
deactivation_sound = pick('sound/mecha/mech_shield_raise.ogg','sound/magic/forcewall.ogg')
- shield_time = rand(10,40) SECONDS
+ shield_time = rand(10,30) SECONDS
radius = rand(1,3)
cooldown_time = shield_time / 3
- potency += radius * 3 + shield_time / 30
-/datum/component/artifact/forcegen/effect_activate()
+/datum/artifact_effect/forcegen/effect_activate()
if(!COOLDOWN_FINISHED(src,cooldown))
- holder.visible_message(span_notice("[holder] wheezes, shutting down."))
- artifact_deactivate(TRUE)
+ our_artifact.holder.visible_message(span_notice("[our_artifact.holder] wheezes, shutting down."))
+ our_artifact.artifact_deactivate(TRUE)
return
- holder.anchored = TRUE
- var/turf/our_turf = get_turf(holder)
+ our_artifact.holder.anchored = TRUE
+ var/turf/our_turf = get_turf(our_artifact.holder)
var/list/bad_turfs
if(radius > 1)
- bad_turfs = range(radius - 1, holder)
- for(var/turf/open/floor in range(radius,holder))
+ bad_turfs = range(radius - 1, our_artifact.holder)
+ for(var/turf/open/floor in range(radius,our_artifact.holder))
if(floor in bad_turfs)
continue
if(floor == our_turf)
@@ -59,11 +63,11 @@
var/obj/field = new /obj/structure/artifact_forcefield(floor)
field.icon_state = shield_iconstate
projected_forcefields += field
- addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/component/artifact, artifact_deactivate)), shield_time)
+ addtimer(CALLBACK(our_artifact, TYPE_PROC_REF(/datum/component/artifact, artifact_deactivate)), shield_time)
COOLDOWN_START(src,cooldown,shield_time + cooldown_time)
-/datum/component/artifact/forcegen/effect_deactivate()
- holder.anchored = FALSE
+/datum/artifact_effect/forcegen/effect_deactivate()
+ our_artifact.holder.anchored = FALSE
for(var/obj/field in projected_forcefields)
projected_forcefields -= field
qdel(field)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/gun.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/gun.dm
similarity index 75%
rename from monkestation/code/modules/art_sci_overrides/artifact_components/gun.dm
rename to monkestation/code/modules/art_sci_overrides/artifact_effects/gun.dm
index 7425c524e47a..a574230764ad 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/gun.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/gun.dm
@@ -1,26 +1,20 @@
#define LOWEST_POSSIBLE_CLICK_CD 3
#define HIGHEST_POSSIBLE_CLICK_CD 15
-/datum/component/artifact/gun
- associated_object = /obj/item/gun/magic/artifact
+/datum/artifact_effect/gun
artifact_size = ARTIFACT_SIZE_SMALL
- type_name = "Ranged Weapon"
+ type_name = "Ranged Weapon Effect"
weight = ARTIFACT_VERYUNCOMMON //rare
- xray_result = "COMPLEX"
valid_activators = list(
/datum/artifact_activator/range/heat,
/datum/artifact_activator/range/shock,
/datum/artifact_activator/range/radiation
)
- valid_faults = list(
- /datum/artifact_fault/ignite = 10,
- /datum/artifact_fault/warp = 10,
- /datum/artifact_fault/reagent/poison = 10,
- /datum/artifact_fault/death = 2,
- /datum/artifact_fault/tesla_zap = 5,
- /datum/artifact_fault/grow = 10,
- /datum/artifact_fault/explosion = 2,
- )
+ valid_type_paths = list(/obj/item/gun/magic/artifact)
+
+ research_value = 500
+
+ examine_discovered = span_warning("It appears to be some sort of projectile weapon!")
//list of projectile exclusive projectiles
///damage each shot does
@@ -46,14 +40,11 @@
var/list/damage_types = list(
BRUTE,
BURN,
- TOX,
- OXY,
- BRAIN,
- STAMINA
+ TOX
)
-/datum/component/artifact/gun/setup()
- var/obj/item/gun/magic/artifact/our_wand = holder
+/datum/artifact_effect/gun/setup()
+ var/obj/item/gun/magic/artifact/our_wand = our_artifact.holder
var/obj/item/ammo_casing/casing = our_wand.chambered
//randomize our casing
casing.click_cooldown_override = rand(LOWEST_POSSIBLE_CLICK_CD, HIGHEST_POSSIBLE_CLICK_CD)
@@ -62,7 +53,7 @@
spread += 0.1
spread += prob(65) ? rand(0.0, 0.2) : rand(0.3, 1.0)
- damage = rand(-5, 25)
+ damage = rand(-10, 30)
projectile_icon = pick("energy","scatterlaser","toxin","energy","spell","pulse1","bluespace","gauss","gaussweak","gaussstrong","redtrac","omnilaser","heavylaser","laser","infernoshot","cryoshot","arcane_barrage")
dam_type = pick(damage_types)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/heal.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/heal.dm
similarity index 59%
rename from monkestation/code/modules/art_sci_overrides/artifact_components/heal.dm
rename to monkestation/code/modules/art_sci_overrides/artifact_effects/heal.dm
index 9177d7dea38e..67c2e1a535f3 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/heal.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/heal.dm
@@ -1,22 +1,23 @@
#define PROCESSES_PER_HEAL 5
-/datum/component/artifact/heal
- associated_object = /obj/structure/artifact/heal
+/datum/artifact_effect/heal
weight = ARTIFACT_VERYUNCOMMON
- type_name = "Single Healer"
+ type_name = "Single Healer Effect"
activation_message = "starts emitting a soothing aura!"
deactivation_message = "becomes silent."
valid_activators = list(
/datum/artifact_activator/touch/carbon,
/datum/artifact_activator/touch/silicon
)
- ///list of damage types we heal, this is randomly removed from at setup
+
+ examine_discovered = span_warning("It appears to heal those who touch it.")
+
+ research_value = 250
+
+ ///list of what we heal
var/list/damage_types = list(
BRUTE,
BURN,
- TOX,
- OXY,
- BRAIN,
- CLONE,
+ TOX
)
///how much do we heal
var/heal_amount
@@ -24,29 +25,19 @@
var/process_count = 0
COOLDOWN_DECLARE(heal_cooldown)
-/datum/component/artifact/heal/setup()
- heal_amount = rand(1,15)
- potency += heal_amount
- var/type_amount = prob(75) ? 4 : rand(2,4) //75% to remove 4 types for 1 heal type or 25% for 2 or 4 types removed
- while(type_amount)
- type_amount--
- damage_types -= pick(damage_types)
- potency += 5 * (length(damage_types) - 1)
+/datum/artifact_effect/heal/setup()
+ heal_amount = rand(5,10)
-/datum/component/artifact/heal/effect_touched(mob/living/user)
+/datum/artifact_effect/heal/effect_touched(mob/living/user)
if(!COOLDOWN_FINISHED(src, heal_cooldown))
return
- var/damage_length = length(damage_types)
for(var/dam_type in damage_types)
- user.heal_damage_type( (heal_amount / damage_length), dam_type)
+ user.heal_damage_type( (heal_amount), dam_type)
to_chat(user, span_notice("You feel slightly refreshed!"))
new /obj/effect/temp_visual/heal(get_turf(user), COLOR_HEALING_CYAN)
COOLDOWN_START(src, heal_cooldown, 5 SECONDS)
-/datum/component/artifact/heal/effect_process()
- if(potency < 75)
- return
-
+/datum/artifact_effect/heal/effect_process()
process_count++
if(process_count < PROCESSES_PER_HEAL)
return
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/injector.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/injector.dm
similarity index 64%
rename from monkestation/code/modules/art_sci_overrides/artifact_components/injector.dm
rename to monkestation/code/modules/art_sci_overrides/artifact_effects/injector.dm
index dc30a0192443..3d82ebf98ed0 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/injector.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/injector.dm
@@ -1,18 +1,20 @@
-/datum/component/artifact/injector
- associated_object = /obj/structure/artifact/injector
+/datum/artifact_effect/injector
weight = ARTIFACT_UNCOMMON
- type_name = "Injector"
+ type_name = "Injector Effect"
activation_message = "opens up to reveal a large needle!"
deactivation_message = "pulls its needle inside, closing itself up."
- xray_result = "SEGMENTED"
var/max_reagents // the total amount to dose the victim with
var/reagent_amount
var/list/reagent_datums = list()
var/cooldown_time = 10 SECONDS
COOLDOWN_DECLARE(activation_cooldown)
-/datum/component/artifact/injector/setup()
- holder.create_reagents(200, NO_REACT | SEALED_CONTAINER)
+ research_value = 250
+
+ examine_discovered = span_warning("It appears to be some sort of chemical injector")
+
+/datum/artifact_effect/injector/setup()
+ our_artifact.holder.create_reagents(200, NO_REACT | SEALED_CONTAINER)
reagent_amount = rand(10,25)
max_reagents = rand(1,2)
var/static/list/poisons_and_medicines = list()
@@ -20,7 +22,7 @@
for(var/datum/reagent/reagent as anything in (subtypesof(/datum/reagent/toxin) + subtypesof(/datum/reagent/medicine)))
if(initial(reagent.chemical_flags) & REAGENT_CAN_BE_SYNTHESIZED)
poisons_and_medicines += reagent
- switch(artifact_origin.type_name)
+ switch(our_artifact.artifact_origin.type_name)
if(ORIGIN_NARSIE)
for(var/i in 1 to max_reagents)
reagent_datums += pick(poisons_and_medicines) //cult likes killing people ok
@@ -36,12 +38,12 @@
reagent_datums += pick(silicon_reagents)
potency += reagent_amount + max_reagents
-/datum/component/artifact/injector/effect_touched(mob/living/user)
+/datum/artifact_effect/injector/effect_touched(mob/living/user)
if(!ishuman(user) || !COOLDOWN_FINISHED(src,activation_cooldown))
- holder.visible_message(span_smallnoticeital("[holder] does not react to [user]."))
+ our_artifact.holder.visible_message(span_smallnoticeital("[our_artifact.holder] does not react to [user]."))
return
for(var/reagent in reagent_datums)
- holder.reagents.add_reagent(reagent, reagent_amount / reagent_datums.len)
- holder.visible_message(span_danger("[holder] pricks [user] with its needle!"), span_userdanger("OW! You are pricked by [holder]!"))
- holder.reagents.trans_to(user, holder.reagents.total_volume, transfered_by = holder, methods = INJECT)
+ our_artifact.holder.reagents.add_reagent(reagent, reagent_amount / reagent_datums.len)
+ our_artifact.holder.visible_message(span_danger("[our_artifact.holder] pricks [user] with its needle!"), span_userdanger("OW! You are pricked by [our_artifact.holder]!"))
+ our_artifact.holder.reagents.trans_to(user, our_artifact.holder.reagents.total_volume, transfered_by = our_artifact.holder, methods = INJECT)
COOLDOWN_START(src,activation_cooldown,cooldown_time)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_effects/itsasecret.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/itsasecret.dm
new file mode 100644
index 000000000000..bcda23c9678a
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/itsasecret.dm
@@ -0,0 +1,44 @@
+/datum/artifact_effect/toeverybody
+ examine_hint = "Seems...off somehow."
+ examine_discovered = "Temporarily tears holes in reality."
+ discovered_credits = CARGO_CRATE_VALUE * 100 //GDM
+ weight = ARTIFACT_VERYRARE/5 //Super rare
+ activation_message = "tears open the fabric of reality!"
+
+ research_value = 10000
+
+ type_name = "Its a Secret to Everybody"
+
+ super_secret = TRUE
+
+ COOLDOWN_DECLARE(trigger_cd)
+
+/datum/artifact_effect/toeverybody/proc/returnthey(mob/living/carbon/human,turf/last_position)
+ human.forceMove(last_position)
+ return
+
+/datum/artifact_effect/toeverybody/effect_activate(silent)
+ if(!COOLDOWN_FINISHED(src,trigger_cd))
+ return
+ var/list/mobs = list()
+ var/mob/living/carbon/human
+
+ var/center_turf = get_turf(our_artifact.parent)
+
+ if(!center_turf)
+ CRASH("[src] had attempted to trigger, but failed to find the center turf!")
+
+ for(var/mob/living/carbon/mob in range(rand(3, 4), center_turf))
+ mobs += mob
+ if(!length(mobs))
+ return
+ human = pick(mobs)
+ if(!human)
+ return
+
+ var/last_position = get_turf(human)
+ human.move_to_error_room()
+ COOLDOWN_START(src,trigger_cd,5 MINUTE)
+ addtimer(CALLBACK(src,PROC_REF(returnthey),human,last_position),5 SECOND)
+ return
+
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_effects/lamp.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/lamp.dm
new file mode 100644
index 000000000000..9e4d4ae0dd1e
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/lamp.dm
@@ -0,0 +1,38 @@
+
+/datum/artifact_effect/lamp
+ weight = ARTIFACT_COMMON
+ type_name = "Lamp"
+ activation_message = "starts shining!"
+ deactivation_message = "stops shining."
+ examine_discovered = span_warning("It appears to be some sort of light source")
+
+ artifact_size = ARTIFACT_SIZE_LARGE
+
+ research_value = 250
+
+/datum/artifact_effect/lamp/setup()
+ var/power
+ var/color = pick(COLOR_RED, COLOR_BLUE, COLOR_YELLOW, COLOR_GREEN, COLOR_PURPLE, COLOR_ORANGE)
+ var/range
+ switch(rand(1,100))
+ if(1 to 75)
+ power = rand(2,5)
+ range = rand(2,5)
+ if(76 to 100)
+ range = rand(4,10)
+ power = rand(5,10) // the sun
+
+ if(our_artifact.artifact_origin.type_name == ORIGIN_NARSIE && prob(40))
+ color = COLOR_BLACK
+ our_artifact.holder.light_system = COMPLEX_LIGHT //We need this to avoid a crash for wrong lighting system.
+ our_artifact.holder.set_light(range, round(range*1.25),power,l_color = color,l_on = FALSE)
+
+/datum/artifact_effect/lamp/effect_touched(mob/user)
+ our_artifact.holder.set_light_on(!our_artifact.holder.light_on) //toggle
+ to_chat(user, span_hear("[our_artifact.holder] clicks."))
+
+/datum/artifact_effect/lamp/effect_activate()
+ our_artifact.holder.set_light_on(TRUE)
+
+/datum/artifact_effect/lamp/effect_deactivate()
+ our_artifact.holder.set_light_on(FALSE)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_effects/meat.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/meat.dm
new file mode 100644
index 000000000000..5781d29458ca
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/meat.dm
@@ -0,0 +1,54 @@
+/datum/artifact_effect/meat
+ examine_discovered = "Summons meat?"
+ weight = ARTIFACT_UNCOMMON
+ discovered_credits = CARGO_CRATE_VALUE * 2 //ME A T
+ activation_message = "begins stealing meat!"
+ deactivation_message = "gets caught stealing meat!"
+ research_value = 500
+ type_name = "Meat Teleportation Effect"
+
+ COOLDOWN_DECLARE(meat_cd)
+
+ var/meat_counter = 0
+
+ ///MY MEAT BYCICLE IS BIGGER THAN YOURS - Kreig borderlands
+ var/static/list/obj/item/food/validmeat = list(
+ /obj/item/food/meat/slab = 20,
+ /obj/item/food/meat/slab/meatwheat = 20,
+ /obj/item/food/meat/slab/monkey = 15,
+ /obj/item/food/meat/slab/bugmeat = 15,
+ /obj/item/food/meat/slab/mothroach = 15,
+ /obj/item/food/meat/slab/mouse = 15,
+ /obj/item/food/meat/slab/pug = 10,
+ /obj/item/food/meat/slab/goliath = 10,
+ /obj/item/food/meat/slab/corgi = 10,
+ /obj/item/food/meat/slab/gorilla = 5,
+ /obj/item/food/meat/slab/bear = 5,
+ /obj/item/food/meat/slab/xeno = 5,
+ /obj/item/food/meat/slab/spider = 5,
+ /obj/item/food/meat/slab/human/mutant/slime = 1,
+ /obj/item/food/meat/slab/human/mutant/lizard = 1,
+ /obj/item/food/meat/slab/human = 1,
+ /obj/item/food/meat/slab/human/mutant/skeleton = 1,
+ )
+
+/datum/artifact_effect/meat/effect_process()
+ if(!COOLDOWN_FINISHED(src,meat_cd))
+ return
+ var/center_turf = get_turf(our_artifact.parent)
+ var/list/turf/valid_turfs = list()
+ if(!center_turf)
+ CRASH("[src] had attempted to trigger, but failed to find the center turf!")
+ for(var/turf/boi in range(3,center_turf))
+ if(boi.density)
+ continue
+ valid_turfs += boi
+ var/obj/item/food/pickedmeat = pick_weight(validmeat)
+ new pickedmeat(pick(valid_turfs))
+ meat_counter++
+ if(meat_counter > round(potency/10))
+ meat_counter = 0
+ toggle_active(FALSE)
+ return
+ COOLDOWN_START(src,meat_cd,(10 SECONDS))
+
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/melee.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/melee.dm
similarity index 72%
rename from monkestation/code/modules/art_sci_overrides/artifact_components/melee.dm
rename to monkestation/code/modules/art_sci_overrides/artifact_effects/melee.dm
index ea9e5a93e826..2d64f29344e3 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/melee.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/melee.dm
@@ -2,33 +2,27 @@
#define SPECIAL_IGNITE "ignite"
#define SPECIAL_TELEPORT "teleport"
-/datum/component/artifact/melee
- associated_object = /obj/item/melee/artifact
+/datum/artifact_effect/melee
artifact_size = ARTIFACT_SIZE_SMALL
- type_name = "Melee Weapon"
+ type_name = "Melee Weapon Effect"
weight = ARTIFACT_VERYUNCOMMON //rare
- xray_result = "DENSE"
valid_activators = list(
/datum/artifact_activator/touch/silicon,
/datum/artifact_activator/range/heat,
/datum/artifact_activator/range/shock,
/datum/artifact_activator/range/radiation
)
- valid_faults = list(
- /datum/artifact_fault/ignite = 10,
- /datum/artifact_fault/warp = 10,
- /datum/artifact_fault/reagent/poison = 10,
- /datum/artifact_fault/death = 2,
- /datum/artifact_fault/tesla_zap = 5,
- /datum/artifact_fault/grow = 10,
- /datum/artifact_fault/explosion = 2,
- )
var/active_force //force when active
var/active_reach
var/active_woundbonus = 0
-/datum/component/artifact/melee/setup() //RNG incarnate
- var/obj/item/melee/artifact/weapon = holder
+ valid_type_paths = list(/obj/item/melee/artifact)
+ research_value = 500
+
+ examine_discovered = span_warning("It appears to be some sort of melee weapon")
+
+/datum/artifact_effect/melee/setup() //RNG incarnate
+ var/obj/item/melee/artifact/weapon = our_artifact.holder
weapon.special_cooldown_time = rand(3,8) SECONDS
active_force = rand(-10,30)
weapon.demolition_mod = rand(-1.0, 2.0)
@@ -58,15 +52,15 @@
potency += 15
weapon.special = pick(SPECIAL_LAUNCH, SPECIAL_IGNITE, SPECIAL_TELEPORT)
-/datum/component/artifact/melee/effect_activate()
- var/obj/item/melee/artifact/weapon = holder
+/datum/artifact_effect/melee/effect_activate()
+ var/obj/item/melee/artifact/weapon = our_artifact.holder
weapon.reach = active_reach
weapon.force = active_force
weapon.wound_bonus = active_woundbonus
weapon.throwforce = weapon.force
-/datum/component/artifact/melee/effect_deactivate()
- var/obj/item/melee/artifact/weapon = holder
+/datum/artifact_effect/melee/effect_deactivate()
+ var/obj/item/melee/artifact/weapon = our_artifact.holder
weapon.force = active_force / 3
weapon.throwforce = weapon.force
weapon.reach = 1
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_effects/money.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/money.dm
new file mode 100644
index 000000000000..6f40d0405932
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/money.dm
@@ -0,0 +1,12 @@
+/datum/artifact_effect/lodedsamoney
+ examine_hint = "Might sell well."
+ examine_discovered = "Generates an aura of value."
+ discovered_credits = CARGO_CRATE_VALUE * 25 //LODESA
+ weight = ARTIFACT_UNCOMMON
+
+ type_name = "Economical Aura Effect"
+ research_value = 250
+
+/datum/artifact_effect/lodedsamoney/setup()
+ discovered_credits *= ((potency+50)/75)
+ discovered_credits = round(discovered_credits)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_effects/narsieoffering.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/narsieoffering.dm
new file mode 100644
index 000000000000..6d1cc4510108
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/narsieoffering.dm
@@ -0,0 +1,52 @@
+/datum/artifact_effect/narsieoffering
+ examine_discovered = "Makes an offering to the dark gods."
+ type_name = "Dark Altar Effect"
+ weight = ARTIFACT_COMMON //Common because can only be on carbon touch narsie
+
+ examine_hint = span_warning("You feel safe and complacent around this...")
+ valid_origins = list(ORIGIN_NARSIE)
+
+ valid_activators = list(/datum/artifact_activator/touch/carbon)
+
+ research_value = 250
+
+ var/blood_to_take = 1
+
+ var/stored_blood = 0
+
+ COOLDOWN_DECLARE(force_take_cooldown)
+ ///List of valid items this artifact can spawn when full on blood (needs 5 bodys of blood.)
+ var/static/list/obj/valid_spawns = list(
+ /obj/item/soulstone/anybody = 1, //Lucky you.
+ /obj/item/clothing/suit/hooded/cultrobes = 20,
+ /obj/item/clothing/suit/hooded/cultrobes/alt = 20,
+ /obj/item/clothing/suit/hooded/cultrobes/hardened = 10,
+ /obj/item/sharpener/cult = 35,
+ /obj/item/shield/mirror = 15
+ )
+
+/datum/artifact_effect/narsieoffering/setup()
+ blood_to_take = round(rand(BLOOD_VOLUME_NORMAL*0.1,BLOOD_VOLUME_NORMAL*0.9))
+
+/datum/artifact_effect/narsieoffering/effect_touched(mob/living/user)
+ if(!COOLDOWN_FINISHED(src,force_take_cooldown))
+ to_chat(user,"You feel [our_artifact.holder] is not yet ready for what it has planned...")
+ if(!user.blood_volume || !iscarbon(user))
+ to_chat(user, span_info("You feel a need to give your non existant blood."))
+ if(user.blood_volume <= blood_to_take)
+ to_chat(user,span_info("You feel a need to give more blood, but [our_artifact.holder] deems you too weak to do so!"))
+ var/yoinked_blood = min(blood_to_take,user.blood_volume)
+ user.blood_volume -= yoinked_blood
+ stored_blood += yoinked_blood
+ to_chat(user,span_boldwarning("You are compelled to give blood to the [our_artifact.holder]; and feel your blood volume lower somehow!"))
+ COOLDOWN_START(src,force_take_cooldown,5 SECOND)
+
+ if(stored_blood >= BLOOD_VOLUME_NORMAL*5)
+ var/obj/tomake = pick_weight(valid_spawns)
+ var/obj/chosen = new tomake(our_artifact.holder.loc)
+ chosen.forceMove(our_artifact.holder.loc)
+ to_chat(user,span_info("[our_artifact.holder] is pleased with your work, and [chosen] appears from seemingly nowhere!"))
+ stored_blood -= BLOOD_VOLUME_NORMAL*5
+ return
+
+
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_effects/plushie_vendor.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/plushie_vendor.dm
new file mode 100644
index 000000000000..9e3642f159fa
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/plushie_vendor.dm
@@ -0,0 +1,30 @@
+/datum/artifact_effect/plushie
+ examine_hint = "Has some sort of claw mechanism."
+
+ examine_discovered = "Its a claw machine of some kind"
+
+ weight = ARTIFACT_UNCOMMON
+
+ activation_message = "summons a toy of some kind!"
+
+ type_name = "Toy Vender Effect"
+ research_value = 250
+
+ var/static/list/obj/item/toy/plush/plushies = list()
+
+ COOLDOWN_DECLARE(plushiefact)
+
+/datum/artifact_effect/plushie/effect_activate(silent)
+ if(!length(plushies))
+ plushies = typecacheof(/obj/item/toy/plush,ignore_root_path = TRUE) //I am not responsible for if this is a bad idea.
+ if(!COOLDOWN_FINISHED(src,plushiefact))
+ return
+ var/obj/item/toy/plush/boi_path = pick(plushies)
+ var/obj/item/toy/plush/boi = new boi_path
+ boi.forceMove(our_artifact.holder.loc)
+ if(prob(clamp(potency-50,0,100)))
+ boi.AddComponent(/datum/component/ghost_object_control,boi,TRUE)
+ var/datum/component/ghost_object_control/spiritholder = boi.GetComponent(/datum/component/ghost_object_control)
+ if(!(spiritholder.bound_spirit))
+ spiritholder.request_control(0.6)
+ COOLDOWN_START(src,plushiefact,3 MINUTE)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_effects/repulsor.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/repulsor.dm
new file mode 100644
index 000000000000..8cefc3bbcfde
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/repulsor.dm
@@ -0,0 +1,55 @@
+
+/datum/artifact_effect/repulsor
+ weight = ARTIFACT_UNCOMMON
+ type_name = "Repulsor/Impulsor Effect"
+ activation_message = "opens up, a weird aura starts emitting from it!"
+ deactivation_message = "closes up."
+ var/attract = FALSE //if FALSE, repulse, otherwise, attract
+ var/strength
+ var/range
+ var/cooldown_time
+ COOLDOWN_DECLARE(cooldown)
+
+ research_value = 1000
+
+ examine_discovered = span_warning("It appears to be some object mover")
+
+/datum/artifact_effect/repulsor/setup()
+ attract = prob(40)
+ range = rand(1,3)
+ cooldown_time = rand(3,5) SECONDS
+ strength = rand(MOVE_FORCE_DEFAULT,MOVE_FORCE_OVERPOWERING)
+ potency += cooldown_time / 4 + strength / 3000
+
+/datum/artifact_effect/repulsor/effect_activate(silent)
+ addtimer(CALLBACK(our_artifact, TYPE_PROC_REF(/datum/component/artifact, artifact_deactivate)), 10 SECONDS)
+
+/datum/artifact_effect/repulsor/effect_touched(mob/user)
+ if(!COOLDOWN_FINISHED(src,cooldown))
+ return
+ pulse()
+ COOLDOWN_START(src,cooldown,cooldown_time)
+
+/datum/artifact_effect/repulsor/effect_process()
+ . = ..()
+ if(prob(100 - potency))
+ return
+ pulse()
+
+/datum/artifact_effect/repulsor/proc/pulse(datum/source,atom/movable/thrown, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
+ SIGNAL_HANDLER
+ if(!our_artifact.active)
+ return
+ our_artifact.holder.visible_message(span_warning("[our_artifact.holder] emits a pulse of energy, throwing things [attract ? "towards it!" : "away from it!"]"))
+ var/owner_turf = get_turf(our_artifact.holder)
+ if(isnull(thrown))
+ for(var/atom/movable/throwee in oview(range,our_artifact.holder))
+ if(throwee.anchored)
+ continue
+ if(attract)
+ throwee.safe_throw_at(our_artifact.holder, strength / 3000, 1, force = strength)
+ else
+ var/throwtarget = get_edge_target_turf(get_turf(throwee), get_dir(owner_turf, get_step_away(throwee, owner_turf)))
+ throwee.safe_throw_at(throwtarget, strength / 3000, 1, force = strength)
+ else if(throwingdatum?.thrower)
+ thrown.safe_throw_at(throwingdatum.thrower, get_dist(our_artifact.holder, throwingdatum.thrower), 1, force = strength)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/smoke_artifacts.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/smoke_artifacts.dm
similarity index 55%
rename from monkestation/code/modules/art_sci_overrides/artifact_components/smoke_artifacts.dm
rename to monkestation/code/modules/art_sci_overrides/artifact_effects/smoke_artifacts.dm
index 9255e0a53706..5a1e83890be3 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/smoke_artifacts.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/smoke_artifacts.dm
@@ -1,10 +1,13 @@
-/datum/component/artifact/smoke
- associated_object = /obj/structure/artifact/smoke
+/datum/artifact_effect/smoke
weight = ARTIFACT_UNCOMMON
- type_name = "Smoke Machine"
- activation_message = "starts spewing out smoke!"
+ type_name = "Smoke Machine (Colorful) Effect"
+ activation_message = "starts spewing out colorful smoke!"
deactivation_message = "becomes silent."
+ research_value = 250
+
+ examine_discovered = span_warning("It appears to be some sort of checmical aerolyzer for coloring things")
+
var/list/valid_chemicals = list(
/datum/reagent/colorful_reagent,
/datum/reagent/colorful_reagent/powder/black,
@@ -19,7 +22,7 @@
var/list/chemicals = list()
var/smoke_range = 3
-/datum/component/artifact/smoke/setup()
+/datum/artifact_effect/smoke/setup()
per_chemical_amount = rand(5, 10)
chemicals_chosen = rand(1, 5)
smoke_range = rand(1, 5)
@@ -28,14 +31,15 @@
for(var/i = 1 to chemicals_chosen)
chemicals += pick(valid_chemicals)
-/datum/component/artifact/smoke/effect_activate(silent)
- for(var/chemical in chemicals)
- do_chem_smoke(smoke_range, holder = holder, location = get_turf(holder), reagent_type = chemical, reagent_volume = per_chemical_amount, log = TRUE)
- artifact_deactivate()
+/datum/artifact_effect/smoke/effect_activate(silent)
+ if(our_artifact.holder)
+ for(var/chemical in chemicals)
+ do_chem_smoke(smoke_range, holder = our_artifact.holder, location = get_turf(our_artifact.holder), reagent_type = chemical, reagent_volume = per_chemical_amount, log = TRUE)
+ our_artifact.artifact_deactivate()
-/datum/component/artifact/smoke/toxin
- associated_object = /obj/structure/artifact/smoke/toxin
+/datum/artifact_effect/smoke/toxin
weight = ARTIFACT_RARE
+ type_name = "Smoke Machine (Harmful) Effect"
activation_message = "starts spewing out toxic smoke!"
valid_chemicals = list(
/datum/reagent/toxin/bonehurtingjuice,
@@ -43,19 +47,23 @@
/datum/reagent/toxin/mindbreaker,
/datum/reagent/toxin/spewium,
)
+ examine_discovered = span_danger("It appears to be some sort of checmical aerolyzer for harming things!")
-/datum/component/artifact/smoke/flesh
- associated_object = /obj/structure/artifact/smoke/flesh
+/datum/artifact_effect/smoke/flesh
+ type_name = "Smoke Machine (Synthflesh) Effect"
weight = ARTIFACT_RARE
activation_message = "starts spewing out flesh mending smoke!"
valid_chemicals = list(
/datum/reagent/medicine/c2/synthflesh
)
+ examine_discovered = span_info("It appears to be some sort of checmical aerolyzer for healing things!")
-/datum/component/artifact/smoke/exotic
- associated_object = /obj/structure/artifact/smoke/exotic
+/datum/artifact_effect/smoke/exotic
+ type_name = "Smoke Machine (Exotic) Effect"
weight = ARTIFACT_RARE
activation_message = "starts spewing out exotic smoke!"
+
+ examine_discovered = span_warning("It appears to be some sort of checmical aerolyzer for... not sure actually.")
valid_chemicals = list(
/datum/reagent/wittel,
/datum/reagent/medicine/omnizine/protozine,
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/surgery.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/surgery.dm
similarity index 52%
rename from monkestation/code/modules/art_sci_overrides/artifact_components/surgery.dm
rename to monkestation/code/modules/art_sci_overrides/artifact_effects/surgery.dm
index 8164e16fce72..c7bca8396e73 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/surgery.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/surgery.dm
@@ -1,7 +1,6 @@
-/datum/component/artifact/surgery
- associated_object = /obj/structure/artifact/surgery
+/datum/artifact_effect/surgery
weight = ARTIFACT_VERYUNCOMMON
- type_name = "Surgery Object"
+ type_name = "Surgery Object Effect"
activation_message = "springs to life!"
deactivation_message = "becomes silent."
valid_activators = list(
@@ -10,14 +9,17 @@
)
COOLDOWN_DECLARE(surgery_cooldown)
+ research_value = 1250
-/datum/component/artifact/surgery/effect_touched(mob/living/user)
+ examine_discovered = span_warning("It appears to be some sort of automated surgery device")
+
+/datum/artifact_effect/surgery/effect_touched(mob/living/user)
if(!COOLDOWN_FINISHED(src, surgery_cooldown))
- holder.visible_message(span_notice("[holder] wheezes, shutting down."))
+ our_artifact.holder.visible_message(span_notice("[our_artifact.holder] wheezes, shutting down."))
return
if(!ishuman(user))
return
var/mob/living/carbon/human/human = user
- human.bioscramble(holder.name)
+ human.bioscramble(our_artifact.holder.name)
COOLDOWN_START(src,surgery_cooldown, 5 SECONDS)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/vomit.dm b/monkestation/code/modules/art_sci_overrides/artifact_effects/vomit.dm
similarity index 54%
rename from monkestation/code/modules/art_sci_overrides/artifact_components/vomit.dm
rename to monkestation/code/modules/art_sci_overrides/artifact_effects/vomit.dm
index 24b901492867..b3154d75e0d4 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_components/vomit.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_effects/vomit.dm
@@ -1,7 +1,6 @@
-/datum/component/artifact/vomit
- associated_object = /obj/structure/artifact/vomit
+/datum/artifact_effect/vomit
weight = ARTIFACT_UNCOMMON
- type_name = "Vomiting Inducer"
+ type_name = "Vomiting Inducer Effect"
activation_message = "starts emitting disgusting imagery!"
deactivation_message = "falls silent, its aura dissipating!"
valid_origins = list(
@@ -15,7 +14,12 @@
var/bloody_vomit = FALSE
COOLDOWN_DECLARE(cooldown)
-/datum/component/artifact/vomit/setup()
+ research_value = 100 //To busy vomiting cant research
+
+
+ examine_discovered = span_warning("It appears to be some sort of sick prank")
+
+/datum/artifact_effect/vomit/setup()
switch(rand(1,100))
if(1 to 84)
range = rand(2,3)
@@ -29,18 +33,12 @@
potency += spew_range
bloody_vomit = prob(50)
potency += (range) * 4
- addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/component/artifact, artifact_deactivate)), round(30 * (potency * 10) SECONDS))
-/datum/component/artifact/vomit/on_examine(atom/source, mob/user, list/examine_list)
- . = ..()
- var/mob/living/carbon/carbon = user
- if(active && istype(carbon) && carbon.stat < UNCONSCIOUS)
- examine_list += span_warning("It has an [spew_organs ? "extremely" : ""] disgusting aura! [prob(20) ? "..is that a felinid?" : ""]")
- carbon.vomit(blood = bloody_vomit, stun = (spew_organs ? TRUE : prob(25)), distance = spew_range)
- if(spew_organs && prob(40))
- carbon.spew_organ()
-/datum/component/artifact/vomit/effect_process()
+/datum/artifact_effect/vomit/effect_activate(silent)
+ addtimer(CALLBACK(our_artifact, TYPE_PROC_REF(/datum/component/artifact,artifact_deactivate)),30 SECONDS)
+
+/datum/artifact_effect/vomit/effect_process()
for(var/mob/living/carbon/viewed in view(range, src))
if(prob(100 - potency))
continue
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_cell.dm b/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_cell.dm
index 5f20f628b830..092275d14b2e 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_cell.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_cell.dm
@@ -4,11 +4,17 @@
resistance_flags = LAVA_PROOF | ACID_PROOF | INDESTRUCTIBLE
ratingdesc = FALSE
charge_light_type = null
- var/datum/component/artifact/assoc_comp = /datum/component/artifact/cell
+ obj_flags = CAN_BE_HIT
+ var/datum/component/artifact/assoc_comp = /datum/component/artifact
-ARTIFACT_SETUP(/obj/item/stock_parts/cell/artifact, SSobj)
+ARTIFACT_SETUP(/obj/item/stock_parts/cell/artifact, SSobj, null, /datum/artifact_effect/cell, ARTIFACT_SIZE_TINY)
/obj/item/stock_parts/cell/artifact/use(amount, force) //dont use power unless active
. = FALSE
if(assoc_comp.active)
return ..()
+
+/obj/item/stock_parts/cell/artifact/attack_self(mob/user, modifiers)
+ . = ..()
+ to_chat(user,span_notice("You squeeze the [src] tightly."))
+ on_artifact_touched(src,user,modifiers)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_effect_disk.dm b/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_effect_disk.dm
new file mode 100644
index 000000000000..f1c2820e49cb
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_effect_disk.dm
@@ -0,0 +1,28 @@
+/obj/item/disk/artifact
+ name = "artifact data disk"
+ desc = "A disk for storing an artifacts effect data. Can be put into an xray machine to maybe copy artifact data, or an artifact wand for it's effect."
+ icon_state = "rndmajordisk"
+ custom_materials = list(/datum/material/iron=30, /datum/material/glass=10)
+ var/datum/artifact_effect/effect
+ var/datum/artifact_activator/activator
+ var/datum/artifact_fault/fault
+ var/read_only = FALSE //Well, it's still a floppy disk
+ obj_flags = UNIQUE_RENAME
+/obj/item/disk/artifact/update_name(updates)
+ . = ..()
+ var/newname = ""
+ if(effect)
+ newname += " ([effect.type_name]) "
+ if(activator)
+ newname += " |[activator.name]| "
+ if(fault)
+ newname += " ![fault.name]! "
+ name = initial(name) + newname
+
+/obj/item/disk/artifact/attack_self(mob/user)
+ read_only = !read_only
+ to_chat(user, "You flip the write-protect tab to [src.read_only ? "protected" : "unprotected"].")
+
+/obj/item/disk/artifact/examine(mob/user)
+ . = ..()
+ . += "The write-protect tab is set to [src.read_only ? "protected" : "unprotected"]."
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_gun.dm b/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_gun.dm
index 37c02bba1e0d..f337be6fb181 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_gun.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_gun.dm
@@ -1,10 +1,21 @@
/obj/item/ammo_casing/magic/artifact
projectile_type = /obj/projectile/magic/artifact
-
+ var/datum/artifact_effect/gun/stored_comp
/obj/item/ammo_casing/magic/artifact/ready_proj(atom/target, mob/living/user, quiet, zone_override = "", atom/fired_from)
if(!loaded_projectile)
return
- var/datum/component/artifact/gun/gun = fired_from.GetComponent(/datum/component/artifact/gun)
+ var/datum/component/artifact/component = fired_from.GetComponent(/datum/component/artifact)
+ var/datum/artifact_effect/gun/gun
+ if(!stored_comp)
+ for(var/datum/artifact_effect/eff in component.artifact_effects)
+ if(istype(eff,/datum/artifact_effect/gun))
+ gun = eff
+ stored_comp = gun
+ break
+ else
+ gun = stored_comp
+ if(!gun)
+ return
loaded_projectile.damage = gun.damage / pellets
loaded_projectile.icon_state = gun.projectile_icon
loaded_projectile.damage_type = gun.dam_type
@@ -39,9 +50,15 @@
pinless = TRUE
recharge_rate = 1
antimagic_flags = null
- var/datum/component/artifact/assoc_comp = /datum/component/artifact/gun
+ obj_flags = CAN_BE_HIT
+ var/datum/component/artifact/assoc_comp = /datum/component/artifact
+
+ARTIFACT_SETUP(/obj/item/gun/magic/artifact, SSobj, null, /datum/artifact_effect/gun, ARTIFACT_SIZE_SMALL)
-ARTIFACT_SETUP(/obj/item/gun/magic/artifact, SSobj)
+/obj/item/gun/magic/artifact/attack_self(mob/user, modifiers)
+ . = ..()
+ to_chat(user,span_notice("You squeeze the [src] tightly."))
+ on_artifact_touched(src,user,modifiers)
/obj/item/gun/magic/artifact/can_shoot()
return assoc_comp.active
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_item.dm b/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_item.dm
new file mode 100644
index 000000000000..e6eaf3e204db
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_item.dm
@@ -0,0 +1,37 @@
+/obj/item/artifact_item
+ //This is literally just an artifact, but item sized for item generation of traits that require it.
+
+ icon = 'icons/obj/artifacts.dmi'
+ icon_state = "narnar-1"
+ resistance_flags = LAVA_PROOF | ACID_PROOF | INDESTRUCTIBLE
+ icon = 'icons/obj/artifacts.dmi'
+ inhand_icon_state = "plasmashiv"
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ obj_flags = CAN_BE_HIT
+ var/datum/component/artifact/assoc_comp = /datum/component/artifact
+
+ARTIFACT_SETUP(/obj/item/artifact_item, SSobj, null, null, ARTIFACT_SIZE_SMALL)
+
+/obj/item/artifact_item/attack_self(mob/user, modifiers)
+ . = ..()
+ to_chat(user,span_notice("You squeeze the [src] tightly."))
+ on_artifact_touched(src,user,modifiers)
+
+
+/obj/item/artifact_item_tiny
+ //This is literally just an artifact, but s m o l for item generation of traits that require it.
+
+ icon = 'icons/obj/artifacts.dmi'
+ icon_state = "narnar-1"
+ resistance_flags = LAVA_PROOF | ACID_PROOF | INDESTRUCTIBLE
+ icon = 'icons/obj/artifacts.dmi'
+ var/datum/component/artifact/assoc_comp = /datum/component/artifact
+ obj_flags = CAN_BE_HIT
+
+ARTIFACT_SETUP(/obj/item/artifact_item_tiny, SSobj, null, null, ARTIFACT_SIZE_TINY)
+
+/obj/item/artifact_item_tiny/attack_self(mob/user, modifiers)
+ . = ..()
+ to_chat(user,span_notice("You squeeze the [src] tightly."))
+ on_artifact_touched(src,user,modifiers)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_manipulators.dm b/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_manipulators.dm
new file mode 100644
index 000000000000..13661ae0735b
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_manipulators.dm
@@ -0,0 +1,158 @@
+/obj/item/artifact_summon_wand
+ name = "artifact manipulation wand"
+ desc = "A one-use device capable of summoning an artifact from... somewhere.Use the item in hand to change modes. Right Click a disk onto it to load the disk. Right Click the item to attempt to summon an artifact, or slap an existing one to modify it."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "memorizer2"
+ inhand_icon_state = "electronic"
+ worn_icon_state = "electronic"
+ lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
+ w_class = WEIGHT_CLASS_SMALL
+ slot_flags = ITEM_SLOT_BELT
+ item_flags = NOBLUDGEON
+ var/obj/item/disk/artifact/slotted_disk
+ var/selected_mode = 0
+ /// modes are- 0 = random, 1 = blank artifact,2 = disk copy, 3 = disc activator, 4 = disc fault, 5 = disc effect.
+ var/max_modes = 5
+
+/obj/item/artifact_summon_wand/attack_self(mob/user, modifiers)
+ . = ..()
+ selected_mode++
+ if(selected_mode > max_modes)
+ selected_mode = 0
+ var/display_text = "cause a bug. Tell the coders quick!"
+ switch(selected_mode)
+ if(0)
+ display_text = "create a random artifact."
+ if(1)
+ display_text = "create a blank artifact."
+ if(2)
+ display_text = "create a copy of inserted disk"
+ if(3)
+ display_text = "create or modify an artifact with just the inserted disks activator."
+ if(4)
+ display_text = "create or modify an artifact with just the inserted disks fault."
+ if(5)
+ display_text = "create or modify an artifact with just the inserted disks effect."
+ to_chat(user,span_notice("You set [src] to [display_text]"))
+
+/obj/item/artifact_summon_wand/attackby_secondary(obj/item/weapon, mob/user, params)
+ . = ..()
+ if(istype(weapon,/obj/item/disk/artifact))
+ if(slotted_disk)
+ to_chat(user,span_notice("You swap the disk inside [src]"))
+ weapon.forceMove(src)
+ if(!user.put_in_hand(slotted_disk))
+ slotted_disk.forceMove(get_turf(user))
+ slotted_disk = weapon
+ else
+ to_chat(user,span_notice("You slot [weapon] inside [src]"))
+ weapon.forceMove(src)
+ slotted_disk = weapon
+/obj/item/artifact_summon_wand/attack_self_secondary(mob/user, modifiers)
+ . = ..()
+ summon_artifact(user)
+
+/obj/item/artifact_summon_wand/proc/summon_artifact(mob/user)
+ var/turf/attempt_location = get_turf(get_step(user,user.dir))
+ if(attempt_location.density)
+ return
+ visible_message(span_notice("[user] begins to summon an artifact using [src]!"),span_notice("You begin attempting to summon an artifact using [src]..."))
+ if(do_after(user,5 SECOND))
+ var/obj/new_artifact = spawn_artifact(attempt_location)
+ var/datum/component/artifact/art_comp = new_artifact.GetComponent(/datum/component/artifact)
+ if(!art_comp)
+ visible_message(span_notice("Something goes wrong, and [src] fizzles!"))
+ return
+ switch(selected_mode)//0 left blank, as we don't need to do anything else.
+ if(1)
+ art_comp.clear_out()
+ if(2)
+ art_comp.clear_out()
+ if(slotted_disk.activator)
+ art_comp.add_activator(slotted_disk.activator)
+ if(slotted_disk.fault)
+ art_comp.change_fault(slotted_disk.fault)
+ if(slotted_disk.effect)
+ art_comp.try_add_effect(slotted_disk.effect)
+ if(3)
+ art_comp.clear_out()
+ if(slotted_disk.activator)
+ art_comp.add_activator(slotted_disk.activator)
+ if(4)
+ art_comp.clear_out()
+ if(slotted_disk.fault)
+ art_comp.change_fault(slotted_disk.fault)
+ if(5)
+ art_comp.clear_out()
+ if(slotted_disk.effect)
+ art_comp.try_add_effect(slotted_disk.effect)
+ visible_message(span_notice("[new_artifact] appears from nowhere!"),span_notice("You summon [new_artifact], and [src] disintegrates!"))
+ if(slotted_disk)
+ if(!user.put_in_active_hand(slotted_disk))
+ slotted_disk.forceMove(get_turf(user))
+ slotted_disk = null
+ qdel(src)
+ else
+ visible_message(span_notice("Something goes wrong, and [src] fizzles!"))
+
+/obj/item/artifact_summon_wand/examine(mob/user)
+ . = ..()
+ if(slotted_disk)
+ . += span_notice("Contains [slotted_disk]")
+ switch (selected_mode)
+ if(0)
+ . += span_notice("Will currently try to summon a random artifact.")
+ if(1)
+ . += span_notice("Will currently try to summon a blank artifact")
+ if(2)
+ . += span_notice("Will currently try to copy the disk to a new or existing artifact.")
+ if(3)
+ . += span_notice("Will currently try to copy the disk activator to a new or existing artifact.")
+ if(4)
+ . += span_notice("Will currently try to copy the disk fault to a new or existing artifact.")
+ if(5)
+ . += span_notice("Will currently try to copy the disk effect to a new or existing artifact.")
+
+/obj/item/artifact_summon_wand/attack_atom(atom/attacked_atom, mob/living/user, params)
+ var/datum/component/artifact/art_comp = attacked_atom.GetComponent(/datum/component/artifact)
+ if(art_comp && slotted_disk)
+ visible_message(span_notice("[user] begins trying to configure [attacked_atom] with [src]!"),span_notice("You begin trying to configure the [attacked_atom] with [src]..."))
+ if(do_after(user,5 SECOND))
+ var/added_anything = FALSE
+ switch(selected_mode)
+ if(0)
+ visible_message(span_notice("...but nothing changed!"))
+ if(1)
+ art_comp.clear_out()
+ visible_message(span_notice("[attacked_atom] is rendered inert!"))
+ added_anything = TRUE
+ if(2)
+ if(slotted_disk.activator)
+ added_anything |= art_comp.add_activator(slotted_disk.activator)
+ if(slotted_disk.fault)
+ added_anything |= art_comp.change_fault(slotted_disk.fault)
+ if(slotted_disk.effect)
+ added_anything |= art_comp.try_add_effect(slotted_disk.effect)
+ if(3)
+ if(slotted_disk.activator)
+ added_anything |= art_comp.add_activator(slotted_disk.activator)
+ if(4)
+ if(slotted_disk.fault)
+ added_anything |= art_comp.change_fault(slotted_disk.fault)
+ if(5)
+ if(slotted_disk.effect)
+ added_anything |= art_comp.try_add_effect(slotted_disk.effect)
+ if(added_anything)
+ visible_message(span_notice("[user] configures the [attacked_atom] with [src]!"),span_notice("You configure the [attacked_atom] with [src], which switftly disintegrates!"))
+ if(slotted_disk)
+ if(!user.put_in_active_hand(slotted_disk))
+ slotted_disk.forceMove(get_turf(user))
+ slotted_disk = null
+ qdel(src)
+ else
+ visible_message(span_notice("...but nothing changed!"))
+ else
+ visible_message(span_notice("Something goes wrong, and [src] fizzles!"))
+ return ..() //I TAKE NO RESPONSIBILITY FOR CALLING THIS L A S T.
+
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_melee.dm b/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_melee.dm
index 3ad1b89385d4..c8ef4201b472 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_melee.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_items/artifact_melee.dm
@@ -10,12 +10,18 @@
inhand_icon_state = "plasmashiv"
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ obj_flags = CAN_BE_HIT
var/special_cooldown_time
var/special
- var/datum/component/artifact/assoc_comp = /datum/component/artifact/melee
+ var/datum/component/artifact/assoc_comp = /datum/component/artifact
COOLDOWN_DECLARE(special_cooldown)
-ARTIFACT_SETUP(/obj/item/melee/artifact, SSobj)
+ARTIFACT_SETUP(/obj/item/melee/artifact, SSobj, null, /datum/artifact_effect/melee, ARTIFACT_SIZE_SMALL)
+
+/obj/item/melee/artifact/attack_self(mob/user, modifiers)
+ . = ..()
+ to_chat(user,span_notice("You squeeze the [src] tightly."))
+ on_artifact_touched(src,user,modifiers)
/obj/item/melee/artifact/afterattack(mob/living/victim, mob/user, proximity)
if(!istype(victim) || !assoc_comp.active || !COOLDOWN_FINISHED(src,special_cooldown) || !special || !proximity)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_objects/artifact_powergen.dm b/monkestation/code/modules/art_sci_overrides/artifact_objects/artifact_powergen.dm
index b2a37bb18adc..b06a9c3d0a65 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_objects/artifact_powergen.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_objects/artifact_powergen.dm
@@ -10,12 +10,10 @@
circuit = null
density = TRUE
anchored = FALSE
- var/datum/component/artifact/assoc_comp = /datum/component/artifact/generator
+ var/datum/component/artifact/assoc_comp = /datum/component/artifact
+ARTIFACT_SETUP(/obj/machinery/power/generator_artifact, SSmachines, null, /datum/artifact_effect/generator, null)
-ARTIFACT_SETUP(/obj/machinery/power/generator_artifact, SSmachines)
-
-/datum/component/artifact/generator
- associated_object = /obj/machinery/power/generator_artifact
+/datum/artifact_effect/generator
type_name = "Power Generator"
weight = ARTIFACT_RARE
valid_activators = list(
@@ -29,16 +27,18 @@ ARTIFACT_SETUP(/obj/machinery/power/generator_artifact, SSmachines)
/datum/artifact_origin/precursor,
/datum/artifact_origin/martian,
) //narnar doesnt need power
+
+ valid_type_paths = list(/obj/machinery/power/generator_artifact)
+ research_value = 10000 //Holy moly lucky you!
activation_message = "begins emitting a faint, droning hum."
deactivation_message = "shortcircuits!"
- xray_result = "COMPLEX"
COOLDOWN_DECLARE(sideeffect_cooldown)
var/power_gen = 0
///does the power output fluctuate
var/unstable_generation = FALSE
-/datum/component/artifact/generator/setup() //TODO: Make this use some weird scaling math to have it pick higher numbers at lower odds
+/datum/artifact_effect/generator/setup() //TODO: Make this use some weird scaling math to have it pick higher numbers at lower odds
if(prob(65))
power_gen = rand(1 KW, MAX_POSSIBLE_GEN / 2)
else
@@ -46,42 +46,42 @@ ARTIFACT_SETUP(/obj/machinery/power/generator_artifact, SSmachines)
unstable_generation = prob(40)
potency = power_gen / (6 KW) // 100 potency at 600kw generation
-/datum/component/artifact/generator/effect_touched(mob/living/user)
- var/obj/machinery/power/generator_artifact/powerholder = holder
+/datum/artifact_effect/generator/effect_touched(mob/living/user)
+ var/obj/machinery/power/generator_artifact/powerholder = our_artifact.holder
//if on cable and not setup, connect and setup
if(!powerholder.anchored && locate(/obj/structure/cable) in get_turf(powerholder))
- powerholder.visible_message(span_warning("[holder] seems to snap to the cable!"))
+ powerholder.visible_message(span_warning("[our_artifact.holder] seems to snap to the cable!"))
playsound(get_turf(powerholder), 'sound/items/deconstruct.ogg', 50, TRUE)
powerholder.anchored = TRUE
powerholder.connect_to_network()
return
- holder.Beam(user, icon_state = "lightning[rand(1,12)]", time = 0.5 SECONDS)
+ our_artifact.holder.Beam(user, icon_state = "lightning[rand(1,12)]", time = 0.5 SECONDS)
playsound(get_turf(powerholder), 'sound/magic/lightningshock.ogg', 100, TRUE, extrarange = 5)
var/damage = user.electrocute_act(power_gen / 2 KW, powerholder, flags = SHOCK_NOSTUN)
- to_chat(user, span_userdanger("You are hit by a burst of electricity from [holder]!"))
+ to_chat(user, span_userdanger("You are hit by a burst of electricity from [our_artifact.holder]!"))
if(damage > 80)
- var/turf/owner_turf = get_turf(holder)
+ var/turf/owner_turf = get_turf(our_artifact.holder)
var/throwtarget = get_edge_target_turf(get_turf(user), get_dir(owner_turf, get_step_away(user, owner_turf)))
user.safe_throw_at(throwtarget, power_gen / 38 KW, 1, force = MOVE_FORCE_EXTREMELY_STRONG)
if(damage > 400 && prob(50))
- user.dust(just_ash = TRUE, drop_items = TRUE)
- artifact_deactivate() //shortcircuit
+ user.death(FALSE)
+ our_artifact.artifact_deactivate() //shortcircuit
if(prob(20)) //try to get yourself shocked with insuls many times to shortcircuit it (in retrospect this sucks)
- artifact_deactivate()
+ our_artifact.artifact_deactivate()
-/datum/component/artifact/generator/effect_process() //todo add more
- if(!holder.anchored)
+/datum/artifact_effect/generator/effect_process() //todo add more
+ if(!our_artifact.holder.anchored)
return
- var/obj/machinery/power/generator_artifact/powerholder = holder
+ var/obj/machinery/power/generator_artifact/powerholder = our_artifact.holder
powerholder.add_avail(power_gen * (unstable_generation ? rand(0.1, 1) : 1))
if(power_gen < SIDEEFFECT_THRESHOLD || !COOLDOWN_FINISHED(src,sideeffect_cooldown)) //sorry boss no can do
return
COOLDOWN_START(src,sideeffect_cooldown,rand(4,8) SECONDS)
//minor to medium side effects
if(power_gen >= (SHITFUCK_THRESHOLD / 3))
- powerholder.visible_message(span_danger("\The [holder] lets out a shower of thunder!"), span_hear("You hear a loud electrical crack!"))
+ powerholder.visible_message(span_danger("\The [our_artifact.holder] lets out a shower of thunder!"), span_hear("You hear a loud electrical crack!"))
playsound(get_turf(powerholder), 'sound/magic/lightningshock.ogg', 100, TRUE, extrarange = 5)
tesla_zap(powerholder, rand(2,3), power_gen / 3500)
@@ -97,12 +97,12 @@ ARTIFACT_SETUP(/obj/machinery/power/generator_artifact, SSmachines)
merger.assert_gas(/datum/gas/carbon_dioxide)
merger.gases[/datum/gas/carbon_dioxide][MOLES] = rand(10,120)
merger.temperature = rand(200,1000)
- var/turf/holder_turf = get_turf(holder)
+ var/turf/holder_turf = get_turf(our_artifact.holder)
holder_turf.assume_air(merger)
-/datum/component/artifact/generator/effect_deactivate()
- var/obj/machinery/power/generator_artifact/powerholder = holder
+/datum/artifact_effect/generator/effect_deactivate()
+ var/obj/machinery/power/generator_artifact/powerholder = our_artifact.holder
powerholder.disconnect_from_network()
powerholder.anchored = FALSE
playsound(get_turf(powerholder), 'sound/items/deconstruct.ogg', 50, TRUE)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_proto_datums.dm b/monkestation/code/modules/art_sci_overrides/artifact_proto_datums.dm
new file mode 100644
index 000000000000..be213e1102cc
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/artifact_proto_datums.dm
@@ -0,0 +1,25 @@
+/datum/design/artifact_summon_wand
+ name = "Artifact Wand"
+ desc = "A wand used to create or modify artifacts."
+ id = "artifact_wand"
+ build_type = PROTOLATHE | AWAY_LATHE
+ materials = list(/datum/material/iron =HALF_SHEET_MATERIAL_AMOUNT, /datum/material/gold =SHEET_MATERIAL_AMOUNT, /datum/material/plasma =SHEET_MATERIAL_AMOUNT * 4, /datum/material/uranium =SHEET_MATERIAL_AMOUNT)
+ build_path = /obj/item/artifact_summon_wand
+ category = list(
+ RND_CATEGORY_EQUIPMENT
+ )
+ lathe_time_factor = 0.2
+ departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
+
+/datum/design/disk/artifact
+ name = "Artifact Disk"
+ desc = "A disk used to store artifact data."
+ id = "disk_artifact"
+ build_type = PROTOLATHE | AWAY_LATHE
+ materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT, /datum/material/glass = SMALL_MATERIAL_AMOUNT/2)
+ build_path = /obj/item/disk/artifact
+ category = list(
+ RND_CATEGORY_EQUIPMENT
+ )
+ lathe_time_factor = 0.1
+ departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_testers/analysis_form.dm b/monkestation/code/modules/art_sci_overrides/artifact_testers/analysis_form.dm
index 871bd0f8eb03..7d11430f2492 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_testers/analysis_form.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_testers/analysis_form.dm
@@ -34,7 +34,8 @@
contraband = STICKER_NOSPAWN
var/chosen_origin = ""
var/list/chosentriggers = list()
- var/chosentype = ""
+ var/list/chosen_effects = list()
+ var/chosen_fault = ""
/obj/item/sticker/analysis_form/attackby(obj/item/item, mob/living/user, params)
if(istype(item, /obj/item/pen))
@@ -59,31 +60,44 @@
if("origin")
chosen_origin = params["origin"]
if("type")
- chosentype = params["type"]
+ var/trig_type = params["type"]
+ if(trig_type in chosen_effects)
+ chosen_effects -= trig_type
+ else
+ chosen_effects += trig_type
+ if("fault")
+ chosen_fault = params["fault"]
if("trigger")
- var/trig = params["trigger"]
- if(trig in chosentriggers)
- chosentriggers -= trig
+ var/trig_act = params["trigger"]
+ if(trig_act in chosentriggers)
+ chosentriggers -= trig_act
else
- chosentriggers += trig
+ chosentriggers += trig_act
if(attached)
analyze_attached()
/obj/item/sticker/analysis_form/ui_static_data(mob/user)
. = ..()
var/list/origins_names = list()
- for(var/datum/artifact_origin/subtype as anything in subtypesof(/datum/artifact_origin))
+ for(var/datum/artifact_origin/subtype as anything in subtypesof(/datum/artifact_origin))
origins_names += initial(subtype.name)
+ var/list/allfaults = list()
+ for(var/datum/artifact_fault/subtype as anything in subtypesof(/datum/artifact_fault))
+ allfaults += initial(subtype.name)
+
var/list/trigger_names = list()
for(var/datum/artifact_activator/subtype as anything in subtypesof(/datum/artifact_activator))
trigger_names += initial(subtype.name)
var/list/artifact_names = list()
- for(var/datum/component/artifact/subtype as anything in subtypesof(/datum/component/artifact))
+ for(var/datum/artifact_effect/subtype as anything in subtypesof(/datum/artifact_effect))
+ if(subtype.super_secret)
+ continue //shhhhh
artifact_names += initial(subtype.type_name)
.["allorigins"] = origins_names
+ .["allfaults"] = allfaults
.["alltypes"] = artifact_names
.["alltriggers"] = trigger_names
return
@@ -91,7 +105,8 @@
/obj/item/sticker/analysis_form/ui_data(mob/user)
. = ..()
.["chosenorigin"] = chosen_origin
- .["chosentype"] = chosentype
+ .["chosenfault"] = chosen_fault
+ .["chosentype"] = chosen_effects
.["chosentriggers"] = chosentriggers
return .
@@ -149,46 +164,60 @@
..()
/obj/item/sticker/analysis_form/proc/analyze_attached()
+ if(!attached)
+ return
var/datum/component/artifact/to_analyze = attached.GetComponent(/datum/component/artifact)
if(!to_analyze)
return
if(chosen_origin)
to_analyze.holder.name = to_analyze.generated_name
- if(chosentype)
- to_analyze.holder.name += " ([chosentype])"
+ if(chosen_fault)
+ to_analyze.holder.name += " ![chosen_fault]! "
+ if(chosen_effects)
+ for(var/effect as anything in chosen_effects)
+ to_analyze.holder.name += " ([effect]) "
+ to_analyze.analysis = src
+ to_analyze.process_stimuli(STIMULUS_DATA,TRUE)
/obj/item/sticker/analysis_form/proc/deanalyze_attached()
var/datum/component/artifact/to_analyze = attached.GetComponent(/datum/component/artifact)
if(!to_analyze)
return
to_analyze.holder.name = to_analyze.fake_name
+ QDEL_NULL(to_analyze.analysis)
/obj/item/sticker/analysis_form/proc/get_export_value(datum/component/artifact/art)
- var/correct = 0
- var/total_guesses = 0
-
+ var/baseval = CARGO_CRATE_VALUE
+ var/labeling_bonus = round(CARGO_CRATE_VALUE * 2.5)
+ var/bonus = 0
+ for(var/datum/artifact_effect/discovered as anything in art.discovered_effects)
+ if(discovered.discovered_credits)
+ bonus += round(discovered.discovered_credits * ((discovered.potency + 25)/50))
+ if(art.chosen_fault && art.fault_discovered)
+ bonus += art.chosen_fault.discovered_credits
if(art.artifact_origin.type_name == chosen_origin)
- correct ++
- if(chosen_origin)
- total_guesses ++
- if(chosentype)
- total_guesses ++
- if(art.type_name == chosentype)
- correct ++
- for(var/name in chosentriggers)
- total_guesses++
-
- for(var/datum/artifact_activator/listed in art.activators)
- if(listed.name != name)
- continue
- correct++
-
- var/incorrect = total_guesses - correct
- return round((CARGO_CRATE_VALUE/4) * art.potency * (max((ARTIFACT_COMMON - art.weight) * 0.01, 0.01) * max(correct - incorrect, 0.01)))
+ bonus += labeling_bonus
+ else
+ bonus -= labeling_bonus
+ if(chosen_effects)
+ for(var/name_effect in chosen_effects)
+ for(var/datum/artifact_effect/effect as anything in art.artifact_effects)
+ if(effect.type_name != name_effect)
+ bonus -= labeling_bonus
+ else
+ bonus += labeling_bonus
+ if(chosentriggers)
+ for(var/name_trigger in chosentriggers)
+ for(var/datum/artifact_activator/activator as anything in art.activators)
+ if(activator.name != name_trigger)
+ bonus -= labeling_bonus
+ else
+ bonus += labeling_bonus
+ return round(baseval + bonus)
/obj/item/analysis_bin
name = "analysis bin"
- desc = "A bin made out of material to resist adhesion, for artifact analysis forms."
+ desc = "A bin containing a seemingly endless supply of fourms for artifact labeling. Correctly labeled artifacts sell for more!"
icon = 'icons/obj/service/bureaucracy.dmi'
icon_state = "analysisbin1"
base_icon_state = "analysisbin"
@@ -196,33 +225,24 @@
lefthand_file = 'icons/mob/inhands/items/sheets_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items/sheets_righthand.dmi'
w_class = WEIGHT_CLASS_NORMAL
- var/forms = 15
- var/form_type = /obj/item/sticker/analysis_form
+ var/form_type = /obj/item/sticker/analysis_form
/obj/item/analysis_bin/Initialize(mapload)
. = ..()
interaction_flags_item &= ~INTERACT_ITEM_ATTACK_HAND_PICKUP
AddElement(/datum/element/drag_pickup)
-/obj/item/analysis_bin/update_icon_state()
- icon_state = "[base_icon_state][forms > 0]"
- return ..()
-
/obj/item/analysis_bin/attack_hand(mob/user, list/modifiers)
if(isliving(user))
var/mob/living/living_mob = user
if(!(living_mob.mobility_flags & MOBILITY_PICKUP))
return
- if(forms)
- forms--
- var/obj/item/form = new form_type
- form.add_fingerprint(user)
- form.forceMove(user.loc)
- user.put_in_hands(form)
- balloon_alert(user, "took form")
- update_appearance()
- else
- balloon_alert(user, "empty!")
+ var/obj/item/form = new form_type
+ form.add_fingerprint(user)
+ form.forceMove(user.loc)
+ user.put_in_hands(form)
+ balloon_alert(user, "took form")
+ update_appearance()
add_fingerprint(user)
return ..()
@@ -232,7 +252,6 @@
return
qdel(item)
balloon_alert(user, "form returned")
- forms++
update_appearance()
else
return ..()
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_testers/xray.dm b/monkestation/code/modules/art_sci_overrides/artifact_testers/xray.dm
index 70d25ee3243d..12cb72675d26 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_testers/xray.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_testers/xray.dm
@@ -1,6 +1,6 @@
/obj/machinery/artifact_xray
name = "artifact x-ray machine"
- desc = "An x-ray machine, used to scan artifacts."
+ desc = "An x-ray machine, used to scan artifacts for what they do and research them. Can be Wrenched to turn on Destructive Scan mode, which when given a disk, may record artifact data."
icon = 'icons/obj/machines/artifact_machines.dmi'
icon_state = "xray-0"
base_icon_state = "xray"
@@ -8,27 +8,59 @@
circuit = /obj/item/circuitboard/machine/artifactxray
use_power = IDLE_POWER_USE
///max radiation level
- var/max_radiation = 3
+ var/max_radiation = 4
///chosen radiation level
var/chosen_level = 1
var/pulse_time = 4 SECONDS
var/pulse_cooldown_time = 3 SECONDS
var/list/last_results = list("NO DATA")
var/pulsing = FALSE
+ var/datum/techweb/stored_research
+ ///Chance to get a disk for an artifact trait on scan. Better scanner, better chance.
+ var/disk_chance = 20
+ ///Are we going for disks, at the risk of destorying artifact?
+ var/destroy_artifact_mode = FALSE
+ ///Chance we accidentally destory the artifact on destuctive scan. Better laser, less chance.
+ var/destroy_chance = 75
+ ///The disk we have inside of us, maybe.
+ var/obj/item/disk/artifact/our_disk
COOLDOWN_DECLARE(message_cooldown)
COOLDOWN_DECLARE(pulse_cooldown)
/obj/machinery/artifact_xray/Initialize(mapload)
. = ..()
+ if(!CONFIG_GET(flag/no_default_techweb_link) && !stored_research)
+ connect_techweb(SSresearch.science_tech)
RefreshParts()
+/obj/machinery/artifact_xray/Destroy()
+ if(stored_research)
+ log_research("[src] disconnected from techweb [stored_research] (destroyed).")
+ stored_research = null
+ QDEL_NULL(wires)
+ return ..()
+
+
+/obj/machinery/artifact_xray/proc/connect_techweb(datum/techweb/new_techweb)
+ if(stored_research)
+ log_research("[src] disconnected from techweb [stored_research] when connected to [new_techweb].")
+ stored_research = new_techweb
+
+/obj/machinery/artifact_xray/multitool_act(mob/living/user, obj/item/multitool/tool)
+ if(!QDELETED(tool.buffer) && istype(tool.buffer, /datum/techweb))
+ connect_techweb(tool.buffer)
+ return TRUE
+ return FALSE
/obj/machinery/artifact_xray/RefreshParts()
. = ..()
var/power_usage = 250
- for(var/obj/item/stock_parts/micro_laser/laser in component_parts)
- max_radiation = round(2.5 * laser.rating)
+ for(var/datum/stock_part/micro_laser/laser in component_parts)
+ max_radiation = laser.tier
+ destroy_chance = round(75 - (15* laser.tier))
for(var/datum/stock_part/capacitor/capac in component_parts)
- power_usage -= 30 * capac.tier
+ power_usage -= round(30 * capac.tier)
+ for(var/datum/stock_part/scanning_module/scanner in component_parts)
+ disk_chance = round(20 * scanner.tier)
update_mode_power_usage(ACTIVE_POWER_USE, power_usage)
/obj/machinery/artifact_xray/update_icon_state()
@@ -39,6 +71,12 @@
. = ..()
if(!can_interact(user))
return
+ if(our_disk)
+ to_chat(user,"You eject the [our_disk.name]")
+ if(!user.put_in_active_hand(our_disk))
+ our_disk.forceMove(get_turf(user))
+ our_disk = null
+ return
toggle_open()
/obj/machinery/artifact_xray/proc/toggle_open()
if(!COOLDOWN_FINISHED(src,pulse_cooldown))
@@ -49,14 +87,29 @@
else
flick("xray-opening", src)
open_machine()
+ if(our_disk)
+ our_disk.forceMove(src)//Hacky way of keeping the disk inside.
/obj/machinery/artifact_xray/attackby(obj/item/item, mob/living/user, params)
if(HAS_TRAIT(item, TRAIT_NODROP))
to_chat(user, span_warning("[item] is stuck to your hand, you can't put it inside [src]!"))
return
- if(state_open && COOLDOWN_FINISHED(src,pulse_cooldown))
- close_machine(item)
+ if(istype(item,/obj/item/disk/artifact))
+ if(our_disk)
+ to_chat(user,"You swap [our_disk.name] for [item.name]")
+ if(!user.put_in_inactive_hand(our_disk))
+ our_disk.forceMove(get_turf(user))
+ item.forceMove(src)
+ our_disk = item
+ else
+ to_chat(user,"You insert [item.name]")
+ item.forceMove(src)
+ our_disk = item
return
+ if(state_open)
+ if(COOLDOWN_FINISHED(src,pulse_cooldown))
+ close_machine(item)
+ return
..()
/obj/machinery/artifact_xray/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
@@ -89,7 +142,7 @@
return
if(isliving(occupant))
if(!(obj_flags & EMAGGED))
- say("Cannot pulse with a living being inside!")
+ say("ERROR: Life-signs detected in chamber!")
return
var/datum/component/artifact/component = occupant.GetComponent(/datum/component/artifact)
if(component)
@@ -106,11 +159,52 @@
playsound(loc, 'sound/machines/chime.ogg', 30, FALSE)
COOLDOWN_START(src,pulse_cooldown,pulse_cooldown_time)
pulsing = FALSE
- if(artifact)
- last_results = list("STRUCTURAL ABNORMALITY ANALYSIS: [artifact.xray_result]", "SIZE: [artifact.artifact_size < ARTIFACT_SIZE_LARGE ? "SMALL" : "LARGE" ]")
+ if(artifact && stored_research)
+ var/research_added = 0
+ if(!artifact.fault_discovered && artifact.chosen_fault)
+ artifact.freebies = 0 //No more freebies, you know what it does now.
+ artifact.fault_discovered = TRUE
+ research_added += artifact.chosen_fault.research_value
+ if(artifact.chosen_fault)
+ last_results = list("ARTIFACT FAULT DISCOVERED: [artifact.chosen_fault.name]", "SIZE: [artifact.artifact_size < ARTIFACT_SIZE_LARGE ? "SMALL" : "LARGE" ]")
+ else
+ research_added += 2500
+ last_results = list("FLAWLESS ARTIFACT. NO FAULTS.", "SIZE: [artifact.artifact_size < ARTIFACT_SIZE_LARGE ? "SMALL" : "LARGE" ]")
+ if(length(artifact.discovered_effects) != length(artifact.artifact_effects))
+ for(var/datum/artifact_effect/eff in artifact.artifact_effects)
+ artifact.discovered_effects += eff.type
+ research_added += eff.research_value
+ last_results += "ARTIFACT EFFECTS REVEALED."
+ if(!length(artifact.artifact_effects))
+ last_results += "MUNDANE ARTIFACT DETECTED. NO NOTEABLE EFFECTS."
+ if(length(artifact.activators) != length(artifact.activators))
+ for(var/datum/artifact_activator/activator in artifact.activators)
+ artifact.discovered_activators += activator.type
+ research_added += activator.research_value
+ last_results += "ARTIFACT ACTIVATORS REVEALED."
+ last_results+= "WARNING: ARTIFACT FAULT NOW ACTIVE."
+ if(research_added > 0 && !artifact.researched)
+ artifact.researched = TRUE
+ src.visible_message(span_notice("[src] blares: ") + span_robot("ARTIFACT RESEARCHED:[research_added] ADDED TO LINKED CONSOLE"))
+ stored_research.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = research_added))
+ if(our_disk && destroy_artifact_mode)
+ destructive_scan_artifact(artifact)
else
- last_results = list("INCONCLUSIVE;", "NO SPECIAL PROPERTIES DETECTED")
-
+ last_results = list("INCONCLUSIVE;", "NO SPECIAL PROPERTIES DETECTED OR NO RESEARCH CONSOLE LINKED.")
+ return
+
+/obj/machinery/artifact_xray/proc/destructive_scan_artifact(datum/component/artifact/the_artifact)
+ if(prob(disk_chance + (5*chosen_level)))
+ our_disk.effect = pick(the_artifact.artifact_effects)
+ our_disk.activator = pick(the_artifact.activators)
+ if(prob(100-destroy_chance)) //Better scanners means better chance of NOT getting fault
+ our_disk.fault = the_artifact.chosen_fault
+ our_disk.update_name()
+ src.visible_message(span_robot("NOTICE: DATA DISK RECORDED."))
+ if(prob(destroy_chance + round(2.5 * chosen_level)))
+ the_artifact.clear_out()
+ src.visible_message(span_robot("WARNING: ARTIFACT RENDERED INERT."))
+ return
/obj/machinery/artifact_xray/ui_data(mob/user)
. = ..()
@@ -145,11 +239,17 @@
else if(!occupant_atom.anchored)
return TRUE
-/obj/machinery/artifact_xray/screwdriver_act(mob/living/user, obj/item/tool)
+/obj/machinery/artifact_xray/wrench_act(mob/living/user, obj/item/tool)
if(pulsing)
return TOOL_ACT_SIGNAL_BLOCKING
- . = default_deconstruction_screwdriver(user, base_icon_state, base_icon_state, tool)
+ destroy_artifact_mode = !destroy_artifact_mode
+ var/modestring = destroy_artifact_mode ? "DESTRUCTIVE SCAN" : "NON-DESTRUCTIVE SCAN"
+ to_chat(user,span_notice("[src] switched to [modestring] mode."))
+ return TOOL_ACT_MELEE_CHAIN_BLOCKING
+
+/obj/machinery/artifact_xray/screwdriver_act(mob/living/user, obj/item/tool)
+ return pulsing ? TOOL_ACT_SIGNAL_BLOCKING : default_deconstruction_screwdriver(user, "xray-maint", "xray-1", tool)
/obj/machinery/artifact_xray/crowbar_act(mob/living/user, obj/item/tool)
return pulsing ? TOOL_ACT_SIGNAL_BLOCKING : default_deconstruction_crowbar(tool)
diff --git a/monkestation/code/modules/art_sci_overrides/artifact_testers/zapper.dm b/monkestation/code/modules/art_sci_overrides/artifact_testers/zapper.dm
index b877d2b4fe91..ca12f10a415c 100644
--- a/monkestation/code/modules/art_sci_overrides/artifact_testers/zapper.dm
+++ b/monkestation/code/modules/art_sci_overrides/artifact_testers/zapper.dm
@@ -1,6 +1,6 @@
/obj/machinery/artifact_zapper
name = "artifact zapper"
- desc = "A directed tesla coil, zaps the artifact that it is facing. VERY power-consuming."
+ desc = "A directed tesla coil, zaps the object that it is facing. VERY power-consuming. Can attempt to turn artifacts into wands if hit with a multi-tool."
icon = 'icons/obj/machines/artifact_machines.dmi'
icon_state = "zapper"
base_icon_state = "zapper"
@@ -12,6 +12,8 @@
///chosen level
var/chosen_level = 100
var/pulse_cooldown_time = 4 SECONDS
+
+ var/into_wand_mode = FALSE
COOLDOWN_DECLARE(pulse_cooldown)
/obj/machinery/artifact_zapper/Initialize(mapload)
@@ -59,14 +61,21 @@
component = object.GetComponent(/datum/component/artifact)
if(component)
break
-
if(!component)
return
Beam(component.parent, icon_state="lightning[rand(1,12)]", time = pulse_cooldown_time)
playsound(get_turf(src), 'sound/magic/lightningshock.ogg', 60, TRUE, extrarange = 2)
use_power(chosen_level)
- component.process_stimuli(STIMULUS_SHOCK, chosen_level)
+ if(!into_wand_mode)
+ component.process_stimuli(STIMULUS_SHOCK, chosen_level)
+ else
+ if(prob(round(chosen_level/100)))
+ var/obj/item/artifact_summon_wand/wand = new(target_turf)
+ visible_message("[component.holder] is shocked into [wand]!")
+ else
+ visible_message("[component.holder] is shocked into oblivion!")
+ qdel(component.holder)
COOLDOWN_START(src,pulse_cooldown, pulse_cooldown_time)
@@ -84,11 +93,20 @@
to_chat(user,span_notice("You short out the safety sensors on the [src]."))
playsound(src, SFX_SPARKS, 75, TRUE, SILENCED_SOUND_EXTRARANGE)
+
+/obj/machinery/artifact_zapper/multitool_act(mob/living/user, obj/item/tool)
+ if(!COOLDOWN_FINISHED(src,pulse_cooldown))
+ return TOOL_ACT_SIGNAL_BLOCKING
+ into_wand_mode = !into_wand_mode
+ visible_message(span_info("[src] switches to [into_wand_mode ? "attempting to break down artifacts." : "just zapping artifacts." ]"))
+ return TOOL_ACT_MELEE_CHAIN_BLOCKING
/obj/machinery/artifact_zapper/screwdriver_act(mob/living/user, obj/item/tool)
if(!COOLDOWN_FINISHED(src,pulse_cooldown))
return TOOL_ACT_SIGNAL_BLOCKING
. = default_deconstruction_screwdriver(user, base_icon_state, base_icon_state, tool)
-
/obj/machinery/artifact_zapper/crowbar_act(mob/living/user, obj/item/tool)
return !COOLDOWN_FINISHED(src,pulse_cooldown) ? TOOL_ACT_SIGNAL_BLOCKING : default_deconstruction_crowbar(tool)
+
+/obj/machinery/artifact_zapper/wrench_act_secondary(mob/living/user, obj/item/tool)
+ return !COOLDOWN_FINISHED(src,pulse_cooldown) ? TOOL_ACT_SIGNAL_BLOCKING : default_unfasten_wrench(user, tool)
diff --git a/monkestation/code/modules/art_sci_overrides/faults/_fault.dm b/monkestation/code/modules/art_sci_overrides/faults/_fault.dm
index 607340139449..2a86f5e958ec 100644
--- a/monkestation/code/modules/art_sci_overrides/faults/_fault.dm
+++ b/monkestation/code/modules/art_sci_overrides/faults/_fault.dm
@@ -4,12 +4,34 @@
var/visible_message
///the chance of us triggering on bad info
var/trigger_chance = 0
+ //how many credits do we get for discovering this? Should be negative.
+ var/discovered_credits = 0
+ //If availible, warns users that this WILL fuck you up. Picks randomly from list
+ var/list/inspect_warning
+ ///Added by xray machine when discovered.
+ var/research_value = 0
+ ///How likely the fault is to roll.
+ var/weight = ARTIFACT_COMMON
+ ///Our Artifact
+ var/datum/component/artifact/our_artifact
-/datum/artifact_fault/proc/on_trigger(datum/component/artifact/component)
+/datum/artifact_fault/Destroy(force)
+ our_artifact = null
+ return ..()
+
+///called when the artifact gets a stimulus, and passes its trigger chance effect.
+/datum/artifact_fault/proc/on_trigger()
+ return
+
+///Called when the artifact trait comes into existance
+/datum/artifact_fault/proc/on_added()
return
/datum/artifact_fault/shutdown
- name = "Generic Shutdown Fault"
+ name = "Random Shutdown Fault"
+ visible_message = "has something malfunction and shuts down!"
+ trigger_chance = 1
+
/datum/artifact_fault/on_trigger(datum/component/artifact/component)
if(component.active)
diff --git a/monkestation/code/modules/art_sci_overrides/faults/bioscrambler.dm b/monkestation/code/modules/art_sci_overrides/faults/bioscrambler.dm
new file mode 100644
index 000000000000..f6ee86615c5e
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/faults/bioscrambler.dm
@@ -0,0 +1,21 @@
+/datum/artifact_fault/bioscramble
+ name = "Bioscrambling Fault"
+ trigger_chance = 3
+ visible_message = "corrupts nearby biological life!"
+
+ inspect_warning = list(span_danger("It looks like its made of patchwork flesh!"),
+ span_danger("It looks Frankenstien like!"))
+
+ research_value = 250
+
+ weight = ARTIFACT_UNCOMMON
+
+/datum/artifact_fault/bioscramble/on_trigger()
+ var/center_turf = get_turf(our_artifact.parent)
+
+ if(!center_turf)
+ CRASH("[src] had attempted to trigger, but failed to find the center turf!")
+
+ for(var/mob/living/carbon/mob in range(rand(3, 4), center_turf))
+ for(var/i in 1 to 3)
+ mob.bioscramble(our_artifact.holder)
diff --git a/monkestation/code/modules/art_sci_overrides/faults/clowning.dm b/monkestation/code/modules/art_sci_overrides/faults/clowning.dm
new file mode 100644
index 000000000000..891e99e825ab
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/faults/clowning.dm
@@ -0,0 +1,19 @@
+/datum/artifact_fault/clown
+ name = "Funny Fault"
+ trigger_chance = 5
+ inspect_warning = list("Smells faintly of bananas","Looks Funny.","Hates mimes.")
+ visible_message = "summons a portal to the HONK DIMENSION!"
+ discovered_credits = -500
+ research_value = 250
+
+ weight = ARTIFACT_VERYRARE
+
+/datum/artifact_fault/clown/on_trigger()
+ var/center_turf = get_turf(our_artifact.parent)
+
+ if(!center_turf)
+ CRASH("[src] had attempted to trigger, but failed to find the center turf!")
+
+ var/obj/structure/spawner/clown/hehe = new(src)
+
+ addtimer(CALLBACK(hehe,PROC_REF(Destroy)),3 MINUTE)
diff --git a/monkestation/code/modules/art_sci_overrides/faults/explosion.dm b/monkestation/code/modules/art_sci_overrides/faults/explosion.dm
index f7c028e58860..e75032695910 100644
--- a/monkestation/code/modules/art_sci_overrides/faults/explosion.dm
+++ b/monkestation/code/modules/art_sci_overrides/faults/explosion.dm
@@ -1,12 +1,16 @@
/datum/artifact_fault/explosion
- name = "Explode"
+ name = "Exploding Fault"
trigger_chance = 3
visible_message = "reaches a catastrophic overload, cracks forming at its surface!"
-/datum/artifact_fault/explosion/on_trigger(datum/component/artifact/component)
- component.holder.Shake(duration = 5 SECONDS, shake_interval = 0.08 SECONDS)
- addtimer(CALLBACK(src, PROC_REF(payload), component), 5 SECONDS)
+ research_value = 500 //nanotrasen always likes weapons IMO
-/datum/artifact_fault/explosion/proc/payload(datum/component/artifact/component)
- explosion(component.holder, light_impact_range = 2, explosion_cause = src)
- qdel(component.holder)
+ weight = ARTIFACT_UNCOMMON
+
+/datum/artifact_fault/explosion/on_trigger()
+ our_artifact.holder.Shake(duration = 5 SECONDS, shake_interval = 0.08 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(payload), our_artifact), 5 SECONDS)
+
+/datum/artifact_fault/explosion/proc/payload()
+ explosion(our_artifact.holder, light_impact_range = 2, explosion_cause = src)
+ qdel(our_artifact.holder)
diff --git a/monkestation/code/modules/art_sci_overrides/faults/greg.dm b/monkestation/code/modules/art_sci_overrides/faults/greg.dm
new file mode 100644
index 000000000000..84b18480cf62
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/faults/greg.dm
@@ -0,0 +1,16 @@
+/datum/artifact_fault/greg
+ name = "Greg Fault"
+ discovered_credits = 250
+ research_value = 1000
+ trigger_chance = 5
+ weight = ARTIFACT_RARE
+
+/datum/artifact_fault/greg/on_added()
+ our_artifact.holder.AddComponent(/datum/component/ghost_object_control,our_artifact.holder,TRUE)
+
+/datum/artifact_fault/greg/on_trigger()
+ var/datum/component/ghost_object_control/spiritholder = our_artifact.holder.GetComponent(/datum/component/ghost_object_control)
+
+ if(!(spiritholder.bound_spirit))
+ spiritholder.request_control(0.8)
+
diff --git a/monkestation/code/modules/art_sci_overrides/faults/ignite.dm b/monkestation/code/modules/art_sci_overrides/faults/ignite.dm
index 631f17f850ae..be0cb8498d11 100644
--- a/monkestation/code/modules/art_sci_overrides/faults/ignite.dm
+++ b/monkestation/code/modules/art_sci_overrides/faults/ignite.dm
@@ -1,10 +1,14 @@
/datum/artifact_fault/ignite
- name = "Combust"
- trigger_chance = 9
+ name = "Combustion Fault"
+ trigger_chance = 10
visible_message = "starts rapidly heating up while covering everything around it in something that seems to be oil."
-/datum/artifact_fault/ignite/on_trigger(datum/component/artifact/component)
- var/center_turf = get_turf(component.parent)
+ research_value = 200
+
+ weight = ARTIFACT_UNCOMMON
+
+/datum/artifact_fault/ignite/on_trigger()
+ var/center_turf = get_turf(our_artifact.parent)
if(!center_turf)
CRASH("[src] had attempted to trigger, but failed to find the center turf!")
diff --git a/monkestation/code/modules/art_sci_overrides/faults/just_death.dm b/monkestation/code/modules/art_sci_overrides/faults/just_death.dm
index 8b7b2f8d3c45..c2585b17aef3 100644
--- a/monkestation/code/modules/art_sci_overrides/faults/just_death.dm
+++ b/monkestation/code/modules/art_sci_overrides/faults/just_death.dm
@@ -1,21 +1,29 @@
/datum/artifact_fault/death
- name = "Instant Death"
- trigger_chance = 1
+ name = "Instant Death Fault"
+ trigger_chance = 50 //God forbid this actually rolls on a touch artifact,like it did during my testing.
visible_message = "blows someone up with mind."
+ inspect_warning = list(span_danger("The grim reapers scythe seems to be reflected in its surface!"),
+ span_danger("An Aura of death surrounds this object!"),
+ span_danger("I'd bet 50/50 someone dies if this turns on!"))
-/datum/artifact_fault/death/on_trigger(datum/component/artifact/component)
+ research_value = 10000 //Wow, this would make a fucking amazing weapon
+
+ weight = ARTIFACT_VERYRARE
+/datum/artifact_fault/death/on_trigger()
var/list/mobs = list()
var/mob/living/carbon/human
- var/center_turf = get_turf(component.parent)
+ var/center_turf = get_turf(our_artifact.parent)
if(!center_turf)
CRASH("[src] had attempted to trigger, but failed to find the center turf!")
- for(var/mob/living/carbon/mob in range(rand(3, 4), center_turf))
+ for(var/mob/living/carbon/mob in range(rand(2, 3), center_turf))
mobs += mob
+ if(!length(mobs))
+ return
human = pick(mobs)
if(!human)
return
- component.holder.Beam(human, icon_state = "lightning[rand(1,12)]", time = 0.5 SECONDS)
+ our_artifact.holder.Beam(human, icon_state = "lightning[rand(1,12)]", time = 0.5 SECONDS)
human.death(FALSE)
diff --git a/monkestation/code/modules/art_sci_overrides/faults/monkey_mode.dm b/monkestation/code/modules/art_sci_overrides/faults/monkey_mode.dm
new file mode 100644
index 000000000000..753de53424a5
--- /dev/null
+++ b/monkestation/code/modules/art_sci_overrides/faults/monkey_mode.dm
@@ -0,0 +1,34 @@
+/datum/artifact_fault/monkey_mode
+ name = "Simian Spawner Fault"
+ trigger_chance = 5
+ visible_message = "summons a mass of simians!"
+
+ research_value = 250
+
+ weight = ARTIFACT_VERYUNCOMMON
+
+/datum/artifact_fault/monkey_mode/on_trigger()
+ var/monkey = rand(1,4)
+ var/center_turf = get_turf(our_artifact.parent)
+ var/list/turf/valid_turfs = list()
+ if(!center_turf)
+ CRASH("[src] had attempted to trigger, but failed to find the center turf!")
+ for(var/turf/boi in range(rand(3,6),center_turf))
+ if(boi.density)
+ continue
+ valid_turfs += boi
+ for(var/i in 1 to monkey)
+ var/turf/spawnon = pick(valid_turfs)
+ valid_turfs -= spawnon
+ var/pain = roll(1,100)
+ var/mob/living/M //For monkey
+ switch(pain)
+ if(1 to 75)
+ M = new /mob/living/carbon/human/species/monkey/angry(spawnon)
+ if(75 to 95)
+ M = new /mob/living/basic/gorilla(spawnon)
+ if(95 to 100)
+ M = new /mob/living/basic/gorilla/lesser(spawnon)//OH GOD ITS TINY
+ if(M) //Just in case.
+ M.forceMove(spawnon)
+
diff --git a/monkestation/code/modules/art_sci_overrides/faults/reagents.dm b/monkestation/code/modules/art_sci_overrides/faults/reagents.dm
index c2ab103e18cb..1b9840223db5 100644
--- a/monkestation/code/modules/art_sci_overrides/faults/reagents.dm
+++ b/monkestation/code/modules/art_sci_overrides/faults/reagents.dm
@@ -1,15 +1,17 @@
/datum/artifact_fault/reagent
- name = "Generic Reagent Injector"
+ name = "Chemical Force Injector Fault"
trigger_chance = 15
visible_message = "shoots a syringe out."
var/list/reagents = list()
-/datum/artifact_fault/reagent/on_trigger(datum/component/artifact/component)
+ research_value = 100
+
+/datum/artifact_fault/reagent/on_trigger()
. = ..()
if(!length(reagents))
return
- var/center_turf = get_turf(component.parent)
+ var/center_turf = get_turf(our_artifact.parent)
if(!center_turf)
CRASH("[src] had attempted to trigger, but failed to find the center turf!")
@@ -21,7 +23,7 @@
/datum/artifact_fault/reagent/poison
name = "Poison"
-/datum/artifact_fault/reagent/poison/on_trigger(datum/component/artifact/component)
+/datum/artifact_fault/reagent/poison/on_trigger()
if(!reagents.len) //mostly copied from reagents.dm but oh well
for(var/datum/reagent/reagent as anything in subtypesof(/datum/reagent/toxin))
if(initial(reagent.chemical_flags) & REAGENT_CAN_BE_SYNTHESIZED)
diff --git a/monkestation/code/modules/art_sci_overrides/faults/say.dm b/monkestation/code/modules/art_sci_overrides/faults/say.dm
index fca9f1d6e653..f9b56de690a1 100644
--- a/monkestation/code/modules/art_sci_overrides/faults/say.dm
+++ b/monkestation/code/modules/art_sci_overrides/faults/say.dm
@@ -1,19 +1,21 @@
/datum/artifact_fault/speech
- name = "Generic Speech"
- trigger_chance = 30
- var/list/speech = list()
+ name = "Talkative Fault"
+ trigger_chance = 25
+ var/list/speech = list("Hello there.","I see you.","I know what you've done.","So hows your shift?","HELP ARTIFACT IS MAKING ME SPEAK","All is one.","One is all.")
-/datum/artifact_fault/speech/on_trigger(datum/component/artifact/component)
+ research_value = 50
+
+/datum/artifact_fault/speech/on_trigger()
if(!length(speech))
return
- var/center_turf = get_turf(component.parent)
+ var/center_turf = get_turf(our_artifact.parent)
if(!center_turf)
CRASH("[src] had attempted to trigger, but failed to find the center turf!")
for(var/mob/living/living in range(rand(7, 10), center_turf))
- if(prob(50))
+ if(prob(10))
living.say("; [pick(speech)]")
else
living.say("[pick(speech)]")
diff --git a/monkestation/code/modules/art_sci_overrides/faults/size_change.dm b/monkestation/code/modules/art_sci_overrides/faults/size_change.dm
index 847a555801ef..cfe4599826c4 100644
--- a/monkestation/code/modules/art_sci_overrides/faults/size_change.dm
+++ b/monkestation/code/modules/art_sci_overrides/faults/size_change.dm
@@ -1,33 +1,40 @@
/datum/artifact_fault/shrink
- name = "Shrink"
+ name = "Shrinking Fault"
trigger_chance = 13
visible_message = "starts to shrink."
-/datum/artifact_fault/shrink/on_trigger(datum/component/artifact/component)
- component.holder.transform = matrix(component.holder.transform, 0.9, 0.9, MATRIX_SCALE)
- if(!isstructure(component.holder))
+ research_value = 200
+
+/datum/artifact_fault/shrink/on_trigger()
+ our_artifact.holder.transform = matrix(our_artifact.holder.transform, 0.9, 0.9, MATRIX_SCALE)
+ if(!isstructure(our_artifact.holder))
return
- var/obj/structure/structure = component.holder
+ var/obj/structure/structure = our_artifact.holder
structure.w_class--
if(structure.w_class < WEIGHT_CLASS_TINY)
- component.holder.visible_message("[component.holder] vanishes into thin air!")
- qdel(component.holder)
+ our_artifact.holder.visible_message("[our_artifact.holder] vanishes into thin air!")
+ qdel(our_artifact.holder)
/datum/artifact_fault/grow
- name = "Grow"
+ name = "Growing Fault"
trigger_chance = 13
visible_message = "starts to grow."
-/datum/artifact_fault/grow/on_trigger(datum/component/artifact/component)
- if(!isitem(component.holder))
+ var/trigger_count = 0
+/datum/artifact_fault/grow/on_trigger()
+ trigger_count++
+ if(trigger_count<5)
+ our_artifact.holder.transform = matrix(our_artifact.holder.transform, 1.1, 1.1, MATRIX_SCALE)
+ else
+ our_artifact.holder.visible_message("[our_artifact.holder] can't possibly grow any larger!")
+ return
+ if(!isitem(our_artifact.holder))
return
- var/obj/item/item = component.holder
+ var/obj/item/item = our_artifact.holder
if(item.w_class > WEIGHT_CLASS_HUGE)
return
- component.holder.transform = matrix(component.holder.transform, 1.1, 1.1, MATRIX_SCALE)
-
item.w_class++
if(item.w_class > WEIGHT_CLASS_HUGE)
- component.holder.visible_message("[component.holder] becomes to cumbersome to carry!")
- component.holder.anchored = TRUE
+ our_artifact.holder.visible_message("[our_artifact.holder] becomes to cumbersome to carry!")
+ our_artifact.holder.anchored = TRUE
diff --git a/monkestation/code/modules/art_sci_overrides/faults/warps.dm b/monkestation/code/modules/art_sci_overrides/faults/warps.dm
index 6014fc2a03eb..72b8b99fbce4 100644
--- a/monkestation/code/modules/art_sci_overrides/faults/warps.dm
+++ b/monkestation/code/modules/art_sci_overrides/faults/warps.dm
@@ -1,14 +1,18 @@
/datum/artifact_fault/warp
- name = "Generic Warp Artifact"
+ name = "Warping Fault"
trigger_chance = 12
visible_message = "warps space sending everyone away."
var/list/warp_areas = list()
-/datum/artifact_fault/warp/on_trigger(datum/component/artifact/component)
+ research_value = 250
+
+ weight = ARTIFACT_UNCOMMON
+
+/datum/artifact_fault/warp/on_trigger()
if(!length(warp_areas))
warp_areas = GLOB.the_station_areas
var/turf/safe_turf = get_safe_random_station_turf(warp_areas)
- var/center_turf = get_turf(component.parent)
+ var/center_turf = get_turf(our_artifact.parent)
if(!center_turf)
CRASH("[src] had attempted to trigger, but failed to find the center turf!")
diff --git a/monkestation/code/modules/art_sci_overrides/faults/whispers.dm b/monkestation/code/modules/art_sci_overrides/faults/whispers.dm
index b85fd5b57427..727c88a3f55c 100644
--- a/monkestation/code/modules/art_sci_overrides/faults/whispers.dm
+++ b/monkestation/code/modules/art_sci_overrides/faults/whispers.dm
@@ -1,13 +1,15 @@
/datum/artifact_fault/whisper
- name = "Generic Whisper"
- trigger_chance = 30
- var/list/whispers = list()
+ name = "Wispering Fault"
+ trigger_chance = 75
+ var/list/whispers = list("Help me!","I've seen your sins","Egg.")
-/datum/artifact_fault/whisper/on_trigger(datum/component/artifact/component)
+ research_value = 50
+
+/datum/artifact_fault/whisper/on_trigger()
if(!length(whispers))
return
- var/center_turf = get_turf(component.parent)
+ var/center_turf = get_turf(our_artifact.parent)
if(!center_turf)
CRASH("[src] had attempted to trigger, but failed to find the center turf!")
diff --git a/monkestation/code/modules/art_sci_overrides/faults/zap.dm b/monkestation/code/modules/art_sci_overrides/faults/zap.dm
index 303bb44c248c..e09cadc74f5b 100644
--- a/monkestation/code/modules/art_sci_overrides/faults/zap.dm
+++ b/monkestation/code/modules/art_sci_overrides/faults/zap.dm
@@ -1,8 +1,23 @@
/datum/artifact_fault/tesla_zap
- name = "Tesla Zap"
+ name = "Energetic Discharge Fault"
trigger_chance = 12
- visible_message = "discharges a large amount of electricity."
+ visible_message = "discharges a large amount of electricity!"
-/datum/artifact_fault/tesla_zap/on_trigger(datum/component/artifact/component)
- . = ..()
- tesla_zap(component.holder, rand(4, 7), ZAP_MOB_DAMAGE)
+ research_value = 200
+
+ weight = ARTIFACT_RARE
+
+/datum/artifact_fault/tesla_zap/on_trigger()
+ var/list/mobs = list()
+
+ var/center_turf = get_turf(our_artifact.parent)
+
+ if(!center_turf)
+ CRASH("[src] had attempted to trigger, but failed to find the center turf!")
+ var/shock_range = rand(4, 7)
+ for(var/mob/living/carbon/mob in range(shock_range, center_turf))
+ mobs += mob
+ if(!length(mobs))
+ return
+
+ tesla_zap(our_artifact.holder, shock_range, ZAP_MOB_DAMAGE,shocked_targets = mobs)
diff --git a/monkestation/code/modules/art_sci_overrides/generic_artifact_objects.dm b/monkestation/code/modules/art_sci_overrides/generic_artifact_objects.dm
index 6443ef06db38..3192c5dd3e98 100644
--- a/monkestation/code/modules/art_sci_overrides/generic_artifact_objects.dm
+++ b/monkestation/code/modules/art_sci_overrides/generic_artifact_objects.dm
@@ -6,10 +6,11 @@
resistance_flags = LAVA_PROOF | ACID_PROOF | INDESTRUCTIBLE
anchored = FALSE
density = TRUE
- var/datum/component/artifact/assoc_comp
+ var/datum/artifact_effect/forced_effect
+ var/datum/component/artifact/assoc_comp = /datum/component/artifact
var/mutable_appearance/extra_effect
-ARTIFACT_SETUP(/obj/structure/artifact, SSobj)
+ARTIFACT_SETUP(/obj/structure/artifact, SSobj, null, forced_effect, null)
/obj/effect/artifact_spawner
name = "Random Artifact Spawner"
@@ -22,54 +23,54 @@ ARTIFACT_SETUP(/obj/structure/artifact, SSobj)
qdel(src)
/obj/structure/artifact/bonk
- assoc_comp = /datum/component/artifact/bonk
+ forced_effect = /datum/artifact_effect/bonk
/obj/structure/artifact/bomb
- assoc_comp = /datum/component/artifact/bomb/explosive
+ forced_effect = /datum/artifact_effect/bomb/explosive
/obj/structure/artifact/bomb/devastating
- assoc_comp = /datum/component/artifact/bomb/explosive/devastating
+ forced_effect = /datum/artifact_effect/bomb/explosive/devastating
/obj/structure/artifact/bomb/gas
- assoc_comp = /datum/component/artifact/bomb/gas
+ forced_effect = /datum/artifact_effect/bomb/gas
/obj/structure/artifact/forcegen
- assoc_comp = /datum/component/artifact/forcegen
+ forced_effect = /datum/artifact_effect/forcegen
/obj/structure/artifact/heal
- assoc_comp = /datum/component/artifact/heal
+ forced_effect = /datum/artifact_effect/heal
/obj/structure/artifact/injector
- assoc_comp = /datum/component/artifact/injector
+ forced_effect = /datum/artifact_effect/injector
/obj/structure/artifact/lamp
- assoc_comp = /datum/component/artifact/lamp
+ forced_effect = /datum/artifact_effect/lamp
light_system = OVERLAY_LIGHT
light_on = FALSE
/obj/structure/artifact/repulsor
- assoc_comp = /datum/component/artifact/repulsor
+ forced_effect = /datum/artifact_effect/repulsor
/obj/structure/artifact/vomit
- assoc_comp = /datum/component/artifact/vomit
+ forced_effect = /datum/artifact_effect/vomit
/obj/structure/artifact/borger
- assoc_comp = /datum/component/artifact/borger
+ forced_effect = /datum/artifact_effect/borger
/obj/structure/artifact/emotegen
- assoc_comp = /datum/component/artifact/emotegen
+ forced_effect = /datum/artifact_effect/emotegen
/obj/structure/artifact/surgery
- assoc_comp = /datum/component/artifact/surgery
+ forced_effect = /datum/artifact_effect/surgery
/obj/structure/artifact/smoke
- assoc_comp = /datum/component/artifact/smoke
+ forced_effect = /datum/artifact_effect/smoke
/obj/structure/artifact/smoke/toxin
- assoc_comp = /datum/component/artifact/smoke/toxin
+ forced_effect = /datum/artifact_effect/smoke/toxin
/obj/structure/artifact/smoke/flesh
- assoc_comp = /datum/component/artifact/smoke/flesh
+ forced_effect = /datum/artifact_effect/smoke/flesh
/obj/structure/artifact/smoke/exotic
- assoc_comp = /datum/component/artifact/smoke/exotic
+ forced_effect = /datum/artifact_effect/smoke/exotic
diff --git a/monkestation/code/modules/art_sci_overrides/globals.dm b/monkestation/code/modules/art_sci_overrides/globals.dm
index 25627d3e71a6..c8353234392e 100644
--- a/monkestation/code/modules/art_sci_overrides/globals.dm
+++ b/monkestation/code/modules/art_sci_overrides/globals.dm
@@ -1,2 +1,2 @@
-GLOBAL_LIST_INIT(artifact_rarity, list())
+GLOBAL_LIST_INIT(artifact_effect_rarity, list())
GLOBAL_LIST_INIT(running_artifact_list, list())
diff --git a/monkestation/code/modules/art_sci_overrides/new_procs.dm b/monkestation/code/modules/art_sci_overrides/new_procs.dm
index 18826afdf0db..dd481d85caca 100644
--- a/monkestation/code/modules/art_sci_overrides/new_procs.dm
+++ b/monkestation/code/modules/art_sci_overrides/new_procs.dm
@@ -1,7 +1,7 @@
/proc/random_rgb_pairlists(list/red_pairs, list/green_pairs, list/blue_pairs, list/alpha_pairs)
if(!length(red_pairs) || !length(blue_pairs) || !length(green_pairs) || !length(alpha_pairs))
return COLOR_CULT_RED
-
+
if(!length(red_pairs) >= 2)
red_pairs[2] = 255
if(!length(blue_pairs) >= 2)
@@ -18,35 +18,37 @@
return rgb(red, green, blue, alpha)
-
-/proc/spawn_artifact(turf/loc, forced_origin)
+///Spawn a new artifact
+/proc/spawn_artifact(turf/loc, forced_origin = null, forced_effect = null)
if (!loc)
return
- if(!length(GLOB.artifact_rarity))
+ if(!length(GLOB.artifact_effect_rarity))
build_weighted_rarities()
- var/list/weighted_list
-
- if(forced_origin)
- weighted_list = GLOB.artifact_rarity[forced_origin]
- else
- weighted_list = GLOB.artifact_rarity["all"]
+ var/obj/type_of_artifact = pick_weight(list(
+ /obj/structure/artifact = 70,
+ /obj/item/artifact_item = 10,
+ /obj/item/artifact_item_tiny = 10,
+ /obj/item/stock_parts/cell/artifact = 2.5,
+ /obj/item/gun/magic/artifact = 2.5,
+ /obj/item/melee/artifact = 2.5,
+ /obj/machinery/power/generator_artifact = 2.5
+ ))
- var/datum/component/artifact/picked = pick_weight(weighted_list)
- var/type = initial(picked.associated_object)
- return new type(loc)
+ var/obj/A = new type_of_artifact(loc,forced_origin,forced_effect)
+ return A
/proc/build_weighted_rarities()
- GLOB.artifact_rarity["all"] = list() ///this needs to be created first for indexing sake
+ GLOB.artifact_effect_rarity["all"] = list() ///this needs to be created first for indexing sake
for(var/datum/artifact_origin/origin as anything in subtypesof(/datum/artifact_origin))
- GLOB.artifact_rarity[initial(origin.type_name)] = list()
+ GLOB.artifact_effect_rarity[initial(origin.type_name)] = list()
- for(var/datum/component/artifact/artifact_type as anything in subtypesof(/datum/component/artifact))
- var/weight = initial(artifact_type.weight)
+ for(var/datum/artifact_effect/artifact_effect as anything in subtypesof(/datum/artifact_effect))
+ var/weight = initial(artifact_effect.weight)
if(!weight)
continue
- GLOB.artifact_rarity["all"][artifact_type] = weight
- for(var/origin in GLOB.artifact_rarity)
- if(origin in initial(artifact_type.valid_origins))
- GLOB.artifact_rarity[origin][artifact_type] = weight
+ GLOB.artifact_effect_rarity["all"][artifact_effect] = weight
+ for(var/origin in GLOB.artifact_effect_rarity)
+ if(origin in initial(artifact_effect.valid_origins))
+ GLOB.artifact_effect_rarity[origin][artifact_effect] = weight
diff --git a/monkestation/code/modules/assault_ops/code/armaments/implants.dm b/monkestation/code/modules/assault_ops/code/armaments/implants.dm
index 06e2e4a8f746..f5facb3ca94e 100644
--- a/monkestation/code/modules/assault_ops/code/armaments/implants.dm
+++ b/monkestation/code/modules/assault_ops/code/armaments/implants.dm
@@ -44,5 +44,5 @@
/datum/armament_entry/assault_operatives/implants/nodrop
name = "Anti-Drop Implant"
description = "When activated forces your hand muscles to tightly grip the object you are holding, preventing you from dropping it involuntarily."
- item_type = /obj/item/autosurgeon/syndicate/nodrop
+ item_type = /obj/item/storage/box/syndie_kit/nodrop
cost = 5
diff --git a/monkestation/code/modules/assault_ops/code/midround_event.dm b/monkestation/code/modules/assault_ops/code/midround_event.dm
index 8e66e0dc0172..9d87f1e4fd1b 100644
--- a/monkestation/code/modules/assault_ops/code/midround_event.dm
+++ b/monkestation/code/modules/assault_ops/code/midround_event.dm
@@ -7,6 +7,7 @@
restricted_roles = list(
JOB_AI,
JOB_CAPTAIN,
+ JOB_BLUESHIELD,
JOB_CHIEF_ENGINEER,
JOB_CHIEF_MEDICAL_OFFICER,
JOB_CYBORG,
@@ -25,6 +26,7 @@
JOB_AI,
JOB_CYBORG,
JOB_CAPTAIN,
+ JOB_BLUESHIELD,
JOB_DETECTIVE,
JOB_HEAD_OF_SECURITY,
JOB_SECURITY_OFFICER,
diff --git a/monkestation/code/modules/assault_ops/code/shuttle.dm b/monkestation/code/modules/assault_ops/code/shuttle.dm
index d1e05c6b25a2..a418019a4894 100644
--- a/monkestation/code/modules/assault_ops/code/shuttle.dm
+++ b/monkestation/code/modules/assault_ops/code/shuttle.dm
@@ -3,7 +3,7 @@
desc = "The terminal used to control the goldeneye cruiser."
shuttleId = "goldeneye_cruiser"
possible_destinations = "goldeneye_cruiser_custom;goldeneye_cruiser_dock;syndicate_away;syndicate_z5;syndicate_ne;syndicate_nw;syndicate_n;syndicate_se;syndicate_sw;syndicate_s;syndicate_cruiser_dock;whiteship_away;whiteship_home;whiteship_z4;whiteship_lavaland;ferry_away"
- circuit = /obj/item/circuitboard/computer/syndicate_shuttle
+ circuit = /obj/item/circuitboard/computer/goldeneye_helm
icon_screen = "syndishuttle"
icon_keyboard = "syndie_key"
light_color = COLOR_SOFT_RED
@@ -22,6 +22,7 @@
name = "goldeneye shuttle recall terminal"
desc = "Use this if your friends left you behind."
possible_destinations = "goldeneye_cruiser_dock"
+ circuit = /obj/item/circuitboard/computer/goldeneye_recall
/obj/machinery/computer/camera_advanced/shuttle_docker/goldeneye_cruiser
name = "goldeneye cruiser navigation computer"
diff --git a/monkestation/code/modules/assembly/flash.dm b/monkestation/code/modules/assembly/flash.dm
index b67ade990435..273ea4cdf1e5 100644
--- a/monkestation/code/modules/assembly/flash.dm
+++ b/monkestation/code/modules/assembly/flash.dm
@@ -5,4 +5,4 @@
var/datum/component/can_flash_from_behind/flash_handler = user.GetComponent(/datum/component/can_flash_from_behind)
if(REF(blood_bond) in flash_handler?.sources)
. += span_boldnotice("In order to convert someone into your blood brother, you must directly flash them, not AoE flash!")
- . += span_warning("Conversion will fail if the target is either dead, unconscious, SSD, mindshielded, a member of security, someone else's brother, or if they are targeted by your objectives.")
+ . += span_warning("Conversion will fail if the target is either dead, unconscious, SSD, mindshielded, someone else's brother or if they are targeted by your objectives.")
diff --git a/monkestation/code/modules/atmospherics/machinery/air_alarm/air_alarm_ac.dm b/monkestation/code/modules/atmospherics/machinery/air_alarm/air_alarm_ac.dm
index 6d39c055972d..b32e3c7aa74a 100644
--- a/monkestation/code/modules/atmospherics/machinery/air_alarm/air_alarm_ac.dm
+++ b/monkestation/code/modules/atmospherics/machinery/air_alarm/air_alarm_ac.dm
@@ -32,6 +32,10 @@
if(air_conditioning)
SSair.start_processing_machine(src)
+/obj/machinery/airalarm/Destroy()
+ SSair.stop_processing_machine(src)
+ return ..()
+
/obj/machinery/airalarm/examine(mob/user)
. = ..()
var/status = air_conditioning ? (ac_active ? "active" : "idle") : "disabled"
diff --git a/monkestation/code/modules/balloon_alert/balloon_alert.dm b/monkestation/code/modules/balloon_alert/balloon_alert.dm
index 1fba56c771bf..2a94efec97c4 100644
--- a/monkestation/code/modules/balloon_alert/balloon_alert.dm
+++ b/monkestation/code/modules/balloon_alert/balloon_alert.dm
@@ -1,5 +1,5 @@
/atom/balloon_alert(mob/viewer, text)
if(istext(viewer) && isnull(text))
stack_trace("Attempted to call balloon_alert with only one argument! This is invalid, but we'll assume that src is the intended viewer.")
- return ..(src, viewer)
+ return ..(ismob(src) ? src : usr, viewer)
return ..()
diff --git a/monkestation/code/modules/blood_for_the_blood_gods/particle.dm b/monkestation/code/modules/blood_for_the_blood_gods/particle.dm
index a74415bfeb8a..cd0966cfb06c 100644
--- a/monkestation/code/modules/blood_for_the_blood_gods/particle.dm
+++ b/monkestation/code/modules/blood_for_the_blood_gods/particle.dm
@@ -19,14 +19,13 @@
return FALSE
/obj/effect/decal/cleanable/blood/particle/proc/start_movement(movement_angle)
- var/datum/component/movable_physics/movable_physics = GetComponent(/datum/component/movable_physics)
- if(!movable_physics)
- movable_physics = initialize_physics()
- if(!isnull(movement_angle))
- movable_physics.set_angle(movement_angle)
+ get_or_init_physics()?.set_angle(movement_angle)
-/obj/effect/decal/cleanable/blood/particle/proc/initialize_physics()
- return AddComponent(/datum/component/movable_physics, \
+/obj/effect/decal/cleanable/blood/particle/proc/get_or_init_physics() as /datum/component/movable_physics
+ RETURN_TYPE(/datum/component/movable_physics)
+ if(QDELETED(src))
+ return
+ return LoadComponent(/datum/component/movable_physics, \
horizontal_velocity = rand(3 * 100, 5.5 * 100) * 0.01, \
vertical_velocity = rand(4 * 100, 4.5 * 100) * 0.01, \
horizontal_friction = rand(0.05 * 100, 0.1 * 100) * 0.01, \
@@ -38,7 +37,9 @@
)
/obj/effect/decal/cleanable/blood/particle/proc/on_bounce()
- if(!isturf(loc) || !splatter_type_floor)
+ if(QDELETED(src))
+ return
+ if(QDELETED(loc) || !isturf(loc) || !splatter_type_floor)
qdel(src)
return
var/obj/effect/decal/cleanable/splatter
@@ -73,7 +74,7 @@
qdel(src)
/obj/effect/decal/cleanable/blood/particle/proc/on_bump(atom/bumped_atom)
- if(!isturf(loc) || !splatter_type_wall)
+ if(QDELETED(src) || QDELETED(bumped_atom) || !isturf(loc) || !splatter_type_wall)
return
if(iswallturf(bumped_atom))
//Adjust pixel offset to make splatters appear on the wall
@@ -119,8 +120,8 @@
update_appearance(UPDATE_ICON)
/obj/effect/decal/cleanable/blood/splatter/stacking/Destroy()
- . = ..()
splat_overlays = null
+ return ..()
/obj/effect/decal/cleanable/blood/splatter/stacking/update_overlays()
. = ..()
diff --git a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_datum.dm b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_datum.dm
index c461448819d2..36e8ea3347ca 100644
--- a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_datum.dm
+++ b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_datum.dm
@@ -91,6 +91,7 @@
TRAIT_RADIMMUNE,
TRAIT_GENELESS,
TRAIT_STABLEHEART,
+ TRAIT_STABLELIVER,
TRAIT_NOSOFTCRIT,
TRAIT_NOHARDCRIT,
TRAIT_AGEUSIA,
@@ -100,6 +101,7 @@
TRAIT_HARDLY_WOUNDED,
TRAIT_NO_MIRROR_REFLECTION,
TRAIT_ETHEREAL_NO_OVERCHARGE,
+ TRAIT_OOZELING_NO_CANNIBALIZE,
)
/// Traits applied during Torpor.
var/static/list/torpor_traits = list(
@@ -107,8 +109,13 @@
TRAIT_FAKEDEATH,
TRAIT_NODEATH,
TRAIT_RESISTHIGHPRESSURE,
- TRAIT_RESISTLOWPRESSURE
+ TRAIT_RESISTLOWPRESSURE,
)
+ /// A typecache of organs we'll expel during Torpor.
+ var/static/list/yucky_organ_typecache = typecacheof(list(
+ /obj/item/organ/internal/body_egg,
+ /obj/item/organ/internal/zombie_infection,
+ ))
/**
* Apply innate effects is everything given to the mob
diff --git a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_life.dm b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_life.dm
index 70c32b35c58d..b1f0cb6b9954 100644
--- a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_life.dm
+++ b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_life.dm
@@ -64,7 +64,7 @@
// Apply to Volume
AddBloodVolume(blood_taken)
// Reagents (NOT Blood!)
- if(target.reagents && target.reagents.total_volume)
+ if(target.reagents?.total_volume)
target.reagents.trans_to(owner.current, INGEST, 1) // Run transfer of 1 unit of reagent from them to me.
owner.current.playsound_local(null, 'sound/effects/singlebeat.ogg', vol = 40, vary = TRUE) // Play THIS sound for user only. The "null" is where turf would go if a location was needed. Null puts it right in their head.
total_blood_drank += blood_taken
@@ -76,11 +76,16 @@
/// Constantly runs on Bloodsucker's LifeTick, and is increased by being in Torpor/Coffins
/datum/antagonist/bloodsucker/proc/HandleHealing(mult = 1)
- var/actual_regen = bloodsucker_regen_rate + additional_regen
+ if(QDELETED(owner?.current))
+ return
+ var/in_torpor = is_in_torpor()
// Don't heal if I'm staked or on Masquerade (+ not in a Coffin). Masqueraded Bloodsuckers in a Coffin however, will heal.
- if(owner.current.am_staked() || (HAS_TRAIT(owner.current, TRAIT_MASQUERADE) && !is_in_torpor()))
+ if(owner.current.am_staked())
return FALSE
- owner.current.adjustCloneLoss(-1 * (actual_regen * 4) * mult, 0)
+ if(!in_torpor && (HAS_TRAIT(owner.current, TRAIT_MASQUERADE) || owner.current.has_status_effect(/datum/status_effect/bloodsucker_sol)))
+ return FALSE
+ var/actual_regen = bloodsucker_regen_rate + additional_regen
+ owner.current.adjustCloneLoss(-1 * (actual_regen * 4) * mult)
owner.current.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1 * (actual_regen * 4) * mult) //adjustBrainLoss(-1 * (actual_regen * 4) * mult, 0)
if(!iscarbon(owner.current)) // Damage Heal: Do I have damage to ANY bodypart?
return
@@ -89,23 +94,23 @@
var/bruteheal = min(user.getBruteLoss_nonProsthetic(), actual_regen) // BRUTE: Always Heal
var/fireheal = 0 // BURN: Heal in Coffin while Fakedeath, or when damage above maxhealth (you can never fully heal fire)
// Checks if you're in a coffin here, additionally checks for Torpor right below it.
- var/amInCoffin = istype(user.loc, /obj/structure/closet/crate/coffin)
- if(amInCoffin && is_in_torpor())
- if(HAS_TRAIT(owner.current, TRAIT_MASQUERADE) && (COOLDOWN_FINISHED(src, bloodsucker_spam_healing)))
- to_chat(user, span_alert("You do not heal while your Masquerade ability is active."))
- COOLDOWN_START(src, bloodsucker_spam_healing, BLOODSUCKER_SPAM_MASQUERADE)
- return
- fireheal = min(user.getFireLoss_nonProsthetic(), actual_regen)
- mult *= 5 // Increase multiplier if we're sleeping in a coffin.
- costMult /= 2 // Decrease cost if we're sleeping in a coffin.
- user.extinguish_mob()
- user.remove_all_embedded_objects() // Remove Embedded!
- if(check_limbs(costMult))
- return TRUE
- // In Torpor, but not in a Coffin? Heal faster anyways.
- else if(is_in_torpor())
- fireheal = min(user.getFireLoss_nonProsthetic(), actual_regen) / 1.2 // 20% slower than being in a coffin
- mult *= 3
+ if(in_torpor)
+ if(istype(user.loc, /obj/structure/closet/crate/coffin))
+ if(HAS_TRAIT(owner.current, TRAIT_MASQUERADE) && (COOLDOWN_FINISHED(src, bloodsucker_spam_healing)))
+ to_chat(user, span_alert("You do not heal while your Masquerade ability is active."))
+ COOLDOWN_START(src, bloodsucker_spam_healing, BLOODSUCKER_SPAM_MASQUERADE)
+ return
+ fireheal = min(user.getFireLoss_nonProsthetic(), actual_regen)
+ mult *= 5 // Increase multiplier if we're sleeping in a coffin.
+ costMult /= 2 // Decrease cost if we're sleeping in a coffin.
+ user.extinguish_mob()
+ user.remove_all_embedded_objects() // Remove Embedded!
+ if(check_limbs(costMult))
+ return TRUE
+ // In Torpor, but not in a Coffin? Heal faster anyways.
+ else
+ fireheal = min(user.getFireLoss_nonProsthetic(), actual_regen) / 1.2 // 20% slower than being in a coffin
+ mult *= 3
// Heal if Damaged
if((bruteheal + fireheal > 0) && mult > 0) // Just a check? Don't heal/spend, and return.
// We have damage. Let's heal (one time)
@@ -164,16 +169,9 @@
bloodsuckeruser.revive()
for(var/datum/wound/iter_wound as anything in bloodsuckeruser.all_wounds)
iter_wound.remove_wound()
- // From [powers/panacea.dm]
- var/list/bad_organs = list(
- bloodsuckeruser.get_organ_by_type(/obj/item/organ/internal/body_egg),
- bloodsuckeruser.get_organ_by_type(/obj/item/organ/internal/zombie_infection))
- for(var/tumors in bad_organs)
- var/obj/item/organ/yucky_organs = tumors
- if(!istype(yucky_organs))
- continue
- yucky_organs.Remove(bloodsuckeruser)
- yucky_organs.forceMove(get_turf(bloodsuckeruser))
+ for(var/obj/item/organ/organ as anything in typecache_filter_list(bloodsuckeruser.organs, yucky_organ_typecache))
+ organ.Remove(bloodsuckeruser)
+ organ.forceMove(bloodsuckeruser.drop_location())
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -198,7 +196,7 @@
final_death()
return
// Fire Damage? (above double health)
- if(owner.current.getFireLoss() >= owner.current.maxHealth * 2.5)
+ if(owner.current.getFireLoss() >= (owner.current.maxHealth * 2.5))
final_death()
return
// Staked while "Temp Death" or Asleep
@@ -208,12 +206,13 @@
// Temporary Death? Convert to Torpor.
if(is_in_torpor())
return
- to_chat(owner.current, span_danger("Your immortal body will not yet relinquish your soul to the abyss. You enter Torpor."))
+ to_chat(owner.current, span_userdanger("Your immortal body will not yet relinquish your soul to the abyss. You enter Torpor."))
check_begin_torpor(TRUE)
/datum/antagonist/bloodsucker/proc/HandleStarving() // I am thirsty for blood!
// Nutrition - The amount of blood is how full we are.
- owner.current.set_nutrition(min(bloodsucker_blood_volume, NUTRITION_LEVEL_FED))
+ if(!isoozeling(owner.current))
+ owner.current.set_nutrition(min(bloodsucker_blood_volume, NUTRITION_LEVEL_FED))
// BLOOD_VOLUME_GOOD: [336] - Pale
// handled in bloodsucker_integration.dm
@@ -286,7 +285,7 @@
owner.current.drop_all_held_items()
owner.current.unequip_everything()
user.remove_all_embedded_objects()
- playsound(owner.current, 'sound/effects/tendril_destroyed.ogg', 40, TRUE)
+ playsound(owner.current, 'sound/effects/tendril_destroyed.ogg', vol = 40, vary = TRUE)
if(SEND_SIGNAL(src, BLOODSUCKER_FINAL_DEATH) & DONT_DUST)
return
diff --git a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_misc_procs.dm b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_misc_procs.dm
index 3f7e1f493825..5d50ac2ac8c0 100644
--- a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_misc_procs.dm
+++ b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_misc_procs.dm
@@ -34,7 +34,7 @@
broke_masquerade = TRUE
antag_hud_name = "masquerade_broken"
add_team_hud(owner.current)
- SEND_GLOBAL_SIGNAL(COMSIG_BLOODSUCKER_BROKE_MASQUERADE)
+ SEND_GLOBAL_SIGNAL(COMSIG_BLOODSUCKER_BROKE_MASQUERADE, src)
///This is admin-only of reverting a broken masquerade, sadly it doesn't remove the Malkavian objectives yet.
/datum/antagonist/bloodsucker/proc/fix_masquerade(mob/admin)
diff --git a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_moodlets.dm b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_moodlets.dm
index 13c398700650..bbca337306b4 100644
--- a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_moodlets.dm
+++ b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_moodlets.dm
@@ -33,16 +33,6 @@
mood_change = 10
timeout = 6 MINUTES
-/datum/mood_event/daylight_1
- description = "I slept poorly in a makeshift coffin during the day.\n"
- mood_change = -3
- timeout = 6 MINUTES
-
-/datum/mood_event/daylight_2
- description = "I have been scorched by the unforgiving rays of the sun.\n"
- mood_change = -6
- timeout = 6 MINUTES
-
///Candelabrum's mood event to non Bloodsucker/Vassals
/datum/mood_event/vampcandle
description = "Something is making your mind feel... loose.\n"
diff --git a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_shaded.dm b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_shaded.dm
index ecaaaae89223..8384c6b6c1be 100644
--- a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_shaded.dm
+++ b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_shaded.dm
@@ -5,6 +5,7 @@
show_in_roundend = FALSE
job_rank = ROLE_BLOODSUCKER
antag_hud_name = "bloodsucker"
+ antag_flags = parent_type::antag_flags | FLAG_ANTAG_CAP_IGNORE
/obj/item/soulstone/bloodsucker
theme = THEME_WIZARD
diff --git a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_sol.dm b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_sol.dm
index 252408b50d54..7c9534a64110 100644
--- a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_sol.dm
+++ b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_sol.dm
@@ -34,49 +34,25 @@
/datum/antagonist/bloodsucker/proc/on_sol_end(atom/source)
SIGNAL_HANDLER
check_end_torpor()
- for(var/datum/action/cooldown/bloodsucker/power in powers)
- if(istype(power, /datum/action/cooldown/bloodsucker/gohome))
- RemovePower(power)
+ for(var/datum/action/cooldown/bloodsucker/gohome/power in powers)
+ RemovePower(power)
/// Cycle through all vamp antags and check if they're inside a closet.
/datum/antagonist/bloodsucker/proc/handle_sol()
SIGNAL_HANDLER
- if(!owner || !owner.current)
- return
-
- if(!istype(owner.current.loc, /obj/structure))
- if(COOLDOWN_FINISHED(src, bloodsucker_spam_sol_burn))
- if(bloodsucker_level > 0)
- to_chat(owner, span_userdanger("The solar flare sets your skin ablaze!"))
- else
- to_chat(owner, span_userdanger("The solar flare scalds your neophyte skin!"))
- COOLDOWN_START(src, bloodsucker_spam_sol_burn, BLOODSUCKER_SPAM_SOL) //This should happen twice per Sol
-
- if(owner.current.fire_stacks <= 0)
- owner.current.fire_stacks = 0
- if(bloodsucker_level > 0)
- owner.current.adjust_fire_stacks(0.2 + bloodsucker_level / 10)
- owner.current.ignite_mob()
- owner.current.adjustFireLoss(2 + (bloodsucker_level / 2))
- owner.current.updatehealth()
- owner.current.add_mood_event("vampsleep", /datum/mood_event/daylight_2)
+ if(!owner?.current)
return
- if(istype(owner.current.loc, /obj/structure/closet/crate/coffin)) // Coffins offer the BEST protection
- if(owner.current.am_staked() && COOLDOWN_FINISHED(src, bloodsucker_spam_sol_burn))
- to_chat(owner.current, span_userdanger("You are staked! Remove the offending weapon from your heart before sleeping."))
- COOLDOWN_START(src, bloodsucker_spam_sol_burn, BLOODSUCKER_SPAM_SOL) //This should happen twice per Sol
- if(!is_in_torpor())
- check_begin_torpor(TRUE)
- owner.current.add_mood_event("vampsleep", /datum/mood_event/coffinsleep)
+ if(!istype(owner.current.loc, /obj/structure/closet/crate/coffin))
+ owner.current.apply_status_effect(/datum/status_effect/bloodsucker_sol)
return
-
- if(COOLDOWN_FINISHED(src, bloodsucker_spam_sol_burn)) // Closets offer SOME protection
- to_chat(owner, span_warning("Your skin sizzles. [owner.current.loc] doesn't protect well against UV bombardment."))
+ owner.current.remove_status_effect(/datum/status_effect/bloodsucker_sol)
+ if(owner.current.am_staked() && COOLDOWN_FINISHED(src, bloodsucker_spam_sol_burn))
+ to_chat(owner.current, span_userdanger("You are staked! Remove the offending weapon from your heart before sleeping."))
COOLDOWN_START(src, bloodsucker_spam_sol_burn, BLOODSUCKER_SPAM_SOL) //This should happen twice per Sol
- owner.current.adjustFireLoss(0.5 + (bloodsucker_level / 4))
- owner.current.updatehealth()
- owner.current.add_mood_event("vampsleep", /datum/mood_event/daylight_1)
+ if(!is_in_torpor())
+ check_begin_torpor(TRUE)
+ owner.current.add_mood_event("vampsleep", /datum/mood_event/coffinsleep)
/datum/antagonist/bloodsucker/proc/give_warning(atom/source, danger_level, vampire_warning_message, vassal_warning_message)
SIGNAL_HANDLER
@@ -86,15 +62,15 @@
switch(danger_level)
if(DANGER_LEVEL_FIRST_WARNING)
- owner.current.playsound_local(null, 'monkestation/sound/bloodsuckers/griffin_3.ogg', 50, 1)
+ owner.current.playsound_local(null, 'monkestation/sound/bloodsuckers/griffin_3.ogg', vol = 50, vary = TRUE)
if(DANGER_LEVEL_SECOND_WARNING)
- owner.current.playsound_local(null, 'monkestation/sound/bloodsuckers/griffin_5.ogg', 50, 1)
+ owner.current.playsound_local(null, 'monkestation/sound/bloodsuckers/griffin_5.ogg', vol = 50, vary = TRUE)
if(DANGER_LEVEL_THIRD_WARNING)
- owner.current.playsound_local(null, 'sound/effects/alert.ogg', 75, 1)
+ owner.current.playsound_local(null, 'sound/effects/alert.ogg', vol = 75, vary = TRUE)
if(DANGER_LEVEL_SOL_ROSE)
- owner.current.playsound_local(null, 'sound/ambience/ambimystery.ogg', 75, 1)
+ owner.current.playsound_local(null, 'sound/ambience/ambimystery.ogg', vol = 75, vary = TRUE)
if(DANGER_LEVEL_SOL_ENDED)
- owner.current.playsound_local(null, 'sound/misc/ghosty_wind.ogg', 90, 1)
+ owner.current.playsound_local(null, 'sound/misc/ghosty_wind.ogg', vol = 90, vary = TRUE)
/**
* # Torpor
@@ -122,7 +98,7 @@
var/total_burn = user.getFireLoss_nonProsthetic()
var/total_damage = total_brute + total_burn
/// Checks - Not daylight & Has more than 10 Brute/Burn & not already in Torpor
- if(!SSsunlight.sunlight_active && total_damage >= 10 && !is_in_torpor())
+ if(!SSsunlight.sunlight_active && (total_damage >= 10 || typecached_item_in_list(user.organs, yucky_organ_typecache)) && !is_in_torpor())
torpor_begin()
/datum/antagonist/bloodsucker/proc/check_end_torpor()
@@ -158,10 +134,93 @@
DisableAllPowers()
/datum/antagonist/bloodsucker/proc/torpor_end()
+ owner.current.remove_status_effect(/datum/status_effect/bloodsucker_sol)
owner.current.grab_ghost()
to_chat(owner.current, span_warning("You have recovered from Torpor."))
owner.current.remove_traits(torpor_traits, TORPOR_TRAIT)
if(!HAS_TRAIT(owner.current, TRAIT_MASQUERADE))
ADD_TRAIT(owner.current, TRAIT_SLEEPIMMUNE, BLOODSUCKER_TRAIT)
heal_vampire_organs()
+ owner.current.update_stat()
SEND_SIGNAL(src, BLOODSUCKER_EXIT_TORPOR)
+
+/datum/status_effect/bloodsucker_sol
+ id = "bloodsucker_sol"
+ tick_interval = -1
+ alert_type = /atom/movable/screen/alert/status_effect/bloodsucker_sol
+ var/list/datum/action/cooldown/bloodsucker/burdened_actions
+ var/static/list/sol_traits = list(
+ TRAIT_EASILY_WOUNDED,
+ TRAIT_NO_SPRINT,
+ )
+
+/datum/status_effect/bloodsucker_sol/on_apply()
+ if(!SSsunlight.sunlight_active || istype(owner.loc, /obj/structure/closet/crate/coffin))
+ return FALSE
+ RegisterSignal(SSsunlight, COMSIG_SOL_END, PROC_REF(on_sol_end))
+ RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(on_owner_moved))
+ owner.add_traits(sol_traits, id)
+ owner.remove_filter(id)
+ owner.add_filter(id, 2, drop_shadow_filter(x = 0, y = 0, size = 3, offset = 1.5, color = "#ee7440"))
+ owner.add_movespeed_modifier(/datum/movespeed_modifier/bloodsucker_sol)
+ owner.add_actionspeed_modifier(/datum/actionspeed_modifier/bloodsucker_sol)
+ to_chat(owner, span_userdanger("Sol has risen! Your powers are suppressed, your body is burdened, and you will not heal outside of a coffin!"), type = MESSAGE_TYPE_INFO)
+ if(ishuman(owner))
+ var/mob/living/carbon/human/human_owner = owner
+ human_owner.physiology?.damage_resistance -= 50
+ for(var/datum/action/cooldown/bloodsucker/power in owner.actions)
+ if(power.sol_multiplier)
+ power.bloodcost *= power.sol_multiplier
+ power.constant_bloodcost *= power.sol_multiplier
+ if(power.active)
+ to_chat(owner, span_warning("[power.name] is harder to upkeep during Sol, now requiring [power.constant_bloodcost] blood while the solar flares last!"), type = MESSAGE_TYPE_INFO)
+ LAZYSET(burdened_actions, power, TRUE)
+ power.update_desc(rebuild = FALSE)
+ power.build_all_button_icons(UPDATE_BUTTON_NAME | UPDATE_BUTTON_STATUS)
+ return TRUE
+
+/datum/status_effect/bloodsucker_sol/on_remove()
+ UnregisterSignal(SSsunlight, COMSIG_SOL_END)
+ UnregisterSignal(owner, COMSIG_MOVABLE_MOVED)
+ owner.remove_traits(sol_traits, id)
+ owner.remove_filter(id)
+ owner.remove_movespeed_modifier(/datum/movespeed_modifier/bloodsucker_sol)
+ owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/bloodsucker_sol)
+ if(ishuman(owner))
+ var/mob/living/carbon/human/human_owner = owner
+ human_owner.physiology?.damage_resistance += 50
+ for(var/datum/action/cooldown/bloodsucker/power in owner.actions)
+ if(LAZYACCESS(burdened_actions, power))
+ power.bloodcost /= power.sol_multiplier
+ power.constant_bloodcost /= power.sol_multiplier
+ power.update_desc(rebuild = FALSE)
+ power.build_all_button_icons(UPDATE_BUTTON_NAME | UPDATE_BUTTON_STATUS)
+ LAZYNULL(burdened_actions)
+
+/datum/status_effect/bloodsucker_sol/get_examine_text()
+ return span_warning("[owner.p_They()] seem[owner.p_s()] sickly and painfully overburned!")
+
+/datum/status_effect/bloodsucker_sol/proc/on_sol_end()
+ SIGNAL_HANDLER
+ if(!QDELING(src))
+ to_chat(owner, span_big(span_boldnotice("Sol has ended, your vampiric powers are no longer strained!")), type = MESSAGE_TYPE_INFO)
+ qdel(src)
+
+/datum/status_effect/bloodsucker_sol/proc/on_owner_moved()
+ SIGNAL_HANDLER
+ if(istype(owner.loc, /obj/structure/closet/crate/coffin))
+ qdel(src)
+
+/atom/movable/screen/alert/status_effect/bloodsucker_sol
+ name = "Solar Flares"
+ desc = "Solar flares bombard the station, heavily weakening your vampiric abilities and burdening your body!\nSleep in a coffin to avoid the effects of the solar flare!"
+ icon = 'monkestation/icons/bloodsuckers/actions_bloodsucker.dmi'
+ icon_state = "sol_alert"
+
+/datum/actionspeed_modifier/bloodsucker_sol
+ multiplicative_slowdown = 1
+ id = ACTIONSPEED_ID_BLOODSUCKER_SOL
+
+/datum/movespeed_modifier/bloodsucker_sol
+ multiplicative_slowdown = 0.45
+ id = ACTIONSPEED_ID_BLOODSUCKER_SOL
diff --git a/monkestation/code/modules/bloodsuckers/clans/malkavian.dm b/monkestation/code/modules/bloodsuckers/clans/malkavian.dm
index e2315557f354..8dc81c3c6522 100644
--- a/monkestation/code/modules/bloodsuckers/clans/malkavian.dm
+++ b/monkestation/code/modules/bloodsuckers/clans/malkavian.dm
@@ -55,8 +55,10 @@
INVOKE_ASYNC(stone, TYPE_PROC_REF(/obj/item/soulstone/bloodsucker, capture_soul), bloodsuckerdatum.owner.current, forced = TRUE, bloodsuckerdatum = bloodsuckerdatum)
return DONT_DUST
-/datum/bloodsucker_clan/malkavian/proc/on_bloodsucker_broke_masquerade(datum/antagonist/bloodsucker/masquerade_breaker)
+/datum/bloodsucker_clan/malkavian/proc/on_bloodsucker_broke_masquerade(datum/source, datum/antagonist/bloodsucker/masquerade_breaker)
SIGNAL_HANDLER
+ if(masquerade_breaker == bloodsuckerdatum)
+ return
to_chat(bloodsuckerdatum.owner.current, span_userdanger("[masquerade_breaker.owner.current] has broken the Masquerade! Ensure [masquerade_breaker.owner.current.p_they()] [masquerade_breaker.owner.current.p_are()] eliminated at all costs!"))
var/datum/objective/assassinate/masquerade_objective = new()
masquerade_objective.target = masquerade_breaker.owner.current
diff --git a/monkestation/code/modules/bloodsuckers/powers/_base_power.dm b/monkestation/code/modules/bloodsuckers/powers/_base_power.dm
index 74f9375984e1..0128330427e5 100644
--- a/monkestation/code/modules/bloodsuckers/powers/_base_power.dm
+++ b/monkestation/code/modules/bloodsuckers/powers/_base_power.dm
@@ -38,16 +38,24 @@
var/bloodcost = 0
///The cost to MAINTAIN this Power - Only used for Constant Cost Powers
var/constant_bloodcost = 0
+ /// A multiplier for the bloodcost during sol.
+ var/sol_multiplier
// Modify description to add cost.
/datum/action/cooldown/bloodsucker/New(Target)
. = ..()
+ update_desc()
+
+/datum/action/cooldown/bloodsucker/proc/update_desc(rebuild = TRUE)
+ desc = initial(desc)
if(bloodcost > 0)
desc += "