Merge remote-tracking branch 'upstream/master' into cool-ipcs

This commit is contained in:
Timothy Teakettle
2020-09-16 05:12:20 +01:00
635 changed files with 23608 additions and 12861 deletions

View File

@@ -6,10 +6,6 @@ GLOBAL_VAR_INIT(dynamic_latejoin_delay_max, (30 MINUTES))
GLOBAL_VAR_INIT(dynamic_midround_delay_min, (10 MINUTES))
GLOBAL_VAR_INIT(dynamic_midround_delay_max, (30 MINUTES))
GLOBAL_VAR_INIT(dynamic_event_delay_min, (10 MINUTES))
GLOBAL_VAR_INIT(dynamic_event_delay_max, (30 MINUTES)) // this is on top of regular events, so can't be quite as often
// -- Roundstart injection delays
GLOBAL_VAR_INIT(dynamic_first_latejoin_delay_min, (2 MINUTES))
GLOBAL_VAR_INIT(dynamic_first_latejoin_delay_max, (30 MINUTES))
@@ -58,7 +54,7 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
// Threat logging vars
/// Starting threat level, for things that increase it but can bring it back down.
var/initial_threat_level = 0
/// Target threat level right now. Events and antags will try to keep the round at this level.
/// Target threat level right now. Antags will try to keep the round at this level.
var/threat_level = 0
/// The current antag threat. Recalculated every time a ruletype starts or ends.
var/threat = 0
@@ -80,8 +76,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
var/list/latejoin_rules = list()
/// List of midround rules used for selecting the rules.
var/list/midround_rules = list()
/// List of events used for reducing threat without causing antag injection (necessarily).
var/list/events = list()
/** # Pop range per requirement.
* If the value is five the range is:
* 0-4, 5-9, 10-14, 15-19, 20-24, 25-29, 30-34, 35-39, 40-54, 45+
@@ -119,8 +113,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
var/latejoin_injection_cooldown = 0
/// When world.time is over this number the mode tries to inject a midround ruleset.
var/midround_injection_cooldown = 0
/// When wor.dtime is over this number the mode tries to do an event.
var/event_injection_cooldown = 0
/// When TRUE GetInjectionChance returns 100.
var/forced_injection = FALSE
/// Forced ruleset to be executed for the next latejoin.
@@ -184,7 +176,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
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)/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)
@@ -204,10 +195,7 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
var/threatadd = input("Specify how much threat to add (negative to subtract). This can inflate the threat level.", "Adjust Threat", 0) as null|num
if(!threatadd)
return
if(threatadd > 0)
create_threat(threatadd)
else
remove_threat(threatadd)
create_threat(threatadd)
else if (href_list["injectlate"])
latejoin_injection_cooldown = 0
forced_injection = TRUE
@@ -216,10 +204,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
midround_injection_cooldown = 0
forced_injection = TRUE
message_admins("[key_name(usr)] forced a midround injection.", 1)
else if (href_list["forceevent"])
event_injection_cooldown = 0
// events always happen anyway
message_admins("[key_name(usr)] forced an event.", 1)
else if (href_list["threatlog"])
show_threatlog(usr)
else if (href_list["stacking_limit"])
@@ -377,8 +361,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
generate_threat()
storyteller.start_injection_cooldowns()
SSevents.frequency_lower = storyteller.event_frequency_lower // 6 minutes by default
SSevents.frequency_upper = storyteller.event_frequency_upper // 20 minutes by default
log_game("DYNAMIC: Dynamic Mode initialized with a Threat Level of... [threat_level]!")
initial_threat_level = threat_level
return TRUE
@@ -397,9 +379,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
if ("Midround")
if (ruleset.weight)
midround_rules += ruleset
if("Event")
if(ruleset.weight)
events += ruleset
for(var/mob/dead/new_player/player in GLOB.player_list)
if(player.ready == PLAYER_READY_TO_PLAY && player.mind)
roundstart_pop_ready++
@@ -596,8 +575,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
latejoin_rules = remove_from_list(latejoin_rules, rule.type)
else if(rule.ruletype == "Midround")
midround_rules = remove_from_list(midround_rules, rule.type)
else if(rule.ruletype == "Event")
events = remove_from_list(events,rule.type)
addtimer(CALLBACK(src, /datum/game_mode/dynamic/.proc/execute_midround_latejoin_rule, rule), rule.delay)
return TRUE
@@ -706,17 +683,6 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
picking_midround_latejoin_rule(drafted_rules)
// get_injection_chance can do things on fail
if(event_injection_cooldown < world.time)
SSblackbox.record_feedback("tally","dynamic",1,"Attempted event injections")
event_injection_cooldown = storyteller.get_event_cooldown() + world.time
message_admins("DYNAMIC: Doing event injection.")
log_game("DYNAMIC: Doing event injection.")
update_playercounts()
var/list/drafted_rules = storyteller.event_draft()
if(drafted_rules.len > 0)
SSblackbox.record_feedback("tally","dynamic",1,"Successful event injections")
picking_midround_latejoin_rule(drafted_rules)
/// Updates current_players.
/datum/game_mode/dynamic/proc/update_playercounts()
current_players[CURRENT_LIVING_PLAYERS] = list()

View File

@@ -1,454 +0,0 @@
/datum/dynamic_ruleset/event
ruletype = "Event"
var/typepath // typepath of the event
var/triggering
var/earliest_start = 20 MINUTES
/datum/dynamic_ruleset/event/get_blackbox_info()
var/list/ruleset_data = list()
ruleset_data["name"] = name
ruleset_data["rule_type"] = ruletype
ruleset_data["cost"] = total_cost
ruleset_data["weight"] = weight
ruleset_data["scaled_times"] = scaled_times
ruleset_data["event_type"] = typepath
ruleset_data["population_tier"] = indice_pop
return ruleset_data
/datum/dynamic_ruleset/event/execute()
var/datum/round_event/E = new typepath()
E.current_players = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1)
// E.control = src // can't be done! we just don't use events that require these, those can be from_ghost almost always
testing("[time2text(world.time, "hh:mm:ss")] [E.type]")
deadchat_broadcast("<span class='deadsay'><b>[name]</b> has just been triggered by dynamic!</span>")
log_game("Random Event triggering: [name] ([typepath])")
return E
/datum/dynamic_ruleset/event/ready(forced = FALSE)
if (!forced)
if(earliest_start >= world.time-SSticker.round_start_time)
return FALSE
var/job_check = 0
if (enemy_roles.len > 0)
for (var/mob/M in mode.current_players[CURRENT_LIVING_PLAYERS])
if (M.stat == DEAD)
continue // Dead players cannot count as opponents
if (M.mind && M.mind.assigned_role && (M.mind.assigned_role in enemy_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)
if (job_check < required_enemies[threat])
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough enemy roles")
return FALSE
return TRUE
//////////////////////////////////////////////
// //
// PIRATES //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/event/pirates
name = "Space Pirates"
config_tag = "pirates"
typepath = /datum/round_event/pirates
antag_flag = ROLE_TRAITOR
enemy_roles = list("AI","Security Officer","Head of Security","Captain")
required_enemies = list(2,2,1,1,0,0,0,0,0,0)
weight = 5
cost = 10
earliest_start = 30 MINUTES
blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear)
requirements = list(70,60,50,50,40,40,40,30,20,15)
property_weights = list("story_potential" = 1, "trust" = 1, "chaos" = 1)
high_population_requirement = 15
/datum/dynamic_ruleset/event/pirates/ready(forced = FALSE)
if (!SSmapping.empty_space)
return FALSE
return ..()
//////////////////////////////////////////////
// //
// SPIDERS //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/event/spiders
name = "Spider Infestation"
config_tag = "spiders"
typepath = /datum/round_event/spider_infestation
enemy_roles = list("AI","Security Officer","Head of Security","Captain")
required_enemies = list(2,2,1,1,0,0,0,0,0,0)
weight = 5
cost = 10
requirements = list(70,60,50,50,40,40,40,30,20,15)
high_population_requirement = 15
property_weights = list("chaos" = 1, "valid" = 1)
//////////////////////////////////////////////
// //
// CLOGGED VENTS //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/event/ventclog
name = "Clogged Vents"
config_tag = "ventclog"
typepath = /datum/round_event/vent_clog
enemy_roles = list("Chemist","Medical Doctor","Chief Medical Officer")
required_enemies = list(1,1,1,0,0,0,0,0,0,0)
cost = 2
weight = 4
repeatable_weight_decrease = 3
requirements = list(5,5,5,5,5,5,5,5,5,5) // yes, can happen on fake-extended
high_population_requirement = 5
repeatable = TRUE
property_weights = list("chaos" = 1, "extended" = 2)
/datum/dynamic_ruleset/event/ventclog/ready()
if(mode.threat_level > 30 && mode.threat >= 5 && prob(20))
name = "Clogged Vents: Threatening"
cost = 5
required_enemies = list(3,3,3,2,2,2,1,1,1,1)
typepath = /datum/round_event/vent_clog/threatening
else if(mode.threat_level > 15 && mode.threat > 15 && prob(30))
name = "Clogged Vents: Catastrophic"
cost = 15
required_enemies = list(2,2,1,1,1,1,0,0,0,0)
typepath = /datum/round_event/vent_clog/catastrophic
else
cost = 2
name = "Clogged Vents: Normal"
required_enemies = list(1,1,1,0,0,0,0,0,0,0)
typepath = /datum/round_event/vent_clog
return ..()
//////////////////////////////////////////////
// //
// ION STORM //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/event/ion_storm
name = "Ion Storm"
config_tag = "ion_storm"
typepath = /datum/round_event/ion_storm
enemy_roles = list("Research Director","Captain","Chief Engineer")
required_enemies = list(1,1,0,0,0,0,0,0,0,0)
weight = 4
// no repeatable weight decrease. too variable to be unfun multiple times in one round
cost = 1
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
property_weights = list("story_potential" = 1, "extended" = 1)
always_max_weight = TRUE
//////////////////////////////////////////////
// //
// METEORS //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/event/meteor_wave
name = "Meteor Wave"
config_tag = "meteor_wave"
typepath = /datum/round_event/meteor_wave
enemy_roles = list("Chief Engineer","Station Engineer","Atmospheric Technician","Captain","Cyborg")
required_enemies = list(3,3,3,3,3,3,3,3,3,3)
cost = 15
weight = 3
earliest_start = 25 MINUTES
repeatable_weight_decrease = 2
requirements = list(60,50,40,30,30,30,30,30,30,30)
high_population_requirement = 30
property_weights = list("extended" = -2)
/datum/dynamic_ruleset/event/meteor_wave/ready()
if(world.time-SSticker.round_start_time > 35 MINUTES && mode.threat_level > 40 && mode.threat >= 25 && prob(30))
name = "Meteor Wave: Threatening"
cost = 25
typepath = /datum/round_event/meteor_wave/threatening
else if(world.time-SSticker.round_start_time > 45 MINUTES && mode.threat_level > 50 && mode.threat >= 40 && prob(30))
name = "Meteor Wave: Catastrophic"
cost = 40
typepath = /datum/round_event/meteor_wave/catastrophic
else
name = "Meteor Wave: Normal"
cost = 15
typepath = /datum/round_event/meteor_wave
return ..()
//////////////////////////////////////////////
// //
// ANOMALIES //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/event/anomaly_bluespace
name = "Anomaly: Bluespace"
config_tag = "anomaly_bluespace"
typepath = /datum/round_event/anomaly/anomaly_bluespace
enemy_roles = list("Chief Engineer","Station Engineer","Atmospheric Technician","Research Director","Scientist","Captain")
required_enemies = list(1,1,1,0,0,0,0,0,0,0)
weight = 2
repeatable_weight_decrease = 1
cost = 3
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/anomaly_flux
name = "Anomaly: Hyper-Energetic Flux"
config_tag = "anomaly_flux"
typepath = /datum/round_event/anomaly/anomaly_flux
enemy_roles = list("Chief Engineer","Station Engineer","Atmospheric Technician","Research Director","Scientist","Captain")
required_enemies = list(1,1,1,0,0,0,0,0,0,0)
weight = 2
repeatable_weight_decrease = 1
cost = 5
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 10
repeatable = TRUE
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/anomaly_gravitational
name = "Anomaly: Gravitational"
config_tag = "anomaly_gravitational"
typepath = /datum/round_event/anomaly/anomaly_grav
weight = 2
repeatable_weight_decrease = 1
cost = 3
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/anomaly_pyroclastic
name = "Anomaly: Pyroclastic"
config_tag = "anomaly_pyroclastic"
typepath = /datum/round_event/anomaly/anomaly_pyro
weight = 2
repeatable_weight_decrease = 1
cost = 5
enemy_roles = list("Chief Engineer","Station Engineer","Atmospheric Technician","Research Director","Scientist","Captain","Cyborg")
required_enemies = list(1,1,1,1,1,1,1,1,1,1)
requirements = list(10,10,10,10,10,10,10,10,10,10)
high_population_requirement = 10
repeatable = TRUE
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/anomaly_vortex
name = "Anomaly: Vortex"
config_tag = "anomaly_vortex"
typepath = /datum/round_event/anomaly/anomaly_vortex
weight = 2
repeatable_weight_decrease = 1
cost = 5
enemy_roles = list("Chief Engineer","Station Engineer","Atmospheric Technician","Research Director","Scientist","Captain","Cyborg")
required_enemies = list(1,1,1,1,1,1,1,1,1,1)
requirements = list(10,10,10,10,10,10,10,10,10,10)
high_population_requirement = 10
repeatable = TRUE
property_weights = list("extended" = 1)
//////////////////////////////////////////////
// //
// WOW THAT'S A LOT OF EVENTS //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/event/brand_intelligence
name = "Brand Intelligence"
config_tag = "brand_intelligence"
typepath = /datum/round_event/brand_intelligence
weight = 1
repeatable_weight_decrease = 1
cost = 2
enemy_roles = list("Chief Engineer","Station Engineer","Atmospheric Technician","Research Director","Scientist","Captain","Cyborg")
required_enemies = list(1,1,1,1,0,0,0,0,0,0)
requirements = list(10,10,10,10,10,10,10,10,10,10)
high_population_requirement = 10
repeatable = TRUE
property_weights = list("extended" = -1, "chaos" = 1)
/datum/dynamic_ruleset/event/carp_migration
name = "Carp Migration"
config_tag = "carp_migration"
typepath = /datum/round_event/carp_migration
weight = 7
repeatable_weight_decrease = 3
cost = 4
requirements = list(10,10,10,10,10,10,10,10,10,10)
high_population_requirement = 10
earliest_start = 10 MINUTES
repeatable = TRUE
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/communications_blackout
name = "Communications Blackout"
config_tag = "communications_blackout"
typepath = /datum/round_event/communications_blackout
cost = 4
weight = 2
repeatable_weight_decrease = 3
enemy_roles = list("Chief Engineer","Station Engineer")
required_enemies = list(1,1,1,0,0,0,0,0,0,0)
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
property_weights = list("extended" = 1, "chaos" = 1)
/datum/dynamic_ruleset/event/processor_overload
name = "Processor Overload"
config_tag = "processor_overload"
typepath = /datum/round_event/processor_overload
cost = 4
weight = 2
repeatable_weight_decrease = 3
enemy_roles = list("Chief Engineer","Station Engineer")
required_enemies = list(1,1,1,0,0,0,0,0,0,0)
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
property_weights = list("extended" = 1, "chaos" = 1)
always_max_weight = TRUE
/datum/dynamic_ruleset/event/space_dust
name = "Minor Space Dust"
config_tag = "space_dust"
typepath = /datum/round_event/space_dust
cost = 2
weight = 2
repeatable_weight_decrease = 1
enemy_roles = list("Chief Engineer","Station Engineer")
required_enemies = list(1,1,1,0,0,0,0,0,0,0)
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
earliest_start = 0 MINUTES
property_weights = list("extended" = 1)
always_max_weight = TRUE
/datum/dynamic_ruleset/event/major_dust
name = "Major Space Dust"
config_tag = "major_dust"
typepath = /datum/round_event/meteor_wave/major_dust
cost = 4
weight = 2
repeatable_weight_decrease = 1
enemy_roles = list("Chief Engineer","Station Engineer")
required_enemies = list(2,2,2,2,2,2,2,2,2,2)
requirements = list(10,10,10,10,10,10,10,10,10,10)
high_population_requirement = 10
repeatable = TRUE
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/electrical_storm
name = "Electrical Storm"
config_tag = "electrical_storm"
typepath = /datum/round_event/electrical_storm
cost = 1
weight = 2
repeatable_weight_decrease = 1
enemy_roles = list("Chief Engineer","Station Engineer")
required_enemies = list(1,1,1,0,0,0,0,0,0,0)
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/heart_attack
name = "Random Heart Attack"
config_tag = "heart_attack"
typepath = /datum/round_event/heart_attack
cost = 3
weight = 2
repeatable_weight_decrease = 1
enemy_roles = list("Medical Doctor","Chief Medical Officer")
required_enemies = list(2,2,2,2,2,2,2,2,2,2)
requirements = list(101,101,101,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
property_weights = list("extended" = 1)
always_max_weight = TRUE
/datum/dynamic_ruleset/event/radiation_storm
name = "Radiation Storm"
config_tag = "radiation_storm"
typepath = /datum/round_event/radiation_storm
cost = 3
weight = 1
enemy_roles = list("Chemist","Chief Medical Officer","Geneticist","Medical Doctor","AI","Captain")
required_enemies = list(1,1,1,1,1,1,1,1,1,1)
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
property_weights = list("extended" = 1,"chaos" = 1)
/datum/dynamic_ruleset/event/portal_storm_syndicate
name = "Portal Storm"
config_tag = "portal_storm"
typepath = /datum/round_event/portal_storm/syndicate_shocktroop
cost = 10
weight = 1
enemy_roles = list("Head of Security","Security Officer","AI","Captain","Shaft Miner")
required_enemies = list(2,2,2,2,2,2,2,2,2,2)
requirements = list(101,101,101,30,30,30,30,30,30,30)
high_population_requirement = 30
earliest_start = 30 MINUTES
property_weights = list("teamwork" = 1,"chaos" = 1, "extended" = -1)
/datum/dynamic_ruleset/event/wormholes
name = "Wormholes"
config_tag = "wormhole"
typepath = /datum/round_event/wormholes
cost = 3
weight = 4
enemy_roles = list("AI","Medical Doctor","Station Engineer","Head of Personnel","Captain")
required_enemies = list(2,2,2,2,2,2,2,2,2,2)
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/swarmers
name = "Swarmers"
config_tag = "swarmer"
typepath = /datum/round_event/spawn_swarmer
cost = 10
weight = 1
earliest_start = 30 MINUTES
enemy_roles = list("AI","Security Officer","Head of Security","Captain","Station Engineer","Atmos Technician","Chief Engineer")
required_enemies = list(4,4,4,4,3,3,2,2,1,1)
requirements = list(101,101,101,101,101,101,101,101,101,101)
high_population_requirement = 5
property_weights = list("extended" = -2)
/datum/dynamic_ruleset/event/sentient_disease
name = "Sentient Disease"
config_tag = "sentient_disease"
typepath = /datum/round_event/ghost_role/sentient_disease
enemy_roles = list("Virologist","Chief Medical Officer","Captain","Chemist")
required_enemies = list(2,1,1,1,0,0,0,0,0,0)
required_candidates = 1
weight = 4
cost = 5
requirements = list(30,30,20,20,15,10,10,10,10,5) // yes, it can even happen in "extended"!
property_weights = list("story_potential" = 1, "extended" = 1, "valid" = -2)
high_population_requirement = 5
/datum/dynamic_ruleset/event/revenant
name = "Revenant"
config_tag = "revenant"
typepath = /datum/round_event/ghost_role/revenant
enemy_roles = list("Chief Engineer","Station Engineer","Captain","Chaplain","AI")
required_enemies = list(2,1,1,1,0,0,0,0,0,0)
required_candidates = 1
weight = 4
cost = 5
requirements = list(30,30,30,30,20,15,15,15,15,15)
high_population_requirement = 15
property_weights = list("story_potential" = -2, "extended" = -1)

View File

@@ -200,13 +200,15 @@
/datum/dynamic_ruleset/latejoin/heretic_smuggler
name = "Heretic Smuggler"
antag_datum = /datum/antagonist/heretic
antag_flag = ROLE_HERETIC
antag_flag = "latejoin_heretic"
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
restricted_roles = list("AI","Cyborg")
required_candidates = 1
weight = 4
cost = 10
requirements = list(40,30,20,10,10,10,10,10,10,10)
cost = 25
requirements = list(60,60,60,55,50,50,50,50,50,50)
high_population_requirement = 50
property_weights = list("story_potential" = 1, "trust" = -1, "chaos" = 2, "extended" = -1, "valid" = 2)
repeatable = TRUE
//////////////////////////////////////////////

View File

@@ -538,7 +538,7 @@
name = "Slaughter Demon"
config_tag = "slaughter_demon"
antag_flag = ROLE_ALIEN
enemy_roles = list("Security Officer","Shaft Miner","Head of Security","Captain","Janitor","AI","Cyborg")
enemy_roles = list("Security Officer","Shaft Miner","Head of Security","Captain","Janitor","AI","Cyborg","Bartender")
required_enemies = list(3,2,2,2,2,1,1,1,1,0)
required_candidates = 1
weight = 4

View File

@@ -151,16 +151,18 @@
/datum/dynamic_ruleset/roundstart/heretics
name = "Heretics"
antag_flag = ROLE_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 = 20
cost = 25
scaling_cost = 15
requirements = list(50,45,45,40,35,20,20,15,10,10)
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/roundstart/heretics/pre_execute()

View File

@@ -22,14 +22,14 @@
var/datum/game_mode/dynamic/mode = null // Cached as soon as it's made, by dynamic.
/**
Property weights are:
Property weights are added to the config weight of the ruleset. They are:
"story_potential" -- essentially how many different ways the antag can be played.
"trust" -- How much it makes the crew trust each other. Negative values means they're suspicious. Team antags are like this.
"chaos" -- How chaotic it makes the round. Has some overlap with "valid" and somewhat contradicts "extended".
"valid" -- How likely the non-antag-enemy crew are to get involved, e.g. nukies encouraging the warden to
let everyone into the armory, wizard moving around and being a nuisance, nightmare busting lights.
"extended" -- How much the antag is conducive to a long round. Nukies and cults are bad for this; Wizard is less bad; and so on.
"conversion" -- Basically a bool. Conversion antags, well, convert. It's its own class for a good reason.
"conversion" -- Basically a bool. Conversion antags, well, convert. It's in its own class 'cause people kinda hate conversion.
*/
/datum/dynamic_storyteller/proc/start_injection_cooldowns()
@@ -39,9 +39,6 @@ Property weights are:
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_midround_delay_min + GLOB.dynamic_first_midround_delay_max)
mode.midround_injection_cooldown = round(clamp(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_first_midround_delay_min, GLOB.dynamic_first_midround_delay_max)) + world.time
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
mode.event_injection_cooldown = (round(clamp(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time)
/datum/dynamic_storyteller/proc/calculate_threat()
var/threat = 0
for(var/datum/antagonist/A in GLOB.antagonists)
@@ -99,10 +96,6 @@ Property weights are:
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min)
return round(clamp(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_midround_delay_min, GLOB.dynamic_midround_delay_max))
/datum/dynamic_storyteller/proc/get_event_cooldown()
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
return round(clamp(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max))
/datum/dynamic_storyteller/proc/get_latejoin_cooldown()
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min)
return round(clamp(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max))
@@ -126,8 +119,9 @@ 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]
if(property_weight > 0)
drafted_rules[rule] = rule.get_weight() * property_weight * rule.weight_mult
var/calced_weight = (rule.get_weight() + property_weight) * rule.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/midround_draft()
@@ -144,21 +138,24 @@ 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]
if(property_weight > 0)
var/threat_weight = 1
if(!(rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)) // makes the traitor rulesets always possible anyway
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.
*/
threat_weight = abs(1-abs(1-LOGISTIC_FUNCTION(2,0.05,cost_difference,0)))
drafted_rules[rule] = rule.get_weight() * property_weight * rule.weight_mult * threat_weight
var/threat_weight = 1
if(!(rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_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.
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.
*/
threat_weight = abs(1-abs(1-LOGISTIC_FUNCTION(2,0.05,abs(cost_difference),0)))
if(cost_difference > 0)
threat_weight /= (1+(cost_difference*0.1))
var/calced_weight = (rule.get_weight() + property_weight) * rule.weight_mult * threat_weight
if(calced_weight > 0)
drafted_rules[rule] = calced_weight
return drafted_rules
/datum/dynamic_storyteller/proc/latejoin_draft(mob/living/carbon/human/newPlayer)
@@ -180,27 +177,17 @@ Property weights are:
for(var/property in property_weights)
if(property in rule.property_weights)
property_weight += rule.property_weights[property] * property_weights[property]
if(property_weight > 0)
var/threat_weight = 1
if(!(rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET))
var/cost_difference = abs(rule.cost-(mode.threat_level-mode.threat))
threat_weight = 1-abs(1-(LOGISTIC_FUNCTION(2,0.05,cost_difference,0)))
drafted_rules[rule] = rule.get_weight() * property_weight * rule.weight_mult * threat_weight
var/threat_weight = 1
if(!(rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_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)
threat_weight /= (1+(cost_difference*0.1))
var/calced_weight = (rule.get_weight() + property_weight) * rule.weight_mult * threat_weight
if(calced_weight > 0)
drafted_rules[rule] = calced_weight
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_level + 20 - mode.threat) >= rule.cost && 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]
if(property_weight > 0)
drafted_rules[rule] = rule.get_weight() + property_weight * rule.weight_mult
return drafted_rules
/datum/dynamic_storyteller/chaotic
name = "Chaotic"
config_tag = "chaotic"
@@ -263,9 +250,6 @@ Property weights are:
/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)
@@ -311,13 +295,6 @@ Property weights are:
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) && rule.ready())
drafted_rules[rule] = 1
return drafted_rules
/datum/dynamic_storyteller/story
name = "Story"
config_tag = "story"
@@ -327,12 +304,6 @@ Property weights are:
flags = USE_PREV_ROUND_WEIGHTS
property_weights = list("story_potential" = 2)
/datum/dynamic_storyteller/story/calculate_threat()
var/current_time = (world.time / SSautotransfer.targettime)*180
mode.threat_level = round((mode.initial_threat_level*(sin(current_time)/2)+0.75),0.1)
return ..()
/datum/dynamic_storyteller/classic
name = "Classic"
config_tag = "classic"
@@ -363,7 +334,7 @@ Property weights are:
/datum/dynamic_storyteller/no_antag
name = "Extended"
config_tag = "semiextended"
desc = "No standard antags. Threatening events may still spawn."
desc = "No standard antags."
curve_centre = -5
curve_width = 0.5
flags = NO_ASSASSIN | FORCE_IF_WON
@@ -375,15 +346,3 @@ Property weights are:
/datum/dynamic_storyteller/no_antag/get_injection_chance(dry_run)
return 0
/datum/dynamic_storyteller/extended
name = "Super Extended"
config_tag = "extended"
desc = "No antags. No dangerous events."
curve_centre = -20
weight = 0
curve_width = 0.5
/datum/dynamic_storyteller/extended/on_start()
..()
GLOB.dynamic_forced_extended = TRUE

