Merge remote-tracking branch 'Citadel-Station-13/master' into Garlic
This commit is contained in:
@@ -39,6 +39,37 @@
|
||||
|
||||
var/zfalling = FALSE
|
||||
|
||||
/// Either FALSE, [EMISSIVE_BLOCK_GENERIC], or [EMISSIVE_BLOCK_UNIQUE]
|
||||
var/blocks_emissive = FALSE
|
||||
///Internal holder for emissive blocker object, do not use directly use blocks_emissive
|
||||
var/atom/movable/emissive_blocker/em_block
|
||||
|
||||
|
||||
/atom/movable/Initialize(mapload)
|
||||
. = ..()
|
||||
switch(blocks_emissive)
|
||||
if(EMISSIVE_BLOCK_GENERIC)
|
||||
update_emissive_block()
|
||||
if(EMISSIVE_BLOCK_UNIQUE)
|
||||
render_target = ref(src)
|
||||
em_block = new(src, render_target)
|
||||
vis_contents += em_block
|
||||
|
||||
/atom/movable/Destroy()
|
||||
QDEL_NULL(em_block)
|
||||
return ..()
|
||||
|
||||
/atom/movable/proc/update_emissive_block()
|
||||
if(blocks_emissive != EMISSIVE_BLOCK_GENERIC)
|
||||
return
|
||||
if(length(managed_vis_overlays))
|
||||
for(var/a in managed_vis_overlays)
|
||||
var/obj/effect/overlay/vis/vs
|
||||
if(vs.plane == EMISSIVE_BLOCKER_PLANE)
|
||||
SSvis_overlays.remove_vis_overlay(src, list(vs))
|
||||
break
|
||||
SSvis_overlays.add_vis_overlay(src, icon, icon_state, EMISSIVE_BLOCKER_LAYER, EMISSIVE_BLOCKER_PLANE)
|
||||
|
||||
/atom/movable/proc/can_zFall(turf/source, levels = 1, turf/target, direction)
|
||||
if(!direction)
|
||||
direction = DOWN
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
antag_flag = ROLE_BLOODSUCKER
|
||||
false_report_weight = 1
|
||||
restricted_jobs = list("AI","Cyborg")
|
||||
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
|
||||
protected_jobs = list("Chaplain", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
|
||||
required_players = 20
|
||||
required_enemies = 2
|
||||
recommended_enemies = 4
|
||||
|
||||
@@ -88,13 +88,13 @@ GLOBAL_VAR(changeling_team_objective_type) //If this is not null, we hand our th
|
||||
user.underwear = chosen_prof.underwear
|
||||
user.undie_color = chosen_prof.undie_color
|
||||
user.undershirt = chosen_prof.undershirt
|
||||
user.shirt_color =chosen_prof.shirt_color
|
||||
user.shirt_color = chosen_prof.shirt_color
|
||||
user.socks = chosen_prof.socks
|
||||
user.socks_color =chosen_prof.socks_color
|
||||
|
||||
chosen_dna.transfer_identity(user, 1)
|
||||
user.updateappearance(mutcolor_update=1)
|
||||
user.update_body()
|
||||
user.update_body(TRUE)
|
||||
user.domutcheck()
|
||||
|
||||
//vars hackery. not pretty, but better than the alternative.
|
||||
|
||||
@@ -351,5 +351,5 @@ Credit where due:
|
||||
|
||||
/obj/item/paper/servant_primer/oui_getcontent(mob/target)
|
||||
if(!is_servant_of_ratvar(target) && !isobserver(target))
|
||||
return "<HTML><HEAD><TITLE>[name]</TITLE></HEAD><BODY>[stars(info)]<HR>[stamps]</BODY></HTML>"
|
||||
return "<HTML><HEAD><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><TITLE>[name]</TITLE></HEAD><BODY>[stars(info)]<HR>[stamps]</BODY></HTML>"
|
||||
return ..()
|
||||
|
||||
@@ -54,16 +54,16 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
// 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
|
||||
/// Target threat level right now. Events and antags will try to keep the round at this level.
|
||||
var/threat_level = 0
|
||||
/// Set at the beginning of the round. Spent by the mode to "purchase" rules.
|
||||
var/threat = 0
|
||||
/// The current antag threat. Recalculated every time a ruletype starts or ends.
|
||||
var/threat = 0
|
||||
/// Starting threat level, for things that increase it but can bring it back down.
|
||||
var/initial_threat_level = 0
|
||||
/// Things that cause a rolling threat adjustment to be displayed at roundend.
|
||||
var/list/threat_tallies = list()
|
||||
/// Running information about the threat. Can store text or datum entries.
|
||||
var/list/threat_log = list()
|
||||
var/list/threat_log = list()
|
||||
/// As above, but with info such as refunds.
|
||||
var/list/threat_log_verbose = list()
|
||||
/// List of roundstart rules used for selecting the rules.
|
||||
@@ -127,6 +127,8 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
var/only_ruleset_executed = FALSE
|
||||
/// Antags rolled by rules so far, to keep track of and discourage scaling past a certain ratio of crew/antags especially on lowpop.
|
||||
var/antags_rolled = 0
|
||||
// Arbitrary threat addition, for fudging purposes.
|
||||
var/added_threat = 50
|
||||
|
||||
/datum/game_mode/dynamic/New() // i have NO IDEA if this is the proper way to do this.
|
||||
..()
|
||||
@@ -150,11 +152,11 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
GLOB.dynamic_first_midround_delay_max = CONFIG_GET(number/dynamic_first_midround_delay_max)*600
|
||||
|
||||
/datum/game_mode/dynamic/admin_panel()
|
||||
var/list/dat = list("<html><head><title>Game Mode Panel</title></head><body><h1><B>Game Mode Panel</B></h1>")
|
||||
var/list/dat = list("<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Game Mode Panel</title></head><body><h1><B>Game Mode Panel</B></h1>")
|
||||
dat += "Dynamic Mode <a href='?_src_=vars;[HrefToken()];Vars=[REF(src)]'>\[VV\]</A><a href='?src=\ref[src];[HrefToken()]'>\[Refresh\]</A><BR>"
|
||||
dat += "Threat Level: <b>[threat_level]</b><br/>"
|
||||
dat += "Target threat: <b>[threat_level]</b><br/>"
|
||||
|
||||
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 += "Current threat: <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/>"
|
||||
@@ -172,9 +174,9 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
else
|
||||
dat += "none.<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>"
|
||||
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)/10] 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)/10] 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)/10] seconds"] <a href='?src=\ref[src];[HrefToken()];forceevent=1'>\[Now!\]</a><BR>"
|
||||
usr << browse(dat.Join(), "window=gamemode_panel;size=500x500")
|
||||
|
||||
/datum/game_mode/dynamic/Topic(href, href_list)
|
||||
@@ -261,11 +263,11 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
. += "<b>Uncharted Space</b></center><BR>"
|
||||
. += "Congratulations and thank you for participating in the NT 'Frontier' space program! Your station is actively orbiting a high value system far from the nearest support stations. Little is known about your region of space, and the opportunity to encounter the unknown invites greater glory. You are encouraged to elevate security as necessary to protect Nanotrasen assets."
|
||||
set_security_level(SEC_LEVEL_BLUE)
|
||||
if(80 to 99)
|
||||
if(80 to 95)
|
||||
. += "<b>Black Orbit</b></center><BR>"
|
||||
. += "As part of a mandatory security protocol, we are required to inform you that as a result of your orbital pattern directly behind an astrological body (oriented from our nearest observatory), your station will be under decreased monitoring and support. It is anticipated that your extreme location and decreased surveillance could pose security risks. Avoid unnecessary risks and attempt to keep your station in one piece."
|
||||
set_security_level(SEC_LEVEL_AMBER)
|
||||
if(100)
|
||||
if(96 to 100)
|
||||
. += "<b>Impending Doom</b></center><BR>"
|
||||
. += "Your station is somehow in the middle of hostile territory, in clear view of any enemy of the corporation. Your likelihood to survive is low, and station destruction is expected and almost inevitable. Secure any sensitive material and neutralize any enemy you will come across. It is important that you at least try to maintain the station.<BR>"
|
||||
. += "Good luck."
|
||||
@@ -330,7 +332,6 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
|
||||
peaceful_percentage = round(LORENTZ_CUMULATIVE_DISTRIBUTION(relative_threat, GLOB.dynamic_curve_centre, GLOB.dynamic_curve_width), 0.01)*100
|
||||
|
||||
threat = threat_level
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Initial threat level")
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",GLOB.dynamic_curve_centre,"Curve centre")
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",GLOB.dynamic_curve_width,"Curve width")
|
||||
@@ -340,9 +341,10 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
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].")
|
||||
message_admins("Dynamic mode parameters for the round:\n\
|
||||
Storyteller is [storyteller.name].\n\
|
||||
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"].\n\
|
||||
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].")
|
||||
log_game("DYNAMIC: Dynamic mode parameters for the round:")
|
||||
log_game("DYNAMIC: 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"].")
|
||||
log_game("DYNAMIC: 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].")
|
||||
@@ -404,7 +406,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
|
||||
/datum/game_mode/dynamic/post_setup(report)
|
||||
update_playercounts()
|
||||
|
||||
|
||||
for(var/datum/dynamic_ruleset/roundstart/rule in executed_rules)
|
||||
addtimer(CALLBACK(src, /datum/game_mode/dynamic/.proc/execute_roundstart_rule, rule), rule.delay)
|
||||
..()
|
||||
@@ -463,7 +465,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
log_game("DYNAMIC: Additional ruleset picked successfully, now [executed_rules.len] picked. [extra_rulesets_amount] remaining.")
|
||||
else
|
||||
|
||||
if(threat >= 50)
|
||||
if(threat_level >= 50)
|
||||
message_admins("DYNAMIC: Picking first roundstart ruleset failed. You should report this.")
|
||||
log_game("DYNAMIC: Picking first roundstart ruleset failed. drafted_rules.len = [drafted_rules.len] and threat = [threat]/[threat_level]")
|
||||
return FALSE
|
||||
@@ -509,10 +511,9 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
drafted_rules -= starting_rule
|
||||
|
||||
starting_rule.trim_candidates()
|
||||
var/added_threat = starting_rule.scale_up(extra_rulesets_amount, threat)
|
||||
starting_rule.scale_up(extra_rulesets_amount, threat)
|
||||
if (starting_rule.pre_execute())
|
||||
spend_threat(starting_rule.cost + added_threat)
|
||||
log_threat("[starting_rule.ruletype] - <b>[starting_rule.name]</b> -[starting_rule.cost + starting_rule.scaled_times * starting_rule.scaling_cost] threat", verbose = TRUE)
|
||||
log_threat("[starting_rule.ruletype] - <b>[starting_rule.name]</b> [starting_rule.cost + starting_rule.scaled_times * starting_rule.scaling_cost] threat", verbose = TRUE)
|
||||
if(starting_rule.flags & HIGHLANDER_RULESET)
|
||||
highlander_executed = TRUE
|
||||
else if(starting_rule.flags & ONLY_RULESET)
|
||||
@@ -534,8 +535,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
/datum/game_mode/dynamic/proc/execute_roundstart_rule(sent_rule)
|
||||
var/datum/dynamic_ruleset/rule = sent_rule
|
||||
if(rule.execute())
|
||||
if(rule.persistent)
|
||||
current_rules += rule
|
||||
current_rules += rule
|
||||
SSblackbox.record_feedback("associative","dynamic_rulesets",1,rule.get_blackbox_info())
|
||||
return TRUE
|
||||
rule.clean_up() // Refund threat, delete teams and so on.
|
||||
@@ -607,8 +607,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
if ((forced || (new_rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && new_rule.cost <= threat)))
|
||||
new_rule.trim_candidates()
|
||||
if (new_rule.ready(forced))
|
||||
spend_threat(new_rule.cost)
|
||||
log_threat("[new_rule.ruletype] - <b>[new_rule.name]</b> -[new_rule.cost] threat", verbose = TRUE)
|
||||
log_threat("[new_rule.ruletype] - <b>[new_rule.name]</b> [new_rule.cost] threat", verbose = TRUE)
|
||||
if (new_rule.execute()) // This should never fail since ready() returned 1
|
||||
if(new_rule.flags & HIGHLANDER_RULESET)
|
||||
highlander_executed = TRUE
|
||||
@@ -617,8 +616,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
log_game("DYNAMIC: Making a call to a specific ruleset...[new_rule.name]!")
|
||||
SSblackbox.record_feedback("associative","dynamic_rulesets",1,new_rule.get_blackbox_info())
|
||||
executed_rules += new_rule
|
||||
if (new_rule.persistent)
|
||||
current_rules += new_rule
|
||||
current_rules += new_rule
|
||||
return TRUE
|
||||
else if (forced)
|
||||
log_game("DYNAMIC: The ruleset [new_rule.name] couldn't be executed due to lack of eligible players.")
|
||||
@@ -629,8 +627,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
var/datum/dynamic_ruleset/rule = sent_rule
|
||||
if (rule.execute())
|
||||
log_game("DYNAMIC: Injected a [rule.ruletype == "latejoin" ? "latejoin" : "midround"] ruleset [rule.name].")
|
||||
spend_threat(rule.cost)
|
||||
log_threat("[rule.ruletype] [rule.name] spent [rule.cost]", verbose = TRUE)
|
||||
log_threat("[rule.ruletype] [rule.name] added [rule.cost]", verbose = TRUE)
|
||||
if(rule.flags & HIGHLANDER_RULESET)
|
||||
highlander_executed = TRUE
|
||||
else if(rule.flags & ONLY_RULESET)
|
||||
@@ -649,7 +646,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
return FALSE
|
||||
|
||||
/datum/game_mode/dynamic/process()
|
||||
if (pop_last_updated < world.time - (60 SECONDS))
|
||||
if (pop_last_updated < world.time - (120 SECONDS))
|
||||
pop_last_updated = world.time
|
||||
update_playercounts()
|
||||
|
||||
@@ -658,9 +655,9 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
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
|
||||
@@ -717,7 +714,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
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)
|
||||
|
||||
threat = storyteller.calculate_threat() + added_threat
|
||||
/// Removes type from the list
|
||||
/datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type)
|
||||
for(var/I in type_list)
|
||||
@@ -767,25 +764,23 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Successful latejoin injections")
|
||||
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)
|
||||
threat = min(threat_level,threat+regain)
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",regain,"Refunded threat")
|
||||
log_threat("[regain] refunded. Threat is now [threat].", verbose = TRUE)
|
||||
|
||||
/// Generate threat and increase the threat_level if it goes beyond, capped at 100
|
||||
/// Increase the threat level.
|
||||
/datum/game_mode/dynamic/proc/create_threat(gain)
|
||||
threat = min(100, threat+gain)
|
||||
if(threat > threat_level)
|
||||
threat_level = threat
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",gain,"Created threat")
|
||||
log_threat("[gain] created. Threat is now [threat] and threat level is now [threat_level].", verbose = TRUE)
|
||||
threat_level += gain
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",gain,"Created threat level")
|
||||
log_threat("[gain] created. Threat level is now [threat_level].", verbose = TRUE)
|
||||
|
||||
/// Expend threat, can't fall under 0.
|
||||
/// Decrease the threat level.
|
||||
/datum/game_mode/dynamic/proc/remove_threat(loss)
|
||||
threat_level -= loss
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",loss,"Removed threat level")
|
||||
log_threat("[loss] removed. Threat level is now [threat_level].", verbose = TRUE)
|
||||
|
||||
/// Fill up more of the threat level.
|
||||
/datum/game_mode/dynamic/proc/spend_threat(cost)
|
||||
threat = max(threat-cost,0)
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",cost,"Threat spent")
|
||||
log_threat("[cost] spent. Threat is now [threat].", verbose = TRUE)
|
||||
added_threat += cost
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",cost,"Threat added")
|
||||
log_threat("[cost] added. Threat is now [threat].", verbose = TRUE)
|
||||
|
||||
/// Turns the value generated by lorentz distribution to threat value between 0 and 100.
|
||||
/datum/game_mode/dynamic/proc/lorentz_to_threat(x)
|
||||
|
||||
@@ -174,8 +174,8 @@
|
||||
|
||||
/// This is called if persistent variable is true everytime SSTicker ticks.
|
||||
/datum/dynamic_ruleset/proc/rule_process()
|
||||
return
|
||||
|
||||
return TRUE
|
||||
|
||||
/// Called on game mode pre_setup for roundstart rulesets.
|
||||
/// Do everything you need to do before job is assigned here.
|
||||
/// IMPORTANT: ASSIGN special_role HERE
|
||||
@@ -201,8 +201,7 @@
|
||||
/// Runs from gamemode process() if ruleset fails to start, like delayed rulesets not getting valid candidates.
|
||||
/// This one only handles refunding the threat, override in ruleset to clean up the rest.
|
||||
/datum/dynamic_ruleset/proc/clean_up()
|
||||
mode.refund_threat(cost + (scaled_times * scaling_cost))
|
||||
mode.log_threat("[ruletype] [name] refunded [cost + (scaled_times * scaling_cost)]",verbose=TRUE)
|
||||
return
|
||||
|
||||
/// Gets weight of the ruleset
|
||||
/// Note that this decreases weight if repeatable is TRUE and repeatable_weight_decrease is higher than 0
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
if (M.mind && M.mind.assigned_role && (M.mind.assigned_role in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role in restricted_roles)))
|
||||
job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it
|
||||
|
||||
var/threat = round(mode.threat_level/10)
|
||||
var/threat = CLAMP(round(mode.threat_level/10),1,10)
|
||||
if (job_check < required_enemies[threat])
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough enemy roles")
|
||||
return FALSE
|
||||
@@ -216,7 +216,7 @@
|
||||
var/player_count = mode.current_players[CURRENT_LIVING_PLAYERS].len
|
||||
var/antag_count = mode.current_players[CURRENT_LIVING_ANTAGS].len
|
||||
var/max_traitors = round(player_count / 10) + 1
|
||||
if ((antag_count < max_traitors) && prob(mode.threat_level))//adding traitors if the antag population is getting low
|
||||
if ((antag_count < max_traitors) && prob(min(100,mode.threat_level)))//adding traitors if the antag population is getting low
|
||||
return ..()
|
||||
else
|
||||
return FALSE
|
||||
@@ -315,7 +315,6 @@
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/wizard
|
||||
name = "Wizard"
|
||||
config_tag = "midround_wizard"
|
||||
persistent = TRUE
|
||||
antag_datum = /datum/antagonist/wizard
|
||||
antag_flag = ROLE_WIZARD
|
||||
enemy_roles = list("Security Officer","Detective","Head of Security", "Captain")
|
||||
@@ -344,7 +343,6 @@
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/wizard/rule_process() // i can literally copy this from are_special_antags_dead it's great
|
||||
if(isliving(wizard.current) && wizard.current.stat!=DEAD)
|
||||
return FALSE
|
||||
|
||||
for(var/obj/item/phylactery/P in GLOB.poi_list) //TODO : IsProperlyDead()
|
||||
if(P.mind && P.mind.has_antag_datum(/datum/antagonist/wizard))
|
||||
return FALSE
|
||||
@@ -417,7 +415,7 @@
|
||||
required_candidates = 1
|
||||
blocking_rules = list(/datum/dynamic_ruleset/roundstart/clockcult)
|
||||
weight = 4
|
||||
cost = 10
|
||||
cost = 20
|
||||
requirements = list(101,101,101,80,60,50,50,50,50,50)
|
||||
high_population_requirement = 50
|
||||
repeatable = TRUE
|
||||
@@ -666,9 +664,6 @@
|
||||
Mind.transfer_to(Ninja)
|
||||
var/datum/antagonist/ninja/ninjadatum = new
|
||||
ninjadatum.helping_station = pick(TRUE,FALSE)
|
||||
if(ninjadatum.helping_station)
|
||||
mode.refund_threat(cost+5)
|
||||
mode.log_threat("Ninja was helping station; [cost+5] cost refunded.")
|
||||
Mind.add_antag_datum(ninjadatum)
|
||||
|
||||
if(Ninja.mind != Mind) //something has gone wrong!
|
||||
|
||||
@@ -802,11 +802,10 @@
|
||||
/datum/dynamic_ruleset/roundstart/bloodsucker
|
||||
name = "Bloodsuckers"
|
||||
config_tag = "bloodsucker"
|
||||
persistent = TRUE
|
||||
antag_flag = ROLE_BLOODSUCKER
|
||||
antag_datum = ANTAG_DATUM_BLOODSUCKER
|
||||
minimum_required_age = 0
|
||||
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
|
||||
protected_roles = list("Chaplain", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
|
||||
restricted_roles = list("Cyborg", "AI")
|
||||
required_candidates = 1
|
||||
weight = 2
|
||||
|
||||
@@ -5,17 +5,19 @@
|
||||
var/list/property_weights = list() // See below.
|
||||
var/curve_centre = 0 // As GLOB.dynamic_curve_centre.
|
||||
var/curve_width = 1.8 // As GLOB.dynamic_curve_width.
|
||||
var/forced_threat_level = -1
|
||||
var/forced_threat_level = -1 // As GLOB.dynamic_forced_threat_level
|
||||
/*
|
||||
NO_ASSASSIN: Will not have permanent assassination targets.
|
||||
WAROPS_ALWAYS_ALLOWED: Can always do warops, regardless of threat level.
|
||||
USE_PREF_WEIGHTS: Will use peoples' preferences to change the threat centre.
|
||||
FORCE_IF_WON: If this mode won the vote, forces it
|
||||
*/
|
||||
var/flags = 0
|
||||
var/dead_player_weight = 1 // How much dead players matter for threat calculation
|
||||
var/weight = 3 // Weights for randomly picking storyteller. Multiplied by score after voting.
|
||||
var/event_frequency_lower = 6 MINUTES // How rare events will be, at least.
|
||||
var/event_frequency_upper = 20 MINUTES // How rare events will be, at most.
|
||||
var/pop_antag_ratio = 5 // How many non-antags there should be vs antags.
|
||||
var/min_players = -1 // How many players are required for this one to start.
|
||||
var/datum/game_mode/dynamic/mode = null // Cached as soon as it's made, by dynamic.
|
||||
|
||||
/**
|
||||
@@ -39,6 +41,29 @@ Property weights are:
|
||||
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/calculate_threat()
|
||||
var/threat = 0
|
||||
for(var/datum/antagonist/A in GLOB.antagonists)
|
||||
if(A?.owner?.current && A.owner.current.stat != DEAD)
|
||||
threat += A.threat()
|
||||
for(var/r in SSevents.running)
|
||||
var/datum/round_event/R = r
|
||||
threat += R.threat()
|
||||
for(var/mob/living/simple_animal/hostile/H in GLOB.mob_living_list)
|
||||
var/turf/T = get_turf(H)
|
||||
if(H.stat != DEAD && is_station_level(T.z) && !("Station" in H.faction))
|
||||
threat += H.threat()
|
||||
for (var/mob/M in mode.current_players[CURRENT_LIVING_PLAYERS])
|
||||
if (M?.mind?.assigned_role && M.stat != DEAD)
|
||||
var/datum/job/J = SSjob.GetJob(M.mind.assigned_role)
|
||||
if(J)
|
||||
if(length(M.mind.antag_datums))
|
||||
threat += J.GetThreat()
|
||||
else
|
||||
threat -= J.GetThreat()
|
||||
threat += (mode.current_players[CURRENT_DEAD_PLAYERS].len)*dead_player_weight
|
||||
return round(threat,0.1)
|
||||
|
||||
/datum/dynamic_storyteller/proc/do_process()
|
||||
return
|
||||
|
||||
@@ -83,30 +108,14 @@ Property weights are:
|
||||
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(pop_antag_ratio,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))
|
||||
var/threat_perc = mode.threat/mode.threat_level
|
||||
|
||||
return round(max(0,100*(1-(threat_perc*threat_perc*threat_perc))))
|
||||
|
||||
/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
|
||||
if (rule.acceptable(mode.roundstart_pop_ready, mode.threat_level)) // If we got the population and threat required
|
||||
rule.candidates = mode.candidates.Copy()
|
||||
rule.trim_candidates()
|
||||
if (rule.ready() && rule.candidates.len > 0)
|
||||
@@ -114,32 +123,41 @@ Property weights are:
|
||||
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
|
||||
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)
|
||||
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
|
||||
// Classic secret : only autotraitor/minor roles
|
||||
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
|
||||
continue
|
||||
rule.trim_candidates()
|
||||
var/cost_difference = abs(rule.cost-(mode.threat_level-mode.threat))
|
||||
/* Basically, the closer the cost is to the current threat-level-away-from-threat, the more likely it is to
|
||||
pick this particular ruleset.
|
||||
Let's use a toy example: there's 60 threat level and 10 threat spent.
|
||||
We want to pick a ruleset that's close to that, so we run the below equation, on two rulesets.
|
||||
Ruleset 1 has 30 cost, ruleset 2 has 5 cost.
|
||||
When we do the math, ruleset 1's threat_weight is 0.538, and ruleset 2's is 0.238, meaning ruleset 1
|
||||
is 2.26 times as likely to be picked, all other things considered.
|
||||
Of course, we don't want it to GUARANTEE the closest, that's no fun, so it's just a weight.
|
||||
*/
|
||||
var/threat_weight = 1-abs(1-LOGISTIC_FUNCTION(2,0.05,cost_difference,0))
|
||||
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")
|
||||
drafted_rules[rule] = round(((rule.get_weight() * property_weight)*rule.weight_mult*threat_weight)*1000,1)
|
||||
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)
|
||||
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level - mode.threat))
|
||||
// Classic secret : only autotraitor/minor roles
|
||||
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
|
||||
continue
|
||||
@@ -150,56 +168,54 @@ Property weights are:
|
||||
|
||||
rule.candidates = list(newPlayer)
|
||||
rule.trim_candidates()
|
||||
var/cost_difference = abs(rule.cost-(mode.threat_level-mode.threat))
|
||||
var/threat_weight = 1-abs(1-(LOGISTIC_FUNCTION(2,0.05,cost_difference,0)))
|
||||
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")
|
||||
drafted_rules[rule] = round(((rule.get_weight() * property_weight)*rule.weight_mult*threat_weight)*1000,1)
|
||||
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.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && (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
|
||||
/datum/dynamic_storyteller/chaotic
|
||||
name = "Chaotic"
|
||||
config_tag = "chaotic"
|
||||
curve_centre = 10
|
||||
desc = "High chaos modes. Revs, wizard, clock cult. Multiple antags at once. Chaos is kept up all round."
|
||||
property_weights = list("extended" = -1, "chaos" = 10)
|
||||
property_weights = list("extended" = -1, "chaos" = 1)
|
||||
weight = 1
|
||||
event_frequency_lower = 2 MINUTES
|
||||
event_frequency_upper = 10 MINUTES
|
||||
flags = WAROPS_ALWAYS_ALLOWED
|
||||
pop_antag_ratio = 4
|
||||
flags = WAROPS_ALWAYS_ALLOWED | FORCE_IF_WON
|
||||
min_players = 40
|
||||
var/refund_cooldown = 0
|
||||
|
||||
/datum/dynamic_storyteller/cowabunga/get_midround_cooldown()
|
||||
/datum/dynamic_storyteller/chaotic/do_process()
|
||||
if(refund_cooldown < world.time)
|
||||
mode.create_threat(20)
|
||||
mode.log_threat("Chaotic storyteller ramped up the chaos. Threat level is now [mode.threat_level].")
|
||||
refund_cooldown = world.time + 20 MINUTES
|
||||
|
||||
/datum/dynamic_storyteller/chaotic/get_midround_cooldown()
|
||||
return ..() / 4
|
||||
|
||||
/datum/dynamic_storyteller/cowabunga/get_latejoin_cooldown()
|
||||
/datum/dynamic_storyteller/chaotic/get_latejoin_cooldown()
|
||||
return ..() / 4
|
||||
|
||||
/datum/dynamic_storyteller/cowabunga/do_process()
|
||||
if(refund_cooldown < world.time)
|
||||
mode.refund_threat(40)
|
||||
mode.log_threat("Chaotic storyteller refunded 40 threat. Threat is now [mode.threat].")
|
||||
refund_cooldown = world.time + 1200 SECONDS
|
||||
|
||||
/datum/dynamic_storyteller/team
|
||||
name = "Teamwork"
|
||||
config_tag = "teamwork"
|
||||
@@ -207,6 +223,7 @@ Property weights are:
|
||||
curve_centre = 2
|
||||
curve_width = 1.5
|
||||
weight = 2
|
||||
min_players = 30
|
||||
flags = WAROPS_ALWAYS_ALLOWED
|
||||
property_weights = list("valid" = 3, "trust" = 5)
|
||||
|
||||
@@ -223,23 +240,93 @@ Property weights are:
|
||||
flags = WAROPS_ALWAYS_ALLOWED
|
||||
property_weights = list("valid" = 1, "conversion" = 20)
|
||||
|
||||
/datum/dynamic_storyteller/classic
|
||||
/datum/dynamic_storyteller/random
|
||||
name = "Random"
|
||||
config_tag = "random"
|
||||
desc = "No special weights attached. Anything goes."
|
||||
weight = 4
|
||||
curve_width = 4
|
||||
pop_antag_ratio = 7
|
||||
flags = USE_PREF_WEIGHTS
|
||||
weight = 1
|
||||
desc = "No weighting at all; every ruleset has the same chance of happening. Cooldowns vary wildly. As random as it gets."
|
||||
forced_threat_level = 100
|
||||
|
||||
/datum/dynamic_storyteller/random/get_midround_cooldown()
|
||||
return rand(GLOB.dynamic_midround_delay_min/2, GLOB.dynamic_midround_delay_max*2)
|
||||
|
||||
/datum/dynamic_storyteller/random/get_event_cooldown()
|
||||
return rand(GLOB.dynamic_event_delay_min/2, GLOB.dynamic_event_delay_max*2)
|
||||
|
||||
/datum/dynamic_storyteller/random/get_latejoin_cooldown()
|
||||
return rand(GLOB.dynamic_latejoin_delay_min/2, GLOB.dynamic_latejoin_delay_max*2)
|
||||
|
||||
/datum/dynamic_storyteller/random/get_injection_chance()
|
||||
return 50 // i would do rand(0,100) but it's actually the same thing when you do the math
|
||||
|
||||
/datum/dynamic_storyteller/random/calculate_threat()
|
||||
return 0 // what IS threat
|
||||
|
||||
/datum/dynamic_storyteller/random/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)) // If we got the population and threat required
|
||||
rule.candidates = mode.candidates.Copy()
|
||||
rule.trim_candidates()
|
||||
if (rule.ready() && rule.candidates.len > 0)
|
||||
drafted_rules[rule] = 1
|
||||
return drafted_rules
|
||||
|
||||
/datum/dynamic_storyteller/random/midround_draft()
|
||||
var/list/drafted_rules = list()
|
||||
for (var/datum/dynamic_ruleset/midround/rule in mode.midround_rules)
|
||||
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
|
||||
// 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())
|
||||
drafted_rules[rule] = 1
|
||||
return drafted_rules
|
||||
|
||||
/datum/dynamic_storyteller/random/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))
|
||||
// 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())
|
||||
drafted_rules[rule] = 1
|
||||
return drafted_rules
|
||||
|
||||
/datum/dynamic_storyteller/random/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))
|
||||
if(rule.ready())
|
||||
drafted_rules[rule] = 1
|
||||
return drafted_rules
|
||||
|
||||
/datum/dynamic_storyteller/story
|
||||
name = "Story"
|
||||
config_tag = "story"
|
||||
desc = "Antags with options for loadouts and gimmicks. Traitor, wizard, nukies."
|
||||
desc = "Antags with options for loadouts and gimmicks. Traitor, wizard, nukies. Has a buildup-climax-falling action threat curve."
|
||||
weight = 2
|
||||
curve_width = 2
|
||||
pop_antag_ratio = 7
|
||||
property_weights = list("story_potential" = 10)
|
||||
property_weights = list("story_potential" = 2)
|
||||
|
||||
|
||||
/datum/dynamic_storyteller/story/do_process()
|
||||
var/current_time = (world.time / SSautotransfer.targettime)*180
|
||||
mode.threat_level = round(mode.initial_threat_level*(sin(current_time)+0.5),0.1)
|
||||
|
||||
/datum/dynamic_storyteller/classic
|
||||
name = "Classic"
|
||||
config_tag = "classic"
|
||||
desc = "No special antagonist weights. Good variety, but not like random. Uses your chaos preference to weight."
|
||||
flags = USE_PREF_WEIGHTS
|
||||
|
||||
/datum/dynamic_storyteller/suspicion
|
||||
name = "Intrigue"
|
||||
@@ -247,8 +334,8 @@ Property weights are:
|
||||
desc = "Antags that instill distrust in the crew. Traitors, bloodsuckers."
|
||||
weight = 2
|
||||
curve_width = 2
|
||||
pop_antag_ratio = 7
|
||||
property_weights = list("trust" = -5)
|
||||
dead_player_weight = 2
|
||||
property_weights = list("trust" = -3)
|
||||
|
||||
/datum/dynamic_storyteller/liteextended
|
||||
name = "Calm"
|
||||
@@ -256,10 +343,10 @@ Property weights are:
|
||||
desc = "Low-chaos round. Few antags. No conversion."
|
||||
curve_centre = -3
|
||||
curve_width = 0.5
|
||||
flags = NO_ASSASSIN
|
||||
flags = NO_ASSASSIN | FORCE_IF_WON
|
||||
weight = 1
|
||||
pop_antag_ratio = 10
|
||||
property_weights = list("extended" = 1, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10)
|
||||
dead_player_weight = 5
|
||||
property_weights = list("extended" = 2, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10)
|
||||
|
||||
/datum/dynamic_storyteller/no_antag
|
||||
name = "Extended"
|
||||
@@ -267,7 +354,7 @@ Property weights are:
|
||||
desc = "No standard antags. Threatening events may still spawn."
|
||||
curve_centre = -5
|
||||
curve_width = 0.5
|
||||
flags = NO_ASSASSIN
|
||||
flags = NO_ASSASSIN | FORCE_IF_WON
|
||||
weight = 1
|
||||
property_weights = list("extended" = 2)
|
||||
|
||||
|
||||
@@ -323,7 +323,7 @@
|
||||
var/free_tickets = CONFIG_GET(number/default_antag_tickets)
|
||||
//Max extra tickets you can use
|
||||
var/additional_tickets = CONFIG_GET(number/max_tickets_per_roll)
|
||||
|
||||
|
||||
var/list/ckey_to_mind = list() //this is admittedly shitcode but I'm webediting
|
||||
var/list/prev_tickets = SSpersistence.antag_rep //cache for hyper-speed in theory. how many tickets someone has stored
|
||||
var/list/curr_tickets = list() //how many tickets someone has for *this* antag roll, so with the free tickets
|
||||
@@ -337,7 +337,7 @@
|
||||
continue
|
||||
curr_tickets[mind_ckey] = amount
|
||||
ckey_to_mind[mind_ckey] = M //make sure we can look them up after picking
|
||||
|
||||
|
||||
if(!return_list) //return a single guy
|
||||
var/ckey
|
||||
if(length(curr_tickets))
|
||||
@@ -584,7 +584,7 @@
|
||||
//By default nuke just ends the round
|
||||
/datum/game_mode/proc/OnNukeExplosion(off_station)
|
||||
nuke_off_station = off_station
|
||||
if(off_station < 2)
|
||||
if(!off_station)
|
||||
station_was_nuked = TRUE //Will end the round on next check.
|
||||
|
||||
//Additional report section in roundend report
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
can_coexist_with_others = FALSE
|
||||
job_rank = ROLE_GANG
|
||||
antagpanel_category = "Gang"
|
||||
threat = 2
|
||||
var/hud_type = "gangster"
|
||||
var/message_name = "Gangster"
|
||||
var/datum/team/gang/gang
|
||||
@@ -167,6 +168,7 @@
|
||||
name = "Gang boss"
|
||||
hud_type = "gang_boss"
|
||||
message_name = "Leader"
|
||||
threat = 10
|
||||
|
||||
/datum/antagonist/gang/boss/on_gain()
|
||||
..()
|
||||
@@ -474,4 +476,4 @@
|
||||
|
||||
|
||||
#undef MAXIMUM_RECALLS
|
||||
#undef INFLUENCE_INTERVAL
|
||||
#undef INFLUENCE_INTERVAL
|
||||
|
||||
@@ -240,10 +240,10 @@
|
||||
to_chat(AI, "<b>[U]</b> holds <a href='?_src_=usr;show_paper=1;'>\a [itemname]</a> up to one of your cameras ...")
|
||||
else
|
||||
to_chat(AI, "<b><a href='?src=[REF(AI)];track=[html_encode(U.name)]'>[U]</a></b> holds <a href='?_src_=usr;show_paper=1;'>\a [itemname]</a> up to one of your cameras ...")
|
||||
AI.last_paper_seen = "<HTML><HEAD><TITLE>[itemname]</TITLE></HEAD><BODY><TT>[info]</TT></BODY></HTML>"
|
||||
AI.last_paper_seen = "<HTML><HEAD><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><TITLE>[itemname]</TITLE></HEAD><BODY><TT>[info]</TT></BODY></HTML>"
|
||||
else if (O.client && O.client.eye == src)
|
||||
to_chat(O, "[U] holds \a [itemname] up to one of the cameras ...")
|
||||
O << browse(text("<HTML><HEAD><TITLE>[]</TITLE></HEAD><BODY><TT>[]</TT></BODY></HTML>", itemname, info), text("window=[]", itemname))
|
||||
O << browse(text("<HTML><HEAD<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>><TITLE>[]</TITLE></HEAD><BODY><TT>[]</TT></BODY></HTML>", itemname, info), text("window=[]", itemname))
|
||||
return
|
||||
|
||||
else if(istype(I, /obj/item/camera_bug))
|
||||
|
||||
@@ -180,6 +180,7 @@
|
||||
|
||||
//Get the clone body ready
|
||||
maim_clone(H)
|
||||
ADD_TRAIT(H, TRAIT_MUTATION_STASIS, CLONING_POD_TRAIT)
|
||||
ADD_TRAIT(H, TRAIT_STABLEHEART, CLONING_POD_TRAIT)
|
||||
ADD_TRAIT(H, TRAIT_STABLELIVER, CLONING_POD_TRAIT)
|
||||
ADD_TRAIT(H, TRAIT_EMOTEMUTE, CLONING_POD_TRAIT)
|
||||
@@ -369,6 +370,7 @@
|
||||
return
|
||||
|
||||
REMOVE_TRAIT(mob_occupant, TRAIT_STABLEHEART, CLONING_POD_TRAIT)
|
||||
REMOVE_TRAIT(mob_occupant, TRAIT_MUTATION_STASIS, CLONING_POD_TRAIT)
|
||||
REMOVE_TRAIT(mob_occupant, TRAIT_STABLELIVER, CLONING_POD_TRAIT)
|
||||
REMOVE_TRAIT(mob_occupant, TRAIT_EMOTEMUTE, CLONING_POD_TRAIT)
|
||||
REMOVE_TRAIT(mob_occupant, TRAIT_MUTE, CLONING_POD_TRAIT)
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
if(stat & BROKEN)
|
||||
overlay_state = "[icon_state]_broken"
|
||||
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, layer, plane, dir)
|
||||
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir, alpha=128)
|
||||
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, EMISSIVE_LAYER, EMISSIVE_PLANE, dir, alpha=128)
|
||||
|
||||
/obj/machinery/computer/power_change()
|
||||
..()
|
||||
|
||||
@@ -94,4 +94,4 @@
|
||||
overlay_state = "alert:0"
|
||||
. |= overlay_state
|
||||
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, layer, plane, dir)
|
||||
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir, alpha=128)
|
||||
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, EMISSIVE_LAYER, EMISSIVE_PLANE, dir, alpha=128)
|
||||
|
||||
@@ -11,16 +11,19 @@
|
||||
|
||||
/obj/machinery/computer/robotics/proc/can_control(mob/user, mob/living/silicon/robot/R)
|
||||
if(!istype(R))
|
||||
return 0
|
||||
return FALSE
|
||||
if(isAI(user))
|
||||
if (R.connected_ai != user)
|
||||
return 0
|
||||
return FALSE
|
||||
if(iscyborg(user))
|
||||
if (R != user)
|
||||
return 0
|
||||
return FALSE
|
||||
if(R.scrambledcodes)
|
||||
return 0
|
||||
return 1
|
||||
return FALSE
|
||||
if (hasSiliconAccessInArea(user) && !issilicon(user))
|
||||
if (!Adjacent(user))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/computer/robotics/ui_interact(mob/user)
|
||||
. = ..()
|
||||
|
||||
@@ -94,4 +94,4 @@
|
||||
overlay_state = "alert:0"
|
||||
. |= overlay_state
|
||||
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, layer, plane, dir)
|
||||
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir, alpha=128)
|
||||
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, EMISSIVE_LAYER, EMISSIVE_PLANE, dir, alpha=128)
|
||||
|
||||
@@ -1,13 +1,68 @@
|
||||
/obj/machinery/door/poddoor/shutters
|
||||
gender = PLURAL
|
||||
name = "shutters"
|
||||
desc = "Heavy duty metal shutters that open mechanically."
|
||||
desc = "Mechanical metal shutters operated by a button with a magnetic seal, keeping them airtight."
|
||||
icon = 'icons/obj/doors/shutters.dmi'
|
||||
layer = SHUTTER_LAYER
|
||||
closingLayer = SHUTTER_LAYER
|
||||
armor = list("melee" = 20, "bullet" = 20, "laser" = 20, "energy" = 75, "bomb" = 25, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70)
|
||||
damage_deflection = 20
|
||||
max_integrity = 100
|
||||
|
||||
/obj/machinery/door/poddoor/shutters/preopen
|
||||
icon_state = "open"
|
||||
density = FALSE
|
||||
opacity = 0
|
||||
|
||||
// The below shutters are the original ones from the TG codebase. They are remaining as "secure" shutters. If anyone wants to improve their animation, feel free.
|
||||
// The original shutters are now shutters_old.dmi; copy the naming format of the files into new a new .dmi to add new shutters that work with the poddoor code for animating the doors.
|
||||
// Originally, the shutters were reskins of blast doors. Eighty hits with the Cap's sabre to destroy one shutter is far too powerful considering shutters cannot be deconstructed (yet).
|
||||
// If you're a mapper and want super strong shutter, use the 'old' ones.
|
||||
|
||||
/obj/machinery/door/poddoor/shutters/old
|
||||
name = "strong shutters"
|
||||
desc = "These shutters have an armoured frame; it looks like plasteel. These shutters look robust enough to survive explosions."
|
||||
icon = 'icons/obj/doors/shutters_old.dmi'
|
||||
icon_state = "closed"
|
||||
armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 75, "bomb" = 30, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70)
|
||||
max_integrity = 300
|
||||
|
||||
/obj/machinery/door/poddoor/shutters/old/preopen
|
||||
icon_state = "open"
|
||||
density = FALSE
|
||||
opacity = 0
|
||||
|
||||
// End of old shutter stuff. Credit for the old shutter sprites to TG.
|
||||
|
||||
/obj/machinery/door/poddoor/shutters/radiation
|
||||
name = "radiation shutters"
|
||||
desc = "Lead-lined shutters painted yellow with a radioactive hazard symbol on it. Blocks out most radiation"
|
||||
icon = 'icons/obj/doors/shutters_radiation.dmi'
|
||||
icon_state = "closed"
|
||||
rad_insulation = 0.2
|
||||
|
||||
/obj/machinery/door/poddoor/shutters/radiation/preopen
|
||||
icon_state = "open"
|
||||
density = FALSE
|
||||
opacity = 0
|
||||
rad_insulation = 1
|
||||
|
||||
/obj/machinery/door/poddoor/shutters/radiation/do_animate(animation)
|
||||
..()
|
||||
switch(animation)
|
||||
if("opening")
|
||||
rad_insulation = 1
|
||||
if("closing")
|
||||
rad_insulation = 0.2
|
||||
|
||||
/obj/machinery/door/poddoor/shutters/window
|
||||
name = "windowed shutters"
|
||||
desc = "Mechanical shutters that have some form of plastic window in them, allowing you to see through the shutters at all times."
|
||||
icon = 'icons/obj/doors/shutters_window.dmi'
|
||||
icon_state = "closed"
|
||||
opacity = 0
|
||||
glass = 1
|
||||
|
||||
/obj/machinery/door/poddoor/shutters/window/preopen
|
||||
icon_state = "open"
|
||||
density = FALSE
|
||||
|
||||
@@ -77,23 +77,23 @@
|
||||
|
||||
if(is_station_level(z))
|
||||
. += "fire_[GLOB.security_level]"
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "fire_[GLOB.security_level]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "fire_[GLOB.security_level]", EMISSIVE_LAYER, EMISSIVE_PLANE, dir)
|
||||
else
|
||||
. += "fire_[SEC_LEVEL_GREEN]"
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "fire_[SEC_LEVEL_GREEN]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "fire_[SEC_LEVEL_GREEN]", EMISSIVE_LAYER, EMISSIVE_PLANE, dir)
|
||||
|
||||
var/area/A = src.loc
|
||||
A = A.loc
|
||||
|
||||
if(!detecting || !A.fire)
|
||||
. += "fire_off"
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "fire_off", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "fire_off", EMISSIVE_LAYER, EMISSIVE_PLANE, dir)
|
||||
else if(obj_flags & EMAGGED)
|
||||
. += "fire_emagged"
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "fire_emagged", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "fire_emagged", EMISSIVE_LAYER, EMISSIVE_PLANE, dir)
|
||||
else
|
||||
. += "fire_on"
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "fire_on", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "fire_on", EMISSIVE_LAYER, EMISSIVE_PLANE, dir)
|
||||
|
||||
/obj/machinery/firealarm/emp_act(severity)
|
||||
. = ..()
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
if(href_list["photo"])
|
||||
var/mob/M = usr
|
||||
M << browse_rsc(picture.picture_image, "pda_photo.png")
|
||||
M << browse("<html><head><title>PDA Photo</title></head>" \
|
||||
M << browse("<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>PDA Photo</title></head>" \
|
||||
+ "<body style='overflow:hidden;margin:0;text-align:center'>" \
|
||||
+ "<img src='pda_photo.png' width='192' style='-ms-interpolation-mode:nearest-neighbor' />" \
|
||||
+ "</body></html>", "window=pdaphoto;size=[picture.psize_x]x[picture.psize_y];can-close=true")
|
||||
|
||||
@@ -42,7 +42,9 @@
|
||||
|
||||
/obj/mecha/combat/honker/get_stats_html()
|
||||
var/output = {"<html>
|
||||
<head><title>[src.name] data</title>
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<title>[src.name] data</title>
|
||||
<style>
|
||||
body {color: #00ff00; background: #32CD32; font-family:"Courier",monospace; font-size: 12px;}
|
||||
hr {border: 1px solid #0f0; color: #fff; background-color: #000;}
|
||||
|
||||
@@ -125,6 +125,7 @@
|
||||
return
|
||||
return {"<html>
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<title>[patient] statistics</title>
|
||||
<script language='javascript' type='text/javascript'>
|
||||
[js_byjax]
|
||||
@@ -259,7 +260,8 @@
|
||||
. = ..()
|
||||
create_reagents(max_volume, NO_REACT)
|
||||
syringes = new
|
||||
known_reagents = list(/datum/reagent/medicine/epinephrine = "Epinephrine", /datum/reagent/medicine/charcoal = "Charcoal")
|
||||
known_reagents = list(/datum/reagent/medicine/epinephrine = "Epinephrine", /datum/reagent/medicine/charcoal = "Charcoal", /datum/reagent/medicine/prussian_blue = "Prussian Blue", \
|
||||
/datum/reagent/medicine/dexalin = "Dexalin", /datum/reagent/medicine/insulin = "Insulin", /datum/reagent/medicine/kelotane = "Kelotane", /datum/reagent/medicine/bicaridine = "Bicaridine")
|
||||
processed_reagents = new
|
||||
|
||||
/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/detach()
|
||||
@@ -390,6 +392,7 @@
|
||||
/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/proc/get_reagents_page()
|
||||
var/output = {"<html>
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<title>Reagent Synthesizer</title>
|
||||
<script language='javascript' type='text/javascript'>
|
||||
[js_byjax]
|
||||
|
||||
@@ -297,7 +297,8 @@
|
||||
left_part += "<hr><a href='?src=[REF(src)];screen=main'>Return</a>"
|
||||
dat = {"<html>
|
||||
<head>
|
||||
<title>[name]</title>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<title>[name] data</title>
|
||||
<style>
|
||||
.res_name {font-weight: bold; text-transform: capitalize;}
|
||||
.red {color: #f00;}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
/obj/machinery/computer/mecha/ui_interact(mob/user)
|
||||
. = ..()
|
||||
var/dat = "<html><head><title>[src.name]</title><style>h3 {margin: 0px; padding: 0px;}</style></head><body>"
|
||||
var/dat = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>[src.name]</title><style>h3 {margin: 0px; padding: 0px;}</style></head><body>"
|
||||
if(screen == 0)
|
||||
dat += "<h3>Tracking beacons data</h3>"
|
||||
var/list/trackerlist = list()
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
|
||||
/obj/mecha/proc/get_stats_html()
|
||||
. = {"<html>
|
||||
<head><title>[src.name] data</title>
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<title>[name] data</title>
|
||||
<style>
|
||||
body {color: #00ff00; background: #000000; font-family:"Lucida Console",monospace; font-size: 12px;}
|
||||
hr {border: 1px solid #0f0; color: #0f0; background-color: #0f0;}
|
||||
@@ -149,7 +151,7 @@
|
||||
|
||||
|
||||
/obj/mecha/proc/get_log_html()
|
||||
. = "<html><head><title>[src.name] Log</title></head><body style='font: 13px 'Courier', monospace;'>"
|
||||
. = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>[src.name] Log</title></head><body style='font: 13px 'Courier', monospace;'>"
|
||||
for(var/list/entry in log)
|
||||
. += {"<div style='font-weight: bold;'>[entry["time"]] [time2text(entry["date"],"MMM DD")] [entry["year"]]</div>
|
||||
<div style='margin-left:15px; margin-bottom:10px;'>[entry["message"]]</div>
|
||||
@@ -162,7 +164,9 @@
|
||||
if(!id_card || !user)
|
||||
return
|
||||
. = {"<html>
|
||||
<head><style>
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<style>
|
||||
h1 {font-size:15px;margin-bottom:4px;}
|
||||
body {color: #00ff00; background: #000000; font-family:"Courier New", Courier, monospace; font-size: 12px;}
|
||||
a {color:#0f0;}
|
||||
@@ -192,6 +196,7 @@
|
||||
return
|
||||
. = {"<html>
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<style>
|
||||
body {color: #00ff00; background: #000000; font-family:"Courier New", Courier, monospace; font-size: 12px;}
|
||||
a {padding:2px 5px; background:#32CD32;color:#000;display:block;margin:2px;text-align:center;text-decoration:none;}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
move_resist = INFINITY
|
||||
obj_flags = 0
|
||||
|
||||
vis_flags = NONE
|
||||
|
||||
/obj/effect/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
|
||||
return
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
|
||||
/obj/item
|
||||
name = "item"
|
||||
icon = 'icons/obj/items_and_weapons.dmi'
|
||||
blocks_emissive = EMISSIVE_BLOCK_GENERIC
|
||||
var/item_state = null
|
||||
var/lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
|
||||
var/righthand_file = 'icons/mob/inhands/items_righthand.dmi'
|
||||
@@ -34,7 +35,12 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
|
||||
var/usesound = null
|
||||
var/throwhitsound = null
|
||||
var/w_class = WEIGHT_CLASS_NORMAL
|
||||
|
||||
/// The amount of stamina it takes to swing an item in a normal melee attack do not lie to me and say it's for realism because it ain't. If null it will autocalculate from w_class.
|
||||
var/total_mass //Total mass in arbitrary pound-like values. If there's no balance reasons for an item to have otherwise, this var should be the item's weight in pounds.
|
||||
/// How long, in deciseconds, this staggers for, if null it will autocalculate from w_class and force. Unlike total mass this supports 0 and negatives.
|
||||
var/stagger_force
|
||||
|
||||
var/slot_flags = 0 //This is used to determine on which slots an item can fit.
|
||||
pass_flags = PASSTABLE
|
||||
pressure_resistance = 4
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
|
||||
/obj/item/areaeditor/attack_self(mob/user)
|
||||
add_fingerprint(user)
|
||||
. = "<BODY><HTML><head><title>[src]</title></head> \
|
||||
. = "<BODY><HTML><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>\
|
||||
<title>[src]</title></head> \
|
||||
<h2>[station_name()] [src.name]</h2> \
|
||||
<small>[fluffnotice]</small><hr>"
|
||||
switch(get_area_type())
|
||||
|
||||
@@ -271,7 +271,7 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
|
||||
user.set_machine(src)
|
||||
|
||||
var/dat = "<!DOCTYPE html><html><head><title>Personal Data Assistant</title><link href=\"https://fonts.googleapis.com/css?family=Orbitron|Share+Tech+Mono|VT323\" rel=\"stylesheet\"></head><body bgcolor=\"" + background_color + "\"><style>body{" + font_mode + "}ul,ol{list-style-type: none;}a, a:link, a:visited, a:active, a:hover { color: #000000;text-decoration:none; }img {border-style:none;}a img{padding-right: 9px;}</style>"
|
||||
var/dat = "<!DOCTYPE html><html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Personal Data Assistant</title><link href=\"https://fonts.googleapis.com/css?family=Orbitron|Share+Tech+Mono|VT323\" rel=\"stylesheet\"></head><body bgcolor=\"" + background_color + "\"><style>body{" + font_mode + "}ul,ol{list-style-type: none;}a, a:link, a:visited, a:active, a:hover { color: #000000;text-decoration:none; }img {border-style:none;}a img{padding-right: 9px;}</style>"
|
||||
dat += assets.css_tag()
|
||||
dat += emoji_s.css_tag()
|
||||
|
||||
@@ -1192,7 +1192,7 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
if(incapacitated())
|
||||
return
|
||||
if(!isnull(aiPDA))
|
||||
var/HTML = "<html><head><title>AI PDA Message Log</title></head><body>[aiPDA.tnote]</body></html>"
|
||||
var/HTML = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>AI PDA Message Log</title></head><body>[aiPDA.tnote]</body></html>"
|
||||
user << browse(HTML, "window=log;size=400x444;border=1;can_resize=1;can_close=1;can_minimize=0")
|
||||
else
|
||||
to_chat(user, "You do not have a PDA. You should make an issue report about this.")
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
flags_1 = CONDUCT_1
|
||||
brightness_on = 2
|
||||
light_color = "#FFDDCC"
|
||||
flashlight_power = 0.3
|
||||
flashlight_power = 0.5
|
||||
var/holo_cooldown = 0
|
||||
|
||||
/obj/item/flashlight/pen/afterattack(atom/target, mob/user, proximity_flag)
|
||||
@@ -227,15 +227,12 @@
|
||||
custom_materials = null
|
||||
on = TRUE
|
||||
|
||||
|
||||
// green-shaded desk lamp
|
||||
/obj/item/flashlight/lamp/green
|
||||
desc = "A classic green-shaded desk lamp."
|
||||
icon_state = "lampgreen"
|
||||
item_state = "lampgreen"
|
||||
|
||||
|
||||
|
||||
/obj/item/flashlight/lamp/verb/toggle_light()
|
||||
set name = "Toggle light"
|
||||
set category = "Object"
|
||||
@@ -258,12 +255,13 @@
|
||||
desc = "A red Nanotrasen issued flare. There are instructions on the side, it reads 'pull cord, make light'."
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
brightness_on = 7 // Pretty bright.
|
||||
total_mass = 0.8
|
||||
light_color = "#FA421A"
|
||||
icon_state = "flare"
|
||||
item_state = "flare"
|
||||
actions_types = list()
|
||||
var/fuel = 0
|
||||
var/on_damage = 7
|
||||
var/on_damage = 9
|
||||
var/produce_heat = 1500
|
||||
heat = 1000
|
||||
light_color = LIGHT_COLOR_FLARE
|
||||
@@ -331,14 +329,15 @@
|
||||
name = "torch"
|
||||
desc = "A torch fashioned from some leaves and a log."
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
brightness_on = 4
|
||||
brightness_on = 6 //When on were like a lantern
|
||||
light_color = "#FAA44B"
|
||||
icon_state = "torch"
|
||||
item_state = "torch"
|
||||
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
|
||||
light_color = LIGHT_COLOR_ORANGE
|
||||
on_damage = 10
|
||||
total_mass = TOTAL_MASS_NORMAL_ITEM
|
||||
on_damage = 12 //Its a log thats on fire
|
||||
slot_flags = null
|
||||
|
||||
/obj/item/flashlight/lantern
|
||||
@@ -348,10 +347,9 @@
|
||||
lefthand_file = 'icons/mob/inhands/equipment/mining_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/mining_righthand.dmi'
|
||||
desc = "A mining lantern."
|
||||
brightness_on = 6 // luminosity when on
|
||||
brightness_on = 6 // luminosity when on
|
||||
light_color = "#FFAA44"
|
||||
flashlight_power = 0.75
|
||||
|
||||
flashlight_power = 0.8
|
||||
|
||||
/obj/item/flashlight/slime
|
||||
gender = PLURAL
|
||||
@@ -372,7 +370,6 @@
|
||||
var/emp_cur_charges = 4
|
||||
var/charge_tick = 0
|
||||
|
||||
|
||||
/obj/item/flashlight/emp/New()
|
||||
..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
@@ -435,7 +432,7 @@
|
||||
var/fuel = 0
|
||||
|
||||
/obj/item/flashlight/glowstick/Initialize()
|
||||
fuel = rand(1600, 2000)
|
||||
fuel = rand(1000, 1500)
|
||||
light_color = color
|
||||
. = ..()
|
||||
|
||||
@@ -540,7 +537,7 @@
|
||||
/obj/item/flashlight/eyelight
|
||||
name = "eyelight"
|
||||
desc = "This shouldn't exist outside of someone's head, how are you seeing this?"
|
||||
brightness_on = 15
|
||||
brightness_on = 10
|
||||
flags_1 = CONDUCT_1
|
||||
item_flags = DROPDEL
|
||||
actions_types = list()
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
//book contents below
|
||||
dat = {"<html>
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<style>
|
||||
h1 {font-size: 18px; margin: 15px 0px 5px;}
|
||||
h2 {font-size: 15px; margin: 15px 0px 5px;}
|
||||
@@ -49,6 +50,7 @@
|
||||
title = "APLU \"Ripley\" Construction and Operation Manual"
|
||||
dat = {"<html>
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<style>
|
||||
h1 {font-size: 18px; margin: 15px 0px 5px;}
|
||||
h2 {font-size: 15px; margin: 15px 0px 5px;}
|
||||
@@ -122,6 +124,7 @@
|
||||
title = "Chef Recipes"
|
||||
dat = {"<html>
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<style>
|
||||
h1 {font-size: 18px; margin: 15px 0px 5px;}
|
||||
h2 {font-size: 15px; margin: 15px 0px 5px;}
|
||||
@@ -202,6 +205,10 @@
|
||||
author = "Syndicate"
|
||||
title = "Fission Mailed: Nuclear Sabotage 101"
|
||||
dat = {"<html>
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
</head>
|
||||
<body>
|
||||
Nuclear Explosives 101:<br>
|
||||
Hello and thank you for choosing the Syndicate for your nuclear information needs.<br>
|
||||
Today's crash course will deal with the operation of a Fusion Class Nanotrasen made Nuclear Device.<br>
|
||||
@@ -229,6 +236,7 @@
|
||||
<b>Disk, Code, Safety, Timer, Disk, RUN!</b><br>
|
||||
Intelligence Analysts believe that normal Nanotrasen procedure is for the Captain to secure the nuclear authorisation disk.<br>
|
||||
Good luck!
|
||||
</body>
|
||||
</html>"}
|
||||
|
||||
// Wiki books that are linked to the configured wiki link.
|
||||
@@ -249,6 +257,7 @@
|
||||
dat = {"
|
||||
|
||||
<html><head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<style>
|
||||
iframe {
|
||||
display: none;
|
||||
@@ -284,6 +293,7 @@
|
||||
dat = {"
|
||||
|
||||
<html><head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<style>
|
||||
iframe {
|
||||
display: none;
|
||||
|
||||
@@ -58,6 +58,21 @@
|
||||
|
||||
new /obj/effect/abstract/DPtarget(get_turf(src), pod)
|
||||
|
||||
/obj/item/choice_beacon/ingredients
|
||||
name = "ingredient box delivery beacon"
|
||||
desc = "Summon a box of ingredients from a wide selection!"
|
||||
icon_state = "gangtool-red"
|
||||
|
||||
/obj/item/choice_beacon/ingredients/generate_display_names()
|
||||
var/static/list/ingredientboxes
|
||||
if(!ingredientboxes)
|
||||
ingredientboxes = list()
|
||||
var/list/templist = typesof(/obj/item/storage/box/ingredients)
|
||||
for(var/V in templist)
|
||||
var/obj/item/storage/box/ingredients/A = V
|
||||
ingredientboxes[initial(A.theme_name)] = A
|
||||
return ingredientboxes
|
||||
|
||||
/obj/item/choice_beacon/hero
|
||||
name = "heroic beacon"
|
||||
desc = "To summon heroes from the past to protect the future."
|
||||
@@ -72,7 +87,6 @@
|
||||
hero_item_list[initial(A.name)] = A
|
||||
return hero_item_list
|
||||
|
||||
|
||||
/obj/item/storage/box/hero
|
||||
name = "Courageous Tomb Raider - 1940's."
|
||||
|
||||
@@ -115,4 +129,5 @@
|
||||
icon = 'icons/obj/supermatter.dmi'
|
||||
icon_state = "supermatterspray"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
var/usesleft = 2
|
||||
var/usesleft = 2
|
||||
|
||||
|
||||
@@ -120,6 +120,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
|
||||
)), \
|
||||
null, \
|
||||
new/datum/stack_recipe("iron door", /obj/structure/mineral_door/iron, 20, one_per_turf = TRUE, on_floor = TRUE), \
|
||||
new/datum/stack_recipe("pestle", /obj/item/pestle, 1, time = 50), \
|
||||
new/datum/stack_recipe("floodlight frame", /obj/structure/floodlight_frame, 5, one_per_turf = TRUE, on_floor = TRUE), \
|
||||
))
|
||||
|
||||
@@ -255,6 +256,7 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \
|
||||
new/datum/stack_recipe("apiary", /obj/structure/beebox, 40, time = 50),\
|
||||
null, \
|
||||
new/datum/stack_recipe("picture frame", /obj/item/wallframe/picture, 1, time = 10),\
|
||||
new/datum/stack_recipe("mortar", /obj/item/reagent_containers/glass/mortar, 3), \
|
||||
new/datum/stack_recipe("honey frame", /obj/item/honey_frame, 5, time = 10),\
|
||||
))
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "bible",
|
||||
return
|
||||
// If H is the Chaplain, we can set the icon_state of the bible (but only once!)
|
||||
if(!GLOB.bible_icon_state && H.job == "Chaplain")
|
||||
var/dat = "<html><head><title>Pick Bible Style</title></head><body><center><h2>Pick a bible style</h2></center><table>"
|
||||
var/dat = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Pick Bible Style</title></head><body><center><h2>Pick a bible style</h2></center><table>"
|
||||
for(var/i in 1 to GLOB.biblestates.len)
|
||||
var/icon/bibleicon = icon('icons/obj/storage.dmi', GLOB.biblestates[i])
|
||||
var/nicename = GLOB.biblenames[i]
|
||||
|
||||
@@ -946,12 +946,47 @@
|
||||
/obj/item/reagent_containers/food/snacks/grown/apple,
|
||||
/obj/item/reagent_containers/food/snacks/chocolatebar,
|
||||
/obj/item/reagent_containers/food/snacks/grown/cherries,
|
||||
/obj/item/reagent_containers/food/snacks/grown/berries,
|
||||
/obj/item/reagent_containers/food/snacks/grown/banana,
|
||||
/obj/item/reagent_containers/food/snacks/grown/cabbage,
|
||||
/obj/item/reagent_containers/food/snacks/grown/soybeans,
|
||||
/obj/item/reagent_containers/food/snacks/grown/corn,
|
||||
/obj/item/reagent_containers/food/snacks/grown/mushroom/plumphelmet,
|
||||
/obj/item/reagent_containers/food/snacks/grown/mushroom/chanterelle)
|
||||
/obj/item/reagent_containers/food/snacks/grown/mushroom/chanterelle,
|
||||
/obj/item/reagent_containers/food/snacks/faggot,
|
||||
/obj/item/reagent_containers/food/snacks/grown/citrus/orange,
|
||||
/obj/item/reagent_containers/food/snacks/grown/citrus/lemon,
|
||||
/obj/item/reagent_containers/food/snacks/grown/citrus/lime,
|
||||
/obj/item/reagent_containers/food/snacks/grown/bluecherries,
|
||||
/obj/item/reagent_containers/food/snacks/grown/cocoapod,
|
||||
/obj/item/reagent_containers/food/snacks/grown/vanillapod,
|
||||
/obj/item/reagent_containers/food/snacks/grown/grapes,
|
||||
/obj/item/reagent_containers/food/snacks/grown/strawberry,
|
||||
/obj/item/reagent_containers/food/snacks/grown/whitebeet,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/bear,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/spider,
|
||||
/obj/item/reagent_containers/food/snacks/spidereggs,
|
||||
/obj/item/reagent_containers/food/snacks/carpmeat,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/xeno,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/corgi,
|
||||
/obj/item/reagent_containers/food/snacks/grown/oat,
|
||||
/obj/item/reagent_containers/food/snacks/grown/wheat,
|
||||
/obj/item/reagent_containers/honeycomb,
|
||||
/obj/item/reagent_containers/food/snacks/grown/watermelon,
|
||||
/obj/item/reagent_containers/food/snacks/grown/onion,
|
||||
/obj/item/reagent_containers/food/snacks/grown/peach,
|
||||
/obj/item/reagent_containers/food/snacks/grown/peanut,
|
||||
/obj/item/reagent_containers/food/snacks/grown/pineapple,
|
||||
/obj/item/reagent_containers/food/snacks/grown/pumpkin,
|
||||
/obj/item/reagent_containers/food/snacks/meat/rawcrab,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/goliath,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/chicken,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/golem,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/lizard,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/skeleton,
|
||||
/obj/item/reagent_containers/food/snacks/egg,
|
||||
/obj/item/reagent_containers/food/snacks/grown/eggplant)
|
||||
new randomFood(src)
|
||||
|
||||
/obj/item/storage/box/ingredients/fiesta
|
||||
@@ -1061,6 +1096,15 @@
|
||||
new /obj/item/reagent_containers/food/snacks/grown/cabbage(src)
|
||||
new /obj/item/reagent_containers/food/snacks/grown/chili(src)
|
||||
|
||||
/obj/item/storage/box/ingredients/sushi
|
||||
theme_name = "sushi"
|
||||
|
||||
/obj/item/storage/box/ingredients/sushi/PopulateContents()
|
||||
for(var/i in 1 to 3)
|
||||
new /obj/item/reagent_containers/food/snacks/sea_weed(src)
|
||||
new /obj/item/reagent_containers/food/snacks/carpmeat(src)
|
||||
new /obj/item/reagent_containers/food/snacks/meat/rawcrab(src)
|
||||
|
||||
/obj/item/storage/box/emptysandbags
|
||||
name = "box of empty sandbags"
|
||||
|
||||
|
||||
@@ -211,9 +211,6 @@
|
||||
/obj/proc/check_uplink_validity()
|
||||
return 1
|
||||
|
||||
/obj/proc/intercept_user_move(dir, mob, newLoc, oldLoc)
|
||||
return
|
||||
|
||||
/obj/vv_get_dropdown()
|
||||
. = ..()
|
||||
.["Delete all of type"] = "?_src_=vars;[HrefToken()];delall=[REF(src)]"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
icon_state = "sofamiddle"
|
||||
icon = 'icons/obj/sofa.dmi'
|
||||
buildstackamount = 1
|
||||
item_chair = null
|
||||
var/mutable_appearance/armrest
|
||||
|
||||
/obj/structure/chair/sofa/Initialize()
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
return FALSE
|
||||
var/turf/T = get_turf(src)
|
||||
for(var/mob/living/L in T)
|
||||
if(L.anchored || horizontal && L.mob_size > MOB_SIZE_TINY && L.density)
|
||||
if(L.move_resist >= MOVE_FORCE_VERY_STRONG || (horizontal && L.mob_size > MOB_SIZE_TINY && L.density))
|
||||
if(user)
|
||||
to_chat(user, "<span class='danger'>There's something large on top of [src], preventing it from opening.</span>" )
|
||||
return FALSE
|
||||
@@ -133,7 +133,7 @@
|
||||
if(closet != src && !closet.wall_mounted)
|
||||
return FALSE
|
||||
for(var/mob/living/L in T)
|
||||
if(L.anchored || horizontal && L.mob_size > MOB_SIZE_TINY && L.density)
|
||||
if(L.move_resist >= MOVE_FORCE_VERY_STRONG || horizontal && L.mob_size > MOB_SIZE_TINY && L.density)
|
||||
if(user)
|
||||
to_chat(user, "<span class='danger'>There's something too large in [src], preventing it from closing.</span>")
|
||||
return FALSE
|
||||
@@ -216,7 +216,7 @@
|
||||
if(!isliving(AM)) //let's not put ghosts or camera mobs inside closets...
|
||||
return FALSE
|
||||
var/mob/living/L = AM
|
||||
if(L.anchored || L.buckled || L.incorporeal_move || L.has_buckled_mobs())
|
||||
if(L.move_resist >= MOVE_FORCE_VERY_STRONG || L.buckled || L.incorporeal_move || L.has_buckled_mobs())
|
||||
return FALSE
|
||||
if(L.mob_size > MOB_SIZE_TINY) // Tiny mobs are treated as items.
|
||||
if(horizontal && L.density)
|
||||
@@ -451,7 +451,7 @@
|
||||
/obj/structure/closet/relaymove(mob/user)
|
||||
if(user.stat || !isturf(loc) || !isliving(user))
|
||||
return
|
||||
if(locked)
|
||||
if(locked || welded)
|
||||
if(message_cooldown <= world.time)
|
||||
message_cooldown = world.time + 50
|
||||
to_chat(user, "<span class='warning'>[src]'s door won't budge!</span>")
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
H.socks_color = recolor_undergarment(H, "socks", H.socks_color)
|
||||
|
||||
add_fingerprint(H)
|
||||
H.update_body()
|
||||
H.update_body(TRUE)
|
||||
|
||||
/obj/structure/dresser/proc/recolor_undergarment(mob/living/carbon/human/H, garment_type = "underwear", default_color)
|
||||
var/n_color = input(H, "Choose your [garment_type]'\s color.", "Character Preference", default_color) as color|null
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
new_spawn.underwear = "Nude" //You're a plant, partner
|
||||
new_spawn.undershirt = "Nude" //changing underwear/shirt/socks doesn't seem to function correctly right now because of some bug elsewhere?
|
||||
new_spawn.socks = "Nude"
|
||||
new_spawn.update_body()
|
||||
new_spawn.update_body(TRUE)
|
||||
|
||||
//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.
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ FLOOR SAFES
|
||||
var/obj/item/P = contents[i]
|
||||
dat += "<tr><td><a href='?src=[REF(src)];retrieve=[REF(P)]'>[P.name]</a></td></tr>"
|
||||
dat += "</table></center>"
|
||||
user << browse("<html><head><title>[name]</title></head><body>[dat]</body></html>", "window=safe;size=350x300")
|
||||
user << browse("<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>[name]</title></head><body>[dat]</body></html>", "window=safe;size=350x300")
|
||||
|
||||
/obj/structure/safe/Topic(href, href_list)
|
||||
if(!ishuman(usr))
|
||||
|
||||
@@ -47,6 +47,9 @@
|
||||
// by default, vis_contents is inherited from the turf that was here before
|
||||
vis_contents.Cut()
|
||||
|
||||
if(color)
|
||||
add_atom_colour(color, FIXED_COLOUR_PRIORITY)
|
||||
|
||||
assemble_baseturfs()
|
||||
|
||||
levelupdate()
|
||||
|
||||
Reference in New Issue
Block a user