Merge branch 'master' into dullahan-2-electric-boogaloo

This commit is contained in:
timothyteakettle
2022-08-11 14:43:48 +01:00
138 changed files with 2006 additions and 306 deletions
+5
View File
@@ -223,6 +223,11 @@
/datum/ai_laws/proc/set_laws_config()
var/list/law_ids = CONFIG_GET(keyed_list/random_laws)
if(HAS_TRAIT(SSstation, STATION_TRAIT_UNIQUE_AI))
pick_weighted_lawset()
return
switch(CONFIG_GET(number/default_laws))
if(0)
add_inherent_law("You may not injure a human being or cause one to come to harm.")
+23
View File
@@ -0,0 +1,23 @@
///Data holder for the announcers that can be used in a game, this can be used to have alternative announcements outside of the default e.g.the intern
/datum/centcom_announcer
///Roundshift start audio
var/welcome_sounds = list()
///Sounds made when announcement is receivedc
var/alert_sounds = list()
///Sounds made when command report is received
var/command_report_sounds = list()
///Event audio, can be used for specific event announcements and is assoc key - sound. If no sound is found the default is used.area
var/event_sounds = list()
///Override this to have a custom message to show instead of the normal priority announcement
var/custom_alert_message
/datum/centcom_announcer/proc/get_rand_welcome_sound()
return pick(welcome_sounds)
/datum/centcom_announcer/proc/get_rand_alert_sound()
return pick(alert_sounds)
/datum/centcom_announcer/proc/get_rand_report_sound()
return pick(command_report_sounds)
@@ -0,0 +1,21 @@
/datum/centcom_announcer/default
welcome_sounds = list('sound/announcer/classic/welcome.ogg')
alert_sounds = list('sound/announcer/classic/attention.ogg')
command_report_sounds = list('sound/announcer/classic/commandreport.ogg')
event_sounds = list(ANNOUNCER_AIMALF = 'sound/announcer/classic/aimalf.ogg',
ANNOUNCER_ALIENS = 'sound/announcer/classic/aliens.ogg',
ANNOUNCER_ANIMES = 'sound/announcer/classic/animes.ogg',
ANNOUNCER_GRANOMALIES = 'sound/announcer/classic/granomalies.ogg',
ANNOUNCER_INTERCEPT = 'sound/announcer/classic/intercept.ogg',
ANNOUNCER_IONSTORM = 'sound/announcer/classic/ionstorm.ogg',
ANNOUNCER_METEORS = 'sound/announcer/classic/meteors.ogg',
ANNOUNCER_NEWAI = 'sound/announcer/classic/newai.ogg',
ANNOUNCER_OUTBREAK5 = 'sound/announcer/classic/outbreak5.ogg',
ANNOUNCER_OUTBREAK7 = 'sound/announcer/classic/outbreak7.ogg',
ANNOUNCER_POWEROFF = 'sound/announcer/classic/poweroff.ogg',
ANNOUNCER_POWERON = 'sound/announcer/classic/poweron.ogg',
ANNOUNCER_RADIATION = 'sound/announcer/classic/radiation.ogg',
ANNOUNCER_SHUTTLECALLED = 'sound/announcer/classic/shuttlecalled.ogg',
ANNOUNCER_SHUTTLEDOCK = 'sound/announcer/classic/shuttledock.ogg',
ANNOUNCER_SHUTTLERECALLED = 'sound/announcer/classic/shuttlerecalled.ogg',
ANNOUNCER_SPANOMALIES = 'sound/announcer/classic/spanomalies.ogg')
@@ -0,0 +1,47 @@
/datum/centcom_announcer/intern
welcome_sounds = list('sound/announcer/intern/welcome/1.ogg',
'sound/announcer/intern/welcome/2.ogg',
'sound/announcer/intern/welcome/3.ogg',
'sound/announcer/intern/welcome/4.ogg',
'sound/announcer/intern/welcome/5.ogg',
'sound/announcer/intern/welcome/6.ogg')
alert_sounds = list('sound/announcer/intern/alerts/1.ogg',
'sound/announcer/intern/alerts/2.ogg',
'sound/announcer/intern/alerts/3.ogg',
'sound/announcer/intern/alerts/4.ogg',
'sound/announcer/intern/alerts/5.ogg',
'sound/announcer/intern/alerts/6.ogg',
'sound/announcer/intern/alerts/7.ogg',
'sound/announcer/intern/alerts/8.ogg',
'sound/announcer/intern/alerts/9.ogg',
'sound/announcer/intern/alerts/10.ogg',
'sound/announcer/intern/alerts/11.ogg',
'sound/announcer/intern/alerts/12.ogg',
'sound/announcer/intern/alerts/13.ogg',
'sound/announcer/intern/alerts/14.ogg')
command_report_sounds = list('sound/announcer/intern/commandreport/1.ogg',
'sound/announcer/intern/commandreport/2.ogg',
'sound/announcer/intern/commandreport/3.ogg')
event_sounds = list(ANNOUNCER_AIMALF = 'sound/announcer/classic/aimalf.ogg',
ANNOUNCER_ALIENS = 'sound/announcer/intern/aliens.ogg',
ANNOUNCER_ANIMES = 'sound/announcer/intern/animes.ogg',
ANNOUNCER_GRANOMALIES = 'sound/announcer/intern/granomalies.ogg',
ANNOUNCER_INTERCEPT = 'sound/announcer/intern/intercept.ogg',
ANNOUNCER_IONSTORM = 'sound/announcer/intern/ionstorm.ogg',
ANNOUNCER_METEORS = 'sound/announcer/intern/meteors.ogg',
ANNOUNCER_NEWAI = 'sound/announcer/classic/newai.ogg',
ANNOUNCER_OUTBREAK5 = 'sound/announcer/intern/outbreak5.ogg',
ANNOUNCER_OUTBREAK7 = 'sound/announcer/intern/outbreak7.ogg',
ANNOUNCER_POWEROFF = 'sound/announcer/intern/poweroff.ogg',
ANNOUNCER_POWERON = 'sound/announcer/intern/poweron.ogg',
ANNOUNCER_RADIATION = 'sound/announcer/intern/radiation.ogg',
ANNOUNCER_SHUTTLECALLED = 'sound/announcer/intern/shuttlecalled.ogg',
ANNOUNCER_SHUTTLEDOCK = 'sound/announcer/intern/shuttledock.ogg',
ANNOUNCER_SHUTTLERECALLED = 'sound/announcer/intern/shuttlerecalled.ogg',
ANNOUNCER_SPANOMALIES = 'sound/announcer/intern/spanomalies.ogg')
custom_alert_message = "<br><span class='alert'>Please stand by for an important message from our new intern.</span><br>"
@@ -0,0 +1,22 @@
/datum/centcom_announcer/medbot
welcome_sounds = list('sound/announcer/medibot/welcome.ogg',
'sound/announcer/medibot/newAI.ogg')
alert_sounds = list('sound/announcer/medibot/attention.ogg')
command_report_sounds = list('sound/announcer/medibot/commandreport.ogg')
event_sounds = list(ANNOUNCER_AIMALF = 'sound/announcer/classic/aimalf.ogg',
ANNOUNCER_ALIENS = 'sound/announcer/medibot/aliens.ogg',
ANNOUNCER_ANIMES = 'sound/announcer/medibot/animes.ogg',
ANNOUNCER_GRANOMALIES = 'sound/announcer/medibot/granomalies.ogg',
ANNOUNCER_INTERCEPT = 'sound/announcer/medibot/intercept.ogg',
ANNOUNCER_IONSTORM = 'sound/announcer/medibot/ionstorm.ogg',
ANNOUNCER_METEORS = 'sound/announcer/medibot/meteors.ogg',
ANNOUNCER_NEWAI = 'sound/announcer/medibot/newai.ogg',
ANNOUNCER_OUTBREAK5 = 'sound/announcer/medibot/outbreak5.ogg',
ANNOUNCER_OUTBREAK7 = 'sound/announcer/medibot/outbreak7.ogg',
ANNOUNCER_POWEROFF = 'sound/announcer/medibot/poweroff.ogg',
ANNOUNCER_POWERON = 'sound/announcer/medibot/poweron.ogg',
ANNOUNCER_RADIATION = 'sound/announcer/medibot/radiation.ogg',
ANNOUNCER_SHUTTLECALLED = 'sound/announcer/medibot/shuttlecalled.ogg',
ANNOUNCER_SHUTTLEDOCK = 'sound/announcer/medibot/shuttledocked.ogg',
ANNOUNCER_SHUTTLERECALLED = 'sound/announcer/medibot/shuttlerecalled.ogg',
ANNOUNCER_SPANOMALIES = 'sound/announcer/medibot/spanomalies.ogg')
+3
View File
@@ -24,6 +24,9 @@
var/target_pressure = rand(minimum_pressure, maximum_pressure)
var/pressure_scale = target_pressure / maximum_pressure
if(HAS_TRAIT(SSstation, STATION_TRAIT_UNNATURAL_ATMOSPHERE))
restricted_chance = restricted_chance + 40
// First let's set up the gasmix and base gases for this template
// We make the string from a gasmix in this proc because gases need to calculate their pressure
var/datum/gas_mixture/gasmix = new
+1 -2
View File
@@ -15,7 +15,6 @@
GAS_BZ=0.1,
GAS_BROMINE=0.1
)
restricted_chance = 30
minimum_pressure = HAZARD_LOW_PRESSURE + 10
maximum_pressure = LAVALAND_EQUIPMENT_EFFECT_PRESSURE - 1
@@ -62,7 +61,7 @@
GAS_METHYL_BROMIDE=0.1,
GAS_HYDROGEN=0.1
)
restricted_chance = 10
restricted_chance = 5
minimum_pressure = HAZARD_LOW_PRESSURE + 10
maximum_pressure = LAVALAND_EQUIPMENT_EFFECT_PRESSURE - 1
+47
View File
@@ -0,0 +1,47 @@
/**
* A simple component that spawns a mob of the same type and transfers itself to it when parent dies.
* For more complex behaviors, use the COMSIG_ON_MULTIPLE_LIVES_RESPAWN comsig.
*/
/datum/component/multiple_lives
can_transfer = TRUE
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
/// The number of respawns the living mob has left.
var/lives_left
/datum/component/multiple_lives/Initialize(lives_left)
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
src.lives_left = lives_left
/datum/component/multiple_lives/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOB_DEATH, .proc/respawn)
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/on_examine)
/datum/component/multiple_lives/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_MOB_DEATH, COMSIG_PARENT_EXAMINE))
/datum/component/multiple_lives/proc/respawn(mob/living/source, gibbed)
SIGNAL_HANDLER
if(source.suiciding) //Freed from this mortail coil.
qdel(src)
return
var/mob/living/respawned_mob = new source.type (source.drop_location())
source.mind?.transfer_to(respawned_mob)
lives_left--
if(lives_left <= 0)
qdel(src)
source.TransferComponents(respawned_mob)
SEND_SIGNAL(source, COMSIG_ON_MULTIPLE_LIVES_RESPAWN, respawned_mob, gibbed, lives_left)
/datum/component/multiple_lives/proc/on_examine(mob/living/source, mob/user, list/examine_list)
SIGNAL_HANDLER
if(isobserver(user) || source == user)
examine_list += "[source.p_theyve(TRUE)] [lives_left] extra lives left."
/datum/component/multiple_lives/InheritComponent(datum/component/multiple_lives/new_comp , lives_left)
src.lives_left += new_comp ? new_comp.lives_left : lives_left
/datum/component/multiple_lives/PostTransfer()
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
+5 -3
View File
@@ -82,12 +82,14 @@
var/move_delay = 2
var/last_move = 0
/datum/component/twitch_plays/simple_movement/auto/Initialize(...)
/datum/component/twitch_plays/simple_movement/auto/Initialize(move_delay)
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE
. = ..()
if(. & COMPONENT_INCOMPATIBLE)
return
if(!isnull(move_delay))
src.move_delay = move_delay
START_PROCESSING(SSfastprocess, src)
/datum/component/twitch_plays/simple_movement/auto/Destroy(force, silent)
@@ -95,10 +97,10 @@
return ..()
/datum/component/twitch_plays/simple_movement/auto/process()
if(world.time < (last_move + move_delay))
return
var/dir = fetch_data(null, TRUE)
if(!dir)
return
if(world.time < (last_move + move_delay))
return
last_move = world.time
step(parent, dir)
@@ -0,0 +1,57 @@
///Base class of station traits. These are used to influence rounds in one way or the other by influencing the levers of the station.
/datum/station_trait
///Name of the trait
var/name = "unnamed station trait"
///The type of this trait. Used to classify how this trait influences the station
var/trait_type = STATION_TRAIT_NEUTRAL
///Whether or not this trait uses process()
var/trait_processes = FALSE
///Chance relative to other traits of its type to be picked
var/weight = 10
///Whether this trait is always enabled; generally used for debugging
var/force = FALSE
///Does this trait show in the centcom report?
var/show_in_report = FALSE
///What message to show in the centcom report?
var/report_message
///What code-trait does this station trait give? gives none if null
var/trait_to_give
///What traits are incompatible with this one?
var/blacklist
///Extra flags for station traits such as it being abstract
var/trait_flags
/// Whether or not this trait can be reverted by an admin
var/can_revert = TRUE
/datum/station_trait/New()
. = ..()
RegisterSignal(SSticker, COMSIG_TICKER_ROUND_STARTING, .proc/on_round_start)
if(trait_processes)
START_PROCESSING(SSstation, src)
if(trait_to_give)
ADD_TRAIT(SSstation, trait_to_give, STATION_TRAIT)
/datum/station_trait/Destroy()
SSstation.station_traits -= src
return ..()
/// Proc ran when round starts. Use this for roundstart effects.
/datum/station_trait/proc/on_round_start()
SIGNAL_HANDLER
return
///type of info the centcom report has on this trait, if any.
/datum/station_trait/proc/get_report()
return "[name] - [report_message]"
/// Will attempt to revert the station trait, used by admins.
/datum/station_trait/proc/revert()
if (!can_revert)
CRASH("revert() was called on [type], which can't be reverted!")
if (trait_to_give)
REMOVE_TRAIT(SSstation, trait_to_give, STATION_TRAIT)
qdel(src)
+133
View File
@@ -0,0 +1,133 @@
/// Opens the station traits admin panel
/datum/admins/proc/station_traits_panel()
set name = "Modify Station Traits"
set category = "Admin.Events"
var/static/datum/station_traits_panel/station_traits_panel = new
station_traits_panel.ui_interact(usr)
/datum/station_traits_panel
var/static/list/future_traits
/datum/station_traits_panel/ui_data(mob/user)
var/list/data = list()
data["too_late_to_revert"] = too_late_to_revert()
var/list/current_station_traits = list()
for (var/datum/station_trait/station_trait as anything in SSstation.station_traits)
current_station_traits += list(list(
"name" = station_trait.name,
"can_revert" = station_trait.can_revert,
"ref" = REF(station_trait),
))
data["current_traits"] = current_station_traits
data["future_station_traits"] = future_traits
return data
/datum/station_traits_panel/ui_static_data(mob/user)
var/list/data = list()
var/list/valid_station_traits = list()
for (var/datum/station_trait/station_trait_path as anything in subtypesof(/datum/station_trait))
valid_station_traits += list(list(
"name" = initial(station_trait_path.name),
"path" = station_trait_path,
))
data["valid_station_traits"] = valid_station_traits
return data
/datum/station_traits_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if (.)
return
switch (action)
if ("revert")
var/ref = params["ref"]
if (!ref)
return TRUE
var/datum/station_trait/station_trait = locate(ref)
if (!istype(station_trait))
return TRUE
if (too_late_to_revert())
to_chat(usr, span_warning("It's too late to revert station traits, the round has already started!"))
return TRUE
if (!station_trait.can_revert)
stack_trace("[station_trait.type] can't be reverted, but was requested anyway.")
return TRUE
var/message = "[key_name(usr)] reverted the station trait [station_trait.name] ([station_trait.type])"
log_admin(message)
message_admins(message)
station_trait.revert()
return TRUE
if ("setup_future_traits")
if (too_late_for_future_traits())
to_chat(usr, span_warning("It's too late to add future station traits, the round is already over!"))
return TRUE
var/list/new_future_traits = list()
var/list/station_trait_names = list()
for (var/station_trait_text in params["station_traits"])
var/datum/station_trait/station_trait_path = text2path(station_trait_text)
if (!ispath(station_trait_path, /datum/station_trait) || station_trait_path == /datum/station_trait)
log_admin("[key_name(usr)] tried to set an invalid future station trait: [station_trait_text]")
to_chat(usr, span_warning("Invalid future station trait: [station_trait_text]"))
return TRUE
station_trait_names += initial(station_trait_path.name)
new_future_traits += list(list(
"name" = initial(station_trait_path.name),
"path" = station_trait_path,
))
var/message = "[key_name(usr)] has prepared the following station traits for next round: [station_trait_names.Join(", ") || "None"]"
log_admin(message)
message_admins(message)
future_traits = new_future_traits
rustg_file_write(json_encode(params["station_traits"]), FUTURE_STATION_TRAITS_FILE)
return TRUE
if ("clear_future_traits")
if (!future_traits)
to_chat(usr, span_warning("There are no future station traits."))
return TRUE
var/message = "[key_name(usr)] has cleared the station traits for next round."
log_admin(message)
message_admins(message)
fdel(FUTURE_STATION_TRAITS_FILE)
future_traits = null
return TRUE
/datum/station_traits_panel/proc/too_late_for_future_traits()
return SSticker.current_state >= GAME_STATE_FINISHED
/datum/station_traits_panel/proc/too_late_to_revert()
return SSticker.current_state >= GAME_STATE_PLAYING
/datum/station_traits_panel/ui_status(mob/user, datum/ui_state/state)
return check_rights_for(user.client, R_FUN) ? UI_INTERACTIVE : UI_CLOSE
/datum/station_traits_panel/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "StationTraitsPanel")
ui.open()
@@ -0,0 +1,287 @@
/datum/station_trait/carp_infestation
name = "Carp infestation"
trait_type = STATION_TRAIT_NEGATIVE
weight = 5
show_in_report = TRUE
report_message = "Dangerous fauna is present in the area of this station."
trait_to_give = STATION_TRAIT_CARP_INFESTATION
/datum/station_trait/distant_supply_lines
name = "Distant supply lines"
trait_type = STATION_TRAIT_NEGATIVE
weight = 3
show_in_report = TRUE
report_message = "Due to the distance to our normal supply lines, cargo orders are more expensive."
blacklist = list(/datum/station_trait/strong_supply_lines)
/datum/station_trait/distant_supply_lines/on_round_start()
SSeconomy.pack_price_modifier *= 1.2
/datum/station_trait/late_arrivals
name = "Late Arrivals"
trait_type = STATION_TRAIT_NEGATIVE
weight = 2
show_in_report = TRUE
report_message = "Sorry for that, we didn't expect to fly into that vomiting goose while bringing you to your new station."
trait_to_give = STATION_TRAIT_LATE_ARRIVALS
blacklist = list(/datum/station_trait/random_spawns, /datum/station_trait/hangover)
/datum/station_trait/random_spawns
name = "Drive-by landing"
trait_type = STATION_TRAIT_NEGATIVE
weight = 2
show_in_report = TRUE
report_message = "Sorry for that, we missed your station by a few miles, so we just launched you towards your station in pods. Hope you don't mind!"
trait_to_give = STATION_TRAIT_RANDOM_ARRIVALS
blacklist = list(/datum/station_trait/late_arrivals, /datum/station_trait/hangover)
/datum/station_trait/hangover
name = "Hangover"
trait_type = STATION_TRAIT_NEGATIVE
weight = 0
show_in_report = TRUE
report_message = "Ohh....Man....That mandatory office party from last shift...God that was awesome..I woke up in some random toilet 3 sectors away..."
trait_to_give = STATION_TRAIT_HANGOVER
blacklist = list(/datum/station_trait/late_arrivals, /datum/station_trait/random_spawns)
/datum/station_trait/hangover/New()
. = ..()
RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_LATEJOIN_SPAWN, .proc/on_job_after_spawn)
/datum/station_trait/hangover/revert()
for (var/obj/effect/landmark/start/hangover/hangover_spot in GLOB.start_landmarks_list)
QDEL_LIST(hangover_spot.debris)
return ..()
/datum/station_trait/hangover/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/spawned_mob)
SIGNAL_HANDLER
if(prob(65)) // most aren't hungover
return
if(!iscarbon(spawned_mob)) // don't want silicons or similar to be counted here
return
if(HAS_TRAIT(spawned_mob, TRAIT_ROBOTIC_ORGANISM)) // robots can't get hungover
return
if(HAS_TRAIT(spawned_mob, TRAIT_TOXIC_ALCOHOL)) // people with alcohol intolerance also can't
return
var/obj/item/hat = pick(
/obj/item/clothing/head/sombrero,
/obj/item/clothing/head/fedora,
/obj/item/clothing/mask/balaclava,
/obj/item/clothing/head/ushanka,
/obj/item/clothing/head/cardborg,
/obj/item/clothing/head/pirate,
/obj/item/clothing/head/cone,
)
hat = new hat(spawned_mob)
spawned_mob.equip_to_slot_or_del(hat, ITEM_SLOT_HEAD)
/datum/station_trait/blackout
name = "Blackout"
trait_type = STATION_TRAIT_NEGATIVE
weight = 3
show_in_report = TRUE
report_message = "Station lights seem to be damaged, be safe when starting your shift today."
/datum/station_trait/blackout/on_round_start()
. = ..()
for(var/obj/machinery/power/apc/apc as anything in GLOB.apcs_list)
if(is_station_level(apc.z) && prob(60))
apc.overload_lighting()
/datum/station_trait/empty_maint
name = "Cleaned out maintenance"
trait_type = STATION_TRAIT_NEGATIVE
weight = 5
show_in_report = TRUE
report_message = "Our workers cleaned out most of the junk in the maintenace areas."
blacklist = list(/datum/station_trait/filled_maint)
trait_to_give = STATION_TRAIT_EMPTY_MAINT
// This station trait is checked when loot drops initialize, so it's too late
can_revert = FALSE
/datum/station_trait/overflow_job_bureaucracy
name = "Overflow bureaucracy mistake"
trait_type = STATION_TRAIT_NEGATIVE
weight = 4
show_in_report = TRUE
var/chosen_job_name
/datum/station_trait/overflow_job_bureaucracy/New()
. = ..()
RegisterSignal(SSjob, COMSIG_SUBSYSTEM_POST_INITIALIZE, .proc/set_overflow_job_override)
/datum/station_trait/overflow_job_bureaucracy/get_report()
return "[name] - It seems for some reason we put out the wrong job-listing for the overflow role this shift...I hope you like [chosen_job_name]s."
/datum/station_trait/overflow_job_bureaucracy/proc/set_overflow_job_override(datum/source)
SIGNAL_HANDLER
var/datum/job/picked_job = pick(get_all_jobs())
chosen_job_name = lowertext(picked_job.title) // like Chief Engineers vs like chief engineers
SSjob.set_overflow_role(picked_job.type)
/datum/station_trait/slow_shuttle
name = "Slow Shuttle"
trait_type = STATION_TRAIT_NEGATIVE
weight = 5
show_in_report = TRUE
report_message = "Due to distance to our supply station, the cargo shuttle will have a slower flight time to your cargo department."
blacklist = list(/datum/station_trait/quick_shuttle)
/datum/station_trait/slow_shuttle/on_round_start()
. = ..()
SSshuttle.supply.callTime *= 1.5
/datum/station_trait/bot_languages
name = "Bot Language Matrix Malfunction"
trait_type = STATION_TRAIT_NEGATIVE
weight = 3
show_in_report = TRUE
report_message = "Your station's friendly bots have had their language matrix fried due to an event, resulting in some strange and unfamiliar speech patterns."
/datum/station_trait/bot_languages/New()
. = ..()
/// What "caused" our robots to go haywire (fluff)
var/event_source = pick(list("an ion storm", "a syndicate hacking attempt", "a malfunction", "issues with your onboard AI", "an intern's mistakes", "budget cuts"))
report_message = "Your station's friendly bots have had their language matrix fried due to [event_source], resulting in some strange and unfamiliar speech patterns."
/datum/station_trait/bot_languages/on_round_start()
. = ..()
//All bots that exist round start have their set language randomized.
for(var/mob/living/simple_animal/bot/found_bot in GLOB.alive_mob_list)
/// The bot's language holder - so we can randomize and change their language
var/datum/language_holder/bot_languages = found_bot.get_language_holder()
bot_languages.selected_language = bot_languages.get_random_spoken_language()
/datum/station_trait/revenge_of_pun_pun
name = "Revenge of Pun Pun"
trait_type = STATION_TRAIT_NEGATIVE
weight = 2
// Way too much is done on atoms SS to be reverted, and it'd look
// kinda clunky on round start. It's not impossible to make this work,
// but it's a project for...someone else.
can_revert = FALSE
var/static/list/weapon_types
/datum/station_trait/revenge_of_pun_pun/New()
if(!weapon_types)
weapon_types = list(
/obj/item/chair = 25,
/obj/item/tailclub = 15,
/obj/item/melee/baseball_bat = 10,
/obj/item/melee/chainofcommand/tailwhip = 15,
/obj/item/melee/chainofcommand/tailwhip/kitty = 15,
/obj/item/reagent_containers/food/drinks/bottle = 25,
/obj/item/gun/ballistic/automatic/pistol = 1,
)
RegisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE, .proc/arm_monke)
/datum/station_trait/revenge_of_pun_pun/proc/arm_monke()
SIGNAL_HANDLER
var/mob/living/carbon/monkey/punpun = locate()
if(!punpun)
return
var/weapon_type = pickweight(weapon_types)
var/obj/item/weapon = new weapon_type
if(!punpun.put_in_l_hand(weapon) && !punpun.put_in_r_hand(weapon))
// Guess they did all this with whatever they have in their hands already
qdel(weapon)
weapon = punpun.get_active_held_item() || punpun.get_inactive_held_item()
weapon?.add_mob_blood(punpun)
punpun.add_mob_blood(punpun)
punpun.aggressive = TRUE
var/area/place = get_area(punpun)
var/list/area_open_turfs = list()
for(var/turf/location in place)
if(location.density)
continue
area_open_turfs += location
punpun.forceMove(pick(area_open_turfs))
for(var/i in 1 to rand(10, 40))
new /obj/effect/decal/cleanable/blood(pick(area_open_turfs))
var/list/blood_path = list()
for(var/i in 1 to 10) // Only 10 attempts
var/turf/destination = pick(area_open_turfs)
var/turf/next_step = get_step_to(punpun, destination)
for(var/k in 1 to 30) // Max 30 steps
if(!next_step)
break
blood_path += next_step
next_step = get_step_to(next_step, destination)
if(length(blood_path))
break
if(!length(blood_path))
CRASH("Unable to make a path from punpun")
var/turf/last_location
for(var/turf/location as anything in blood_path)
last_location = location
if(prob(80))
new /obj/effect/decal/cleanable/blood(location)
if(prob(50))
var/static/blood_types = list(
/obj/effect/decal/cleanable/blood/splatter,
/obj/effect/decal/cleanable/blood/gibs,
)
var/blood_type = pick(blood_types)
new blood_type(get_turf(pick(orange(location, 2))))
new /obj/effect/decal/cleanable/blood/gibs/torso(last_location)
// Abstract station trait used for traits that modify a random event in some way (their weight or max occurrences).
/datum/station_trait/random_event_weight_modifier
name = "Random Event Modifier"
report_message = "A random event has been modified this shift! Someone forgot to set this!"
show_in_report = TRUE
trait_flags = STATION_TRAIT_ABSTRACT
weight = 0
/// The path to the round_event_control that we modify.
var/event_control_path
/// Multiplier applied to the weight of the event.
var/weight_multiplier = 1
/// Flat modifier added to the amount of max occurances the random event can have.
var/max_occurrences_modifier = 0
/datum/station_trait/random_event_weight_modifier/on_round_start()
. = ..()
var/datum/round_event_control/modified_event = locate(event_control_path) in SSevents.control
if(!modified_event)
CRASH("[type] could not find a round event controller to modify on round start (likely has an invalid event_control_path set)!")
modified_event.weight *= weight_multiplier
modified_event.max_occurrences += max_occurrences_modifier
/datum/station_trait/random_event_weight_modifier/ion_storms
name = "Ionic Stormfront"
report_message = "An ionic stormfront is passing over your station's system. Expect an increased likelihood of ion storms afflicting your station's silicon units."
trait_type = STATION_TRAIT_NEGATIVE
trait_flags = NONE
weight = 3
event_control_path = /datum/round_event_control/ion_storm
weight_multiplier = 2
/datum/station_trait/random_event_weight_modifier/rad_storms
name = "Radiation Stormfront"
report_message = "A radioactive stormfront is passing through your station's system. Expect an increased likelihood of radiation storms passing over your station, as well the potential for multiple radiation storms to occur during your shift."
trait_type = STATION_TRAIT_NEGATIVE
trait_flags = NONE
weight = 2
event_control_path = /datum/round_event_control/radiation_storm
weight_multiplier = 1.5
max_occurrences_modifier = 2
@@ -0,0 +1,116 @@
/datum/station_trait/bananium_shipment
name = "Bananium Shipment"
trait_type = STATION_TRAIT_NEUTRAL
weight = 5
report_message = "Rumors has it that the clown planet has been sending support packages to clowns in this system"
trait_to_give = STATION_TRAIT_BANANIUM_SHIPMENTS
/datum/station_trait/unnatural_atmosphere
name = "Unnatural atmospherical properties"
trait_type = STATION_TRAIT_NEUTRAL
weight = 5
show_in_report = TRUE
report_message = "System's local planet has irregular atmospherical properties"
trait_to_give = STATION_TRAIT_UNNATURAL_ATMOSPHERE
// This station trait modifies the atmosphere, which is too far past the time admins are able to revert it
can_revert = FALSE
/datum/station_trait/unique_ai
name = "Unique AI"
trait_type = STATION_TRAIT_NEUTRAL
weight = 5
show_in_report = TRUE
report_message = "For experimental purposes, this station AI might show divergence from default lawset. Do not meddle with this experiment."
trait_to_give = STATION_TRAIT_UNIQUE_AI
/datum/station_trait/ian_adventure
name = "Ian's Adventure"
trait_type = STATION_TRAIT_NEUTRAL
weight = 5
show_in_report = FALSE
report_message = "Ian has gone exploring somewhere in the station."
/datum/station_trait/ian_adventure/on_round_start()
for(var/mob/living/simple_animal/pet/dog/corgi/dog in GLOB.mob_list)
if(!(istype(dog, /mob/living/simple_animal/pet/dog/corgi/Ian) || istype(dog, /mob/living/simple_animal/pet/dog/corgi/puppy)))
continue
// Makes this station trait more interesting. Ian probably won't go anywhere without a little external help.
// Also gives him a couple extra lives to survive eventual tiders.
dog.AddComponent(/datum/component/twitch_plays/simple_movement/auto, 3 SECONDS)
dog.AddComponent(/datum/component/multiple_lives, 2)
RegisterSignal(dog, COMSIG_ON_MULTIPLE_LIVES_RESPAWN, .proc/do_corgi_respawn)
// The extended safety checks at time of writing are about chasms and lava
// if there are any chasms and lava on stations in the future, woah
var/turf/current_turf = get_turf(dog)
var/turf/adventure_turf = find_safe_turf(extended_safety_checks = TRUE, dense_atoms = FALSE)
// Poof!
do_smoke(location=current_turf)
dog.forceMove(adventure_turf)
do_smoke(location=adventure_turf)
/// Moves the new dog somewhere safe, equips it with the old one's inventory and makes it deadchat_playable.
/datum/station_trait/ian_adventure/proc/do_corgi_respawn(mob/living/simple_animal/pet/dog/corgi/old_dog, mob/living/simple_animal/pet/dog/corgi/new_dog, gibbed, lives_left)
SIGNAL_HANDLER
var/turf/current_turf = get_turf(new_dog)
var/turf/adventure_turf = find_safe_turf(extended_safety_checks = TRUE, dense_atoms = FALSE)
do_smoke(location=current_turf)
new_dog.forceMove(adventure_turf)
do_smoke(location=adventure_turf)
if(old_dog.inventory_back)
var/obj/item/old_dog_back = old_dog.inventory_back
old_dog.inventory_back = null
old_dog_back.forceMove(new_dog)
new_dog.inventory_back = old_dog_back
if(old_dog.inventory_head)
var/obj/item/old_dog_hat = old_dog.inventory_head
old_dog.inventory_head = null
new_dog.place_on_head(old_dog_hat)
new_dog.update_corgi_fluff()
new_dog.regenerate_icons()
new_dog.AddComponent(/datum/component/twitch_plays/simple_movement/auto, 3 SECONDS)
if(lives_left)
RegisterSignal(new_dog, COMSIG_ON_MULTIPLE_LIVES_RESPAWN, .proc/do_corgi_respawn)
if(!gibbed) //The old dog will now disappear so we won't have more than one Ian at a time.
qdel(old_dog)
/datum/station_trait/glitched_pdas
name = "PDA glitch"
trait_type = STATION_TRAIT_NEUTRAL
weight = 15
show_in_report = TRUE
report_message = "Something seems to be wrong with the PDAs issued to you all this shift. Nothing too bad though."
trait_to_give = STATION_TRAIT_PDA_GLITCHED
/datum/station_trait/announcement_intern
name = "Announcement Intern"
trait_type = STATION_TRAIT_NEUTRAL
weight = 1
show_in_report = TRUE
report_message = "Please be nice to him."
blacklist = list(/datum/station_trait/announcement_medbot)
/datum/station_trait/announcement_intern/New()
. = ..()
SSstation.announcer = /datum/centcom_announcer/intern
/datum/station_trait/announcement_medbot
name = "Announcement \"System\""
trait_type = STATION_TRAIT_NEUTRAL
weight = 1
show_in_report = TRUE
report_message = "Our announcement system is under scheduled maintanance at the moment. Thankfully, we have a backup."
blacklist = list(/datum/station_trait/announcement_intern)
/datum/station_trait/announcement_medbot/New()
. = ..()
SSstation.announcer = /datum/centcom_announcer/medbot
@@ -0,0 +1,279 @@
#define PARTY_COOLDOWN_LENGTH_MIN 6 MINUTES
#define PARTY_COOLDOWN_LENGTH_MAX 12 MINUTES
/datum/station_trait/lucky_winner
name = "Lucky winner"
trait_type = STATION_TRAIT_POSITIVE
weight = 1
show_in_report = TRUE
report_message = "Your station has won the grand prize of the annual station charity event. Free snacks will be delivered to the bar every now and then."
trait_processes = TRUE
COOLDOWN_DECLARE(party_cooldown)
/datum/station_trait/lucky_winner/on_round_start()
. = ..()
COOLDOWN_START(src, party_cooldown, rand(PARTY_COOLDOWN_LENGTH_MIN, PARTY_COOLDOWN_LENGTH_MAX))
/datum/station_trait/lucky_winner/process(delta_time)
if(!COOLDOWN_FINISHED(src, party_cooldown))
return
COOLDOWN_START(src, party_cooldown, rand(PARTY_COOLDOWN_LENGTH_MIN, PARTY_COOLDOWN_LENGTH_MAX))
var/area/area_to_spawn_in = pick(GLOB.bar_areas)
var/turf/T = pick(area_to_spawn_in.contents)
var/obj/structure/closet/supplypod/centcompod/toLaunch = new()
var/obj/item/pizzabox/pizza_to_spawn = pick(list(/obj/item/pizzabox/margherita, /obj/item/pizzabox/mushroom, /obj/item/pizzabox/meat, /obj/item/pizzabox/vegetable, /obj/item/pizzabox/pineapple))
new pizza_to_spawn(toLaunch)
for(var/i in 1 to 6)
if(prob(50))
new /obj/item/reagent_containers/food/drinks/beer(toLaunch)
else
for(var/m in 1 to 2)
var/obj/item/soda_to_spawn = pick(list(/obj/item/reagent_containers/food/drinks/soda_cans/sol_dry,
/obj/item/reagent_containers/food/drinks/soda_cans/space_up,
/obj/item/reagent_containers/food/drinks/soda_cans/starkist,
/obj/item/reagent_containers/food/drinks/soda_cans/cola,
/obj/item/reagent_containers/food/drinks/soda_cans/space_mountain_wind,
/obj/item/reagent_containers/food/drinks/soda_cans/dr_gibb,
/obj/item/reagent_containers/food/drinks/soda_cans/pwr_game))
new soda_to_spawn(toLaunch)
new /obj/effect/pod_landingzone(T, toLaunch)
/datum/station_trait/galactic_grant
name = "Galactic grant"
trait_type = STATION_TRAIT_POSITIVE
weight = 6
show_in_report = TRUE
report_message = "Your station has been selected for a special grant. Some extra funds has been made available to your cargo department."
/datum/station_trait/galactic_grant/on_round_start()
var/datum/bank_account/cargo_bank = SSeconomy.get_dep_account(ACCOUNT_CAR)
cargo_bank.adjust_money(rand(2000, 5000))
/datum/station_trait/premium_internals_box
name = "Premium internals boxes"
trait_type = STATION_TRAIT_POSITIVE
weight = 10
show_in_report = TRUE
report_message = "The internals boxes for your crew have been filled with bonus equipment."
trait_to_give = STATION_TRAIT_PREMIUM_INTERNALS
/datum/station_trait/bountiful_bounties
name = "Bountiful bounties"
trait_type = STATION_TRAIT_POSITIVE
weight = 5
show_in_report = TRUE
report_message = "It seems collectors in this system are extra keen to on bounties, and will pay more to see their completion."
/datum/station_trait/bountiful_bounties/on_round_start()
SSeconomy.bounty_modifier *= 1.2
/datum/station_trait/strong_supply_lines
name = "Strong supply lines"
trait_type = STATION_TRAIT_POSITIVE
weight = 5
show_in_report = TRUE
report_message = "Prices are low in this system, BUY BUY BUY!"
blacklist = list(/datum/station_trait/distant_supply_lines)
/datum/station_trait/strong_supply_lines/on_round_start()
SSeconomy.pack_price_modifier *= 0.8
/datum/station_trait/scarves
name = "Scarves"
trait_type = STATION_TRAIT_POSITIVE
weight = 5
show_in_report = TRUE
var/list/scarves
/datum/station_trait/scarves/New()
. = ..()
report_message = pick(
"Nanotrasen is experimenting with seeing if neck warmth improves employee morale.",
"After Space Fashion Week, scarves are the hot new accessory.",
"Everyone was simultaneously a little bit cold when they packed to go to the station.",
"The station is definitely not under attack by neck grappling aliens masquerading as wool. Definitely not.",
"You all get free scarves. Don't ask why.",
"A shipment of scarves was delivered to the station.",
)
scarves = typesof(/obj/item/clothing/neck/scarf) + list(
/obj/item/clothing/neck/stripedredscarf,
/obj/item/clothing/neck/stripedgreenscarf,
/obj/item/clothing/neck/stripedbluescarf,
)
scarves -= /obj/item/clothing/neck/scarf/zomb // donator snowflake code--mayhaps we should make a glob for this or similar
RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_SPAWN, .proc/on_job_after_spawn)
/datum/station_trait/scarves/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/spawned, client/player_client)
SIGNAL_HANDLER
var/scarf_type = pick(scarves)
spawned.equip_to_slot_or_del(new scarf_type(spawned), ITEM_SLOT_NECK)
/datum/station_trait/filled_maint
name = "Filled up maintenance"
trait_type = STATION_TRAIT_POSITIVE
weight = 5
show_in_report = TRUE
report_message = "Our workers accidentaly forgot more of their personal belongings in the maintenace areas."
blacklist = list(/datum/station_trait/empty_maint)
trait_to_give = STATION_TRAIT_FILLED_MAINT
// This station trait is checked when loot drops initialize, so it's too late
can_revert = FALSE
/datum/station_trait/quick_shuttle
name = "Quick Shuttle"
trait_type = STATION_TRAIT_POSITIVE
weight = 6
show_in_report = TRUE
report_message = "Due to proximity to our supply station, the cargo shuttle will have a quicker flight time to your cargo department."
blacklist = list(/datum/station_trait/slow_shuttle)
/datum/station_trait/quick_shuttle/on_round_start()
. = ..()
SSshuttle.supply.callTime *= 0.5
/datum/station_trait/deathrattle_department
name = "deathrattled department"
trait_type = STATION_TRAIT_POSITIVE
show_in_report = TRUE
trait_flags = STATION_TRAIT_ABSTRACT
blacklist = list(/datum/station_trait/deathrattle_all)
var/department_head
var/department_name = "department"
var/datum/deathrattle_group/deathrattle_group
/datum/station_trait/deathrattle_department/New()
. = ..()
deathrattle_group = new("[department_name] group")
blacklist += subtypesof(/datum/station_trait/deathrattle_department) - type //All but ourselves
report_message = "All members of [department_name] have received an implant to notify each other if one of them dies. This should help improve job-safety!"
RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_SPAWN, .proc/on_job_after_spawn)
/datum/station_trait/deathrattle_department/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/spawned, client/player_client)
SIGNAL_HANDLER
if(department_head != job.title && !(src.department_head in job.department_head))
return
var/obj/item/implant/deathrattle/implant_to_give = new()
deathrattle_group.register(implant_to_give)
implant_to_give.implant(spawned, spawned, TRUE, TRUE)
/datum/station_trait/deathrattle_department/service
name = "Deathrattled Service"
trait_flags = NONE
weight = 1
department_head = "Head of Personnel"
department_name = "Service"
/datum/station_trait/deathrattle_department/cargo
name = "Deathrattled Cargo"
trait_flags = NONE
weight = 2
department_head = "Quartermaster"
department_name = "Cargo"
/datum/station_trait/deathrattle_department/engineering
name = "Deathrattled Engineering"
trait_flags = NONE
weight = 2
department_head = "Chief Engineer"
department_name = "Engineering"
/datum/station_trait/deathrattle_department/command
name = "Deathrattled Command"
trait_flags = NONE
weight = 1
department_head = "Captain"
department_name = "Command"
/datum/station_trait/deathrattle_department/science
name = "Deathrattled Science"
trait_flags = NONE
weight = 1
department_head = "Research Director"
department_name = "Science"
/datum/station_trait/deathrattle_department/security
name = "Deathrattled Security"
trait_flags = NONE
weight = 1
department_head = "Head of Security"
department_name = "Security"
/datum/station_trait/deathrattle_department/medical
name = "Deathrattled Medical"
trait_flags = NONE
weight = 1
department_head = "Chief Medical Officer"
department_name = "Medical"
/datum/station_trait/deathrattle_all
name = "Deathrattled Station"
trait_type = STATION_TRAIT_POSITIVE
show_in_report = TRUE
weight = 1
report_message = "All members of the station have received an implant to notify each other if one of them dies. This should help improve job-safety!"
var/datum/deathrattle_group/deathrattle_group
/datum/station_trait/deathrattle_all/New()
. = ..()
deathrattle_group = new("station group")
blacklist = subtypesof(/datum/station_trait/deathrattle_department)
RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_SPAWN, .proc/on_job_after_spawn)
/datum/station_trait/deathrattle_all/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/spawned, client/player_client)
SIGNAL_HANDLER
var/obj/item/implant/deathrattle/implant_to_give = new()
deathrattle_group.register(implant_to_give)
implant_to_give.implant(spawned, spawned, TRUE, TRUE)
/datum/station_trait/wallets
name = "Wallets!"
trait_type = STATION_TRAIT_POSITIVE
show_in_report = TRUE
weight = 10
report_message = "It has become temporarily fashionable to use a wallet, so everyone on the station has been issued one."
/datum/station_trait/wallets/New()
. = ..()
RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_SPAWN, .proc/on_job_after_spawn)
/datum/station_trait/wallets/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/living_mob, mob/M, joined_late)
SIGNAL_HANDLER
var/obj/item/card/id/id_card = living_mob.get_item_by_slot(ITEM_SLOT_ID)
if(!istype(id_card))
return
living_mob.temporarilyRemoveItemFromInventory(id_card, force=TRUE)
// "Doc, what's wrong with me?"
var/obj/item/storage/wallet/wallet = new(src)
// "You've got a wallet embedded in your chest."
wallet.add_fingerprint(living_mob, ignoregloves = TRUE)
living_mob.equip_to_slot_if_possible(wallet, ITEM_SLOT_ID)
id_card.forceMove(wallet)
// Put our filthy fingerprints all over the contents
for(var/obj/item/item in wallet)
item.add_fingerprint(living_mob, ignoregloves = TRUE)