View File

@@ -112,7 +112,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
var/turf/T = get_turf(loc)
ram_turf(T)
if(prob(10) && !isspaceturf(T))//randomly takes a 'hit' from ramming
if(prob(10) && !isspaceturf(T) && !istype(T, /turf/closed/mineral) && !istype(T, /turf/open/floor/plating/asteroid))//randomly takes a 'hit' from ramming
get_hit()
/obj/effect/meteor/Destroy()
@@ -136,7 +136,8 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
if(A)
ram_turf(get_turf(A))
playsound(src.loc, meteorsound, 40, 1)
get_hit()
if(!istype(A, /turf/closed/mineral) && !istype(A, /turf/open/floor/plating/asteroid))
get_hit()
/obj/effect/meteor/proc/ram_turf(turf/T)
//first bust whatever is in the turf

View File

@@ -551,4 +551,4 @@ Class Procs:
AM.pixel_y = -8 + (round( . / 3)*8)
/obj/machinery/rust_heretic_act()
take_damage(500, BRUTE, "melee", 1)
take_damage(500, BRUTE, "melee", 1)

View File

@@ -49,7 +49,6 @@
dat += "</b></center>"
var/datum/browser/popup = new(user, "arcade", "Space Villain 2000")
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.open()
/obj/machinery/computer/arcade/battle/Topic(href, href_list)

