Merge remote-tracking branch 'ParadiseSS13/master' into ci-upgrades

This commit is contained in:
AffectedArc07
2021-09-26 17:02:34 +01:00
103 changed files with 6274 additions and 4807 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -886,7 +886,7 @@
},
/obj/effect/decal/cleanable/cobweb,
/obj/machinery/computer/prisoner{
dir = 1
dir = 4
},
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
dir = 6
@@ -4371,6 +4371,7 @@
"sO" = (
/obj/effect/decal/cleanable/dirt,
/obj/machinery/computer/security{
dir = 4;
network = list("Labor Camp")
},
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
@@ -5275,7 +5276,7 @@
/area/mine/production)
"VJ" = (
/obj/machinery/computer/secure_data{
dir = 1
dir = 8
},
/turf/simulated/floor/plasteel{
dir = 1;
+131 -45
View File
@@ -651,7 +651,9 @@
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/administration)
"cH" = (
/obj/machinery/computer/shuttle/ferry,
/obj/machinery/computer/shuttle/ferry{
dir = 8
},
/turf/simulated/floor/plating,
/area/centcom/evac)
"cI" = (
@@ -661,7 +663,9 @@
/turf/simulated/floor/plating/airless,
/area/shuttle/specops)
"cK" = (
/obj/machinery/computer/shuttle/admin,
/obj/machinery/computer/shuttle/admin{
dir = 8
},
/turf/simulated/floor/plating,
/area/centcom/evac)
"cL" = (
@@ -1387,7 +1391,9 @@
/turf/simulated/floor/wood,
/area/syndicate_mothership)
"fi" = (
/obj/machinery/computer/shuttle/syndicate/recall,
/obj/machinery/computer/shuttle/syndicate/recall{
dir = 1
},
/turf/simulated/floor/wood,
/area/syndicate_mothership)
"fj" = (
@@ -1411,8 +1417,8 @@
/turf/simulated/wall/indestructible/riveted,
/area/centcom/gamma)
"fn" = (
/obj/machinery/computer/library/checkout,
/obj/structure/table/wood,
/obj/machinery/computer/library/checkout,
/turf/simulated/floor/engine/cult,
/area/wizard_station)
"fo" = (
@@ -2028,7 +2034,9 @@
/turf/simulated/floor/plasteel/dark,
/area/syndicate_mothership)
"hP" = (
/obj/machinery/computer/communications,
/obj/machinery/computer/communications{
dir = 8
},
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/administration)
"hQ" = (
@@ -3500,7 +3508,9 @@
/turf/simulated/floor/carpet,
/area/centcom/control)
"mP" = (
/obj/machinery/computer/pod/deathsquad,
/obj/machinery/computer/pod/deathsquad{
dir = 8
},
/turf/simulated/floor/plasteel/dark{
dir = 1;
icon_state = "vault"
@@ -3523,7 +3533,9 @@
/turf/simulated/floor/wood,
/area/centcom/control)
"mV" = (
/obj/machinery/computer/card/centcom,
/obj/machinery/computer/card/centcom{
dir = 8
},
/turf/simulated/floor/wood,
/area/centcom/control)
"mW" = (
@@ -3588,7 +3600,9 @@
/turf/simulated/floor/carpet,
/area/centcom/control)
"nh" = (
/obj/machinery/computer/communications,
/obj/machinery/computer/communications{
dir = 8
},
/turf/simulated/floor/carpet,
/area/centcom/control)
"nj" = (
@@ -4306,7 +4320,9 @@
d2 = 8;
icon_state = "0-8"
},
/obj/machinery/computer/mech_bay_power_console,
/obj/machinery/computer/mech_bay_power_console{
dir = 1
},
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/gamma/space)
"pu" = (
@@ -4417,12 +4433,12 @@
/turf/simulated/floor/carpet,
/area/centcom/specops)
"pP" = (
/obj/structure/table/wood,
/obj/machinery/computer/security/telescreen{
desc = "Used for watching the Special Ops.";
name = "Special Ops. Monitor";
network = list("ERT")
},
/obj/structure/table/wood,
/turf/simulated/floor/carpet,
/area/centcom/specops)
"pQ" = (
@@ -4430,10 +4446,12 @@
/turf/simulated/floor/plasteel/dark,
/area/centcom/specops)
"pR" = (
/obj/machinery/computer/station_alert,
/obj/structure/sign/poster/official/nanotrasen_logo{
pixel_x = 32
},
/obj/machinery/computer/station_alert{
dir = 8
},
/turf/simulated/floor/plasteel{
dir = 5;
icon_state = "darkyellow"
@@ -4940,7 +4958,9 @@
},
/area/centcom/evac)
"rz" = (
/obj/machinery/computer/communications,
/obj/machinery/computer/communications{
dir = 8
},
/turf/simulated/floor/plasteel/dark,
/area/centcom/specops)
"rA" = (
@@ -5185,10 +5205,12 @@
/turf/simulated/floor/wood,
/area/centcom/evac)
"sy" = (
/obj/machinery/computer/secure_data,
/obj/machinery/light/spot{
dir = 8
},
/obj/machinery/computer/secure_data{
dir = 4
},
/turf/simulated/floor/mineral/plastitanium/red,
/area/centcom/evac)
"sz" = (
@@ -6168,7 +6190,9 @@
/turf/simulated/floor/plasteel/grimy,
/area/centcom/specops)
"vf" = (
/obj/machinery/computer/secure_data,
/obj/machinery/computer/secure_data{
dir = 4
},
/turf/simulated/floor/plasteel,
/area/centcom/control)
"vg" = (
@@ -6185,7 +6209,9 @@
/turf/simulated/floor/plasteel,
/area/admin)
"vi" = (
/obj/machinery/computer/med_data,
/obj/machinery/computer/med_data{
dir = 8
},
/turf/simulated/floor/plasteel,
/area/centcom/control)
"vj" = (
@@ -6235,11 +6261,11 @@
/turf/simulated/floor/plasteel,
/area/admin)
"vp" = (
/obj/machinery/computer/mech_bay_power_console,
/obj/structure/cable{
d2 = 2;
icon_state = "0-2"
},
/obj/machinery/computer/mech_bay_power_console,
/turf/simulated/floor/plasteel,
/area/admin)
"vr" = (
@@ -6257,24 +6283,33 @@
/area/admin)
"vs" = (
/obj/machinery/computer/security{
dir = 1;
network = list("SS13","Telecomms","Research Outpost","Mining Outpost","ERT","CentComm","Thunderdome")
},
/turf/simulated/floor/plasteel,
/area/centcom/control)
"vt" = (
/obj/machinery/computer/station_alert,
/obj/machinery/computer/station_alert{
dir = 1
},
/turf/simulated/floor/plasteel,
/area/centcom/control)
"vu" = (
/obj/machinery/computer/communications,
/obj/machinery/computer/communications{
dir = 1
},
/turf/simulated/floor/plasteel,
/area/centcom/control)
"vv" = (
/obj/machinery/computer/robotics,
/obj/machinery/computer/robotics{
dir = 1
},
/turf/simulated/floor/plasteel,
/area/centcom/control)
"vw" = (
/obj/machinery/computer/crew,
/obj/machinery/computer/crew{
dir = 1
},
/turf/simulated/floor/plasteel,
/area/centcom/control)
"vx" = (
@@ -6852,8 +6887,10 @@
/turf/simulated/floor/carpet,
/area/admin)
"wS" = (
/obj/machinery/computer/secure_data,
/obj/item/paper/sop,
/obj/machinery/computer/secure_data{
dir = 8
},
/turf/simulated/floor/carpet,
/area/admin)
"wT" = (
@@ -6881,11 +6918,15 @@
/turf/simulated/floor/carpet,
/area/admin)
"wY" = (
/obj/machinery/computer/communications,
/obj/machinery/computer/communications{
dir = 4
},
/turf/simulated/floor/carpet,
/area/admin)
"wZ" = (
/obj/machinery/computer/med_data,
/obj/machinery/computer/med_data{
dir = 8
},
/turf/simulated/floor/carpet,
/area/admin)
"xa" = (
@@ -7363,7 +7404,9 @@
},
/area/trader_station/sol)
"yJ" = (
/obj/machinery/computer/communications,
/obj/machinery/computer/communications{
dir = 1
},
/turf/simulated/floor/plasteel,
/area/admin)
"yK" = (
@@ -7380,7 +7423,9 @@
},
/area/trader_station/sol)
"yP" = (
/obj/machinery/computer/atmos_alert,
/obj/machinery/computer/atmos_alert{
dir = 1
},
/turf/simulated/floor/plasteel,
/area/admin)
"yQ" = (
@@ -7390,12 +7435,15 @@
},
/area/centcom/evac)
"yR" = (
/obj/machinery/computer/message_monitor,
/obj/item/paper/monitorkey,
/obj/machinery/computer/message_monitor{
dir = 1
},
/turf/simulated/floor/plasteel,
/area/admin)
"yS" = (
/obj/machinery/computer/account_database{
dir = 1;
name = "Admin Accounts Database"
},
/turf/simulated/floor/plasteel,
@@ -7407,7 +7455,9 @@
/turf/simulated/floor/plating,
/area/centcom/evac)
"yU" = (
/obj/machinery/computer/secure_data,
/obj/machinery/computer/secure_data{
dir = 1
},
/turf/simulated/floor/plasteel,
/area/admin)
"yV" = (
@@ -7452,7 +7502,9 @@
/turf/simulated/floor/plasteel,
/area/admin)
"zc" = (
/obj/machinery/computer/arcade,
/obj/machinery/computer/arcade{
dir = 8
},
/turf/simulated/floor/mineral/titanium/blue,
/area/centcom/evac)
"zd" = (
@@ -7473,7 +7525,9 @@
/turf/simulated/floor/plasteel/dark,
/area/admin)
"zh" = (
/obj/machinery/computer/card,
/obj/machinery/computer/card{
dir = 4
},
/turf/simulated/floor/mineral/plastitanium/red,
/area/centcom/evac)
"zi" = (
@@ -7944,7 +7998,9 @@
/turf/simulated/floor/plasteel,
/area/admin)
"AS" = (
/obj/machinery/computer/teleporter,
/obj/machinery/computer/teleporter{
dir = 1
},
/turf/simulated/floor/plasteel,
/area/admin)
"AT" = (
@@ -8000,11 +8056,15 @@
/turf/simulated/floor/plasteel/dark,
/area/syndicate_mothership)
"Bb" = (
/obj/machinery/computer/crew,
/obj/machinery/computer/crew{
dir = 4
},
/turf/simulated/floor/mineral/titanium/blue,
/area/centcom/evac)
"Bc" = (
/obj/machinery/computer/communications,
/obj/machinery/computer/communications{
dir = 8
},
/turf/simulated/floor/mineral/titanium/blue,
/area/centcom/evac)
"Bd" = (
@@ -8040,6 +8100,7 @@
/area/centcom/evac)
"Bj" = (
/obj/machinery/computer{
dir = 1;
name = "EMERGENCY SHUTTLE COMPUTER"
},
/turf/simulated/floor/mineral/titanium/blue,
@@ -8100,7 +8161,9 @@
/turf/simulated/floor/plasteel/dark,
/area/syndicate_mothership)
"Bv" = (
/obj/machinery/computer/camera_advanced,
/obj/machinery/computer/camera_advanced{
dir = 8
},
/turf/simulated/floor/plasteel/dark,
/area/syndicate_mothership)
"Bw" = (
@@ -8117,6 +8180,7 @@
/area/syndicate_mothership)
"By" = (
/obj/machinery/computer/shuttle/syndicate{
dir = 1;
name = "Nuclear Operatives Shuttle Console"
},
/turf/simulated/floor/plasteel/dark,
@@ -9000,7 +9064,9 @@
/turf/simulated/floor/mineral/titanium/blue,
/area/shuttle/supply)
"FJ" = (
/obj/machinery/computer/secure_data,
/obj/machinery/computer/secure_data{
dir = 4
},
/turf/simulated/floor/plasteel{
dir = 10;
icon_state = "darkred"
@@ -9043,7 +9109,9 @@
},
/area/shuttle/escape)
"Ga" = (
/obj/machinery/computer/shuttle/ert,
/obj/machinery/computer/shuttle/ert{
dir = 1
},
/turf/simulated/floor/plasteel/dark,
/area/centcom/specops)
"Gd" = (
@@ -9119,7 +9187,9 @@
/turf/simulated/wall/mineral/plastitanium,
/area/shuttle/syndicate_sit)
"GD" = (
/obj/machinery/computer/camera_advanced/shuttle_docker/syndicate/sst,
/obj/machinery/computer/camera_advanced/shuttle_docker/syndicate/sst{
dir = 1
},
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/syndicate_elite)
"GJ" = (
@@ -9376,10 +9446,12 @@
/turf/simulated/floor/plating,
/area/tdome/tdome2)
"Il" = (
/obj/machinery/computer/communications,
/obj/item/radio/intercom/specops{
pixel_y = -28
},
/obj/machinery/computer/communications{
dir = 1
},
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/specops)
"Io" = (
@@ -9622,7 +9694,9 @@
/turf/simulated/floor/mineral/plastitanium/red/nitrogen,
/area/shuttle/vox)
"JH" = (
/obj/machinery/computer/camera_advanced/shuttle_docker/syndicate/sit,
/obj/machinery/computer/camera_advanced/shuttle_docker/syndicate/sit{
dir = 1
},
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/syndicate_sit)
"JM" = (
@@ -9636,6 +9710,7 @@
/area/shuttle/administration)
"JO" = (
/obj/machinery/computer/security{
dir = 1;
network = list("SS13","Research Outpost","Mining Outpost")
},
/turf/simulated/floor/mineral/plastitanium/red,
@@ -10628,7 +10703,9 @@
},
/area/shuttle/escape)
"PU" = (
/obj/machinery/computer/shuttle/sit,
/obj/machinery/computer/shuttle/sit{
dir = 1
},
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/syndicate_sit)
"PX" = (
@@ -11190,12 +11267,13 @@
},
/area/holodeck/source_thunderdomecourt)
"TA" = (
/obj/machinery/computer/security{
network = list("SS13","Research Outpost","Mining Outpost","Telecomms")
},
/obj/structure/sign/poster/official/nanotrasen_logo{
pixel_x = -32
},
/obj/machinery/computer/security{
dir = 4;
network = list("SS13","Research Outpost","Mining Outpost","Telecomms")
},
/turf/simulated/floor/plasteel{
dir = 9;
icon_state = "darkred"
@@ -11379,7 +11457,9 @@
},
/area/holodeck/source_knightarena)
"UJ" = (
/obj/machinery/computer/shuttle/sst,
/obj/machinery/computer/shuttle/sst{
dir = 1
},
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/syndicate_elite)
"UK" = (
@@ -11495,6 +11575,7 @@
/area/shuttle/escape)
"Vr" = (
/obj/machinery/computer/camera_advanced/shuttle_docker/admin{
dir = 8;
name = "NTV Argos shuttle navigation computer"
},
/turf/simulated/floor/mineral/plastitanium/red,
@@ -11568,7 +11649,9 @@
},
/area/shuttle/escape)
"VS" = (
/obj/machinery/computer/atmos_alert,
/obj/machinery/computer/atmos_alert{
dir = 8
},
/turf/simulated/floor/plasteel{
dir = 6;
icon_state = "darkyellow"
@@ -11600,6 +11683,7 @@
/area/shuttle/vox)
"Wc" = (
/obj/machinery/computer/shuttle/admin{
dir = 8;
name = "NTV Argos shuttle console"
},
/turf/simulated/floor/mineral/plastitanium/red,
@@ -12043,7 +12127,6 @@
},
/area/shuttle/escape)
"YV" = (
/obj/machinery/computer/shuttle/trade/sol,
/obj/machinery/door_control{
id = "soltrader_south";
name = "Trade Deposits Door";
@@ -12057,6 +12140,9 @@
pixel_x = 24;
pixel_y = -8
},
/obj/machinery/computer/shuttle/trade/sol{
dir = 8
},
/turf/simulated/floor/mineral/titanium/blue,
/area/shuttle/trade/sol)
"YW" = (
+8
View File
@@ -115,3 +115,11 @@
#define _2FA_ENABLED_IP "ENABLED_IP"
/// Client will be prompted for 2FA always
#define _2FA_ENABLED_ALWAYS "ENABLED_ALWAYS"
#define MAX_SAVE_SLOTS 30 // Save slots for regular players
#define MAX_SAVE_SLOTS_MEMBER 30 // Save slots for BYOND members
#define TAB_CHAR 0
#define TAB_GAME 1
#define TAB_GEAR 2
+3
View File
@@ -51,3 +51,6 @@
return A.plane - B.plane
else
return A.layer - B.layer
/proc/cmp_login_processor_priority(datum/client_login_processor/A, datum/client_login_processor/B)
return A.priority - B.priority
+7
View File
@@ -128,6 +128,13 @@
continue
GLOB.world_topic_handlers[wth.topic_key] = topic_handler_type
// Setup client login processors.
for(var/processor_type in subtypesof(/datum/client_login_processor))
var/datum/client_login_processor/CLP = new processor_type
GLOB.client_login_processors += CLP
// Sort them by priority, lowest first
sortTim(GLOB.client_login_processors, /proc/cmp_login_processor_priority)
/* // Uncomment to debug chemical reaction list.
/client/verb/debug_chemical_list()
+1 -1
View File
@@ -1554,7 +1554,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
if(orbiting == A) //make sure we haven't started orbiting something else.
orbiting = null
SpinAnimation(0, 0)
SpinAnimation(0, 0, parallel = FALSE)
transform = cached_transform
+2
View File
@@ -56,3 +56,5 @@ GLOBAL_LIST_INIT(cooking_ingredients, list(RECIPE_MICROWAVE = list(), RECIPE_OVE
GLOBAL_LIST_INIT(cooking_reagents, list(RECIPE_MICROWAVE = list(), RECIPE_OVEN = list(), RECIPE_GRILL = list(), RECIPE_CANDY = list()))
#define EGG_LAYING_MESSAGES list("lays an egg.", "squats down and croons.", "begins making a huge racket.", "begins clucking raucously.")
GLOBAL_LIST_EMPTY(client_login_processors)
@@ -10,6 +10,8 @@
var/medal_hub_password = null
/// Do we want the server to kill on reboot instead of keeping the same DD session
var/shutdown_on_reboot = FALSE
/// Is this server a production server (Has higher security and requires 2FA)
var/is_production = FALSE
/// If above is true, you can run a shell command
var/shutdown_shell_command = null
/// 2FA backend server host
@@ -20,6 +22,7 @@
/datum/configuration_section/system_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_BOOL(shutdown_on_reboot, data["shutdown_on_reboot"])
CONFIG_LOAD_BOOL(is_production, data["is_production"])
CONFIG_LOAD_STR(topic_key, data["communications_password"])
CONFIG_LOAD_STR(medal_hub_address, data["medal_hub_address"])
+2 -3
View File
@@ -54,9 +54,8 @@ SUBSYSTEM_DEF(changelog)
/datum/controller/subsystem/changelog/proc/UpdatePlayerChangelogDate(client/C)
if(!ss_ready)
return // Only return here, we dont have to worry about a queue list because this will be called from ShowChangelog()
// Technically this is only for the date but we can also do the UI button at the same time
var/datum/preferences/P = GLOB.preferences_datums[C.ckey]
if(P.toggles & PREFTOGGLE_UI_DARKMODE)
if(C.prefs.toggles & PREFTOGGLE_UI_DARKMODE)
winset(C, "rpane.changelog", "background-color=#40628a;font-color=#ffffff;font-style=none")
else
winset(C, "rpane.changelog", "background-color=none;font-style=none")
+12 -12
View File
@@ -65,7 +65,7 @@ SUBSYSTEM_DEF(jobs)
return type_occupations[jobtype]
/datum/controller/subsystem/jobs/proc/GetPlayerAltTitle(mob/new_player/player, rank)
return player.client.prefs.GetPlayerAltTitle(GetJob(rank))
return player.client.prefs.active_character.GetPlayerAltTitle(GetJob(rank))
/datum/controller/subsystem/jobs/proc/AssignRole(mob/new_player/player, rank, latejoin = 0)
Debug("Running AR, Player: [player], Rank: [rank], LJ: [latejoin]")
@@ -121,7 +121,7 @@ SUBSYSTEM_DEF(jobs)
Debug("Running FOC, Job: [job], Level: [level], Flag: [flag]")
var/list/candidates = list()
for(var/mob/new_player/player in unassigned)
Debug(" - Player: [player] Banned: [jobban_isbanned(player, job.title)] Old Enough: [!job.player_old_enough(player.client)] AvInPlaytime: [job.available_in_playtime(player.client)] Flag && Be Special: [flag] && [player.client.prefs.be_special] Job Department: [player.client.prefs.GetJobDepartment(job, level)] Job Flag: [job.flag] Job Department Flag = [job.department_flag]")
Debug(" - Player: [player] Banned: [jobban_isbanned(player, job.title)] Old Enough: [!job.player_old_enough(player.client)] AvInPlaytime: [job.available_in_playtime(player.client)] Flag && Be Special: [flag] && [player.client.prefs.be_special] Job Department: [player.client.prefs.active_character.GetJobDepartment(job, level)] Job Flag: [job.flag] Job Department Flag = [job.department_flag]")
if(jobban_isbanned(player, job.title))
Debug("FOC isbanned failed, Player: [player]")
continue
@@ -140,7 +140,7 @@ SUBSYSTEM_DEF(jobs)
if(player.mind && (job.title in player.mind.restricted_roles))
Debug("FOC incompatbile with antagonist role, Player: [player]")
continue
if(player.client.prefs.GetJobDepartment(job, level) & job.flag)
if(player.client.prefs.active_character.GetJobDepartment(job, level) & job.flag)
Debug("FOC pass, Player: [player], Level:[level]")
candidates += player
return candidates
@@ -367,12 +367,12 @@ SUBSYSTEM_DEF(jobs)
continue
// If the player wants that job on this level, then try give it to him.
if(player.client.prefs.GetJobDepartment(job, level) & job.flag)
if(player.client.prefs.active_character.GetJobDepartment(job, level) & job.flag)
// If the job isn't filled
if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)
Debug("DO pass, Player: [player], Level:[level], Job:[job.title]")
Debug(" - Job Flag: [job.flag] Job Department: [player.client.prefs.GetJobDepartment(job, level)] Job Current Pos: [job.current_positions] Job Spawn Positions = [job.spawn_positions]")
Debug(" - Job Flag: [job.flag] Job Department: [player.client.prefs.active_character.GetJobDepartment(job, level)] Job Current Pos: [job.current_positions] Job Spawn Positions = [job.spawn_positions]")
AssignRole(player, job.title)
unassigned -= player
break
@@ -380,7 +380,7 @@ SUBSYSTEM_DEF(jobs)
// Hand out random jobs to the people who didn't get any in the last check
// Also makes sure that they got their preference correct
for(var/mob/new_player/player in unassigned)
if(player.client.prefs.alternate_option == GET_RANDOM_JOB)
if(player.client.prefs.active_character.alternate_option == GET_RANDOM_JOB)
GiveRandomJob(player)
Debug("DO, Standard Check end")
@@ -390,7 +390,7 @@ SUBSYSTEM_DEF(jobs)
// Antags, who have to get in, come first
for(var/mob/new_player/player in unassigned)
if(player.mind.special_role)
if(player.client.prefs.alternate_option != BE_ASSISTANT)
if(player.client.prefs.active_character.alternate_option != BE_ASSISTANT)
GiveRandomJob(player)
if(player in unassigned)
AssignRole(player, "Civilian")
@@ -399,10 +399,10 @@ SUBSYSTEM_DEF(jobs)
// Then we assign what we can to everyone else.
for(var/mob/new_player/player in unassigned)
if(player.client.prefs.alternate_option == BE_ASSISTANT)
if(player.client.prefs.active_character.alternate_option == BE_ASSISTANT)
Debug("AC2 Assistant located, Player: [player]")
AssignRole(player, "Civilian")
else if(player.client.prefs.alternate_option == RETURN_TO_LOBBY)
else if(player.client.prefs.active_character.alternate_option == RETURN_TO_LOBBY)
player.ready = 0
unassigned -= player
@@ -564,11 +564,11 @@ SUBSYSTEM_DEF(jobs)
if(job.barred_by_disability(player.client))
disabled++
continue
if(player.client.prefs.GetJobDepartment(job, 1) & job.flag)
if(player.client.prefs.active_character.GetJobDepartment(job, 1) & job.flag)
high++
else if(player.client.prefs.GetJobDepartment(job, 2) & job.flag)
else if(player.client.prefs.active_character.GetJobDepartment(job, 2) & job.flag)
medium++
else if(player.client.prefs.GetJobDepartment(job, 3) & job.flag)
else if(player.client.prefs.active_character.GetJobDepartment(job, 3) & job.flag)
low++
else never++ //not selected
+1 -1
View File
@@ -209,7 +209,7 @@ SUBSYSTEM_DEF(vote)
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, code_invoked = FALSE)
if(!mode)
if(started_time != null && !check_rights(R_ADMIN))
if(usr && started_time != null && !check_rights(R_ADMIN)) // Allow the game to call votes whenever. But check other callers
var/next_allowed_time = (started_time + GLOB.configuration.vote.vote_delay)
if(next_allowed_time > world.time)
return 0
+5
View File
@@ -55,6 +55,10 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
var/list/announce_beacons = list() // Particular beacons that we'll notify the relevant department when we reach
var/special = FALSE //Event/Station Goals/Admin enabled packs
var/special_enabled = FALSE
/// The number of times one can order a cargo crate, before it becomes restricted. -1 for infinite
var/order_limit = -1
/// Number of times a crate has been ordered in a shift
var/times_ordered = 0
/// List of names for being done in TGUI
var/list/ui_manifest = list()
@@ -200,6 +204,7 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
containertype = /obj/structure/closet/crate
containername = "crate"
hidden = 1
order_limit = 5
//////////////////////////////////////////////////////////////////////////////
//////////////////////////// Security ////////////////////////////////////////
+3 -5
View File
@@ -90,13 +90,11 @@
/atom/movable/proc/stop_pulling()
if(pulling)
pulling.pulledby = null
var/mob/living/ex_pulled = pulling
pulling.pulledby = null
pulling = null
pulledby = null
if(isliving(ex_pulled))
var/mob/living/L = ex_pulled
L.update_canmove()// mob gets up if it was lyng down in a chokehold
if(!QDELETED(ex_pulled) && istype(ex_pulled))
ex_pulled.update_canmove()// mob gets up if it was lyng down in a chokehold
/atom/movable/proc/check_pulling()
if(pulling)
@@ -23,7 +23,7 @@
secondary_enemies = CEILING((secondary_enemies_scaling * num_players()), 1)
for(var/mob/new_player/player in GLOB.player_list)
if((player.mind in possible_changelings) && (player.client.prefs.species in secondary_protected_species))
if((player.mind in possible_changelings) && (player.client.prefs.active_character.species in secondary_protected_species))
possible_changelings -= player.mind
if(possible_changelings.len > 0)
+1 -1
View File
@@ -251,7 +251,7 @@
// Get a list of all the people who want to be the antagonist for this round, except those with incompatible species
for(var/mob/new_player/player in players)
if(!player.client.skip_antag)
if((role in player.client.prefs.be_special) && !(player.client.prefs.species in protected_species))
if((role in player.client.prefs.be_special) && !(player.client.prefs.active_character.species in protected_species))
player_draft_log += "[player.key] had [roletext] enabled, so we are drafting them."
candidates += player.mind
players -= player
+1 -1
View File
@@ -110,7 +110,7 @@
/datum/intercept_text/proc/get_suspect()
var/list/dudes = list()
for(var/mob/living/carbon/human/man in GLOB.player_list)
if(man.client && man.client.prefs.nanotrasen_relation == "Opposed")
if(man.client && man.client.prefs.active_character.nanotrasen_relation == "Opposed")
//don't include suspects who can't possibly be the antag based on their job (no suspecting the captain of being a damned dirty tator)
if(man.mind && man.mind.assigned_role)
if((man.mind.assigned_role in SSticker.mode.protected_jobs) || (man.mind.assigned_role in SSticker.mode.restricted_jobs))
-8
View File
@@ -279,14 +279,6 @@
synd_mob.equip_to_slot_or_del(U, slot_in_backpack)
if(synd_mob.dna.species)
/*
Incase anyone ever gets the burning desire to have nukeops with randomized apperances. -- Dave
synd_mob.gender = pick(MALE, FEMALE) // Randomized appearances for the nukeops.
var/datum/preferences/pref = new()
A.randomize_appearance_for(synd_mob)
*/
var/race = synd_mob.dna.species.name
switch(race)
+1 -1
View File
@@ -507,7 +507,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
if(SSticker.current_state == GAME_STATE_SETTING_UP)
for(var/mob/new_player/P in GLOB.player_list)
if(P.client && P.ready && P.mind != owner)
if(P.client.prefs && (P.client.prefs.species == "Machine")) // Special check for species that can't be absorbed. No better solution.
if(P.client.prefs && (P.client.prefs.active_character.species == "Machine")) // Special check for species that can't be absorbed. No better solution.
continue
n_p++
else if(SSticker.current_state == GAME_STATE_PLAYING)
+1 -1
View File
@@ -24,7 +24,7 @@
secondary_enemies = CEILING((secondary_enemies_scaling * num_players()), 1)
for(var/mob/new_player/player in GLOB.player_list)
if((player.mind in possible_vampires) && (player.client.prefs.species in secondary_protected_species))
if((player.mind in possible_vampires) && (player.client.prefs.active_character.species in secondary_protected_species))
possible_vampires -= player.mind
if(possible_vampires.len > 0)
+2 -2
View File
@@ -347,9 +347,9 @@ GLOBAL_LIST_EMPTY(multiverse)
/obj/item/multisword/proc/spawn_copy(client/C, turf/T, mob/user)
var/mob/living/carbon/human/M = new/mob/living/carbon/human(T)
if(duplicate_self)
user.client.prefs.copy_to(M)
user.client.prefs.active_character.copy_to(M)
else
C.prefs.copy_to(M)
C.prefs.active_character.copy_to(M)
M.key = C.key
M.mind.name = user.real_name
to_chat(M, "<B>You are an alternate version of [user.real_name] from another universe! Help [user.p_them()] accomplish [user.p_their()] goals at all costs.</B>")
+1 -1
View File
@@ -147,7 +147,7 @@
if(!G || !G.key)
return // Let's not steal someone's soul here
var/mob/living/carbon/human/new_character = new(pick(GLOB.latejoin))
G.client.prefs.copy_to(new_character)
G.client.prefs.active_character.copy_to(new_character)
new_character.key = G.key
return new_character
+4 -4
View File
@@ -123,7 +123,7 @@
var/list/prohibited_disabilities = list(DISABILITY_FLAG_BLIND, DISABILITY_FLAG_DEAF, DISABILITY_FLAG_MUTE, DISABILITY_FLAG_DIZZY)
for(var/i = 1, i < prohibited_disabilities.len, i++)
var/this_disability = prohibited_disabilities[i]
if(C.prefs.disabilities & this_disability)
if(C.prefs.active_character.disabilities & this_disability)
return 1
return 0
@@ -173,8 +173,8 @@
if(box && H.dna.species.speciesbox)
box = H.dna.species.speciesbox
if(allow_loadout && H.client && (H.client.prefs.loadout_gear && H.client.prefs.loadout_gear.len))
for(var/gear in H.client.prefs.loadout_gear)
if(allow_loadout && H.client && length(H.client.prefs.active_character.loadout_gear))
for(var/gear in H.client.prefs.active_character.loadout_gear)
var/datum/gear/G = GLOB.gear_datums[gear]
if(G)
var/permitted = FALSE
@@ -212,7 +212,7 @@
if(gear_leftovers.len)
for(var/datum/gear/G in gear_leftovers)
var/atom/placed_in = H.equip_or_collect(G.spawn_item(null, H.client.prefs.loadout_gear[G.display_name]))
var/atom/placed_in = H.equip_or_collect(G.spawn_item(null, H.client.prefs.active_character.loadout_gear[G.display_name]))
if(istype(placed_in))
if(isturf(placed_in))
to_chat(H, "<span class='notice'>Placing [G.display_name] on [placed_in]!</span>")
+3 -5
View File
@@ -25,11 +25,9 @@
/obj/machinery/computer/operating/New()
..()
for(dir in list(NORTH,EAST,SOUTH,WEST))
table = locate(/obj/machinery/optable, get_step(src, dir))
if(table)
table.computer = src
break
table = locate(/obj/machinery/optable, orange(1, src))
if(table)
table.computer = src
/obj/machinery/computer/operating/Destroy()
if(table)
+13 -1
View File
@@ -462,6 +462,17 @@
drop_computer_parts()
return ..() // will qdel the frame
/obj/structure/computerframe/AltClick(mob/user)
if(user.incapacitated())
to_chat(user, "<span class='warning'>You can't do that right now!</span>")
return
if(!Adjacent(user))
return
if(anchored)
to_chat(user, "<span class='warning'>The frame is anchored to the floor!</span>")
return
setDir(turn(dir, 90))
/obj/structure/computerframe/obj_break(damage_flag)
deconstruct()
@@ -544,7 +555,8 @@
if(STATE_GLASS)
to_chat(user, "<span class='notice'>You connect the monitor.</span>")
I.play_tool_sound(src)
var/B = new circuit.build_path(loc)
var/obj/machinery/computer/B = new circuit.build_path(loc)
B.setDir(dir)
if(istype(circuit, /obj/item/circuitboard/supplycomp))
var/obj/machinery/computer/supplycomp/SC = B
var/obj/item/circuitboard/supplycomp/C = circuit
-5
View File
@@ -284,11 +284,6 @@
/obj/machinery/suit_storage_unit/Destroy()
SStgui.close_uis(wires)
QDEL_NULL(suit)
QDEL_NULL(helmet)
QDEL_NULL(mask)
QDEL_NULL(boots)
QDEL_NULL(storage)
QDEL_NULL(wires)
return ..()
+1 -4
View File
@@ -41,10 +41,7 @@
/obj/machinery/computer/teleporter/proc/link_power_station()
if(power_station)
return
for(dir in list(NORTH,EAST,SOUTH,WEST))
power_station = locate(/obj/machinery/teleport/station, get_step(src, dir))
if(power_station)
break
power_station = locate(/obj/machinery/teleport/station, orange(1, src))
return power_station
/obj/machinery/computer/teleporter/attackby(obj/item/I, mob/living/user, params)
@@ -84,7 +84,7 @@ GLOBAL_LIST_EMPTY(splatter_cache)
/obj/effect/decal/cleanable/blood/drip
name = "drips of blood"
desc = "It's red."
desc = "A drop of blood in an ocean of mess."
gender = PLURAL
icon = 'icons/effects/drip.dmi'
icon_state = "1"
@@ -204,6 +204,11 @@
duration = 8
randomdir = FALSE
/obj/effect/temp_visual/mummy_animation
icon = 'icons/mob/mob.dmi'
icon_state = "mummy_revive"
duration = 20
/obj/effect/temp_visual/heal //color is white by default, set to whatever is needed
name = "healing glow"
icon_state = "heal"
+1 -1
View File
@@ -118,7 +118,7 @@
else if(issilicon(M))
add_attack_logs(user, M, "Flashed with [src]")
if(M.flash_eyes(affect_silicon = 1))
M.Weaken(rand(5,10))
M.Weaken(rand(4, 6))
user.visible_message("<span class='disarm'>[user] overloads [M]'s sensors with [src]!</span>", "<span class='danger'>You overload [M]'s sensors with [src]!</span>")
return 1
user.visible_message("<span class='disarm'>[user] fails to blind [M] with [src]!</span>", "<span class='warning'>You fail to blind [M] with [src]!</span>")
@@ -31,6 +31,115 @@
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
add_attack_logs(user, M, "Stunned with [src] ([uppertext(user.a_intent)])")
#define CYBORG_HUGS 0
#define CYBORG_HUG 1
#define CYBORG_SHOCK 2
#define CYBORG_CRUSH 3
/obj/item/borg/cyborghug
name = "hugging module"
icon_state = "hugmodule"
desc = "For when a someone really needs a hug."
var/mode = CYBORG_HUGS //0 = Hugs 1 = "Hug" 2 = Shock 3 = CRUSH
var/ccooldown = 0
var/scooldown = 0
var/shockallowed = FALSE //Can it be a stunarm when emagged. Only PK borgs get this by default.
/obj/item/borg/cyborghug/attack_self(mob/living/user)
if(isrobot(user))
var/mob/living/silicon/robot/P = user
if(P.emagged && shockallowed)
if(mode < CYBORG_CRUSH)
mode++
else
mode = CYBORG_HUGS
else if(mode < CYBORG_HUG)
mode++
else
mode = CYBORG_HUGS
switch(mode)
if(CYBORG_HUGS)
to_chat(user, "Power reset. Hugs!")
if(CYBORG_HUG)
to_chat(user, "Power increased!")
if(CYBORG_SHOCK)
to_chat(user, "BZZT. Electrifying arms...")
if(CYBORG_CRUSH)
to_chat(user, "ERROR: ARM ACTUATORS OVERLOADED.")
/obj/item/borg/cyborghug/attack(mob/living/M, mob/living/silicon/robot/user, params)
if(M == user)
return
switch(mode)
if(CYBORG_HUGS)
if(M.health >= 0)
if(isanimal(M))
var/list/modifiers = params2list(params)
M.attack_hand(user, modifiers) //This enables borgs to get the floating heart icon and mob emote from simple_animal's that have petbonus == true.
return
if(user.zone_selected == BODY_ZONE_HEAD)
user.visible_message("<span class='notice'>[user] playfully boops [M] on the head!</span>", "<span class='notice'>You playfully boop [M] on the head!</span>")
user.do_attack_animation(M, ATTACK_EFFECT_BOOP)
playsound(loc, 'sound/weapons/tap.ogg', 50, TRUE, -1)
else if(ishuman(M))
if(user.lying)
user.visible_message("<span class='notice'>[user] shakes [M] trying to get [M.p_them()] up!</span>", "<span class='notice'>You shake [M] trying to get [M.p_them()] up!</span>")
else
user.visible_message("<span class='notice'>[user] hugs [M] to make [M.p_them()] feel better!</span>", "<span class='notice'>You hug [M] to make [M.p_them()] feel better!</span>")
if(M.resting)
M.resting = FALSE
M.update_canmove()
else
user.visible_message("<span class='notice'>[user] pets [M]!</span>", "<span class='notice'>You pet [M]!</span>")
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
if(CYBORG_HUG)
if(M.health >= 0)
if(ishuman(M))
if(M.lying)
user.visible_message("<span class='notice'>[user] shakes [M] trying to get [M.p_them()] up!</span>", "<span class='notice'>You shake [M] trying to get [M.p_them()] up!</span>")
else if(user.zone_selected == BODY_ZONE_HEAD)
user.visible_message("<span class='warning'>[user] bops [M] on the head!</span>", "<span class='warning'>You bop [M] on the head!</span>")
user.do_attack_animation(M, ATTACK_EFFECT_PUNCH)
else
user.visible_message("<span class='warning'>[user] hugs [M] in a firm bear-hug! [M] looks uncomfortable...</span>", "<span class='warning'>You hug [M] firmly to make [M.p_them()] feel better! [M] looks uncomfortable...</span>")
if(M.resting)
M.resting = FALSE
M.update_canmove()
else
user.visible_message("<span class='warning'>[user] bops [M] on the head!</span>", "<span class='warning'>You bop [M] on the head!</span>")
playsound(loc, 'sound/weapons/tap.ogg', 50, TRUE, -1)
if(CYBORG_SHOCK)
if(scooldown < world.time)
if(M.health >= 0)
if(ishuman(M))
M.electrocute_act(5, "[user]", flags = SHOCK_NOGLOVES)
user.visible_message("<span class='userdanger'>[user] electrocutes [M] with [user.p_their()] touch!</span>", "<span class='danger'>You electrocute [M] with your touch!</span>")
else
if(!isrobot(M))
M.adjustFireLoss(10)
user.visible_message("<span class='userdanger'>[user] shocks [M]!</span>", "<span class='danger'>You shock [M]!</span>")
else
user.visible_message("<span class='userdanger'>[user] shocks [M]. It does not seem to have an effect</span>", "<span class='danger'>You shock [M] to no effect.</span>")
playsound(loc, 'sound/effects/sparks2.ogg', 50, TRUE, -1)
user.cell.use(500)
scooldown = world.time + 20
if(CYBORG_CRUSH)
if(ccooldown < world.time)
if(M.health >= 0)
if(ishuman(M))
user.visible_message("<span class='userdanger'>[user] crushes [M] in [user.p_their()] grip!</span>", "<span class='danger'>You crush [M] in your grip!</span>")
else
user.visible_message("<span class='userdanger'>[user] crushes [M]!</span>", "<span class='danger'>You crush [M]!</span>")
playsound(loc, 'sound/weapons/smash.ogg', 50, TRUE, -1)
M.adjustBruteLoss(15)
user.cell.use(300)
ccooldown = world.time + 10
#undef CYBORG_HUGS
#undef CYBORG_HUG
#undef CYBORG_SHOCK
#undef CYBORG_CRUSH
/obj/item/borg/overdrive
name = "Overdrive"
icon = 'icons/obj/decals.dmi'
@@ -120,6 +120,7 @@
desc = "Some sterile gauze to wrap around bloody stumps."
icon_state = "gauze"
origin_tech = "biotech=2"
max_amount = 12
heal_brute = 10
stop_bleeding = 1800
@@ -169,6 +170,7 @@
singular_name = "advanced trauma kit"
desc = "An advanced trauma kit for severe injuries."
icon_state = "traumakit"
max_amount = 6
heal_brute = 25
stop_bleeding = 0
@@ -233,6 +235,7 @@
icon = 'icons/obj/hydroponics/harvest.dmi'
icon_state = "tea_aspera_leaves"
color = "#378C61"
max_amount = 6
stop_bleeding = 0
heal_brute = 12
drop_sound = 'sound/misc/moist_impact.ogg'
+18 -11
View File
@@ -3,6 +3,7 @@
#define CUT_TIME 100
#define CLIMB_TIME 150
#define FULL_CUT_TIME 300
#define NO_HOLE 0 //section is intact
#define MEDIUM_HOLE 1 //medium hole in the section - can climb through
@@ -88,29 +89,34 @@
. = TRUE
if(shock(user, 100))
return
if(!cuttable)
to_chat(user, "<span class='warning'>This section of the fence can't be cut!</span>")
return
if(invulnerable)
to_chat(user, "<span class='warning'>This fence is too strong to cut through!</span>")
return
if(!cuttable)
user.visible_message("<span class='warning'>[user] starts dismantling [src] with [W].</span>",\
"<span class='warning'>You start dismantling [src] with [W].</span>")
if(W.use_tool(src, user, FULL_CUT_TIME, volume = W.tool_volume))
user.visible_message("<span class='notice'>[user] completely dismantles [src].</span>",\
"<span class='info'>You completely dismantle [src].</span>")
qdel(src)
return
var/current_stage = hole_size
user.visible_message("<span class='danger'>\The [user] starts cutting through \the [src] with \the [W].</span>",\
"<span class='danger'>You start cutting through \the [src] with \the [W].</span>")
user.visible_message("<span class='warning'>[user] starts cutting through [src] with [W].</span>",\
"<span class='warning'>You start cutting through [src] with [W].</span>")
if(W.use_tool(src, user, CUT_TIME * W.toolspeed, volume = W.tool_volume))
if(current_stage == hole_size)
switch(hole_size)
if(NO_HOLE)
visible_message("<span class='notice'>\The [user] cuts into \the [src] some more.</span>")
to_chat(user, "<span class='info'>You could probably fit yourself through that hole now. Although climbing through would be much faster if you made it even bigger.</span>")
user.visible_message("<span class='notice'>[user] cuts into [src] some more.</span>",\
"<span class='info'>You could probably fit yourself through that hole now. Although climbing through would be much faster if you made it even bigger.</span>")
hole_size = MEDIUM_HOLE
if(MEDIUM_HOLE)
visible_message("<span class='notice'>\The [user] completely cuts through \the [src].</span>")
to_chat(user, "<span class='info'>The hole in \the [src] is now big enough to walk through.</span>")
user.visible_message("<span class='notice'>[user] completely cuts through [src].</span>",\
"<span class='info'>The hole in [src] is now big enough to walk through.</span>")
hole_size = LARGE_HOLE
if(LARGE_HOLE)
visible_message("<span class='notice'>\The [user] completely dismantles \the [src].</span>")
to_chat(user, "<span class='info'>You completely take apart \the [src].</span>")
user.visible_message("<span class='notice'>[user] completely dismantles [src].</span>",\
"<span class='info'>You completely take apart [src].</span>")
qdel(src)
return
update_cut_status()
@@ -208,6 +214,7 @@
#undef CUT_TIME
#undef CLIMB_TIME
#undef FULL_CUT_TIME
#undef NO_HOLE
#undef MEDIUM_HOLE
+29 -4
View File
@@ -9,6 +9,7 @@
climbable = TRUE
layer = ABOVE_MOB_LAYER
var/currently_climbed = FALSE
var/mover_dir = null
/obj/structure/railing/corner //aesthetic corner sharp edges hurt oof ouch
icon_state = "railing_corner"
@@ -73,7 +74,11 @@
return TRUE
if(mover.throwing)
return TRUE
if(get_dir(loc, target) != dir)
mover_dir = get_dir(loc, target)
//Due to how the other check is done, it would always return density for ordinal directions no matter what
if(ordinal_direction_check())
return FALSE
if(mover_dir != dir)
return density
return FALSE
@@ -92,10 +97,30 @@
return TRUE
if(currently_climbed)
return TRUE
if(get_dir(O.loc, target) == dir)
mover_dir = get_dir(O.loc, target)
if(mover_dir == dir)
return FALSE
if(ordinal_direction_check())
return FALSE
return TRUE
// Checks if the direction the mob is trying to move towards would be blocked by a corner railing
/obj/structure/railing/proc/ordinal_direction_check()
switch(dir)
if(5)
if(mover_dir == 1 || mover_dir == 4)
return TRUE
if(6)
if(mover_dir == 2 || mover_dir == 4)
return TRUE
if(9)
if(mover_dir == 1 || mover_dir == 8)
return TRUE
if(10)
if(mover_dir == 2 || mover_dir == 8)
return TRUE
return FALSE
/obj/structure/railing/do_climb(mob/living/user)
var/initial_mob_loc = get_turf(user)
. = ..()
@@ -112,7 +137,7 @@
to_chat(user, "<span class='warning'>[src] cannot be rotated while it is fastened to the floor!</span>")
return FALSE
var/target_dir = turn(dir, -90)
var/target_dir = turn(dir, -45)
if(!valid_window_location(loc, target_dir)) //Expanded to include rails, as well!
to_chat(user, "<span class='warning'>[src] cannot be rotated in that direction!</span>")
@@ -133,4 +158,4 @@
if(!Adjacent(user))
return
if(can_be_rotated(user))
setDir(turn(dir, 90))
setDir(turn(dir, 45))
+12
View File
@@ -0,0 +1,12 @@
/client/verb/webmap()
set name = "webmap"
set hidden = TRUE
if(!SSmapping.map_datum.webmap_url)
to_chat(usr, "<span class='warning'>The current map has no defined webmap. Please file an issue report.</span>")
return
if(alert(usr, "Do you want to open this map's Webmap in your browser?", "Webmap", "Yes", "No") != "Yes")
return
usr << link(SSmapping.map_datum.webmap_url)
+1 -1
View File
@@ -130,7 +130,7 @@ GLOBAL_VAR_INIT(nologevent, 0)
if(GLOB.configuration.url.forum_playerinfo_url)
body += "<A href='?_src_=holder;webtools=[M.ckey]'>WebInfo</A> | "
if(M.client)
if(check_watchlist(M.client.ckey))
if(M.client.watchlisted)
body += "<A href='?_src_=holder;watchremove=[M.ckey]'>Remove from Watchlist</A> | "
body += "<A href='?_src_=holder;watchedit=[M.ckey]'>Edit Watchlist Reason</A> "
else
+9 -1
View File
@@ -667,6 +667,10 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
to_chat(src, "Error while re-adminning, admin rank ([rank]) does not exist.")
return
// Do a little check here
if(GLOB.configuration.system.is_production && (GLOB.admin_ranks[rank] & R_ADMIN) && prefs._2fa_status == _2FA_DISABLED) // If they are an admin and their 2FA is disabled
to_chat(src,"<span class='boldannounce'><big>You do not have 2FA enabled. Admin verbs will be unavailable until you have enabled 2FA.</big></span>") // Very fucking obvious
return
D = new(rank, GLOB.admin_ranks[rank], ckey)
else
if(!SSdbcore.IsConnected())
@@ -697,8 +701,12 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
if(istext(flags))
flags = text2num(flags)
// Do a little check here
if(GLOB.configuration.system.is_production && (flags & R_ADMIN) && prefs._2fa_status == _2FA_DISABLED) // If they are an admin and their 2FA is disabled
to_chat(src,"<span class='boldannounce'><big>You do not have 2FA enabled. Admin verbs will be unavailable until you have enabled 2FA.</big></span>") // Very fucking obvious
qdel(admin_read)
return
D = new(admin_rank, flags, ckey)
qdel(admin_read)
var/client/C = GLOB.directory[ckey]
+18
View File
@@ -95,6 +95,24 @@ you will have to do something like if(client.holder.rights & R_ADMIN) yourself.
to_chat(user, "<font color='red'>Error: You are not an admin.</font>")
return 0
// Basically the above proc but checks at a /client level
/proc/check_rights_client(rights_required, show_msg = TRUE, client/C)
if(C)
if(rights_required)
if(C.holder)
if(rights_required & C.holder.rights)
return TRUE
else
if(show_msg)
to_chat(C, "<font color='red'>Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].</font>")
else
if(C.holder)
return TRUE
else
if(show_msg)
to_chat(C, "<font color='red'>Error: You are not an admin.</font>")
return FALSE
//probably a bit iffy - will hopefully figure out a better solution
/proc/check_if_greater_rights_than(client/other)
if(usr && usr.client)
+6 -6
View File
@@ -1361,7 +1361,7 @@
to_chat(usr, "<span class='warning'>[M] doesn't seem to have an active client.</span>")
return
if(M.flavor_text == "" && M.client.prefs.flavor_text == "")
if(M.flavor_text == "" && M.client.prefs.active_character.flavor_text == "")
to_chat(usr, "<span class='warning'>[M] has no flavor text set.</span>")
return
@@ -1375,8 +1375,8 @@
M.flavor_text = ""
// Clear and save the DB character's flavor text
M.client.prefs.flavor_text = ""
M.client.prefs.save_character(M.client)
M.client.prefs.active_character.flavor_text = ""
M.client.prefs.active_character.save(M.client)
else if(href_list["userandomname"])
if(!check_rights(R_ADMIN))
@@ -1399,12 +1399,12 @@
message_admins("[key_name_admin(usr)] has forced [key_name_admin(M)] to use a random name.")
// Update the mob's name with a random one straight away
var/random_name = random_name(M.client.prefs.gender, M.client.prefs.species)
var/random_name = random_name(M.client.prefs.active_character.gender, M.client.prefs.active_character.species)
M.rename_character(M.real_name, random_name)
// Save that random name for next rounds
M.client.prefs.real_name = random_name
M.client.prefs.save_character(M.client)
M.client.prefs.active_character.real_name = random_name
M.client.prefs.active_character.save(M.client)
else if(href_list["asays"])
if(!check_rights(R_ADMIN))
+4 -3
View File
@@ -63,9 +63,10 @@
for(var/mob/thisplayer in players_to_spawn)
var/mob/living/carbon/human/H = new /mob/living/carbon/human(T)
H.name = random_name(pick(MALE,FEMALE))
var/datum/preferences/A = new() //Randomize appearance
A.real_name = H.name
A.copy_to(H)
var/datum/character_save/S = new //Randomize appearance
S.randomise()
S.real_name = H.name
S.copy_to(H)
H.dna.ready_dna(H)
H.mind_initialize()
@@ -124,9 +124,10 @@ GLOBAL_VAR_INIT(sent_syndicate_infiltration_team, 0)
var/syndicate_infiltrator_name = random_name(pick(MALE,FEMALE))
var/datum/preferences/A = new() //Randomize appearance
A.real_name = syndicate_infiltrator_name
A.copy_to(new_syndicate_infiltrator)
var/datum/character_save/S = new //Randomize appearance
S.randomise()
S.real_name = syndicate_infiltrator_name
S.copy_to(new_syndicate_infiltrator)
new_syndicate_infiltrator.dna.ready_dna(new_syndicate_infiltrator)
//Creates mind stuff.
+13 -11
View File
@@ -32,7 +32,7 @@
if(M.stat || !M.mind || M.mind.special_role || M.mind.offstation_role)
return FALSE
if(temp)
if((M.mind.assigned_role in temp.restricted_jobs) || (M.client.prefs.species in temp.protected_species))
if((M.mind.assigned_role in temp.restricted_jobs) || (M.client.prefs.active_character.species in temp.protected_species))
return FALSE
if(role) // Don't even bother evaluating if there's no role
if(player_old_enough_antag(M.client,role) && (role in M.client.prefs.be_special) && !M.client.skip_antag && (!jobban_isbanned(M, role)))
@@ -374,8 +374,8 @@
//First we spawn a dude.
var/mob/living/carbon/human/new_character = new(pick(GLOB.latejoin))//The mob being spawned.
var/datum/preferences/A = new(G_found.client)
A.copy_to(new_character)
// Then clone stuff over
G_found.client.prefs.active_character.copy_to(new_character)
new_character.dna.ready_dna(new_character)
new_character.key = G_found.key
@@ -388,13 +388,14 @@
var/syndicate_commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major")
var/syndicate_commando_name = pick(GLOB.last_names)
var/datum/preferences/A = new()//Randomize appearance for the commando.
var/datum/character_save/S = new //Randomize appearance for the commando.
S.randomise()
if(syndicate_leader_selected)
A.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]"
A.age = rand(35,45)
S.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]"
S.age = rand(35, 45)
else
A.real_name = "[syndicate_commando_rank] [syndicate_commando_name]"
A.copy_to(new_syndicate_commando)
S.real_name = "[syndicate_commando_rank] [syndicate_commando_name]"
S.copy_to(new_syndicate_commando)
new_syndicate_commando.dna.ready_dna(new_syndicate_commando)//Creates DNA.
@@ -576,7 +577,8 @@
if(candidates.len)
var/teamOneMembers = 5
var/teamTwoMembers = 5
var/datum/preferences/A = new()
var/datum/character_save/S = new
S.randomise()
for(var/thing in GLOB.landmarks_list)
var/obj/effect/landmark/L = thing
if(L.name == "tdome1")
@@ -585,7 +587,7 @@
var/mob/living/carbon/human/newMember = new(L.loc)
A.copy_to(newMember)
S.copy_to(newMember)
newMember.dna.ready_dna(newMember)
@@ -607,7 +609,7 @@
var/mob/living/carbon/human/newMember = new(L.loc)
A.copy_to(newMember)
S.copy_to(newMember)
newMember.dna.ready_dna(newMember)
+3 -2
View File
@@ -11,8 +11,9 @@
continue
if(is_type_in_list(H.dna.species, incompatible_species))
H.set_species(/datum/species/human)
var/datum/preferences/A = new() // Randomize appearance
A.copy_to(H)
var/datum/character_save/S = new // Randomize appearance
S.randomise()
S.copy_to(H)
SSticker.mode.traitors += H.mind
H.mind.special_role = SPECIAL_ROLE_TRAITOR
+3 -2
View File
@@ -12,8 +12,9 @@
continue
if(is_type_in_list(H.dna.species, incompatible_species))
H.set_species(/datum/species/human)
var/datum/preferences/A = new() // Randomize appearance
A.copy_to(H)
var/datum/character_save/S = new // Randomize appearance
S.randomise()
S.copy_to(H)
for(var/obj/item/I in H)
if(istype(I, /obj/item/implant))
+5 -3
View File
@@ -396,10 +396,12 @@ Traitors and the like can also be revived with the previous role mostly intact.
new_character.age = record_found.fields["age"]
new_character.dna.blood_type = record_found.fields["blood_type"]
else
// We make a random character
new_character.change_gender(pick(MALE,FEMALE))
var/datum/preferences/A = new()
A.real_name = G_found.real_name
A.copy_to(new_character)
var/datum/character_save/S = new
S.randomise()
S.real_name = G_found.real_name
S.copy_to(new_character)
if(!new_character.real_name)
new_character.real_name = random_name(new_character.gender)
+6 -5
View File
@@ -138,13 +138,14 @@ GLOBAL_VAR_INIT(sent_strike_team, 0)
var/commando_leader_rank = pick("Lieutenant", "Captain", "Major")
var/commando_name = pick(GLOB.commando_names)
var/datum/preferences/A = new()//Randomize appearance for the commando.
var/datum/character_save/S = new //Randomize appearance for the commando.
S.randomise()
if(is_leader)
A.age = rand(35,45)
A.real_name = "[commando_leader_rank] [commando_name]"
S.age = rand(35, 45)
S.real_name = "[commando_leader_rank] [commando_name]"
else
A.real_name = "[commando_name]"
A.copy_to(new_commando)
S.real_name = "[commando_name]"
S.copy_to(new_commando)
new_commando.dna.ready_dna(new_commando)//Creates DNA.
@@ -104,13 +104,14 @@ GLOBAL_VAR_INIT(sent_syndicate_strike_team, 0)
var/syndicate_commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major")
var/syndicate_commando_name = pick(GLOB.last_names)
var/datum/preferences/A = new()//Randomize appearance for the commando.
var/datum/character_save/S = new //Randomize appearance for the commando.
S.randomise()
if(is_leader)
A.age = rand(35,45)
A.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]"
S.age = rand(35, 45)
S.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]"
else
A.real_name = "[syndicate_commando_rank] [syndicate_commando_name]"
A.copy_to(new_syndicate_commando)
S.real_name = "[syndicate_commando_rank] [syndicate_commando_name]"
S.copy_to(new_syndicate_commando)
new_syndicate_commando.dna.ready_dna(new_syndicate_commando)//Creates DNA.
+14 -16
View File
@@ -16,8 +16,21 @@
qdel(query_watchfind)
return
else
qdel(query_watchfind)
target_ckey = new_ckey
if(check_watchlist(target_ckey))
var/already_watched = FALSE
var/datum/db_query/query_watch = SSdbcore.NewQuery("SELECT reason FROM watch WHERE ckey=:target_ckey", list(
"target_ckey" = target_ckey
))
if(!query_watch.warn_execute())
qdel(query_watch)
return
if(query_watch.NextRow())
already_watched = TRUE
qdel(query_watch)
if(already_watched)
to_chat(usr, "<span class='redtext'>[target_ckey] is already on the watchlist.</span>")
return
var/reason = input(usr,"Please state the reason","Reason") as message|null
@@ -132,18 +145,3 @@
output += "<br>[reason]<hr style='background:#000000; border:0; height:1px'>"
usr << browse(output, "window=watchwin;size=900x500")
qdel(query_watchlist)
/proc/check_watchlist(target_ckey)
var/datum/db_query/query_watch = SSdbcore.NewQuery("SELECT reason FROM watch WHERE ckey=:target_ckey", list(
"target_ckey" = target_ckey
))
if(!query_watch.warn_execute())
qdel(query_watch)
return
if(query_watch.NextRow())
var/entry = query_watch.item[1]
qdel(query_watch)
return entry
else
qdel(query_watch)
return 0
@@ -16,25 +16,28 @@
/obj/item/golem_shell/attackby(obj/item/I, mob/user, params)
..()
var/static/list/golem_shell_species_types = list(
/obj/item/stack/sheet/metal = /datum/species/golem,
/obj/item/stack/sheet/glass = /datum/species/golem/glass,
/obj/item/stack/sheet/plasteel = /datum/species/golem/plasteel,
/obj/item/stack/ore/glass = /datum/species/golem/sand,
/obj/item/stack/sheet/mineral/sandstone = /datum/species/golem/sand,
/obj/item/stack/sheet/mineral/plasma = /datum/species/golem/plasma,
/obj/item/stack/sheet/mineral/diamond = /datum/species/golem/diamond,
/obj/item/stack/sheet/mineral/gold = /datum/species/golem/gold,
/obj/item/stack/sheet/mineral/silver = /datum/species/golem/silver,
/obj/item/stack/sheet/mineral/uranium = /datum/species/golem/uranium,
/obj/item/stack/sheet/mineral/bananium = /datum/species/golem/bananium,
/obj/item/stack/sheet/mineral/tranquillite = /datum/species/golem/tranquillite,
/obj/item/stack/sheet/mineral/titanium = /datum/species/golem/titanium,
/obj/item/stack/sheet/mineral/plastitanium = /datum/species/golem/plastitanium,
/obj/item/stack/sheet/mineral/abductor = /datum/species/golem/alloy,
/obj/item/stack/sheet/wood = /datum/species/golem/wood,
/obj/item/stack/sheet/bluespace_crystal = /datum/species/golem/bluespace,
/obj/item/stack/sheet/mineral/adamantine = /datum/species/golem/adamantine,
/obj/item/stack/sheet/plastic = /datum/species/golem/plastic)
/obj/item/stack/sheet/metal = /datum/species/golem,
/obj/item/stack/sheet/glass = /datum/species/golem/glass,
/obj/item/stack/sheet/plasteel = /datum/species/golem/plasteel,
/obj/item/stack/ore/glass = /datum/species/golem/sand,
/obj/item/stack/sheet/mineral/sandstone = /datum/species/golem/sand,
/obj/item/stack/sheet/mineral/plasma = /datum/species/golem/plasma,
/obj/item/stack/sheet/mineral/diamond = /datum/species/golem/diamond,
/obj/item/stack/sheet/mineral/gold = /datum/species/golem/gold,
/obj/item/stack/sheet/mineral/silver = /datum/species/golem/silver,
/obj/item/stack/sheet/mineral/uranium = /datum/species/golem/uranium,
/obj/item/stack/sheet/mineral/bananium = /datum/species/golem/bananium,
/obj/item/stack/sheet/mineral/tranquillite = /datum/species/golem/tranquillite,
/obj/item/stack/sheet/mineral/titanium = /datum/species/golem/titanium,
/obj/item/stack/sheet/mineral/plastitanium = /datum/species/golem/plastitanium,
/obj/item/stack/sheet/mineral/abductor = /datum/species/golem/alloy,
/obj/item/stack/sheet/wood = /datum/species/golem/wood,
/obj/item/stack/sheet/bluespace_crystal = /datum/species/golem/bluespace,
/obj/item/stack/medical/bruise_pack = /datum/species/golem/cloth,
/obj/item/stack/medical/bruise_pack/improvised = /datum/species/golem/cloth,
/obj/item/stack/sheet/cloth = /datum/species/golem/cloth,
/obj/item/stack/sheet/mineral/adamantine = /datum/species/golem/adamantine,
/obj/item/stack/sheet/plastic = /datum/species/golem/plastic)
if(istype(I, /obj/item/stack))
var/obj/item/stack/O = I
+67 -213
View File
@@ -268,8 +268,8 @@
show_update_prompt = TRUE
else if(byond_version == SUGGESTED_CLIENT_VERSION && byond_build < SUGGESTED_CLIENT_BUILD)
show_update_prompt = TRUE
// Actually sent to client much later, so it appears after MOTD.
// Actually sent to client much later, so it appears after MOTD.
to_chat(src, "<span class='warning'>If the title screen is black, resources are still downloading. Please be patient until the title screen appears.</span>")
GLOB.directory[ckey] = src
@@ -278,25 +278,53 @@
if(GLOB.configuration.admin.enable_localhost_autoadmin)
if(is_connecting_from_localhost())
new /datum/admins("!LOCALHOST!", R_HOST, ckey) // Makes localhost rank
holder = GLOB.admin_datums[ckey]
if(holder)
GLOB.admins += src
holder.owner = src
//preferences datum - also holds some persistant data for the client (because we may as well keep these datums to a minimum)
prefs = GLOB.preferences_datums[ckey]
if(!prefs)
prefs = new /datum/preferences(src)
GLOB.preferences_datums[ckey] = prefs
log_client_to_db(tdata) // Make sure our client exists in the DB
// This is where we load all of the clients stuff from the DB
if(SSdbcore.IsConnected())
// Load in all our client data from the DB
var/list/datum/db_query/login_queries = list() // List of queries to run for login processing
for(var/datum/client_login_processor/CLP as anything in GLOB.client_login_processors)
login_queries[CLP.type] = CLP.get_query(src)
SSdbcore.MassExecute(login_queries, TRUE, FALSE, TRUE, FALSE) // Warn, no qdel, assoc, no log
// Now do fancy things with the results
for(var/datum/client_login_processor/CLP as anything in GLOB.client_login_processors)
CLP.process_result(login_queries[CLP.type], src)
QDEL_LIST_ASSOC_VAL(login_queries) // Clear out the used queries
else
prefs.parent = src
// Set vars here that need to be set if the DB is offline
// Give blank prefs
prefs = new /datum/preferences(src)
prefs.character_saves = list()
// Random character
prefs.character_saves += new /datum/character_save
prefs.active_character = prefs.character_saves[1]
// ToS accepted
tos_consent = TRUE
prefs.last_ip = address //these are gonna be used for banning
prefs.last_id = computer_id //these are gonna be used for banning
if(world.byond_version >= 511 && byond_version >= 511 && prefs.clientfps)
fps = prefs.clientfps
fps = prefs.clientfps
// Check if the client has or has not accepted TOS
check_tos_consent()
// Log alts
if(length(related_accounts_ip))
log_admin("[key_name(src)] Alts by IP: [jointext(related_accounts_ip, " ")]")
if(length(related_accounts_cid))
log_admin("[key_name(src)] Alts by CID: [jointext(related_accounts_cid, " ")]")
// This has to go here to avoid issues
// If you sleep past this point, you will get SSinput errors as well as goonchat errors
@@ -307,11 +335,21 @@
spawn() // Goonchat does some non-instant checks in start()
chatOutput.start()
var/_2fa_alert = FALSE // This is so we can display the message where it will be seen
if(holder)
on_holder_add()
add_admin_verbs()
// Must be async because any sleeps (happen in sql queries) will break connectings clients
INVOKE_ASYNC(src, .proc/admin_memo_output, "Show", FALSE, TRUE)
if(GLOB.configuration.system.is_production && (holder.rights & R_ADMIN) && prefs._2fa_status == _2FA_DISABLED) // If they are an admin and their 2FA is disabled
// No, check_rights() does not work in the above proc, because we dont have a mob yet
_2fa_alert = TRUE
// This also has to be manually done since no mob to use check_rights() on
deadmin()
verbs += /client/proc/readmin
GLOB.deadmins += ckey
else
on_holder_add()
add_admin_verbs()
// Must be async because any sleeps (happen in sql queries) will break connectings clients
INVOKE_ASYNC(src, .proc/admin_memo_output, "Show", FALSE, TRUE)
// Forcibly enable hardware-accelerated graphics, as we need them for the lighting overlays.
// (but turn them off first, since sometimes BYOND doesn't turn them on properly otherwise)
@@ -320,7 +358,6 @@
winset(src, null, "command=\".configure graphics-hwmode off\"")
winset(src, null, "command=\".configure graphics-hwmode on\"")
log_client_to_db(tdata)
. = ..() //calls mob.Login()
@@ -332,7 +369,6 @@
if(SSinput.initialized)
set_macros()
donator_check()
check_ip_intel()
send_resources()
@@ -383,11 +419,12 @@
if(world.TgsAvailable() && length(GLOB.revision_info.testmerges))
to_chat(src, GLOB.revision_info.get_testmerge_chatmessage(TRUE))
INVOKE_ASYNC(src, .proc/cid_count_check)
if(check_rights(R_ADMIN, FALSE, mob)) // Mob is required. Dont even try without it.
to_chat(src, "The queue server is currently [SSqueue.queue_enabled ? "<font color='green'>enabled</font>" : "<font color='disabled'>disabled</font>"], with a threshold of <b>[SSqueue.queue_threshold]</b>. This <b>[SSqueue.persist_queue ? "will" : "will not"]</b> persist through rounds.")
if(_2fa_alert)
to_chat(src,"<span class='boldannounce'><big>You do not have 2FA enabled. Admin verbs will be unavailable until you have enabled 2FA.</big></span>") // Very fucking obvious
/client/proc/is_connecting_from_localhost()
var/localhost_addresses = list("127.0.0.1", "::1") // Adresses
@@ -420,52 +457,18 @@
return QDEL_HINT_HARDDEL_NOW
/client/proc/donator_check()
set waitfor = FALSE // This needs to run async because any sleep() inside /client/New() breaks stuff badly
if(IsGuestKey(key))
return
if(!SSdbcore.IsConnected())
return
if(check_rights(R_ADMIN, 0, mob)) // Yes, the mob is required, regardless of other examples in this file, it won't work otherwise
donator_level = DONATOR_LEVEL_MAX
donor_loadout_points()
return
//Donator stuff.
var/datum/db_query/query_donor_select = SSdbcore.NewQuery("SELECT ckey, tier, active FROM donators WHERE ckey=:ckey", list(
"ckey" = ckey
))
if(!query_donor_select.warn_execute())
qdel(query_donor_select)
return
while(query_donor_select.NextRow())
if(!text2num(query_donor_select.item[3]))
// Inactive donator.
donator_level = 0
qdel(query_donor_select)
return
donator_level = text2num(query_donor_select.item[2])
donor_loadout_points()
break
qdel(query_donor_select)
/client/proc/donor_loadout_points()
if(donator_level > 0 && prefs)
prefs.max_gear_slots = GLOB.configuration.general.base_loadout_points + 5
/client/proc/log_client_to_db(connectiontopic)
set waitfor = FALSE // This needs to run async because any sleep() inside /client/New() breaks stuff badly
if(IsGuestKey(key))
return
if(!SSdbcore.IsConnected())
return
var/datum/db_query/query = SSdbcore.NewQuery("SELECT id, datediff(Now(),firstseen) as age FROM player WHERE ckey=:ckey", list(
var/datum/db_query/query = SSdbcore.NewQuery("SELECT id, datediff(Now(), firstseen) as age FROM player WHERE ckey=:ckey", list(
"ckey" = ckey
))
if(!query.warn_execute())
@@ -480,67 +483,29 @@
break
qdel(query)
var/datum/db_query/query_ip = SSdbcore.NewQuery("SELECT ckey FROM player WHERE ip=:address", list(
"address" = address
))
if(!query_ip.warn_execute())
qdel(query_ip)
return
related_accounts_ip = list()
while(query_ip.NextRow())
if(ckey != query_ip.item[1])
related_accounts_ip.Add("[query_ip.item[1]]")
qdel(query_ip)
var/datum/db_query/query_cid = SSdbcore.NewQuery("SELECT ckey FROM player WHERE computerid=:cid", list(
"cid" = computer_id
))
if(!query_cid.warn_execute())
qdel(query_cid)
return
related_accounts_cid = list()
while(query_cid.NextRow())
if(ckey != query_cid.item[1])
related_accounts_cid.Add("[query_cid.item[1]]")
qdel(query_cid)
var/admin_rank = "Player"
// Admins don't get slammed by this, I guess
if(holder)
admin_rank = holder.rank
// Admins don't get slammed by this, I guess
else
if(check_randomizer(connectiontopic))
return
//Log all the alts
if(related_accounts_cid.len)
log_admin("[key_name(src)] alts:[jointext(related_accounts_cid, " - ")]")
var/watchreason = check_watchlist(ckey)
if(watchreason)
message_admins("<font color='red'><B>Notice: </B></font><font color='#EB4E00'>[key_name_admin(src)] is on the watchlist and has just connected - Reason: [watchreason]</font>")
SSdiscord.send2discord_simple_noadmins("**\[Watchlist]** [key_name(src)] is on the watchlist and has just connected - Reason: [watchreason]")
watchlisted = TRUE
//Just the standard check to see if it's actually a number
if(sql_id)
//Just the standard check to see if it's actually a number
if(istext(sql_id))
sql_id = text2num(sql_id)
if(!isnum(sql_id))
return
return // Return here because if we somehow didnt pull a number from an INT column, EVERYTHING is breaking
if(sql_id)
var/client_address = address
if(!client_address) // Localhost can sometimes have no address set
client_address = "127.0.0.1"
//Player already identified previously, we need to just update the 'lastseen', 'ip' and 'computer_id' variables
var/datum/db_query/query_update = SSdbcore.NewQuery("UPDATE player SET lastseen = Now(), ip=:sql_ip, computerid=:sql_cid, lastadminrank=:sql_ar WHERE id=:sql_id", list(
var/datum/db_query/query_update = SSdbcore.NewQuery("UPDATE player SET lastseen=NOW(), ip=:sql_ip, computerid=:sql_cid, lastadminrank=:sql_ar WHERE id=:sql_id", list(
"sql_ip" = client_address,
"sql_cid" = computer_id,
"sql_ar" = admin_rank,
@@ -550,6 +515,7 @@
if(!query_update.warn_execute())
qdel(query_update)
return
qdel(query_update)
// After the regular update
INVOKE_ASYNC(src, /client/.proc/get_byond_account_date, FALSE) // Async to avoid other procs in the client chain being delayed by a web request
@@ -565,7 +531,7 @@
qdel(query_insert)
return
qdel(query_insert)
// This is their first connection instance, so TRUE here to nofiy admins
// This is their first connection instance, so TRUE here to notify admins
// This needs to happen here to ensure they actually have a row to update
INVOKE_ASYNC(src, /client/.proc/get_byond_account_date, TRUE) // Async to avoid other procs in the client chain being delayed by a web request
@@ -868,6 +834,7 @@
winset(src, "rpane.forumb", "background-color=#40628a;text-color=#FFFFFF")
winset(src, "rpane.rulesb", "background-color=#40628a;text-color=#FFFFFF")
winset(src, "rpane.githubb", "background-color=#40628a;text-color=#FFFFFF")
winset(src, "rpane.webmap", "background-color=#40628a;text-color=#FFFFFF")
/* Mainwindow */
winset(src, "mainwindow.saybutton", "background-color=#40628a;text-color=#FFFFFF")
winset(src, "mainwindow.mebutton", "background-color=#40628a;text-color=#FFFFFF")
@@ -900,6 +867,7 @@
winset(src, "rpane.forumb", "background-color=none;text-color=#000000")
winset(src, "rpane.rulesb", "background-color=none;text-color=#000000")
winset(src, "rpane.githubb", "background-color=none;text-color=#000000")
winset(src, "rpane.webmap", "background-color=none;text-color=#000000")
/* Mainwindow */
winset(src, "mainwindow.saybutton", "background-color=none;text-color=#000000")
winset(src, "mainwindow.mebutton", "background-color=none;text-color=#000000")
@@ -1051,7 +1019,7 @@
qdel(query_date)
// They have a date, lets bail
// They have a date already, lets bail
if(byondacc_date)
return
@@ -1092,120 +1060,6 @@
/client/proc/show_update_notice()
to_chat(src, "<span class='userdanger'>Your BYOND client (v: [byond_version].[byond_build]) is out of date. This can cause glitches. We highly suggest you download the latest client from <a href='https://www.byond.com/download/'>byond.com</a> before playing. You can also update via the BYOND launcher application.</span>")
/**
* Checks if the client has accepted TOS
*
* Runs some checks against vars and the DB to see if the client has accepted TOS.
* Returns TRUE or FALSE if they have or have not
*/
/client/proc/check_tos_consent()
// If there is no TOS, auto accept
if(!GLOB.join_tos)
tos_consent = TRUE
return TRUE
// If theres no DB, assume yes
if(!SSdbcore.IsConnected())
tos_consent = TRUE
return TRUE
var/datum/db_query/query = SSdbcore.NewQuery("SELECT ckey FROM privacy WHERE ckey=:ckey AND consent=1", list(
"ckey" = ckey
))
if(!query.warn_execute())
qdel(query)
// If our query failed, just assume yes
tos_consent = TRUE
return TRUE
// If we returned a row, they accepted
while(query.NextRow())
qdel(query)
tos_consent = TRUE
return TRUE
qdel(query)
// If we are here, they have not accepted, and need to read it
return FALSE
/**
* Checks if the client has more than a configured amount of CIDs tied to them in the past
*/
/client/proc/cid_count_check()
// If the config is 0, disable this
if(GLOB.configuration.general.max_client_cid_history == 0)
return
// If we have no DB, dont even bother
if(!SSdbcore.IsConnected())
return
// Now query how many cids they have
var/datum/db_query/query_cidcheck = SSdbcore.NewQuery("SELECT COUNT(DISTINCT computerID) FROM connection_log WHERE ckey=:ckey", list(
"ckey" = ckey
))
if(!query_cidcheck.warn_execute())
qdel(query_cidcheck)
return
var/cidcount = 0
if(query_cidcheck.NextRow())
cidcount = query_cidcheck.item[1]
qdel(query_cidcheck)
if(cidcount > GLOB.configuration.general.max_client_cid_history)
// Check their notes for CID tracking in the past
var/has_note = FALSE
var/note_text = ""
var/datum/db_query/query_find_track_note = SSdbcore.NewQuery("SELECT notetext FROM notes WHERE ckey=:ckey AND adminckey=:ackey", list(
"ckey" = ckey,
"ackey" = CIDTRACKING_PSUEDO_CKEY
))
if(!query_find_track_note.warn_execute())
qdel(query_find_track_note)
return
if(query_find_track_note.NextRow())
note_text = query_find_track_note.item[1] // Grab existing note text
has_note = TRUE
qdel(query_find_track_note)
if(has_note) // They have a note. Update it.
var/new_text = "Connected on the date of this note with unique CID #[cidcount]"
// Only update the note if the text is different. Otherwise it bumps the timestamp when it shouldnt
if(note_text != new_text)
var/datum/db_query/query_update_track_note = SSdbcore.NewQuery("UPDATE notes SET notetext=:notetext, timestamp=NOW(), round_id=:rid WHERE ckey=:ckey AND adminckey=:ackey", list(
"notetext" = new_text,
"ckey" = ckey,
"ackey" = CIDTRACKING_PSUEDO_CKEY,
"rid" = GLOB.round_id
))
if(!query_update_track_note.warn_execute())
qdel(query_update_track_note)
return
qdel(query_update_track_note)
else // They dont have a note. Make one.
// NOT logged because its automatic and will spam logs otherwise
// Also right checking must be disabled because its a psuedockey, not a real one
add_note(ckey, "Connected on the date of this note with unique CID #[cidcount]", adminckey = CIDTRACKING_PSUEDO_CKEY, logged = FALSE, checkrights = FALSE, automated = TRUE)
var/show_warning = TRUE
// Check if they have a note that matches the warning suppressor
var/datum/db_query/query_find_note = SSdbcore.NewQuery("SELECT id FROM notes WHERE ckey=:ckey AND notetext=:notetext", list(
"ckey" = ckey,
"notetext" = CIDWARNING_SUPPRESSED_NOTETEXT
))
if(!query_find_note.warn_execute())
qdel(query_find_note)
return
if(query_find_note.NextRow())
show_warning = FALSE
qdel(query_find_note)
if(show_warning)
message_admins("<font color='red'>[ckey] has just connected and has a history of [cidcount] different CIDs.</font> (<a href='?_src_=holder;webtools=[ckey]'>WebInfo</a>) (<a href='?_src_=holder;suppresscidwarning=[ckey]'>Suppress Warning</a>)")
/client/proc/update_ambience_pref()
if(prefs.sound & SOUND_AMBIENCE)
if(SSambience.ambience_listening_clients[src] > world.time)
@@ -0,0 +1,31 @@
/datum/client_login_processor/load_preferences
priority = 10
/datum/client_login_processor/load_preferences/get_query(client/C)
var/datum/db_query/query = SSdbcore.NewQuery({"SELECT
ooccolor,
UI_style,
UI_style_color,
UI_style_alpha,
be_role,
default_slot,
toggles,
toggles_2,
sound,
volume_mixer,
lastchangelog,
exp,
clientfps,
atklog,
fuid,
parallax,
2fa_status
FROM player
WHERE ckey=:ckey"}, list(
"ckey" = C.ckey
))
return query
/datum/client_login_processor/load_preferences/process_result(datum/db_query/Q, client/C)
C.prefs = new /datum/preferences(C, Q)
@@ -0,0 +1,102 @@
/datum/client_login_processor/load_characters
priority = 20
/datum/client_login_processor/load_characters/get_query(client/C)
// Get all their characters
var/datum/db_query/query = SSdbcore.NewQuery({"SELECT
OOC_Notes,
real_name,
name_is_always_random,
gender,
age,
species,
language,
hair_colour,
secondary_hair_colour,
facial_hair_colour,
secondary_facial_hair_colour,
skin_tone,
skin_colour,
marking_colours,
head_accessory_colour,
hair_style_name,
facial_style_name,
marking_styles,
head_accessory_style_name,
alt_head_name,
eye_colour,
underwear,
undershirt,
backbag,
b_type,
alternate_option,
job_support_high,
job_support_med,
job_support_low,
job_medsci_high,
job_medsci_med,
job_medsci_low,
job_engsec_high,
job_engsec_med,
job_engsec_low,
job_karma_high,
job_karma_med,
job_karma_low,
flavor_text,
med_record,
sec_record,
gen_record,
disabilities,
player_alt_titles,
organ_data,
rlimb_data,
nanotrasen_relation,
speciesprefs,
socks,
body_accessory,
gear,
autohiss,
slot
FROM characters WHERE ckey=:ckey"}, list(
"ckey" = C.ckey
))
return query
/datum/client_login_processor/load_characters/process_result(datum/db_query/Q, client/C)
// If we already loaded their characters, dont do it all again
if(C.prefs.characters_loaded)
return
// Step one, initialize their list of characters
C.prefs.character_saves = list()
C.prefs.character_saves.len = C.prefs.max_save_slots // Fill the list with empty indexes
// Fill it with blank characters first
for(var/i in 1 to C.prefs.max_save_slots)
var/datum/character_save/CS = new
CS.slot_number = i
C.prefs.character_saves[i] = CS
// Did we load at all
var/character_loaded = FALSE
while(Q.NextRow())
character_loaded = TRUE
var/datum/character_save/CS = C.prefs.character_saves[Q.item[53]] // Get the slot referenced by this query
CS.load(Q) // Let the save handle the query processing
CS.valid_save = TRUE
if(character_loaded)
// They have a character, set their active as their default slot
C.prefs.active_character = C.prefs.character_saves[C.prefs.default_slot]
else
// If we are here, they dont have a character. Lets make them a random one
var/datum/character_save/CS = C.prefs.character_saves[1] // Get slot 1
CS.randomise()
CS.real_name = random_name(CS.gender) // Pick a name
CS.valid_save = TRUE
CS.save(C)
C.prefs.active_character = C.prefs.character_saves[1] // Set slot 1 as their active
C.prefs.default_slot = 1
C.prefs.save_preferences(C)
C.prefs.characters_loaded = TRUE // Avoid this happening again
@@ -0,0 +1,24 @@
/datum/client_login_processor/tos_consent
priority = 30
/datum/client_login_processor/tos_consent/get_query(client/C)
var/datum/db_query/query = SSdbcore.NewQuery("SELECT ckey FROM privacy WHERE ckey=:ckey AND consent=1", list(
"ckey" = C.ckey
))
return query
/datum/client_login_processor/tos_consent/process_result(datum/db_query/Q, client/C)
// If there is no TOS, auto accept
if(!GLOB.join_tos)
C.tos_consent = TRUE
return
// If our query failed, just assume yes
if(Q.last_error)
C.tos_consent = TRUE
return
// If we returned a row, they accepted
while(Q.NextRow())
C.tos_consent = TRUE
@@ -0,0 +1,27 @@
/datum/client_login_processor/donator_check
priority = 35
/datum/client_login_processor/donator_check/get_query(client/C)
var/datum/db_query/query = SSdbcore.NewQuery("SELECT ckey, tier, active FROM donators WHERE ckey=:ckey", list(
"ckey" = C.ckey
))
return query
/datum/client_login_processor/donator_check/process_result(datum/db_query/Q, client/C)
if(IsGuestKey(C.key))
return
// Admins get all donor perks
if(check_rights_client(R_ADMIN, FALSE, C))
C.donator_level = DONATOR_LEVEL_MAX
C.donor_loadout_points()
return
while(Q.NextRow())
if(!text2num(Q.item[3]))
// Inactive donator.
C.donator_level = 0
return
C.donator_level = text2num(Q.item[2])
C.donor_loadout_points()
@@ -0,0 +1,15 @@
/datum/client_login_processor/watchlist
priority = 36
/datum/client_login_processor/watchlist/get_query(client/C)
var/datum/db_query/query = SSdbcore.NewQuery("SELECT reason FROM watch WHERE ckey=:ckey", list(
"ckey" = C.ckey
))
return query
/datum/client_login_processor/watchlist/process_result(datum/db_query/Q, client/C)
if(Q.NextRow())
var/watchreason = Q.item[1]
message_admins("<font color='red'><B>Notice: </B></font><font color='#EB4E00'>[key_name_admin(C)] is on the watchlist and has just connected - Reason: [watchreason]</font>")
SSdiscord.send2discord_simple_noadmins("**\[Watchlist]** [key_name(C)] is on the watchlist and has just connected - Reason: [watchreason]")
C.watchlisted = TRUE
@@ -0,0 +1,14 @@
/datum/client_login_processor/alts_ip
priority = 37
/datum/client_login_processor/alts_ip/get_query(client/C)
var/datum/db_query/query = SSdbcore.NewQuery("SELECT ckey FROM player WHERE ip=:address", list(
"address" = C.address
))
return query
/datum/client_login_processor/alts_ip/process_result(datum/db_query/Q, client/C)
while(Q.NextRow())
if("[Q.item[1]]" == C.ckey)
continue
C.related_accounts_ip += "[Q.item[1]]"
@@ -0,0 +1,14 @@
/datum/client_login_processor/alts_cid
priority = 38
/datum/client_login_processor/alts_cid/get_query(client/C)
var/datum/db_query/query = SSdbcore.NewQuery("SELECT ckey FROM player WHERE computerid=:cid", list(
"cid" = C.computer_id
))
return query
/datum/client_login_processor/alts_cid/process_result(datum/db_query/Q, client/C)
while(Q.NextRow())
if("[Q.item[1]]" == C.ckey)
continue
C.related_accounts_cid += "[Q.item[1]]"
@@ -0,0 +1,71 @@
/datum/client_login_processor/cid_count
priority = 39
/datum/client_login_processor/cid_count/get_query(client/C)
var/datum/db_query/query = SSdbcore.NewQuery("SELECT COUNT(DISTINCT computerID) FROM connection_log WHERE ckey=:ckey", list(
"ckey" = C.ckey
))
return query
/datum/client_login_processor/cid_count/process_result(datum/db_query/Q, client/C)
// If the config is 0, disable this
if(GLOB.configuration.general.max_client_cid_history == 0)
return
// Now query how many cids they have
var/cidcount = 0
if(Q.NextRow())
cidcount = Q.item[1]
if(cidcount > GLOB.configuration.general.max_client_cid_history)
// Check their notes for CID tracking in the past
var/has_note = FALSE
var/note_text = ""
var/datum/db_query/query_find_track_note = SSdbcore.NewQuery("SELECT notetext FROM notes WHERE ckey=:ckey AND adminckey=:ackey", list(
"ckey" = C.ckey,
"ackey" = CIDTRACKING_PSUEDO_CKEY
))
if(!query_find_track_note.warn_execute())
qdel(query_find_track_note)
return
if(query_find_track_note.NextRow())
note_text = query_find_track_note.item[1] // Grab existing note text
has_note = TRUE
qdel(query_find_track_note)
if(has_note) // They have a note. Update it.
var/new_text = "Connected on the date of this note with unique CID #[cidcount]"
// Only update the note if the text is different. Otherwise it bumps the timestamp when it shouldnt
if(note_text != new_text)
var/datum/db_query/query_update_track_note = SSdbcore.NewQuery("UPDATE notes SET notetext=:notetext, timestamp=NOW(), round_id=:rid WHERE ckey=:ckey AND adminckey=:ackey", list(
"notetext" = new_text,
"ckey" = C.ckey,
"ackey" = CIDTRACKING_PSUEDO_CKEY,
"rid" = GLOB.round_id
))
if(!query_update_track_note.warn_execute())
qdel(query_update_track_note)
return
qdel(query_update_track_note)
else // They dont have a note. Make one.
// NOT logged because its automatic and will spam logs otherwise
// Also right checking must be disabled because its a psuedockey, not a real one
add_note(C.ckey, "Connected on the date of this note with unique CID #[cidcount]", adminckey = CIDTRACKING_PSUEDO_CKEY, logged = FALSE, checkrights = FALSE, automated = TRUE)
var/show_warning = TRUE
// Check if they have a note that matches the warning suppressor
var/datum/db_query/query_find_note = SSdbcore.NewQuery("SELECT id FROM notes WHERE ckey=:ckey AND notetext=:notetext", list(
"ckey" = C.ckey,
"notetext" = CIDWARNING_SUPPRESSED_NOTETEXT
))
if(!query_find_note.warn_execute())
qdel(query_find_note)
return
if(query_find_note.NextRow())
show_warning = FALSE
qdel(query_find_note)
if(show_warning)
message_admins("<font color='red'>[C.ckey] has just connected and has a history of [cidcount] different CIDs.</font> (<a href='?_src_=holder;webtools=[C.ckey]'>WebInfo</a>) (<a href='?_src_=holder;suppresscidwarning=[C.ckey]'>Suppress Warning</a>)")
@@ -0,0 +1,51 @@
/**
* # Client Login Processor Framework
*
* The holder class for all client data processing
*
* This framework is designed for loading in client data from the database.
* Login processors have their own queries, which will be put into one async batch and
* executed at the same time, to reduce the time it takes for a client to login.
* Login processors can also be given priorities to have things fire in specific orders
* EG: Load their preferences before their job bans, etc etc
*
* When creating these, please name the files with the priority at the start, and the typepath after
* EG: 10-load_preferences.dm
* This makes it easier to track stuff down -AA07
*
* Also if you have used other languages before with "interface" types (Java, C# (Microsoft Java), etc),
* treat this class as one of those. [get_query(client/C)] and [process_result(datum/db_query/Q, client/C)] MUST be overriden
*/
/datum/client_login_processor
/// The login priority. A lower priority will fire first
var/priority = 0
/**
* Query Getter
*
* Gets the DB query for this login processor
*
* Takes the client as an arg instead of just the ckey incase we need more data (IP, CID, etc).
* Returns a DB query datum.
*
* Arguments:
* * C - The client to use to generate the query
*/
/datum/client_login_processor/proc/get_query(client/C)
RETURN_TYPE(/datum/db_query)
CRASH("get_query() not overriden for [type]!")
/**
* Result Processor
*
* Takes the (now executed) query and the client and parses the required data out
* Note: This can be a no-op if you want to just update the DB, just return on the override
*
* Arguments:
* * Q - The DB query to process data from
* * C - The client to store stuff on
*/
/datum/client_login_processor/proc/process_result(datum/db_query/Q, client/C)
CRASH("process_result() not overriden for [type]!")
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,32 +1,6 @@
/datum/preferences/proc/load_preferences(client/C)
var/datum/db_query/query = SSdbcore.NewQuery({"SELECT
ooccolor,
UI_style,
UI_style_color,
UI_style_alpha,
be_role,
default_slot,
toggles,
toggles_2,
sound,
volume_mixer,
lastchangelog,
exp,
clientfps,
atklog,
fuid,
parallax,
2fa_status
FROM player
WHERE ckey=:ckey"}, list(
"ckey" = C.ckey
))
if(!query.warn_execute())
qdel(query)
return
/datum/preferences/proc/load_preferences(datum/db_query/query)
// Looking for the query?
// Check ../login_processing/10-load_preferences.dm
//general preferences
while(query.NextRow())
@@ -48,8 +22,6 @@
parallax = text2num(query.item[16])
_2fa_status = query.item[17]
qdel(query)
//Sanitize
ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor))
UI_style = sanitize_inlist(UI_style, list("White", "Midnight", "Plasmafire", "Retro", "Slimecore", "Operative"), initial(UI_style))
@@ -65,7 +37,7 @@
atklog = sanitize_integer(atklog, 0, 100, initial(atklog))
fuid = sanitize_integer(fuid, 0, 10000000, initial(fuid))
parallax = sanitize_integer(parallax, 0, 16, initial(parallax))
return 1
return TRUE
/datum/preferences/proc/save_preferences(client/C)
@@ -126,550 +98,23 @@
qdel(query)
return 1
/datum/preferences/proc/load_character(client/C, slot)
saved = FALSE
if(!slot)
slot = default_slot
slot = sanitize_integer(slot, 1, max_save_slots, initial(default_slot))
if(slot != default_slot)
default_slot = slot
var/datum/db_query/firstquery = SSdbcore.NewQuery("UPDATE player SET default_slot=:slot WHERE ckey=:ckey", list(
"slot" = slot,
"ckey" = C.ckey
))
if(!firstquery.warn_execute(async = FALSE)) // Dont make this async. It makes roundstart slow.
qdel(firstquery)
return
qdel(firstquery)
if(!C) // If the client disconnected during the query, try again later.
qdel(src)
return TRUE
// Let's not have this explode if you sneeze on the DB
var/datum/db_query/query = SSdbcore.NewQuery({"SELECT
OOC_Notes,
real_name,
name_is_always_random,
gender,
age,
species,
language,
hair_colour,
secondary_hair_colour,
facial_hair_colour,
secondary_facial_hair_colour,
skin_tone,
skin_colour,
marking_colours,
head_accessory_colour,
hair_style_name,
facial_style_name,
marking_styles,
head_accessory_style_name,
alt_head_name,
eye_colour,
underwear,
undershirt,
backbag,
b_type,
alternate_option,
job_support_high,
job_support_med,
job_support_low,
job_medsci_high,
job_medsci_med,
job_medsci_low,
job_engsec_high,
job_engsec_med,
job_engsec_low,
job_karma_high,
job_karma_med,
job_karma_low,
flavor_text,
med_record,
sec_record,
gen_record,
disabilities,
player_alt_titles,
organ_data,
rlimb_data,
nanotrasen_relation,
speciesprefs,
socks,
body_accessory,
gear,
autohiss
FROM characters WHERE ckey=:ckey AND slot=:slot"}, list(
"ckey" = C.ckey,
"slot" = slot
))
if(!query.warn_execute(async = FALSE)) // Dont make this async. It makes roundstart slow.
qdel(query)
return
while(query.NextRow())
//Character
metadata = query.item[1]
real_name = query.item[2]
be_random_name = text2num(query.item[3])
gender = query.item[4]
age = text2num(query.item[5])
species = query.item[6]
language = query.item[7]
h_colour = query.item[8]
h_sec_colour = query.item[9]
f_colour = query.item[10]
f_sec_colour = query.item[11]
s_tone = text2num(query.item[12])
s_colour = query.item[13]
m_colours = params2list(query.item[14])
hacc_colour = query.item[15]
h_style = query.item[16]
f_style = query.item[17]
m_styles = params2list(query.item[18])
ha_style = query.item[19]
alt_head = query.item[20]
e_colour = query.item[21]
underwear = query.item[22]
undershirt = query.item[23]
backbag = query.item[24]
b_type = query.item[25]
//Jobs
alternate_option = text2num(query.item[26])
job_support_high = text2num(query.item[27])
job_support_med = text2num(query.item[28])
job_support_low = text2num(query.item[29])
job_medsci_high = text2num(query.item[30])
job_medsci_med = text2num(query.item[31])
job_medsci_low = text2num(query.item[32])
job_engsec_high = text2num(query.item[33])
job_engsec_med = text2num(query.item[34])
job_engsec_low = text2num(query.item[35])
job_karma_high = text2num(query.item[36])
job_karma_med = text2num(query.item[37])
job_karma_low = text2num(query.item[38])
//Miscellaneous
flavor_text = query.item[39]
med_record = query.item[40]
sec_record = query.item[41]
gen_record = query.item[42]
// Apparently, the preceding vars weren't always encoded properly...
if(findtext(flavor_text, "<")) // ... so let's clumsily check for tags!
flavor_text = html_encode(flavor_text)
if(findtext(med_record, "<"))
med_record = html_encode(med_record)
if(findtext(sec_record, "<"))
sec_record = html_encode(sec_record)
if(findtext(gen_record, "<"))
gen_record = html_encode(gen_record)
disabilities = text2num(query.item[43])
player_alt_titles = params2list(query.item[44])
organ_data = params2list(query.item[45])
rlimb_data = params2list(query.item[46])
nanotrasen_relation = query.item[47]
speciesprefs = text2num(query.item[48])
//socks
socks = query.item[49]
body_accessory = query.item[50]
loadout_gear = params2list(query.item[51])
autohiss_mode = text2num(query.item[52])
saved = TRUE
qdel(query)
//Sanitize
var/datum/species/SP = GLOB.all_species[species]
metadata = sanitize_text(metadata, initial(metadata))
real_name = reject_bad_name(real_name, 1)
if(isnull(species)) species = "Human"
if(isnull(language)) language = "None"
if(isnull(nanotrasen_relation)) nanotrasen_relation = initial(nanotrasen_relation)
if(isnull(speciesprefs)) speciesprefs = initial(speciesprefs)
if(!real_name) real_name = random_name(gender,species)
be_random_name = sanitize_integer(be_random_name, 0, 1, initial(be_random_name))
gender = sanitize_gender(gender, FALSE, !SP.has_gender)
age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
h_colour = sanitize_hexcolor(h_colour)
h_sec_colour = sanitize_hexcolor(h_sec_colour)
f_colour = sanitize_hexcolor(f_colour)
f_sec_colour = sanitize_hexcolor(f_sec_colour)
s_tone = sanitize_integer(s_tone, -185, 34, initial(s_tone))
s_colour = sanitize_hexcolor(s_colour)
for(var/marking_location in m_colours)
m_colours[marking_location] = sanitize_hexcolor(m_colours[marking_location], DEFAULT_MARKING_COLOURS[marking_location])
hacc_colour = sanitize_hexcolor(hacc_colour)
h_style = sanitize_inlist(h_style, GLOB.hair_styles_public_list, initial(h_style))
f_style = sanitize_inlist(f_style, GLOB.facial_hair_styles_list, initial(f_style))
for(var/marking_location in m_styles)
m_styles[marking_location] = sanitize_inlist(m_styles[marking_location], GLOB.marking_styles_list, DEFAULT_MARKING_STYLES[marking_location])
ha_style = sanitize_inlist(ha_style, GLOB.head_accessory_styles_list, initial(ha_style))
alt_head = sanitize_inlist(alt_head, GLOB.alt_heads_list, initial(alt_head))
e_colour = sanitize_hexcolor(e_colour)
underwear = sanitize_text(underwear, initial(underwear))
undershirt = sanitize_text(undershirt, initial(undershirt))
backbag = sanitize_text(backbag, initial(backbag))
b_type = sanitize_text(b_type, initial(b_type))
autohiss_mode = sanitize_integer(autohiss_mode, 0, 2, initial(autohiss_mode))
alternate_option = sanitize_integer(alternate_option, 0, 2, initial(alternate_option))
job_support_high = sanitize_integer(job_support_high, 0, 65535, initial(job_support_high))
job_support_med = sanitize_integer(job_support_med, 0, 65535, initial(job_support_med))
job_support_low = sanitize_integer(job_support_low, 0, 65535, initial(job_support_low))
job_medsci_high = sanitize_integer(job_medsci_high, 0, 65535, initial(job_medsci_high))
job_medsci_med = sanitize_integer(job_medsci_med, 0, 65535, initial(job_medsci_med))
job_medsci_low = sanitize_integer(job_medsci_low, 0, 65535, initial(job_medsci_low))
job_engsec_high = sanitize_integer(job_engsec_high, 0, 65535, initial(job_engsec_high))
job_engsec_med = sanitize_integer(job_engsec_med, 0, 65535, initial(job_engsec_med))
job_engsec_low = sanitize_integer(job_engsec_low, 0, 65535, initial(job_engsec_low))
job_karma_high = sanitize_integer(job_karma_high, 0, 65535, initial(job_karma_high))
job_karma_med = sanitize_integer(job_karma_med, 0, 65535, initial(job_karma_med))
job_karma_low = sanitize_integer(job_karma_low, 0, 65535, initial(job_karma_low))
disabilities = sanitize_integer(disabilities, 0, 65535, initial(disabilities))
socks = sanitize_text(socks, initial(socks))
body_accessory = sanitize_text(body_accessory, initial(body_accessory))
// if(isnull(disabilities)) disabilities = 0
if(!player_alt_titles) player_alt_titles = new()
if(!organ_data) src.organ_data = list()
if(!rlimb_data) src.rlimb_data = list()
if(!loadout_gear) loadout_gear = list()
// Check if the current body accessory exists
if(!GLOB.body_accessory_by_name[body_accessory])
body_accessory = null
return 1
/datum/preferences/proc/save_character(client/C)
var/organlist
var/rlimblist
var/playertitlelist
var/gearlist
var/markingcolourslist = list2params(m_colours)
var/markingstyleslist = list2params(m_styles)
if(!isemptylist(organ_data))
organlist = list2params(organ_data)
if(!isemptylist(rlimb_data))
rlimblist = list2params(rlimb_data)
if(!isemptylist(player_alt_titles))
playertitlelist = list2params(player_alt_titles)
if(!isemptylist(loadout_gear))
gearlist = list2params(loadout_gear)
var/datum/db_query/firstquery = SSdbcore.NewQuery("SELECT slot FROM characters WHERE ckey=:ckey ORDER BY slot", list(
"ckey" = C.ckey
))
if(!firstquery.warn_execute())
qdel(firstquery)
return
while(firstquery.NextRow())
if(text2num(firstquery.item[1]) == default_slot)
var/datum/db_query/query = SSdbcore.NewQuery({"UPDATE characters
SET
OOC_Notes=:metadata,
real_name=:real_name,
name_is_always_random=:be_random_name,
gender=:gender,
age=:age,
species=:species,
language=:language,
hair_colour=:h_colour,
secondary_hair_colour=:h_sec_colour,
facial_hair_colour=:f_colour,
secondary_facial_hair_colour=:f_sec_colour,
skin_tone=:s_tone,
skin_colour=:s_colour,
marking_colours=:markingcolourslist,
head_accessory_colour=:hacc_colour,
hair_style_name=:h_style,
facial_style_name=:f_style,
marking_styles=:markingstyleslist,
head_accessory_style_name=:ha_style,
alt_head_name=:alt_head,
eye_colour=:e_colour,
underwear=:underwear,
undershirt=:undershirt,
backbag=:backbag,
b_type=:b_type,
alternate_option=:alternate_option,
job_support_high=:job_support_high,
job_support_med=:job_support_med,
job_support_low=:job_support_low,
job_medsci_high=:job_medsci_high,
job_medsci_med=:job_medsci_med,
job_medsci_low=:job_medsci_low,
job_engsec_high=:job_engsec_high,
job_engsec_med=:job_engsec_med,
job_engsec_low=:job_engsec_low,
job_karma_high=:job_karma_high,
job_karma_med=:job_karma_med,
job_karma_low=:job_karma_low,
flavor_text=:flavor_text,
med_record=:med_record,
sec_record=:sec_record,
gen_record=:gen_record,
player_alt_titles=:playertitlelist,
disabilities=:disabilities,
organ_data=:organlist,
rlimb_data=:rlimblist,
nanotrasen_relation=:nanotrasen_relation,
speciesprefs=:speciesprefs,
socks=:socks,
body_accessory=:body_accessory,
gear=:gearlist,
autohiss=:autohiss_mode
WHERE ckey=:ckey
AND slot=:slot"}, list(
// OH GOD SO MANY PARAMETERS
"metadata" = metadata,
"real_name" = real_name,
"be_random_name" = be_random_name,
"gender" = gender,
"age" = age,
"species" = species,
"language" = language,
"h_colour" = h_colour,
"h_sec_colour" = h_sec_colour,
"f_colour" = f_colour,
"f_sec_colour" = f_sec_colour,
"s_tone" = s_tone,
"s_colour" = s_colour,
"markingcolourslist" = markingcolourslist,
"hacc_colour" = hacc_colour,
"h_style" = h_style,
"f_style" = f_style,
"markingstyleslist" = markingstyleslist,
"ha_style" = ha_style,
"alt_head" = (alt_head ? alt_head : ""), // This it intentional. It wont work without it!
"e_colour" = e_colour,
"underwear" = underwear,
"undershirt" = undershirt,
"backbag" = backbag,
"b_type" = b_type,
"alternate_option" = alternate_option,
"job_support_high" = job_support_high,
"job_support_med" = job_support_med,
"job_support_low" = job_support_low,
"job_medsci_high" = job_medsci_high,
"job_medsci_med" = job_medsci_med,
"job_medsci_low" = job_medsci_low,
"job_engsec_high" = job_engsec_high,
"job_engsec_med" = job_engsec_med,
"job_engsec_low" = job_engsec_low,
"job_karma_high" = job_karma_high,
"job_karma_med" = job_karma_med,
"job_karma_low" = job_karma_low,
"flavor_text" = flavor_text,
"med_record" = med_record,
"sec_record" = sec_record,
"gen_record" = gen_record,
"playertitlelist" = (playertitlelist ? playertitlelist : ""), // This it intentnional. It wont work without it!
"disabilities" = disabilities,
"organlist" = (organlist ? organlist : ""),
"rlimblist" = (rlimblist ? rlimblist : ""),
"nanotrasen_relation" = nanotrasen_relation,
"speciesprefs" = speciesprefs,
"socks" = socks,
"body_accessory" = (body_accessory ? body_accessory : ""),
"gearlist" = (gearlist ? gearlist : ""),
"autohiss_mode" = autohiss_mode,
"ckey" = C.ckey,
"slot" = default_slot
)
)
if(!query.warn_execute())
qdel(firstquery)
qdel(query)
return
qdel(firstquery)
qdel(query)
return 1
qdel(firstquery)
var/datum/db_query/query = SSdbcore.NewQuery({"
INSERT INTO characters (ckey, slot, OOC_Notes, real_name, name_is_always_random, gender,
age, species, language,
hair_colour, secondary_hair_colour,
facial_hair_colour, secondary_facial_hair_colour,
skin_tone, skin_colour,
marking_colours,
head_accessory_colour,
hair_style_name,
facial_style_name,
marking_styles,
head_accessory_style_name,
alt_head_name,
eye_colour,
underwear, undershirt,
backbag, b_type, alternate_option,
job_support_high, job_support_med, job_support_low,
job_medsci_high, job_medsci_med, job_medsci_low,
job_engsec_high, job_engsec_med, job_engsec_low,
job_karma_high, job_karma_med, job_karma_low,
flavor_text,
med_record,
sec_record,
gen_record,
player_alt_titles,
disabilities, organ_data, rlimb_data, nanotrasen_relation, speciesprefs,
socks, body_accessory, gear, autohiss)
VALUES
(:ckey, :slot, :metadata, :name, :be_random_name, :gender,
:age, :species, :language,
:h_colour, :h_sec_colour,
:f_colour, :f_sec_colour,
:s_tone, :s_colour,
:markingcolourslist,
:hacc_colour,
:h_style,
:f_style,
:markingstyleslist,
:ha_style,
:alt_head,
:e_colour,
:underwear, :undershirt,
:backbag, :b_type, :alternate_option,
:job_support_high, :job_support_med, :job_support_low,
:job_medsci_high, :job_medsci_med, :job_medsci_low,
:job_engsec_high, :job_engsec_med, :job_engsec_low,
:job_karma_high, :job_karma_med, :job_karma_low,
:flavor_text,
:med_record,
:sec_record,
:gen_record,
:playertitlelist,
:disabilities, :organlist, :rlimblist, :nanotrasen_relation, :speciesprefs,
:socks, :body_accessory, :gearlist, :autohiss_mode)
"}, list(
// This has too many params for anyone to look at this without going insae
"ckey" = C.ckey,
"slot" = default_slot,
"metadata" = metadata,
"name" = real_name,
"be_random_name" = be_random_name,
"gender" = gender,
"age" = age,
"species" = species,
"language" = language,
"h_colour" = h_colour,
"h_sec_colour" = h_sec_colour,
"f_colour" = f_colour,
"f_sec_colour" = f_sec_colour,
"s_tone" = s_tone,
"s_colour" = s_colour,
"markingcolourslist" = markingcolourslist,
"hacc_colour" = hacc_colour,
"h_style" = h_style,
"f_style" = f_style,
"markingstyleslist" = markingstyleslist,
"ha_style" = ha_style,
"alt_head" = alt_head,
"e_colour" = e_colour,
"underwear" = underwear,
"undershirt" = undershirt,
"backbag" = backbag,
"b_type" = b_type,
"alternate_option" = alternate_option,
"job_support_high" = job_support_high,
"job_support_med" = job_support_med,
"job_support_low" = job_support_low,
"job_medsci_high" = job_medsci_high,
"job_medsci_med" = job_medsci_med,
"job_medsci_low" = job_medsci_low,
"job_engsec_high" = job_engsec_high,
"job_engsec_med" = job_engsec_med,
"job_engsec_low" = job_engsec_low,
"job_karma_high" = job_karma_high,
"job_karma_med" = job_karma_med,
"job_karma_low" = job_karma_low,
"flavor_text" = flavor_text,
"med_record" = med_record,
"sec_record" = sec_record,
"gen_record" = gen_record,
"playertitlelist" = (playertitlelist ? playertitlelist : ""), // This it intentnional. It wont work without it!
"disabilities" = disabilities,
"organlist" = (organlist ? organlist : ""),
"rlimblist" = (rlimblist ? rlimblist : ""),
"nanotrasen_relation" = nanotrasen_relation,
"speciesprefs" = speciesprefs,
"socks" = socks,
"body_accessory" = (body_accessory ? body_accessory : ""),
"gearlist" = (gearlist ? gearlist : ""),
"autohiss_mode" = autohiss_mode
))
if(!query.warn_execute())
qdel(query)
return
qdel(query)
saved = TRUE
return 1
/datum/preferences/proc/load_random_character_slot(client/C)
var/datum/db_query/query = SSdbcore.NewQuery("SELECT slot FROM characters WHERE ckey=:ckey ORDER BY slot", list(
"ckey" = C.ckey
))
var/list/saves = list()
var/list/datum/character_save/valid_slots = list()
for(var/datum/character_save/CS in character_saves)
if(CS.valid_save)
valid_slots += CS
if(!query.warn_execute(async = FALSE)) // Dont async this. Youll make roundstart slow.
qdel(query)
if(!length(valid_slots))
// They have no valid saves. Lets just randomise #1
var/datum/character_save/CS = C.prefs.character_saves[1] // Get slot 1
CS.randomise()
CS.real_name = random_name(CS.gender) // Pick a name
C.prefs.active_character = C.prefs.character_saves[1] // Set slot 1 as their active
return
while(query.NextRow())
saves += text2num(query.item[1])
qdel(query)
if(!saves.len)
load_character(C)
return 0
load_character(C,pick(saves))
return 1
/datum/preferences/proc/clear_character_slot(client/C)
. = FALSE
// Is there a character in that slot?
var/datum/db_query/query = SSdbcore.NewQuery("SELECT slot FROM characters WHERE ckey=:ckey AND slot=:slot", list(
"ckey" = C.ckey,
"slot" = default_slot
))
if(!query.warn_execute())
qdel(query)
return
if(!query.NextRow())
qdel(query)
return
qdel(query)
var/datum/db_query/delete_query = SSdbcore.NewQuery("DELETE FROM characters WHERE ckey=:ckey AND slot=:slot", list(
"ckey" = C.ckey,
"slot" = default_slot
))
if(!delete_query.warn_execute())
qdel(delete_query)
return
qdel(delete_query)
saved = FALSE
return TRUE
var/datum/character_save/CS = pick(valid_slots)
C.prefs.active_character = CS
/**
* Saves [/datum/preferences/proc/volume_mixer] for the current client.
+4 -2
View File
@@ -458,10 +458,12 @@ BLIND // can't see anything
/obj/item/clothing/shoes/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/match) && src.loc == user)
var/obj/item/match/M = I
if(M.matchignite()) // Match isn't lit, but isn't burnt.
if(!M.lit && !M.burnt) // Match isn't lit, but isn't burnt.
user.visible_message("<span class='warning'>[user] strikes a [M] on the bottom of [src], lighting it.</span>","<span class='warning'>You strike [M] on the bottom of [src] to light it.</span>")
M.matchignite()
playsound(user.loc, 'sound/goonstation/misc/matchstick_light.ogg', 50, 1)
else
return
if(M.lit && !M.burnt)
user.visible_message("<span class='warning'>[user] crushes [M] into the bottom of [src], extinguishing it.</span>","<span class='warning'>You crush [M] into the bottom of [src], extinguishing it.</span>")
M.dropped()
return
+6 -2
View File
@@ -5,7 +5,7 @@
icon_state = "hardsuit0-ert_commander"
item_state = "helm-command"
item_color = "ert_commander"
armor = list(melee = 45, bullet = 25, laser = 30, energy = 10, bomb = 25, bio = 100, rad = 50, fire = 80, acid = 80)
armor = list(MELEE = 45, BULLET = 25, LASER = 30, ENERGY = 10, BOMB = 25, BIO = 100, RAD = 50, FIRE = 80, ACID = 80)
resistance_flags = FIRE_PROOF
var/obj/machinery/camera/camera
var/has_camera = TRUE
@@ -50,7 +50,7 @@
item_state = "suit-command"
w_class = WEIGHT_CLASS_NORMAL
allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank/internals)
armor = list(melee = 45, bullet = 25, laser = 30, energy = 10, bomb = 25, bio = 100, rad = 50, fire = 80, acid = 80)
armor = list(MELEE = 45, BULLET = 25, LASER = 30, ENERGY = 10, BOMB = 25, BIO = 100, RAD = 50, FIRE = 80, ACID = 80)
allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/t_scanner, /obj/item/rcd, /obj/item/crowbar, \
/obj/item/screwdriver, /obj/item/weldingtool, /obj/item/wirecutters, /obj/item/wrench, /obj/item/multitool, \
/obj/item/radio, /obj/item/analyzer, /obj/item/gun, /obj/item/melee/baton, /obj/item/reagent_containers/spray/pepper, \
@@ -121,6 +121,7 @@
/obj/item/clothing/head/helmet/space/hardsuit/ert/engineer
name = "emergency response team engineer helmet"
desc = "A helmet worn by engineers of a Nanotrasen Emergency Response Team. Has yellow highlights. Armoured and space ready."
armor = list(MELEE = 45, BULLET = 25, LASER = 30, ENERGY = 10, BOMB = 25, BIO = 100, RAD = 75, FIRE = 80, ACID = 80)
icon_state = "hardsuit0-ert_engineer"
item_state = "helm-orange"
item_color = "ert_engineer"
@@ -129,6 +130,7 @@
/obj/item/clothing/head/helmet/space/hardsuit/ert/engineer/gamma
name = "elite emergency response team engineer helmet"
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
armor = list(MELEE = 45, BULLET = 25, LASER = 30, ENERGY = 10, BOMB = 25, BIO = 100, RAD = 100, FIRE = 80, ACID = 80)
icon_state = "hardsuit0-gammaengineer"
item_color = "gammaengineer"
@@ -137,12 +139,14 @@
desc = "A suit worn by the engineers of a Nanotrasen Emergency Response Team. Has yellow highlights. Armoured, space ready, and fire resistant."
icon_state = "ert_engineer"
item_state = "suit-orange"
armor = list(MELEE = 45, BULLET = 25, LASER = 30, ENERGY = 10, BOMB = 25, BIO = 100, RAD = 75, FIRE = 80, ACID = 80)
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/engineer
/obj/item/clothing/suit/space/hardsuit/ert/engineer/gamma
name = "elite emergency response team engineer suit"
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
icon_state = "ert_gengineer"
armor = list(MELEE = 45, BULLET = 25, LASER = 30, ENERGY = 10, BOMB = 25, BIO = 100, RAD = 100, FIRE = 80, ACID = 80)
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/engineer/gamma
//Medical
+1
View File
@@ -27,6 +27,7 @@
var/mob/living/simple_animal/mouse/blobinfected/B = new(vent.loc)
var/mob/M = pick(candidates)
B.key = M.key
B.mind.special_role = SPECIAL_ROLE_BLOB
SSticker.mode.update_blob_icons_added(B.mind)
to_chat(B, "<span class='userdanger'>You are now a mouse, infected with blob spores. Find somewhere isolated... before you burst and become the blob! Use ventcrawl (alt-click on vents) to move around.</span>")
@@ -3,7 +3,7 @@
name = "carp fillet"
desc = "A fillet of spess carp meat."
icon = 'icons/obj/food/seafood.dmi'
icon_state = "fishfillet"
icon_state = "carpfillet" // Sprite created by https://github.com/binarysudoku for Goonstation, They have relicensed it for our use.
filling_color = "#FFDEFE"
bitesize = 6
list_reagents = list("protein" = 3, "carpotoxin" = 2, "vitamin" = 2)
@@ -13,7 +13,7 @@
name = "raw salmon"
desc = "A fillet of raw salmon."
icon = 'icons/obj/food/seafood.dmi'
icon_state = "fishfillet"
icon_state = "salmonfillet" // Sprite created by https://github.com/binarysudoku for Goonstation, They have relicensed it for our use.
filling_color = "#FFDEFE"
bitesize = 6
list_reagents = list("protein" = 3, "vitamin" = 2)
@@ -23,7 +23,7 @@
name = "salmon steak"
desc = "A fillet of freshly-grilled salmon meat."
icon = 'icons/obj/food/seafood.dmi'
icon_state = "salmonsteak"
icon_state = "salmonsteak" // Sprite based on one created by https://github.com/binarysudoku for Goonstation, They have relicensed it for our use.
trash = /obj/item/trash/plate
filling_color = "#7A3D11"
bitesize = 3
@@ -34,7 +34,7 @@
name = "raw catfish"
desc = "A fillet of raw catfish."
icon = 'icons/obj/food/seafood.dmi'
icon_state = "fishfillet"
icon_state = "catfishfillet" // Sprite created by https://github.com/binarysudoku for Goonstation, They have relicensed it for our use.
filling_color = "#FFDEFE"
bitesize = 6
list_reagents = list("protein" = 3, "vitamin" = 2)
+2
View File
@@ -12,3 +12,5 @@
var/technical_name
/// Path to the map file
var/map_path
/// URL to the maps webmap
var/webmap_url
+1
View File
@@ -2,3 +2,4 @@
fluff_name = "NSS Cyberiad"
technical_name = "Cyberiad"
map_path = "_maps/map_files/cyberiad/cyberiad.dmm"
webmap_url = "https://affectedarc07.github.io/SS13WebMap/Paradise/Cyberiad/"
+1
View File
@@ -2,3 +2,4 @@
fluff_name = "NSS Kerberos"
technical_name = "Delta"
map_path = "_maps/map_files/Delta/delta.dmm"
webmap_url = "https://affectedarc07.github.io/SS13WebMap/Paradise/Delta/"
+1
View File
@@ -2,3 +2,4 @@
fluff_name = "NSS Cerebron"
technical_name = "MetaStation"
map_path = "_maps/map_files/MetaStation/MetaStation.dmm"
webmap_url = "https://affectedarc07.github.io/SS13WebMap/Paradise/MetaStation/"
@@ -82,23 +82,6 @@
new random_crystal(loc)
qdel(src)
/obj/machinery/anomalous_crystal/honk //Strips and equips you as a clown. I apologize for nothing
activation_method = "mob_bump"
activation_sound = 'sound/items/bikehorn.ogg'
/obj/machinery/anomalous_crystal/honk/ActivationReaction(mob/user)
if(..() && ishuman(user) && !(user in affected_targets))
var/mob/living/carbon/human/H = user
for(var/obj/item/W in H)
H.unEquip(W)
var/datum/job/clown/C = SSjobs.GetJob("Clown")
C.equip(H)
affected_targets.Add(H)
/obj/machinery/anomalous_crystal/honk/New()
..()
activation_method = pick("mob_bump","speech")
/obj/machinery/anomalous_crystal/theme_warp //Warps the area you're in to look like a new one
activation_method = "touch"
cooldown_add = 200
+1 -1
View File
@@ -743,7 +743,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
return
var/mob/living/carbon/human/new_char = new(get_turf(src))
client.prefs.copy_to(new_char)
client.prefs.active_character.copy_to(new_char)
if(mind)
mind.active = TRUE
mind.transfer_to(new_char)
+5 -4
View File
@@ -31,10 +31,6 @@
update_static_data(owner, ui)
. = TRUE
/datum/orbit_menu/ui_data(mob/user)
var/list/data = list()
return data
/datum/orbit_menu/ui_static_data(mob/user)
var/list/data = list()
@@ -96,6 +92,7 @@
)
if(SSticker && SSticker.mode)
other_antags += list(
"Blob" = (mind.special_role == SPECIAL_ROLE_BLOB),
"Cultist" = (mind in SSticker.mode.cult),
"Wizard" = (mind in SSticker.mode.wizards),
"Wizard's Apprentice" = (mind in SSticker.mode.apprentices),
@@ -121,6 +118,10 @@
var/list/antag_serialized = serialized.Copy()
antag_serialized["antag"] = "Terror Spider"
antagonists += list(antag_serialized)
else if(istype(M, /mob/living/simple_animal/revenant))
var/list/antag_serialized = serialized.Copy()
antag_serialized["antag"] = "Revenant"
antagonists += list(antag_serialized)
else
misc += list(serialized)
+8
View File
@@ -207,6 +207,14 @@
items += s_store
return items
/mob/living/proc/unequip_everything()
var/list/items = list()
items |= get_equipped_items(TRUE)
for(var/I in items)
unEquip(I)
drop_l_hand()
drop_r_hand()
/obj/item/proc/equip_to_best_slot(mob/M)
if(src != M.get_active_hand())
to_chat(M, "<span class='warning'>You are not holding anything to equip!</span>")
+6 -6
View File
@@ -2,17 +2,17 @@
return message // no autohiss at this level
/mob/living/carbon/human/handle_autohiss(message, datum/language/L)
if(!client || client.prefs.autohiss_mode == AUTOHISS_OFF) // no need to process if there's no client or they have autohiss off
if(!client || client.prefs.active_character.autohiss_mode == AUTOHISS_OFF) // no need to process if there's no client or they have autohiss off
return message
return dna.species.handle_autohiss(message, L, client.prefs.autohiss_mode)
return dna.species.handle_autohiss(message, L, client.prefs.active_character.autohiss_mode)
/client/verb/toggle_autohiss()
set name = "Toggle Auto-Accent"
set desc = "Toggle automatic accents for your species"
set category = "OOC"
prefs.autohiss_mode = (prefs.autohiss_mode + 1) % AUTOHISS_NUM
switch(prefs.autohiss_mode)
prefs.active_character.autohiss_mode = (prefs.active_character.autohiss_mode + 1) % AUTOHISS_NUM
switch(prefs.active_character.autohiss_mode)
if(AUTOHISS_OFF)
to_chat(src, "Auto-hiss is now OFF.")
if(AUTOHISS_BASIC)
@@ -20,7 +20,7 @@
if(AUTOHISS_FULL)
to_chat(src, "Auto-hiss is now FULL.")
else
prefs.autohiss_mode = AUTOHISS_OFF
prefs.active_character.autohiss_mode = AUTOHISS_OFF
to_chat(src, "Auto-hiss is now OFF.")
/datum/species
@@ -61,7 +61,7 @@
/datum/species/drask
autohiss_basic_map = list(
"o" = list ("oo", "ooo"),
"u" = list ("uu", "uuu")
"u" = list ("uu", "uuu")
)
autohiss_extra_map = list(
"m" = list ("mm", "mmm")
@@ -1955,7 +1955,7 @@ Eyes need to have significantly high darksight to shine unless the mob has the X
if(GLOB.configuration.general.allow_character_metadata)
if(client)
to_chat(usr, "[src]'s Metainfo:<br>[sanitize(client.prefs.metadata)]")
to_chat(usr, "[src]'s Metainfo:<br>[sanitize(client.prefs.active_character.metadata)]")
else
to_chat(usr, "[src] does not have any stored infomation!")
else
@@ -674,3 +674,107 @@
/datum/unarmed_attack/golem/tranquillite
attack_sound = null
/datum/species/golem/cloth
name = "Cloth Golem"
icobase = 'icons/mob/human_races/r_cloth_golem.dmi'
flesh_color = "#E9E9E9"
blood_color = "#E9E9E9"
info_text = "As a <span class='danger'>Cloth Golem</span>, you are able to reform yourself after death, provided your remains aren't burned or destroyed. You are, of course, very flammable. \
Being made of cloth, your body is magic resistant and faster than that of other golems, but weaker and less resilient."
inherent_traits = list(TRAIT_RESISTCOLD, TRAIT_NOBREATH, TRAIT_RESISTHIGHPRESSURE, TRAIT_RESISTLOWPRESSURE, TRAIT_RADIMMUNE, TRAIT_PIERCEIMMUNE, TRAIT_CHUNKYFINGERS, TRAIT_NOPAIN)
inherent_biotypes = MOB_UNDEAD | MOB_HUMANOID
brute_mod = 0.85 //15% damage reduction
burn_mod = 1.7 // don't get burned
tox_mod = 0.85
clone_mod = 0.85
brain_mod = 0.85
stamina_mod = 0.85
speed_mod = 1 // not as heavy as stone
punchdamagelow = 4
punchstunthreshold = 7
punchdamagehigh = 8 // not as heavy as stone
prefix = "Cloth"
golem_colour = null
special_names = null
/datum/species/golem/cloth/get_random_name()
var/pharaoh_name = pick("Neferkare", "Hudjefa", "Khufu", "Mentuhotep", "Ahmose", "Amenhotep", "Thutmose", "Hatshepsut", "Tutankhamun", "Ramses", "Seti", \
"Merenptah", "Djer", "Semerkhet", "Nynetjer", "Khafre", "Pepi", "Intef", "Ay") //yes, Ay was an actual pharaoh
var/golem_name = "[pharaoh_name] \Roman[rand(1,99)]"
return golem_name
/datum/species/golem/cloth/handle_life(mob/living/carbon/human/H)
if(H.fire_stacks < 1)
H.adjust_fire_stacks(1) //always prone to burning
..()
/datum/species/golem/cloth/handle_death(gibbed, mob/living/carbon/human/H)
if(gibbed)
return
if(H.on_fire)
H.visible_message("<span class='danger'>[H] burns into ash!</span>")
H.dust()
return
H.visible_message("<span class='danger'>[H] falls apart into a pile of bandages!</span>")
new /obj/structure/cloth_pile(get_turf(H), H)
..()
/obj/structure/cloth_pile
name = "pile of bandages"
desc = "It emits a strange aura, as if there was still life within it..."
max_integrity = 50
armor = list(MELEE = 90, BULLET = 90, LASER = 25, ENERGY = 80, BOMB = 50, BIO = 100, FIRE = -50, ACID = -50)
icon = 'icons/obj/items.dmi'
icon_state = "pile_bandages"
resistance_flags = FLAMMABLE
var/revive_time = 900
var/mob/living/carbon/human/cloth_golem
/obj/structure/cloth_pile/Initialize(mapload, mob/living/carbon/human/H)
. = ..()
if(!QDELETED(H) && is_species(H, /datum/species/golem/cloth))
H.unequip_everything()
H.forceMove(src)
cloth_golem = H
to_chat(cloth_golem, "<span class='notice'>You start gathering your life energy, preparing to rise again...</span>")
addtimer(CALLBACK(src, .proc/revive), revive_time)
else
return INITIALIZE_HINT_QDEL
/obj/structure/cloth_pile/Destroy()
QDEL_NULL(cloth_golem)
return ..()
/obj/structure/cloth_pile/burn()
visible_message("<span class='danger'>[src] burns into ash!</span>")
new /obj/effect/decal/cleanable/ash(get_turf(src))
..()
/obj/structure/cloth_pile/proc/revive()
if(QDELETED(src) || QDELETED(cloth_golem)) //QDELETED also checks for null, so if no cloth golem is set this won't runtime
return
if(cloth_golem.suiciding)
QDEL_NULL(cloth_golem)
return
invisibility = INVISIBILITY_MAXIMUM //disappear before the animation
new /obj/effect/temp_visual/mummy_animation(get_turf(src))
cloth_golem.revive()
cloth_golem.grab_ghost() //won't pull if it's a suicide
sleep(20)
cloth_golem.forceMove(get_turf(src))
cloth_golem.visible_message("<span class='danger'>[src] rises and reforms into [cloth_golem]!</span>", "<span class='userdanger'>You reform into yourself!</span>")
cloth_golem = null
qdel(src)
/obj/structure/cloth_pile/attackby(obj/item/P, mob/living/carbon/human/user, params)
. = ..()
if(resistance_flags & ON_FIRE)
return
if(is_hot(P))
visible_message("<span class='danger'>[src] bursts into flames!</span>")
fire_act()
@@ -67,7 +67,7 @@
to_chat(H, "<span class='warning'>The water stings[volume < 10 ? " you, but isn't concentrated enough to harm you" : null]!</span>")
/datum/species/grey/after_equip_job(datum/job/J, mob/living/carbon/human/H)
var/translator_pref = H.client.prefs.speciesprefs
var/translator_pref = H.client.prefs.active_character.speciesprefs
if(translator_pref || ((ismindshielded(H) || J.is_command || J.supervisors == "the captain") && HAS_TRAIT(H, TRAIT_WINGDINGS)))
if(J.title == "Mime")
return
@@ -81,7 +81,7 @@
H.unEquip(H.wear_mask)
H.equip_or_collect(new /obj/item/clothing/mask/breath/vox(H), slot_wear_mask)
var/tank_pref = H.client && H.client.prefs ? H.client.prefs.speciesprefs : null
var/tank_pref = H.client && H.client.prefs ? H.client.prefs.active_character.speciesprefs : null
var/obj/item/tank/internal_tank
if(tank_pref)//Diseasel, here you go
internal_tank = new /obj/item/tank/internals/nitrogen(H)
+2 -21
View File
@@ -300,7 +300,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
/mob/living/silicon/robot/proc/pick_module()
if(module)
return
var/list/modules = list("Generalist", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security")
var/list/modules = list("Engineering", "Medical", "Miner", "Janitor", "Service")
if(islist(force_modules) && force_modules.len)
modules = force_modules.Copy()
if(mmi != null && mmi.alien)
@@ -315,14 +315,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
return
switch(modtype)
if("Generalist")
module = new /obj/item/robot_module/standard(src)
module.channels = list("Engineering" = 1, "Medical" = 1, "Security" = 1, "Service" = 1, "Supply" = 1)
module_sprites["Basic"] = "robot_old"
module_sprites["Android"] = "droid"
module_sprites["Default"] = "Standard"
module_sprites["Noble-STD"] = "Noble-STD"
if("Service")
module = new /obj/item/robot_module/butler(src)
module.channels = list("Service" = 1)
@@ -365,17 +357,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
see_reagents = TRUE
if("Security")
if(!weapons_unlock)
var/count_secborgs = 0
for(var/mob/living/silicon/robot/R in GLOB.alive_mob_list)
if(R && R.stat != DEAD && R.module && istype(R.module, /obj/item/robot_module/security))
count_secborgs++
var/max_secborgs = 2
if(GLOB.security_level == SEC_LEVEL_GREEN)
max_secborgs = 1
if(count_secborgs >= max_secborgs)
to_chat(src, "<span class='warning'>There are too many Security cyborgs active. Please choose another module.</span>")
return
module = new /obj/item/robot_module/security(src)
module.channels = list("Security" = 1)
module_sprites["Basic"] = "secborg"
@@ -1349,7 +1330,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
scrambledcodes = 1
req_one_access = list(ACCESS_CENT_SPECOPS)
ionpulse = 1
force_modules = list("Engineering", "Medical", "Security")
force_modules = list("Engineering", "Medical")
static_radio_channels = 1
allow_rename = FALSE
weapons_unlock = TRUE
@@ -265,47 +265,6 @@
/obj/item/robot_module/proc/handle_death(mob/living/silicon/robot/R, gibbed)
return
// Standard cyborg module.
/obj/item/robot_module/standard
// if station is fine, assist with constructing station goal room, cleaning, and repairing cables chewed by rats
// if medical crisis, assist by providing basic healthcare, retrieving corpses, and monitoring crew lifesigns
// if eng crisis, assist by helping repair hull breaches
// if sec crisis, assist by opening doors for sec and providing backup zipties on patrols
name = "generalist robot module"
module_type = "Standard"
subsystems = list(/mob/living/silicon/proc/subsystem_power_monitor, /mob/living/silicon/proc/subsystem_crew_monitor)
basic_modules = list(
// sec
/obj/item/flash/cyborg,
/obj/item/restraints/handcuffs/cable/zipties/cyborg,
// janitorial
/obj/item/soap/nanotrasen,
/obj/item/lightreplacer/cyborg,
// eng
/obj/item/crowbar/cyborg,
/obj/item/wrench/cyborg,
/obj/item/extinguisher, // for firefighting, and propulsion in space
/obj/item/weldingtool/largetank/cyborg,
// mining
/obj/item/pickaxe,
/obj/item/t_scanner/adv_mining_scanner,
/obj/item/storage/bag/ore/cyborg,
// med
/obj/item/healthanalyzer,
/obj/item/reagent_containers/borghypo/basic,
/obj/item/roller_holder, // for taking the injured to medbay without worsening their injuries or leaving a blood trail the whole way
/obj/item/stack/sheet/metal/cyborg,
/obj/item/stack/cable_coil/cyborg,
/obj/item/stack/rods/cyborg,
/obj/item/stack/tile/plasteel/cyborg
)
emag_modules = list(/obj/item/melee/energy/sword/cyborg)
special_rechargables = list(
/obj/item/extinguisher,
/obj/item/weldingtool/largetank/cyborg,
/obj/item/lightreplacer/cyborg
)
// Medical cyborg module.
/obj/item/robot_module/medical
name = "medical robot module"
@@ -319,6 +278,7 @@
/obj/item/borg_defib,
/obj/item/handheld_defibrillator,
/obj/item/roller_holder,
/obj/item/borg/cyborghug,
/obj/item/reagent_containers/borghypo,
/obj/item/scalpel/laser/laser1,
/obj/item/hemostat,
@@ -108,6 +108,8 @@
to_chat(src, "You feel your being twine with that of [M] as you merge with its biomass.")
throw_alert(gestalt_alert, /obj/screen/alert/nymph, new_master = src) //adds a screen alert that can call resist
forceMove(M)
else if(isrobot(M))
M.visible_message("<span class='notice'>[M] playfully boops [src] on the head!</span>", "<span class='notice'>You playfully boop [src] on the head!</span>")
else
get_scooped(M)
else
@@ -1,7 +1,7 @@
//Gutlunches, passive mods that devour blood and gibs
/mob/living/simple_animal/hostile/asteroid/gutlunch
name = "gutlunch"
desc = "A scavenger that eats raw meat, often found alongside ash walkers. Produces a thick, nutritious milk."
desc = "A scavenger that eats raw meat, often found alongside ash walkers. Produces a thick, medicinally nutritious milk."
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
icon_state = "gutlunch"
icon_living = "gutlunch"
@@ -104,7 +104,9 @@
if(prob(60))
reagents.add_reagent("cream", rand(2, 5))
if(prob(45))
reagents.add_reagent("salglu_solution", rand(2,5))
reagents.add_reagent("salglu_solution", rand(2, 5))
if(prob(30))
reagents.add_reagent("epinephrine", rand(2, 5))
//Male gutlunch. They're smaller and more colorful!
+2 -2
View File
@@ -684,13 +684,13 @@ GLOBAL_LIST_INIT(intents, list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM
/mob/proc/has_valid_preferences()
if(!client)
return FALSE //Not sure how this would get run without the mob having a client, but let's just be safe.
if(client.prefs.alternate_option != RETURN_TO_LOBBY)
if(client.prefs.active_character.alternate_option != RETURN_TO_LOBBY)
return TRUE
// If they have antags enabled, they're potentially doing this on purpose instead of by accident. Notify admins if so.
var/has_antags = FALSE
if(client.prefs.be_special.len > 0)
has_antags = TRUE
if(!client.prefs.check_any_job())
if(!client.prefs.active_character.check_any_job())
to_chat(src, "<span class='danger'>You have no jobs enabled, along with return to lobby if job is unavailable. This makes you ineligible for any round start role, please update your job preferences.</span>")
if(has_antags)
log_admin("[src.ckey] just got booted back to lobby with no jobs, but antags enabled.")
+26 -26
View File
@@ -42,7 +42,7 @@
/mob/new_player/proc/new_player_panel_proc()
var/real_name = client.prefs.real_name
var/real_name = client.prefs.active_character.real_name
if(client.prefs.toggles2 & PREFTOGGLE_2_RANDOMSLOT)
real_name = "Random Character Slot"
var/output = "<center><p><a href='byond://?src=[UID()];show_preferences=1'>Setup Character</A><br /><i>[real_name]</i></p>"
@@ -184,13 +184,13 @@
to_chat(src, "<span class='notice'>Now teleporting.</span>")
observer.forceMove(O.loc)
observer.timeofdeath = world.time // Set the time of death so that the respawn timer works correctly.
client.prefs.update_preview_icon(1)
observer.icon = client.prefs.preview_icon
client.prefs.active_character.update_preview_icon(1)
observer.icon = client.prefs.active_character.preview_icon
observer.alpha = 127
if(client.prefs.be_random_name)
client.prefs.real_name = random_name(client.prefs.gender,client.prefs.species)
observer.real_name = client.prefs.real_name
if(client.prefs.active_character.be_random_name)
client.prefs.active_character.real_name = random_name(client.prefs.active_character.gender,client.prefs.active_character.species)
observer.real_name = client.prefs.active_character.real_name
observer.name = observer.real_name
observer.key = key
QDEL_NULL(mind)
@@ -212,10 +212,10 @@
if(!SSticker || SSticker.current_state != GAME_STATE_PLAYING)
to_chat(usr, "<span class='warning'>The round is either not ready, or has already finished...</span>")
return
if(client.prefs.species in GLOB.whitelisted_species)
if(client.prefs.active_character.species in GLOB.whitelisted_species)
if(!is_alien_whitelisted(src, client.prefs.species))
to_chat(src, alert("You are currently not whitelisted to play [client.prefs.species]."))
if(!is_alien_whitelisted(src, client.prefs.active_character.species))
to_chat(src, alert("You are currently not whitelisted to play [client.prefs.active_character.species]."))
return FALSE
LateChoices()
@@ -232,12 +232,12 @@
if(client.prefs.toggles2 & PREFTOGGLE_2_RANDOMSLOT)
client.prefs.load_random_character_slot(client)
if(client.prefs.species in GLOB.whitelisted_species)
if(!is_alien_whitelisted(src, client.prefs.species))
to_chat(src, alert("You are currently not whitelisted to play [client.prefs.species]."))
if(client.prefs.active_character.species in GLOB.whitelisted_species)
if(!is_alien_whitelisted(src, client.prefs.active_character.species))
to_chat(src, alert("You are currently not whitelisted to play [client.prefs.active_character.species]."))
return FALSE
AttemptLateSpawn(href_list["SelectedJob"],client.prefs.spawnpoint)
AttemptLateSpawn(href_list["SelectedJob"],client.prefs.active_character.spawnpoint)
return
if(!ready && href_list["preference"])
@@ -543,9 +543,9 @@
new_character.lastarea = get_area(loc)
if(SSticker.random_players || appearance_isbanned(new_character))
client.prefs.random_character()
client.prefs.real_name = random_name(client.prefs.gender)
client.prefs.copy_to(new_character)
client.prefs.active_character.randomise()
client.prefs.active_character.real_name = random_name(client.prefs.active_character.gender)
client.prefs.active_character.copy_to(new_character)
stop_sound_channel(CHANNEL_LOBBYMUSIC)
@@ -569,19 +569,19 @@
// This is to check that the player only has preferences set that they're supposed to
/mob/new_player/proc/check_prefs_are_sane()
var/datum/species/chosen_species
if(client.prefs.species)
chosen_species = GLOB.all_species[client.prefs.species]
if(client.prefs.active_character.species)
chosen_species = GLOB.all_species[client.prefs.active_character.species]
if(!(chosen_species && (is_species_whitelisted(chosen_species) || has_admin_rights())))
// Have to recheck admin due to no usr at roundstart. Latejoins are fine though.
log_runtime(EXCEPTION("[src] had species [client.prefs.species], though they weren't supposed to. Setting to Human."), src)
client.prefs.species = "Human"
log_runtime(EXCEPTION("[src] had species [client.prefs.active_character.species], though they weren't supposed to. Setting to Human."), src)
client.prefs.active_character.species = "Human"
var/datum/language/chosen_language
if(client.prefs.language)
chosen_language = GLOB.all_languages[client.prefs.language]
if((chosen_language == null && client.prefs.language != "None") || (chosen_language && chosen_language.flags & RESTRICTED))
log_runtime(EXCEPTION("[src] had language [client.prefs.language], though they weren't supposed to. Setting to None."), src)
client.prefs.language = "None"
if(client.prefs.active_character.language)
chosen_language = GLOB.all_languages[client.prefs.active_character.language]
if((chosen_language == null && client.prefs.active_character.language != "None") || (chosen_language && chosen_language.flags & RESTRICTED))
log_runtime(EXCEPTION("[src] had language [client.prefs.active_character.language], though they weren't supposed to. Setting to None."), src)
client.prefs.active_character.language = "None"
/mob/new_player/proc/ViewManifest()
GLOB.generic_crew_manifest.ui_interact(usr, state = GLOB.always_state)
@@ -607,7 +607,7 @@
/mob/new_player/get_gender()
if(!client || !client.prefs) ..()
return client.prefs.gender
return client.prefs.active_character.gender
/mob/new_player/is_ready()
return ready && ..()
+1 -1
View File
@@ -40,7 +40,7 @@
/obj/machinery/power/treadmill/proc/throw_off(atom/movable/A)
// if 2fast, throw the person, otherwise they just slide off, if there's reasonable speed at all
if(speed)
if(speed && A.move_resist < INFINITY)
var/dist = max(throw_dist * speed / MAX_SPEED, 1)
A.throw_at(get_distant_turf(get_turf(src), reverse_direction(dir), dist), A.throw_range, A.throw_speed, src, 1)
+4 -3
View File
@@ -267,9 +267,10 @@
if("human")
new_mob = new /mob/living/carbon/human(M.loc)
var/mob/living/carbon/human/H = new_mob
var/datum/preferences/A = new() //Randomize appearance for the human
A.species = get_random_species(TRUE)
A.copy_to(new_mob)
var/datum/character_save/S = new //Randomize appearance for the human
S.species = get_random_species(TRUE)
S.randomise()
S.copy_to(new_mob)
randomize = H.dna.species.name
else
return
@@ -319,6 +319,19 @@
return
ui_interact(user)
/obj/machinery/chem_dispenser/AltClick(mob/user)
if(!is_drink || !Adjacent(user))
return
if(user.incapacitated())
to_chat(user, "<span class='warning'>You can't do that right now!</span>")
return
if(anchored)
to_chat(user, "<span class='warning'>[src] is anchored to the floor!</span>")
return
pixel_x = 0
pixel_y = 0
setDir(turn(dir, 90))
/obj/machinery/chem_dispenser/soda
icon_state = "soda_dispenser"
name = "soda fountain"
@@ -17,75 +17,76 @@
//IMPORTANT NOTE! A negative number is a multiplier, a positive number is a flat amount to add. 0 means equal to the amount of the original reagent
var/list/blend_items = list (
//Sheets
/obj/item/stack/sheet/mineral/plasma = list("plasma_dust" = 20),
/obj/item/stack/sheet/metal = list("iron" = 20),
/obj/item/stack/rods = list("iron" = 10),
/obj/item/stack/sheet/plasteel = list("iron" = 20, "plasma_dust" = 20),
/obj/item/stack/sheet/wood = list("carbon" = 20),
/obj/item/stack/sheet/glass = list("silicon" = 20),
/obj/item/stack/sheet/rglass = list("silicon" = 20, "iron" = 20),
/obj/item/stack/sheet/mineral/uranium = list("uranium" = 20),
/obj/item/stack/sheet/mineral/bananium = list("banana" = 20),
/obj/item/stack/sheet/mineral/tranquillite = list("nothing" = 20),
/obj/item/stack/sheet/mineral/silver = list("silver" = 20),
/obj/item/stack/sheet/mineral/gold = list("gold" = 20),
/obj/item/grown/nettle/basic = list("wasabi" = 0),
/obj/item/grown/nettle/death = list("facid" = 0, "sacid" = 0),
/obj/item/grown/novaflower = list("capsaicin" = 0, "condensedcapsaicin" = 0),
//Sheets
/obj/item/stack/sheet/mineral/plasma = list("plasma_dust" = 20),
/obj/item/stack/sheet/metal = list("iron" = 20),
/obj/item/stack/rods = list("iron" = 10),
/obj/item/stack/sheet/plasteel = list("iron" = 20, "plasma_dust" = 20),
/obj/item/stack/sheet/wood = list("carbon" = 20),
/obj/item/stack/sheet/glass = list("silicon" = 20),
/obj/item/stack/sheet/rglass = list("silicon" = 20, "iron" = 20),
/obj/item/stack/sheet/mineral/uranium = list("uranium" = 20),
/obj/item/stack/sheet/mineral/bananium = list("banana" = 20),
/obj/item/stack/sheet/mineral/tranquillite = list("nothing" = 20),
/obj/item/stack/sheet/mineral/silver = list("silver" = 20),
/obj/item/stack/sheet/mineral/gold = list("gold" = 20),
//Blender Stuff
/obj/item/reagent_containers/food/snacks/grown/tomato = list("ketchup" = 0),
/obj/item/reagent_containers/food/snacks/grown/wheat = list("flour" = -5),
/obj/item/reagent_containers/food/snacks/grown/oat = list("flour" = -5),
/obj/item/reagent_containers/food/snacks/grown/cherries = list("cherryjelly" = 0),
/obj/item/reagent_containers/food/snacks/grown/bluecherries = list("bluecherryjelly" = 0),
/obj/item/reagent_containers/food/snacks/egg = list("egg" = -5),
/obj/item/reagent_containers/food/snacks/grown/rice = list("rice" = -5),
/obj/item/grown/nettle/basic = list("wasabi" = 0),
/obj/item/grown/nettle/death = list("facid" = 0, "sacid" = 0),
/obj/item/grown/novaflower = list("capsaicin" = 0, "condensedcapsaicin" = 0),
//Grinder stuff, but only if dry
/obj/item/reagent_containers/food/snacks/grown/coffee/robusta = list("coffeepowder" = 0, "morphine" = 0),
/obj/item/reagent_containers/food/snacks/grown/coffee = list("coffeepowder" = 0),
/obj/item/reagent_containers/food/snacks/grown/tea/astra = list("teapowder" = 0, "salglu_solution" = 0),
/obj/item/reagent_containers/food/snacks/grown/tea = list("teapowder" = 0),
//Blender Stuff
/obj/item/reagent_containers/food/snacks/grown/tomato = list("ketchup" = 0),
/obj/item/reagent_containers/food/snacks/grown/wheat = list("flour" = -5),
/obj/item/reagent_containers/food/snacks/grown/oat = list("flour" = -5),
/obj/item/reagent_containers/food/snacks/grown/cherries = list("cherryjelly" = 0),
/obj/item/reagent_containers/food/snacks/grown/bluecherries = list("bluecherryjelly" = 0),
/obj/item/reagent_containers/food/snacks/egg = list("egg" = -5),
/obj/item/reagent_containers/food/snacks/grown/rice = list("rice" = -5),
//Grinder stuff, but only if dry
/obj/item/reagent_containers/food/snacks/grown/coffee/robusta = list("coffeepowder" = 0, "morphine" = 0),
/obj/item/reagent_containers/food/snacks/grown/coffee = list("coffeepowder" = 0),
/obj/item/reagent_containers/food/snacks/grown/tea/astra = list("teapowder" = 0, "salglu_solution" = 0),
/obj/item/reagent_containers/food/snacks/grown/tea = list("teapowder" = 0),
//All types that you can put into the grinder to transfer the reagents to the beaker. !Put all recipes above this.!
/obj/item/slime_extract = list(),
/obj/item/reagent_containers/food = list(),
/obj/item/reagent_containers/honeycomb = list()
//All types that you can put into the grinder to transfer the reagents to the beaker. !Put all recipes above this.!
/obj/item/slime_extract = list(),
/obj/item/reagent_containers/food = list(),
/obj/item/reagent_containers/honeycomb = list()
)
var/list/juice_items = list (
//Juicer Stuff
/obj/item/reagent_containers/food/snacks/grown/soybeans = list("soymilk" = 0),
/obj/item/reagent_containers/food/snacks/grown/corn = list("corn_starch" = 0),
/obj/item/reagent_containers/food/snacks/grown/tomato = list("tomatojuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/carrot = list("carrotjuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/berries = list("berryjuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/banana = list("banana" = 0),
/obj/item/reagent_containers/food/snacks/grown/potato = list("potato" = 0),
/obj/item/reagent_containers/food/snacks/grown/citrus/lemon = list("lemonjuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/citrus/orange = list("orangejuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/citrus/lime = list("limejuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/watermelon = list("watermelonjuice" = 0),
/obj/item/reagent_containers/food/snacks/watermelonslice = list("watermelonjuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/berries/poison = list("poisonberryjuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/pumpkin = list("pumpkinjuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/blumpkin = list("blumpkinjuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/apple = list("applejuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/grapes = list("grapejuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/grapes/green = list("grapejuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/pineapple = list("pineapplejuice" = 0)
//Juicer Stuff
/obj/item/reagent_containers/food/snacks/grown/soybeans = list("soymilk" = 0),
/obj/item/reagent_containers/food/snacks/grown/corn = list("corn_starch" = 0),
/obj/item/reagent_containers/food/snacks/grown/tomato = list("tomatojuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/carrot = list("carrotjuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/berries = list("berryjuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/banana = list("banana" = 0),
/obj/item/reagent_containers/food/snacks/grown/potato = list("potato" = 0),
/obj/item/reagent_containers/food/snacks/grown/citrus/lemon = list("lemonjuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/citrus/orange = list("orangejuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/citrus/lime = list("limejuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/watermelon = list("watermelonjuice" = 0),
/obj/item/reagent_containers/food/snacks/watermelonslice = list("watermelonjuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/berries/poison = list("poisonberryjuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/pumpkin = list("pumpkinjuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/blumpkin = list("blumpkinjuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/apple = list("applejuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/grapes = list("grapejuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/grapes/green = list("grapejuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/pineapple = list("pineapplejuice" = 0)
)
var/list/dried_items = list(
//Grinder stuff, but only if dry,
/obj/item/reagent_containers/food/snacks/grown/coffee/robusta = list("coffeepowder" = 0, "morphine" = 0),
/obj/item/reagent_containers/food/snacks/grown/coffee = list("coffeepowder" = 0),
/obj/item/reagent_containers/food/snacks/grown/tea/astra = list("teapowder" = 0, "salglu_solution" = 0),
/obj/item/reagent_containers/food/snacks/grown/tea = list("teapowder" = 0)
//Grinder stuff, but only if dry,
/obj/item/reagent_containers/food/snacks/grown/coffee/robusta = list("coffeepowder" = 0, "morphine" = 0),
/obj/item/reagent_containers/food/snacks/grown/coffee = list("coffeepowder" = 0),
/obj/item/reagent_containers/food/snacks/grown/tea/astra = list("teapowder" = 0, "salglu_solution" = 0),
/obj/item/reagent_containers/food/snacks/grown/tea = list("teapowder" = 0)
)
var/list/holdingitems = list()
@@ -231,59 +232,59 @@
/obj/machinery/reagentgrinder/attack_ai(mob/user)
return FALSE
return FALSE
/obj/machinery/reagentgrinder/attack_hand(mob/user)
user.set_machine(src)
interact(user)
user.set_machine(src)
interact(user)
/obj/machinery/reagentgrinder/interact(mob/user) // The microwave Menu
var/is_chamber_empty = 0
var/is_beaker_ready = 0
var/processing_chamber = ""
var/beaker_contents = ""
var/dat = ""
var/is_chamber_empty = 0
var/is_beaker_ready = 0
var/processing_chamber = ""
var/beaker_contents = ""
var/dat = ""
if(!operating)
for (var/obj/item/O in holdingitems)
processing_chamber += "\A [html_encode(O.name)]<BR>"
if(!operating)
for (var/obj/item/O in holdingitems)
processing_chamber += "\A [html_encode(O.name)]<BR>"
if (!processing_chamber)
is_chamber_empty = 1
processing_chamber = "Nothing."
if (!beaker)
beaker_contents = "<B>No beaker attached.</B><br>"
else
is_beaker_ready = 1
beaker_contents = "<B>The beaker contains:</B><br>"
var/anything = 0
for(var/datum/reagent/R in beaker.reagents.reagent_list)
anything = 1
beaker_contents += "[R.volume] - [R.name]<br>"
if(!anything)
beaker_contents += "Nothing<br>"
dat = {"
<b>Processing chamber contains:</b><br>
[processing_chamber]<br>
[beaker_contents]<hr>
"}
if (is_beaker_ready && !is_chamber_empty && !(stat & (NOPOWER|BROKEN)))
dat += "<A href='?src=[src.UID()];action=grind'>Grind the reagents</a><BR>"
dat += "<A href='?src=[src.UID()];action=juice'>Juice the reagents</a><BR><BR>"
if(holdingitems && holdingitems.len > 0)
dat += "<A href='?src=[src.UID()];action=eject'>Eject the reagents</a><BR>"
if (beaker)
dat += "<A href='?src=[src.UID()];action=detach'>Detach the beaker</a><BR>"
if(!processing_chamber)
is_chamber_empty = 1
processing_chamber = "Nothing."
if(!beaker)
beaker_contents = "<B>No beaker attached.</B><br>"
else
dat += "Please wait..."
is_beaker_ready = 1
beaker_contents = "<B>The beaker contains:</B><br>"
var/anything = 0
for(var/datum/reagent/R in beaker.reagents.reagent_list)
anything = 1
beaker_contents += "[R.volume] - [R.name]<br>"
if(!anything)
beaker_contents += "Nothing<br>"
var/datum/browser/popup = new(user, "reagentgrinder", "All-In-One Grinder")
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open(1)
return
dat = {"
<b>Processing chamber contains:</b><br>
[processing_chamber]<br>
[beaker_contents]<hr>
"}
if(is_beaker_ready && !is_chamber_empty && !(stat & (NOPOWER|BROKEN)))
dat += "<A href='?src=[src.UID()];action=grind'>Grind the reagents</a><BR>"
dat += "<A href='?src=[src.UID()];action=juice'>Juice the reagents</a><BR><BR>"
if(holdingitems && holdingitems.len > 0)
dat += "<A href='?src=[src.UID()];action=eject'>Eject the reagents</a><BR>"
if(beaker)
dat += "<A href='?src=[src.UID()];action=detach'>Detach the beaker</a><BR>"
else
dat += "Please wait..."
var/datum/browser/popup = new(user, "reagentgrinder", "All-In-One Grinder")
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open(1)
return
/obj/machinery/reagentgrinder/Topic(href, href_list)
if(..())
@@ -293,223 +294,228 @@
updateUsrDialog()
return
switch(href_list["action"])
if ("grind")
if("grind")
grind()
if("juice")
juice()
if("eject")
eject()
if ("detach")
if("detach")
detach()
/obj/machinery/reagentgrinder/proc/detach()
if (usr.stat != 0)
return
if (!beaker)
return
beaker.loc = src.loc
beaker = null
update_icon()
updateUsrDialog()
if(usr.stat != 0)
return
if(!beaker)
return
beaker.loc = src.loc
beaker = null
update_icon()
updateUsrDialog()
/obj/machinery/reagentgrinder/proc/eject()
if(usr.stat != 0)
return
if(holdingitems && holdingitems.len == 0)
return
if (usr.stat != 0)
return
if (holdingitems && holdingitems.len == 0)
return
for(var/obj/item/O in holdingitems)
O.loc = src.loc
holdingitems -= O
holdingitems = list()
updateUsrDialog()
for(var/obj/item/O in holdingitems)
O.loc = src.loc
holdingitems -= O
holdingitems = list()
updateUsrDialog()
/obj/machinery/reagentgrinder/proc/is_allowed(obj/item/reagent_containers/O)
for (var/i in blend_items)
if(istype(O, i))
return TRUE
return FALSE
for (var/i in blend_items)
if(istype(O, i))
return TRUE
return FALSE
/obj/machinery/reagentgrinder/proc/get_allowed_by_id(obj/item/O)
for (var/i in blend_items)
if (istype(O, i))
return blend_items[i]
for (var/i in blend_items)
if(istype(O, i))
return blend_items[i]
/obj/machinery/reagentgrinder/proc/get_allowed_snack_by_id(obj/item/reagent_containers/food/snacks/O)
for(var/i in blend_items)
if(istype(O, i))
return blend_items[i]
for(var/i in blend_items)
if(istype(O, i))
return blend_items[i]
/obj/machinery/reagentgrinder/proc/get_allowed_juice_by_id(obj/item/reagent_containers/food/snacks/O)
for(var/i in juice_items)
if(istype(O, i))
return juice_items[i]
for(var/i in juice_items)
if(istype(O, i))
return juice_items[i]
/obj/machinery/reagentgrinder/proc/get_grownweapon_amount(obj/item/grown/O)
if (!istype(O) || !O.seed)
return 5
else if (O.seed.potency == -1)
return 5
else
return round(O.seed.potency)
if(!istype(O) || !O.seed)
return 5
else if(O.seed.potency == -1)
return 5
else
return round(O.seed.potency)
/obj/machinery/reagentgrinder/proc/get_juice_amount(obj/item/reagent_containers/food/snacks/grown/O)
if (!istype(O) || !O.seed)
return 5
else if (O.seed.potency == -1)
return 5
else
return round(5*sqrt(O.seed.potency))
if(!istype(O) || !O.seed)
return 5
else if(O.seed.potency == -1)
return 5
else
return round(5*sqrt(O.seed.potency))
/obj/machinery/reagentgrinder/proc/remove_object(obj/item/O)
holdingitems -= O
qdel(O)
holdingitems -= O
qdel(O)
/obj/machinery/reagentgrinder/proc/juice()
power_change()
if(stat & (NOPOWER|BROKEN))
return
if (!beaker || (beaker && beaker.reagents.total_volume >= beaker.reagents.maximum_volume))
return
playsound(src.loc, 'sound/machines/juicer.ogg', 20, 1)
var/offset = prob(50) ? -2 : 2
animate(src, pixel_x = pixel_x + offset, time = 0.2, loop = 250) //start shaking
operating = 1
power_change()
if(stat & (NOPOWER|BROKEN))
return
if(!beaker || (beaker && beaker.reagents.total_volume >= beaker.reagents.maximum_volume))
return
playsound(src.loc, 'sound/machines/juicer.ogg', 20, 1)
var/offset = prob(50) ? -2 : 2
animate(src, pixel_x = pixel_x + offset, time = 0.2, loop = 250) //start shaking
operating = 1
updateUsrDialog()
spawn(50)
pixel_x = initial(pixel_x) //return to its spot after shaking
operating = 0
updateUsrDialog()
spawn(50)
pixel_x = initial(pixel_x) //return to its spot after shaking
operating = 0
updateUsrDialog()
//Snacks
for (var/obj/item/reagent_containers/food/snacks/O in holdingitems)
if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
break
//Snacks
for (var/obj/item/reagent_containers/food/snacks/O in holdingitems)
if(beaker.reagents.holder_full())
break
var/allowed = get_allowed_juice_by_id(O)
if(isnull(allowed))
break
var/allowed = get_allowed_juice_by_id(O)
if(isnull(allowed))
break
for (var/r_id in allowed)
for (var/r_id in allowed)
var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
var/amount = get_juice_amount(O)
var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
var/amount = get_juice_amount(O)
beaker.reagents.add_reagent(r_id, min(amount*efficiency, space))
beaker.reagents.add_reagent(r_id, min(amount * efficiency, space))
if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
break
if(beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
break
remove_object(O)
remove_object(O)
/obj/machinery/reagentgrinder/proc/grind()
power_change()
if(stat & (NOPOWER|BROKEN))
return
if (!beaker || (beaker && beaker.reagents.total_volume >= beaker.reagents.maximum_volume))
return
playsound(src.loc, 'sound/machines/blender.ogg', 50, 1)
var/offset = prob(50) ? -2 : 2
animate(src, pixel_x = pixel_x + offset, time = 0.2, loop = 250) //start shaking
operating = 1
power_change()
if(stat & (NOPOWER|BROKEN))
return
if(!beaker || (beaker && beaker.reagents.holder_full()))
return
playsound(src.loc, 'sound/machines/blender.ogg', 50, 1)
var/offset = prob(50) ? -2 : 2
animate(src, pixel_x = pixel_x + offset, time = 0.2, loop = 250) //start shaking
operating = 1
updateUsrDialog()
spawn(60)
pixel_x = initial(pixel_x) //return to its spot after shaking
operating = 0
updateUsrDialog()
spawn(60)
pixel_x = initial(pixel_x) //return to its spot after shaking
operating = 0
updateUsrDialog()
//Snacks and Plants
for (var/obj/item/reagent_containers/food/snacks/O in holdingitems)
if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
break
//Snacks and Plants
for (var/obj/item/reagent_containers/food/snacks/O in holdingitems)
if(beaker.reagents.holder_full())
break
var/allowed = get_allowed_snack_by_id(O)
if(isnull(allowed))
break
var/allowed = get_allowed_snack_by_id(O)
if(isnull(allowed))
break
for (var/r_id in allowed)
for (var/r_id in allowed)
var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
var/amount = allowed[r_id]
if(amount <= 0)
if(amount == 0)
if (O.reagents != null && O.reagents.has_reagent("nutriment"))
beaker.reagents.add_reagent(r_id, min(O.reagents.get_reagent_amount("nutriment")*efficiency, space))
O.reagents.remove_reagent("nutriment", min(O.reagents.get_reagent_amount("nutriment"), space))
if (O.reagents != null && O.reagents.has_reagent("plantmatter"))
beaker.reagents.add_reagent(r_id, min(O.reagents.get_reagent_amount("plantmatter")*efficiency, space))
O.reagents.remove_reagent("plantmatter", min(O.reagents.get_reagent_amount("plantmatter"), space))
else
if (O.reagents != null && O.reagents.has_reagent("nutriment"))
beaker.reagents.add_reagent(r_id, min(round(O.reagents.get_reagent_amount("nutriment")*abs(amount)*efficiency), space))
O.reagents.remove_reagent("nutriment", min(O.reagents.get_reagent_amount("nutriment"), space))
if (O.reagents != null && O.reagents.has_reagent("plantmatter"))
beaker.reagents.add_reagent(r_id, min(round(O.reagents.get_reagent_amount("plantmatter")*abs(amount)*efficiency), space))
O.reagents.remove_reagent("plantmatter", min(O.reagents.get_reagent_amount("plantmatter"), space))
var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
var/amount = allowed[r_id]
if(amount <= 0)
if(amount == 0)
if(O.reagents != null && O.reagents.has_reagent("nutriment"))
beaker.reagents.add_reagent(r_id, min(O.reagents.get_reagent_amount("nutriment") * efficiency, space))
O.reagents.remove_reagent("nutriment", min(O.reagents.get_reagent_amount("nutriment"), space))
if(O.reagents != null && O.reagents.has_reagent("plantmatter"))
beaker.reagents.add_reagent(r_id, min(O.reagents.get_reagent_amount("plantmatter") * efficiency, space))
O.reagents.remove_reagent("plantmatter", min(O.reagents.get_reagent_amount("plantmatter"), space))
else
if(O.reagents != null && O.reagents.has_reagent("nutriment"))
beaker.reagents.add_reagent(r_id, min(round(O.reagents.get_reagent_amount("nutriment") * abs(amount) * efficiency), space))
O.reagents.remove_reagent("nutriment", min(O.reagents.get_reagent_amount("nutriment"), space))
if(O.reagents != null && O.reagents.has_reagent("plantmatter"))
beaker.reagents.add_reagent(r_id, min(round(O.reagents.get_reagent_amount("plantmatter") * abs(amount) * efficiency), space))
O.reagents.remove_reagent("plantmatter", min(O.reagents.get_reagent_amount("plantmatter"), space))
else
O.reagents.trans_id_to(beaker, r_id, min(amount, space))
else
O.reagents.trans_id_to(beaker, r_id, min(amount, space))
if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
break
if(beaker.reagents.holder_full())
break
if(O.reagents.reagent_list.len == 0)
remove_object(O)
if(O.reagents.reagent_list.len == 0)
remove_object(O)
//Sheets
for (var/obj/item/stack/sheet/O in holdingitems)
var/allowed = get_allowed_by_id(O)
if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
break
for(var/i = 1; i <= round(O.amount, 1); i++)
for (var/r_id in allowed)
var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
var/amount = allowed[r_id]
beaker.reagents.add_reagent(r_id,min(amount*efficiency, space))
if (space < amount)
break
if (i == round(O.amount, 1))
remove_object(O)
break
//Plants
for (var/obj/item/grown/O in holdingitems)
if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
break
var/allowed = get_allowed_by_id(O)
for (var/r_id in allowed)
var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
var/amount = allowed[r_id]
if (amount == 0)
if (O.reagents != null && O.reagents.has_reagent(r_id))
beaker.reagents.add_reagent(r_id,min(O.reagents.get_reagent_amount(r_id)*efficiency, space))
else
beaker.reagents.add_reagent(r_id,min(amount*efficiency, space))
//Sheets and rods(!)
for (var/obj/item/stack/O in holdingitems)
if(beaker.reagents.holder_full())
break
if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
break
var/allowed = get_allowed_by_id(O)
if(isnull(allowed))
break
var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
while(O.amount) //Grind until there's no more reagents
if(!space) //if no free space - exit
break
O.amount -= 1 //remove one from stack
for (var/r_id in allowed)
var/spaceused = min(allowed[r_id] * efficiency, space)
space -= spaceused
beaker.reagents.add_reagent(r_id, spaceused)
if(O.amount < 1) //if leftover small - destroy
remove_object(O)
break
//Slime Extractis
for (var/obj/item/slime_extract/O in holdingitems)
if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
break
var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
if (O.reagents != null)
var/amount = O.reagents.total_volume
O.reagents.trans_to(beaker, min(amount, space))
if (O.Uses > 0)
beaker.reagents.add_reagent("slimejelly",min(20*efficiency, space))
remove_object(O)
//Plants
for (var/obj/item/grown/O in holdingitems)
if(beaker.reagents.holder_full())
break
var/allowed = get_allowed_by_id(O)
for (var/r_id in allowed)
var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
var/amount = allowed[r_id]
if(amount == 0)
if(O.reagents != null && O.reagents.has_reagent(r_id))
beaker.reagents.add_reagent(r_id,min(O.reagents.get_reagent_amount(r_id) * efficiency, space))
else
beaker.reagents.add_reagent(r_id,min(amount * efficiency, space))
//Everything else - Transfers reagents from it into beaker
for (var/obj/item/reagent_containers/O in holdingitems)
if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
break
var/amount = O.reagents.total_volume
O.reagents.trans_to(beaker, amount)
if(!O.reagents.total_volume)
remove_object(O)
if(beaker.reagents.holder_full())
break
remove_object(O)
//Slime Extractis
for (var/obj/item/slime_extract/O in holdingitems)
if(beaker.reagents.holder_full())
break
var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
if(O.reagents != null)
var/amount = O.reagents.total_volume
O.reagents.trans_to(beaker, min(amount, space))
if(O.Uses > 0)
beaker.reagents.add_reagent("slimejelly",min(20 * efficiency, space))
remove_object(O)
//Everything else - Transfers reagents from it into beaker
for (var/obj/item/reagent_containers/O in holdingitems)
if(beaker.reagents.holder_full())
break
var/amount = O.reagents.total_volume
O.reagents.trans_to(beaker, amount)
if(!O.reagents.total_volume)
remove_object(O)
@@ -568,21 +568,21 @@
/datum/reagent/fliptonium/on_mob_life(mob/living/M)
var/update_flags = STATUS_UPDATE_NONE
if(current_cycle == 5)
M.SpinAnimation(speed = 11, loops = -1)
M.SpinAnimation(speed = 11, loops = -1, parallel = FALSE)
if(current_cycle == 10)
M.SpinAnimation(speed = 10, loops = -1)
M.SpinAnimation(speed = 10, loops = -1, parallel = FALSE)
if(current_cycle == 15)
M.SpinAnimation(speed = 9, loops = -1)
M.SpinAnimation(speed = 9, loops = -1, parallel = FALSE)
if(current_cycle == 20)
M.SpinAnimation(speed = 8, loops = -1)
M.SpinAnimation(speed = 8, loops = -1, parallel = FALSE)
if(current_cycle == 25)
M.SpinAnimation(speed = 7, loops = -1)
M.SpinAnimation(speed = 7, loops = -1, parallel = FALSE)
if(current_cycle == 30)
M.SpinAnimation(speed = 6, loops = -1)
M.SpinAnimation(speed = 6, loops = -1, parallel = FALSE)
if(current_cycle == 40)
M.SpinAnimation(speed = 5, loops = -1)
M.SpinAnimation(speed = 5, loops = -1, parallel = FALSE)
if(current_cycle == 50)
M.SpinAnimation(speed = 4, loops = -1)
M.SpinAnimation(speed = 4, loops = -1, parallel = FALSE)
M.AdjustDrowsy(-6)
update_flags |= M.AdjustParalysis(-1.5, FALSE)
@@ -594,11 +594,11 @@
/datum/reagent/fliptonium/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume)
if(method == REAGENT_INGEST || method == REAGENT_TOUCH)
M.SpinAnimation(speed = 12, loops = -1)
M.SpinAnimation(speed = 12, loops = -1, parallel = FALSE)
..()
/datum/reagent/fliptonium/on_mob_delete(mob/living/M)
M.SpinAnimation(speed = 12, loops = -1)
M.SpinAnimation(speed = 12, loops = -1, parallel = FALSE)
/datum/reagent/fliptonium/overdose_process(mob/living/M, severity)
var/list/overdose_info = ..()
@@ -155,7 +155,7 @@
current_potion = O
to_chat(user, "<span class='notice'>You load [O] in the console's potion slot[replaced ? ", replacing the one that was there before" : ""].</span>")
return
else if(istype(O, /obj/item/storage/bag))
else if(istype(O, /obj/item/storage/bag) || istype(O, /obj/item/storage/box))
var/obj/item/storage/P = O
var/loaded = 0
for(var/obj/item/reagent_containers/food/snacks/monkeycube/MC in P.contents)
@@ -191,7 +191,10 @@
var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control
var/obj/machinery/computer/camera_advanced/xenobio/X = target
if(GLOB.cameranet.checkTurfVis(remote_eye.loc))
if(iswallturf(remote_eye.loc))
to_chat(owner, "You can't place slime here.")
return
else if(GLOB.cameranet.checkTurfVis(remote_eye.loc))
for(var/mob/living/simple_animal/slime/S in X.stored_slimes)
S.forceMove(remote_eye.loc)
S.visible_message("[S] warps in!")
@@ -238,6 +241,12 @@
if(LAZYLEN(SSmobs.cubemonkeys) >= GLOB.configuration.general.monkey_cube_cap)
to_chat(owner, "<span class='warning'>Bluespace harmonics prevent the spawning of more than [GLOB.configuration.general.monkey_cube_cap] monkeys on the station at one time!</span>")
return
else if(iswallturf(remote_eye.loc))
to_chat(owner, "You can't place monkey here.")
return
else if(!X.monkeys)
to_chat(owner, "[X] doesn't have monkeys.")
return
else if(X.monkeys >= 1)
var/mob/living/carbon/human/monkey/food = new /mob/living/carbon/human/monkey(remote_eye.loc)
SSmobs.cubemonkeys += food
@@ -323,6 +332,8 @@
to_chat(owner, "Ctrl-click a slime to scan it.")
to_chat(owner, "Alt-click a slime to feed it a potion.")
to_chat(owner, "Ctrl-click or a dead monkey to recycle it, or the floor to place a new monkey.")
var/obj/machinery/computer/camera_advanced/xenobio/X = owner.machine
to_chat(owner, "[X] now has [X.monkeys] monkeys left.")
//
// Alternate clicks for slime, monkey and open turf if using a xenobio console
@@ -415,7 +426,10 @@
var/mob/camera/aiEye/remote/xenobio/E = C.remote_control
var/obj/machinery/computer/camera_advanced/xenobio/X = E.origin
var/area/turfarea = get_area(T)
if(turfarea.name == E.allowed_area || turfarea.xenobiology_compatible)
if(iswallturf(T))
to_chat(user, "You can't place slime here.")
return
else if(turfarea.name == E.allowed_area || turfarea.xenobiology_compatible)
for(var/mob/living/simple_animal/slime/S in X.stored_slimes)
S.forceMove(T)
S.visible_message("<span class='notice'>[S] warps in!</span>")
@@ -430,8 +444,10 @@
var/mob/camera/aiEye/remote/xenobio/E = C.remote_control
var/obj/machinery/computer/camera_advanced/xenobio/X = E.origin
var/area/turfarea = get_area(T)
if(turfarea.name == E.allowed_area || turfarea.xenobiology_compatible)
if(iswallturf(T))
to_chat(user, "You can't place monkey here.")
return
else if(turfarea.name == E.allowed_area || turfarea.xenobiology_compatible)
if(X.monkeys >= 1)
var/mob/living/carbon/human/monkey/food = new /mob/living/carbon/human/monkey(T)
food.LAssailant = C
+12 -6
View File
@@ -521,6 +521,14 @@
visible_message("<b>[src]</b>'s monitor flashes, \"[world.time - reqtime] seconds remaining until another requisition form may be printed.\"")
return
var/datum/supply_packs/P = locateUID(params["crate"])
if(!istype(P))
return
if(P.times_ordered >= P.order_limit && P.order_limit != -1) //If the crate has reached the limit, do not allow it to be ordered.
to_chat(usr, "<span class='warning'>[P.name] is out of stock, and can no longer be ordered.</span>")
return
var/amount = 1
if(params["multiple"] == "1") // 1 is a string here. DO NOT MAKE THIS A BOOLEAN YOU DORK
var/num_input = input(usr, "Amount", "How many crates? (20 Max)") as null|num
@@ -528,11 +536,6 @@
return
amount = clamp(round(num_input), 1, 20)
var/datum/supply_packs/P = locateUID(params["crate"])
if(!istype(P))
return
var/timeout = world.time + 600 // If you dont type the reason within a minute, theres bigger problems here
var/reason = input(usr, "Reason", "Why do you require this item?","") as null|text
if(world.time > timeout || !reason || (!is_public && !is_authorized(usr)) || ..())
@@ -573,10 +576,13 @@
if(SO.ordernum == ordernum)
O = SO
P = O.object
if(SSshuttle.points >= P.cost)
if(P.times_ordered >= P.order_limit && P.order_limit != -1) //If this order would put it over the limit, deny it
to_chat(usr, "<span class='warning'>[P.name] is out of stock, and can no longer be ordered.</span>")
else if(SSshuttle.points >= P.cost)
SSshuttle.requestlist.Cut(i,i+1)
SSshuttle.points -= P.cost
SSshuttle.shoppinglist += O
P.times_ordered += 1
investigate_log("[key_name(usr)] has authorized an order for [P.name]. Remaining points: [SSshuttle.points].", "cargo")
else
to_chat(usr, "<span class='warning'>There are insufficient supply points for this request.</span>")
+3
View File
@@ -719,6 +719,9 @@ shutdown_on_reboot = false
#_2fa_auth_host = "http://127.0.0.1:8080"
# List of IP addresses to be ignored by the world/Topic rate limiting. Useful if you have other services
topic_ip_ratelimit_bypass = ["127.0.0.1"]
# Turn this to true if you are running a production server
is_production = false
################################################################
Binary file not shown.

Before

Width:  |  Height:  |  Size: 901 KiB

After

Width:  |  Height:  |  Size: 900 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Some files were not shown because too many files have changed in this diff Show More