Merge remote-tracking branch 'upstream/master' into system-corruption

This commit is contained in:
DeltaFire
2020-11-13 01:00:50 +01:00
121 changed files with 27775 additions and 488 deletions
+53 -16
View File
@@ -72,6 +72,8 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
var/list/threat_log_verbose = list()
/// List of roundstart rules used for selecting the rules.
var/list/roundstart_rules = list()
/// List of minor roundstart rules used for selecting the rules.
var/list/minor_rules = list()
/// List of latejoin rules used for selecting the rules.
var/list/latejoin_rules = list()
/// List of midround rules used for selecting the rules.
@@ -121,10 +123,12 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
var/pop_last_updated = 0
/// How many percent of the rounds are more peaceful.
var/peaceful_percentage = 50
/// If a highlander executed.
/// If a highlander executed. No other highlander rulesets will be run.
var/highlander_executed = FALSE
/// If a only ruleset has been executed.
/// If a only ruleset has been executed. No other rulesets will be run.
var/only_ruleset_executed = FALSE
/// If the first picked ruleset was a minor ruleset. Minor antagonists will be weighted higher.
var/minor_ruleset_start = 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.
@@ -372,6 +376,8 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
if(ruleset.name == "")
continue
switch(ruleset.ruletype)
if("Minor")
minor_rules += ruleset
if("Roundstart")
roundstart_rules += ruleset
if ("Latejoin")
@@ -396,21 +402,47 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
rigged_roundstart()
else
roundstart()
var/starting_rulesets = ""
for (var/datum/dynamic_ruleset/roundstart/DR in executed_rules)
starting_rulesets += "[DR.name], "
log_game("DYNAMIC: Picked the following roundstart rules: [starting_rulesets]")
if(minor_ruleset_start)
log_game("DYNAMIC: Starting a minor ruleset round.")
else
var/starting_rulesets = ""
for (var/datum/dynamic_ruleset/roundstart/DR in executed_rules)
starting_rulesets += "[DR.name], "
log_game("DYNAMIC: Picked the following roundstart rules: [starting_rulesets]")
candidates.Cut()
return TRUE
/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)
if(minor_ruleset_start)
addtimer(CALLBACK(src, /datum/game_mode/dynamic/.proc/minor_roundstart),rand(1 MINUTES,5 MINUTES))
else
for(var/datum/dynamic_ruleset/roundstart/rule in executed_rules)
addtimer(CALLBACK(src, /datum/game_mode/dynamic/.proc/execute_roundstart_rule, rule), rule.delay)
..()
/datum/game_mode/dynamic/proc/minor_roundstart()
message_admins("Dynamic beginning minor antag roundstart rolls.")
var/list/potential_minor_rulesets = storyteller.minor_rule_draft()
var/iterations = 0
var/num_rulesets_executed = 0
while(threat < threat_level && potential_minor_rulesets.len && (!CHECK_TICK || iterations < 100))
var/datum/dynamic_ruleset/minor/rule = pickweight(potential_minor_rulesets)
rule.candidates = current_players[CURRENT_LIVING_PLAYERS].Copy()
rule.trim_candidates()
if(!check_blocking(rule.blocking_rules, executed_rules) && rule.ready())
rule.execute()
executed_rules |= rule
log_threat("[rule.ruletype] - <b>[rule.name]</b> [rule.cost] threat", verbose = TRUE)
num_rulesets_executed++
else
potential_minor_rulesets -= rule
update_playercounts()
iterations++
message_admins("Minor antag roundstart rolls completed, with [num_rulesets_executed] antags or antag teams made.")
log_game("DYNAMIC: Minor antag roundstart made [num_rulesets_executed] antags or antag teams.")
/// A simple roundstart proc used when dynamic_forced_roundstart_ruleset has rules in it.
/datum/game_mode/dynamic/proc/rigged_roundstart()
message_admins("[GLOB.dynamic_forced_roundstart_ruleset.len] rulesets being forced. Will now attempt to draft players for them.")
@@ -429,12 +461,17 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
if (GLOB.dynamic_forced_extended)
log_game("DYNAMIC: Starting a round of forced extended.")
return TRUE
if(prob(storyteller.minor_start_chance()))
minor_ruleset_start = TRUE
message_admins("Dynamic has initialized a minor antag start. Antags will be assigned in 1-5 minutes.")
log_game("DYNAMIC: Minor start initialized.")
return TRUE
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!")
midround_injection_cooldown = round((midround_injection_cooldown + world.time) / 2, 1)
latejoin_injection_cooldown = round((latejoin_injection_cooldown + world.time) / 2, 1)
message_admins("No roundstart antags drafted! Falling back to minor ruleset start.")
log_game("DYNAMIC: No roundstart antags drafted! Falling back to minor ruleset start.")
minor_ruleset_start = TRUE
return FALSE
var/indice_pop = min(10,round(roundstart_pop_ready/pop_per_requirement)+1)
extra_rulesets_amount = 0
if (GLOB.dynamic_classic_secret)
@@ -468,8 +505,9 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
else
if(threat_level >= 50)
message_admins("DYNAMIC: Picking first roundstart ruleset failed. You should report this.")
message_admins("DYNAMIC: Picking first roundstart ruleset failed. You should report this. Falling back to minor antag start.")
log_game("DYNAMIC: Picking first roundstart ruleset failed. drafted_rules.len = [drafted_rules.len] and threat = [threat]/[threat_level]")
minor_ruleset_start = TRUE
return FALSE
return TRUE
@@ -513,7 +551,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
drafted_rules -= starting_rule
starting_rule.trim_candidates()
starting_rule.scale_up(extra_rulesets_amount, threat_level-added_threat)
if (starting_rule.pre_execute())
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)
@@ -50,8 +50,12 @@
/// Used for the roundend report
var/total_cost = 0
/// A flag that determines how the ruleset is handled
/// ONLY_RULESET are rulesets that prevent ALL other rulesets from rolling.
/// HIGHLANDER_RULESET are rulesets can end the round.
/// TRAITOR_RULESET and MINOR_RULESET can't end the round and have no difference right now.
/// TRAITOR_RULESET are the "default" ruleset--they should always be addable to a round, if the round type allows antags and dynamic thinks there should be another.
/// MINOR_RULESET is for rulesets whose antags can have multiple instances without causing too much issue. As roundstarts, they have their weights reduced based on the storyteller's minor-antag-round chance.
/// FAKE_ANTAG_RULESET is for rulesets whose antags aren't actually antagonistic--essentially just flavor meant to spice the round up.
/// ALWAYS_MAX_WEIGHT_RULESET means that the ruleset doesn't have its weight reduced based on recency.
var/flags = 0
/// Pop range per requirement. If zero defaults to mode's pop_per_requirement.
var/pop_per_requirement = 0
@@ -82,9 +86,6 @@
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
@@ -100,7 +101,7 @@
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)
if(!always_max_weight && SSpersistence.saved_dynamic_rules.len == 3 && repeated_mode_adjust.len == 3)
if(!(flags & ALWAYS_MAX_WEIGHT_RULESET) && 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])
@@ -119,6 +120,9 @@
/datum/dynamic_ruleset/roundstart // One or more of those drafted at roundstart
ruletype = "Roundstart"
/datum/dynamic_ruleset/minor // drafted at roundstart in minor rounds, one antag at a time, for a "mixed" round
ruletype = "Minor"
// Can be drafted when a player joins the server
/datum/dynamic_ruleset/latejoin
ruletype = "Latejoin"
@@ -175,7 +179,7 @@
/// This is called if persistent variable is true everytime SSTicker ticks.
/datum/dynamic_ruleset/proc/rule_process()
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
@@ -70,9 +70,8 @@
requirements = list(40,30,20,15,15,15,15,15,15,15)
high_population_requirement = 15
repeatable = TRUE
flags = TRAITOR_RULESET
flags = TRAITOR_RULESET | MINOR_RULESET | ALWAYS_MAX_WEIGHT_RULESET
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1)
always_max_weight = TRUE
//////////////////////////////////////////////
// //
@@ -207,6 +206,7 @@
weight = 4
cost = 25
requirements = list(60,60,60,55,50,50,50,50,50,50)
flags = MINOR_RULESET
high_population_requirement = 50
property_weights = list("story_potential" = 1, "trust" = -1, "chaos" = 2, "extended" = -1, "valid" = 2)
repeatable = TRUE
@@ -229,6 +229,7 @@
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)
flags = MINOR_RULESET
high_population_requirement = 30
repeatable = TRUE
@@ -258,6 +259,7 @@
required_candidates = 1
weight = 3
cost = 15
flags = MINOR_RULESET
requirements = list(101,101,101,101,101,101,101,101,101,101)
property_weights = list("trust" = -2, "valid" = 2)
high_population_requirement = 101
@@ -281,5 +283,5 @@
requirements = list(10,10,10,10,10,10,10,10,10,10)
high_population_requirement = 10
repeatable = TRUE
flags = TRAITOR_RULESET | MINOR_RULESET
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 2)
flags = TRAITOR_RULESET | MINOR_RULESET | FAKE_ANTAG_RULESET
property_weights = list("story_potential" = 1, "trust" = -1, "extended" = 2)
@@ -207,9 +207,8 @@
requirements = list(30,25,20,15,15,15,15,15,15,15)
repeatable = TRUE
high_population_requirement = 15
flags = TRAITOR_RULESET
flags = TRAITOR_RULESET | MINOR_RULESET | ALWAYS_MAX_WEIGHT_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)
var/player_count = mode.current_players[CURRENT_LIVING_PLAYERS].len
@@ -494,6 +493,7 @@
required_candidates = 1
weight = 3
cost = 10
flags = MINOR_RULESET
requirements = list(101,101,101,70,50,40,20,15,15,15)
high_population_requirement = 50
repeatable_weight_decrease = 2
@@ -630,6 +630,7 @@
required_candidates = 1
weight = 4
cost = 15
flags = MINOR_RULESET
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)
@@ -0,0 +1,212 @@
//////////////////////////////////////////////
// //
// SYNDICATE TRAITORS //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/minor/traitor
name = "Traitors"
config_tag = "traitor" // these having identical config tags to the roundstart modes is 100% intentional, so that config edits are simpler
persistent = TRUE
antag_flag = ROLE_TRAITOR
antag_datum = /datum/antagonist/traitor/
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", "Cyborg")
restricted_roles = list("Cyborg", "AI")
required_candidates = 1
weight = 5
flags = TRAITOR_RULESET | ALWAYS_MAX_WEIGHT_RULESET
cost = 10 // Avoid raising traitor threat above 10, as it is the default low cost ruleset.
requirements = list(50,50,50,50,50,50,50,50,50,50)
high_population_requirement = 40
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1, "valid" = 1)
/datum/dynamic_ruleset/minor/traitor/execute()
var/mob/M = pick_n_take(candidates)
assigned += 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
//////////////////////////////////////////
// //
// BLOOD BROTHERS //
// //
//////////////////////////////////////////
/datum/dynamic_ruleset/minor/traitorbro
name = "Blood Brothers"
config_tag = "traitorbro"
antag_flag = ROLE_BROTHER
antag_datum = /datum/antagonist/brother
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 = 2
weight = 4
cost = 10
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
/datum/dynamic_ruleset/minor/traitorbro/execute()
if(candidates.len < min_team_size || candidates.len < required_candidates)
return FALSE
var/datum/team/brother_team/team = new
var/team_size = prob(10) ? min(3, candidates.len) : 2
for(var/k = 1 to team_size)
var/mob/bro = pick_n_take(candidates)
assigned += bro.mind
team.add_member(bro.mind)
bro.mind.special_role = "brother"
bro.mind.restricted_roles = restricted_roles
team.pick_meeting_area()
team.forge_brother_objectives()
for(var/datum/mind/M in team.members)
M.add_antag_datum(/datum/antagonist/brother, team)
team.update_name()
mode.brother_teams += team
//////////////////////////////////////////////
// //
// CHANGELINGS //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/minor/changeling
name = "Changelings"
config_tag = "changeling"
antag_flag = ROLE_CHANGELING
antag_datum = /datum/antagonist/changeling
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 = 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
/datum/dynamic_ruleset/minor/changeling/execute()
var/mob/M = pick_n_take(candidates)
assigned += M.mind
M.mind.restricted_roles = restricted_roles
M.mind.special_role = ROLE_CHANGELING
var/datum/antagonist/changeling/new_antag = new antag_datum()
M.mind.add_antag_datum(new_antag)
return TRUE
//////////////////////////////////////////////
// //
// ELDRITCH CULT //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/minor/heretics
name = "Heretic"
antag_flag = "heretic"
antag_datum = /datum/antagonist/heretic
protected_roles = list("Prisoner","Security Officer", "Warden", "Detective", "Head of Security", "Captain")
restricted_roles = list("AI", "Cyborg")
required_candidates = 1
weight = 3
cost = 25
scaling_cost = 15
requirements = list(60,60,60,55,50,50,50,50,50,50)
property_weights = list("story_potential" = 1, "trust" = -1, "chaos" = 2, "extended" = -1, "valid" = 2)
antag_cap = list(1,1,1,1,2,2,2,2,3,3)
high_population_requirement = 50
/datum/dynamic_ruleset/minor/heretics/pre_execute()
var/mob/picked_candidate = pick_n_take(candidates)
assigned += picked_candidate.mind
picked_candidate.mind.restricted_roles = restricted_roles
picked_candidate.mind.special_role = ROLE_HERETIC
var/datum/antagonist/heretic/new_antag = new antag_datum()
picked_candidate.mind.add_antag_datum(new_antag)
return TRUE
//////////////////////////////////////////////
// //
// DEVIL //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/minor/devil
name = "Devil"
config_tag = "devil"
antag_flag = ROLE_DEVIL
antag_datum = /datum/antagonist/devil
restricted_roles = list("Lawyer", "Curator", "Chaplain", "Head of Security", "Captain", "AI")
required_candidates = 1
weight = 3
cost = 0
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/minor/devil/pre_execute()
var/mob/devil = pick_n_take(candidates)
assigned += devil.mind
devil.mind.special_role = ROLE_DEVIL
devil.mind.restricted_roles = restricted_roles
log_game("[key_name(devil)] has been selected as a devil")
add_devil(devil, ascendable = TRUE)
add_devil_objectives(devil.mind,2)
return TRUE
/datum/dynamic_ruleset/minor/devil/proc/add_devil_objectives(datum/mind/devil_mind, quantity)
var/list/validtypes = list(/datum/objective/devil/soulquantity, /datum/objective/devil/soulquality, /datum/objective/devil/sintouch, /datum/objective/devil/buy_target)
var/datum/antagonist/devil/D = devil_mind.has_antag_datum(/datum/antagonist/devil)
for(var/i = 1 to quantity)
var/type = pick(validtypes)
var/datum/objective/devil/objective = new type(null)
objective.owner = devil_mind
D.objectives += objective
if(!istype(objective, /datum/objective/devil/buy_target))
validtypes -= type
else
objective.find_target()
//////////////////////////////////////////////
// //
// BLOODSUCKERS //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/minor/bloodsucker
name = "Bloodsuckers"
config_tag = "bloodsucker"
antag_flag = ROLE_BLOODSUCKER
antag_datum = ANTAG_DATUM_BLOODSUCKER
minimum_required_age = 0
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
cost = 15
scaling_cost = 10
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
/datum/dynamic_ruleset/minor/bloodsucker/execute()
var/mob/M = pick_n_take(candidates)
assigned += M.mind
M.mind.special_role = ROLE_BLOODSUCKER
M.mind.restricted_roles = restricted_roles
mode.check_start_sunlight()
if(mode.make_bloodsucker(M.mind))
mode.bloodsuckers += M.mind
return TRUE
@@ -15,6 +15,7 @@
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster", "Cyborg")
restricted_roles = list("Cyborg", "AI")
required_candidates = 1
flags = TRAITOR_RULESET | MINOR_RULESET | ALWAYS_MAX_WEIGHT_RULESET
weight = 5
cost = 10 // Avoid raising traitor threat above 10, as it is the default low cost ruleset.
scaling_cost = 10
@@ -22,7 +23,6 @@
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)
/datum/dynamic_ruleset/roundstart/traitor/pre_execute()
@@ -57,6 +57,7 @@
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 = 2
flags = MINOR_RULESET
weight = 4
cost = 10
requirements = list(101,101,101,101,101,101,101,101,101,101)
@@ -106,6 +107,7 @@
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
flags = MINOR_RULESET
weight = 3
cost = 15
scaling_cost = 15
@@ -156,6 +158,7 @@
protected_roles = list("Prisoner","Security Officer", "Warden", "Detective", "Head of Security", "Captain")
restricted_roles = list("AI", "Cyborg")
required_candidates = 1
flags = MINOR_RULESET
weight = 3
cost = 25
scaling_cost = 15
@@ -694,6 +697,7 @@
antag_datum = /datum/antagonist/devil
restricted_roles = list("Lawyer", "Curator", "Chaplain", "Head of Security", "Captain", "AI")
required_candidates = 1
flags = MINOR_RULESET
weight = 3
cost = 0
requirements = list(101,101,101,101,101,101,101,101,101,101)
@@ -850,6 +854,7 @@
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
flags = MINOR_RULESET
weight = 2
cost = 15
scaling_cost = 10
@@ -32,6 +32,9 @@ Property weights are added to the config weight of the ruleset. They are:
"conversion" -- Basically a bool. Conversion antags, well, convert. It's in its own class 'cause people kinda hate conversion.
*/
/datum/dynamic_storyteller/proc/minor_start_chance()
return clamp(60 - mode.threat_level,0,100) // by default higher threat = lower chance of minor round
/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
@@ -104,8 +107,27 @@ Property weights are added to the config weight of the ruleset. They are:
/datum/dynamic_storyteller/proc/roundstart_draft()
var/list/drafted_rules = list()
var/minor_round_weight_mult = (100-minor_start_chance()) / 100
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)
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]
var/calced_weight = (rule.get_weight() + property_weight) * rule.weight_mult
if(CHECK_BITFIELD(rule.flags, MINOR_RULESET))
calced_weight *= minor_round_weight_mult
if(calced_weight > 0) // negatives in the list might cause problems
drafted_rules[rule] = calced_weight
return drafted_rules
/datum/dynamic_storyteller/proc/minor_rule_draft()
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/minor/rule in mode.minor_rules)
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
rule.candidates = mode.candidates.Copy()
rule.trim_candidates()
if (rule.ready() && rule.candidates.len > 0)
@@ -124,7 +146,7 @@ Property weights are added to the config weight of the ruleset. They are:
// 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))
// Classic secret : only autotraitor/minor roles
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET)))
continue
rule.trim_candidates()
if (rule.ready())
@@ -133,7 +155,7 @@ Property weights are added to the config weight of the ruleset. They are:
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]
var/threat_weight = 1
if(!(rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)) // makes the traitor rulesets always possible anyway
if(!(rule.flags & TRAITOR_RULESET)) // makes the traitor rulesets always possible anyway
var/cost_difference = 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.
@@ -157,7 +179,7 @@ Property weights are added to the config weight of the ruleset. They are:
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))
// Classic secret : only autotraitor/minor roles
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_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)
@@ -172,7 +194,7 @@ Property weights are added to the config weight of the ruleset. They are:
if(property in rule.property_weights)
property_weight += rule.property_weights[property] * property_weights[property]
var/threat_weight = 1
if(!(rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET))
if(!(rule.flags & TRAITOR_RULESET))
var/cost_difference = rule.cost-(mode.threat_level-mode.threat)
threat_weight = 1-abs(1-(LOGISTIC_FUNCTION(2,0.05,abs(cost_difference),0)))
if(cost_difference > 0)
@@ -195,6 +217,9 @@ Property weights are added to the config weight of the ruleset. They are:
min_players = 30
var/refund_cooldown = 0
/datum/dynamic_storyteller/chaotic/minor_start_chance()
return 0
/datum/dynamic_storyteller/chaotic/do_process()
if(refund_cooldown < world.time)
mode.create_threat(20)
@@ -215,6 +240,9 @@ Property weights are added to the config weight of the ruleset. They are:
flags = WAROPS_ALWAYS_ALLOWED | USE_PREV_ROUND_WEIGHTS
property_weights = list("valid" = 3, "trust" = 5)
/datum/dynamic_storyteller/chaotic/minor_start_chance()
return 0
/datum/dynamic_storyteller/team/should_inject_antag(dry_run = FALSE)
return (mode.current_players[CURRENT_LIVING_ANTAGS].len ? FALSE : ..())
@@ -228,6 +256,9 @@ Property weights are added to the config weight of the ruleset. They are:
flags = WAROPS_ALWAYS_ALLOWED
property_weights = list("valid" = 1, "conversion" = 20)
/datum/dynamic_storyteller/chaotic/minor_start_chance()
return 0
/datum/dynamic_storyteller/random
name = "Random"
config_tag = "random"
@@ -244,6 +275,9 @@ Property weights are added to the config weight of the ruleset. They are:
/datum/dynamic_storyteller/random/should_inject_antag()
return prob(50)
/datum/dynamic_storyteller/chaotic/minor_start_chance()
return 20
/datum/dynamic_storyteller/random/roundstart_draft()
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/roundstart/rule in mode.roundstart_rules)
@@ -254,12 +288,22 @@ Property weights are added to the config weight of the ruleset. They are:
drafted_rules[rule] = 1
return drafted_rules
/datum/dynamic_storyteller/random/minor_rule_draft()
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/minor/rule in mode.minor_rules)
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
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)))
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET)))
continue
rule.trim_candidates()
if (rule.ready())
@@ -271,7 +315,7 @@ Property weights are added to the config weight of the ruleset. They are:
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)))
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_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)
@@ -286,7 +330,7 @@ Property weights are added to the config weight of the ruleset. They are:
/datum/dynamic_storyteller/story
name = "Story"
config_tag = "story"
desc = "Antags with options for loadouts and gimmicks. Traitor, wizard, nukies. Has a buildup-climax-falling action threat curve."
desc = "Antags with options for loadouts and gimmicks. Traitor, wizard, nukies."
weight = 2
curve_width = 2
flags = USE_PREV_ROUND_WEIGHTS
@@ -308,6 +352,19 @@ Property weights are added to the config weight of the ruleset. They are:
flags = USE_PREV_ROUND_WEIGHTS
property_weights = list("trust" = -2)
/datum/dynamic_storyteller/intrigue/minor_start_chance()
return 100 - mode.threat_level
/datum/dynamic_storyteller/grabbag
name = "Grab Bag"
config_tag = "grabbag"
desc = "Crew antags (e.g. traitor, changeling, bloodsucker, heretic) only, all mixed together."
weight = 2
flags = USE_PREF_WEIGHTS | USE_PREV_ROUND_WEIGHTS
/datum/dynamic_storyteller/grabbag/minor_start_chance()
return 100
/datum/dynamic_storyteller/liteextended
name = "Calm"
config_tag = "calm"
@@ -319,6 +376,9 @@ Property weights are added to the config weight of the ruleset. They are:
dead_player_weight = 5
property_weights = list("extended" = 2, "chaos" = -1, "valid" = -1, "conversion" = -10)
/datum/dynamic_storyteller/liteextended/minor_start_chance()
return 100
/datum/dynamic_storyteller/no_antag
name = "Extended"
config_tag = "semiextended"
+4 -2
View File
@@ -114,7 +114,7 @@ GLOBAL_LIST_EMPTY(announcement_systems)
. = ..()
if(.)
return
if(!usr.canUseTopic(src, !issilicon(usr)))
if(!usr.canUseTopic(src, !hasSiliconAccessInArea(usr)))
return
if(stat & BROKEN)
visible_message("<span class='warning'>[src] buzzes.</span>", "<span class='hear'>You hear a faint buzz.</span>")
@@ -147,7 +147,7 @@ GLOBAL_LIST_EMPTY(announcement_systems)
. = attack_ai(user)
/obj/machinery/announcement_system/attack_ai(mob/user)
if(!user.canUseTopic(src, !issilicon(user)))
if(!user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return
if(stat & BROKEN)
to_chat(user, "<span class='warning'>[src]'s firmware appears to be malfunctioning!</span>")
@@ -167,7 +167,9 @@ GLOBAL_LIST_EMPTY(announcement_systems)
act_up()
/obj/machinery/announcement_system/emag_act()
. = ..()
if(obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
act_up()
return TRUE
+15
View File
@@ -21,6 +21,9 @@
return
if(R.scrambledcodes)
return
if(hasSiliconAccessInArea(user) && !issilicon(user))
if(!Adjacent(user))
return
return TRUE
/obj/machinery/computer/robotics/ui_interact(mob/user, datum/tgui/ui)
@@ -40,6 +43,10 @@
else if(IsAdminGhost(user))
data["can_hack"] = TRUE
data["can_convert"] = FALSE
if(isAI(user) && is_servant_of_ratvar(user))
data["can_convert"] = TRUE
data["cyborgs"] = list()
for(var/mob/living/silicon/robot/R in GLOB.silicon_mobs)
if(!can_control(user, R))
@@ -54,6 +61,7 @@
module = R.module ? "[R.module.name] Module" : "No Module Detected",
synchronization = R.connected_ai,
emagged = R.emagged,
servant = is_servant_of_ratvar(R),
ref = REF(R)
)
data["cyborgs"] += list(cyborg_data)
@@ -110,6 +118,13 @@
log_game("[key_name(usr)] emagged [key_name(R)] using robotic console!")
message_admins("[ADMIN_LOOKUPFLW(usr)] emagged cyborg [key_name_admin(R)] using robotic console!")
R.SetEmagged(TRUE)
if("convert")
if(isAI(usr) && is_servant_of_ratvar(usr))
var/mob/living/silicon/robot/R = locate(params["ref"]) in GLOB.silicon_mobs
if(istype(R) && !is_servant_of_ratvar(R) && R.connected_ai == usr)
log_game("[key_name(usr)] converted [key_name(R)] using robotic console!")
message_admins("[ADMIN_LOOKUPFLW(usr)] converted cyborg [key_name_admin(R)] using robotic console!")
add_servant_of_ratvar(R)
if("killdrone")
if(allowed(usr))
var/mob/living/simple_animal/drone/D = locate(params["ref"]) in GLOB.mob_list
+9
View File
@@ -32,6 +32,8 @@
. = ..()
if(in_range(user, src) || isobserver(user))
. += "<span class='notice'>The status display reads: Radiation pulse accuracy increased by factor <b>[precision_coeff**2]</b>.<br>Radiation pulse damage decreased by factor <b>[damage_coeff**2]</b>.</span>"
if(scan_level >= 3)
. += "<span class='notice'>Scanner has been upgraded to support autoprocessing.</span>"
/obj/machinery/dna_scannernew/update_icon_state()
//no power or maintenance
@@ -94,6 +96,13 @@
..(user)
// search for ghosts, if the corpse is empty and the scanner is connected to a cloner
var/mob/living/mob_occupant = get_mob_or_brainmob(occupant)
if(istype(mob_occupant))
if(locate_computer(/obj/machinery/computer/cloning))
if(!mob_occupant.suiciding && !(HAS_TRAIT(mob_occupant, TRAIT_NOCLONE)) && !mob_occupant.hellbound)
mob_occupant.notify_ghost_cloning("Your corpse has been placed into a cloning scanner. Re-enter your corpse if you want to be cloned!", source = src)
// DNA manipulators cannot operate on severed heads or brains
if(iscarbon(occupant))
if(linked_console)
+10
View File
@@ -1457,6 +1457,16 @@
ui.open()
return TRUE
/obj/machinery/door/airlock/ui_status(mob/user)
. = ..()
if (!issilicon(user) && hasSiliconAccessInArea(user))
. = UI_INTERACTIVE
/obj/machinery/door/airlock/can_interact(mob/user)
. = ..()
if (!issilicon(user) && hasSiliconAccessInArea(user))
return TRUE
/obj/machinery/door/airlock/ui_data()
var/list/data = list()
+1 -1
View File
@@ -508,7 +508,7 @@
/obj/machinery/door/airlock/cult/allowed(mob/living/L)
if(!density)
return 1
if(friendly || iscultist(L) || istype(L, /mob/living/simple_animal/shade) || isconstruct(L))
if(friendly || iscultist(L) || isshade(L) || isconstruct(L))
if(!stealthy)
new openingoverlaytype(loc)
return 1
+1 -1
View File
@@ -616,7 +616,7 @@
if(defib.healdisk)
H.heal_overall_damage(25, 25)
var/list/policies = CONFIG_GET(keyed_list/policyconfig)
var/timelimit = CONFIG_GET(number/defib_cmd_time_limit)
var/timelimit = CONFIG_GET(number/defib_cmd_time_limit) * 10 //the config is in seconds, not deciseconds
var/late = timelimit && (tplus > timelimit)
var/policy = late? policies[POLICYCONFIG_ON_DEFIB_LATE] : policies[POLICYCONFIG_ON_DEFIB_INTACT]
if(policy)
+9 -1
View File
@@ -8,6 +8,14 @@
* Gifts
*/
/var/static/blacklisted_items = typecacheof(list(
/obj/effect,
/obj/belly,
/obj/mafia_game_board,
/obj/docking_port,
/obj/shapeshift_holder,
/obj/screen))
GLOBAL_LIST_EMPTY(possible_gifts)
/obj/item/a_gift
@@ -95,7 +103,7 @@ GLOBAL_LIST_EMPTY(possible_gifts)
var/list/gift_types_list = subtypesof(/obj/item)
for(var/V in gift_types_list)
var/obj/item/I = V
if((!initial(I.icon_state)) || (!initial(I.item_state)) || (initial(I.item_flags) & ABSTRACT))
if((!initial(I.icon_state)) || (!initial(I.item_state)) || (initial(I.item_flags) & ABSTRACT) || (is_type_in_typecache(I, blacklisted_items)))
gift_types_list -= V
GLOB.possible_gifts = gift_types_list
var/gift_type = pick(GLOB.possible_gifts)
+3 -3
View File
@@ -492,7 +492,7 @@
var/possessed = FALSE
/obj/item/nullrod/scythe/talking/process()
for(var/mob/living/simple_animal/shade/S in contents)
for(var/mob/living/simple_animal/hostile/construct/shade/S in contents)
if(S.mind)
return
else
@@ -516,7 +516,7 @@
if(LAZYLEN(candidates))
var/mob/C = pick(candidates)
var/mob/living/simple_animal/shade/S = new(src)
var/mob/living/simple_animal/hostile/construct/shade/S = new(src)
S.real_name = name
S.name = name
S.ckey = C.ckey
@@ -537,7 +537,7 @@
possessed = FALSE
/obj/item/nullrod/scythe/talking/Destroy()
for(var/mob/living/simple_animal/shade/S in contents)
for(var/mob/living/simple_animal/hostile/construct/shade/S in contents)
to_chat(S, "You were destroyed!")
qdel(S)
return ..()
@@ -92,10 +92,17 @@
/obj/item/implant/hijack/proc/hijack_remotely(obj/machinery/power/apc/apc)
if (apc.hijacker || hijacking)
return FALSE //can't remotely hijack an already hijacked APC
if(apc.being_hijacked)
to_chat(imp_in, "<span class='warning'>This APC is already being hijacked!</span>")
return FALSE
apc.being_hijacked = TRUE
hijacking = TRUE
to_chat(imp_in, "<span class='notice'>Establishing remote connection with APC.</span>")
if (!do_after(imp_in, 4 SECONDS,target=apc))
to_chat(imp_in, "<span class='warning'>Aborting.</span>")
apc.being_hijacked = FALSE
hijacking = FALSE
return TRUE
if (LAZYLEN(imp_in.siliconaccessareas) >= HIJACK_APC_MAX_AMOUNT)
@@ -118,6 +125,7 @@
toggle_eyes()
else
to_chat(imp_in, "<span class='warning'>Aborting.</span>")
apc.being_hijacked = FALSE
hijacking = FALSE
imp_in.light_power = 0
imp_in.light_range = 0
+2 -2
View File
@@ -150,8 +150,8 @@
item_state = "knife"
desc = "A military combat utility survival knife."
embedding = list("pain_mult" = 4, "embed_chance" = 65, "fall_chance" = 10, "ignore_throwspeed_threshold" = TRUE)
force = 20
throwforce = 20
force = 16
throwforce = 16
attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "cut")
bayonet = TRUE
+2 -2
View File
@@ -198,7 +198,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "bible",
if(istype(A, /obj/item/cult_bastard))
for(var/obj/item/soulstone/SS in A.contents)
SS.usability = TRUE
for(var/mob/living/simple_animal/shade/EX in SS)
for(var/mob/living/simple_animal/hostile/construct/shade/EX in SS)
SSticker.mode.remove_cultist(EX.mind, 1, 0)
EX.icon_state = "ghost1"
EX.name = "Purified [EX.name]"
@@ -217,7 +217,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "bible",
if(do_after(user, 40, target = SS))
playsound(src,'sound/effects/pray_chaplain.ogg',60,1)
SS.usability = TRUE
for(var/mob/living/simple_animal/shade/EX in SS)
for(var/mob/living/simple_animal/hostile/construct/shade/EX in SS)
SSticker.mode.remove_cultist(EX.mind, 1, 0)
EX.icon_state = "ghost1"
EX.name = "Purified [EX.name]"
+42 -47
View File
@@ -21,6 +21,9 @@
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30)
resistance_flags = FIRE_PROOF
var/self_fueling = FALSE //Do we refill ourselves or not
var/nextrefueltick = 0 // How long it takes before we get a new fuel unit
custom_materials = list(/datum/material/iron=70, /datum/material/glass=30)
var/welding = 0 //Whether or not the welding tool is off(0), on(1) or currently welding(2)
var/status = TRUE //Whether the welder is secured or unsecured (able to attach rods to it to make a flamethrower)
@@ -82,12 +85,18 @@
//This is to start fires. process() is only called if the welder is on.
open_flame()
//This handles refueling. Its looking at how much fuel the tool has and comparing that to how much it holds
//This then looks if the refuel tick has come based on world time.
//Then looks if we refuel ourselves or not.
if(get_fuel() < max_fuel && nextrefueltick < world.time && self_fueling)
nextrefueltick = world.time + 10
reagents.add_reagent(/datum/reagent/fuel, 1)
/obj/item/weldingtool/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] welds [user.p_their()] every orifice closed! It looks like [user.p_theyre()] trying to commit suicide!</span>")
return (FIRELOSS)
/obj/item/weldingtool/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/screwdriver))
flamethrower_screwdriver(I, user)
@@ -318,7 +327,6 @@
/obj/item/weldingtool/largetank/flamethrower_screwdriver()
return
/obj/item/weldingtool/mini
name = "emergency welding tool"
desc = "A miniature welder used during emergencies."
@@ -331,20 +339,6 @@
/obj/item/weldingtool/mini/flamethrower_screwdriver()
return
/obj/item/weldingtool/abductor
name = "alien welding tool"
desc = "An alien welding tool. Whatever fuel it uses, it never runs out."
icon = 'icons/obj/abductor.dmi'
icon_state = "welder"
toolspeed = 0.1
light_intensity = 0
change_icons = 0
/obj/item/weldingtool/abductor/process()
if(get_fuel() <= max_fuel)
reagents.add_reagent(/datum/reagent/fuel, 1)
..()
/obj/item/weldingtool/hugetank
name = "upgraded industrial welding tool"
desc = "An upgraded welder based of the industrial welder."
@@ -353,27 +347,6 @@
max_fuel = 80
custom_materials = list(/datum/material/iron=70, /datum/material/glass=120)
/obj/item/weldingtool/experimental
name = "experimental welding tool"
desc = "An experimental welder capable of self-fuel generation and less harmful to the eyes."
icon_state = "exwelder"
item_state = "exwelder"
max_fuel = 40
custom_materials = list(/datum/material/iron=70, /datum/material/glass=120)
var/last_gen = 0
change_icons = 0
can_off_process = 1
light_intensity = 1
toolspeed = 0.5
var/nextrefueltick = 0
/obj/item/weldingtool/experimental/brass
name = "brass welding tool"
desc = "A brass welder that seems to constantly refuel itself. It is faintly warm to the touch."
resistance_flags = FIRE_PROOF | ACID_PROOF
icon_state = "clockwelder"
item_state = "brasswelder"
/obj/item/weldingtool/bronze
name = "bronze plated welding tool"
desc = "A bronze plated welder."
@@ -382,24 +355,46 @@
icon_state = "brasswelder"
item_state = "brasswelder"
/obj/item/weldingtool/experimental/process()
..()
if(get_fuel() < max_fuel && nextrefueltick < world.time)
nextrefueltick = world.time + 10
reagents.add_reagent(/datum/reagent/fuel, 1)
//Self filling welders below
/obj/item/weldingtool/experimental
name = "experimental welding tool"
desc = "An experimental welder capable of self-fuel generation and less harmful to the eyes."
icon_state = "exwelder"
item_state = "exwelder"
max_fuel = 40
custom_materials = list(/datum/material/iron=70, /datum/material/glass=120)
change_icons = 0
self_fueling = TRUE
can_off_process = 1
light_intensity = 1
toolspeed = 0.5
/obj/item/weldingtool/experimental/brass
name = "brass welding tool"
desc = "A brass welder that seems to constantly refuel itself. It is faintly warm to the touch."
resistance_flags = FIRE_PROOF | ACID_PROOF
icon_state = "clockwelder"
item_state = "brasswelder"
/obj/item/weldingtool/abductor
name = "alien welding tool"
desc = "An alien welding tool. Whatever fuel it uses, it never runs out."
icon = 'icons/obj/abductor.dmi'
icon_state = "welder"
self_fueling = TRUE
toolspeed = 0.1
light_intensity = 0
change_icons = 0
/obj/item/weldingtool/advanced
name = "advanced welding tool"
desc = "A modern welding tool combined with an alien welding tool, it never runs out of fuel and works almost as fast."
icon = 'icons/obj/advancedtools.dmi'
icon_state = "welder"
self_fueling = TRUE
toolspeed = 0.2
light_intensity = 0
change_icons = 0
/obj/item/weldingtool/advanced/process()
if(get_fuel() <= max_fuel)
reagents.add_reagent(/datum/reagent/fuel, 1)
..()
#undef WELDER_FUEL_BURN_INTERVAL