View File

@@ -178,9 +178,12 @@
table[y1][x1] += 10
if(href_list["same_board"]) //Reset the board... kinda
if(game_status != MINESWEEPER_GAME_PLAYING)
mine_sound = TRUE
game_status = MINESWEEPER_GAME_PLAYING
if(table[y1][x1] >= 10) //If revealed, become unrevealed!
playsound(loc, 'sound/arcade/minesweeper_menuselect.ogg', 50, 0, extrarange = -3, falloff = 10)
if(mine_sound)
playsound(loc, 'sound/arcade/minesweeper_menuselect.ogg', 50, 0, extrarange = -3, falloff = 10)
mine_sound = FALSE
table[y1][x1] -= 10
if(table[y1][x1] > 10 && !reset_board)
safe_squares_revealed += 1

View File

@@ -160,7 +160,6 @@
dat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];close=1'>Close</a></P>"
var/datum/browser/popup = new(user, "arcade", "The Orion Trail",400,700)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.open()
return

View File

@@ -353,7 +353,6 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
dat = list("<tt>", header.Join(), body, "<br></tt>")
var/datum/browser/popup = new(user, "id_com", src.name, 900, 620)
popup.set_content(dat.Join())
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/computer/card/Topic(href, href_list)

View File

@@ -276,7 +276,6 @@
var/datum/browser/popup = new(user, "cloning", "Cloning System Control")
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/computer/cloning/Topic(href, href_list)

View File

