This commit is contained in:
Ghommie
2020-01-04 13:35:05 +01:00
489 changed files with 24949 additions and 14497 deletions
+11 -3
View File
@@ -326,8 +326,13 @@ GLOBAL_LIST_EMPTY(teleportlocs)
F.update_fire_light(fire)
for(var/obj/machinery/light/L in src)
L.update()
/area/proc/update_icon()
/**
* Update the icon state of the area
*
* Im not sure what the heck this does, somethign to do with weather being able to set icon
* states on areas?? where the heck would that even display?
*/
/area/update_icon_state()
var/weather_icon
for(var/V in SSweather.processing)
var/datum/weather/W = V
@@ -337,7 +342,10 @@ GLOBAL_LIST_EMPTY(teleportlocs)
if(!weather_icon)
icon_state = null
/area/space/update_icon()
/**
* Update the icon of the area (overridden to always be null for space
*/
/area/space/update_icon_state()
icon_state = null
/*
+30 -2
View File
@@ -24,6 +24,8 @@
var/list/add_overlays // a very temporary list of overlays to add
var/list/managed_vis_overlays //vis overlays managed by SSvis_overlays to automaticaly turn them like other overlays
///overlays managed by update_overlays() to prevent removing overlays that weren't added by the same proc
var/list/managed_overlays
var/datum/proximity_monitor/proximity_monitor
var/buckle_message_cooldown = 0
@@ -45,7 +47,7 @@
/atom/New(loc, ...)
//atom creation method that preloads variables at creation
if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New()
GLOB._preloader.load(src)
world.preloader_load(src)
if(datum_flags & DF_USE_TAG)
GenerateTag()
@@ -164,7 +166,7 @@
return FALSE
/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0)
/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user)
if(does_attack_animation)
user.changeNext_move(CLICK_CD_MELEE)
@@ -310,6 +312,32 @@
SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .)
/// Updates the icon of the atom
/atom/proc/update_icon()
// I expect we're going to need more return flags and options in this proc
var/signalOut = SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_ICON)
if(!(signalOut & COMSIG_ATOM_NO_UPDATE_ICON_STATE))
update_icon_state()
if(!(signalOut & COMSIG_ATOM_NO_UPDATE_OVERLAYS))
var/list/new_overlays = update_overlays()
if(managed_overlays)
cut_overlay(managed_overlays)
managed_overlays = null
if(length(new_overlays))
managed_overlays = new_overlays
add_overlay(new_overlays)
/// Updates the icon state of the atom
/atom/proc/update_icon_state()
/// Updates the overlays of the atom
/atom/proc/update_overlays()
SHOULD_CALL_PARENT(1)
. = list()
SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_OVERLAYS, .)
/atom/proc/relaymove(mob/user)
if(buckle_message_cooldown <= world.time)
buckle_message_cooldown = world.time + 50
+3 -3
View File
@@ -519,12 +519,12 @@
step(src, AM.dir)
..()
/atom/movable/proc/safe_throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY)
/atom/movable/proc/safe_throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY, messy_throw = TRUE)
if((force < (move_resist * MOVE_FORCE_THROW_RATIO)) || (move_resist == INFINITY))
return
return throw_at(target, range, speed, thrower, spin, diagonals_first, callback, force)
return throw_at(target, range, speed, thrower, spin, diagonals_first, callback, force, messy_throw)
/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY) //If this returns FALSE then callback will not be called.
/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY, messy_throw = TRUE) //If this returns FALSE then callback will not be called.
. = FALSE
if (!target || speed <= 0)
return
@@ -57,7 +57,6 @@
..()
if(visualsOnly)
return
H.dna.add_mutation(CLOWNMUT)
H.dna.add_mutation(SMILE)
/datum/outfit/syndicate/clownop/leader
+30 -109
View File
@@ -1,14 +1,3 @@
#define CURRENT_LIVING_PLAYERS 1
#define CURRENT_LIVING_ANTAGS 2
#define CURRENT_DEAD_PLAYERS 3
#define CURRENT_OBSERVERS 4
#define ONLY_RULESET 1
#define HIGHLANDER_RULESET 2
#define TRAITOR_RULESET 4
#define MINOR_RULESET 8
#define RULESET_STOP_PROCESSING 1
// -- Injection delays
GLOBAL_VAR_INIT(dynamic_latejoin_delay_min, (10 MINUTES))
@@ -52,6 +41,8 @@ GLOBAL_LIST_EMPTY(dynamic_forced_roundstart_ruleset)
// Forced threat level, setting this to zero or higher forces the roundstart threat to the value.
GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
/datum/game_mode/dynamic
name = "dynamic mode"
config_tag = "dynamic"
@@ -60,7 +51,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
announce_text = "Dynamic mode!" // This needs to be changed maybe
reroll_friendly = FALSE;
// Current storyteller
var/datum/dynamic_storyteller/storyteller = null
// Threat logging vars
/// The "threat cap", threat shouldn't normally go above this and is used in ruleset calculations
var/threat_level = 0
@@ -164,6 +156,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
dat += "Threat to Spend: <b>[threat]</b> <a href='?src=\ref[src];[HrefToken()];adjustthreat=1'>\[Adjust\]</A> <a href='?src=\ref[src];[HrefToken()];threatlog=1'>\[View Log\]</a><br/>"
dat += "<br/>"
dat += "Storyteller: <b>[storyteller.name]</b><br/>"
dat += "Parameters: centre = [GLOB.dynamic_curve_centre] ; width = [GLOB.dynamic_curve_width].<br/>"
dat += "<i>On average, <b>[peaceful_percentage]</b>% of the rounds are more peaceful.</i><br/>"
dat += "Forced extended: <a href='?src=\ref[src];[HrefToken()];forced_extended=1'><b>[GLOB.dynamic_forced_extended ? "On" : "Off"]</b></a><br/>"
@@ -178,7 +171,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
dat += "[DR.ruletype] - <b>[DR.name]</b><br>"
else
dat += "none.<br>"
dat += "<br>Injection Timers: (<b>[get_injection_chance(TRUE)]%</b> chance)<BR>"
dat += "<br>Injection Timers: (<b>[storyteller.get_injection_chance(TRUE)]%</b> chance)<BR>"
dat += "Latejoin: [(latejoin_injection_cooldown-world.time)>60*10 ? "[round((latejoin_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(latejoin_injection_cooldown-world.time)] seconds"] <a href='?src=\ref[src];[HrefToken()];injectlate=1'>\[Now!\]</a><BR>"
dat += "Midround: [(midround_injection_cooldown-world.time)>60*10 ? "[round((midround_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(midround_injection_cooldown-world.time)] seconds"] <a href='?src=\ref[src];[HrefToken()];injectmid=1'>\[Now!\]</a><BR>"
dat += "Event: [(event_injection_cooldown-world.time)>60*10 ? "[round((event_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(event_injection_cooldown-world.time)] seconds"] <a href='?src=\ref[src];[HrefToken()];forceevent=1'>\[Now!\]</a><BR>"
@@ -336,6 +329,9 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
SSblackbox.record_feedback("tally","dynamic_threat",peaceful_percentage,"Percent of same-vote rounds that are more peaceful")
/datum/game_mode/dynamic/can_start()
storyteller = new GLOB.dynamic_storyteller_type // this is where all the initialization happens
storyteller.on_start()
SSblackbox.record_feedback("text","dynamic_storyteller",1,storyteller.name)
message_admins("Dynamic mode parameters for the round:")
message_admins("Centre is [GLOB.dynamic_curve_centre], Width is [GLOB.dynamic_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].")
message_admins("Stacking limit is [GLOB.dynamic_stacking_limit], Classic secret is [GLOB.dynamic_classic_secret ? "Enabled" : "Disabled"], High population limit is [GLOB.dynamic_high_pop_limit].")
@@ -345,19 +341,12 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
if(GLOB.dynamic_forced_threat_level >= 0)
threat_level = round(GLOB.dynamic_forced_threat_level, 0.1)
threat = threat_level
SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Threat level (forced by admins)")
SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Threat level (forced)")
else
generate_threat()
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_latejoin_delay_max + GLOB.dynamic_first_latejoin_delay_min)
latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_first_latejoin_delay_min, GLOB.dynamic_first_latejoin_delay_max)) + world.time
storyteller.start_injection_cooldowns()
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_midround_delay_min + GLOB.dynamic_first_midround_delay_max)
midround_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_first_midround_delay_min, GLOB.dynamic_first_midround_delay_max)) + world.time
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time)
log_game("DYNAMIC: Dynamic Mode initialized with a Threat Level of... [threat_level]!")
initial_threat_level = threat_level
return TRUE
@@ -391,7 +380,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
log_game("DYNAMIC: [roundstart_rules.len] rules.")
return TRUE
SSblackbox.record_feedback("tally","dynamic",roundstart_rules.len,"Roundstart rules considered")
SSblackbox.record_feedback("tally","dynamic",roundstart_rules.len,"Players readied up")
SSblackbox.record_feedback("tally","dynamic",roundstart_pop_ready,"Players readied up")
if(GLOB.dynamic_forced_roundstart_ruleset.len > 0)
rigged_roundstart()
else
@@ -429,13 +418,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
if (GLOB.dynamic_forced_extended)
log_game("DYNAMIC: Starting a round of forced extended.")
return TRUE
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/roundstart/rule in roundstart_rules)
if (rule.acceptable(roundstart_pop_ready, threat_level) && threat >= rule.cost) // If we got the population and threat required
rule.candidates = candidates.Copy()
rule.trim_candidates()
if (rule.ready() && rule.candidates.len > 0)
drafted_rules[rule] = rule.weight
var/list/drafted_rules = storyteller.roundstart_draft()
if(!drafted_rules.len)
message_admins("Not enough threat level for roundstart antags!")
log_game("DYNAMIC: Not enough threat level for roundstart antags!")
@@ -653,7 +636,6 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
if (rule.persistent)
current_rules += rule
return TRUE
rule.clean_up()
stack_trace("The [rule.ruletype] rule \"[rule.name]\" failed to execute.")
return FALSE
@@ -667,62 +649,41 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
current_rules -= rule
SSblackbox.record_feedback("tally","dynamic",1,"Rulesets finished")
SSblackbox.record_feedback("associative","dynamic_rulesets_finished",1,rule.get_blackbox_info())
storyteller.do_process()
if (midround_injection_cooldown < world.time)
if (GLOB.dynamic_forced_extended)
return
// Somehow it managed to trigger midround multiple times so this was moved here.
// There is no way this should be able to trigger an injection twice now.
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min)
midround_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_midround_delay_min, GLOB.dynamic_midround_delay_max)) + world.time)
midround_injection_cooldown = storyteller.get_midround_cooldown() + world.time
// Time to inject some threat into the round
if(EMERGENCY_ESCAPED_OR_ENDGAMED) // Unless the shuttle is gone
return
if((world.realtime - SSshuttle.realtimeofstart) > SSshuttle.auto_call) // no rules after shuttle is auto-called
return
message_admins("DYNAMIC: Checking for midround injection.")
log_game("DYNAMIC: Checking for midround injection.")
update_playercounts()
if (get_injection_chance())
if (prob(storyteller.get_injection_chance()))
SSblackbox.record_feedback("tally","dynamic",1,"Attempted midround injections")
var/cur_threat_frac = threat/threat_level
var/list/drafted_rules = list()
var/antag_num = current_players[CURRENT_LIVING_ANTAGS].len
for (var/datum/dynamic_ruleset/midround/rule in midround_rules)
// if there are antags OR the rule is an antag rule, antag_acceptable will be true.
if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost)
// Classic secret : only autotraitor/minor roles
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
continue
rule.trim_candidates()
if (rule.ready())
if(!antag_num)
drafted_rules[rule] = round(rule.get_weight() + (rule.cost * cur_threat_frac))
else
drafted_rules[rule] = rule.get_weight()
else if(threat < rule.cost)
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
var/list/drafted_rules = storyteller.midround_draft()
if (drafted_rules.len > 0)
SSblackbox.record_feedback("tally","dynamic",1,"Successful midround injections")
picking_midround_latejoin_rule(drafted_rules)
else
midround_injection_cooldown = (midround_injection_cooldown + world.time)/2
// get_injection_chance can do things on fail
if(event_injection_cooldown < world.time)
SSblackbox.record_feedback("tally","dynamic",1,"Attempted event injections")
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time)
event_injection_cooldown = storyteller.get_event_cooldown() + world.time
message_admins("DYNAMIC: Doing event injection.")
log_game("DYNAMIC: Doing event injection.")
update_playercounts()
var/list/drafted_rules = list()
for(var/datum/dynamic_ruleset/event/rule in events)
if(rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost)
if(rule.ready())
drafted_rules[rule] = rule.get_weight()
else if(threat < rule.cost)
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
var/list/drafted_rules = storyteller.event_draft()
if(drafted_rules.len > 0)
SSblackbox.record_feedback("tally","dynamic",1,"Successful event injections")
picking_midround_latejoin_rule(drafted_rules)
@@ -741,38 +702,13 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
if (M.mind && (M.mind.special_role || M.mind.antag_datums?.len > 0))
current_players[CURRENT_LIVING_ANTAGS].Add(M)
else
if (istype(M,/mob/dead/observer))
if (isobserver(M))
var/mob/dead/observer/O = M
if (O.started_as_observer) // Observers
current_players[CURRENT_OBSERVERS].Add(M)
continue
current_players[CURRENT_DEAD_PLAYERS].Add(M) // Players who actually died (and admins who ghosted, would be nice to avoid counting them somehow)
/// Gets the chance for latejoin and midround injection, the dry_run argument is only used for forced injection.
/datum/game_mode/dynamic/proc/get_injection_chance(dry_run = FALSE)
if(forced_injection)
forced_injection = !dry_run
return 100
var/chance = 0
// If the high pop override is in effect, we reduce the impact of population on the antag injection chance
var/high_pop_factor = (current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit)
var/max_pop_per_antag = max(5,15 - round(threat_level/10) - round(current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5)))
if (!current_players[CURRENT_LIVING_ANTAGS].len)
chance += 80 // No antags at all? let's boost those odds!
else
var/current_pop_per_antag = current_players[CURRENT_LIVING_PLAYERS].len / current_players[CURRENT_LIVING_ANTAGS].len
if (current_pop_per_antag > max_pop_per_antag)
chance += min(50, 25+10*(current_pop_per_antag-max_pop_per_antag))
else
chance += 25-10*(max_pop_per_antag-current_pop_per_antag)
if (current_players[CURRENT_DEAD_PLAYERS].len > current_players[CURRENT_LIVING_PLAYERS].len)
chance -= 30 // More than half the crew died? ew, let's calm down on antags
if (threat > 70)
chance += 15
if (threat < 30)
chance -= 15
return round(max(0,chance))
/// Removes type from the list
/datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type)
for(var/I in type_list)
@@ -803,7 +739,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
return
if(EMERGENCY_ESCAPED_OR_ENDGAMED) // No more rules after the shuttle has left
return
if((world.realtime - SSshuttle.realtimeofstart) > SSshuttle.auto_call) // no rules after shuttle is auto-called
return
update_playercounts()
if (forced_latejoin_rule)
@@ -814,28 +751,12 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
picking_midround_latejoin_rule(list(forced_latejoin_rule), forced = TRUE)
forced_latejoin_rule = null
else if (latejoin_injection_cooldown < world.time && prob(get_injection_chance()))
else if (latejoin_injection_cooldown < world.time && prob(storyteller.get_injection_chance()))
SSblackbox.record_feedback("tally","dynamic",1,"Attempted latejoin injections")
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/latejoin/rule in latejoin_rules)
if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost)
// Classic secret : only autotraitor/minor roles
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
continue
// No stacking : only one round-ender, unless threat level > stacking_limit.
if (threat_level > GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
if(rule.flags & HIGHLANDER_RULESET && highlander_executed)
continue
rule.candidates = list(newPlayer)
rule.trim_candidates()
if (rule.ready())
drafted_rules[rule] = rule.get_weight()
var/list/drafted_rules = storyteller.latejoin_draft(newPlayer)
if (drafted_rules.len > 0 && picking_midround_latejoin_rule(drafted_rules))
SSblackbox.record_feedback("tally","dynamic",1,"Successful latejoin injections")
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min)
latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max)) + world.time
latejoin_injection_cooldown = storyteller.get_latejoin_cooldown() + world.time
/// Refund threat, but no more than threat_level.
/datum/game_mode/dynamic/proc/refund_threat(regain)
@@ -80,9 +80,13 @@
/// Delay for when execute will get called from the time of post_setup (roundstart) or process (midround/latejoin).
/// Make sure your ruleset works with execute being called during the game when using this, and that the clean_up proc reverts it properly in case of faliure.
var/delay = 0
/// List of tags for use in storytellers.
var/list/property_weights = list()
/// Whether or not recent-round weight values are taken into account for this ruleset.
/// Weight reduction uses the same values as secret's recent-round mode weight reduction.
var/always_max_weight = FALSE
/// Weight reduction by recent-rounds. Saved on new.
var/weight_mult = 1
/datum/dynamic_ruleset/New()
..()
@@ -96,13 +100,11 @@
var/high_population_requirements = CONFIG_GET(keyed_list/dynamic_high_population_requirement)
var/list/repeated_mode_adjust = CONFIG_GET(number_list/repeated_mode_adjust)
if(config_tag in weights)
var/weight_mult = 1
if(!always_max_weight && SSpersistence.saved_dynamic_rules.len == 3 && repeated_mode_adjust.len == 3)
var/saved_dynamic_rules = SSpersistence.saved_dynamic_rules
for(var/i in 1 to 3)
if(config_tag in saved_dynamic_rules[i])
weight_mult -= (repeated_mode_adjust[i]/100)
weight = weights[config_tag] * weight_mult
if(config_tag in costs)
cost = costs[config_tag]
if(config_tag in requirementses)
@@ -58,6 +58,7 @@
cost = 10
blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear)
requirements = list(70,60,50,50,40,40,40,30,20,15)
property_weights = list("story_potential" = 1, "trust" = 1, "chaos" = 1)
high_population_requirement = 15
/datum/dynamic_ruleset/event/pirates/ready(forced = FALSE)
@@ -81,6 +82,7 @@
cost = 10
requirements = list(70,60,50,50,40,40,40,30,20,15)
high_population_requirement = 15
property_weights = list("chaos" = 1, "valid" = 1)
//////////////////////////////////////////////
// //
@@ -100,6 +102,7 @@
requirements = list(5,5,5,5,5,5,5,5,5,5) // yes, can happen on fake-extended
high_population_requirement = 5
repeatable = TRUE
property_weights = list("chaos" = 1, "extended" = 2)
/datum/dynamic_ruleset/event/ventclog/ready()
if(mode.threat_level > 30 && mode.threat >= 5 && prob(20))
@@ -133,10 +136,11 @@
required_enemies = list(1,1,0,0,0,0,0,0,0,0)
weight = 4
// no repeatable weight decrease. too variable to be unfun multiple times in one round
cost = 3
cost = 1
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
property_weights = list("story_potential" = 1, "extended" = 1)
always_max_weight = TRUE
//////////////////////////////////////////////
@@ -156,6 +160,7 @@
repeatable_weight_decrease = 2
requirements = list(60,50,40,30,30,30,30,30,30,30)
high_population_requirement = 30
property_weights = list("extended" = -2)
/datum/dynamic_ruleset/event/meteor_wave/ready()
if(mode.threat_level > 40 && mode.threat >= 25 && prob(20))
@@ -190,6 +195,7 @@
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/anomaly_flux
name = "Anomaly: Hyper-Energetic Flux"
@@ -203,6 +209,7 @@
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 10
repeatable = TRUE
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/anomaly_gravitational
name = "Anomaly: Gravitational"
@@ -214,6 +221,7 @@
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/anomaly_pyroclastic
name = "Anomaly: Pyroclastic"
@@ -227,6 +235,7 @@
requirements = list(10,10,10,10,10,10,10,10,10,10)
high_population_requirement = 10
repeatable = TRUE
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/anomaly_vortex
name = "Anomaly: Vortex"
@@ -240,6 +249,7 @@
requirements = list(10,10,10,10,10,10,10,10,10,10)
high_population_requirement = 10
repeatable = TRUE
property_weights = list("extended" = 1)
//////////////////////////////////////////////
// //
@@ -259,6 +269,7 @@
requirements = list(10,10,10,10,10,10,10,10,10,10)
high_population_requirement = 10
repeatable = TRUE
property_weights = list("extended" = -1, "chaos" = 1)
/datum/dynamic_ruleset/event/carp_migration
name = "Carp Migration"
@@ -270,6 +281,7 @@
requirements = list(10,10,10,10,10,10,10,10,10,10)
high_population_requirement = 10
repeatable = TRUE
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/communications_blackout
name = "Communications Blackout"
@@ -283,6 +295,7 @@
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
property_weights = list("extended" = 1, "chaos" = 1)
/datum/dynamic_ruleset/event/processor_overload
name = "Processor Overload"
@@ -296,6 +309,7 @@
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
property_weights = list("extended" = 1, "chaos" = 1)
always_max_weight = TRUE
/datum/dynamic_ruleset/event/space_dust
@@ -310,6 +324,7 @@
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
property_weights = list("extended" = 1)
always_max_weight = TRUE
/datum/dynamic_ruleset/event/major_dust
@@ -324,6 +339,7 @@
requirements = list(10,10,10,10,10,10,10,10,10,10)
high_population_requirement = 10
repeatable = TRUE
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/electrical_storm
name = "Electrical Storm"
@@ -337,6 +353,7 @@
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/heart_attack
name = "Random Heart Attack"
@@ -350,6 +367,7 @@
requirements = list(101,101,101,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
property_weights = list("extended" = 1)
always_max_weight = TRUE
/datum/dynamic_ruleset/event/radiation_storm
@@ -362,3 +380,4 @@
required_enemies = list(1,1,1,1,1,1,1,1,1,1)
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
property_weights = list("extended" = 1,"chaos" = 1)
@@ -69,8 +69,15 @@
high_population_requirement = 15
repeatable = TRUE
flags = TRAITOR_RULESET
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1)
always_max_weight = TRUE
/datum/dynamic_ruleset/latejoin/infiltrator/execute()
. = ..()
for(var/datum/mind/M in assigned)
log_admin("[M.name] was made into a traitor by dynamic.")
message_admins("[M.name] was made into a traitor by dynamic.")
//////////////////////////////////////////////
// //
// REVOLUTIONARY PROVOCATEUR //
@@ -94,6 +101,7 @@
requirements = list(101,101,70,40,40,40,40,40,40,40)
high_population_requirement = 40
flags = HIGHLANDER_RULESET
property_weights = list("trust" = -2, "chaos" = 2, "extended" = -2, "valid" = 2, "conversion" = 1)
var/required_heads_of_staff = 3
var/finished = FALSE
var/datum/team/revolution/revolution
@@ -123,6 +131,8 @@
revolution.update_objectives()
revolution.update_heads()
SSshuttle.registerHostileEnvironment(src)
log_admin("[M.name] was made into a revolutionary by dynamic.")
message_admins("[M.name] was made into a revolutionary by dynamic.")
return TRUE
else
log_game("DYNAMIC: [ruletype] [name] discarded [M.name] from head revolutionary due to ineligibility.")
@@ -187,30 +197,31 @@
//////////////////////////////////////////////
// //
// VAMPIRE //
// BLOODSUCKERS //
// //
//////////////////////////////////////////////
/*
/datum/dynamic_ruleset/latejoin/vampire
name = "vampire"
config_tag = "vampire_latejoin"
antag_flag = ROLE_VAMPIRE
antag_datum = ANTAG_DATUM_VAMPIRE
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain")
/datum/dynamic_ruleset/latejoin/bloodsucker
name = "Bloodsucker Infiltrator"
config_tag = "latejoin_bloodsucker"
antag_datum = ANTAG_DATUM_BLOODSUCKER
antag_flag = ROLE_TRAITOR
restricted_roles = list("AI", "Cyborg")
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
required_candidates = 1
weight = 5
cost = 15
requirements = list(80,70,60,50,40,20,20,15,15,15)
weight = 3
cost = 10
property_weights = list("story_potential" = 2, "extended" = 2, "trust" = -2, "valid" = 1)
requirements = list(70,65,60,55,50,45,40,35,30,30)
high_population_requirement = 30
repeatable = TRUE
high_population_requirement = 15
/datum/dynamic_ruleset/latejoin/vampire/pre_execute()
/datum/dynamic_ruleset/latejoin/bloodsucker/execute()
var/mob/M = pick(candidates)
candidates -= M
assigned += M.mind
M.mind.restricted_roles = restricted_roles
M.mind.special_role = ROLE_VAMPIRE
M.mind.special_role = antag_flag
if(mode.make_bloodsucker(M.mind))
mode.bloodsuckers += M
log_admin("[M.name] was made into a bloodsucker by dynamic.")
message_admins("[M.name] was made into a bloodsucker by dynamic.")
return TRUE
*/
@@ -108,8 +108,12 @@
candidates = pollGhostCandidates("The mode is looking for volunteers to become a [name]", antag_flag, SSticker.mode, antag_flag, poll_time = 300)
if(!candidates || candidates.len <= required_candidates)
if(!candidates || candidates.len < required_candidates)
message_admins("The ruleset [name] did not receive enough applications.")
if(candidates)
message_admins("Only received [candidates.len], needed [required_candidates].")
else
message_admins("There were no candidates.")
log_game("DYNAMIC: The ruleset [name] did not receive enough applications.")
return FALSE
@@ -180,6 +184,7 @@
repeatable = TRUE
high_population_requirement = 15
flags = TRAITOR_RULESET
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1)
always_max_weight = TRUE
/datum/dynamic_ruleset/midround/autotraitor/acceptable(population = 0, threat = 0)
@@ -214,6 +219,8 @@
living_players -= M
var/datum/antagonist/traitor/newTraitor = new
M.mind.add_antag_datum(newTraitor)
log_admin("[M] was made into a traitor by dynamic.")
message_admins("[M] was made into a traitor by dynamic.")
return TRUE
@@ -237,6 +244,7 @@
requirements = list(101,101,70,50,50,50,40,30,30,30)
high_population_requirement = 30
required_type = /mob/living/silicon/ai
property_weights = list("story_potential" = 2, "trust" = 1, "chaos" = 2)
var/ion_announce = 33
var/removeDontImproveChance = 10
@@ -261,6 +269,8 @@
var/datum/antagonist/traitor/AI = new
M.mind.special_role = antag_flag
M.mind.add_antag_datum(AI)
log_admin("[M] was made into a malf AI by dynamic.")
message_admins("[M] was made into a malf AI by dynamic.")
if(prob(ion_announce))
priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", "ionstorm")
if(prob(removeDontImproveChance))
@@ -289,6 +299,7 @@
requirements = list(90,90,70,50,50,50,50,40,30,30)
high_population_requirement = 30
repeatable = TRUE
property_weights = list("story_potential" = 2, "trust" = 1, "chaos" = 2, "extended" = -2)
var/datum/mind/wizard
/datum/dynamic_ruleset/midround/from_ghosts/wizard/ready(forced = FALSE)
@@ -337,6 +348,7 @@
cost = 35
requirements = list(90,90,90,80,70,60,50,40,40,40)
high_population_requirement = 40
property_weights = list("story_potential" = 2, "trust" = 2, "chaos" = 2, "extended" = -2, "valid" = 2)
var/operative_cap = list(2,2,3,3,4,5,5,5,5,5)
var/datum/team/nuclear/nuke_team
flags = HIGHLANDER_RULESET
@@ -390,6 +402,7 @@
requirements = list(101,101,101,80,60,50,50,50,50,50)
high_population_requirement = 50
repeatable = TRUE
property_weights = list("story_potential" = -1, "trust" = 2, "chaos" = 2, "extended" = -2, "valid" = 2)
/datum/dynamic_ruleset/midround/from_ghosts/blob/ready(forced = FALSE)
if (required_candidates > (dead_players.len + list_observers.len))
@@ -421,6 +434,7 @@
high_population_requirement = 50
repeatable_weight_decrease = 2
repeatable = TRUE
property_weights = list("story_potential" = -1, "trust" = 1, "chaos" = 2, "extended" = -2, "valid" = 2)
var/list/vents = list()
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/ready(forced = FALSE)
@@ -476,6 +490,7 @@
high_population_requirement = 50
repeatable_weight_decrease = 2
repeatable = TRUE
property_weights = list("story_potential" = 1, "trust" = 1, "extended" = 1, "valid" = 2, "integrity" = 2)
var/list/spawn_locs = list()
/datum/dynamic_ruleset/midround/from_ghosts/nightmare/execute()
@@ -521,6 +536,7 @@
weight = 4
cost = 5
requirements = list(30,30,20,20,15,10,10,10,10,5) // yes, it can even happen in "extended"!
property_weights = list("story_potential" = 1, "extended" = 1, "valid" = -2)
high_population_requirement = 5
/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease/ready(forced = FALSE)
@@ -555,14 +571,18 @@
cost = 5
requirements = list(30,30,30,30,20,15,15,15,15,15)
high_population_requirement = 15
property_weights = list("story_potential" = -2, "extended" = -1)
var/list/spawn_locs = list()
/datum/dynamic_ruleset/midround/from_ghosts/revenant/ready(forced = FALSE)
/datum/dynamic_ruleset/midround/from_ghosts/revenant/acceptable(population = 0,threat = 0)
var/deadMobs = 0
for(var/mob/M in GLOB.dead_mob_list)
deadMobs++
if(deadMobs < REVENANT_SPAWN_THRESHOLD)
return FALSE
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/revenant/ready(forced = FALSE)
if(required_candidates > (dead_players.len + list_observers.len))
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
return FALSE
@@ -607,6 +627,7 @@
weight = 4
cost = 15
requirements = list(101,101,101,90,80,70,60,50,40,30)
property_weights = list("story_potential" = -2, "extended" = -2, "integrity" = 2, "valid" = 2, "trust" = 2)
high_population_requirement = 30
var/list/spawn_locs = list()
@@ -659,6 +680,7 @@
blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear)
high_population_requirement = 15
var/datum/team/abductor_team/team
property_weights = list("story_potential" = 1, "extended" = -2, "valid" = 1, "trust" = -1, "chaos" = 2)
repeatable_weight_decrease = 4
repeatable = TRUE
@@ -699,6 +721,7 @@
cost = 15
requirements = list(101,101,101,90,80,70,60,50,40,30)
high_population_requirement = 30
property_weights = list("story_potential" = 1, "extended" = -2, "valid" = 2)
var/list/spawn_locs = list()
var/spawn_loc
@@ -745,31 +768,3 @@
#undef ABDUCTOR_MAX_TEAMS
#undef REVENANT_SPAWN_THRESHOLD
//////////////////////////////////////////////
// //
// BLOODSUCKERS //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/latejoin/bloodsucker
name = "Bloodsucker Infiltrator"
config_tag = "latejoin_bloodsucker"
antag_datum = ANTAG_DATUM_BLOODSUCKER
antag_flag = ROLE_TRAITOR
restricted_roles = list("AI", "Cyborg")
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
required_candidates = 1
weight = 3
cost = 10
requirements = list(90,80,70,60,55,50,45,40,35,30)
high_population_requirement = 30
repeatable = TRUE
/datum/dynamic_ruleset/latejoin/bloodsucker/execute()
var/mob/M = pick(candidates)
assigned += M.mind
M.mind.special_role = antag_flag
if(mode.make_bloodsucker(M.mind))
mode.bloodsuckers += M
return TRUE
@@ -21,6 +21,7 @@
requirements = list(50,50,50,50,50,50,50,50,50,50)
high_population_requirement = 40
antag_cap = list(1,1,1,1,2,2,2,2,3,3)
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1, "valid" = 1)
always_max_weight = TRUE
var/autotraitor_cooldown = 450 // 15 minutes (ticks once per 2 sec)
@@ -61,6 +62,7 @@
requirements = list(101,101,101,101,101,101,101,101,101,101)
high_population_requirement = 101
antag_cap = list(2,2,2,2,2,2,2,2,2,2) // Can pick 3 per team, but rare enough it doesn't matter.
property_weights = list("story_potential" = 1, "trust" = -1, "extended" = 1, "valid" = 1)
var/list/datum/team/brother_team/pre_brother_teams = list()
var/const/min_team_size = 2
@@ -108,6 +110,7 @@
cost = 15
scaling_cost = 15
requirements = list(101,101,101,101,101,101,101,101,101,101)
property_weights = list("trust" = -2, "valid" = 2)
high_population_requirement = 10
antag_cap = list(1,1,1,1,1,2,2,2,2,3)
var/team_mode_probability = 30
@@ -160,6 +163,7 @@
cost = 30
requirements = list(101,101,101,60,50,50,50,50,50,50)
high_population_requirement = 50
property_weights = list("story_potential" = 2, "trust" = 1, "chaos" = 2, "extended" = -2, "valid" = 2)
var/list/roundstart_wizards = list()
/datum/dynamic_ruleset/roundstart/wizard/acceptable(population=0, threat=0)
@@ -222,6 +226,7 @@
weight = 3
cost = 30
requirements = list(101,101,101,80,70,60,50,50,50,50)
property_weights = list("story_potential" = -1, "trust" = -1, "chaos" = 1, "conversion" = 1, "extended" = -2, "valid" = 2)
high_population_requirement = 50
flags = HIGHLANDER_RULESET
antag_cap = list(2,2,2,3,3,4,4,4,4,4)
@@ -283,6 +288,7 @@
high_population_requirement = 50
flags = HIGHLANDER_RULESET
antag_cap = list(1,1,2,3,4,5,5,5,5,5)
property_weights = list("story_potential" = 2, "trust" = 2, "chaos" = 2, "extended" = -2, "valid" = 2)
var/datum/team/nuclear/nuke_team
/datum/dynamic_ruleset/roundstart/nuclear/ready(forced = FALSE)
@@ -373,6 +379,7 @@
flags = HIGHLANDER_RULESET
// I give up, just there should be enough heads with 35 players...
minimum_players = 35
property_weights = list("trust" = -2, "chaos" = 2, "extended" = -2, "valid" = 2, "conversion" = 1)
var/datum/team/revolution/revolution
var/finished = FALSE
@@ -490,6 +497,7 @@
weight = 3
cost = 0
requirements = list(101,101,101,101,101,101,101,101,101,101)
property_weights = list("extended" = 2)
high_population_requirement = 101
/datum/dynamic_ruleset/roundstart/extended/pre_execute()
@@ -517,6 +525,7 @@
high_population_requirement = 50
flags = HIGHLANDER_RULESET
antag_cap = list(2,3,3,4,4,4,4,4,4,4)
property_weights = list("trust" = 2, "chaos" = 2, "extended" = -2, "conversion" = 1, "valid" = 2)
var/ark_time
/datum/dynamic_ruleset/roundstart/clockcult/pre_execute()
@@ -616,6 +625,8 @@
antag_leader_datum = /datum/antagonist/nukeop/leader/clownop
requirements = list(101,101,101,101,101,101,101,101,101,101)
high_population_requirement = 101
property_weights = list("trust" = 2, "chaos" = 2, "extended" = -2, "story_potential" = 2, "valid" = 2)
/datum/dynamic_ruleset/roundstart/nuclear/clown_ops/pre_execute()
. = ..()
@@ -647,6 +658,7 @@
requirements = list(101,101,101,101,101,101,101,101,101,101)
high_population_requirement = 101
antag_cap = list(1,1,1,2,2,2,3,3,3,4)
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/roundstart/devil/pre_execute()
var/num_devils = antag_cap[indice_pop]
@@ -698,6 +710,7 @@
cost = 0
requirements = list(101,101,101,101,101,101,101,101,101,101)
high_population_requirement = 101
property_weights = list("extended" = -2, "chaos" = 2, "conversion" = 1, "valid" = 2)
var/players_per_carrier = 30
var/monkeys_to_win = 1
var/escaped_monkeys = 0
@@ -759,6 +772,7 @@
cost = 0
requirements = list(101,101,101,101,101,101,101,101,101,101)
high_population_requirement = 101
property_weights = list("extended" = -2, "chaos" = 2, "trust" = 2)
var/meteordelay = 2000
var/nometeors = 0
var/rampupdelta = 5
@@ -799,7 +813,8 @@
weight = 2
cost = 15
scaling_cost = 10
requirements = list(90,80,70,60,50,50,50,50,50,50)
property_weights = list("story_potential" = 1, "extended" = 1, "trust" = -2, "valid" = 1)
requirements = list(70,65,60,55,50,50,50,50,50,50)
high_population_requirement = 50
antag_cap = list(1,1,1,1,1,2,2,2,2,2)
@@ -0,0 +1,235 @@
/datum/dynamic_storyteller
var/name = "none"
var/desc = "A coder's idiocy."
var/list/property_weights = list()
var/curve_centre = 0
var/curve_width = 1.8
var/forced_threat_level = -1
var/flags = 0
var/weight = 3 // how many rounds need to have been recently played for this storyteller to be left out of the vote
var/datum/game_mode/dynamic/mode = null
/**
Property weights are:
"story_potential" -- essentially how many different ways the antag can be played.
"trust" -- How much it makes the crew trust each other. Negative values means they're suspicious. Team antags are like this.
"chaos" -- How chaotic it makes the round. Has some overlap with "valid" and somewhat contradicts "extended".
"valid" -- How likely the non-antag-enemy crew are to get involved, e.g. nukies encouraging the warden to
let everyone into the armory, wizard moving around and being a nuisance, nightmare busting lights.
"extended" -- How much the antag is conducive to a long round. Nukies and cults are bad for this; Wizard is less bad; and so on.
"conversion" -- Basically a bool. Conversion antags, well, convert. It's its own class for a good reason.
*/
/datum/dynamic_storyteller/New()
..()
if (istype(SSticker.mode, /datum/game_mode/dynamic))
mode = SSticker.mode
GLOB.dynamic_curve_centre = curve_centre
GLOB.dynamic_curve_width = curve_width
GLOB.dynamic_forced_threat_level = forced_threat_level
/datum/dynamic_storyteller/proc/start_injection_cooldowns()
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_latejoin_delay_max + GLOB.dynamic_first_latejoin_delay_min)
mode.latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_first_latejoin_delay_min, GLOB.dynamic_first_latejoin_delay_max)) + world.time
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_midround_delay_min + GLOB.dynamic_first_midround_delay_max)
mode.midround_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_first_midround_delay_min, GLOB.dynamic_first_midround_delay_max)) + world.time
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
mode.event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time)
/datum/dynamic_storyteller/proc/do_process()
return
/datum/dynamic_storyteller/proc/on_start()
return
/datum/dynamic_storyteller/proc/get_midround_cooldown()
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min)
return round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_midround_delay_min, GLOB.dynamic_midround_delay_max))
/datum/dynamic_storyteller/proc/get_event_cooldown()
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
return round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max))
/datum/dynamic_storyteller/proc/get_latejoin_cooldown()
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min)
return round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max))
/datum/dynamic_storyteller/proc/get_injection_chance(dry_run = FALSE)
if(mode.forced_injection)
mode.forced_injection = !dry_run
return 100
var/chance = 0
// If the high pop override is in effect, we reduce the impact of population on the antag injection chance
var/high_pop_factor = (mode.current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit)
var/max_pop_per_antag = max(5,15 - round(mode.threat_level/10) - round(mode.current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5)))
if (!mode.current_players[CURRENT_LIVING_ANTAGS].len)
chance += 80 // No antags at all? let's boost those odds!
else
var/current_pop_per_antag = mode.current_players[CURRENT_LIVING_PLAYERS].len / mode.current_players[CURRENT_LIVING_ANTAGS].len
if (current_pop_per_antag > max_pop_per_antag)
chance += min(50, 25+10*(current_pop_per_antag-max_pop_per_antag))
else
chance += 25-10*(max_pop_per_antag-current_pop_per_antag)
if (mode.current_players[CURRENT_DEAD_PLAYERS].len > mode.current_players[CURRENT_LIVING_PLAYERS].len)
chance -= 30 // More than half the crew died? ew, let's calm down on antags
if (mode.threat > 70)
chance += 15
if (mode.threat < 30)
chance -= 15
return round(max(0,chance))
/datum/dynamic_storyteller/proc/roundstart_draft()
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/roundstart/rule in mode.roundstart_rules)
if (rule.acceptable(mode.roundstart_pop_ready, mode.threat_level) && mode.threat >= rule.cost) // If we got the population and threat required
rule.candidates = mode.candidates.Copy()
rule.trim_candidates()
if (rule.ready() && rule.candidates.len > 0)
var/property_weight = 0
for(var/property in property_weights)
if(property in rule.property_weights) // just treat it as 0 if it's not in there
property_weight += rule.property_weights[property] * property_weights[property]
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
return drafted_rules
/datum/dynamic_storyteller/proc/midround_draft()
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/midround/rule in mode.midround_rules)
// if there are antags OR the rule is an antag rule, antag_acceptable will be true.
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
// Classic secret : only autotraitor/minor roles
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
continue
rule.trim_candidates()
if (rule.ready())
var/property_weight = 0
for(var/property in property_weights)
if(property in rule.property_weights)
property_weight += rule.property_weights[property] * property_weights[property]
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
else if(mode.threat < rule.cost)
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
return drafted_rules
/datum/dynamic_storyteller/proc/latejoin_draft(mob/living/carbon/human/newPlayer)
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/latejoin/rule in mode.latejoin_rules)
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
// Classic secret : only autotraitor/minor roles
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
continue
// No stacking : only one round-ender, unless threat level > stacking_limit.
if (mode.threat_level > GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
if(rule.flags & HIGHLANDER_RULESET && mode.highlander_executed)
continue
rule.candidates = list(newPlayer)
rule.trim_candidates()
if (rule.ready())
var/property_weight = 0
for(var/property in property_weights)
if(property in rule.property_weights)
property_weight += rule.property_weights[property] * property_weights[property]
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
else if(mode.threat < rule.cost)
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
return drafted_rules
/datum/dynamic_storyteller/proc/event_draft()
var/list/drafted_rules = list()
for(var/datum/dynamic_ruleset/event/rule in mode.events)
if(rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
if(rule.ready())
var/property_weight = 0
for(var/property in property_weights)
if(property in rule.property_weights)
property_weight += rule.property_weights[property] * property_weights[property]
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
else if(mode.threat < rule.cost)
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
return drafted_rules
/datum/dynamic_storyteller/cowabunga
name = "Chaotic"
curve_centre = 10
desc = "Chaos: high. Variation: high. Likely antags: clock cult, revs, wizard."
property_weights = list("extended" = -1, "chaos" = 10)
weight = 2
flags = WAROPS_ALWAYS_ALLOWED
var/refund_cooldown
/datum/dynamic_storyteller/cowabunga/get_midround_cooldown()
return ..() / 4
/datum/dynamic_storyteller/cowabunga/get_latejoin_cooldown()
return ..() / 4
/datum/dynamic_storyteller/cowabunga/do_process()
if(refund_cooldown < world.time)
mode.refund_threat(10)
mode.log_threat("Cowabunga it is. Refunded 10 threat. Threat is now [mode.threat].")
refund_cooldown = world.time + 300 SECONDS
/datum/dynamic_storyteller/team
name = "Teamwork"
desc = "Chaos: high. Variation: low. Likely antags: nukies, clockwork cult, wizard, blob, xenomorph."
curve_centre = 2
curve_width = 1.5
weight = 2
flags = WAROPS_ALWAYS_ALLOWED
property_weights = list("valid" = 3, "trust" = 5)
/datum/dynamic_storyteller/team/get_injection_chance(dry_run = FALSE)
return (mode.current_players[CURRENT_LIVING_ANTAGS].len ? 0 : ..())
/datum/dynamic_storyteller/conversion
name = "Conversion"
desc = "Chaos: high. Variation: medium. Likely antags: cults, bloodsuckers, revs."
curve_centre = 3
curve_width = 1
weight = 2
flags = WAROPS_ALWAYS_ALLOWED
property_weights = list("valid" = 1, "conversion" = 20)
/datum/dynamic_storyteller/classic
name = "Random"
desc = "Chaos: varies. Variation: highest. No special weights attached."
weight = 6
curve_width = 4
/datum/dynamic_storyteller/memes
name = "Story"
desc = "Chaos: varies. Variation: high. Likely antags: abductors, nukies, wizard, traitor."
curve_width = 4
property_weights = list("story_potential" = 10)
/datum/dynamic_storyteller/suspicion
name = "Intrigue"
desc = "Chaos: low. Variation: high. Likely antags: traitor, bloodsucker. Rare: revs, blood cult."
curve_width = 4
property_weights = list("trust" = -5)
/datum/dynamic_storyteller/liteextended
name = "Calm"
desc = "Chaos: low. Variation: medium. Likely antags: bloodsuckers, traitors, sentient disease, revenant."
curve_centre = -5
curve_width = 0.5
flags = NO_ASSASSIN
weight = 2
property_weights = list("extended" = 1, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10)
/datum/dynamic_storyteller/liteextended/get_injection_chance(dry_run = FALSE)
return ..()/2
/datum/dynamic_storyteller/extended
name = "Extended"
desc = "Chaos: none. Variation: none. Likely antags: none."
curve_centre = -20
weight = 2
curve_width = 0.5
/datum/dynamic_storyteller/extended/on_start()
GLOB.dynamic_forced_extended = TRUE
+14 -13
View File
@@ -38,21 +38,22 @@
board.one_access = 1
board.accesses = req_one_access
/obj/machinery/button/update_icon()
cut_overlays()
/obj/machinery/button/update_icon_state()
if(panel_open)
icon_state = "button-open"
if(device)
add_overlay("button-device")
if(board)
add_overlay("button-board")
else if(stat & (NOPOWER|BROKEN))
icon_state = "[skin]-p"
else
if(stat & (NOPOWER|BROKEN))
icon_state = "[skin]-p"
else
icon_state = skin
icon_state = skin
/obj/machinery/button/update_overlays()
. = ..()
if(!panel_open)
return
if(device)
. += "button-device"
if(board)
. += "button-board"
/obj/machinery/button/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/screwdriver))
@@ -168,7 +169,7 @@
if(device)
device.pulsed()
addtimer(CALLBACK(src, .proc/update_icon), 15)
addtimer(CALLBACK(src, /atom/.proc/update_icon), 15)
/obj/machinery/button/power_change()
..()
+1 -1
View File
@@ -292,7 +292,7 @@
new /obj/item/stack/cable_coil(loc, 2)
qdel(src)
/obj/machinery/camera/update_icon()
/obj/machinery/camera/update_icon_state()
if(!status)
icon_state = "[initial(icon_state)]1"
else if (stat & EMPED)
+3 -1
View File
@@ -67,7 +67,9 @@
/obj/item/clothing/shoes/kindleKicks = ARCADE_WEIGHT_RARE,
/obj/item/storage/belt/military/snack = ARCADE_WEIGHT_RARE,
/obj/item/clothing/mask/fakemoustache/italian = ARCADE_WEIGHT_RARE
/obj/item/clothing/mask/fakemoustache/italian = ARCADE_WEIGHT_RARE,
/obj/item/clothing/suit/hooded/wintercoat/ratvar/fake = ARCADE_WEIGHT_TRICK,
/obj/item/clothing/suit/hooded/wintercoat/narsie/fake = ARCADE_WEIGHT_TRICK
)
light_color = LIGHT_COLOR_GREEN
+15 -11
View File
@@ -38,17 +38,21 @@
defib.cell.give(180) //90% efficiency, slightly better than the cell charger's 87.5%
update_icon()
/obj/machinery/defibrillator_mount/update_icon()
cut_overlays()
if(defib)
add_overlay("defib")
if(defib.powered)
add_overlay(defib.safety ? "online" : "emagged")
var/ratio = defib.cell.charge / defib.cell.maxcharge
ratio = CEILING(ratio * 4, 1) * 25
add_overlay("charge[ratio]")
if(clamps_locked)
add_overlay("clamps")
/obj/machinery/defibrillator_mount/update_overlays()
. = ..()
if(!defib)
return
. += "defib"
if(defib.powered)
. += (defib.safety ? "online" : "emagged")
var/ratio = defib.cell.charge / defib.cell.maxcharge
ratio = CEILING(ratio * 4, 1) * 25
. += "charge[ratio]"
if(clamps_locked)
. += "clamps"
/obj/machinery/defibrillator_mount/get_cell()
if(defib)
+1 -1
View File
@@ -180,7 +180,7 @@
limit--
while(!FoundDoor && limit)
if (!FoundDoor)
log_world("### MAP WARNING, [src] at [AREACOORD(src)] failed to find a valid airlock to cyclelink with!")
log_mapping("[src] at [AREACOORD(src)] failed to find a valid airlock to cyclelink with!")
return
FoundDoor.cyclelinkedairlock = src
cyclelinkedairlock = FoundDoor
+17 -1
View File
@@ -222,5 +222,21 @@
. += "\t<span class='notice'>[attached ? attached : "No one"] is attached.</span>"
/obj/machinery/iv_drip/telescopic
name = "telescopic IV drip"
desc = "An IV drip with an advanced infusion pump that can both drain blood into and inject liquids from attached containers. Blood packs are processed at an accelerated rate. This one is telescopic, and can be picked up and put down."
icon_state = "iv_drip"
/obj/machinery/iv_drip/telescopic/update_icon()
..()
icon_state = icon_state + "_tele"
/obj/machinery/iv_drip/telescopic/AltClick(mob/user)
if (attached || beaker || !user.canUseTopic(src, BE_CLOSE))
return ..()
new /obj/item/tele_iv(get_turf(src))
qdel(src)
return TRUE
#undef IV_TAKING
#undef IV_INJECTING
#undef IV_INJECTING
@@ -262,14 +262,14 @@
occupant_message("Deconstructing [W]...")
if(do_after_cooldown(W))
chassis.spark_system.start()
W.ScrapeAway()
W.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
playsound(W, 'sound/items/deconstruct.ogg', 50, 1)
else if(isfloorturf(target))
var/turf/open/floor/F = target
occupant_message("Deconstructing [F]...")
if(do_after_cooldown(target))
chassis.spark_system.start()
F.ScrapeAway()
F.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
playsound(F, 'sound/items/deconstruct.ogg', 50, 1)
else if (istype(target, /obj/machinery/door/airlock))
occupant_message("Deconstructing [target]...")
@@ -282,7 +282,7 @@
var/turf/open/space/S = target
occupant_message("Building Floor...")
if(do_after_cooldown(S))
S.PlaceOnTop(/turf/open/floor/plating)
S.PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
playsound(S, 'sound/items/deconstruct.ogg', 50, 1)
chassis.spark_system.start()
else if(isfloorturf(target))
@@ -123,7 +123,7 @@
if(metal)
var/turf/T = get_turf(src)
if(isspaceturf(T)) //Block up any exposed space
T.PlaceOnTop(/turf/open/floor/plating/foam)
T.PlaceOnTop(/turf/open/floor/plating/foam, flags = CHANGETURF_INHERIT_AIR)
for(var/direction in GLOB.cardinals)
var/turf/cardinal_turf = get_step(T, direction)
if(get_area(cardinal_turf) != get_area(T)) //We're at an area boundary, so let's block off this turf!
+49
View File
@@ -432,3 +432,52 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player)
GLOB.ruin_landmarks -= src
ruin_template = null
. = ..()
//------Station Rooms Landmarks------------//
/obj/effect/landmark/stationroom
var/list/template_names = list()
layer = BULLET_HOLE_LAYER
/obj/effect/landmark/stationroom/New()
..()
GLOB.stationroom_landmarks += src
/obj/effect/landmark/stationroom/Destroy()
if(src in GLOB.stationroom_landmarks)
GLOB.stationroom_landmarks -= src
return ..()
/obj/effect/landmark/stationroom/proc/load(template_name)
var/turf/T = get_turf(src)
if(!T)
return FALSE
if(!template_name)
for(var/t in template_names)
if(!SSmapping.station_room_templates[t])
log_world("Station room spawner placed at ([T.x], [T.y], [T.z]) has invalid ruin name of \"[t]\" in its list")
template_names -= t
template_name = safepick(template_names)
if(!template_name)
GLOB.stationroom_landmarks -= src
qdel(src)
return FALSE
var/datum/map_template/template = SSmapping.station_room_templates[template_name]
if(!template)
return FALSE
testing("Room \"[template_name]\" placed at ([T.x], [T.y], [T.z])")
template.load(T, centered = FALSE)
template.loaded++
GLOB.stationroom_landmarks -= src
qdel(src)
return TRUE
// The landmark for the Engine on Box
/obj/effect/landmark/stationroom/box/engine
template_names = list("Engine SM", "Engine Singulo", "Engine Tesla")
icon = 'icons/rooms/box/engine.dmi'
/obj/effect/landmark/stationroom/box/engine/New()
. = ..()
template_names = CONFIG_GET(keyed_list/box_random_engine)
+9 -8
View File
@@ -578,21 +578,22 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
itempush = 0 //too light to push anything
return hit_atom.hitby(src, 0, itempush, throwingdatum=throwingdatum)
/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force)
/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, messy_throw = TRUE)
thrownby = thrower
callback = CALLBACK(src, .proc/after_throw, callback) //replace their callback with our own
callback = CALLBACK(src, .proc/after_throw, callback, (spin && messy_throw)) //replace their callback with our own
. = ..(target, range, speed, thrower, spin, diagonals_first, callback, force)
/obj/item/proc/after_throw(datum/callback/callback)
/obj/item/proc/after_throw(datum/callback/callback, messy_throw)
if (callback) //call the original callback
. = callback.Invoke()
throw_speed = initial(throw_speed) //explosions change this.
item_flags &= ~IN_INVENTORY
var/matrix/M = matrix(transform)
M.Turn(rand(-170, 170))
transform = M
pixel_x = rand(-8, 8)
pixel_y = rand(-8, 8)
if(messy_throw)
var/matrix/M = matrix(transform)
M.Turn(rand(-170, 170))
transform = M
pixel_x = rand(-8, 8)
pixel_y = rand(-8, 8)
/obj/item/proc/remove_item_from_storage(atom/newLoc) //please use this if you're going to snowflake an item out of a obj/item/storage
if(!newLoc)
+202
View File
@@ -863,3 +863,205 @@ CIGARETTE PACKETS ARE IN FANCY.DM
if(reagents && reagents.total_volume)
hand_reagents()
///////////////
/////BONGS/////
///////////////
/obj/item/bong
name = "bong"
desc = "A water bong used for smoking dried plants."
icon = 'icons/obj/bongs.dmi'
icon_state = null
item_state = null
w_class = WEIGHT_CLASS_NORMAL
light_color = "#FFCC66"
var/icon_off = "bong"
var/icon_on = "bong_lit"
var/chem_volume = 100
var/last_used_time //for cooldown
var/firecharges = 0 //used for counting how many hits can be taken before the flame goes out
var/list/list_reagents = list() //For the base reagents bongs could get
/obj/item/bong/Initialize()
. = ..()
create_reagents(chem_volume, NO_REACT) // so it doesn't react until you light it
reagents.add_reagent_list(list_reagents)
icon_state = icon_off
/obj/item/bong/attackby(obj/item/O, mob/user, params)
. = ..()
//If we're using a dried plant..
if(istype(O,/obj/item/reagent_containers/food/snacks))
var/obj/item/reagent_containers/food/snacks/DP = O
if (DP.dry)
//Nothing if our bong is full
if (reagents.holder_full())
user.show_message("<span class='notice'>The bowl is full!</span>", MSG_VISUAL)
return
//Transfer reagents and remove the plant
user.show_message("<span class='notice'>You stuff the [DP] into the [src]'s bowl.</span>", MSG_VISUAL)
DP.reagents.trans_to(src, 100)
qdel(DP)
return
else
user.show_message("<span class='warning'>[DP] must be dried first!</span>", MSG_VISUAL)
return
if (O.get_temperature() <= 500)
return
if (reagents && reagents.total_volume) //if there's stuff in the bong
var/lighting_text = O.ignition_effect(src, user)
if(lighting_text)
//Logic regarding igniting it on
if (firecharges == 0)
user.show_message("<span class='notice'>You light the [src] with the [O]!</span>", MSG_VISUAL)
bongturnon()
else
user.show_message("<span class='notice'>You rekindle [src]'s flame with the [O]!</span>", MSG_VISUAL)
firecharges = 1
return
else
user.show_message("<span warning='notice'>There's nothing to light up in the bowl.</span>", MSG_VISUAL)
return
/obj/item/bong/CtrlShiftClick(mob/user) //empty reagents on alt click
..()
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
return
if (reagents && reagents.total_volume)
user.show_message("<span class='notice'>You empty the [src].</span>", MSG_VISUAL)
reagents.clear_reagents()
if(firecharges)
firecharges = 0
bongturnoff()
else
user.show_message("<span class='notice'>The [src] is already empty.</span>", MSG_VISUAL)
/obj/item/bong/AltClick(mob/user)
..()
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
return
if(firecharges)
firecharges = 0
bongturnoff()
user.show_message("<span class='notice'>You quench the flame.</span>", MSG_VISUAL)
return TRUE
/obj/item/bong/examine(mob/user)
. = ..()
if(!reagents.total_volume)
. += "<span class='notice'>The bowl is empty.</span>"
else if (reagents.total_volume > 80)
. += "<span class='notice'>The bowl is filled to the brim.</span>"
else if (reagents.total_volume > 40)
. += "<span class='notice'>The bowl has plenty weed in it.</span>"
else
. += "<span class='notice'>The bowl has some weed in it.</span>"
. += "<span class='notice'>Ctrl+Shift-click to empty.</span>"
. += "<span class='notice'>Alt-click to extinguish.</span>"
/obj/item/bong/ignition_effect(atom/A, mob/user)
if(firecharges)
. = "<span class='notice'>[user] lights [A] off of the [src].</span>"
else
. = ""
/obj/item/bong/attack(mob/living/carbon/M, mob/living/carbon/user, obj/target)
//if it's lit up, some stuff in the bowl and the user is a target, and we're not on cooldown
if (M != user)
return ..()
if(user.is_mouth_covered(head_only = 1))
to_chat(user, "<span class='warning'>Remove your headgear first.</span>")
return ..()
if(user.is_mouth_covered(mask_only = 1))
to_chat(user, "<span class='warning'>Remove your mask first.</span>")
return ..()
if (!reagents.total_volume)
to_chat(user, "<span class='warning'>There's nothing in the bowl.</span>")
return ..()
if (!firecharges)
to_chat(user, "<span class='warning'>You have to light it up first.</span>")
return ..()
if (last_used_time + 30 >= world.time)
return ..()
var/hit_strength
var/noise
var/hittext = ""
//if the intent is help then you take a small hit, else a big one
if (user.a_intent == INTENT_HARM)
hit_strength = 2
noise = 100
hittext = "big hit"
else
hit_strength = 1
noise = 70
hittext = "hit"
//bubbling sound
playsound(user.loc,'sound/effects/bonghit.ogg', noise, 1)
last_used_time = world.time
//message
user.visible_message("<span class='notice'>[user] begins to take a [hittext] from the [src]!</span>", \
"<span class='notice'>You begin to take a [hittext] from [src].</span>")
//we take a hit here, after an uninterrupted delay
if(!do_after(user, 25, target = user))
return
if (!(reagents && reagents.total_volume))
return
var/fraction = 12 * hit_strength
var/datum/effect_system/smoke_spread/chem/smoke_machine/s = new
s.set_up(reagents, hit_strength, 18, user.loc)
s.start()
reagents.reaction(user, INGEST, fraction)
if(!reagents.trans_to(user, fraction))
reagents.remove_any(fraction)
if (hit_strength == 2 && prob(15))
user.emote("cough")
user.adjustOxyLoss(15)
user.visible_message("<span class='notice'>[user] takes a [hittext] from the [src]!</span>", \
"<span class='notice'>You take a [hittext] from [src].</span>")
firecharges = firecharges - 1
if (!firecharges)
bongturnoff()
if (!reagents.total_volume)
firecharges = 0
bongturnoff()
/obj/item/bong/proc/bongturnon()
icon_state = icon_on
set_light(3, 0.8)
/obj/item/bong/proc/bongturnoff()
icon_state = icon_off
set_light(0, 0.0)
/obj/item/bong/coconut
name = "coconut bong"
icon_off = "coconut_bong"
icon_on = "coconut_bong_lit"
desc = "A water bong used for smoking dried plants. This one's made out of a coconut and some bamboo."
+9 -13
View File
@@ -47,8 +47,7 @@
/obj/item/defibrillator/update_icon()
update_power()
update_overlays()
update_charge()
return ..()
/obj/item/defibrillator/proc/update_power()
if(!QDELETED(cell))
@@ -59,23 +58,20 @@
else
powered = FALSE
/obj/item/defibrillator/proc/update_overlays()
cut_overlays()
/obj/item/defibrillator/update_overlays()
. = ..()
if(!on)
add_overlay("[initial(icon_state)]-paddles")
. += "[initial(icon_state)]-paddles"
if(powered)
add_overlay("[initial(icon_state)]-powered")
if(!cell)
add_overlay("[initial(icon_state)]-nocell")
if(!safety)
add_overlay("[initial(icon_state)]-emagged")
/obj/item/defibrillator/proc/update_charge()
if(powered) //so it doesn't show charge if it's unpowered
. += "[initial(icon_state)]-powered"
if(!QDELETED(cell))
var/ratio = cell.charge / cell.maxcharge
ratio = CEILING(ratio*4, 1) * 25
add_overlay("[initial(icon_state)]-charge[ratio]")
if(!cell)
. += "[initial(icon_state)]-nocell"
if(!safety)
. += "[initial(icon_state)]-emagged"
/obj/item/defibrillator/CheckParts(list/parts_list)
..()
@@ -2,7 +2,7 @@
name = "forcefield projector"
desc = "An experimental device that can create several forcefields at a distance."
icon = 'icons/obj/device.dmi'
icon_state = "signmaker_engi"
icon_state = "signmaker_forcefield"
slot_flags = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
item_flags = NOBLUDGEON
@@ -44,11 +44,6 @@
icon_state = "eng_cypherkey"
channels = list(RADIO_CHANNEL_ENGINEERING = 1)
/obj/item/encryptionkey/headset_rob
name = "robotics radio encryption key"
icon_state = "rob_cypherkey"
channels = list(RADIO_CHANNEL_SCIENCE = 1, RADIO_CHANNEL_ENGINEERING = 1)
/obj/item/encryptionkey/headset_med
name = "medical radio encryption key"
icon_state = "med_cypherkey"
@@ -123,12 +123,6 @@ GLOBAL_LIST_INIT(channel_tokens, list(
icon_state = "eng_headset"
keyslot = new /obj/item/encryptionkey/headset_eng
/obj/item/radio/headset/headset_rob
name = "robotics radio headset"
desc = "Made specifically for the roboticists, who cannot decide between departments."
icon_state = "rob_headset"
keyslot = new /obj/item/encryptionkey/headset_rob
/obj/item/radio/headset/headset_med
name = "medical radio headset"
desc = "A headset for the trained staff of the medbay."
@@ -230,6 +224,14 @@ GLOBAL_LIST_INIT(channel_tokens, list(
icon_state = "srv_headset"
keyslot = new /obj/item/encryptionkey/headset_service
/obj/item/radio/headset/headset_clown
name = "clown's headset"
desc = "A headset for the clown. Finally. A megaphone you can't take away."
icon_state = "srv_headset"
keyslot = new /obj/item/encryptionkey/headset_service
command = TRUE
commandspan = SPAN_CLOWN
/obj/item/radio/headset/headset_cent
name = "\improper CentCom headset"
desc = "A headset used by the upper echelons of Nanotrasen."
@@ -29,6 +29,7 @@
var/freqlock = FALSE // Frequency lock to stop the user from untuning specialist radios.
var/use_command = FALSE // If true, broadcasts will be large and BOLD.
var/command = FALSE // If true, use_command can be toggled at will.
var/commandspan = SPAN_COMMAND //allow us to set what the fuck we want for headsets
// Encryption key handling
var/obj/item/encryptionkey/keyslot
@@ -206,7 +207,7 @@
return
if(use_command)
spans |= SPAN_COMMAND
spans |= commandspan
/*
Roughly speaking, radios attempt to make a subspace transmission (which
@@ -224,7 +224,7 @@
merge_gases()
for(var/i in 1 to 6)
addtimer(CALLBACK(src, .proc/update_icon), 20 + (i - 1) * 10)
addtimer(CALLBACK(src, /atom/.proc/update_icon), 20 + (i - 1) * 10)
else if(valve_open && tank_one && tank_two)
split_gases()
+17
View File
@@ -414,6 +414,23 @@
name = "empty scroll"
icon_state = "blankscroll"
/obj/item/book/granter/martial/bass
martial = /datum/martial_art/the_rising_bass
name = "shifting scroll"
martialname = "rising bass"
desc = "A paper scroll that seems to move even as you read it, the letters never seem to stay still."
greet = "<span class='sciradio'>You have learned the ancient martial art of the Rising Bass. Your skill at running away has increased quite a bit. Use the combos to get away from opponents quickly. Along with this, you now dodge all projectiles and catch anything thrown at you.</span>"
icon = 'icons/obj/wizard.dmi'
icon_state = "scroll2"
remarks = list("The trick is to disarm them...","Running away helps in many situations...","Never stay still...","Fighting won't help unless you're forced to...", "Crush their limbs to incapacitate them...", "Stay as far away as possible...")
/obj/item/book/granter/martial/bass/onlearned(mob/living/carbon/user)
..()
if(oneuse == TRUE)
desc = "It's completely blank."
name = "empty scroll"
icon_state = "blankscroll"
/obj/item/book/granter/martial/plasma_fist
martial = /datum/martial_art/plasma_fist
name = "frayed scroll"
+1 -1
View File
@@ -79,7 +79,7 @@
/obj/item/holosign_creator/atmos
name = "ATMOS holofan projector"
desc = "A holographic projector that creates holographic barriers that prevent changes in atmosphere conditions."
icon_state = "signmaker_engi"
icon_state = "signmaker_atmos"
holosign_type = /obj/structure/holosign/barrier/atmos
creation_time = 0
max_signs = 3
+1 -1
View File
@@ -374,7 +374,7 @@
It appears to have a wooden grip and a shaved down guard."
icon_state = "cxsword_hilt_traitor"
force_on = 30
armour_penetration = 50
armour_penetration = 35
embedding = list("embedded_pain_multiplier" = 10, "embed_chance" = 75, "embedded_fall_chance" = 0, "embedded_impact_pain_multiplier" = 10)
block_chance = 50
hitsound_on = 'sound/weapons/blade1.ogg'
+3 -2
View File
@@ -216,10 +216,11 @@
return
else
if(last_hit < world.time)
if(target.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
playsound(target, 'sound/weapons/genhit.ogg', 50, 1)
return
if(ishuman(target))
var/mob/living/carbon/human/H = target
if (H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
return
if(check_martial_counter(H, user))
return
playsound(get_turf(src), 'sound/effects/woodhit.ogg', 75, 1, -1)
+3 -5
View File
@@ -11,11 +11,9 @@
var/charge_cost = 30
/obj/item/borg/stun/attack(mob/living/M, mob/living/user)
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.check_shields(src, 0, "[M]'s [name]", MELEE_ATTACK))
playsound(M, 'sound/weapons/genhit.ogg', 50, 1)
return FALSE
if(M.check_shields(src, 0, "[M]'s [name]", MELEE_ATTACK))
playsound(M, 'sound/weapons/genhit.ogg', 50, 1)
return FALSE
if(iscyborg(user))
var/mob/living/silicon/robot/R = user
if(!R.cell.use(charge_cost))
+5 -7
View File
@@ -2,8 +2,8 @@
name = "medical pack"
singular_name = "medical pack"
icon = 'icons/obj/stack_objects.dmi'
amount = 6
max_amount = 6
amount = 12
max_amount = 12
w_class = WEIGHT_CLASS_TINY
full_w_class = WEIGHT_CLASS_TINY
throw_speed = 3
@@ -55,9 +55,8 @@
icon_state = "brutepack"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
var/heal_brute = 25
var/heal_brute = 20
self_delay = 20
max_amount = 12
grind_results = list("styptic_powder" = 10)
/obj/item/stack/medical/bruise_pack/heal(mob/living/M, mob/user)
@@ -92,7 +91,6 @@
var/stop_bleeding = 1800
var/heal_brute = 5
self_delay = 10
max_amount = 12
/obj/item/stack/medical/gauze/heal(mob/living/M, mob/user)
if(ishuman(M))
@@ -125,6 +123,7 @@
singular_name = "improvised gauze"
desc = "A roll of cloth roughly cut from something that can stop bleeding, but does not heal wounds."
stop_bleeding = 900
heal_brute = 0
/obj/item/stack/medical/gauze/cyborg
materials = list()
@@ -139,9 +138,8 @@
icon_state = "ointment"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
var/heal_burn = 25
var/heal_burn = 20
self_delay = 20
max_amount = 12
grind_results = list("silver_sulfadiazine" = 10)
/obj/item/stack/medical/ointment/heal(mob/living/M, mob/user)
@@ -673,7 +673,8 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \
merge_type = /obj/item/stack/sheet/bone
GLOBAL_LIST_INIT(plastic_recipes, list(
new /datum/stack_recipe("plastic flaps", /obj/structure/plasticflaps, 5, one_per_turf = TRUE, on_floor = TRUE, time = 40), \
new /datum/stack_recipe("see-through plastic flaps", /obj/structure/plasticflaps, 5, one_per_turf = TRUE, on_floor = TRUE, time = 40), \
new /datum/stack_recipe("opaque plastic flaps", /obj/structure/plasticflaps/opaque, 5, one_per_turf = TRUE, on_floor = TRUE, time = 40), \
new /datum/stack_recipe("water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/empty), \
new /datum/stack_recipe("large water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/large/empty,3), \
new /datum/stack_recipe("large trash cart", /obj/structure/closet/crate/bin,50),\
+7 -2
View File
@@ -192,9 +192,14 @@
var/obj/O
if(R.max_res_amount > 1) //Is it a stack?
O = new R.result_type(usr.drop_location(), R.res_amount * multiplier)
else
else if(ispath(R.result_type, /turf))
var/turf/T = usr.drop_location()
if(!isturf(T))
return
T.PlaceOnTop(R.result_type, flags = CHANGETURF_INHERIT_AIR)
O = new R.result_type(usr.drop_location())
O.setDir(usr.dir)
if(O)
O.setDir(usr.dir)
use(R.req_amount * multiplier)
//START: oh fuck i'm so sorry
+5 -4
View File
@@ -61,6 +61,7 @@
/obj/item/radio,
/obj/item/clothing/gloves,
/obj/item/holosign_creator,
/obj/item/forcefield_projector,
/obj/item/assembly/signaler
))
STR.can_hold = can_hold
@@ -458,8 +459,7 @@
/obj/item/extinguisher/mini,
/obj/item/radio,
/obj/item/clothing/gloves,
/obj/item/holosign_creator/atmos,
/obj/item/holosign_creator/engineering,
/obj/item/holosign_creator,
/obj/item/forcefield_projector,
/obj/item/assembly/signaler,
/obj/item/lightreplacer,
@@ -571,6 +571,7 @@
/obj/item/reagent_containers/spray,
/obj/item/soap,
/obj/item/holosign_creator,
/obj/item/forcefield_projector,
/obj/item/key/janitor,
/obj/item/clothing/gloves,
/obj/item/melee/flyswatter,
@@ -582,7 +583,7 @@
/obj/item/storage/belt/bandolier
name = "bandolier"
desc = "A bandolier for holding shotgun ammunition."
desc = "A bandolier for holding ammunition."
icon_state = "bandolier"
item_state = "bandolier"
@@ -592,7 +593,7 @@
STR.max_items = 18
STR.display_numerical_stacking = TRUE
STR.can_hold = typecacheof(list(
/obj/item/ammo_casing/shotgun
/obj/item/ammo_casing
))
/obj/item/storage/belt/bandolier/durathread
+9
View File
@@ -1259,3 +1259,12 @@
var/obj/item/stack/sheet/cardboard/I = new(user.drop_location())
qdel(src)
user.put_in_hands(I)
/obj/item/storage/box/marshmallow
name = "box of marshmallows"
desc = "A box of marshmallows."
illustration = "marshmallow"
/obj/item/storage/box/marshmallow/PopulateContents()
for (var/i in 1 to 5)
new /obj/item/reagent_containers/food/snacks/marshmallow(src)
@@ -390,4 +390,10 @@
new /obj/item/seeds/random(src)
if(prob(50))
new /obj/item/seeds/random(src) //oops, an additional packet might have slipped its way into the box
new /obj/item/seeds/random(src) //oops, an additional packet might have slipped its way into the box
/obj/item/storage/box/syndie_kit/revolver
/obj/item/storage/box/syndie_kit/revolver/PopulateContents()
new /obj/item/gun/ballistic/revolver(src)
new /obj/item/ammo_box/a357(src)
+4 -6
View File
@@ -36,7 +36,7 @@
. = ..()
if(preload_cell_type)
if(!ispath(preload_cell_type,/obj/item/stock_parts/cell))
log_world("### MAP WARNING, [src] at [AREACOORD(src)] had an invalid preload_cell_type: [preload_cell_type].")
log_mapping("[src] at [AREACOORD(src)] had an invalid preload_cell_type: [preload_cell_type].")
else
cell = new preload_cell_type(src)
update_icon()
@@ -168,11 +168,9 @@
/obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/user)
if(ishuman(L))
var/mob/living/carbon/human/H = L
if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) //No message; check_shields() handles that
playsound(L, 'sound/weapons/genhit.ogg', 50, 1)
return FALSE
if(L.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) //No message; check_shields() handles that
playsound(L, 'sound/weapons/genhit.ogg', 50, 1)
return FALSE
var/stunpwr = stunforce
var/obj/item/stock_parts/cell/our_cell = get_cell()
if(!our_cell)
+17
View File
@@ -0,0 +1,17 @@
/obj/item/tele_iv
name = "telescopic IV drip"
desc = "An IV drip with an advanced infusion pump that can both drain blood into and inject liquids from attached containers. Blood packs are processed at an accelerated rate. This one is telescopic, and can be picked up and put down."
icon = 'icons/obj/iv_drip.dmi'
icon_state = "tele_iv"
/obj/item/tele_iv/attack_self(mob/user)
deploy_iv(user, user.loc)
/obj/item/tele_iv/afterattack(atom/target, mob/user, proximity)
. = ..()
if(proximity && isopenturf(target) && user.CanReach(target))
deploy_iv(user, target)
/obj/item/tele_iv/proc/deploy_iv(mob/user, atom/location)
new /obj/machinery/iv_drip/telescopic(location)
qdel(src)
+1 -1
View File
@@ -872,7 +872,7 @@
user.visible_message("<span class='danger'>[user] blasts \the [target] with \the [src]!</span>")
playsound(target, 'sound/magic/disintegrate.ogg', 100, 1)
W.break_wall()
W.ScrapeAway()
W.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
return
//HF blade
+2 -6
View File
@@ -80,13 +80,12 @@
SEND_SIGNAL(src, COMSIG_OBJ_SETANCHORED, anchorvalue)
anchored = anchorvalue
/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force)
..()
/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, messy_throw = TRUE)
. = ..()
if(obj_flags & FROZEN)
visible_message("<span class='danger'>[src] shatters into a million pieces!</span>")
qdel(src)
/obj/assume_air(datum/gas_mixture/giver)
if(loc)
return loc.assume_air(giver)
@@ -173,9 +172,6 @@
/obj/proc/container_resist(mob/living/user)
return
/obj/proc/update_icon()
return
/mob/proc/unset_machine()
if(machine)
machine.on_unset_machine(src)
@@ -12,18 +12,12 @@
new /obj/item/clothing/head/hardhat/white(src)
new /obj/item/clothing/head/hardhat/weldhat/white(src)
new /obj/item/clothing/gloves/color/yellow(src)
new /obj/item/clothing/shoes/sneakers/brown(src)
new /obj/item/tank/jetpack/suit(src)
new /obj/item/cartridge/ce(src)
new /obj/item/radio/headset/heads/ce(src)
new /obj/item/storage/toolbox/mechanical(src)
new /obj/item/clothing/suit/hazardvest(src)
new /obj/item/megaphone/command(src)
new /obj/item/areaeditor/blueprints(src)
new /obj/item/airlock_painter(src)
new /obj/item/holosign_creator/engineering(src)
new /obj/item/clothing/mask/gas(src)
new /obj/item/multitool(src)
new /obj/item/assembly/flash/handheld(src)
new /obj/item/clothing/glasses/meson/engine(src)
new /obj/item/door_remote/chief_engineer(src)
@@ -67,7 +61,7 @@
for(var/i in 1 to 3)
new /obj/item/clothing/head/welding(src)
for(var/i in 1 to 3)
new /obj/item/weldingtool(src)
new /obj/item/weldingtool/largetank(src)
/obj/structure/closet/secure_closet/engineering_personal
name = "engineer's locker"
@@ -84,7 +78,6 @@
new /obj/item/clothing/glasses/meson/engine(src)
new /obj/item/storage/box/emptysandbags(src)
/obj/structure/closet/secure_closet/atmospherics
name = "\proper atmospheric technician's locker"
req_access = list(ACCESS_ATMOSPHERICS)
@@ -103,3 +96,33 @@
new /obj/item/clothing/head/hardhat/atmos(src)
new /obj/item/clothing/glasses/meson/engine/tray(src)
new /obj/item/extinguisher/advanced(src)
/*
* Empty lockers
* Some of the lockers are filled with junk, and sometimes its nice to just fill it with your own set-up for your own map gimmicks.
*/
/obj/structure/closet/secure_closet/engineering_chief/empty
/obj/structure/closet/secure_closet/engineering_chief/empty/PopulateContents()
return
/obj/structure/closet/secure_closet/engineering_electrical/empty
/obj/structure/closet/secure_closet/engineering_electrical/empty/PopulateContents()
return
/obj/structure/closet/secure_closet/engineering_welding/empty
/obj/structure/closet/secure_closet/engineering_welding/empty/PopulateContents()
return
/obj/structure/closet/secure_closet/engineering_personal/empty
/obj/structure/closet/secure_closet/engineering_personal/empty/PopulateContents()
return
/obj/structure/closet/secure_closet/atmospherics/empty
/obj/structure/closet/secure_closet/atmospherics/empty/PopulateContents()
return
+1 -1
View File
@@ -41,7 +41,7 @@
to_chat(user, "<span class='notice'>The water feels warm and soothing as you touch it. The fountain immediately dries up shortly afterwards.</span>")
user.reagents.add_reagent("godblood",20)
update_icon()
addtimer(CALLBACK(src, .proc/update_icon), time_between_uses)
addtimer(CALLBACK(src, /atom/.proc/update_icon), time_between_uses)
/obj/structure/healingfountain/update_icon()
@@ -73,7 +73,7 @@
else if(glass)
user.visible_message("[user] welds the glass panel out of the airlock assembly.", "You start to weld the glass panel out of the airlock assembly...")
if(W.use_tool(src, user, 40, volume=50))
if(W.use_tool(src, user, 40, volume=50) && glass)
to_chat(user, "<span class='notice'>You weld the glass panel out.</span>")
if(heat_proof_finished)
new /obj/item/stack/sheet/rglass(get_turf(src))
+3 -3
View File
@@ -30,11 +30,11 @@
return
var/list/undergarment_choices = list("Underwear", "Underwear Color", "Undershirt", "Undershirt Color", "Socks", "Socks Color")
if(!UNDIE_COLORABLE(GLOB.underwear_list[H.underwear]))
if(!(GLOB.underwear_list[H.underwear]?.has_color))
undergarment_choices -= "Underwear Color"
if(!UNDIE_COLORABLE(GLOB.undershirt_list[H.undershirt]))
if(!(GLOB.undershirt_list[H.undershirt]?.has_color))
undergarment_choices -= "Undershirt Color"
if(!UNDIE_COLORABLE(GLOB.socks_list[H.socks]))
if(!(GLOB.socks_list[H.socks]?.has_color))
undergarment_choices -= "Socks Color"
var/choice = input(H, "Underwear, Undershirt, or Socks?", "Changing") as null|anything in undergarment_choices
@@ -30,6 +30,8 @@
new/obj/structure/fluff/empty_terrarium(get_turf(src))
return ..()
/obj/effect/mob_spawn/human/seed_vault/special(mob/living/carbon/human/new_spawn)
ADD_TRAIT(new_spawn,TRAIT_EXEMPT_HEALTH_EVENTS,GHOSTROLE_TRAIT)
//Ash walker eggs: Spawns in ash walker dens in lavaland. Ghosts become unbreathing lizards that worship the Necropolis and are advised to retrieve corpses to create more ash walkers.
/obj/effect/mob_spawn/human/ash_walker
@@ -253,6 +255,9 @@
new/obj/structure/fluff/empty_cryostasis_sleeper(get_turf(src))
return ..()
/obj/effect/mob_spawn/human/hermit/special(mob/living/carbon/human/new_spawn)
ADD_TRAIT(new_spawn,TRAIT_EXEMPT_HEALTH_EVENTS,GHOSTROLE_TRAIT)
//Broken rejuvenation pod: Spawns in animal hospitals in lavaland. Ghosts become disoriented interns and are advised to search for help.
/obj/effect/mob_spawn/human/doctor/alive/lavaland
name = "broken rejuvenation pod"
@@ -355,6 +360,9 @@
new/obj/structure/fluff/empty_sleeper/syndicate(get_turf(src))
..()
/obj/effect/mob_spawn/human/hotel_staff/special(mob/living/carbon/human/new_spawn)
ADD_TRAIT(new_spawn,TRAIT_EXEMPT_HEALTH_EVENTS,GHOSTROLE_TRAIT)
/obj/effect/mob_spawn/human/demonic_friend
name = "Essence of friendship"
desc = "Oh boy! Oh boy! A friend!"
@@ -597,3 +605,64 @@
/obj/effect/mob_spawn/human/pirate/gunner
rank = "Gunner"
/obj/effect/mob_spawn/human/ghostcafe
name = "Ghost Cafe Sleeper"
uses = -1
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
mob_name = "a ghost cafe visitor"
roundstart = FALSE
anchored = TRUE
density = FALSE
death = FALSE
assignedrole = "Ghost Cafe Visitor"
flavour_text = "Is this what life after death is like?"
skip_reentry_check = TRUE
banType = "ghostcafe"
/obj/effect/mob_spawn/human/ghostcafe/special(mob/living/carbon/human/new_spawn)
if(new_spawn.client)
new_spawn.client.prefs.copy_to(new_spawn)
var/datum/outfit/O = new /datum/outfit/ghostcafe()
O.equip(new_spawn, FALSE, new_spawn.client)
SSjob.equip_loadout(null, new_spawn, FALSE)
SSquirks.AssignQuirks(new_spawn, new_spawn.client, TRUE, TRUE, null, FALSE, new_spawn)
ADD_TRAIT(new_spawn,TRAIT_EXEMPT_HEALTH_EVENTS,GHOSTROLE_TRAIT)
/datum/outfit/ghostcafe
name = "ID, jumpsuit and shoes"
uniform = /obj/item/clothing/under/color/random
shoes = /obj/item/clothing/shoes/sneakers/black
id = /obj/item/card/id
r_hand = /obj/item/storage/box/syndie_kit/chameleon/ghostcafe
/datum/outfit/ghostcafe/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
..()
var/suited = !preference_source || preference_source.prefs.jumpsuit_style == PREF_SUIT
if (CONFIG_GET(flag/grey_assistants))
if(suited)
uniform = /obj/item/clothing/under/color/grey
else
uniform = /obj/item/clothing/under/skirt/color/grey
else
if(suited)
uniform = /obj/item/clothing/under/color/random
else
uniform = /obj/item/clothing/under/skirt/color/random
/obj/item/storage/box/syndie_kit/chameleon/ghostcafe
name = "ghost cafe costuming kit"
desc = "Look just the way you did in life - or better!"
/obj/item/storage/box/syndie_kit/chameleon/ghostcafe/PopulateContents() // Doesn't contain a PDA, for isolation reasons.
new /obj/item/clothing/under/chameleon(src)
new /obj/item/clothing/suit/chameleon(src)
new /obj/item/clothing/gloves/chameleon(src)
new /obj/item/clothing/shoes/chameleon(src)
new /obj/item/clothing/glasses/chameleon(src)
new /obj/item/clothing/head/chameleon(src)
new /obj/item/clothing/mask/chameleon(src)
new /obj/item/storage/backpack/chameleon(src)
new /obj/item/clothing/neck/cloak/chameleon(src)
+1 -1
View File
@@ -60,7 +60,7 @@
to_chat(user, "<span class='notice'>You build a floor.</span>")
var/turf/T = src.loc
if(isspaceturf(T))
T.PlaceOnTop(/turf/open/floor/plating)
T.PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
qdel(src)
return TRUE
return FALSE
@@ -93,5 +93,5 @@ GLOBAL_LIST_INIT(tendrils, list())
visible_message("<span class='boldannounce'>The tendril falls inward, the ground around it widening into a yawning chasm!</span>")
for(var/turf/T in range(2,src))
if(!T.density)
T.TerraformTurf(/turf/open/chasm/lavaland, /turf/open/chasm/lavaland)
T.TerraformTurf(/turf/open/chasm/lavaland, /turf/open/chasm/lavaland, flags = CHANGETURF_INHERIT_AIR)
qdel(src)
+2 -2
View File
@@ -102,13 +102,13 @@
/obj/structure/stairs/proc/force_open_above()
var/turf/open/openspace/T = get_step_multiz(get_turf(src), UP)
if(T && !istype(T))
T.ChangeTurf(/turf/open/openspace)
T.ChangeTurf(/turf/open/openspace, flags = CHANGETURF_INHERIT_AIR)
/obj/structure/stairs/proc/on_multiz_new(turf/source, dir)
if(dir == UP)
var/turf/open/openspace/T = get_step_multiz(get_turf(src), UP)
if(T && !istype(T))
T.ChangeTurf(/turf/open/openspace)
T.ChangeTurf(/turf/open/openspace, flags = CHANGETURF_INHERIT_AIR)
/obj/structure/stairs/intercept_zImpact(atom/movable/AM, levels = 1)
. = ..()
+6 -1
View File
@@ -10,4 +10,9 @@
/turf/baseturf_skipover/shuttle
name = "Shuttle baseturf skipover"
desc = "Acts as the bottom of the shuttle, if this isn't here the shuttle floor is broken through."
desc = "Acts as the bottom of the shuttle, if this isn't here the shuttle floor is broken through."
/turf/baseturf_bottom
name = "Z-level baseturf placeholder"
desc = "Marker for z-level baseturf, usually resolves to space."
baseturfs = /turf/baseturf_bottom
+23 -15
View File
@@ -1,6 +1,7 @@
// This is a list of turf types we dont want to assign to baseturfs unless through initialization or explicitly
GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
/turf/open/space,
/turf/baseturf_bottom
)))
/turf/proc/empty(turf_type=/turf/open/space, baseturf_type, list/ignore_typecache, flags)
@@ -56,12 +57,20 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
// Creates a new turf
// new_baseturfs can be either a single type or list of types, formated the same as baseturfs. see turf.dm
/turf/proc/ChangeTurf(path, list/new_baseturfs, flags)
if(!path)
return
if(path == /turf/open/space/basic)
// basic doesn't initialize and this will cause issues
// no warning though because this can happen naturaly as a result of it being built on top of
path = /turf/open/space
switch(path)
if(null)
return
if(/turf/baseturf_bottom)
path = SSmapping.level_trait(z, ZTRAIT_BASETURF) || /turf/open/space
if (!ispath(path))
path = text2path(path)
if (!ispath(path))
warning("Z-level [z] has invalid baseturf '[SSmapping.level_trait(z, ZTRAIT_BASETURF)]'")
path = /turf/open/space
if(/turf/open/space/basic)
// basic doesn't initialize and this will cause issues
// no warning though because this can happen naturaly as a result of it being built on top of
path = /turf/open/space
if(!GLOB.use_preloader && path == type && !(flags & CHANGETURF_FORCEOP)) // Don't no-op if the map loader requires it to be reconstructed
return src
if(flags & CHANGETURF_SKIP)
@@ -128,16 +137,15 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
/turf/open/ChangeTurf(path, list/new_baseturfs, flags)
if ((flags & CHANGETURF_INHERIT_AIR) && ispath(path, /turf/open))
SSair.remove_from_active(src)
var/stashed_air = air
air = null // so that it doesn't get deleted
var/datum/gas_mixture/stashed_air = new()
stashed_air.copy_from(air)
. = ..()
if (!. || . == src) // changeturf failed or didn't do anything
air = stashed_air
if (!.) // changeturf failed or didn't do anything
QDEL_NULL(stashed_air)
return
var/turf/open/newTurf = .
if (!istype(newTurf.air, /datum/gas_mixture/immutable/space))
QDEL_NULL(newTurf.air)
newTurf.air = stashed_air
newTurf.air.copy_from(stashed_air)
QDEL_NULL(stashed_air)
SSair.add_to_active(newTurf)
else
if(ispath(path,/turf/closed))
@@ -215,7 +223,7 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
newT.assemble_baseturfs(initial(fake_turf_type.baseturfs)) // The baseturfs list is created like roundstart
if(!length(newT.baseturfs))
newT.baseturfs = list(baseturfs)
newT.baseturfs -= newT.baseturfs & GLOB.blacklisted_automated_baseturfs
newT.baseturfs -= GLOB.blacklisted_automated_baseturfs
newT.baseturfs.Insert(1, old_baseturfs) // The old baseturfs are put underneath
return newT
if(!length(baseturfs))
@@ -315,5 +323,5 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
SSair.add_to_active(src)
/turf/proc/ReplaceWithLattice()
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
new /obj/structure/lattice(locate(x, y, z))
+1 -1
View File
@@ -23,7 +23,7 @@
icon = 'icons/turf/walls.dmi'
explosion_block = 50
/turf/closed/indestructible/TerraformTurf(path, defer_change = FALSE, ignore_air = FALSE)
/turf/closed/indestructible/TerraformTurf(path, new_baseturf, flags, defer_change = FALSE, ignore_air = FALSE)
return
/turf/closed/indestructible/acid_act(acidpwr, acid_volume, acid_id)
+1 -1
View File
@@ -58,7 +58,7 @@
/turf/open/indestructible/singularity_act()
return
/turf/open/indestructible/TerraformTurf(path, defer_change = FALSE, ignore_air = FALSE)
/turf/open/indestructible/TerraformTurf(path, new_baseturf, flags, defer_change = FALSE, ignore_air = FALSE)
return
/turf/open/indestructible/sound
+1 -1
View File
@@ -106,7 +106,7 @@
qdel(L)
playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
to_chat(user, "<span class='notice'>You build a floor.</span>")
PlaceOnTop(/turf/open/floor/plating)
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
else
to_chat(user, "<span class='warning'>You need one floor tile to build a floor!</span>")
else
+2 -2
View File
@@ -38,7 +38,7 @@
switch(passed_mode)
if(RCD_FLOORWALL)
to_chat(user, "<span class='notice'>You build a floor.</span>")
PlaceOnTop(/turf/open/floor/plating)
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
return TRUE
return FALSE
@@ -70,7 +70,7 @@
playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
to_chat(user, "<span class='notice'>You build a floor.</span>")
// Create a floor, which has this chasm underneath it
PlaceOnTop(/turf/open/floor/plating)
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
else
to_chat(user, "<span class='warning'>You need one floor tile to build a floor!</span>")
else
+13 -13
View File
@@ -62,29 +62,29 @@
if(severity != 1 && shielded && target != src)
return
if(target == src)
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
return
if(target != null)
severity = 3
switch(severity)
if(1)
ScrapeAway(2)
ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
if(2)
switch(pick(1,2;75,3))
if(1)
if(!length(baseturfs) || !ispath(baseturfs[baseturfs.len-1], /turf/open/floor))
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
ReplaceWithLattice()
else
ScrapeAway(2)
ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
if(prob(33))
new /obj/item/stack/sheet/metal(src)
if(2)
ScrapeAway(2)
ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
if(3)
if(prob(80))
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
else
break_tile()
hotspot_expose(1000,CELL_VOLUME)
@@ -103,9 +103,9 @@
/turf/open/floor/blob_act(obj/structure/blob/B)
return
/turf/open/floor/proc/update_icon()
/turf/open/floor/update_icon()
. = ..()
update_visuals()
return 1
/turf/open/floor/attack_paw(mob/user)
return attack_hand(user)
@@ -135,7 +135,7 @@
burnt = 1
/turf/open/floor/proc/make_plating()
return ScrapeAway()
return ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
/turf/open/floor/ChangeTurf(path, new_baseturf, flags)
if(!isfloorturf(src))
@@ -213,15 +213,15 @@
/turf/open/floor/narsie_act(force, ignore_mobs, probability = 20)
. = ..()
if(.)
ChangeTurf(/turf/open/floor/engine/cult)
ChangeTurf(/turf/open/floor/engine/cult, flags = CHANGETURF_INHERIT_AIR)
/turf/open/floor/ratvar_act(force, ignore_mobs)
. = ..()
if(.)
ChangeTurf(/turf/open/floor/clockwork)
ChangeTurf(/turf/open/floor/clockwork, flags = CHANGETURF_INHERIT_AIR)
/turf/open/floor/acid_melt()
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
/turf/open/floor/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
switch(the_rcd.mode)
@@ -267,7 +267,7 @@
A.autoclose = TRUE
return TRUE
if(RCD_DECONSTRUCT)
if(ScrapeAway() == src)
if(ScrapeAway(flags = CHANGETURF_INHERIT_AIR) == src)
return FALSE
to_chat(user, "<span class='notice'>You deconstruct [src].</span>")
return TRUE
+7 -7
View File
@@ -11,7 +11,7 @@
name = "plating"
icon_state = "plating"
intact = FALSE
baseturfs = /turf/open/space
baseturfs = /turf/baseturf_bottom
footstep = FOOTSTEP_PLATING
barefootstep = FOOTSTEP_HARD_BAREFOOT
clawfootstep = FOOTSTEP_HARD_CLAW
@@ -61,7 +61,7 @@
to_chat(user, "<span class='notice'>You begin reinforcing the floor...</span>")
if(do_after(user, 30, target = src))
if (R.get_amount() >= 2 && !istype(src, /turf/open/floor/engine))
PlaceOnTop(/turf/open/floor/engine)
PlaceOnTop(/turf/open/floor/engine, flags = CHANGETURF_INHERIT_AIR)
playsound(src, 'sound/items/deconstruct.ogg', 80, 1)
R.use(2)
to_chat(user, "<span class='notice'>You reinforce the floor.</span>")
@@ -76,7 +76,7 @@
var/obj/item/stack/tile/W = C
if(!W.use(1))
return
var/turf/open/floor/T = PlaceOnTop(W.turf_type)
var/turf/open/floor/T = PlaceOnTop(W.turf_type, flags = CHANGETURF_INHERIT_AIR)
if(istype(W, /obj/item/stack/tile/light)) //TODO: get rid of this ugly check somehow
var/obj/item/stack/tile/light/L = W
var/turf/open/floor/light/F = T
@@ -117,7 +117,7 @@
qdel(L)
to_chat(user, "<span class='notice'>You reinforce the foamed plating with tiling.</span>")
playsound(src, 'sound/weapons/Genhit.ogg', 50, TRUE)
ChangeTurf(/turf/open/floor/plating)
ChangeTurf(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
else
playsound(src, 'sound/weapons/tap.ogg', 100, TRUE) //The attack sound is muffled by the foam itself
user.changeNext_move(CLICK_CD_MELEE)
@@ -125,7 +125,7 @@
if(prob(I.force * 20 - 25))
user.visible_message("<span class='danger'>[user] smashes through [src]!</span>", \
"<span class='danger'>You smash through [src] with [I]!</span>")
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
else
to_chat(user, "<span class='danger'>You hit [src], to no effect!</span>")
@@ -136,13 +136,13 @@
/turf/open/floor/plating/foam/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode)
if(passed_mode == RCD_FLOORWALL)
to_chat(user, "<span class='notice'>You build a floor.</span>")
ChangeTurf(/turf/open/floor/plating)
ChangeTurf(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
return TRUE
return FALSE
/turf/open/floor/plating/foam/ex_act()
..()
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
/turf/open/floor/plating/foam/tool_act(mob/living/user, obj/item/I, tool_type)
return
@@ -76,11 +76,6 @@
for(var/obj/item/stack/ore/O in src)
SEND_SIGNAL(W, COMSIG_PARENT_ATTACKBY, O)
/turf/open/floor/plating/asteroid/singularity_act()
if(is_planet_level(z))
return ..()
ScrapeAway()
/turf/open/floor/plating/asteroid/ex_act(severity, target)
. = SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, severity, target)
contents_explosion(severity, target)
@@ -132,6 +127,7 @@
/turf/open/floor/plating/asteroid/airless
initial_gas_mix = AIRLESS_ATMOS
baseturfs = /turf/open/floor/plating/asteroid/airless
turf_type = /turf/open/floor/plating/asteroid/airless
@@ -43,7 +43,7 @@
return TRUE
if(floor_tile)
new floor_tile(src, 2)
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
return TRUE
/turf/open/floor/engine/acid_act(acidpwr, acid_volume)
@@ -56,23 +56,23 @@
if(severity != 1 && shielded && target != src)
return
if(target == src)
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
return
switch(severity)
if(1)
if(prob(80))
if(!length(baseturfs) || !ispath(baseturfs[baseturfs.len-1], /turf/open/floor))
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
ReplaceWithLattice()
else
ScrapeAway(2)
ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
else if(prob(50))
ScrapeAway(2)
ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
else
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
if(2)
if(prob(50))
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
/turf/open/floor/engine/singularity_pull(S, current_size)
..()
+1 -1
View File
@@ -54,7 +54,7 @@
switch(passed_mode)
if(RCD_FLOORWALL)
to_chat(user, "<span class='notice'>You build a floor.</span>")
PlaceOnTop(/turf/open/floor/plating)
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
return TRUE
return FALSE
@@ -202,15 +202,20 @@
return 1
return 0
/turf/closed/wall/r_wall/proc/update_icon()
/turf/closed/wall/r_wall/update_icon()
. = ..()
if(d_state != INTACT)
smooth = SMOOTH_FALSE
clear_smooth_overlays()
icon_state = "r_wall-[d_state]"
else
smooth = SMOOTH_TRUE
queue_smooth_neighbors(src)
queue_smooth(src)
/turf/closed/wall/r_wall/update_icon_state()
if(d_state != INTACT)
icon_state = "r_wall-[d_state]"
else
icon_state = "r_wall"
/turf/closed/wall/r_wall/singularity_pull(S, current_size)
+2 -2
View File
@@ -125,7 +125,7 @@
qdel(L)
playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
to_chat(user, "<span class='notice'>You build a floor.</span>")
PlaceOnTop(/turf/open/floor/plating)
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
else
to_chat(user, "<span class='warning'>You need one floor tile to build a floor!</span>")
else
@@ -212,7 +212,7 @@
switch(passed_mode)
if(RCD_FLOORWALL)
to_chat(user, "<span class='notice'>You build a floor.</span>")
PlaceOnTop(/turf/open/floor/plating)
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
return TRUE
return FALSE
+5 -2
View File
@@ -103,10 +103,13 @@
for(var/atom/movable/AM in src)
throw_atom(AM)
/turf/open/space/transit/proc/update_icon()
icon_state = "speedspace_ns_[get_transit_state(src)]"
/turf/open/space/transit/update_icon()
. = ..()
transform = turn(matrix(), get_transit_angle(src))
/turf/open/space/transit/update_icon_state()
icon_state = "speedspace_ns_[get_transit_state(src)]"
/proc/get_transit_state(turf/T)
var/p = 9
. = 1
+3 -3
View File
@@ -9,7 +9,7 @@
// A list will be created in initialization that figures out the baseturf's baseturf etc.
// In the case of a list it is sorted from bottom layer to top.
// This shouldn't be modified directly, use the helper procs.
var/list/baseturfs = /turf/open/space
var/list/baseturfs = /turf/baseturf_bottom
var/temperature = T20C
var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed
@@ -391,7 +391,7 @@
continue
if(O.invisibility == INVISIBILITY_MAXIMUM)
O.singularity_act()
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
return(2)
/turf/proc/can_have_cabling()
@@ -564,4 +564,4 @@
//Whatever happens after high temperature fire dies out or thermite reaction works.
//Should return new turf
/turf/proc/Melt()
return ScrapeAway()
return ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
+1
View File
@@ -108,6 +108,7 @@ GLOBAL_VAR(restart_counter)
GLOB.world_href_log = "[GLOB.log_directory]/hrefs.log"
GLOB.sql_error_log = "[GLOB.log_directory]/sql.log"
GLOB.world_qdel_log = "[GLOB.log_directory]/qdel.log"
GLOB.world_map_error_log = "[GLOB.log_directory]/map_errors.log"
GLOB.world_runtime_log = "[GLOB.log_directory]/runtime.log"
GLOB.query_debug_log = "[GLOB.log_directory]/query_debug.log"
GLOB.world_job_debug_log = "[GLOB.log_directory]/job_debug.log"