@@ -1,3 +1,15 @@
#define STATE_DEFAULT 1
#define STATE_CALLSHUTTLE 2
#define STATE_CANCELSHUTTLE 3
#define STATE_MESSAGELIST 4
#define STATE_VIEWMESSAGE 5
#define STATE_DELMESSAGE 6
#define STATE_STATUSDISPLAY 7
#define STATE_ALERT_LEVEL 8
#define STATE_CONFIRM_LEVEL 9
#define STATE_TOGGLE_EMERGENCY 10
#define STATE_PURCHASE 11
// The communications computer
/obj/machinery/computer/communications
name = "communications console"
@@ -6,6 +18,7 @@
icon_keyboard = "tech_key"
req_access = list(ACCESS_HEADS)
circuit = /obj/item/circuitboard/computer/communications
light_color = LIGHT_COLOR_BLUE
var/auth_id = "Unknown" //Who is currently logged in?
var/list/datum/comm_message/messages = list()
var/datum/comm_message/currmsg
@@ -16,22 +29,10 @@
var/ai_message_cooldown = 0
var/tmp_alertlevel = 0
var/static/security_level_cd // used to stop mass spam.
var/const/STATE_DEFAULT = 1
var/const/STATE_CALLSHUTTLE = 2
var/const/STATE_CANCELSHUTTLE = 3
var/const/STATE_MESSAGELIST = 4
var/const/STATE_VIEWMESSAGE = 5
var/const/STATE_DELMESSAGE = 6
var/const/STATE_STATUSDISPLAY = 7
var/const/STATE_ALERT_LEVEL = 8
var/const/STATE_CONFIRM_LEVEL = 9
var/const/STATE_TOGGLE_EMERGENCY = 10
var/const/STATE_PURCHASE = 11
var/stat_msg1
var/stat_msg2
light_color = LIGHT_COLOR_BLUE
/obj/machinery/computer/communications/proc/checkCCcooldown()
var/obj/item/circuitboard/computer/communications/CM = circuit
@@ -46,7 +47,7 @@
/obj/machinery/computer/communications/Topic(href, href_list)
if(..())
return
if(!usr.canUseTopic(src))
if(!usr.canUseTopic(src, !issilicon(usr)))
return
if(!is_station_level(z) && !is_reserved_level(z)) //Can only use in transit and on SS13
to_chat(usr, "<span class='boldannounce'>Unable to establish a connection</span>: \black You're too far away from the station!")
@@ -132,15 +133,20 @@
if("crossserver")
if(authenticated==2)
var/dest = href_list["cross_dest"]
if(!checkCCcooldown())
to_chat(usr, "<span class='warning'>Arrays recycling. Please stand by.</span>")
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
return
var/input = stripped_multiline_input(usr, "Please choose a message to transmit to allied stations. Please be aware that this process is very expensive, and abuse will lead to... termination.", "Send a message to an allied station.", "")
var/warning = dest == "all" ? "Please choose a message to transmit to allied stations." : "Please choose a message to transmit to [dest] sector station."
var/input = stripped_multiline_input(usr, "[warning] Please be aware that this process is very expensive, and abuse will lead to... termination.", "Send a message to an allied station.", "")
if(!input || !(usr in view(1,src)) || !checkCCcooldown())
return
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
send2otherserver("[station_name()]", input,"Comms_Console")
if(dest == "all")
send2otherserver("[station_name()]", input,"Comms_Console")
else
send2otherserver("[station_name()]", input,"Comms_Console", list(dest))
minor_announce(input, title = "Outgoing message to allied station")
usr.log_talk(input, LOG_SAY, tag="message to the other server")
message_admins("[ADMIN_LOOKUPFLW(usr)] has sent a message to the other server.")
@@ -156,12 +162,12 @@
var/datum/map_template/shuttle/S = locate(href_list["chosen_shuttle"]) in shuttles
if(S && istype(S))
if(SSshuttle.emergency.mode != SHUTTLE_RECALL && SSshuttle.emergency.mode != SHUTTLE_IDLE)
to_chat(usr, "It's a bit late to buy a new shuttle, don't you think?")
to_chat(usr, "<span class='alert'>It's a bit late to buy a new shuttle, don't you think?</span>")
return
if(SSshuttle.shuttle_purchased)
to_chat(usr, "A replacement shuttle has already been purchased.")
to_chat(usr, "<span class='alert'>A replacement shuttle has already been purchased.</span>")
else if(!S.prerequisites_met())
to_chat(usr, "You have not met the requirements for purchasing this shuttle.")
to_chat(usr, "<span class='alert'>You have not met the requirements for purchasing this shuttle.</span>")
else
var/points_to_check
var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
@@ -183,7 +189,7 @@
if("callshuttle")
state = STATE_DEFAULT
if(authenticated)
if(authenticated && SSshuttle.canEvac(usr))
state = STATE_CALLSHUTTLE
if("callshuttle2")
if(authenticated)
@@ -284,12 +290,12 @@
if("MessageCentCom")
if(authenticated)
if(!checkCCcooldown())
to_chat(usr, "<span class='warning'>Arrays recycling. Please stand by.</span>")
to_chat(usr, "<span class='warning'>Arrays recycling. Please stand by.</span>")
return
var/input = stripped_input(usr, "Please choose a message to transmit to CentCom via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "Send a message to CentCom.", "")
if(!input || !(usr in view(1,src)) || !checkCCcooldown())
return
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
CentCom_announce(input, usr)
to_chat(usr, "<span class='notice'>Message transmitted to Central Command.</span>")
for(var/client/X in GLOB.admins)
@@ -302,7 +308,7 @@
// OMG SYNDICATE ...LETTERHEAD
if("MessageSyndicate")
if((authenticated==2) && (obj_flags & EMAGGED))
if((authenticated) && (obj_flags & EMAGGED))
if(!checkCCcooldown())
to_chat(usr, "<span class='warning'>Arrays recycling. Please stand by.</span>")
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
@@ -332,7 +338,7 @@
if(!checkCCcooldown())
to_chat(usr, "<span class='warning'>Arrays recycling. Please stand by.</span>")
return
var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self Destruct Code Request.","")
var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self-Destruct Code Request.","")
if(!input || !(usr in view(1,src)) || !checkCCcooldown())
return
Nuke_request(input, usr)
@@ -347,7 +353,9 @@
aicurrmsg = null
aistate = STATE_DEFAULT
if("ai-callshuttle")
aistate = STATE_CALLSHUTTLE
aistate = STATE_DEFAULT
if(SSshuttle.canEvac(usr))
aistate = STATE_CALLSHUTTLE
if("ai-callshuttle2")
SSshuttle.requestEvac(usr, href_list["call"])
aistate = STATE_DEFAULT
@@ -460,9 +468,8 @@
var/datum/browser/popup = new(user, "communications", "Communications Console", 400, 500)
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
if(issilicon(user) || (hasSiliconAccessInArea(user) && !in_range(user,src)))
if(issilicon(user))
var/dat2 = interact_ai(user) // give the AI a different interact proc to limit its access
if(dat2)
dat += dat2
@@ -493,9 +500,15 @@
if (authenticated==2)
dat += "<BR><BR><B>Captain Functions</B>"
dat += "<BR>\[ <A HREF='?src=[REF(src)];operation=announce'>Make a Captain's Announcement</A> \]"
var/cross_servers_count = length(CONFIG_GET(keyed_list/cross_server))
if(cross_servers_count)
dat += "<BR>\[ <A HREF='?src=[REF(src)];operation=crossserver'>Send a message to [cross_servers_count == 1 ? "an " : ""]allied station[cross_servers_count > 1 ? "s" : ""]</A> \]"
var/list/cross_servers = CONFIG_GET(keyed_list/cross_server)
var/our_id = CONFIG_GET(string/cross_comms_name)
if(cross_servers.len)
for(var/server in cross_servers)
if(server == our_id)
continue
dat += "<BR>\[ <A HREF='?src=[REF(src)];operation=crossserver=all;cross_dest=[server]'>Send a message to station in [server] sector.</A> \]"
if(cross_servers.len > 2)
dat += "<BR>\[ <A HREF='?src=[REF(src)];operation=crossserver;cross_dest=all'>Send a message to all allied stations</A> \]"
if(SSmapping.config.allow_custom_shuttles)
dat += "<BR>\[ <A HREF='?src=[REF(src)];operation=purchase_menu'>Purchase Shuttle</A> \]"
dat += "<BR>\[ <A HREF='?src=[REF(src)];operation=changeseclevel'>Change Alert Level</A> \]"
@@ -721,8 +734,13 @@
to_chat(user, "<span class='alert'>Intercomms recharging. Please stand by.</span>")
return
var/input = stripped_input(user, "Please choose a message to announce to the station crew.", "What?")
if(!input || !user.canUseTopic(src))
if(!input || !user.canUseTopic(src, !issilicon(usr)))
return
if(!(user.can_speak())) //No more cheating, mime/random mute guy!
input = "..."
to_chat(user, "<span class='warning'>You find yourself unable to speak.</span>")
else
input = user.treat_message(input) //Adds slurs and so on. Someone should make this use languages too.
SScommunications.make_announcement(user, is_silicon, input)
deadchat_broadcast("<span class='deadsay'><span class='name'>[user.real_name]</span> made an priority announcement from <span class='name'>[get_area_name(usr, TRUE)]</span>.</span>", user)
@@ -771,3 +789,15 @@
content = new_content
if(new_possible_answers)
possible_answers = new_possible_answers
#undef STATE_DEFAULT
#undef STATE_CALLSHUTTLE
#undef STATE_CANCELSHUTTLE
#undef STATE_MESSAGELIST
#undef STATE_VIEWMESSAGE
#undef STATE_DELMESSAGE
#undef STATE_STATUSDISPLAY
#undef STATE_ALERT_LEVEL
#undef STATE_CONFIRM_LEVEL
#undef STATE_TOGGLE_EMERGENCY
#undef STATE_PURCHASE

View File

@@ -217,9 +217,6 @@
// already discovered mutations
stored_research = SSresearch.science_tech
/obj/machinery/computer/scan_consolenew/examine(mob/user)
. = ..()
/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, datum/tgui/ui)
// Most of ui_interact is spent setting variables for passing to the tgui
// interface.
@@ -266,6 +263,10 @@
if(!ui)
ui = new(user, src, "DnaConsole")
ui.open()
/obj/machinery/computer/scan_consolenew/ui_assets()
. = ..() || list()
. += get_asset_datum(/datum/asset/simple/genetics)
/obj/machinery/computer/scan_consolenew/ui_data(mob/user)
var/list/data = list()
@@ -357,7 +358,7 @@
return data
/obj/machinery/computer/scan_consolenew/ui_act(action, var/list/params)
/obj/machinery/computer/scan_consolenew/ui_act(action, list/params)
if(..())
return TRUE
@@ -482,6 +483,7 @@
// Resolve mutation's BYOND path from the alias
var/alias = params["alias"]
var/path = GET_MUTATION_TYPE_FROM_ALIAS(alias)
// Make sure the occupant still has this mutation
if(!(path in scanner_occupant.dna.mutation_index))
return

View File

@@ -178,7 +178,6 @@
dat += "<A href='?src=[REF(src)];login=1'>{Log In}</A>"
var/datum/browser/popup = new(user, "med_rec", "Medical Records Console", 600, 400)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.open()
/obj/machinery/computer/med_data/Topic(href, href_list)

View File

@@ -67,7 +67,6 @@
add_fingerprint(usr)
var/datum/browser/popup = new(user, "computer", title, 400, 500)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.open()
/obj/machinery/computer/pod/process()

View File

@@ -66,7 +66,6 @@
dat += "<HR><A href='?src=[REF(src)];lock=1'>{Log Out}</A>"
var/datum/browser/popup = new(user, "computer", "Prisoner Management Console", 400, 500)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
return

View File

@@ -250,7 +250,6 @@
dat += "<A href='?src=[REF(src)];choice=Log In'>{Log In}</A>"
var/datum/browser/popup = new(user, "secure_rec", "Security Records Console", 600, 400)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
return

View File

@@ -103,7 +103,6 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E
var/datum/browser/popup = new(user, "computer", "Telecrystal Upload/Receive Station", 700, 500)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/computer/telecrystals/uplinker/Topic(href, href_list)
@@ -185,7 +184,6 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E
var/datum/browser/popup = new(user, "computer", "Team Telecrystal Management Console", 700, 500)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/computer/telecrystals/boss/Topic(href, href_list)

View File

@@ -76,7 +76,6 @@
var/datum/browser/popup = new(user, "cryopod_console", "Cryogenic System Control")
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/computer/cryopod/Topic(href, href_list)
@@ -308,7 +307,7 @@
var/mob/living/mob_occupant = occupant
var/list/obj/item/cryo_items = list()
investigate_log("Despawning [key_name(mob_occupant)].", INVESTIGATE_CRYOGENICS)
//Handle Borg stuff first

View File

@@ -74,12 +74,6 @@
/obj/machinery/door/firedoor/Bumped(atom/movable/AM)
if(panel_open || operating || welded)
return
if(ismob(AM))
var/mob/user = AM
if(density && !welded && !operating && !(stat & NOPOWER) && (!density || allow_hand_open(user)))
add_fingerprint(user)
open()
return TRUE
return FALSE
/obj/machinery/door/firedoor/power_change()
@@ -90,14 +84,6 @@
stat |= NOPOWER
/obj/machinery/door/firedoor/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!welded && !operating && !(stat & NOPOWER) && (!density || allow_hand_open(user)))
add_fingerprint(user)
if(density)
emergency_close_timer = world.time + 30 // prevent it from instaclosing again if in space
open()
else
close()
return TRUE
if(operating || !density)
return

View File

@@ -260,7 +260,6 @@
/obj/machinery/doorButtons/airlock_controller/ui_interact(mob/user)
var/datum/browser/popup = new(user, "computer", name)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.set_content(returnText())
popup.open()

View File

@@ -29,7 +29,6 @@
. = ..()
user.set_machine(src)
var/datum/browser/popup = new(user, "computer", name) // Set up the popup browser window
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.set_content(return_text())
popup.open()

View File

@@ -38,3 +38,14 @@
if(stat & (BROKEN|NOPOWER))
return
drive()
/obj/machinery/mass_driver/pressure_plate
name = "pressure plated mass driver"
var/drive_delay = 10
/obj/machinery/mass_driver/pressure_plate/Crossed(atom/movable/O)
. = ..()
if(isliving(O))
var/mob/living/L = O
to_chat(L, "<span class='warning'>You feel something click beneath you!</span>")
addtimer(CALLBACK(src, .proc/drive), drive_delay)

View File

@@ -218,10 +218,10 @@ GLOBAL_LIST_EMPTY(allConsoles)
dat += "<b>Message Authentication</b> <br><br>"
dat += "<b>Message for [dpt]:</b> [message] <br><br>"
dat += "<div class='notice'>You may authenticate your message now by scanning your ID or your stamp</div> <br>"
dat += "<b>Validated by:</b> [msgVerified ? "<span class='good'><b>[msgVerified]</b></span>" : "<i>Not Validated</i>"] <br>"
dat += "<b>Stamped by:</b> [msgStamped ? "<span class='boldnotice'>[msgStamped]</span>" : "<i>Not Stamped</i>"] <br><br>"
dat += "<a href='?src=[REF(src)];department=[dpt]'>Send Message</a> <br><br>"
dat += "<a href='?src=[REF(src)];setScreen=0'><< Discard Message</a> <br>"
@@ -271,7 +271,6 @@ GLOBAL_LIST_EMPTY(allConsoles)
var/datum/browser/popup = new(user, "req_console", "[department] Requests Console", 450, 440)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/requests_console/Topic(href, href_list)
@@ -279,7 +278,7 @@ GLOBAL_LIST_EMPTY(allConsoles)
return
usr.set_machine(src)
add_fingerprint(usr)
if(href_list["write"])
dpt = ckey(reject_bad_text(href_list["write"])) //write contains the string of the receiving department's name
var/new_message = stripped_input(usr, "Write your message:", "Awaiting Input", "", MAX_MESSAGE_LEN)
@@ -358,7 +357,7 @@ GLOBAL_LIST_EMPTY(allConsoles)
workingServer = TRUE
if(!workingServer)
screen = 7
screen = 7
say("NOTICE: No server detected! Please contact your local engineering team.")
updateUsrDialog()
return
@@ -539,7 +538,7 @@ GLOBAL_LIST_EMPTY(allConsoles)
to_chat(user, "<span class='warning'>You are not authorized to send announcements!</span>")
updateUsrDialog()
return
if(istype(O, /obj/item/stamp))
if(screen == 9)
var/obj/item/stamp/T = O

View File

@@ -132,7 +132,6 @@
var/datum/browser/popup = new(user, "slotmachine", "Slot Machine")
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.open()
/obj/machinery/computer/slot_machine/Topic(href, href_list)

View File

@@ -1,12 +1,10 @@
/obj/mecha/proc/get_armour_facing(relative_dir)
switch(relative_dir)
if(0) // BACKSTAB!
if(180) // BACKSTAB!
return facing_modifiers[BACK_ARMOUR]
if(45, 90, 270, 315)
return facing_modifiers[SIDE_ARMOUR]
if(225, 180, 135)
if(0, 45) // direct or 45 degrees off
return facing_modifiers[FRONT_ARMOUR]
return 1 //always return non-0
return facing_modifiers[SIDE_ARMOUR] //if its not a front hit or back hit then assume its from the side
/obj/mecha/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
. = ..()
@@ -43,7 +41,7 @@
break
if(attack_dir)
var/facing_modifier = get_armour_facing(dir2angle(attack_dir) - dir2angle(src))
var/facing_modifier = get_armour_facing(abs(dir2angle(dir) - dir2angle(attack_dir)))
booster_damage_modifier /= facing_modifier
booster_deflection_modifier *= facing_modifier
if(prob(deflect_chance * booster_deflection_modifier))

View File

@@ -17,11 +17,15 @@
var/countdown_colour
var/obj/effect/countdown/anomaly/countdown
/obj/effect/anomaly/Initialize(mapload, new_lifespan)
/// chance we drop a core when neutralized
var/core_drop_chance = 100
/obj/effect/anomaly/Initialize(mapload, new_lifespan, core_drop_chance = 100)
. = ..()
GLOB.poi_list |= src
START_PROCESSING(SSobj, src)
impact_area = get_area(src)
src.core_drop_chance = core_drop_chance
if (!impact_area)
return INITIALIZE_HINT_QDEL
@@ -54,6 +58,8 @@
GLOB.poi_list.Remove(src)
STOP_PROCESSING(SSobj, src)
qdel(countdown)
if(aSignal)
QDEL_NULL(aSignal)
return ..()
/obj/effect/anomaly/proc/anomalyEffect()
@@ -70,12 +76,12 @@
/obj/effect/anomaly/proc/anomalyNeutralize()
new /obj/effect/particle_effect/smoke/bad(loc)
for(var/atom/movable/O in src)
O.forceMove(drop_location())
if(prob(core_drop_chance))
aSignal.forceMove(drop_location())
aSignal = null
qdel(src)
/obj/effect/anomaly/attackby(obj/item/I, mob/user, params)
if(I.tool_behaviour == TOOL_ANALYZER) //revert if runtimed
to_chat(user, "<span class='notice'>Analyzing... [src]'s unstable field is fluctuating along frequency [format_frequency(aSignal.frequency)], code [aSignal.code].</span>")
@@ -285,7 +291,7 @@
S.rabid = TRUE
S.amount_grown = SLIME_EVOLUTION_THRESHOLD
S.Evolve()
offer_control(S)
offer_control(S,POLL_IGNORE_SENTIENCE_POTION)
/////////////////////

View File

@@ -46,4 +46,5 @@
var/turf/T = loc
if(!istype(T)) //you know this will happen somehow
CRASH("Turf decal initialized in an object/nullspace")
T.AddElement(/datum/element/decal, icon, icon_state, turn(dir, -dir2angle(T.dir)), CLEAN_GOD, color, null, null, alpha)
var/turn_dir = 180 - dir2angle(T.dir) //Turning a dir by 0 results in a roulette of random dirs.
T.AddElement(/datum/element/decal, icon, icon_state, turn_dir ? turn(dir, turn_dir) : dir, CLEAN_GOD, color, null, null, alpha)

View File

@@ -242,7 +242,6 @@ RLD
var/datum/browser/popup = new(user, "rcd_access", "Access Control", 900, 500, src)
popup.set_content(t1)
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.open()
/obj/item/construction/rcd/Topic(href, href_list)

View File

@@ -28,39 +28,37 @@
/// triggered on wield of two handed item
/obj/item/broom/proc/on_wield(obj/item/source, mob/user)
to_chat(user, "<span class='notice'>You brace the [src] against the ground in a firm sweeping stance.</span>")
RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/sweep)
RegisterSignal(user, COMSIG_MOVABLE_PRE_MOVE, .proc/sweep)
/// triggered on unwield of two handed item
/obj/item/broom/proc/on_unwield(obj/item/source, mob/user)
UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
UnregisterSignal(user, COMSIG_MOVABLE_PRE_MOVE)
/obj/item/broom/afterattack(atom/A, mob/user, proximity)
. = ..()
if(!proximity)
return
sweep(user, A, FALSE)
sweep(user, A)
/obj/item/broom/proc/sweep(mob/user, atom/A, moving = TRUE)
var/turf/target
if (!moving)
if (isturf(A))
target = A
else
target = A.loc
else
target = user.loc
if (!isturf(target))
/obj/item/broom/proc/sweep(datum/source, atom/newLoc)
if(!ismob(source) || !isturf(newLoc) || (get_dist(source, newLoc) > 1))
return
if (locate(/obj/structure/table) in target.contents)
var/turf/target = newLoc
var/atom/movable/AM
var/sweep_dir = get_dir(source, target)
if(!sweep_dir)
return
for(var/i in target.contents)
AM = i
if(AM.density) // eh good enough heuristic check
return
var/i = 0
for(var/obj/item/garbage in target.contents)
if(!garbage.anchored)
garbage.Move(get_step(target, user.dir), user.dir)
i++
if(i >= 20)
step(garbage, sweep_dir)
if(++i > 20)
break
if(i >= 1)
if(i)
playsound(loc, 'sound/weapons/thudswoosh.ogg', 30, TRUE, -1)
/obj/item/broom/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J) //bless you whoever fixes this copypasta

View File

@@ -13,6 +13,7 @@
var/ignores_timeout = FALSE
var/response_timer_id = null
var/approval_time = 600
var/allow_unicode = FALSE
var/static/regex/standard_station_regex
@@ -48,6 +49,9 @@
if(!new_name)
return
if(!allow_unicode && (length(new_name) != length_char(new_name)))
to_chat(user, "Unicode is not allowed. Adminhelp if you want to use it so badly.")
return
log_game("[key_name(user)] has proposed to name the station as \
[new_name]")

View File

@@ -390,7 +390,7 @@
/obj/item/circuitboard/machine/thermomachine/examine()
. = ..()
. += "<span class='notice'>It is set to layer [pipe_layer].</span>"
. += "<span class='notice'>It is set to layer [pipe_layer]. Use a Multitool on the circuit to change this.</span>"
/obj/item/circuitboard/machine/thermomachine/heater
name = "Heater (Machine Board)"
@@ -1146,3 +1146,8 @@
build_path = /obj/machinery/atmospherics/components/unary/shuttle/heater
req_components = list(/obj/item/stock_parts/micro_laser = 2,
/obj/item/stock_parts/matter_bin = 1)
/obj/item/circuitboard/machine/explosive_compressor
name = "Explosive Compressor (Machine Board)"
build_path = /obj/machinery/research/explosive_compressor
req_components = list(/obj/item/stock_parts/matter_bin = 3)

View File

@@ -735,7 +735,11 @@
if(isobj(target))
if(actually_paints)
var/list/hsl = rgb2hsl(hex2num(copytext(paint_color,2,4)),hex2num(copytext(paint_color,4,6)),hex2num(copytext(paint_color,6,8)))
if(hsl[3] < 0.25 && !istype(target, /obj/structure/window) && !istype(target, /obj/effect/decal/cleanable/crayon)) //Colors too dark are rejected
var/static/whitelisted = typecacheof(list(/obj/structure/window,
/obj/effect/decal/cleanable/crayon,
/obj/machinery/door/window)
)
if(hsl[3] < 0.25 && !whitelisted[target]) //Colors too dark are rejected
to_chat(usr, "<span class='warning'>A color that dark on an object like this? Surely not...</span>")
return FALSE

View File

@@ -394,8 +394,6 @@
to_chat(user, "<span class='warning'>[src] are recharging!</span>")
return
user.stop_pulling() //User has hands full, and we don't care about anyone else pulling on it, their problem. CLEAR!!
if(user.a_intent == INTENT_DISARM)
do_disarm(M, user)
return
@@ -447,8 +445,7 @@
if(do_after(user, isnull(defib?.disarm_shock_time)? disarm_shock_time : defib.disarm_shock_time, target = M))
M.visible_message("<span class='danger'>[user] zaps [M] with [src]!</span>", \
"<span class='userdanger'>[user] zaps [M] with [src]!</span>")
M.adjustStaminaLoss(50)
M.DefaultCombatKnockdown(100)
M.DefaultCombatKnockdown(140)
M.updatehealth() //forces health update before next life tick
playsound(src, 'sound/machines/defib_zap.ogg', 50, 1, -1)
M.emote("gasp")

View File

@@ -266,7 +266,7 @@ GLOBAL_LIST_EMPTY(PDAs)
var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/simple/pda)
assets.send(user)
var/datum/asset/spritesheet/emoji_s = get_asset_datum(/datum/asset/spritesheet/goonchat)
var/datum/asset/spritesheet/emoji_s = get_asset_datum(/datum/asset/spritesheet/chat)
emoji_s.send(user) //Already sent by chat but no harm doing this
user.set_machine(src)

View File

@@ -590,7 +590,7 @@ Code:
var/static/list/emoji_icon_states
var/static/emoji_table
if(!emoji_table)
var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/goonchat)
var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/chat)
var/list/collate = list("<br><table>")
for(var/emoji in sortList(icon_states(icon('icons/emoji.dmi'))))
var/tag = sheet.icon_tag("emoji-[emoji]")

View File

@@ -0,0 +1,207 @@
/obj/item/storage/portable_chem_mixer
name = "Portable Chemical Mixer"
desc = "A portable device that dispenses and mixes chemicals. All necessary reagents need to be supplied with beakers. A label indicates that a screwdriver is required to open it for refills. This device can be worn on a belt. The letters 'S&T' are imprinted on the side."
icon = 'icons/obj/chemical.dmi'
icon_state = "portablechemicalmixer_open"
w_class = WEIGHT_CLASS_HUGE
slot_flags = ITEM_SLOT_BELT
custom_price = 2000
custom_premium_price = 2000
var/obj/item/reagent_containers/beaker = null ///Creating an empty slot for a beaker that can be added to dispense into
var/amount = 30 ///The amount of reagent that is to be dispensed currently
var/list/dispensable_reagents = list() ///List in which all currently dispensable reagents go
/obj/item/storage/portable_chem_mixer/ComponentInitialize()
. = ..()
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
STR.max_combined_w_class = 200
STR.max_items = 50
STR.insert_preposition = "in"
STR.can_hold = typecacheof(list(
/obj/item/reagent_containers/glass/beaker,
))
/obj/item/storage/portable_chem_mixer/Destroy()
QDEL_NULL(beaker)
return ..()
/obj/item/storage/portable_chem_mixer/ex_act(severity, target)
if(severity < 3)
..()
/obj/item/storage/portable_chem_mixer/attackby(obj/item/I, mob/user, params)
var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
if (I.tool_behaviour == TOOL_SCREWDRIVER)
SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, !locked)
if (!locked)
update_contents()
if (locked)
replace_beaker(user)
update_icon()
I.play_tool_sound(src, 50)
return
else if (istype(I, /obj/item/reagent_containers) && !(I.item_flags & ABSTRACT) && I.is_open_container() && locked)
var/obj/item/reagent_containers/B = I
. = TRUE //no afterattack
if(!user.transferItemToLoc(B, src))
return
replace_beaker(user, B)
update_icon()
updateUsrDialog()
return
return ..()
/**
* Updates the contents of the portable chemical mixer
*
* A list of dispensable reagents is created by iterating through each source beaker in the portable chemical beaker and reading its contents
*/
/obj/item/storage/portable_chem_mixer/proc/update_contents()
dispensable_reagents.Cut()
for (var/obj/item/reagent_containers/glass/beaker/B in contents)
var/key = B.reagents.get_master_reagent_id()
if (!(key in dispensable_reagents))
dispensable_reagents[key] = list()
dispensable_reagents[key]["reagents"] = list()
dispensable_reagents[key]["reagents"] += B.reagents
return
/obj/item/storage/portable_chem_mixer/update_icon_state()
var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
if (!locked)
icon_state = "portablechemicalmixer_open"
else if (beaker)
icon_state = "portablechemicalmixer_full"
else
icon_state = "portablechemicalmixer_empty"
/obj/item/storage/portable_chem_mixer/AltClick(mob/living/user)
var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
if (!locked)
return ..()
if(!can_interact(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
return
replace_beaker(user)
update_icon()
/**
* Replaces the beaker of the portable chemical mixer with another beaker, or simply adds the new beaker if none is in currently
*
* Checks if a valid user and a valid new beaker exist and attempts to replace the current beaker in the portable chemical mixer with the one in hand. Simply places the new beaker in if no beaker is currently loaded
* Arguments:
* * mob/living/user - The user who is trying to exchange beakers
* * obj/item/reagent_containers/new_beaker - The new beaker that the user wants to put into the device
*/
/obj/item/storage/portable_chem_mixer/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker)
if(!user)
return FALSE
if(beaker)
user.put_in_hands(beaker)
beaker = null
if(new_beaker)
beaker = new_beaker
return TRUE
/obj/item/storage/portable_chem_mixer/attack_hand(mob/user)
if (loc != user)
return ..()
if(SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED))
ui_interact(user)
return
/obj/item/storage/portable_chem_mixer/attack_self(mob/user)
if(loc == user)
var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
if (locked)
ui_interact(user)
return
else
to_chat(user, "<span class='notice'>The portable chemical mixer is currently open and its contents can be accessed.</span>")
return
return
/obj/item/storage/portable_chem_mixer/MouseDrop(obj/over_object)
. = ..()
if(ismob(loc))
var/mob/M = loc
if(!M.incapacitated() && istype(over_object, /obj/screen/inventory/hand))
var/obj/screen/inventory/hand/H = over_object
M.putItemFromInventoryInHandIfPossible(src, H.held_index)
/obj/item/storage/portable_chem_mixer/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "PortableChemMixer", name)
if(user.hallucinating())
// to not ruin the immersion by constantly changing the fake chemicals
ui.set_autoupdate(FALSE)
ui.open()
/obj/item/storage/portable_chem_mixer/ui_data(mob/user)
var/list/data = list()
data["amount"] = amount
data["isBeakerLoaded"] = beaker ? 1 : 0
data["beakerCurrentVolume"] = beaker ? beaker.reagents.total_volume : null
data["beakerMaxVolume"] = beaker ? beaker.volume : null
data["beakerTransferAmounts"] = beaker ? beaker.possible_transfer_amounts : null
var/chemicals[0]
var/is_hallucinating = user.hallucinating()
if(user.hallucinating())
is_hallucinating = TRUE
for(var/re in dispensable_reagents)
var/value = dispensable_reagents[re]
var/datum/reagent/temp = GLOB.chemical_reagents_list[re]
if(temp)
var/chemname = temp.name
var/total_volume = 0
for (var/datum/reagents/rs in value["reagents"])
total_volume += rs.total_volume
if(is_hallucinating && prob(5))
chemname = "[pick_list_replacements("hallucination.json", "chemicals")]"
chemicals.Add(list(list("title" = chemname, "id" = ckey(temp.name), "volume" = total_volume )))
data["chemicals"] = chemicals
var/beakerContents[0]
if(beaker)
for(var/datum/reagent/R in beaker.reagents.reagent_list)
beakerContents.Add(list(list("name" = R.name, "id" = ckey(R.name), "volume" = R.volume))) // list in a list because Byond merges the first list...
data["beakerContents"] = beakerContents
return data
/obj/item/storage/portable_chem_mixer/ui_act(action, params)
if(..())
return
switch(action)
if("amount")
var/target = text2num(params["target"])
amount = target
. = TRUE
if("dispense")
var/reagent_name = params["reagent"]
var/datum/reagent/reagent = GLOB.name2reagent[reagent_name]
var/entry = dispensable_reagents[reagent]
if(beaker)
var/datum/reagents/R = beaker.reagents
var/actual = min(amount, 1000, R.maximum_volume - R.total_volume)
// todo: add check if we have enough reagent left
for (var/datum/reagents/source in entry["reagents"])
var/to_transfer = min(source.total_volume, actual)
source.trans_to(beaker, to_transfer)
actual -= to_transfer
if (actual <= 0)
break
. = TRUE
if("remove")
var/amount = text2num(params["amount"])
beaker.reagents.remove_all(amount)
. = TRUE
if("eject")
replace_beaker(usr)
update_icon()
. = TRUE

View File

@@ -16,9 +16,6 @@
var/on = TRUE
var/shock_cooldown = FALSE
var/ui_x = 260
var/ui_y = 137
/obj/item/electropack/suicide_act(mob/living/carbon/user)
user.visible_message("<span class='suicide'>[user] hooks [user.p_them()]self to the electropack and spams the trigger! It looks like [user.p_theyre()] trying to commit suicide!</span>")
return (FIRELOSS)
@@ -201,17 +198,7 @@
else
return ..()
/obj/item/electropack/shockcollar/ui_interact(mob/user) //note to src: use tgooey
var/dat = {"
<TT>
<B>Frequency/Code</B> for shock collar:<BR>
Frequency:
[format_frequency(src.frequency)]
<A href='byond://?src=[REF(src)];set=freq'>Set</A><BR>
Code:
[src.code]
<A href='byond://?src=[REF(src)];set=code'>Set</A><BR>
</TT>"}
user << browse(dat, "window=radio")
onclose(user, "radio")
return
/obj/item/electropack/ui_act(action, params)
if(action == "power") // DO. NOT.
return FALSE
return ..()

View File

@@ -729,10 +729,10 @@ GENETICS SCANNER
to_chat(user, "<span class='notice'>[target] is empty!</span>")
if(cached_scan_results && cached_scan_results["fusion"]) //notify the user if a fusion reaction was detected
var/fusion_power = round(cached_scan_results["fusion"], 0.01)
var/tier = fusionpower2text(fusion_power)
var/instability = round(cached_scan_results["fusion"], 0.01)
var/tier = instability2text(instability)
to_chat(user, "<span class='boldnotice'>Large amounts of free neutrons detected in the air indicate that a fusion reaction took place.</span>")
to_chat(user, "<span class='notice'>Power of the last fusion reaction: [fusion_power]\n This power indicates it was a [tier]-tier fusion reaction.</span>")
to_chat(user, "<span class='notice'>Instability of the last fusion reaction: [instability]\n This indicates it was [tier].</span>")
return
/obj/item/analyzer/proc/scan_turf(mob/user, turf/location)
@@ -783,10 +783,10 @@ GENETICS SCANNER
to_chat(user, "<span class='info'>Temperature: [round(environment.return_temperature()-T0C, 0.01)] &deg;C ([round(environment.return_temperature(), 0.01)] K)</span>")
if(cached_scan_results && cached_scan_results["fusion"]) //notify the user if a fusion reaction was detected
var/fusion_power = round(cached_scan_results["fusion"], 0.01)
var/tier = fusionpower2text(fusion_power)
var/instability = round(cached_scan_results["fusion"], 0.01)
var/tier = instability2text(instability)
to_chat(user, "<span class='boldnotice'>Large amounts of free neutrons detected in the air indicate that a fusion reaction took place.</span>")
to_chat(user, "<span class='notice'>Power of the last fusion reaction: [fusion_power]\n This power indicates it was a [tier]-tier fusion reaction.</span>")
to_chat(user, "<span class='notice'>Instability of the last fusion reaction: [instability]\n This indicates it was [tier].</span>")
/obj/item/analyzer/ranged
desc = "A hand-held scanner which uses advanced spectroscopy and infrared readings to analyze gases as a distance. Alt-Click to use the built in barometer function."
@@ -992,4 +992,4 @@ GENETICS SCANNER
#undef SCANMODE_CHEMICAL
#undef SCANMODE_WOUND
#undef SCANNER_CONDENSED
#undef SCANNER_VERBOSE
#undef SCANNER_VERBOSE

View File

@@ -289,3 +289,9 @@
. = TRUE
update_icon()
/**
* Returns if this is ready to be detonated. Checks if both tanks are in place.
*/
/obj/item/transfer_valve/proc/ready()
return tank_one && tank_two

View File

@@ -26,6 +26,7 @@
wound_bonus = -110
bare_wound_bonus = 20
block_parry_data = /datum/block_parry_data/dual_esword
block_chance = 60
var/hacked = FALSE
/// Can this reflect all energy projectiles?
var/can_reflect = TRUE
@@ -38,7 +39,8 @@
var/wielded = FALSE // track wielded status on item
var/slowdown_wielded = 0
/datum/block_parry_data/dual_esword
/datum/block_parry_data/dual_esword // please run at the man going apeshit with his funny doublesword
can_block_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST | BLOCK_DIR_WEST | BLOCK_DIR_EAST
block_damage_absorption = 2
block_damage_multiplier = 0.15
block_damage_multiplier_override = list(
@@ -50,10 +52,10 @@
block_lock_sprinting = TRUE
// no attacking while blocking
block_lock_attacking = TRUE
block_projectile_mitigation = 75
block_projectile_mitigation = 85
// more efficient vs projectiles
block_stamina_efficiency_override = list(
TEXT_ATTACK_TYPE_PROJECTILE = 4
TEXT_ATTACK_TYPE_PROJECTILE = 6
)
parry_time_windup = 0

View File

@@ -234,6 +234,9 @@
/obj/item/melee/rapier/attack(mob/living/target, mob/living/user)
. = ..()
if(iscarbon(target))
if(HAS_TRAIT(user, TRAIT_PACIFISM))
visible_message("<span class='warning'>[user] gently taps [target] with [src].</span>",null,null,COMBAT_MESSAGE_RANGE)
log_combat(user, target, "slept", src)
var/mob/living/carbon/H = target
H.Dizzy(10)
H.adjustStaminaLoss(30)

View File

@@ -255,7 +255,7 @@
/obj/item/choice_beacon/box/plushie/generate_display_names()
var/list/plushie_list = list()
//plushie set 1: just subtypes of /obj/item/toy/plush
var/list/plushies_set_one = subtypesof(/obj/item/toy/plush) - list(/obj/item/toy/plush/narplush, /obj/item/toy/plush/awakenedplushie, /obj/item/toy/plush/random_snowflake, /obj/item/toy/plush/random) //don't allow these special ones (you can still get narplush/hugbox)
var/list/plushies_set_one = subtypesof(/obj/item/toy/plush) - list(/obj/item/toy/plush/narplush, /obj/item/toy/plush/awakenedplushie, /obj/item/toy/plush/random_snowflake, /obj/item/toy/plush/plushling, /obj/item/toy/plush/random) //don't allow these special ones (you can still get narplush/hugbox)
for(var/V in plushies_set_one)
var/atom/A = V
plushie_list[initial(A.name)] = A

View File

@@ -167,7 +167,7 @@
return
log_game("[key_name(user)] activated a hidden grenade in [src].")
grenade.preprime(user, msg = FALSE, volume = 10)
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT,"plushpet", /datum/mood_event/plushpet)
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT,"plushpet", /datum/mood_event/plushpet)
else
to_chat(user, "<span class='notice'>You try to pet [src], but it has no stuffing. Aww...</span>")
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT,"plush_nostuffing", /datum/mood_event/plush_nostuffing)
@@ -688,18 +688,6 @@ GLOBAL_LIST_INIT(valid_plushie_paths, valid_plushie_paths())
icon_state = "scrubpuppy"
item_state = "scrubpuppy"
/obj/item/toy/plush/borgplushie/meddrake
name = "MediDrake Plushie"
desc = "An adorable stuffed toy of a Medidrake."
icon_state = "meddrake"
item_state = "meddrake"
/obj/item/toy/plush/borgplushie/secdrake
name = "SecDrake Plushie"
desc = "An adorable stuffed toy of a Secdrake."
icon_state = "secdrake"
item_state = "secdrake"
/obj/item/toy/plush/aiplush
name = "AI plushie"
desc = "A little stuffed toy AI core... it appears to be malfunctioning."
@@ -766,8 +754,8 @@ GLOBAL_LIST_INIT(valid_plushie_paths, valid_plushie_paths())
attack_verb = list("headbutt", "scritched", "bit")
squeak_override = list('modular_citadel/sound/voice/nya.ogg' = 1)
can_random_spawn = FALSE
/obj/item/toy/plush/hairball
name = "Hairball"
desc = "A bundle of undigested fibers and scales. Yuck."
@@ -777,3 +765,78 @@ GLOBAL_LIST_INIT(valid_plushie_paths, valid_plushie_paths())
squeak_override = list('sound/misc/splort.ogg'=1)
attack_verb = list("sploshed", "splorted", "slushed")
can_random_spawn = FALSE
/obj/item/toy/plush/plushling
name = "peculiar plushie"
desc = "An adorable stuffed toy- wait, did it just move?"
can_random_spawn = FALSE
var/absorb_cooldown = 100 //ticks cooldown between absorbs
var/next_absorb = 0 //When can it absorb another plushie
var/check_interval = 20
var/next_check = 0
//Overrides parent proc
/obj/item/toy/plush/plushling/attack_self(mob/user)
if(!user) //hmmmmm
return
to_chat(user, "<span class='warning'>You try to pet the plushie, but recoil as it bites your hand instead! OW!</span>")
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT,"plush_bite", /datum/mood_event/plush_bite)
var/mob/living/carbon/human/H = user
if(!H)
return //Type safety.
H.apply_damage(5, BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
addtimer(CALLBACK(H, /mob/living/carbon/human.proc/dropItemToGround, src, TRUE), 1)
/obj/item/toy/plush/plushling/New()
var/initial_state = pick("plushie_lizard", "plushie_snake", "plushie_slime", "fox")
icon_state = initial_state
item_state = initial_state
START_PROCESSING(SSobj, src)
. = ..()
/obj/item/toy/plush/plushling/Destroy()
STOP_PROCESSING(SSobj, src)
. = ..()
/obj/item/toy/plush/plushling/process()
if(world.time < next_absorb || world.time < next_check)
return
next_check = world.time + check_interval
var/obj/item/toy/plush/target
for(var/obj/item/toy/plush/possible_target in loc) //First, it tries to get anything in its same location, be it a tile or a backpack
if(possible_target == src || istype(possible_target, /obj/item/toy/plush/plushling))
continue
target = possible_target
break
if(!target)
if(!isturf(loc))
return
for(var/obj/item/toy/plush/P in oview(1, src)) //If that doesn't work, it hunts for plushies adjacent to its own tile
if(istype(P, /obj/item/toy/plush/plushling)) //These do not hunt their own kind
continue
src.throw_at(P, 1, 2)
visible_message("<span class='danger'>[src] leaps at [P]!</span>")
break
return
if(istype(target, /obj/item/toy/plush/plushling)) //These do not consume their own.
return
next_absorb = world.time + absorb_cooldown
plushie_absorb(target)
/obj/item/toy/plush/plushling/proc/plushie_absorb(obj/item/toy/plush/victim)
if(!victim)
return
visible_message("<span class='warning'>[src] gruesomely mutilliates [victim], leaving nothing more than dust!</span>")
name = victim.name
desc = victim.desc + " Wait, did it just move..?"
icon_state = victim.icon_state
item_state = victim.item_state
squeak_override = victim.squeak_override
attack_verb = victim.attack_verb
new /obj/effect/decal/cleanable/ash(get_turf(victim))
qdel(victim)
/obj/item/toy/plush/plushling/love(obj/item/toy/plush/Kisser, mob/living/user) //You shouldn't have come here, poor plush.
if(!Kisser)
return
plushie_absorb(Kisser)

View File

@@ -117,6 +117,7 @@ GLOBAL_LIST_INIT(diamond_recipes, list ( \
new/datum/stack_recipe("Captain Statue", /obj/structure/statue/diamond/captain, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("AI Hologram Statue", /obj/structure/statue/diamond/ai1, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("AI Core Statue", /obj/structure/statue/diamond/ai2, 5, one_per_turf = 1, on_floor = 1), \
// new/datum/stack_recipe("diamond brick", /obj/item/ingot/diamond, 6, time = 100), \ not yet
))
/obj/item/stack/sheet/mineral/diamond/get_main_recipes()
@@ -145,6 +146,7 @@ GLOBAL_LIST_INIT(uranium_recipes, list ( \
new/datum/stack_recipe("uranium tile", /obj/item/stack/tile/mineral/uranium, 1, 4, 20), \
new/datum/stack_recipe("Nuke Statue", /obj/structure/statue/uranium/nuke, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Engineer Statue", /obj/structure/statue/uranium/eng, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("uranium ingot", /obj/item/ingot/uranium, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/uranium/get_main_recipes()
@@ -177,6 +179,7 @@ GLOBAL_LIST_INIT(plasma_recipes, list ( \
new/datum/stack_recipe("plasma door", /obj/structure/mineral_door/transparent/plasma, 10, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("plasma tile", /obj/item/stack/tile/mineral/plasma, 1, 4, 20), \
new/datum/stack_recipe("Scientist Statue", /obj/structure/statue/plasma/scientist, 5, one_per_turf = 1, on_floor = 1), \
// new/datum/stack_recipe("plasma ingot", /obj/item/ingot/plasma, 6, time = 100), \ no
))
/obj/item/stack/sheet/mineral/plasma/get_main_recipes()
@@ -221,6 +224,7 @@ GLOBAL_LIST_INIT(gold_recipes, list ( \
new/datum/stack_recipe("RD Statue", /obj/structure/statue/gold/rd, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Simple Crown", /obj/item/clothing/head/crown, 5), \
new/datum/stack_recipe("CMO Statue", /obj/structure/statue/gold/cmo, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("gold ingot", /obj/item/ingot/gold, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/gold/get_main_recipes()
@@ -252,6 +256,7 @@ GLOBAL_LIST_INIT(silver_recipes, list ( \
new/datum/stack_recipe("Sec Officer Statue", /obj/structure/statue/silver/sec, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Sec Borg Statue", /obj/structure/statue/silver/secborg, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Med Borg Statue", /obj/structure/statue/silver/medborg, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("silver ingot", /obj/item/ingot/silver, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/silver/get_main_recipes()
@@ -278,6 +283,7 @@ GLOBAL_LIST_INIT(silver_recipes, list ( \
GLOBAL_LIST_INIT(bananium_recipes, list ( \
new/datum/stack_recipe("bananium tile", /obj/item/stack/tile/mineral/bananium, 1, 4, 20), \
new/datum/stack_recipe("Clown Statue", /obj/structure/statue/bananium/clown, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("hilarious ingot", /obj/item/ingot/bananium, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/bananium/get_main_recipes()
@@ -306,6 +312,7 @@ GLOBAL_LIST_INIT(bananium_recipes, list ( \
GLOBAL_LIST_INIT(titanium_recipes, list ( \
new/datum/stack_recipe("titanium tile", /obj/item/stack/tile/mineral/titanium, 1, 4, 20), \
new/datum/stack_recipe("titanic ingot", /obj/item/ingot/titanium, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/titanium/get_main_recipes()
@@ -353,6 +360,7 @@ GLOBAL_LIST_INIT(plastitanium_recipes, list ( \
*/
GLOBAL_LIST_INIT(adamantine_recipes, list(
new /datum/stack_recipe("incomplete servant golem shell", /obj/item/golem_shell/servant, req_amount=1, res_amount=1),
new/datum/stack_recipe("adamant ingot", /obj/item/ingot/adamantine, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/adamantine

View File

@@ -121,6 +121,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
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), \
new/datum/stack_recipe("iron ingot", /obj/item/ingot/iron, 6, time = 100), \
))
/obj/item/stack/sheet/metal
@@ -556,6 +557,9 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list ( \
new/datum/stack_recipe("forge", /obj/structure/destructible/cult/forge, 3, time = 40, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("archives", /obj/structure/destructible/cult/tome, 3, time = 40, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("altar", /obj/structure/destructible/cult/talisman, 3, time = 40, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("anvil", /obj/structure/anvil/obtainable/narsie, 4, time = 40, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("runic ingot", /obj/item/ingot/cult, 2, time = 100), \
new/datum/stack_recipe("rune smith's hammer", /obj/item/melee/smith/hammer/narsie, 6), \
))
/obj/item/stack/sheet/runed_metal
@@ -618,6 +622,8 @@ GLOBAL_LIST_INIT(brass_recipes, list ( \
new/datum/stack_recipe("brass bar stool", /obj/structure/chair/stool/bar/brass, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("brass stool", /obj/structure/chair/stool/brass, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("brass table frame", /obj/structure/table_frame/brass, 1, time = 5, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("brass anvil", /obj/structure/anvil/obtainable/ratvar, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("brass furnace", /obj/structure/furnace/infinite/ratvar, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
null, \
new/datum/stack_recipe("sender - pressure sensor", /obj/structure/destructible/clockwork/trap/trigger/pressure_sensor, 2, time = 20, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("sender - mech sensor", /obj/structure/destructible/clockwork/trap/trigger/pressure_sensor/mech, 2, time = 20, one_per_turf = TRUE, on_floor = TRUE), \
@@ -629,6 +635,8 @@ GLOBAL_LIST_INIT(brass_recipes, list ( \
new/datum/stack_recipe("receiver - power nullifier", /obj/structure/destructible/clockwork/trap/power_nullifier, 5, time = 20, one_per_turf = TRUE, on_floor = TRUE, placement_checks = STACK_CHECK_CARDINALS), \
null, \
new/datum/stack_recipe("brass flask", /obj/item/reagent_containers/food/drinks/bottle/holyoil/empty), \
new/datum/stack_recipe("brass smith's hammer", /obj/item/melee/smith/hammer/ratvar, 6), \
new/datum/stack_recipe("brass ingot", /obj/item/ingot/ratvar, 6, time = 100), \
))
/obj/item/stack/tile/brass
@@ -684,7 +692,10 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \
new/datum/stack_recipe("bronze chair", /obj/structure/chair/bronze, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("bronze bar stool", /obj/structure/chair/stool/bar/bronze, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("bronze stool", /obj/structure/chair/stool/bronze, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new /datum/stack_recipe("bronze floor tiles", /obj/item/stack/tile/bronze, 1, 4, 20), \
new/datum/stack_recipe("bronze anvil",/obj/structure/anvil/obtainable/bronze, 20, time = 110, one_per_turf = TRUE, on_floor = TRUE), \
null,
new/datum/stack_recipe("bronze ingot", /obj/item/ingot/bronze, 6, time = 100), \
new/datum/stack_recipe("bronze floor tiles", /obj/item/stack/tile/bronze, 1, 4, 20), \
))
/obj/item/stack/sheet/bronze

View File

@@ -815,3 +815,18 @@
attack_verb = list("bashed", "slashes", "prods", "pokes")
fitting_swords = list(/obj/item/melee/rapier)
starting_sword = /obj/item/melee/rapier
/obj/item/storage/belt/sabre/twin
name = "twin sheath"
desc = "Two sheaths. One is capable of holding a katana (or bokken) and the other a wakizashi. You could put two wakizashis in if you really wanted to. Now you can really roleplay as a samurai."
icon_state = "twinsheath"
item_state = "quiver" //this'll do.
w_class = WEIGHT_CLASS_BULKY
fitting_swords = list(/obj/item/melee/smith/wakizashi, /obj/item/melee/smith/twohand/katana, /obj/item/melee/bokken)
starting_sword = null
/obj/item/storage/belt/sabre/twin/ComponentInitialize()
. = ..()
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
STR.max_items = 2
STR.max_w_class = WEIGHT_CLASS_BULKY + WEIGHT_CLASS_NORMAL //katana and waki.

View File

@@ -237,6 +237,11 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
resistance_flags = FIRE_PROOF
total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
/obj/item/katana/lavaland
desc = "Woefully underpowered in Lavaland."
block_chance = 30
force = 25 //Like a fireaxe but one handed and can block!
/obj/item/katana/cursed
slot_flags = null

View File

@@ -143,10 +143,18 @@
var/amt = max(0, ((force - (move_resist * MOVE_FORCE_CRUSH_RATIO)) / (move_resist * MOVE_FORCE_CRUSH_RATIO)) * 10)
take_damage(amt, BRUTE)
#define BLACKLISTED_OBJECTS list(/obj/machinery/power/apc, /obj/machinery/airalarm, /obj/machinery/power/smes, /obj/structure/cable)
/obj/attack_slime(mob/living/simple_animal/slime/user)
if(!user.is_adult)
return
attack_generic(user, rand(10, 15), "melee", 1)
if(src.type in BLACKLISTED_OBJECTS)
return
if(istype(src, /obj/machinery/atmospherics))
return
attack_generic(user, rand(10, 15), BRUTE, "melee", 1)
#undef BLACKLISTED_OBJECTS
/obj/mech_melee_attack(obj/mecha/M)
M.do_attack_animation(src)

View File

@@ -304,18 +304,20 @@
/obj/proc/reskin_obj(mob/M)
if(!LAZYLEN(unique_reskin))
return
var/dat = "<b>Reskin options for [name]:</b>\n"
for(var/V in unique_reskin)
var/output = icon2html(src, M, unique_reskin[V])
dat += "[V]: <span class='reallybig'>[output]</span>\n"
to_chat(M, dat)
var/choice = input(M, always_reskinnable ? "Choose the a reskin for [src]" : "Warning, you can only reskin [src] once!","Reskin Object") as null|anything in unique_reskin
if(QDELETED(src) || !choice || (current_skin && !always_reskinnable) || M.incapacitated() || !in_range(M,src) || !unique_reskin[choice] || unique_reskin[choice] == current_skin)
return
current_skin = choice
var/list/skins = list()
for(var/S in unique_reskin)
skins[S] = image(icon = icon, icon_state = unique_reskin[S])
var/choice = show_radial_menu(M, src, skins, custom_check = CALLBACK(src, .proc/check_skinnable, M), radius = 40, require_near = TRUE)
if(!choice)
return FALSE
icon_state = unique_reskin[choice]
to_chat(M, "[src] is now skinned as '[choice]'.")
current_skin = choice
return
/obj/proc/check_skinnable(/mob/M)
if(current_skin || !always_reskinnable)
return FALSE
return TRUE
/obj/update_overlays()
. = ..()

View File

@@ -4,25 +4,6 @@
req_access = list(ACCESS_ALL_PERSONAL_LOCKERS)
var/registered_name = null
/obj/structure/closet/secure_closet/personal/examine(mob/user)
. = ..()
if(registered_name)
. += "<span class='notice'>The display reads, \"Owned by [registered_name]\".</span>"
/obj/structure/closet/secure_closet/personal/check_access(obj/item/I)
. = ..()
if(!I || !istype(I))
return
if(istype(I,/obj/item/modular_computer/tablet))
var/obj/item/modular_computer/tablet/ourTablet = I
var/obj/item/computer_hardware/card_slot/card_slot = ourTablet.all_components[MC_CARD]
if(card_slot)
return registered_name == card_slot.stored_card.registered_name || registered_name == card_slot.stored_card2.registered_name
var/obj/item/card/id/ID = I.GetID()
if(ID && registered_name == ID.registered_name)
return TRUE
return FALSE
/obj/structure/closet/secure_closet/personal/PopulateContents()
..()
if(prob(50))
@@ -54,15 +35,24 @@
/obj/structure/closet/secure_closet/personal/attackby(obj/item/W, mob/user, params)
var/obj/item/card/id/I = W.GetID()
if(!I || !istype(I))
return ..()
if(!can_lock(user, FALSE)) //Can't do anything if there isn't a lock!
return
if(I.registered_name && !registered_name)
to_chat(user, "<span class='notice'>You claim [src].</span>")
registered_name = I.registered_name
if(istype(I))
if(broken)
to_chat(user, "<span class='danger'>It appears to be broken.</span>")
return
if(!I || !I.registered_name)
return
if(allowed(user) || !registered_name || (istype(I) && (registered_name == I.registered_name)))
//they can open all lockers, or nobody owns this, or they own this locker
locked = !locked
update_icon()
if(!registered_name)
registered_name = I.registered_name
desc = "Owned by [I.registered_name]."
else
to_chat(user, "<span class='danger'>Access Denied.</span>")
else
..()
return ..()
/obj/structure/closet/secure_closet/personal/handle_lock_addition() //If lock construction is successful we don't care what access the electronics had, so we override it
if(..())

View File

@@ -10,6 +10,7 @@
max_mobs = 3
max_integrity = 250
mob_types = list(/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/tendril)
var/loot_type = /obj/structure/closet/crate/necropolis/tendril/all
move_resist=INFINITY // just killing it tears a massive hole in the ground, let's not move it
anchored = TRUE
@@ -41,7 +42,7 @@ GLOBAL_LIST_INIT(tendrils, list())
/obj/structure/spawner/lavaland/deconstruct(disassembled)
new /obj/effect/collapse(loc)
new /obj/structure/closet/crate/necropolis/tendril(loc)
new loot_type(loc)
return ..()

View File

@@ -13,7 +13,7 @@
var/icon_regular_floor = "floor" //used to remember what icon the tile should have by default
var/icon_plating = "plating"
thermal_conductivity = 0.040
thermal_conductivity = 0.004
heat_capacity = 10000
intact = 1
var/broken = 0

View File

@@ -3,7 +3,7 @@
name = "reinforced floor"
desc = "Extremely sturdy."
icon_state = "engine"
thermal_conductivity = 0.025
thermal_conductivity = 0.0025
heat_capacity = INFINITY
floor_tile = /obj/item/stack/rods
footstep = FOOTSTEP_PLATING

View File

@@ -90,7 +90,7 @@
icon = 'icons/turf/walls/plasma_wall.dmi'
icon_state = "plasma"
sheet_type = /obj/item/stack/sheet/mineral/plasma
thermal_conductivity = 0.04
thermal_conductivity = 0.004
canSmoothWith = list(/turf/closed/wall/mineral/plasma, /obj/structure/falsewall/plasma)
/turf/closed/wall/mineral/plasma/attackby(obj/item/W, mob/user, params)

View File

@@ -11,6 +11,11 @@ GLOBAL_LIST(topic_status_cache)
/world/New()
if (fexists(EXTOOLS))
call(EXTOOLS, "maptick_initialize")()
#ifdef EXTOOLS_LOGGING
call(EXTOOLS, "init_logging")()
else
CRASH("[EXTOOLS] does not exist!")
#endif
enable_debugger()
#ifdef REFERENCE_TRACKING
enable_reference_tracking()
@@ -20,8 +25,6 @@ GLOBAL_LIST(topic_status_cache)
log_world("World loaded at [TIME_STAMP("hh:mm:ss", FALSE)]!")
SetupExternalRSC()
GLOB.config_error_log = GLOB.world_manifest_log = GLOB.world_pda_log = GLOB.world_job_debug_log = GLOB.sql_error_log = GLOB.world_href_log = GLOB.world_runtime_log = GLOB.world_attack_log = GLOB.world_game_log = "data/logs/config_error.[GUID()].log" //temporary file used to record errors with loading config, moved to log directory once logging is set bl
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
@@ -87,17 +90,6 @@ GLOBAL_LIST(topic_status_cache)
#endif
SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, /proc/addtimer, cb, 10 SECONDS))
/world/proc/SetupExternalRSC()
#if (PRELOAD_RSC == 0)
GLOB.external_rsc_urls = world.file2list("[global.config.directory]/external_rsc_urls.txt","\n")
var/i=1
while(i<=GLOB.external_rsc_urls.len)
if(GLOB.external_rsc_urls[i])
i++
else
GLOB.external_rsc_urls.Cut(i,i+1)
#endif
/world/proc/SetupLogs()
var/override_dir = params[OVERRIDE_LOG_DIRECTORY_PARAMETER]
if(!override_dir)
@@ -287,6 +279,7 @@ GLOBAL_LIST(topic_status_cache)
GM.__gasmixture_unregister()
num_deleted++
log_world("Deallocated [num_deleted] gas mixtures")
shutdown_logging() // makes sure the thread is closed before end, else we terminate
..()
/world/proc/update_status()