diff --git a/ByondPOST.dll b/ByondPOST.dll
index b33f70b1ec8..93dff79d7f6 100644
Binary files a/ByondPOST.dll and b/ByondPOST.dll differ
diff --git a/SQL/Aurora_SQL_Schema.sql b/SQL/Aurora_SQL_Schema.sql
index 06e20ef8c48..f2dd7018bf9 100644
--- a/SQL/Aurora_SQL_Schema.sql
+++ b/SQL/Aurora_SQL_Schema.sql
@@ -173,10 +173,10 @@ CREATE TABLE `ss13_characters_flavour` (
`robot_standard` text,
`robot_engineering` text,
`robot_construction` text,
- `robot_surgeon` text,
- `robot_crisis` text,
+ `robot_medical` text,
+ `robot_rescue` text,
`robot_miner` text,
- `robot_janitor` text,
+ `robot_custodial` text,
`robot_service` text,
`robot_clerical` text,
`robot_security` text,
diff --git a/baystation12.dme b/baystation12.dme
index da106f53395..f053c299991 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -136,7 +136,9 @@
#include "code\controllers\Processes\inactivity.dm"
#include "code\controllers\Processes\machinery.dm"
#include "code\controllers\Processes\mob.dm"
+#include "code\controllers\Processes\modifier.dm"
#include "code\controllers\Processes\nanoui.dm"
+#include "code\controllers\Processes\night_lighting.dm"
#include "code\controllers\Processes\obj.dm"
#include "code\controllers\Processes\scheduler.dm"
#include "code\controllers\Processes\Shuttle.dm"
@@ -555,6 +557,8 @@
#include "code\game\mecha\working\hoverpod.dm"
#include "code\game\mecha\working\ripley.dm"
#include "code\game\mecha\working\working.dm"
+#include "code\game\modifiers\modifiers.dm"
+#include "code\game\modifiers\modifiers_chem.dm"
#include "code\game\objects\buckling.dm"
#include "code\game\objects\empulse.dm"
#include "code\game\objects\explosion.dm"
@@ -991,6 +995,7 @@
#include "code\modules\awaymissions\trigger.dm"
#include "code\modules\awaymissions\zlevel.dm"
#include "code\modules\blob\blob.dm"
+#include "code\modules\cargo\randomstock.dm"
#include "code\modules\cciaa\cciaa.dm"
#include "code\modules\cciaa\cciaa_items.dm"
#include "code\modules\client\client defines.dm"
@@ -1157,6 +1162,7 @@
#include "code\modules\events\spacevine.dm"
#include "code\modules\events\spider_infestation.dm"
#include "code\modules\events\spontaneous_appendicitis.dm"
+#include "code\modules\events\supply_drop.dm"
#include "code\modules\events\vent_clog.dm"
#include "code\modules\events\viral_infection.dm"
#include "code\modules\events\wallrot.dm"
@@ -1688,6 +1694,7 @@
#include "code\modules\projectiles\guns\projectile\revolver.dm"
#include "code\modules\projectiles\guns\projectile\shotgun.dm"
#include "code\modules\projectiles\guns\projectile\sniper.dm"
+#include "code\modules\projectiles\guns\projectile\svd.dm"
#include "code\modules\projectiles\projectile\animate.dm"
#include "code\modules\projectiles\projectile\beams.dm"
#include "code\modules\projectiles\projectile\bullets.dm"
diff --git a/code/_helpers/icons.dm b/code/_helpers/icons.dm
index bac27c1f5a9..207a0640bbc 100644
--- a/code/_helpers/icons.dm
+++ b/code/_helpers/icons.dm
@@ -863,6 +863,7 @@ proc/sort_atoms_by_layer(var/list/atoms)
result.Swap(i, gap + i)
swapped = 1
return result
+
/*
generate_image function generates image of specified range and location
arguments tx, ty, tz are target coordinates (requred), range defines render distance to opposite corner (requred)
@@ -913,3 +914,15 @@ proc/generate_image(var/tx as num, var/ty as num, var/tz as num, var/range as nu
return cap
+proc/percentage_to_colour(var/P)
+ //Takes a value between 0-1
+ //Returns a colour - pure green if 1, pure red if 0
+ //Inbetween values will gradiant through green, yellow, orange, red
+
+
+ var/green = min(1, P*2)*255
+ var/red = 255 - (min(1, (P-0.5)*2)*255)
+ //var/green = (max(0, P-0.5)*2)*255
+ //var/red = 255 - (min(1, P*2)*255)
+
+ return rgb(red,green,0)
diff --git a/code/_helpers/lists.dm b/code/_helpers/lists.dm
index d1af8afc932..1909075ba69 100644
--- a/code/_helpers/lists.dm
+++ b/code/_helpers/lists.dm
@@ -71,6 +71,32 @@ proc/isemptylist(list/list)
return 1
return 0
+/proc/is_type_in_oview(var/type, var/dist = 0, var/center = src)
+ if (!ispath(type))
+ CRASH("Not a valid type in 'is_type_in_oview()'")
+ if (!isnum(dist))
+ CRASH("Not a valid dist in 'is_type_in_oview()'")
+ if (!isloc(center))
+ CRASH("Not a valid center in 'is_type_in_oview()'")
+ var/list/atoms = oview(dist, center)
+ for (var/A in atoms)
+ if (istype(A, type))
+ return 1
+ return 0
+
+/proc/is_type_in_view(var/type, var/dist = 0, var/center = src)
+ if (!ispath(type))
+ CRASH("Not a valid type in 'is_type_in_view()'")
+ if (!isnum(dist))
+ CRASH("Not a valid dist in 'is_type_in_view()'")
+ if (!isloc(center))
+ CRASH("Not a valid center in 'is_type_in_view()'")
+ var/list/atoms = view(dist, center)
+ for (var/A in atoms)
+ if (istype(A, type))
+ return 1
+ return 0
+
/proc/instances_of_type_in_list(var/atom/A, var/list/L)
var/instances = 0
for(var/type in L)
@@ -122,23 +148,29 @@ proc/listclearnulls(list/list)
result = first ^ second
return result
-//Pretends to pick an element based on its weight but really just seems to pick a random element.
+
+//Picks a random element by weight from a list. The list must be correctly constructed in this format:
+//mylist[myelement1] = myweight1
+//mylist[myelement2] = myweight2
+//The proc will return the element index, and not the weight.
/proc/pickweight(list/L)
var/total = 0
var/item
for (item in L)
- if (!L[item])
+ if (isnull(L[item]))
+ //Change by nanako, a default weight will no longer overwrite an explicitly set weight of 0
+ //It will only use a default if no weight is defined
L[item] = 1
total += L[item]
-
- total = rand(1, total)
+ total = rand() * total//Fix by nanako, allows it to handle noninteger weights
for (item in L)
- total -=L [item]
+ total -= L[item]
if (total <= 0)
return item
return null
+
//Pick a random element from the list and remove it from the list.
/proc/pick_n_take(list/listfrom)
if (listfrom.len > 0)
diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm
index b2176cdd599..50de55b528e 100644
--- a/code/_helpers/time.dm
+++ b/code/_helpers/time.dm
@@ -10,6 +10,11 @@ proc/worldtime2text(time = world.time, timeshift = 1)
if(!roundstart_hour) roundstart_hour = pick(2,7,12,17)
return timeshift ? time2text(time+(36000*roundstart_hour), "hh:mm") : time2text(time, "hh:mm")
+proc/worldtime2ticks(time = world.time)
+ if(!roundstart_hour)
+ worldtime2text()
+ return ((roundstart_hour * 60 MINUTES) + time) % TICKS_IN_DAY
+
proc/worlddate2text()
return num2text(game_year) + "-" + time2text(world.timeofday, "MM-DD")
diff --git a/code/_helpers/turfs.dm b/code/_helpers/turfs.dm
index 5315695fde4..2ac174f09f3 100644
--- a/code/_helpers/turfs.dm
+++ b/code/_helpers/turfs.dm
@@ -25,6 +25,30 @@
return 0
return 1
+// This proc will check if a neighboring tile in the stated direction "dir" is dense or not
+// Will return 1 if it is dense and zero if not
+/proc/check_neighbor_density(turf/T, var/dir)
+ if (!T.loc)
+ CRASH("The Turf has no location!")
+ switch (dir)
+ if (NORTH)
+ return !turf_clear(get_turf(locate(T.x, T.y+1, T.z)))
+ if (NORTHEAST)
+ return !turf_clear(get_turf(locate(T.x+1, T.y+1, T.z)))
+ if (EAST)
+ return !turf_clear(get_turf(locate(T.x+1, T.y, T.z)))
+ if (SOUTHEAST)
+ return !turf_clear(get_turf(locate(T.x+1, T.y-1, T.z)))
+ if (SOUTH)
+ return !turf_clear(get_turf(locate(T.x, T.y-1, T.z)))
+ if (SOUTHWEST)
+ return !turf_clear(get_turf(locate(T.x-1, T.y-1, T.z)))
+ if (WEST)
+ return !turf_clear(get_turf(locate(T.x-1, T.y, T.z)))
+ if (NORTHWEST)
+ return !turf_clear(get_turf(locate(T.x-1, T.y+1, T.z)))
+ else return
+
// Picks a turf without a mob from the given list of turfs, if one exists.
// If no such turf exists, picks any random turf from the given list of turfs.
/proc/pick_mobless_turf_if_exists(var/list/start_turfs)
diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm
index c9ae70686ed..06ba7ec55a9 100644
--- a/code/_helpers/unsorted.dm
+++ b/code/_helpers/unsorted.dm
@@ -1394,3 +1394,23 @@ var/mob/dview/dview_mob = new
// call to generate a stack trace and print to runtime logs
/proc/crash_with(msg)
CRASH(msg)
+
+/atom/proc/find_up_hierarchy(var/atom/target)
+ //This function will recurse up the hierarchy containing src, in search of the target
+ //It will stop when it reaches an area, as areas have no loc
+ var/x = 0//As a safety, we'll crawl up a maximum of ten layers
+ var/atom/a = src
+ while (x < 10)
+ x++
+ if (isnull(a))
+ return 0
+
+ if (a == target)//we found it!
+ return 1
+
+ if (istype(a, /area))
+ return 0//Can't recurse any higher than this.
+
+ a = a.loc
+
+ return 0//If we get here, we must be buried many layers deep in nested containers. Shouldn't happen
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 7266f3d3abe..fd581d44fac 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -192,8 +192,17 @@
L.resist()
if("mov_intent")
+
+ var/list/modifiers = params2list(params)
+ //This is here instead of outside the switch to save adding overhead. its not used for any other UI icons right now
+ //If its needed in future for other UI icons, then move it up to above the switch
+
if(iscarbon(usr))
var/mob/living/carbon/C = usr
+ if (modifiers["alt"])
+ C.set_walk_speed()
+ return
+
if(C.legcuffed)
C << "You are legcuffed! You cannot run until you get [C.legcuffed] removed!"
C.m_intent = "walk" //Just incase
@@ -202,10 +211,9 @@
switch(usr.m_intent)
if("run")
usr.m_intent = "walk"
- usr.hud_used.move_intent.icon_state = "walking"
if("walk")
usr.m_intent = "run"
- usr.hud_used.move_intent.icon_state = "running"
+ update_move_icon(usr)
if("m_intent")
if(!usr.m_int)
switch(usr.m_intent)
@@ -504,3 +512,25 @@
usr.update_inv_l_hand(0)
usr.update_inv_r_hand(0)
return 1
+
+
+
+
+
+//This updates the run/walk button on the hud
+/obj/screen/proc/update_move_icon(var/mob/living/user)
+ switch(name)
+ if("mov_intent")
+ overlays.Cut()
+ switch(user.m_intent)
+ if("run")//When in run mode, the button will have a flashing coloured overlay which gives a visual indicator of stamina
+ icon_state = "running"
+ if (user.max_stamina != -1)//If max stamina is -1, this species doesnt use stamina. no overlay for them
+ var/image/holder = image('icons/mob/screen1.dmi', src, "run_overlay")
+ var/staminaportion = user.stamina / user.max_stamina
+ holder.color = percentage_to_colour(staminaportion)
+ holder.blend_mode = BLEND_MULTIPLY
+ overlays += holder
+
+ if("walk")
+ icon_state = "walking"
diff --git a/code/controllers/ProcessScheduler/core/process.dm b/code/controllers/ProcessScheduler/core/process.dm
index 3c091a4b045..52c7b8f08e8 100644
--- a/code/controllers/ProcessScheduler/core/process.dm
+++ b/code/controllers/ProcessScheduler/core/process.dm
@@ -107,6 +107,8 @@
last_task = 0
last_object = null
+/datum/controller/process/proc/preStart()
+
/datum/controller/process/proc/started()
var/timeofhour = TimeOfHour
// Initialize last_slept so we can record timing information
diff --git a/code/controllers/ProcessScheduler/core/processScheduler.dm b/code/controllers/ProcessScheduler/core/processScheduler.dm
index 08dc827b065..ad58468a0d9 100644
--- a/code/controllers/ProcessScheduler/core/processScheduler.dm
+++ b/code/controllers/ProcessScheduler/core/processScheduler.dm
@@ -84,10 +84,16 @@ var/global/datum/controller/processScheduler/processScheduler
isRunning = 1
// tick_lag will have been set by now, so re-initialize these
scheduler_sleep_interval = world.tick_lag
+ callPreStart()
updateStartDelays()
spawn(0)
process()
+/datum/controller/processScheduler/proc/callPreStart()
+ for (var/datum/controller/process/P in processes)
+ if (!P.disabled)
+ P.preStart()
+
/datum/controller/processScheduler/proc/process()
while(isRunning)
checkRunningProcesses()
diff --git a/code/controllers/Processes/modifier.dm b/code/controllers/Processes/modifier.dm
new file mode 100644
index 00000000000..7bbb181d120
--- /dev/null
+++ b/code/controllers/Processes/modifier.dm
@@ -0,0 +1,22 @@
+/datum/controller/process/modifier/setup()
+ name = "modifiers"
+ schedule_interval = 10
+ start_delay = 8
+
+/datum/controller/process/modifier/started()
+ ..()
+ if(!processing_modifiers)
+ processing_modifiers = list()
+
+/datum/controller/process/modifier/doWork()
+ for(last_object in processing_modifiers)
+ var/datum/modifier/O = last_object
+ if(isnull(O.gcDestroyed))
+ O.process()
+ else
+ catchBadType(O)
+ processing_objects -= O
+
+/datum/controller/process/modifier/statProcess()
+ ..()
+ stat(null, "[processing_modifiers.len] modifiers")
diff --git a/code/controllers/Processes/night_lighting.dm b/code/controllers/Processes/night_lighting.dm
new file mode 100644
index 00000000000..ed3f8ada71d
--- /dev/null
+++ b/code/controllers/Processes/night_lighting.dm
@@ -0,0 +1,89 @@
+/datum/controller/process/night_lighting/
+
+ var/isactive = 0
+ var/firstrun = 1
+
+ var/list/area/lighting_areas = list(
+ /area/hallway/primary/fore,
+ /area/hallway/primary/starboard,
+ /area/hallway/primary/aft,
+ /area/hallway/primary/port,
+ /area/hallway/primary/central_one,
+ /area/hallway/primary/central_two,
+ /area/hallway/primary/central_three,
+ /area/hallway/secondary/exit,
+ /area/hallway/secondary/entry/fore,
+ /area/hallway/secondary/entry/port,
+ /area/hallway/secondary/entry/starboard,
+ /area/hallway/secondary/entry/aft,
+ /area/crew_quarters/sleep,
+ /area/crew_quarters/locker,
+ /area/crew_quarters/fitness,
+ /area/crew_quarters/bar,
+ /area/engineering/foyer,
+ /area/security/lobby,
+ /area/storage/tools,
+ /area/storage/primary
+ )
+
+/datum/controller/process/night_lighting/setup()
+ name = "night lighting controller"
+ schedule_interval = 3600 // Every 5 minutes.
+
+ if (!config.night_lighting)
+ // Stop trying to delete processes. Not how it goes.
+ disabled = 1
+
+/datum/controller/process/night_lighting/preStart()
+
+ switch (worldtime2ticks())
+ if (0 to MORNING_LIGHT_RESET)
+ deactivate()
+ if (NIGHT_LIGHT_ACTIVE to TICKS_IN_DAY)
+ activate()
+
+
+/datum/controller/process/night_lighting/doWork()
+
+ switch (worldtime2ticks())
+ if (0 to MORNING_LIGHT_RESET)
+ if (isactive)
+ command_announcement.Announce("Good morning. The time is [worldtime2text()]. \n\nThe automated systems aboard the [station_name()] will now return the public hallway lighting levels to normal.", "Automated Lighting System", new_sound = 'sound/misc/bosuns_whistle.ogg')
+ deactivate()
+
+ if (NIGHT_LIGHT_ACTIVE to TICKS_IN_DAY)
+ if (!isactive)
+ command_announcement.Announce("Good evening. The time is [worldtime2text()]. \n\nThe automated systems aboard the [station_name()] will now dim lighting in the public hallways in order to accommodate the circadian rhythm of some species.", "Automated Lighting System", new_sound = 'sound/misc/bosuns_whistle.ogg')
+ activate()
+
+ else
+ if (isactive)
+ deactivate()
+
+/datum/controller/process/night_lighting/proc/activate()
+ for (var/obj/machinery/power/apc/APC in get_apc_list())
+ APC.toggle_nightlight("on")
+ isactive = 1
+
+ SCHECK
+
+/datum/controller/process/night_lighting/proc/deactivate()
+ for (var/obj/machinery/power/apc/APC in get_apc_list())
+ APC.toggle_nightlight("off")
+ isactive = 0
+
+ SCHECK
+
+/datum/controller/process/night_lighting/proc/get_apc_list()
+ var/list/obj/machinery/power/apc/lighting_apcs = list()
+
+ for (var/A in all_areas)
+ var/area/B = A
+ if (!(B.type in lighting_areas))
+ continue
+ if (B.apc)
+ lighting_apcs += B.apc
+
+ SCHECK
+
+ return lighting_apcs
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index b72a2e6cc3c..1c7fd963d43 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -145,8 +145,10 @@ var/list/gamemode_cache = list()
//Used for modifying movement speed for mobs.
//Unversal modifiers
- var/run_speed = 0
var/walk_speed = 0
+ var/walk_delay_multiplier = 1
+ var/run_delay_multiplier = 1
+ var/vehicle_delay_multiplier = 1
//Mob specific modifiers. NOTE: These will affect different mob types in different ways
var/human_delay = 0
@@ -173,6 +175,8 @@ var/list/gamemode_cache = list()
var/gateway_delay = 18000 //How long the gateway takes before it activates. Default is half an hour.
var/ghost_interaction = 0
+ var/night_lighting = 0
+
var/comms_password = ""
var/enter_allowed = 1
@@ -615,6 +619,9 @@ var/list/gamemode_cache = list()
if("ghost_interaction")
config.ghost_interaction = 1
+ if("night_lighting")
+ config.night_lighting = 1
+
if("disable_player_mice")
config.disable_player_mice = 1
@@ -810,11 +817,17 @@ var/list/gamemode_cache = list()
if("limbs_can_break")
config.limbs_can_break = value
- if("run_speed")
- config.run_speed = value
if("walk_speed")
config.walk_speed = value
+ // These should never go to 0 or below. So, we clamp them.
+ if("walk_delay_multiplier")
+ config.walk_delay_multiplier = max(0.1, value)
+ if("run_delay_multiplier")
+ config.run_delay_multiplier = max(0.1, value)
+ if("vehicle_delay_multiplier")
+ config.vehicle_delay_multiplier = max(0.1, value)
+
if("human_delay")
config.human_delay = value
if("robot_delay")
diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm
index 682b0fb3d03..860d0f397a0 100644
--- a/code/controllers/master_controller.dm
+++ b/code/controllers/master_controller.dm
@@ -70,6 +70,17 @@ datum/controller/game_controller/proc/setup_objects()
var/obj/machinery/atmospherics/unary/vent_scrubber/T = U
T.broadcast_status()
+
+ //Spawn the contents of the cargo warehouse
+ sleep(-1)
+ spawn_cargo_stock()
+
+
+ // Create the mining ore distribution map.
+ // These values determine the specific area that the map is applied to.
+ // If you do not use the official Baycode asteroid map, you will need to change them.
+ asteroid_ore_map = new /datum/random_map/ore(null,13,32,5,217,223)
+
// Set up antagonists.
populate_antag_type_list()
diff --git a/code/datums/api.dm b/code/datums/api.dm
index e90eceec6c0..77a572beafc 100644
--- a/code/datums/api.dm
+++ b/code/datums/api.dm
@@ -14,6 +14,12 @@
topic_commands_names.Add(A.name)
listclearnulls(topic_commands)
listclearnulls(topic_commands_names)
+
+ if (config.api_rate_limit_whitelist.len)
+ // To make the api_rate_limit_whitelist[addr] grabs actually work.
+ for (var/addr in config.api_rate_limit_whitelist)
+ config.api_rate_limit_whitelist[addr] = 1
+
return 1
/world/proc/api_do_auth_check(var/addr, var/auth, var/datum/topic_command/command)
diff --git a/code/datums/server_greeting.dm b/code/datums/server_greeting.dm
index b395e402d6e..70c9409ef95 100644
--- a/code/datums/server_greeting.dm
+++ b/code/datums/server_greeting.dm
@@ -14,6 +14,8 @@
#define OUTDATED_MEMO 2
#define OUTDATED_NOTE 4
+#define JS_SANITIZE(msg) list2params(list(json_encode(msg)))
+
/datum/server_greeting
// Hashes to figure out if we need to display the greeting message.
// These correspond to motd_hash and memo_hash on /datum/preferences for each client.
@@ -21,24 +23,19 @@
var/memo_hash = ""
// The stored strings of general subcomponents.
- var/motd = ""
- var/memo_list[] = list()
- var/memo = ""
+ var/motd = "
No new announcements to showcase.
"
- var/raw_data_user = ""
- var/raw_data_staff = ""
- // The near-final string to be displayed.
- // Only one placeholder remains: .
- var/user_data = ""
- var/staff_data = ""
+ var/memo_list[] = list()
+ var/memo = "
No memos have been posted.
"
+
+ // Cached outdated information.
+ var/list/client_cache = list()
/datum/server_greeting/New()
..()
load_from_file()
- prepare_data()
-
/*
* Populates variables from save file, and loads the raw HTML data.
* Needs to be called at least once for successful initialization.
@@ -52,51 +49,33 @@
if (F["memo"])
F["memo"] >> memo_list
- raw_data_staff = file2text('html/templates/welcome_screen.html')
-
- // This is a lazy way, but it disables the user from being able to see the memo button.
- var/staff_button = "
"
- raw_data_user = replacetextEx(raw_data_staff, staff_button, "")
+ update_data()
/*
- * Generates hashes, placeholders, and reparses var/memo.
- * Then updates staff_data and user_data with the new contents.
- * To be called after load_from_file or update_value.
+ * A helper to regenerate the hashes for all data fields.
+ * As well as to reparse the staff memo list.
+ * Separated for the sake of avoiding the duplication of code.
*/
-/datum/server_greeting/proc/prepare_data()
- if (!motd)
- motd = "
"
+ memo = initial(memo)
memo_hash = ""
- var/html_one = raw_data_staff
- html_one = replacetextEx(html_one, "", motd)
- html_one = replacetextEx(html_one, "", memo)
- staff_data = html_one
-
- var/html_two = raw_data_user
- html_two = replacetextEx(html_two, "", motd)
- user_data = html_two
-
- return
-
/*
* Helper to update the MoTD or memo contents.
* Args:
@@ -113,7 +92,6 @@
switch (change)
if ("motd")
motd = new_value
- motd_hash = md5(new_value)
if ("memo_write")
memo_list[new_value[1]] = list("date" = time2text(world.realtime, "DD-MMM-YYYY"), "content" = new_value[2])
@@ -131,7 +109,7 @@
F["motd"] << motd
F["memo"] << memo_list
- prepare_data()
+ update_data()
return 1
@@ -142,10 +120,13 @@
* Returns:
* - int
*/
-/datum/server_greeting/proc/find_outdated_info(var/client/user)
+/datum/server_greeting/proc/find_outdated_info(var/client/user, var/force_eval = 0)
if (!user || !user.prefs)
return 0
+ if (!force_eval && !isnull(client_cache[user]))
+ return client_cache[user]
+
var/outdated_info = 0
if (motd_hash && user.prefs.motd_hash != motd_hash)
@@ -157,53 +138,88 @@
if (user.prefs.notifications.len)
outdated_info |= OUTDATED_NOTE
+ client_cache[user] = outdated_info
+
return outdated_info
/*
- * Composes the final message and displays it to the user.
- * Also clears the user's notifications, should he have any.
+ * A proc used to open the server greeting window for a user.
+ * Args:
+ * - var/user client
+ * - var/outdated_info int
*/
-/datum/server_greeting/proc/display_to_client(var/client/user, var/outdated_info = 0)
+/datum/server_greeting/proc/display_to_client(var/client/user)
if (!user)
return
- var/notifications = "
You do not have any notifications to show.
"
- var/list/outdated_tabs = list()
+ user << browse('html/templates/welcome_screen.html', "window=greeting;size=640x500")
+
+/*
+ * Sends data to the JS controllers used in the server greeting.
+ * Also updates the user's preferences, if any of the hashes were out of date.
+ * Args:
+ * - var/user client
+ * - var/outdated_info int
+ */
+/datum/server_greeting/proc/send_to_javascript(var/client/user)
+ if (!user)
+ return
+
+ // This is fine now, because it uses cached information.
+ var/outdated_info = server_greeting.find_outdated_info(user)
+
var/save_prefs = 0
+ var/list/data = list("div" = "", "content" = "", "update" = 1)
if (outdated_info & OUTDATED_NOTE)
- outdated_tabs += "#note-tab"
+ user << output("#note-placeholder", "greeting.browser:RemoveElement")
+
+ data["div"] = "#note"
+ data["update"] = 1
- notifications = ""
for (var/datum/client_notification/a in user.prefs.notifications)
- notifications += a.get_html()
+ data["content"] = a.get_html()
+ user << output(JS_SANITIZE(data), "greeting.browser:AddContent")
- if (outdated_info & OUTDATED_MEMO)
- outdated_tabs += "#memo-tab"
- user.prefs.memo_hash = memo_hash
- save_prefs = 1
+ if (!user.holder)
+ user << output("#memo-tab", "greeting.browser:RemoveElement")
+ else
+ if (outdated_info & OUTDATED_MEMO)
+ data["update"] = 1
+ user.prefs.memo_hash = memo_hash
+ save_prefs = 1
+ else
+ data["update"] = 0
+
+ data["div"] = "#memo"
+ data["content"] = memo
+ user << output(JS_SANITIZE(data), "greeting.browser:AddContent")
if (outdated_info & OUTDATED_MOTD)
- outdated_tabs += "#motd-tab"
+ data["update"] = 1
user.prefs.motd_hash = motd_hash
save_prefs = 1
+ else
+ data["update"] = 0
- var/data = user_data
-
- if (user.holder)
- data = staff_data
-
- data = replacetextEx(data, "", notifications)
-
- if (outdated_tabs.len)
- var/tab_string = json_encode(outdated_tabs)
- data = replacetextEx(data, "var updated_tabs = \[\]", "var updated_tabs = [tab_string]")
-
- user << browse(data, "window=welcome_screen;size=640x500")
+ data["div"] = "#motd"
+ data["content"] = motd
+ user << output(JS_SANITIZE(data), "greeting.browser:AddContent")
if (save_prefs)
user.prefs.handle_preferences_save(user)
+/*
+ * Basically the Topic proc for the greeting datum.
+ */
+/datum/server_greeting/proc/handle_call(var/href_list, var/client/C)
+ if (!href_list || !href_list["command"] || !C)
+ return
+
+ switch (href_list["command"])
+ if ("request_data")
+ send_to_javascript(C)
+
#undef OUTDATED_NOTE
#undef OUTDATED_MEMO
#undef OUTDATED_MOTD
diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm
index 11db6931a8b..9e5b01e3eab 100644
--- a/code/defines/obj/weapon.dm
+++ b/code/defines/obj/weapon.dm
@@ -89,11 +89,12 @@
attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed")
/obj/item/weapon/cane/concealed
+ w_class = 4
var/concealed_blade
/obj/item/weapon/cane/concealed/New()
..()
- var/obj/item/weapon/material/butterfly/switchblade/temp_blade = new(src)
+ var/obj/item/weapon/canesword/temp_blade = new(src)
concealed_blade = temp_blade
temp_blade.attack_self()
@@ -111,7 +112,7 @@
else
..()
-/obj/item/weapon/cane/concealed/attackby(var/obj/item/weapon/material/butterfly/W, var/mob/user)
+/obj/item/weapon/cane/concealed/attackby(var/obj/item/weapon/canesword/W, var/mob/user)
if(!src.concealed_blade && istype(W))
user.visible_message("[user] has sheathed \a [W] into \his [src]!", "You sheathe \the [W] into \the [src].")
user.drop_from_inventory(W)
diff --git a/code/game/antagonist/outsider/raider.dm b/code/game/antagonist/outsider/raider.dm
index cfa54b39104..428f5eaa308 100644
--- a/code/game/antagonist/outsider/raider.dm
+++ b/code/game/antagonist/outsider/raider.dm
@@ -27,6 +27,7 @@ var/datum/antagonist/raider/raiders
/obj/item/clothing/under/captain_fly,
/obj/item/clothing/under/det,
/obj/item/clothing/under/brown,
+ /obj/item/clothing/under/syndicate/tracksuit
)
var/list/raider_shoes = list(
@@ -92,6 +93,7 @@ var/datum/antagonist/raider/raiders
/obj/item/weapon/gun/projectile/pistol,
/obj/item/weapon/gun/projectile/revolver,
/obj/item/weapon/gun/projectile/revolver/deckard,
+ /obj/item/weapon/gun/projectile/revolver/derringer,
/obj/item/weapon/gun/projectile/pirate,
/obj/item/weapon/gun/projectile/tanto
)
diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm
index 5edf058b02a..d985f42affc 100755
--- a/code/game/area/Space Station 13 areas.dm
+++ b/code/game/area/Space Station 13 areas.dm
@@ -1518,10 +1518,14 @@ area/space/atmosalert()
icon_state = "quartoffice"
/area/quartermaster/storage
- name = "\improper Cargo Bay"
+ name = "\improper Cargo Warehouse"
icon_state = "quartstorage"
sound_env = LARGE_ENCLOSED
+/area/quartermaster/loading
+ name = "\improper Cargo Bay"
+ icon_state = "quartloading"
+
/area/quartermaster/qm
name = "\improper Cargo - Quartermaster's Office"
icon_state = "quart"
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 0ce3d103586..0d359830aae 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -16,7 +16,7 @@
var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom.
var/simulated = 1 //filter for actions - used by lighting overlays
var/fluorescent // Shows up under a UV light.
-
+ var/list/modifiers = list()
///Chemistry.
var/datum/reagents/reagents = null
diff --git a/code/game/machinery/bots/mulebot.dm b/code/game/machinery/bots/mulebot.dm
index 2a2ca7a6287..099b1038c28 100644
--- a/code/game/machinery/bots/mulebot.dm
+++ b/code/game/machinery/bots/mulebot.dm
@@ -144,11 +144,9 @@
unload(0)
switch(severity)
if(2)
- BITRESET(wires, rand(0,9))
- BITRESET(wires, rand(0,9))
- BITRESET(wires, rand(0,9))
+ wires.RandomCutAll(40)//Fix by nanako because the old code was throwing runtimes
if(3)
- BITRESET(wires, rand(0,9))
+ wires.RandomCutAll(20)
..()
return
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 5164f9a12f9..a1c85801566 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -606,6 +606,10 @@ About the new airlock wires panel:
flick("door_deny", src)
if(secured_wires)
playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0)
+ if ("braced")
+ if (src.arePowerSystemsOn())
+ flick("door_deny", src)
+ playsound(src.loc, 'sound/machines/hydraulic_short.ogg', 50, 0)
return
/obj/machinery/door/airlock/attack_ai(mob/user as mob)
@@ -898,10 +902,21 @@ About the new airlock wires panel:
spawn(0) close(1)
else
user << "You need to be wielding \the [C] to do that."
+
+ else if(istype(C, /obj/item/weapon/melee/hammer) && !arePowerSystemsOn())
+ if(locked)
+ user << "The airlock's bolts prevent it from being forced."
+ else if( !welded && !operating )
+
+ if(density)
+ spawn(0) open(1)
+ else
+ spawn(0) close(1)
else
..()
return
+
/obj/machinery/door/airlock/phoron/attackby(C as obj, mob/user as mob)
if(C)
ignite(is_hot(C))
@@ -930,9 +945,9 @@ About the new airlock wires panel:
//if the door is unpowered then it doesn't make sense to hear the woosh of a pneumatic actuator
if(arePowerSystemsOn())
- playsound(src.loc, open_sound_powered, 100, 1)
+ playsound(src.loc, open_sound_powered, 60, 1)
else
- playsound(src.loc, open_sound_unpowered, 100, 1)
+ playsound(src.loc, open_sound_unpowered, 60, 1)
if(src.closeOther != null && istype(src.closeOther, /obj/machinery/door/airlock/) && !src.closeOther.density)
src.closeOther.close()
@@ -943,7 +958,8 @@ About the new airlock wires panel:
if(!arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR))
return 0
if (bracer)
- visible_message("[src]'s actuators whirr, but the door does not open.")
+ do_animate("braced")
+ visible_message("[src]'s actuators whirr, but the door does not open.")
return 0
if(locked || welded)
return 0
@@ -1000,7 +1016,7 @@ About the new airlock wires panel:
adjustBruteLoss(round(crush_damage / AIRLOCK_CRUSH_DIVISOR))
SetStunned(5)
SetWeakened(5)
-
+
var/turf/T = get_turf(src)
var/list/valid_turfs = list()
@@ -1012,7 +1028,7 @@ About the new airlock wires panel:
while(valid_turfs.len)
T = pick(valid_turfs)
valid_turfs -= T
-
+
if(src.forceMove(T))
return
diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm
index a84976c1fe4..22c7da36546 100644
--- a/code/game/machinery/doors/blast_door.dm
+++ b/code/game/machinery/doors/blast_door.dm
@@ -103,8 +103,8 @@
// This only works on broken doors or doors without power. Also allows repair with Plasteel.
/obj/machinery/door/blast/attackby(obj/item/weapon/C as obj, mob/user as mob)
src.add_fingerprint(user)
- if(istype(C, /obj/item/weapon/crowbar) || (istype(C, /obj/item/weapon/material/twohanded/fireaxe) && C:wielded == 1))
- if(((stat & NOPOWER) || (stat & BROKEN)) && !( src.operating ))
+ if(istype(C, /obj/item/weapon/crowbar) || (istype(C, /obj/item/weapon/material/twohanded/fireaxe) && C:wielded == 1) || (istype(C, /obj/item/weapon/melee/hammer)))
+ if (((stat & NOPOWER) || (stat & BROKEN)) && !( src.operating ))
force_toggle()
else
usr << "[src]'s motors resist your effort."
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 8a784f304b2..68b5e0de827 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -26,6 +26,7 @@
var/destroy_hits = 10 //How many strong hits it takes to destroy the door
var/min_force = 10 //minimum amount of force needed to damage the door with a melee weapon
var/hitsound = 'sound/weapons/smash.ogg' //sound door makes when hit with a weapon
+ var/hitsound_light = 'sound/effects/Glasshit.ogg'//Sound door makes when hit very gently
var/obj/item/stack/material/steel/repairing
var/block_air_zones = 1 //If set, air zones cannot merge across the door even when it is opened.
var/close_door_at = 0 //When to automatically close the door, if possible
@@ -53,9 +54,11 @@
/obj/machinery/door/attack_generic(var/mob/user, var/damage)
if(damage >= 10)
visible_message("\The [user] smashes into the [src]!")
+ playsound(src.loc, hitsound, 60, 1)
take_damage(damage)
else
visible_message("\The [user] bonks \the [src] harmlessly.")
+ playsound(src.loc, hitsound_light, 8, 1, -1)
user.do_attack_animation(src)
/obj/machinery/door/New()
@@ -244,9 +247,14 @@
tforce = 15 * (speed/5)
else
tforce = AM:throwforce * (speed/5)
- playsound(src.loc, hitsound, 100, 1)
- take_damage(tforce)
- return
+
+ if (tforce > 0)
+ var/volume = 100
+ if (tforce < 20)//No more stupidly loud banging sound from throwing a piece of paper at a door
+ volume *= (tforce / 20)
+ playsound(src.loc, hitsound, volume, 1)
+ take_damage(tforce)
+ return
/obj/machinery/door/attack_ai(mob/user as mob)
return src.attack_hand(user)
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index e56a858d436..82381013812 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -250,7 +250,7 @@
user << "\The [src] is welded shut!"
return
- if(istype(C, /obj/item/weapon/crowbar) || istype(C,/obj/item/weapon/material/twohanded/fireaxe))
+ if(istype(C, /obj/item/weapon/crowbar) || istype(C,/obj/item/weapon/material/twohanded/fireaxe) || (istype(C, /obj/item/weapon/melee/hammer)))
if(operating)
return
@@ -269,7 +269,7 @@
"You start forcing \the [src] [density ? "open" : "closed"] with \the [C]!",\
"You hear metal strain.")
if(do_after(user,30))
- if(istype(C, /obj/item/weapon/crowbar))
+ if(istype(C, /obj/item/weapon/crowbar) || (istype(C, /obj/item/weapon/melee/hammer)))
if(stat & (BROKEN|NOPOWER) || !density)
user.visible_message("\The [user] forces \the [src] [density ? "open" : "closed"] with \a [C]!",\
"You force \the [src] [density ? "open" : "closed"] with \the [C]!",\
diff --git a/code/game/machinery/floorlayer.dm b/code/game/machinery/floorlayer.dm
index c682a2b228a..fd9907f85c7 100644
--- a/code/game/machinery/floorlayer.dm
+++ b/code/game/machinery/floorlayer.dm
@@ -70,7 +70,10 @@
var/dismantle = mode["dismantle"]
var/laying = mode["laying"]
var/collect = mode["collect"]
- user << "\The [src] [!T?"don't ":""]has [!T?"":"[T.get_amount()] [T] "]tile\s, dismantle is [dismantle?"on":"off"], laying is [laying?"on":"off"], collect is [collect?"on":"off"]."
+ var/number = 0
+ if (T)
+ number = T.get_amount()
+ user << "\The [src] has [number] tile\s, dismantle is [dismantle?"on":"off"], laying is [laying?"on":"off"], collect is [collect?"on":"off"]."
/obj/machinery/floorlayer/proc/reset()
on=0
@@ -80,8 +83,11 @@
if(istype(new_turf, /turf/simulated/floor))
var/turf/simulated/floor/T = new_turf
if(!T.is_plating())
- T.make_plating(!(T.broken || T.burnt))
- return new_turf.is_plating()
+ if(!T.broken && !T.burnt)
+ new T.floor_type(T)
+ T.make_plating()
+ return !new_turf.intact
+ return 0
/obj/machinery/floorlayer/proc/TakeNewStack()
for(var/obj/item/stack/tile/tile in contents)
@@ -91,8 +97,13 @@
/obj/machinery/floorlayer/proc/SortStacks()
for(var/obj/item/stack/tile/tile1 in contents)
- for(var/obj/item/stack/tile/tile2 in contents)
- tile2.transfer_to(tile1)
+ if (tile1 && tile1.get_amount() > 0)
+ if (!T || T.type == tile1.type)
+ T = tile1
+ if (tile1.get_amount() < tile1.max_amount)
+ for(var/obj/item/stack/tile/tile2 in contents)
+ if (tile2 != tile1 && tile2.type == tile1.type)
+ tile2.transfer_to(tile1)
/obj/machinery/floorlayer/proc/layFloor(var/turf/w_turf)
if(!T)
diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm
index 84a821a5af4..fd975a129a8 100644
--- a/code/game/machinery/iv_drip.dm
+++ b/code/game/machinery/iv_drip.dm
@@ -125,6 +125,8 @@
update_icon()
/obj/machinery/iv_drip/attack_hand(mob/user as mob)
+ if (isAI(user))
+ return
if(src.beaker)
src.beaker.loc = get_turf(src)
src.beaker = null
diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm
index 3f4ea4a7d10..18729bd0b0c 100644
--- a/code/game/machinery/vending.dm
+++ b/code/game/machinery/vending.dm
@@ -723,7 +723,7 @@
vend_power_usage = 85000 //85 kJ to heat a 250 mL cup of coffee
products = list(/obj/item/weapon/reagent_containers/food/drinks/coffee = 25,/obj/item/weapon/reagent_containers/food/drinks/tea = 25,/obj/item/weapon/reagent_containers/food/drinks/h_chocolate = 25)
contraband = list(/obj/item/weapon/reagent_containers/food/drinks/ice = 10)
- prices = list(/obj/item/weapon/reagent_containers/food/drinks/coffee = 3, /obj/item/weapon/reagent_containers/food/drinks/tea = 3, /obj/item/weapon/reagent_containers/food/drinks/h_chocolate = 3)
+ prices = list(/obj/item/weapon/reagent_containers/food/drinks/coffee = 10, /obj/item/weapon/reagent_containers/food/drinks/tea = 10, /obj/item/weapon/reagent_containers/food/drinks/h_chocolate = 12)
@@ -739,10 +739,10 @@
/obj/item/weapon/reagent_containers/food/snacks/cheesiehonkers = 6, /obj/item/weapon/reagent_containers/food/snacks/tastybread = 6, /obj/item/weapon/reagent_containers/food/snacks/skrellsnacks = 3,
/obj/item/weapon/reagent_containers/food/snacks/meatsnack = 2, /obj/item/weapon/reagent_containers/food/snacks/maps = 2, /obj/item/weapon/reagent_containers/food/snacks/nathisnack = 2)
contraband = list(/obj/item/weapon/reagent_containers/food/snacks/syndicake = 6)
- prices = list(/obj/item/weapon/reagent_containers/food/snacks/candy = 1,/obj/item/weapon/reagent_containers/food/drinks/dry_ramen = 5,/obj/item/weapon/reagent_containers/food/snacks/chips = 1,
- /obj/item/weapon/reagent_containers/food/snacks/sosjerky = 2,/obj/item/weapon/reagent_containers/food/snacks/no_raisin = 1,/obj/item/weapon/reagent_containers/food/snacks/spacetwinkie = 1,
- /obj/item/weapon/reagent_containers/food/snacks/cheesiehonkers = 1, /obj/item/weapon/reagent_containers/food/snacks/tastybread = 2, /obj/item/weapon/reagent_containers/food/snacks/skrellsnacks = 4,
- /obj/item/weapon/reagent_containers/food/snacks/meatsnack = 4, /obj/item/weapon/reagent_containers/food/snacks/maps = 5, /obj/item/weapon/reagent_containers/food/snacks/nathisnack = 6)
+ prices = list(/obj/item/weapon/reagent_containers/food/snacks/candy = 5,/obj/item/weapon/reagent_containers/food/drinks/dry_ramen = 10,/obj/item/weapon/reagent_containers/food/snacks/chips = 7,
+ /obj/item/weapon/reagent_containers/food/snacks/sosjerky = 10,/obj/item/weapon/reagent_containers/food/snacks/no_raisin = 2,/obj/item/weapon/reagent_containers/food/snacks/spacetwinkie = 5,
+ /obj/item/weapon/reagent_containers/food/snacks/cheesiehonkers = 5, /obj/item/weapon/reagent_containers/food/snacks/tastybread = 8, /obj/item/weapon/reagent_containers/food/snacks/skrellsnacks = 30,
+ /obj/item/weapon/reagent_containers/food/snacks/meatsnack = 12, /obj/item/weapon/reagent_containers/food/snacks/maps = 13, /obj/item/weapon/reagent_containers/food/snacks/nathisnack = 14)
/obj/machinery/vending/cola
@@ -756,10 +756,10 @@
/obj/item/weapon/reagent_containers/food/drinks/cans/waterbottle = 10,/obj/item/weapon/reagent_containers/food/drinks/cans/space_up = 10,
/obj/item/weapon/reagent_containers/food/drinks/cans/iced_tea = 10, /obj/item/weapon/reagent_containers/food/drinks/cans/grape_juice = 10)
contraband = list(/obj/item/weapon/reagent_containers/food/drinks/cans/thirteenloko = 5, /obj/item/weapon/reagent_containers/food/snacks/liquidfood = 6)
- prices = list(/obj/item/weapon/reagent_containers/food/drinks/cans/cola = 1,/obj/item/weapon/reagent_containers/food/drinks/cans/space_mountain_wind = 1,
- /obj/item/weapon/reagent_containers/food/drinks/cans/dr_gibb = 1,/obj/item/weapon/reagent_containers/food/drinks/cans/starkist = 1,
- /obj/item/weapon/reagent_containers/food/drinks/cans/waterbottle = 2,/obj/item/weapon/reagent_containers/food/drinks/cans/space_up = 1,
- /obj/item/weapon/reagent_containers/food/drinks/cans/iced_tea = 1,/obj/item/weapon/reagent_containers/food/drinks/cans/grape_juice = 1)
+ prices = list(/obj/item/weapon/reagent_containers/food/drinks/cans/cola = 5,/obj/item/weapon/reagent_containers/food/drinks/cans/space_mountain_wind = 1,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/dr_gibb = 6,/obj/item/weapon/reagent_containers/food/drinks/cans/starkist = 5,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/waterbottle = 2,/obj/item/weapon/reagent_containers/food/drinks/cans/space_up = 5,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/iced_tea = 3,/obj/item/weapon/reagent_containers/food/drinks/cans/grape_juice = 6)
idle_power_usage = 211 //refrigerator - believe it or not, this is actually the average power consumption of a refrigerated vending machine according to NRCan.
//This one's from bay12
diff --git a/code/game/machinery/wishgranter.dm b/code/game/machinery/wishgranter.dm
index 469276a6b32..6a26e3d639b 100644
--- a/code/game/machinery/wishgranter.dm
+++ b/code/game/machinery/wishgranter.dm
@@ -1,89 +1,22 @@
/obj/machinery/wish_granter
name = "Wish Granter"
- desc = "You're not so sure about this, anymore..."
- icon = 'icons/obj/device.dmi'
- icon_state = "syndbeacon"
+ desc = "You are not so sure about this anymore..."
+ icon = 'icons/obj/machines/wishgranter.dmi' //thanks cakeisossim for the sprites
+ icon_state = "wishgranter"
- use_power = 0
- anchored = 1
- density = 1
-
- var/charges = 1
- var/insisting = 0
-
-/obj/machinery/wish_granter/attack_hand(var/mob/user as mob)
- usr.set_machine(src)
-
- if(charges <= 0)
- user << "The Wish Granter lies silent."
- return
-
- else if(!istype(user, /mob/living/carbon/human))
- user << "You feel a dark stirring inside of the Wish Granter, something you want nothing of. Your instincts are better than any man's."
- return
-
- else if(is_special_character(user))
- user << "Even to a heart as dark as yours, you know nothing good will come of this. Something instinctual makes you pull away."
-
- else if (!insisting)
- user << "Your first touch makes the Wish Granter stir, listening to you. Are you really sure you want to do this?"
- insisting++
-
- else
- user << "You speak. [pick("I want the station to disappear","Humanity is corrupt, mankind must be destroyed","I want to be rich", "I want to rule the world","I want immortality.")]. The Wish Granter answers."
- user << "Your head pounds for a moment, before your vision clears. You are the avatar of the Wish Granter, and your power is LIMITLESS! And it's all yours. You need to make sure no one can take it from you. No one can know, first."
-
- charges--
- insisting = 0
-
- if (!(HULK in user.mutations))
- user.mutations.Add(HULK)
-
- if (!(LASER in user.mutations))
- user.mutations.Add(LASER)
-
- if (!(XRAY in user.mutations))
- user.mutations.Add(XRAY)
- user.sight |= (SEE_MOBS|SEE_OBJS|SEE_TURFS)
- user.see_in_dark = 8
- user.see_invisible = SEE_INVISIBLE_LEVEL_TWO
-
- if (!(COLD_RESISTANCE in user.mutations))
- user.mutations.Add(COLD_RESISTANCE)
-
- if (!(TK in user.mutations))
- user.mutations.Add(TK)
-
- if(!(HEAL in user.mutations))
- user.mutations.Add(HEAL)
-
- user.update_mutations()
- user.mind.special_role = "Avatar of the Wish Granter"
-
- var/datum/objective/silence/silence = new
- silence.owner = user.mind
- user.mind.objectives += silence
-
- show_objectives(user.mind)
- user << "You have a very bad feeling about this."
-
- return
-
-
-/obj/machinery/wish_granter_dark
- name = "Wish Granter"
- desc = "You're not so sure about this, anymore..."
- icon = 'icons/obj/device.dmi'
- icon_state = "syndbeacon"
+ light_color = "#458F94"
+ light_power = 3
+ light_range = 4
anchored = 1
density = 1
+ layer = 9
use_power = 0
var/chargesa = 1
var/insistinga = 0
-/obj/machinery/wish_granter_dark/attack_hand(var/mob/living/carbon/human/user as mob)
+/obj/machinery/wish_granter/attack_hand(var/mob/living/carbon/human/user as mob)
usr.set_machine(src)
if(chargesa <= 0)
@@ -118,10 +51,13 @@
if (!(COLD_RESISTANCE in user.mutations))
user.mutations.Add(COLD_RESISTANCE)
user << "\blue Your body feels warm."
+ if (!(TK in user.mutations))
+ user.mutations.Add(TK)
+ if(!(HEAL in user.mutations))
+ user.mutations.Add(HEAL)
if (!(XRAY in user.mutations))
user.mutations.Add(XRAY)
user.sight |= (SEE_MOBS|SEE_OBJS|SEE_TURFS)
- user.see_in_dark = 8
user.see_invisible = SEE_INVISIBLE_LEVEL_TWO
user << "\blue The walls suddenly disappear."
user.set_species("Shadow")
@@ -140,28 +76,38 @@
user.mind.special_role = "Avatar of the Wish Granter"
if("The station is corrupt, it must be destroyed")
user << "Your wish is granted, but at a terrible cost..."
- user << "The Wish Granter punishes you for your wickedness, claiming your soul and warping your body to match the darkness in your heart."
+ user << "The Wish Granter punishes you for your wickedness, claiming your soul and slaving you to its own dark purposes."
user.mind.special_role = "Avatar of the Wish Granter"
- var/datum/objective/hijack/hijack = new
- hijack.owner = user.mind
- user.mind.objectives += hijack
- user << "Your inhibitions are swept away, the bonds of loyalty broken, you are free to murder as you please!"
+ user.hallucination += 10
+ user.adjustBrainLoss(30)
+ user.show_message("[user] screams!")
+ playsound(user, 'sound/hallucinations/wail.ogg', 40, 1)
+ sleep(30)
+ user << "Your mind is assaulted by endless horrors, your only desire is to end it, you must fulfill the Wish Granter's desires!"
+ var/datum/objective/nuclear/nuclear = new
+ nuclear.owner = user.mind
+ user.mind.objectives += nuclear
var/obj_count = 1
for(var/datum/objective/OBJ in user.mind.objectives)
user << "Objective #[obj_count]: [OBJ.explanation_text]"
obj_count++
- user.set_species("Shadow")
+ for(var/obj/machinery/nuclearbomb/N in world)
+ user << "[N.r_code]...!"
+ user.mind.store_memory("Nuclear Bomb Code: [N.r_code]", 0, 0)
if("I want peace")
user << "Your wish is granted..."
user << "Everything lies silently and then the station, its crew and troubles are gone in a blink of light. You found peace at last."
user.sdisabilities += BLIND
user.sdisabilities += DEAF
+ user.mind.special_role = "Avatar of the Wish Granter"
+ chargesa = 1
/////For the Wishgranter///////////
/mob/living/carbon/proc/immortality()
- set category = "Immortality"
+ set category = "Abilities"
set name = "Resurrection"
+ set desc = "Rise from your grave."
var/mob/living/carbon/C = usr
if(!C.stat)
@@ -169,7 +115,7 @@
return
C << "Death is not your end!"
- spawn(rand(800,1200))
+ spawn(rand(400,800))
if(C.stat == DEAD)
dead_mob_list -= C
living_mob_list += C
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index f088bf1da40..a76d28778b0 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -88,6 +88,8 @@
var/power_alert_status = 0
var/damage_alert_status = 0
+ var/noexplode = 0//Used for cases where an exosuit is spawned and turned into wreckage
+
/obj/mecha/drain_power(var/drain_check)
if(drain_check)
@@ -125,7 +127,7 @@
if(loc)
loc.Exited(src)
- if(prob(30))
+ if(!noexplode && prob(30))
explosion(get_turf(loc), 0, 0, 1, 3)
if(wreckage)
@@ -161,6 +163,7 @@
qdel(pr_inertial_movement)
qdel(pr_give_air)
qdel(pr_internal_damage)
+ qdel(pr_manage_warnings)
qdel(spark_system)
pr_int_temp_processor = null
pr_give_air = null
@@ -1915,6 +1918,50 @@
return 1
+//Used by randomstock.dm in generating damaged exosuits.
+//This does an individual check for each piece of equipment on the exosuit, and removes it if
+//this probability passes a check
+/obj/mecha/proc/lose_equipment(var/probability)
+ for(var/obj/item/mecha_parts/mecha_equipment/E in equipment)
+ if (prob(probability))
+ E.detach(loc)
+ qdel(E)
+
+
+//Used by randomstock.dm in generating damaged exosuits.
+//Does a random check for each possible type of internal damage, and adds it if it passes
+//The probability should be somewhat low unless you just want to saturate it with damage
+//Fire is excepted. We're not going to set the exosuit on fire while its in longterm storage
+/obj/mecha/proc/random_internal_damage(var/probability)
+ if (prob(probability))
+ setInternalDamage(MECHA_INT_TEMP_CONTROL)
+ if (prob(probability))
+ setInternalDamage(MECHA_INT_SHORT_CIRCUIT)
+ if (prob(probability))
+ setInternalDamage(MECHA_INT_TANK_BREACH)
+ if (prob(probability))
+ setInternalDamage(MECHA_INT_CONTROL_LOST)
+
+
+//Does a number of checks at probability, and alters some configuration values if succeeded
+/obj/mecha/proc/misconfigure_systems(var/probability)
+ if (prob(probability))
+ internal_tank_valve = rand(0,10000)//Screw up the cabin air pressure.
+ //This will probably kill the pilot if they dont check it before climbing in
+ if (prob(probability))
+ state = 1//Enable maintenance mode. It won't move.
+ if (prob(probability))
+ use_internal_tank = !use_internal_tank//Flip internal tank mode on or off
+ if (prob(probability))
+ toggle_lights()//toggle the lights
+ if (prob(probability))//Some settings to screw up the radio
+ radio.broadcasting = !radio.broadcasting
+ if (prob(probability))
+ radio.listening = !radio.listening
+ if (prob(probability))
+ radio.set_frequency(rand(1200,1600))
+ if (prob(probability))
+ maint_access = 0//Disallow maintenance mode
//////////////////////////////////////////
//////// Mecha global iterators ////////
//////////////////////////////////////////
@@ -1939,6 +1986,9 @@
process(var/obj/mecha/mecha)
+ if (!mecha)
+ return
+
if (!mecha.power_alert_status && mecha.cell)//If we're in the fine status
if (mecha.cell.charge < (mecha.cell.maxcharge*0.3))//but power is below 30%
mecha.power_alert_status = 1//Switch to the alert status
diff --git a/code/game/modifiers/modifiers.dm b/code/game/modifiers/modifiers.dm
new file mode 100644
index 00000000000..7c14dc81da3
--- /dev/null
+++ b/code/game/modifiers/modifiers.dm
@@ -0,0 +1,452 @@
+/*
+
+//Temporary modifiers system, by Nanako
+
+//This system is designed to allow making non-permanant, reversible changes to variables of any atom,
+//Though the system will be primarily used for altering properties of mobs, it can work on anything.
+
+//Intended uses are to allow equipment and items that modify a mob's various stats and attributes
+//As well as to replace the badly designed chem effects system
+
+
+This system works through a few main procs which should be overridden:
+All overridden procs should contain a call to parent at the start, before any other code
+
+
+Activate: This applies the effects. its here that you change any variables.
+ The author is also responsible here for storing any information necessary to later revert these changes
+
+Deactivate: This proc removes the effects. The author is responsible for writing it to reverse the changes cleanly
+ If using a strength var or any other kind of dynamic determinor of effects
+ It is very important NOT to factor that in when deactivating, because it may be changed while active
+ Instead, factor it in while activating and save the delta of the changed values.
+ that is, how much you added/subtracted
+ Apply that saved value in reverse when deactivating.
+
+Both activate and deactivate are stateful. Activate will not run if active is true.
+Deactivate will not run if active is false
+
+Process: Called once every second while the effect is active. Usually this will only see if its time
+ to recheck validity, but it can be overridden to add extra per-tick functionality.
+
+When created, a status effect will take the following parameters in new
+ Mandatory:
+ 1. Affected atom
+ 2. Modifier type
+ 3. Source atom (not mandatory if type is custom)
+
+ Optional:
+ 4. Source data
+ 5. Strength
+ 6. Duration
+ 7. Check interval
+
+
+
+//The affected atom is mandatory, without something to affect the modifier cannot exist
+
+//Modifier type is one of a selection of constants which determines the automated validity checking.
+ It does not enforce anything about the changes or other functionality. A valid option is mandatory
+
+//Source object is the thing that this modifier is based on, or anchored to.
+ //It is used as a point of reference in validity checks. Usually mandatory but some types do not require it
+
+//Source data provides additional information for validity, such as a maximum range from the source.
+ //Only required for certain types
+
+//Strength can be passed in by the caller and used insetting or removing the variable changes.
+ //It is never required for anything and not incorporated in base behaviour
+
+//Duration can be used for any type except custom. The modifier will cease to be valid
+ //this long after it is created
+ //Duration is decremented every proc until it falls below zero.
+ //This is used so that duration can be refreshed or increased before the modifier expires to prolong it
+
+//Check interval is a time, in deciseconds, between validity checks. a >0 value is required here,
+ //the default is 300 (every 30 seconds),
+ //Check interval is used to generate a world time at which the next check will run
+
+
+Please note that automated validity checking is primarily as a safety, to ensure things aren't left
+when they shouldn't be. If you desire something removed in a timely manner, it's recommended to manually
+call the effect's end proc from your code when you're done with it. For example, if a piece of equipment
+applying a modifier is taken off.
+
+Setting the check interval very low just to cause the effect to be removed quickly is bad practise
+it should be avoided in favour of manual removal where possible
+*/
+
+
+
+
+
+
+//Modifier types
+//These are needed globally and cannot be undefined
+
+#define MODIFIER_EQUIPMENT 1
+//The status effect remains valid as long as it is worn upon the affected mob.
+//Worn here means it must be held in a valid equip slot, which does not include pockets, storage, or held in hands.
+//The affected atom must be a mob
+
+#define MODIFIER_ITEM 2
+//The modifier remains valid as long as the item is in the target's contents,
+//no matter how many layers deep, if it can be found by recursing up, it is valid
+//This is essentially a more permissable version of equipment, and works when held, in backpacks, pockets, etc
+//It can also be used on non-mob targets
+
+#define MODIFIER_REAGENT 3
+//The status effect remains valid as long as the dose of this chemical in a mob's reagents is above
+//a specified dose value (specified in source data).
+//The default of zero will keep it valid if the chemical is in them at all
+//This checks for the reagent by type, in any of a mob's reagent holders - touching, blood, ingested
+//Affected atom must be a mob
+
+#define MODIFIER_AURA 4
+//The modifier remains valid as long as the target's turf is within a range of the source's turf
+//The range is defined in source data
+//A range of zero is still valid if source and target are on the same turf. Sub-zero range is invalid
+//Works on any affected atom
+
+#define MODIFIER_TIMED 5
+//The modifier remains valid as long as the duration has not expired.
+//Note that a duration can be used on any time, this type is just one that does not
+//check anything else but duration.
+//Does not require or use a source atom
+//Duration is mandatory for this type.
+//Works on any atom
+
+
+#define MODIFIER_CUSTOM 6
+//The validity check will always return 1. The author is expected to override
+//it with custom validity checking behaviour.
+//Does not require or use a source atom
+//Does not support duration
+
+
+
+//Override Modes:
+//An override parameter is passed in with New, which determines what to do if a modifier of
+//the same type already exists on the target
+
+#define MODIFIER_OVERRIDE_DENY 0
+//The default. If a modifier of our type already exists, the new one is discarded. It will Qdel itself
+//Without adding itself to any lists
+
+#define MODIFIER_OVERRIDE_NEIGHBOR 1
+//The new modifier ignores the existing one, and adds itself to the list alongside it
+//This is not recommended but you may have a specific application
+//Using the strength var and updating the effects is preferred if you want to stack multiples
+//of the same type of modifier on one mob
+
+#define MODIFIER_OVERRIDE_REPLACE 2
+//Probably the most common nondefault and most useful. If an old modifier of the same type exists,
+//Then the old one is first stopped without suspending, and deleted.
+//Then the new one will add itself as normal
+
+#define MODIFIER_OVERRIDE_REFRESH 3
+//This mode will overwrite the variables of the old one with our new values
+//It will also force it to remove and reapply its effects
+//This is useful for applying a lingering modifier, by refreshing its duration
+
+#define MODIFIER_OVERRIDE_STRENGTHEN 4
+//Almost identical to refresh, but it will only apply if the new modifer has a higher strength value
+//If the existing modifier's strength is higher than the new one, the new is discarded
+
+#define MODIFIER_OVERRIDE_CUSTOM 5
+//Calls a custom override function to be overwritten
+
+
+
+//This is the main proc you should call to create a modifier on a target object
+/datum/proc/add_modifier(var/typepath, var/_modifier_type, var/_source = null, var/_source_data = 0, var/_strength = 0, var/_duration = 0, var/_check_interval = 0, var/override = 0)
+ var/datum/modifier/D = new typepath(src, _modifier_type, _source, _source_data, _strength, _duration, _check_interval)
+ if (D && !D.gcDestroyed)
+ return D.handle_registration(override)
+ else
+ return null//The modifier must have failed creation and deleted itself
+
+
+/datum/modifier
+//Config
+ var/check_interval = 300//How often, in deciseconds, we will recheck the validity
+ var/atom/target = null
+ var/atom/source = null
+ var/modifier_type = 0
+ var/source_data = 0
+ var/strength = 0
+ var/duration = null
+
+ //A list of equip slots which are considered 'worn'.
+ //For equipment modifier type to be valid, the source object must be in a mob's contents
+ //and equipped to one of these whitelisted slots
+ //This list can be overridden if you want a custom slot whitelist
+ var/list/valid_equipment_slots = list(slot_back, slot_wear_mask, slot_handcuffed, slot_belt, \
+ slot_wear_id, slot_l_ear, slot_glasses, slot_gloves, slot_head, slot_shoes, slot_wear_suit, \
+ slot_w_uniform,slot_legcuffed, slot_r_ear, slot_legs, slot_tie)
+
+
+
+//Operating Vars
+ var/active = 0//Whether or not the effects are currently applied
+ var/next_check = 0
+ var/last_tick = 0
+
+
+//If creation of a modifier is successful, it will return a reference to itself
+//If creation fails for any reason, it will return null as well as giving some debug output
+/datum/modifier/New(var/atom/_target, var/_modifier_type, var/_source = null, var/_source_data = 0, var/_strength = 0, var/_duration = 0, var/_check_interval = 0)
+ ..()
+ target = _target
+ modifier_type = _modifier_type
+ source = _source
+ source_data = _source_data
+ strength = _strength
+ last_tick = world.time
+ if (_duration)
+ duration = _duration
+
+ if (_check_interval)
+ check_interval = _check_interval
+
+ if (!target || !modifier_type)
+ return invalid_creation("No target and/or no modifier type was submitted")
+
+ switch (modifier_type)
+ if (MODIFIER_EQUIPMENT)
+ if (!istype(target, /mob))
+ return invalid_creation("Equipment type requires a mob target")
+
+ if (!source || !istype(source, /obj))
+ return invalid_creation("Equipment type requires an object source")
+
+ //TODO: Port equip slot var
+ if (MODIFIER_ITEM)
+ if (!source || !istype(source, /obj))
+ return invalid_creation("Item type requires a source")
+
+ if (MODIFIER_REAGENT)
+ if (!istype(target, /mob) || !istype(source, /datum/reagent))
+ return invalid_creation("Reagent type requires a mob target and a reagent source")
+
+ if (MODIFIER_AURA)
+ if (!source || !istype(source, /atom))
+ return invalid_creation("Aura type requires an atom source")
+
+ if (MODIFIER_TIMED)
+ if (!duration || duration <= 0)
+ return invalid_creation("Timed type requires a duration")
+ if (MODIFIER_CUSTOM)
+ //No code here, just to prevent else
+ else
+ return invalid_creation("Invalid or unrecognised modifier type")//Not a valid modifier type.
+ return 1
+
+
+/datum/modifier/proc/handle_registration(var/override = 0)
+ var/datum/modifier/existing = null
+ for (var/datum/modifier/D in target.modifiers)
+ if (D.type == type)
+ existing = D
+ if (!existing)
+ processing_modifiers += src
+ target.modifiers += src
+ activate()
+ return src
+ else
+ return handle_override(override, existing)
+
+/datum/modifier/proc/activate()
+ if (!gcDestroyed && !active && target)
+ active = 1
+ return 1
+ return 0
+
+/datum/modifier/proc/deactivate()
+ active = 0
+ return 1
+
+/datum/modifier/proc/process()
+
+ if (!active)
+ last_tick = world.time
+ return 0
+
+ if (!isnull(duration))duration -= world.time - last_tick
+ if (world.time > next_check)
+ last_tick = world.time
+ return check_validity()
+ last_tick = world.time
+ return 1
+
+/datum/modifier/proc/check_validity()
+ next_check = world.time + check_interval
+ if (!target || target.gcDestroyed)
+ return validity_fail("Target is gone!")
+
+ if (modifier_type == MODIFIER_CUSTOM)
+ if (custom_validity())
+ return 1
+ else
+ return validity_fail("Custom failed")
+
+ if (!isnull(duration) && duration <= 0)
+ return validity_fail("Duration expired")
+
+ else if (modifier_type == MODIFIER_TIMED)
+ return 1
+
+ if (!source || source.gcDestroyed)//If we're not timed or custom, then we need a source. If our source is gone, we are invalid
+ return validity_fail("Source is gone and we need one")
+
+ switch (modifier_type)
+ if (MODIFIER_EQUIPMENT)
+ if (source.loc != target)
+ return validity_fail("Not in contents of mob")
+
+ var/obj/item/I = source
+ if (!I.equip_slot || !(I.equip_slot in valid_equipment_slots))
+ return validity_fail("Not equipped in the correct place")
+
+ //TODO: Port equip slot var. this cant be done properly without it. This is a temporary implementation
+ if (MODIFIER_ITEM)
+ if (!source.find_up_hierarchy(target))//If source is somewhere inside target, this will be true
+ return validity_fail("Not found in parent hierarchy")
+ if (MODIFIER_REAGENT)
+ var/totaldose = 0
+ if (!istype(source, /datum/reagent))//this shouldnt happen
+ return validity_fail("Source is not a reagent!")
+
+ var/ourtype = source.type
+
+ for (var/datum/reagent/R in target.reagents.reagent_list)
+ if (istype(R, ourtype))
+ totaldose += R.dose
+
+ if (istype(target, /mob/living))
+ var/mob/living/L = target
+
+ for (var/datum/reagent/R in L.ingested.reagent_list)
+ if (istype(R, ourtype))
+ totaldose += R.dose
+
+ if (istype(target, /mob/living/carbon))
+ var/mob/living/carbon/C = target
+
+ for (var/datum/reagent/R in C.bloodstr.reagent_list)
+ if (istype(R, ourtype))
+ totaldose += R.dose
+
+ for (var/datum/reagent/R in C.touching.reagent_list)
+ if (istype(R, ourtype))
+ totaldose += R.dose
+
+ if (totaldose < source_data)
+ return validity_fail("Dose is too low!")
+
+ if (MODIFIER_AURA)
+ if (!(get_turf(target) in range(source_data, get_turf(source))))
+ return validity_fail("Target not in range of source")
+
+ return 1
+
+
+//Override this without a call to parent, for custom validity conditions
+/datum/modifier/proc/custom_validity()
+ return 1
+
+/datum/modifier/proc/validity_fail(var/reason)
+ //world << "MODIFIER VALIDITY FAIL: [reason]"
+ qdel(src)
+ return 0
+
+/datum/modifier/proc/invalid_creation(var/reason)
+ log_debug("ERROR: [src] MODIFIER CREATION FAILED on [target]: [reason]")
+ qdel(src)
+ return 0
+
+//called by any object to either pause or remove the proc.
+/datum/modifier/proc/stop(var/instant = 0, var/suspend = 0)
+
+ //Instant var removes us from the lists immediately, instead of waiting til next frame when qdel goes through
+ if (instant)
+ if (target)
+ target.modifiers -= src
+ processing_modifiers -= src
+
+ if (suspend)
+ deactivate()
+ else
+ qdel(src)
+
+//Suspends and immediately restarts the proc, thus reapplying its effects
+/datum/modifier/proc/refresh()
+ deactivate()
+ activate()
+
+
+/datum/modifier/Destroy()
+ if (active)
+ deactivate()
+ if (target)
+ target.modifiers -= src
+ processing_modifiers -= src
+ ..()
+
+
+//Handles overriding an existing modifier of the same type.
+//This function should return either src or the existing, depending on whether or not src will be kept
+/datum/modifier/proc/handle_override(var/override, var/datum/modifier/existing)
+ switch(override)
+ if (MODIFIER_OVERRIDE_DENY)
+ qdel(src)
+ return existing
+ if (MODIFIER_OVERRIDE_NEIGHBOR)
+ processing_modifiers += src
+ target.modifiers += src
+ return src
+ if (MODIFIER_OVERRIDE_REPLACE)
+ existing.stop()
+ processing_modifiers += src
+ target.modifiers += src
+ activate()
+ return src
+ if (MODIFIER_OVERRIDE_REFRESH)
+ existing.strength = strength
+ existing.duration = duration
+ existing.source = source
+ existing.source_data = source_data
+ if (existing.check_validity())
+ existing.refresh()
+ qdel(src)
+ return existing
+ else
+ qdel(src)
+ return null//this should only happen if you overwrote the existing with bad values.
+ //It will result in both existing and src being deleted
+ //The null return will allow the source to see this went wrong and remake the modifier
+ if (MODIFIER_OVERRIDE_STRENGTHEN)
+ if (strength > existing.strength)
+ existing.strength = strength
+ existing.duration = duration
+ existing.source = source
+ existing.source_data = source_data
+ if (existing.check_validity())
+ existing.refresh()
+ qdel(src)
+ return existing
+ qdel(src)
+ return null
+ qdel(src)
+ return existing
+
+ if (MODIFIER_OVERRIDE_CUSTOM)
+ return custom_override(existing)
+ else
+ qdel(src)
+ return existing
+
+//This function should be completely overwritten, without a call to parent, to specify custom override
+/datum/modifier/proc/custom_override(var/datum/modifier/existing)
+ qdel(src)
+ return existing
diff --git a/code/game/modifiers/modifiers_chem.dm b/code/game/modifiers/modifiers_chem.dm
new file mode 100644
index 00000000000..c99c09dba9a
--- /dev/null
+++ b/code/game/modifiers/modifiers_chem.dm
@@ -0,0 +1,73 @@
+//Stimulant modifier. Applied in varying strengths by hyperzine and caffienated drinks
+//Increases sprinting speed, walk speed, and stamina regen
+/datum/modifier/stimulant
+ var/sprint_speed_added = 0
+ var/regen_added = 0
+ var/delay_added = 0
+
+/datum/modifier/stimulant/activate()
+ ..()
+ if (isliving(target))
+ var/mob/living/L = target
+
+ sprint_speed_added = 0.2 * strength
+ L.sprint_speed_factor += sprint_speed_added
+
+ regen_added = L.stamina_recovery * 0.3 * strength
+ L.stamina_recovery += regen_added
+
+ delay_added = -1.5 * strength
+ L.move_delay_mod += delay_added
+
+
+
+/datum/modifier/stimulant/deactivate()
+ ..()
+ if (isliving(target))
+ var/mob/living/L = target
+ L.sprint_speed_factor -= sprint_speed_added
+ L.stamina_recovery -= regen_added
+ L.move_delay_mod -= delay_added
+
+
+
+//Adrenaline, granted by synaptizine and inaprovaline, with different strengths for each
+//Allows the body to endure more, increasing speed a little, stamina a lot, stamina regen a lot,
+//and reducing sprint costs
+//Synaptizine applies it at strength 1, inaprovaline applies it at strength 0.6
+//Is applied using strengthen override mode, so synaptizine will replace inaprovaline if both are present
+/datum/modifier/adrenaline
+ var/speed_added = 0
+ var/stamina_added = 0
+ var/cost_added = 0
+ var/regen_added = 0
+
+/datum/modifier/adrenaline/activate()
+ ..()
+ if (isliving(target))
+ var/mob/living/L = target
+ speed_added += 0.1*strength
+ L.sprint_speed_factor += speed_added
+
+ stamina_added = L.max_stamina * strength
+ L.max_stamina += stamina_added
+
+ cost_added = -0.35 * strength
+ L.sprint_cost_factor += cost_added
+
+ regen_added = max ((L.stamina_recovery * 0.7 * strength), 5)
+ L.stamina_recovery += regen_added
+
+/datum/modifier/adrenaline/deactivate()
+ ..()
+ if (isliving(target))
+ var/mob/living/L = target
+
+ L.stamina_recovery -= regen_added
+ L.max_stamina -= stamina_added
+ L.sprint_cost_factor -= cost_added
+ L.sprint_speed_factor -= speed_added
+
+
+
+
diff --git a/code/game/objects/effects/decals/posters/bs12.dm b/code/game/objects/effects/decals/posters/bs12.dm
index a727e2804c0..734254e5876 100644
--- a/code/game/objects/effects/decals/posters/bs12.dm
+++ b/code/game/objects/effects/decals/posters/bs12.dm
@@ -1,4 +1,4 @@
-// baystation12 posters
+// aurora station posters
/datum/poster/bay_1
icon_state="bsposter1"
name = "Unlucky Space Explorer"
@@ -286,5 +286,45 @@
/datum/poster/bay_58
icon_state="bsposter58"
- name = "space carp information poster"
- desc = "This poster showcases an old spacer saying on the dangers of migrant space carp."
\ No newline at end of file
+ name = "space bear information poster"
+ desc = "This poster displays a picture of a legendary space bear."
+
+/datum/poster/bay_59
+ icon_state = "bsposter59"
+ name = "ATLAS poster"
+ desc = "ATLAS: For all of Humanity."
+
+/datum/poster/bay_60
+ icon_state = "bsposter60"
+ name = "N.S.S. Aurora"
+ desc = "This poster is a picture of the old, now defunct, N.S.S. Aurora. Commissioned in 2454 and decommissioned in the early days of 2458."
+
+/datum/poster/bay_61
+ icon_state = "bsposter61"
+ name = "Xenobiology Safety Protocols"
+ desc = "This posters warms the crew about the dangers of xenobiology outbreaks."
+
+/datum/poster/bay_62
+ icon_state = "bsposter62"
+ name = "Xenobiology Division"
+ desc = "This one depicts a green skrell research director doing autopsy on an alien lifeform."
+
+/datum/poster/bay_63
+ icon_state = "bsposter63"
+ name = "Suit Sensors"
+ desc = "This particular one depicts a female doctor tending to an injured crewmember. It says; \"Remember to enable your suit sensors!\"."
+
+/datum/poster/bay_64
+ icon_state = "bsposter64"
+ name = "Maintenance Drones"
+ desc = "This particular one depicts a maintenance drone. It reminders the crew to don't disturb them when they are doing repairs."
+
+/datum/poster/bay_65
+ icon_state = "bsposter65"
+ name = "Importance of Plasma"
+ desc = "A corporate morale poster showing three forms of plasma. Teaching the very basics of this really important substance."
+
+/datum/poster/bay_66
+ icon_state = "bsposter66"
+ name = "BFG 9000"
+ desc = "A picture of a big freakin' gun."
diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm
index b081530e10b..357b519d909 100644
--- a/code/game/objects/explosion.dm
+++ b/code/game/objects/explosion.dm
@@ -3,8 +3,8 @@
proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1, z_transfer = UP|DOWN)
src = null //so we don't abort once src is deleted
spawn(0)
+ var/power = max(0,devastation_range) * 2 + max(0,heavy_impact_range) + max(0,light_impact_range)
if(config.use_recursive_explosions)
- var/power = devastation_range * 2 + heavy_impact_range + light_impact_range //The ranges add up, ie light 14 includes both heavy 7 and devestation 3. So this calculation means devestation counts for 4, heavy for 2 and light for 1 power, giving us a cap of 27 power.
explosion_rec(epicenter, power)
return
@@ -28,30 +28,73 @@ proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impa
var/far_dist = 0
far_dist += heavy_impact_range * 5
far_dist += devastation_range * 20
+// Play sounds; we want sounds to be different depending on distance so we will manually do it ourselves.
+
+// Stereo users will also hear the direction of the explosion!
+
+// Calculate far explosion sound range. Only allow the sound effect for heavy/devastating explosions.
+
+// 3/7/14 will calculate to 80 + 35
+ var/volume = 10 + (power * 20)
+
var/frequency = get_rand_frequency()
- for(var/mob/M in player_list)
- // Double check for client
- if(M && M.client)
- var/turf/M_turf = get_turf(M)
- if(M_turf && M_turf.z == epicenter.z)
- var/dist = get_dist(M_turf, epicenter)
- // If inside the blast radius + world.view - 2
- if(dist <= round(max_range + world.view - 2, 1))
- M.playsound_local(epicenter, get_sfx("explosion"), 100, 1, frequency, falloff = 5) // get_sfx() is so that everyone gets the same sound
+ var/closedist = round(max_range + world.view - 2, 1)
- //You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station.
+ //Whether or not this explosion causes enough vibration to send sound or shockwaves through the station
+ var/vibration = 1
+ if (istype(epicenter,/turf/space))
+ vibration = 0
+ for (var/turf/T in range(src, max_range))
+ if (!istype(T,/turf/space))
+ //If there is a nonspace tile within the explosion radius
+ //Then we can reverberate shockwaves through that, and allow it to be felt in a vacuum
+ vibration = 1
- else if(dist <= far_dist)
- var/far_volume = Clamp(far_dist, 30, 50) // Volume is based on explosion size and dist
- far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion
- M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', far_volume, 1, frequency, falloff = 5)
+ if (vibration)
+ for(var/mob/M in player_list)
+ // Double check for client
+ var/reception = 2//Whether the person can be shaken or hear sound
+ //2 = BOTH
+ //1 = shockwaves only
+ //0 = no effect
+ if(M && M.client)
+ var/turf/M_turf = get_turf(M)
+
+
+
+ if(M_turf && M_turf.z == epicenter.z)
+ if (istype(M_turf,/turf/space))
+ //If the person is standing in space, they wont hear
+ //But they may still feel the shaking
+ reception = 0
+ for (var/turf/T in range(M, 1))
+ if (!istype(T,/turf/space))
+ //If theyre touching the hull or on some extruding part of the station
+ reception = 1//They will get screenshake
+ break
+
+ if (!reception)
+ continue
+
+ var/dist = get_dist(M_turf, epicenter)
+ if (reception == 2 && (M.ear_deaf <= 0 || !M.ear_deaf))//Dont play sounds to deaf people
+ // If inside the blast radius + world.view - 2
+ if(dist <= closedist)
+ M.playsound_local(epicenter, get_sfx("explosion"), min(100, volume), 1, frequency, falloff = 5) // get_sfx() is so that everyone gets the same sound
+ //You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station.
+
+ else
+ volume = M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', volume, 1, frequency, usepressure = 0, falloff = 1000)
+ //Playsound local will return the final volume the sound is actually played at
+ //It will return 0 if the sound volume falls to 0 due to falloff or pressure
+ //Also return zero if sound playing failed for some other reason
+
+ //Deaf people will feel vibrations though
+ if (volume > 0)//Only shake camera if someone was close enough to hear it
+ shake_camera(M, min(60,max(2,(power*18) / dist)), min(3.5,((power*3) / dist)),0.05)
+ //Maximum duration is 6 seconds, and max strength is 3.5
+ //Becuse values higher than those just get really silly
- var/close = range(world.view+round(devastation_range,1), epicenter)
- // to all distanced mobs play a different sound
- for(var/mob/M in world) if(M.z == epicenter.z) if(!(M in close))
- // check if the mob can hear
- if(M.ear_deaf <= 0 || !M.ear_deaf) if(!istype(M.loc,/turf/space))
- M << 'sound/effects/explosionfar.ogg'
if(adminlog)
message_admins("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z]) (JMP)")
log_game("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range]) in area [epicenter.loc.name] ")
@@ -104,7 +147,6 @@ proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impa
return 1
-
proc/secondaryexplosion(turf/epicenter, range)
for(var/turf/tile in range(range, epicenter))
tile.ex_act(2)
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 5ce7f6055f4..9e6c79660f6 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -243,6 +243,7 @@
// note this isn't called during the initial dressing of a player
/obj/item/proc/equipped(var/mob/user, var/slot)
layer = 20
+ equip_slot = slot
if(user.client) user.client.screen |= src
if(user.pulling == src) user.stop_pulling()
return
@@ -654,3 +655,47 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out.
/obj/item/proc/pwr_drain()
return 0 // Process Kill
+
+
+//a proc that any worn thing can call to update its itemstate
+//Should be cheaper than calling regenerate icons on the mob
+/obj/item/proc/update_worn_icon()
+ if (!equip_slot || !istype(loc, /mob))
+ return
+
+ var/mob/M = loc
+ switch (equip_slot)
+ if (slot_back)
+ M.update_inv_back()
+ if (slot_wear_mask)
+ M.update_inv_wear_mask()
+ if (slot_l_hand)
+ M.update_inv_l_hand()
+ if (slot_r_hand)
+ M.update_inv_r_hand()
+ if (slot_belt)
+ M.update_inv_belt()
+ if (slot_wear_id)
+ M.update_inv_wear_id()
+ if (slot_l_ear)
+ M.update_inv_ears()
+ if (slot_r_ear)
+ M.update_inv_ears()
+ if (slot_glasses)
+ M.update_inv_glasses()
+ if (slot_gloves)
+ M.update_inv_gloves()
+ if (slot_head)
+ M.update_inv_head()
+ if (slot_shoes)
+ M.update_inv_shoes()
+ if (slot_wear_suit)
+ M.update_inv_wear_suit()
+ if (slot_w_uniform)
+ M.update_inv_w_uniform()
+ if (slot_l_store)
+ M.update_inv_pockets()
+ if (slot_r_store)
+ M.update_inv_pockets()
+ if (slot_s_store)
+ M.update_inv_s_store()
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index f67c3215984..0e1373ea858 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -343,7 +343,8 @@ var/global/list/obj/item/device/pda/PDAs = list()
return id
/obj/item/device/pda/AltClick(var/mob/user)
- verb_remove_id()
+ if (ismob(src.loc))
+ verb_remove_id()
/obj/item/device/pda/MouseDrop(obj/over_object as obj, src_location, over_location)
var/mob/M = usr
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index 302192e831a..ee56196506c 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -271,7 +271,15 @@
on = 0
update_icon()
-/obj/item/device/flashlight/glowstick/attack_self(mob/user)
+/obj/item/device/flashlight/glowstick/attack_self(var/mob/living/user)
+
+ if(((CLUMSY in user.mutations)) && prob(50))
+ user << "You break \the [src] apart, spilling its contents everywhere!"
+ fuel = 0
+ new /obj/effect/decal/cleanable/greenglow(get_turf(user))
+ user.apply_effect((rand(15,30)),IRRADIATE,blocked = user.getarmor(null, "rad"))
+ qdel(src)
+ return
if(!fuel)
user << "\The [src] has already been used."
diff --git a/code/game/objects/items/devices/magnetic_lock.dm b/code/game/objects/items/devices/magnetic_lock.dm
index 280f1db812c..c6765569211 100644
--- a/code/game/objects/items/devices/magnetic_lock.dm
+++ b/code/game/objects/items/devices/magnetic_lock.dm
@@ -1,6 +1,6 @@
#define STATUS_INACTIVE 0
#define STATUS_ACTIVE 1
-#define STATUS_BROKEN 2
+#define STATUS_BROKEN -1
#define LAYER_ATTACHED 3.2
#define LAYER_NORMAL 3
@@ -8,50 +8,75 @@
/obj/item/device/magnetic_lock
name = "magnetic door lock"
desc = "A large, ID locked device used for completely locking down airlocks."
- icon = 'icons/obj/magnetic_locks/centcom.dmi'
- icon_state = "inactive"
+ icon = 'icons/obj/magnetic_locks.dmi'
+ icon_state = "inactive_CENTCOM"
+ //icon_state = "inactive"
w_class = 3
- req_access = list(103)
+ req_access = list(access_cent_specops)
health = 90
var/department = "CENTCOM"
var/status = 0
+ var/locked = 1
+ var/hacked = 0
var/constructionstate = 0
- var/drainamount = 20
+ var/drain_per_second = 3
+ var/last_process_time = 0
+ var/obj/machinery/door/airlock/target_node1 = null
+ var/obj/machinery/door/airlock/target_node2 = null
var/obj/machinery/door/airlock/target = null
var/obj/item/weapon/cell/powercell
+ var/obj/item/weapon/cell/internal_cell
/obj/item/device/magnetic_lock/security
- icon = 'icons/obj/magnetic_locks/security.dmi'
department = "Security"
- req_access = list(1)
+ icon_state = "inactive_Security"
+ req_access = list(access_security)
/obj/item/device/magnetic_lock/engineering
- icon = 'icons/obj/magnetic_locks/engineering.dmi'
department = "Engineering"
+ icon_state = "inactive_Engineering"
req_access = null
- req_one_access = list(11, 24)
+ req_one_access = list(access_engine_equip, access_atmospherics)
/obj/item/device/magnetic_lock/New()
..()
powercell = new /obj/item/weapon/cell/high()
+ internal_cell = new /obj/item/weapon/cell/device()
-/obj/item/device/magnetic_lock/examine()
- ..()
+ if (istext(department))
+ desc += " It is painted with [department] colors."
+
+ update_icon()
+
+/obj/item/device/magnetic_lock/examine(mob/user)
+ ..(user)
if (status == STATUS_BROKEN)
- usr << "It looks broken!"
+ user << "It looks broken!"
else
if (powercell)
var/power = round(powercell.charge / powercell.maxcharge * 100)
- usr << "\blue The powercell is at [power]% charge."
+ user << "The powercell is at [power]% charge."
else
- usr << "\red It has no powercell to power it!"
+ var/int_power = round(internal_cell.charge / internal_cell.maxcharge * 100)
+ user << "It has no powercell to power it! Internal cell is at [int_power]% charge."
/obj/item/device/magnetic_lock/attack_hand(var/mob/user)
- if (status == STATUS_ACTIVE)
- ui_interact(user)
+ add_fingerprint(user)
+ if (constructionstate == 1 && powercell)
+ powercell.update_icon()
+ powercell.add_fingerprint(user)
+ user.put_in_active_hand(powercell)
+ user << "You remove \the [powercell]."
+ powercell = null
+ setconstructionstate(2)
+ else if (anchored)
+ if (!locked)
+ detach()
+ else
+ user << "\The [src] is locked in place!"
else
..()
@@ -64,6 +89,24 @@
user << "[src] is broken beyond repair!"
return
+ if (istype(I, /obj/item/weapon/card/id))
+ if (!constructionstate && !hacked)
+ if (check_access(I))
+ locked = !locked
+ playsound(src, 'sound/machines/ping.ogg', 30, 1)
+ var/msg = "[I] through \the [src] and it [locked ? "locks" : "unlocks"] with a beep."
+ var/pos_adj = "[user.name] swipes \his "
+ var/fp_adj = "You swipe your "
+ user.visible_message("[addtext(pos_adj, msg)]", "[addtext(fp_adj, msg)]")
+ update_icon()
+ else
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 30, 1)
+ user << span("warning", "\The [src] buzzes as you swipe your [I].")
+ return
+ else
+ user << "You cannot swipe your [I] through [src] with it partially dismantled!"
+ return
+
if (istype(I, /obj/item/weapon) && user.a_intent == "hurt")
if (I.force >= 8)
user.visible_message("[user] bashes [src] with [I]!", "You strike [src] with [I], damaging it!")
@@ -80,220 +123,283 @@
emagcard.uses--
visible_message("[src] sparks and falls off the door!", "You emag [src], frying its circuitry[status == STATUS_ACTIVE ? " and making it drop onto the floor" : ""]!")
- setstatus(STATUS_BROKEN)
+ status = STATUS_BROKEN
+ if (target)
+ detach()
+ update_icon()
return
- if (status == STATUS_ACTIVE && istype(I, /obj/item/weapon/card/id))
- if (check_access(I) && !constructionstate)
- user << "You swipe your [I] through [src], making it drop onto the floor with a thud."
- setstatus(STATUS_INACTIVE)
- return
- else if (constructionstate)
- user << "You cannot swipe your [I] through [src] with it partially dismantled!"
- return
- else
- user << "A red light flashes on [src] as you swipe your [I] through it."
- flick("deny",src)
+ if (iswelder(I))
+ var/obj/item/weapon/weldingtool/WT = I
+ if (WT.remove_fuel(2, user))
+ user.visible_message(span("notice", "[user] starts welding the metal shell of [src]."), span("notice", "You start [hacked ? "repairing" : "welding open"] the metal covering of [src]."))
+ playsound(loc, 'sound/items/Welder.ogg', 50, 1)
+ overlays += "overlay_welding"
+ if (do_after(user, 25, 1))
+ user << span("notice", "You are able to [hacked ? "repair" : "weld through"] the metal shell of [src].")
+ if (hacked) locked = 1
+ else locked = 0
+ hacked = !hacked
+ overlays -= "overlay_welding"
+ else
+ overlays -= "overlay_welding"
+ update_icon()
return
- if (istype(I, /obj/item/weapon/screwdriver))
- user << "You unfasten and remove the plastic cover from [src], revealing a thick metal shell."
- playsound(loc, 'sound/items/Screwdriver.ogg', 50, 1)
- setconstructionstate(1)
+ if (iscrowbar(I))
+ if (!locked)
+ user << span("notice", "You pry the cover off [src].")
+ setconstructionstate(1)
+ else
+ user << span("notice", "You try to pry the cover off [src] but it doesn't budge.")
return
if (1)
- if (istype(I, /obj/item/weapon/screwdriver))
- user << "You put the metal cover of back onto [src], and screw it tight."
- playsound(loc, 'sound/items/Screwdriver.ogg', 50, 1)
+ if (istype(I, /obj/item/weapon/cell))
+ if (powercell)
+ user << span("notice","There's already a powercell in \the [src].")
+ return
+
+ if (iscrowbar(I))
+ user << span("notice", "You wedge the cover back in place.")
setconstructionstate(0)
return
- if (istype(I, /obj/item/weapon/cell))
- user.drop_item()
- I.loc = src
- powercell = I
- return
- if (istype(I, /obj/item/weapon/crowbar))
- if (isnull(powercell))
- user << "There is no powercell in \the [src]."
- return
- user << "You remove \the [powercell] from \the [src]."
- if (loc == user)
- powercell.forceMove(user.loc)
- else
- powercell.forceMove(loc)
- powercell = null
- return
- if (istype(I, /obj/item/weapon/weldingtool))
- var/obj/item/weapon/weldingtool/WT = I
- if (WT.remove_fuel(1, user))
- user.visible_message("[user] starts welding through the metal shell of [src].", "You start welding through the metal covering of [src]")
- playsound(loc, 'sound/items/Welder.ogg', 50, 1)
- if (do_after(user, 25))
- user << "You are able to weld through the metal shell of [src]."
- setconstructionstate(2)
- return
+
if (2)
- if (istype(I, /obj/item/weapon/crowbar))
- user << "You pry off the metal covering from [src]."
- playsound(loc, 'sound/items/Crowbar.ogg', 50, 1)
+ if (isscrewdriver(I))
+ user << span("notice", "You unscrew and remove the wiring cover from \the [src].")
+ playsound(loc, 'sound/items/Screwdriver.ogg', 50, 1)
setconstructionstate(3)
return
+
+ if (iscrowbar(I))
+ user << span("notice", "You wedge the cover back in place.")
+ setconstructionstate(0)
+ return
+
+ if (istype(I, /obj/item/weapon/cell))
+ if (!powercell)
+ user << span("notice","You place the [I] inside \the [src].")
+ user.drop_item()
+ I.loc = src
+ powercell = I
+ setconstructionstate(1)
+ return
+
if (3)
- if (istype(I, /obj/item/weapon/wirecutters))
- user << "You cut the wires connecting the [src]'s magnets to their powersupply, [target ? "making the device fall off [target] and rendering it unusable." : "rendering the device unusable."]"
+ if (iswirecutter(I))
+ user << span("notice", "You cut the wires connecting the [src]'s magnets to their internal powersupply, [target ? "making the device fall off [target] and rendering it unusable." : "rendering the device unusable."]")
playsound(loc, 'sound/items/Wirecutter.ogg', 50, 1)
setconstructionstate(4)
return
-/obj/item/device/magnetic_lock/process()
- if (powercell && powercell.charge > drainamount)
- powercell.charge -= drainamount
- else
- if (powercell)
- powercell.charge = 0
- visible_message("[src] beeps loudly and falls off \the [target]; its powercell having run out of power.")
- setstatus(STATUS_INACTIVE)
+ if (isscrewdriver(I))
+ user << span("notice", "You replace and screw tight the wiring cover from \the [src].")
+ playsound(loc, 'sound/items/Screwdriver.ogg', 50, 1)
+ setconstructionstate(2)
+ return
-/obj/item/device/magnetic_lock/proc/attachto(var/obj/machinery/door/airlock/newtarget, var/mob/user as mob)
+ if (4)
+ if (iswirecutter(I))
+ user << span("notice", "You repair the wires connecting the [src]'s magnets to their internal powersupply")
+ setconstructionstate(3)
+ return
+
+/obj/item/device/magnetic_lock/process()
+ var/obj/item/weapon/cell/C = powercell // both of these are for viewing ease
+ var/obj/item/weapon/cell/BU = internal_cell
+ var/delta_sec = (world.time - last_process_time) / 10
+ var/drainamount = drain_per_second * delta_sec
+ if (C)
+ if (C.charge > drainamount)
+ C.charge -= drainamount
+ var/int_diff = min(drainamount, BU.maxcharge - BU.charge)
+ if (C.charge > int_diff && BU.charge != BU.maxcharge)
+ if (int_diff < drainamount)
+ BU.charge = BU.maxcharge
+ C.charge -= int_diff
+ else
+ BU.charge += drainamount
+ C.charge -= drainamount
+ else if (BU.charge > (drainamount - C.charge))
+ var/diff = drainamount - C.charge
+ C.charge = 0
+ BU.charge -= diff
+ else
+ BU.charge = 0
+ visible_message(span("danger", "[src] beeps loudly and falls off \the [target]; its powercell having run out of power."))
+ detach(0)
+ else if (BU.charge > drainamount)
+ BU.charge -= drainamount
+ else
+ BU.charge = 0
+ visible_message(span("danger", "[src] beeps loudly and falls off \the [target]; its powercell having run out of power."))
+ detach(0)
+ last_process_time = world.time
+
+/obj/item/device/magnetic_lock/proc/check_target(var/obj/machinery/door/airlock/newtarget, var/mob/user as mob)
if (status == STATUS_BROKEN)
- user << "[src] is damaged beyond repair! It cannot be used!"
- return
+ user << span("danger", "[src] is damaged beyond repair! It cannot be used!")
+ return 0
+
+ if (hacked)
+ user << span("danger", "[src] buzzes; it can't be used until you repair it!")
+ return 0
if (!newtarget.density || newtarget.operating)
- user << "[newtarget] must be closed before you can attach [src] to it!"
- return
+ user << span("danger", "[newtarget] must be closed before you can attach [src] to it!")
+ return 0
if (newtarget.p_open)
- user << "You must close [newtarget]'s maintenance panel before attaching [src] to it!"
- return
+ user << span("danger", "You must close [newtarget]'s maintenance panel before attaching [src] to it!")
+ return 0
+
+ if (!user.Adjacent(newtarget))
+ user << span("danger", "You must stand next to [newtarget] while attaching it!")
+ return 0
+
+ return 1
+
+/obj/item/device/magnetic_lock/proc/attachto(var/obj/machinery/door/airlock/newtarget, var/mob/user as mob)
+ if (!check_target(newtarget, user)) return
user.visible_message("[user] starts mounting [src] onto [newtarget].", "You begin mounting [src] onto [newtarget].")
+
if (do_after(user, 35, 1))
- if (status == STATUS_BROKEN)
- user << "[src] is damaged beyond repair! It cannot be used!"
+
+ if (!check_target(newtarget, user)) return
+
+ if(!internal_cell.charge)
+ user << "\The [src] looks dead and out of power."
return
- if (!newtarget.density)
- user << "[newtarget] must be closed before you can attach [src] to it!"
- return
+ var/direction = get_dir(user, newtarget)
+ if ((direction in alldirs) && !(direction in cardinal))
+ direction = turn(direction, -45)
+ if (check_neighbor_density(get_turf(newtarget.loc), direction))
+ direction = turn(direction, 90)
+ if (check_neighbor_density(get_turf(newtarget.loc), direction))
+ user << "There is something in the way of \the [newtarget]!"
+ return
- if (newtarget.p_open)
- user << "You must close [newtarget]'s maintenance panel before attaching [src] to it!"
- return
+ if (is_type_in_oview(/obj/machinery/door/airlock, 1, newtarget))
+ if (alert("Brace adjacent airlocks?",,"Yes", "No") == "Yes")
+ if (!check_target(newtarget, user)) return
+ for (var/obj/machinery/door/airlock/A in get_step(newtarget.loc, turn(direction, -90)))
+ if (istype(A, newtarget.type))
+ if (!check_target(A, user)) return
+ target_node1 = A
+ target_node1.bracer = src
+ break
+ for (var/obj/machinery/door/airlock/B in get_step(newtarget.loc, turn(direction, 90)))
+ if (istype(B, newtarget.type))
+ if (!check_target(B, user)) return
+ target_node2 = B
+ target_node2.bracer = src
+ break
user.visible_message("[user] attached [src] onto [newtarget] and flicks it on. The magnetic lock now seals [newtarget].", "You attached [src] onto [newtarget] and switched on the magnetic lock.")
user.drop_item()
- setstatus(STATUS_ACTIVE, newtarget)
+ forceMove(get_step(newtarget.loc, reverse_direction(direction)))
+ set_dir(reverse_direction(direction))
+ status = STATUS_ACTIVE
+ attach(newtarget)
return
-/obj/item/device/magnetic_lock/proc/setstatus(var/newstatus, var/obj/machinery/door/airlock/newtarget as obj)
- switch (newstatus)
- if (STATUS_INACTIVE)
- if (status != STATUS_ACTIVE)
- return
- if (!target)
- return
-
- detach()
- icon_state = "inactive"
- status = newstatus
-
- if (STATUS_ACTIVE)
- if (status != STATUS_INACTIVE)
- return
- if (!newtarget)
- return
-
- attach(newtarget)
- icon_state = "active"
- status = newstatus
-
- if (STATUS_BROKEN)
- spark()
-
- if (target)
- var/playflick = 1
- if (constructionstate)
- playflick = 0
-
- detach(playflick)
-
- icon_state = "broken"
- status = newstatus
-
/obj/item/device/magnetic_lock/proc/setconstructionstate(var/newstate)
- constructionstate = newstate
- if (newstate == 0)
- if (status == STATUS_ACTIVE)
- icon_state = "active"
- else
- icon_state = "inactive"
- else if (newstate == 2)
- flick("deconstruct_2_anim", src)
+ if (!powercell && newstate == 1)
+ setconstructionstate(2)
+ return
else if (newstate == 4)
- setstatus(STATUS_BROKEN)
- else
- icon_state = "deconstruct_[constructionstate]"
+ detach(playflick = 0)
+ update_icon()
+ constructionstate = newstate
+ update_icon()
/obj/item/device/magnetic_lock/proc/detach(var/playflick = 1)
if (target)
if (playflick)
- spawn(-15) flick("release", src)
+ spawn(-15) flick("release_[department]", src)
- adjustsprite(null)
+ status = STATUS_INACTIVE
+ set_dir(SOUTH)
+ update_icon()
layer = LAYER_NORMAL
target.bracer = null
-
- processing_objects.Remove(src)
-
+ target = null
+ if (target_node1)
+ target_node1.bracer = null
+ target_node1 = null
+ if (target_node2)
+ target_node2.bracer = null
+ target_node2 = null
anchored = 0
+ processing_objects.Remove(src)
+ last_process_time = 0
+
/obj/item/device/magnetic_lock/proc/attach(var/obj/machinery/door/airlock/newtarget as obj)
- adjustsprite(newtarget)
layer = LAYER_ATTACHED
- flick("deploy", src)
newtarget.bracer = src
target = newtarget
+ last_process_time = world.time
processing_objects.Add(src)
-
anchored = 1
-/obj/item/device/magnetic_lock/proc/adjustsprite(var/obj/target as obj)
- if (target)
- switch (get_dir(src, target))
+ spawn(-15)
+ flick("deploy_[department]", src)
+ update_icon()
+
+/obj/item/device/magnetic_lock/update_icon()
+ if (status == STATUS_ACTIVE && target)
+ icon_state = "active_[department]"
+ switch (dir)
if (NORTH)
pixel_x = 0
- pixel_y = 32
- if (NORTHEAST)
- pixel_x = 32
- pixel_y = 32
- if (EAST)
- pixel_x = 32
- pixel_y = 0
- if (SOUTHEAST)
- pixel_x = 32
pixel_y = -32
+ if (EAST)
+ pixel_x = -32
+ pixel_y = 0
if (SOUTH)
pixel_x = 0
- pixel_y = -32
- if (SOUTHWEST)
- pixel_x = -32
- pixel_y = -32
- if (WEST)
- pixel_x = -32
- pixel_y = 0
- if (NORTHWEST)
- pixel_x = -32
pixel_y = 32
- else
+ if (WEST)
+ pixel_x = 32
+ pixel_y = 0
+ else if (status >= STATUS_INACTIVE)
+ icon_state = "inactive_[department]"
pixel_x = 0
pixel_y = 0
+ else
+ icon_state = "broken_[department]"
+ pixel_x = 0
+ pixel_y = 0
+ update_overlays()
+
+/obj/item/device/magnetic_lock/proc/update_overlays()
+ overlays.Cut()
+ switch (status)
+ if (STATUS_BROKEN)
+ icon_state = "broken"
+ return
+
+ if (STATUS_INACTIVE to STATUS_ACTIVE)
+ if (hacked)
+ overlays += "overlay_hacked"
+ else if (locked)
+ overlays += "overlay_locked"
+ else
+ overlays += "overlay_unlocked"
+ switch (constructionstate)
+ if (0)
+ return
+ if (1 to 4)
+ overlays += "overlay_deconstruct_[constructionstate]"
/obj/item/device/magnetic_lock/proc/takedamage(var/damage)
health -= damage
@@ -303,7 +409,8 @@
if (health <= 0)
visible_message("[src] sparks[target ? " and falls off of \the [target]!" : "!"] It is now completely unusable!")
- setstatus(STATUS_BROKEN)
+ status = STATUS_BROKEN
+ update_icon()
return
if (prob(50))
diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm
index 88b53bd342d..9f7e24dc15d 100644
--- a/code/game/objects/items/devices/transfer_valve.dm
+++ b/code/game/objects/items/devices/transfer_valve.dm
@@ -150,7 +150,7 @@
tank_two = null
else
return
-
+
T.loc = get_turf(src)
update_icon()
@@ -166,18 +166,18 @@
/obj/item/device/transfer_valve/proc/split_gases()
if(!valve_open)
return
-
+
valve_open = 0
-
- if(deleted(tank_one) || deleted(tank_two))
+
+ if(deleted(tank_one) || deleted(tank_two) || !tank_one.air_contents || !tank_two.air_contents)
return
-
+
var/ratio1 = tank_one.air_contents.volume/tank_two.air_contents.volume
var/datum/gas_mixture/temp
temp = tank_two.air_contents.remove_ratio(ratio1)
tank_one.air_contents.merge(temp)
tank_two.air_contents.volume -= tank_one.air_contents.volume
-
+
/*
Exadv1: I know this isn't how it's going to work, but this was just to check
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index e22d7724386..34d7cdde1ac 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -161,3 +161,19 @@
R.emagged = 1
return 1
+
+/obj/item/borg/upgrade/combat
+ name = "combat cyborg module"
+ desc = "Unlocks the combat cyborg module"
+ construction_cost = list(DEFAULT_WALL_MATERIAL=10000,"glass"=15000,"gold"= 5000,"diamond" = 1000)
+ icon_state = "cyborg_upgrade3"
+ require_module = 0
+
+/obj/item/borg/upgrade/combat/action(var/mob/living/silicon/robot/R)
+ if(..()) return 0
+
+ if(R.crisis_override == 1)
+ return 0
+
+ R.crisis_override = 1
+ return 1
diff --git a/code/game/objects/items/weapons/grenades/flashbang.dm b/code/game/objects/items/weapons/grenades/flashbang.dm
index 0b175b495dc..a6eeb42a444 100644
--- a/code/game/objects/items/weapons/grenades/flashbang.dm
+++ b/code/game/objects/items/weapons/grenades/flashbang.dm
@@ -128,6 +128,7 @@
/obj/item/weapon/grenade/flashbang/clusterbang/prime()
var/numspawned = rand(4,8)
var/again = 0
+ var/atom/A = loc
for(var/more = numspawned,more > 0,more--)
if(prob(35))
again++
@@ -135,15 +136,15 @@
for(,numspawned > 0, numspawned--)
spawn(0)
- new /obj/item/weapon/grenade/flashbang/cluster(src.loc)//Launches flashbangs
+ new /obj/item/weapon/grenade/flashbang/cluster(A)//Launches flashbangs
playsound(src.loc, 'sound/weapons/armbomb.ogg', 75, 1, -3)
for(,again > 0, again--)
spawn(0)
- new /obj/item/weapon/grenade/flashbang/clusterbang/segment(src.loc)//Creates a 'segment' that launches a few more flashbangs
+ new /obj/item/weapon/grenade/flashbang/clusterbang/segment(A)//Creates a 'segment' that launches a few more flashbangs
playsound(src.loc, 'sound/weapons/armbomb.ogg', 75, 1, -3)
- qdel(src)
- return
+ spawn(1)
+ qdel(src)
/obj/item/weapon/grenade/flashbang/clusterbang/segment
desc = "A smaller segment of a clusterbang. Better run."
@@ -152,6 +153,7 @@
icon_state = "clusterbang_segment"
/obj/item/weapon/grenade/flashbang/clusterbang/segment/New()//Segments should never exist except part of the clusterbang, since these immediately 'do their thing' and asplode
+
icon_state = "clusterbang_segment_active"
active = 1
banglet = 1
@@ -168,13 +170,14 @@
for(var/more = numspawned,more > 0,more--)
if(prob(35))
numspawned --
-
+ var/atom/A = src.loc
for(,numspawned > 0, numspawned--)
spawn(0)
- new /obj/item/weapon/grenade/flashbang/cluster(src.loc)
+ new /obj/item/weapon/grenade/flashbang/cluster(A)
playsound(src.loc, 'sound/weapons/armbomb.ogg', 75, 1, -3)
- qdel(src)
- return
+
+ spawn(1)
+ qdel(src)
/obj/item/weapon/grenade/flashbang/cluster/New()//Same concept as the segments, so that all of the parts don't become reliant on the clusterbang
spawn(0)
@@ -186,5 +189,5 @@
walk_away(src,temploc,stepdist)
var/dettime = rand(15,60)
spawn(dettime)
- prime()
- ..()
+ prime()
+ ..()
diff --git a/code/game/objects/items/weapons/material/swords.dm b/code/game/objects/items/weapons/material/swords.dm
index a003bb7bbd1..76bfabd7587 100644
--- a/code/game/objects/items/weapons/material/swords.dm
+++ b/code/game/objects/items/weapons/material/swords.dm
@@ -31,8 +31,10 @@
/obj/item/weapon/material/sword/rapier
name = "rapier"
desc = "A slender, fancy and sharply pointed sword."
+ icon = 'icons/obj/sword.dmi'
icon_state = "rapier"
- item_state = "claymore"
+ item_state = "rapier"
+ contained_sprite = 1
slot_flags = SLOT_BELT
attack_verb = list("attacked", "stabbed", "prodded", "poked", "lunged")
@@ -61,3 +63,15 @@
icon_state = "sabre"
item_state = "katana"
slot_flags = SLOT_BELT
+
+/obj/item/weapon/material/sword/axe
+ name = "battle axe"
+ desc = "A one handed battle axe, still a deadly weapon."
+ icon = 'icons/obj/sword.dmi'
+ icon_state = "axe"
+ item_state = "axe"
+ contained_sprite = 1
+ slot_flags = SLOT_BACK
+ attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut")
+ applies_material_colour = 0
+
diff --git a/code/game/objects/items/weapons/material/twohanded.dm b/code/game/objects/items/weapons/material/twohanded.dm
index 61728b68a19..492b70d2428 100644
--- a/code/game/objects/items/weapons/material/twohanded.dm
+++ b/code/game/objects/items/weapons/material/twohanded.dm
@@ -223,10 +223,21 @@
var/image/IM = image(I.icon,I.icon_state)
IM.overlays = I.overlays.Copy()
HS.overlays += IM
+ HS.name = "[I.name] on a spear"
qdel(src)
return
return ..()
+//predefined materials for spears
+/obj/item/weapon/material/twohanded/spear/steel/New(var/newloc)
+ ..(newloc,"steel")
+
+/obj/item/weapon/material/twohanded/spear/plasteel/New(var/newloc)
+ ..(newloc,"plasteel")
+
+/obj/item/weapon/material/twohanded/spear/diamond/New(var/newloc)
+ ..(newloc,"diamond")
+
/obj/structure/headspear
name = "head on a spear"
desc = "How barbaric."
@@ -239,4 +250,4 @@
new /obj/item/weapon/material/twohanded/spear(user.loc)
for(var/obj/item/organ/external/head/H in src)
H.loc = user.loc
- qdel(src)
\ No newline at end of file
+ qdel(src)
diff --git a/code/game/objects/items/weapons/melee/misc.dm b/code/game/objects/items/weapons/melee/misc.dm
index b2018cf9512..b4fcc6e84c7 100644
--- a/code/game/objects/items/weapons/melee/misc.dm
+++ b/code/game/objects/items/weapons/melee/misc.dm
@@ -50,4 +50,25 @@
/obj/item/weapon/melee/chainsword/suicide_act(mob/user)
viewers(user) << "\red [user] is slicing \himself apart with the [src.name]! It looks like \he's trying to commit suicide."
return (BRUTELOSS|OXYLOSS)
-*/
\ No newline at end of file
+*/
+
+//This is essentially a crowbar and a baseball bat in one.
+/obj/item/weapon/melee/hammer
+ name = "kneebreaker hammer"
+ desc = "A heavy hammer made of plasteel, the other end could be used to pry open doors."
+ icon = 'icons/obj/kneehammer.dmi'
+ icon_state = "kneehammer"
+ item_state = "kneehammer"
+ contained_sprite = 1
+ slot_flags = SLOT_BELT
+ force = 22
+ throwforce = 70
+//This should do around 15 brute when you throw it, there's probably a better way to do it.
+ throw_speed = 1
+ throw_range = 5
+ attack_verb = list("smashed", "beaten", "slammed", "smacked", "struck", "battered", "bonked")
+ w_class = 3
+ origin_tech = "materials=3;syndicate=2"
+ sharp = 0
+ edge = 0
+ hitsound = 'sound/weapons/genhit3.ogg'
diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm
index e90b8c3cf2b..5bb25e9e38a 100644
--- a/code/game/objects/items/weapons/storage/belt.dm
+++ b/code/game/objects/items/weapons/storage/belt.dm
@@ -198,7 +198,7 @@
/obj/item/device/flashlight,
/obj/item/device/pda,
/obj/item/device/radio/headset,
- /obj/item/weapon/melee/energy/sword,
+ /obj/item/weapon/melee,
/obj/item/weapon/shield/energy,
/obj/item/weapon/pinpointer,
/obj/item/weapon/plastique,
diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm
index fa008ca45f5..d544a5102e9 100644
--- a/code/game/objects/items/weapons/storage/boxes.dm
+++ b/code/game/objects/items/weapons/storage/boxes.dm
@@ -15,7 +15,8 @@
* Handcuff, mousetrap, and pillbottle boxes,
* Snap-pops and matchboxes,
* Replacement light boxes.
- *
+ * Kitchen utensil box
+ * Random preserved snack box
* For syndicate call-ins see uplink_kits.dm
*/
@@ -323,6 +324,7 @@
new /obj/item/weapon/grenade/flashbang(src)
new /obj/item/weapon/grenade/flashbang(src)
+
/obj/item/weapon/storage/box/teargas
name = "box of pepperspray grenades"
desc = "A box containing 7 tear gas grenades. A gas mask is printed on the label. WARNING: Exposure carries risk of serious injury or death. Keep away from persons with lung conditions."
@@ -337,7 +339,20 @@
new /obj/item/weapon/grenade/chem_grenade/teargas(src)
new /obj/item/weapon/grenade/chem_grenade/teargas(src)
+/obj/item/weapon/storage/box/smokebombs
+ name = "box of smoke grenades"
+ desc = "A box full of smoke grenades, used by special law enforcement teams and military organisations. Provides cover, confusion, and distraction."
+ icon_state = "flashbang"
+ New()
+ ..()
+ new /obj/item/weapon/grenade/smokebomb(src)
+ new /obj/item/weapon/grenade/smokebomb(src)
+ new /obj/item/weapon/grenade/smokebomb(src)
+ new /obj/item/weapon/grenade/smokebomb(src)
+ new /obj/item/weapon/grenade/smokebomb(src)
+ new /obj/item/weapon/grenade/smokebomb(src)
+ new /obj/item/weapon/grenade/smokebomb(src)
/obj/item/weapon/storage/box/emps
name = "box of emp grenades"
@@ -730,3 +745,57 @@
can_hold = list(/obj/item/organ, /obj/item/weapon/reagent_containers/food, /obj/item/weapon/reagent_containers/glass)
max_storage_space = 21
use_to_pickup = 1 // for picking up broken bulbs, not that most people will try
+
+/obj/item/weapon/storage/box/kitchen
+ name = "kitchen supplies"
+ desc = "Contains an assortment of utensils and containers useful in the preparation of food and drinks."
+
+/obj/item/weapon/storage/box/kitchen/New()
+ new /obj/item/weapon/material/knife(src)//Should always have a knife
+
+ var/list/utensils = list(/obj/item/weapon/material/kitchen/rollingpin,
+/obj/item/weapon/reagent_containers/glass/beaker,
+/obj/item/weapon/material/kitchen/utensil/fork,
+/obj/item/weapon/reagent_containers/food/condiment/enzyme,
+/obj/item/weapon/material/kitchen/utensil/spoon,
+/obj/item/weapon/material/kitchen/utensil/knife,
+/obj/item/weapon/reagent_containers/food/drinks/shaker)
+ for (var/i = 0,i<6,i++)
+ var/type = pick(utensils)
+ new type(src)
+ ..()
+
+
+
+/obj/item/weapon/storage/box/snack
+ name = "rations box"
+ desc = "Contains a random assortment of preserved foods. Guaranteed to remain edible* in room-temperature longterm storage for centuries!"
+
+/obj/item/weapon/storage/box/snack/New()
+ var/list/snacks = list(
+ /obj/item/weapon/reagent_containers/food/snacks/koisbar,
+ /obj/item/weapon/reagent_containers/food/snacks/candy,
+ /obj/item/weapon/reagent_containers/food/snacks/candy_corn,
+ /obj/item/weapon/reagent_containers/food/snacks/chips,
+ /obj/item/weapon/reagent_containers/food/snacks/chocolatebar,
+ /obj/item/weapon/reagent_containers/food/snacks/chocolateegg,
+ /obj/item/weapon/reagent_containers/food/snacks/popcorn,
+ /obj/item/weapon/reagent_containers/food/snacks/sosjerky,
+ /obj/item/weapon/reagent_containers/food/snacks/no_raisin,
+ /obj/item/weapon/reagent_containers/food/snacks/spacetwinkie,
+ /obj/item/weapon/reagent_containers/food/snacks/cheesiehonkers,
+ /obj/item/weapon/reagent_containers/food/snacks/syndicake,
+ /obj/item/weapon/reagent_containers/food/snacks/fortunecookie,
+ /obj/item/weapon/reagent_containers/food/snacks/poppypretzel,
+ /obj/item/weapon/reagent_containers/food/snacks/cracker,
+ /obj/item/weapon/reagent_containers/food/snacks/liquidfood,
+ /obj/item/weapon/reagent_containers/food/snacks/skrellsnacks,
+ /obj/item/weapon/reagent_containers/food/snacks/tastybread,
+ /obj/item/weapon/reagent_containers/food/snacks/meatsnack,
+ /obj/item/weapon/reagent_containers/food/snacks/maps,
+ /obj/item/weapon/reagent_containers/food/snacks/nathisnack,
+ )
+ for (var/i = 0,i<7,i++)
+ var/type = pick(snacks)
+ new type(src)
+ ..()
diff --git a/code/game/objects/items/weapons/storage/firstaid.dm b/code/game/objects/items/weapons/storage/firstaid.dm
index 40a05659ec1..d0a7bafa366 100644
--- a/code/game/objects/items/weapons/storage/firstaid.dm
+++ b/code/game/objects/items/weapons/storage/firstaid.dm
@@ -131,6 +131,8 @@
/obj/item/weapon/storage/firstaid/surgery
name = "surgery kit"
desc = "Contains tools for surgery. Has precise foam fitting for safe transport."
+ icon_state = "purplefirstaid"
+ item_state = "firstaid-advanced"
/obj/item/weapon/storage/firstaid/surgery/New()
..()
diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm
index 866c2680fc3..ff9193e0862 100644
--- a/code/game/objects/items/weapons/storage/storage.dm
+++ b/code/game/objects/items/weapons/storage/storage.dm
@@ -579,13 +579,13 @@
max_storage_space += I.get_storage_cost()
//Useful for spilling the contents of containers all over the floor
-/obj/item/weapon/storage/proc/spill()
- if (istype(loc, /turf))//If its not on the floor this might cause issues
- var/turf/T = get_turf(src)
- for (var/obj/O in contents)
- contents.Remove(O)
- O.forceMove(T)
- O.tumble(2)
+/obj/item/weapon/storage/proc/spill(var/dist = 2, var/turf/T = null)
+ if (!T || !istype(T, /turf))//If its not on the floor this might cause issues
+ T = get_turf(src)
+
+ for (var/obj/O in contents)
+ remove_from_storage(O, T)
+ O.tumble(2)
//Returns the storage depth of an atom. This is the number of storage items the atom is contained in before reaching toplevel (the area).
diff --git a/code/game/objects/items/weapons/storage/toolbox.dm b/code/game/objects/items/weapons/storage/toolbox.dm
index 11dcdc8879a..c558b933601 100644
--- a/code/game/objects/items/weapons/storage/toolbox.dm
+++ b/code/game/objects/items/weapons/storage/toolbox.dm
@@ -14,6 +14,11 @@
max_storage_space = 14 //enough to hold all starting contents
origin_tech = list(TECH_COMBAT = 1)
attack_verb = list("robusted")
+ var/stunhit = 0
+
+/obj/item/weapon/storage/toolbox/initialize()
+ spawn(3)
+ update_force()
/obj/item/weapon/storage/toolbox/emergency
name = "emergency toolbox"
@@ -79,3 +84,24 @@
new /obj/item/weapon/crowbar(src)
new /obj/item/weapon/wirecutters(src)
new /obj/item/device/multitool(src)
+
+
+/obj/item/weapon/storage/toolbox/proc/update_force()
+ force = initial(force)
+ for (var/obj/item/I in contents)
+ force += I.w_class*1.5
+
+/obj/item/weapon/storage/toolbox/handle_item_insertion(obj/item/W as obj, prevent_warning = 0)
+ if (..(W, prevent_warning))
+ update_force()
+
+
+/obj/item/weapon/storage/toolbox/attack(mob/living/M as mob, mob/user as mob)
+ update_force()
+ ..(M, user)
+ if (contents.len)
+ spill(3, get_turf(M))
+ playsound(M, 'sound/items/trayhit2.ogg', 100, 1) //sound playin' again
+ update_force()
+ user.visible_message(span("danger", "[user] smashes the [src] into [M], causing it to break open and strew its contents across the area"))
+
diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm
index fcba152247e..ba3711fb8b4 100644
--- a/code/game/objects/items/weapons/stunbaton.dm
+++ b/code/game/objects/items/weapons/stunbaton.dm
@@ -184,3 +184,32 @@
slot_flags = null
baton_color = "#FFDF00"
+/obj/item/weapon/melee/baton/stunrod
+ name = "stunrod"
+ desc = "A more-than-lethal weapon used to deal with high threat situations."
+ icon = 'icons/obj/stunrod.dmi'
+ icon_state = "stunrod"
+ item_state = "stunrod"
+ force = 20
+ baton_color = "#75ACFF"
+ origin_tech = "combat=4,illegal=2"
+ contained_sprite = 1
+
+/obj/item/weapon/melee/baton/stunrod/New()
+ ..()
+ bcell = new/obj/item/weapon/cell/high(src)
+ update_icon()
+ return
+
+/obj/item/weapon/melee/baton/stunrod/update_icon() //this is needed due to how contained sprites work
+ if(status)
+ icon_state = "[initial(name)]_active"
+ item_state = "[initial(name)]_active"
+ else if(!bcell)
+ icon_state = "[initial(name)]_nocell"
+ item_state = "[initial(name)]"
+ else
+ icon_state = "[initial(name)]"
+ item_state = "[initial(name)]"
+
+ ..()
diff --git a/code/game/objects/items/weapons/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm
index ed9edb586b5..888dba02b99 100644
--- a/code/game/objects/items/weapons/swords_axes_etc.dm
+++ b/code/game/objects/items/weapons/swords_axes_etc.dm
@@ -11,7 +11,7 @@
M << " You have been banned FOR NO REISIN by [user]"
user << " You have BANNED [M]"
playsound(loc, 'sound/effects/adminhelp.ogg', 15)
-
+
/*
* Classic Baton
*/
@@ -79,14 +79,14 @@
add_fingerprint(user)
if(blood_overlay && blood_DNA && (blood_DNA.len >= 1)) //updates blood overlay, if any
- overlays.Cut()//this might delete other item overlays as well but eeeeeeeh
-
+ overlays.Remove(blood_overlay)
var/icon/I = new /icon(src.icon, src.icon_state)
I.Blend(new /icon('icons/effects/blood.dmi', rgb(255,255,255)),ICON_ADD)
I.Blend(new /icon('icons/effects/blood.dmi', "itemblood"),ICON_MULTIPLY)
- blood_overlay = I
-
+ blood_overlay = image(I)
+ blood_overlay.color = blood_color
overlays += blood_overlay
+ update_icon()
return
@@ -101,8 +101,10 @@
else
user.take_organ_damage(2*force)
return
- if(..())
- //playsound(src.loc, "swing_hit", 50, 1, -1)
+ if(..() == 1)
+ playsound(src.loc, "swing_hit", 50, 1, -1)
+ if(user.zone_sel.selecting == "r_leg" || user.zone_sel.selecting == "l_leg")
+ target.Weaken(5) //nerfed, because yes.
return
else
return ..()
diff --git a/code/game/objects/items/weapons/tools.dm b/code/game/objects/items/weapons/tools.dm
index 3f52251b582..1dd13fb75cf 100644
--- a/code/game/objects/items/weapons/tools.dm
+++ b/code/game/objects/items/weapons/tools.dm
@@ -303,15 +303,24 @@
var/mob/living/carbon/human/H = M
if(H.species.flags & IS_SYNTHETIC)
if(M == user)
- user << "\red You can't repair damage to your own body - it's against OH&S."
+ user << "You can't repair damage to your own body - it's against OH&S."
return
-
- if(S.brute_dam)
- S.heal_damage(15,0,0,1)
- user.visible_message("\red \The [user] patches some dents on \the [M]'s [S.name] with \the [src].")
+ if(S.brute_dam == 0)
+ // Organ undamaged
+ user << "Nothing to fix here!"
return
+ if (!src.welding)
+ // Welder is switched off!
+ user << "You need to light the welding tool, first!"
+ return
+ if (src.remove_fuel(0))
+ // Use a bit of fuel and repair
+ S.heal_damage(15,0,0,1)
+ user.visible_message("\The [user] patches some dents on \the [M]'s [S.name] with \the [src].")
else
- user << "Nothing to fix!"
+ // Welding tool is out of fuel
+ user << "Need more welding fuel!"
+ return
else
return ..()
diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm
index baf8d0bb667..6721a76e61e 100644
--- a/code/game/objects/items/weapons/weaponry.dm
+++ b/code/game/objects/items/weapons/weaponry.dm
@@ -214,3 +214,18 @@
health -= W.force
healthcheck()
..()
+
+/obj/item/weapon/canesword
+ name = "thin sword"
+ desc = "A thin, sharp blade with an elegant handle."
+ icon = 'icons/obj/sword.dmi'
+ icon_state = "canesword"
+ item_state = "canesword"
+ force = 20
+ throwforce = 5
+ w_class = 4
+ sharp = 1
+ edge = 1
+ attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ contained_sprite = 1
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 2e6a308ece0..9fe43ba1087 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -23,6 +23,7 @@
var/list/icon_supported_species_tags //Used with icon_auto_adapt, a list of species which have differing appearances for this item
var/icon_species_in_hand = 0//If 1, we will use the species tag even for rendering this item in the left/right hand.
+ var/equip_slot = 0
/obj/Destroy()
processing_objects -= src
return ..()
diff --git a/code/game/objects/random/random.dm b/code/game/objects/random/random.dm
index b30774c7160..cd739b769d0 100644
--- a/code/game/objects/random/random.dm
+++ b/code/game/objects/random/random.dm
@@ -102,7 +102,7 @@
desc = "This is a random piece of technology supplies."
icon = 'icons/obj/power.dmi'
icon_state = "cell"
- spawn_nothing_percentage = 50
+// spawn_nothing_percentage = 50
item_to_spawn()
return pick(prob(3);/obj/random/powercell,\
prob(2);/obj/random/technology_scanner,\
@@ -157,7 +157,7 @@
desc = "Hot Stuff."
icon = 'icons/obj/items.dmi'
icon_state = "purplecomb"
- spawn_nothing_percentage = 50
+// spawn_nothing_percentage = 50
item_to_spawn()
return pick(prob(3);/obj/item/weapon/storage/pill_bottle/tramadol,\
prob(4);/obj/item/weapon/haircomb,\
@@ -280,3 +280,414 @@
/obj/item/toy/plushie/kitten,\
/obj/item/toy/plushie/lizard)
+/obj/random/smalltank/item_to_spawn()
+ if (prob(40))
+ return /obj/item/weapon/tank/emergency_oxygen
+ else if (prob(60))
+ return /obj/item/weapon/tank/emergency_oxygen/engi
+ else
+ return /obj/item/weapon/tank/emergency_oxygen/double
+
+/obj/random/belt/item_to_spawn()
+ var/list/belts = list("/obj/item/weapon/storage/belt/utility" = 1,
+ "/obj/item/weapon/storage/belt/medical" = 0.4,
+ "/obj/item/weapon/storage/belt/medical/emt" = 0.4,
+ "/obj/item/weapon/storage/belt/security/tactical" = 0.1,
+ "/obj/item/weapon/storage/belt/military" = 0.1,
+ "/obj/item/weapon/storage/belt/janitor" = 0.4
+ )
+ return pickweight(belts)
+
+
+//Spawns a random backpack
+//Novelty and rare backpacks have lower weights
+/obj/random/backpack/item_to_spawn()
+ var/list/packs = list(
+ "/obj/item/weapon/storage/backpack" = 3,
+ "/obj/item/weapon/storage/backpack/holding" = 0.5,
+ "/obj/item/weapon/storage/backpack/santabag" = 2,
+ "/obj/item/weapon/storage/backpack/cultpack" = 2,
+ "/obj/item/weapon/storage/backpack/clown" = 2,
+ "/obj/item/weapon/storage/backpack/medic" = 3,
+ "/obj/item/weapon/storage/backpack/security" = 3,
+ "/obj/item/weapon/storage/backpack/captain" = 2,
+ "/obj/item/weapon/storage/backpack/industrial" = 3,
+ "/obj/item/weapon/storage/backpack/toxins" = 3,
+ "/obj/item/weapon/storage/backpack/hydroponics" = 3,
+ "/obj/item/weapon/storage/backpack/genetics" = 3,
+ "/obj/item/weapon/storage/backpack/virology" = 3,
+ "/obj/item/weapon/storage/backpack/chemistry" = 3,
+ "/obj/item/weapon/storage/backpack/cloak" = 2,
+ "/obj/item/weapon/storage/backpack/syndie" = 2,
+ "/obj/item/weapon/storage/backpack/wizard" = 2,
+ "/obj/item/weapon/storage/backpack/satchel" = 3,
+ "/obj/item/weapon/storage/backpack/satchel_norm" = 3,
+ "/obj/item/weapon/storage/backpack/satchel_eng" = 3,
+ "/obj/item/weapon/storage/backpack/satchel_med" = 3,
+ "/obj/item/weapon/storage/backpack/satchel_vir" = 3,
+ "/obj/item/weapon/storage/backpack/satchel_chem" = 3,
+ "/obj/item/weapon/storage/backpack/satchel_gen" = 3,
+ "/obj/item/weapon/storage/backpack/satchel_tox" = 3,
+ "/obj/item/weapon/storage/backpack/satchel_sec" = 3,
+ "/obj/item/weapon/storage/backpack/satchel_hyd" = 3,
+ "/obj/item/weapon/storage/backpack/satchel_cap" = 2,
+ "/obj/item/weapon/storage/backpack/satchel_syndie" = 2,
+ "/obj/item/weapon/storage/backpack/satchel_wizard" = 2,
+ "/obj/item/weapon/storage/backpack/ert" = 2,
+ "/obj/item/weapon/storage/backpack/ert/security" = 2,
+ "/obj/item/weapon/storage/backpack/ert/engineer" = 2,
+ "/obj/item/weapon/storage/backpack/ert/medical" = 2,
+ "/obj/item/weapon/storage/backpack/duffel" = 3,
+ "/obj/item/weapon/storage/backpack/duffel/cap" = 2,
+ "/obj/item/weapon/storage/backpack/duffel/hyd" = 3,
+ "/obj/item/weapon/storage/backpack/duffel/vir" = 3,
+ "/obj/item/weapon/storage/backpack/duffel/med" = 3,
+ "/obj/item/weapon/storage/backpack/duffel/eng" = 3,
+ "/obj/item/weapon/storage/backpack/duffel/tox" = 3,
+ "/obj/item/weapon/storage/backpack/duffel/sec" = 3,
+ "/obj/item/weapon/storage/backpack/duffel/gen" = 3,
+ "/obj/item/weapon/storage/backpack/duffel/chem" = 3,
+ "/obj/item/weapon/storage/backpack/duffel/syndie" = 2,
+ "/obj/item/weapon/storage/backpack/duffel/wizard" = 2
+ )
+ return pickweight(packs)
+
+
+/obj/random/voidsuit
+ var/damaged = 0
+
+/obj/random/voidsuit/New(var/_damaged = 0)
+ damaged = _damaged
+ ..()
+
+/obj/random/voidsuit/spawn_item()
+ var/list/suit_types = list(
+ "/space/void" = 2,
+ "/space/void/engineering" = 2,
+ "/space/void/mining" = 2,
+ "/space/void/medical" = 2.3,
+ "/space/void/security" = 1,
+ "/space/void/atmos" = 1.5,
+ "/space/void/merc" = 0.5,
+ "/space/void/captain" = 0.3
+ )
+ var/atom/L = src.loc
+ var/suffix = pickweight(suit_types)
+
+ var/stype = "/obj/item/clothing/suit[suffix]"
+ var/htype = "/obj/item/clothing/head/helmet[suffix]"
+ var/obj/item/clothing/suit/space/newsuit = new stype(L)
+ new htype(L)
+ new /obj/item/clothing/shoes/magboots(L)
+ if (damaged && prob(60))//put some damage on it
+ var/damtype = pick(BRUTE,BURN)
+ var/amount = rand(1,5)
+ newsuit.create_breaches(damtype, amount)
+
+/obj/random/vendor
+ var/depleted = 0
+
+/obj/random/vendor/New(var/_depleted = 0)
+ depleted = _depleted
+ ..()
+
+/obj/random/vendor/spawn_item()
+ var/list/options = list(
+ "/obj/machinery/vending/boozeomat" = 1,
+ "/obj/machinery/vending/coffee" = 1,
+ "/obj/machinery/vending/snack" = 1,
+ "/obj/machinery/vending/cola" = 1,
+ "/obj/machinery/vending/cart" = 1.5,
+ "/obj/machinery/vending/cigarette" = 1,
+ "/obj/machinery/vending/medical" = 1.2,
+ "/obj/machinery/vending/phoronresearch" = 0.7,
+ "/obj/machinery/vending/security" = 0.3,
+ "/obj/machinery/vending/hydronutrients" = 1,
+ "/obj/machinery/vending/hydroseeds" = 1,
+ "/obj/machinery/vending/magivend" = 0.5,//The things it dispenses are just costumes to non-wizards
+ "/obj/machinery/vending/dinnerware" = 1,
+ "/obj/machinery/vending/sovietsoda" = 2,
+ "/obj/machinery/vending/tool" = 1,
+ "/obj/machinery/vending/engivend" = 0.6,
+ "/obj/machinery/vending/engineering" = 1,
+ "/obj/machinery/vending/robotics" = 1
+ )
+ var/turf/L = get_turf(src)
+ var/type = pickweight(options)
+ var/obj/machinery/vending/V = new type(L)
+
+ if (!depleted)
+ return
+
+ //Greatly reduce the contents. it will have 0-20% of what it usually has
+ for (var/content in V.products)
+ if (prob(40))
+ V.products[content] = 0//40% chance to completely lose an item
+ else
+ var/multiplier = rand(0,20)//Else, we reduce it to a very low percentage
+ if (multiplier)
+ multiplier /= 100
+
+ V.products[content] *= multiplier
+ if (V.products[content] < 1 && V.products[content] > 0)//But we'll usually have at least 1 left
+ V.products[content] = 0
+
+
+/obj/random/pda_cart/item_to_spawn()
+ var/list/options = typesof(/obj/item/weapon/cartridge)
+ var/type = pick(options)
+
+ //reroll syndicate cartridge once to make it less common
+ if (type == /obj/item/weapon/cartridge/syndicate)
+ type = pick(options)
+
+ return type
+
+/obj/random/glowstick
+ name = "random glowstick"
+ desc = "This is a random glowstick."
+ icon = 'icons/obj/glowsticks.dmi'
+ icon_state = "glowstick"
+
+/obj/random/glowstick/item_to_spawn()
+ return pick(/obj/item/device/flashlight/glowstick,\
+ /obj/item/device/flashlight/glowstick/red,\
+ /obj/item/device/flashlight/glowstick/blue,\
+ /obj/item/device/flashlight/glowstick/orange,\
+ /obj/item/device/flashlight/glowstick/yellow)
+
+/obj/random/booze
+ name = "random alcoholic drink"
+ desc = "This is a random alcoholic drink."
+ icon = 'icons/obj/drinks.dmi'
+ icon_state = "broken_bottle"
+
+/obj/random/booze/item_to_spawn()
+ return pick(/obj/item/weapon/reagent_containers/food/drinks/bottle/gin,\
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/whiskey,\
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/vodka,\
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/tequilla,\
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/bottleofnothing,\
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/rum,\
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/vermouth,\
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/kahlua,\
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/cognac,\
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/wine,\
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/absinthe,\
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/melonliquor,\
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/pwine,\
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/brandy,\
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/guinnes,\
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/drambuie,\
+ /obj/item/weapon/reagent_containers/food/drinks/cans/beer,\
+ /obj/item/weapon/reagent_containers/food/drinks/cans/ale)
+
+/obj/random/melee
+ name = "random melee weapon"
+ desc = "This is a random melee weapon."
+ icon = 'icons/obj/weapons.dmi'
+ icon_state = "baton"
+
+/obj/random/melee/item_to_spawn()
+ return pick(/obj/item/weapon/melee/telebaton,\
+ /obj/item/weapon/melee/energy/sword,\
+ /obj/item/weapon/melee/energy/sword/pirate,\
+ /obj/item/weapon/melee/energy/glaive,\
+ /obj/item/weapon/melee/chainsword,\
+ /obj/item/weapon/melee/baton/stunrod,\
+ /obj/item/weapon/material/harpoon,\
+ /obj/item/weapon/material/scythe,\
+ /obj/item/weapon/material/twohanded/spear/plasteel,\
+ /obj/item/weapon/material/sword/trench,\
+ /obj/item/weapon/material/sword/rapier)
+
+/obj/random/coin
+ name = "random coin"
+ desc = "This is a random coin."
+ icon = 'icons/obj/items.dmi'
+ icon_state = "coin"
+
+/obj/random/coin/item_to_spawn()
+ var/list/coin = list("/obj/item/weapon/coin/iron" = 5,
+ "/obj/item/weapon/coin/silver" = 3,
+ "/obj/item/weapon/coin/gold" = 1,
+ "/obj/item/weapon/coin/silver" = 0.7,
+ "/obj/item/weapon/coin/phoron" = 0.5,
+ "obj/item/weapon/coin/uranium" = 0.5,
+ "/obj/item/weapon/coin/platinum" = 0.2,
+ "/obj/item/weapon/coin/diamond" = 0.1,
+ )
+ return pickweight(coin)
+
+/obj/random/energy_antag
+ name = "random energy weapon"
+ desc = "This is a random energy weapon."
+ icon = 'icons/obj/gun.dmi'
+ icon_state = "retro100"
+
+/obj/random/energy_antag/item_to_spawn()
+
+ return pick(/obj/item/weapon/gun/energy/retro,\
+ /obj/item/weapon/gun/energy/xray,\
+ /obj/item/weapon/gun/energy/gun,\
+ /obj/item/weapon/gun/energy/pistol,\
+ /obj/item/weapon/gun/energy/rifle,\
+ /obj/item/weapon/gun/energy/mindflayer,\
+ /obj/item/weapon/gun/energy/toxgun,\
+ /obj/item/weapon/gun/energy/vaurca/gatlinglaser,\
+ /obj/item/weapon/gun/energy/vaurca/blaster,\
+ /obj/item/weapon/gun/energy/crossbow/largecrossbow)
+
+/obj/random/colored_jumpsuit
+ name = "random colored jumpsuit"
+ desc = "This is a random colowerd jumpsuit."
+ icon = 'icons/obj/clothing/uniforms.dmi'
+ icon_state = "black"
+
+/obj/random/colored_jumpsuit/item_to_spawn()
+
+ return pick(/obj/item/clothing/under/color/black,\
+ /obj/item/clothing/under/color/blackf,\
+ /obj/item/clothing/under/color/blue,\
+ /obj/item/clothing/under/color/green,\
+ /obj/item/clothing/under/color/grey,\
+ /obj/item/clothing/under/color/orange,\
+ /obj/item/clothing/under/color/pink,\
+ /obj/item/clothing/under/color/red,\
+ /obj/item/clothing/under/color/white,\
+ /obj/item/clothing/under/color/yellow,\
+ /obj/item/clothing/under/lightblue,\
+ /obj/item/clothing/under/aqua,\
+ /obj/item/clothing/under/purple,\
+ /obj/item/clothing/under/lightpurple,\
+ /obj/item/clothing/under/lightgreen,\
+ /obj/item/clothing/under/lightbrown,\
+ /obj/item/clothing/under/brown,\
+ /obj/item/clothing/under/yellowgreen,\
+ /obj/item/clothing/under/darkblue,\
+ /obj/item/clothing/under/lightred,\
+ /obj/item/clothing/under/darkred)
+
+/obj/random/loot
+ name = "random maintenance loot items"
+ desc = "Stuff for the maint-dwellers."
+ icon = 'icons/obj/items.dmi'
+ icon_state = "gift3"
+
+/obj/random/loot/item_to_spawn()
+
+ var/list/maint = list("/obj/item/clothing/glasses/meson" = 1,
+ "/obj/item/clothing/glasses/meson/prescription" = 0.7,
+ "/obj/item/clothing/glasses/material" = 0.8,
+ "/obj/item/clothing/glasses/sunglasses" = 1.5,
+ "/obj/item/clothing/glasses/welding" = 1.2,
+ "/obj/item/clothing/under/captain_fly" = 0.7,
+ "/obj/item/clothing/under/rank/mailman" = 0.6,
+ "/obj/item/clothing/under/rank/vice" = 0.8,
+ "/obj/item/clothing/under/assistantformal" = 1,
+ "/obj/item/clothing/under/rainbow" = 0.9,
+ "/obj/item/clothing/under/overalls" = 1,
+ "/obj/item/clothing/under/redcoat" = 0.5,
+ "/obj/item/clothing/under/serviceoveralls" = 1,
+ "/obj/item/clothing/under/psyche" = 0.5,
+ "/obj/item/clothing/under/track" = 0.9,
+ "/obj/item/clothing/under/rank/dispatch" = 1,
+ "/obj/item/clothing/under/syndicate/tacticool" = 1,
+ "/obj/item/clothing/under/syndicate/tracksuit" = 0.2,
+ "/obj/item/clothing/accessory/badge" = 0.2,
+ "/obj/item/clothing/accessory/badge/old" = 0.2,
+ "/obj/item/clothing/accessory/storage/webbing" = 0.6,
+ "/obj/item/clothing/accessory/storage/knifeharness" = 0.3,
+ "/obj/item/clothing/head/collectable/petehat" = 0.3,
+ "/obj/item/clothing/head/hardhat" = 1.2,
+ "/obj/item/clothing/head/redcoat" = 0.4,
+ "/obj/item/clothing/head/syndicatefake" = 0.5,
+ "/obj/item/clothing/head/richard" = 0.3,
+ "/obj/item/clothing/head/soft/rainbow" = 0.7,
+ "/obj/item/clothing/head/plaguedoctorhat" = 0.5,
+ "/obj/item/clothing/head/cueball" = 0.5,
+ "/obj/item/clothing/head/pirate" = 0.4,
+ "/obj/item/clothing/head/bearpelt" = 0.4,
+ "/obj/item/clothing/head/witchwig" = 0.5,
+ "/obj/item/clothing/head/pumpkinhead" = 0.6,
+ "/obj/item/clothing/head/kitty" = 0.2,
+ "/obj/item/clothing/head/ushanka" = 0.6,
+ "/obj/item/clothing/head/helmet/augment" = 0.1,
+ "/obj/item/clothing/mask/balaclava" = 1,
+ "/obj/item/clothing/mask/gas" = 1.5,
+ "/obj/item/clothing/mask/gas/cyborg" = 0.7,
+ "/obj/item/clothing/mask/gas/owl_mask" = 0.8,
+ "/obj/item/clothing/mask/gas/syndicate" = 0.4,
+ "/obj/item/clothing/mask/fakemoustache" = 0.4,
+ "/obj/item/clothing/mask/horsehead" = 0.9,
+ "/obj/item/clothing/shoes/rainbow" = 1,
+ "/obj/item/clothing/shoes/jackboots" = 1,
+ "/obj/item/clothing/shoes/workboots" = 1,
+ "/obj/item/clothing/shoes/cyborg" = 0.4,
+ "/obj/item/clothing/shoes/galoshes" = 0.6,
+ "/obj/item/clothing/shoes/slippers_worn" = 0.5,
+ "/obj/item/clothing/shoes/combat" = 0.2,
+ "/obj/item/clothing/shoes/clown_shoes" = 0.1,
+ "/obj/item/clothing/suit/storage/hazardvest" = 1,
+ "/obj/item/clothing/suit/storage/leather_jacket/nanotrasen" = 0.7,
+ "/obj/item/clothing/suit/storage/toggle/tracksuit" = 0.7,
+ "/obj/item/clothing/suit/ianshirt" = 0.5,
+ "/obj/item/clothing/suit/syndicatefake" = 0.6,
+ "/obj/item/clothing/suit/imperium_monk" = 0.4,
+ "/obj/item/clothing/suit/storage/vest" = 0.2,
+ "/obj/item/clothing/gloves/black" = 1,
+ "/obj/item/clothing/gloves/fyellow" = 1.2,
+ "/obj/item/clothing/gloves/yellow" = 0.9,
+ "/obj/item/clothing/gloves/watch" = 0.3,
+ "/obj/item/clothing/gloves/boxing" = 0.3,
+ "/obj/item/clothing/gloves/boxing/green" = 0.8,
+ "/obj/item/clothing/gloves/botanic_leather" = 0.7,
+ "/obj/item/clothing/gloves/combat" = 0.2,
+ "/obj/item/toy/bosunwhistle" = 0.5,
+ "/obj/item/toy/balloon" = 0.4,
+ "/obj/item/weapon/haircomb" = 0.5,
+ "/obj/item/weapon/lipstick" = 0.6,
+ "/obj/item/weapon/material/knife/hook" = 0.3,
+ "/obj/item/weapon/material/hatchet/tacknife" = 0.4,
+ "/obj/item/weapon/storage/fancy/cigarettes/dromedaryco" = 1.2,
+ "/obj/item/weapon/storage/bag/plasticbag" = 1,
+ "/obj/item/weapon/extinguisher" = 1.3,
+ "/obj/item/weapon/extinguisher/mini" = 0.9,
+ "/obj/item/device/flashlight" = 1,
+ "/obj/item/device/flashlight/heavy" = 0.5,
+ "/obj/item/device/flashlight/maglight" = 0.4,
+ "/obj/item/device/flashlight/flare" = 0.5,
+ "/obj/item/device/flashlight/lantern" = 0.4,
+ "/obj/item/weapon/reagent_containers/food/drinks/teapot" = 0.4,
+ "/obj/item/weapon/reagent_containers/food/drinks/flask/shiny" = 0.3,
+ "/obj/item/weapon/reagent_containers/food/drinks/flask/lithium" = 0.3,
+ "/obj/item/bodybag" = 0.7,
+ "/obj/item/weapon/reagent_containers/spray/cleaner" = 0.6,
+ "/obj/item/weapon/tank/emergency_oxygen" = 0.7,
+ "/obj/item/weapon/tank/emergency_oxygen/double" = 0.4,
+ "/obj/item/clothing/mask/smokable/pipe/cobpipe" = 0.5,
+ "/obj/item/clothing/mask/smokable/cigarette/cigar/cohiba" = 0.7,
+ "/obj/item/weapon/flame/lighter" = 0.9,
+ "/obj/item/weapon/flame/lighter/zippo" = 0.7,
+ "/obj/item/device/gps/engineering" = 0.6,
+ "/obj/item/device/megaphone" = 0.5,
+ "/obj/item/device/floor_painter" = 0.6,
+ "/obj/random/toolbox" = 1,
+ "/obj/random/coin" = 1.2,
+ "/obj/random/tech_supply" = 1.2,
+ "/obj/random/powercell" = 0.8,
+ "/obj/random/colored_jumpsuit" = 0.7,
+ "/obj/random/booze" = 1.1,
+ "/obj/random/belt" = 0.9,
+ "/obj/random/contraband" = 0.9,
+ "/obj/random/glowstick" = 0.7,
+ "/obj/item/weapon/caution/cone" = 0.7,
+ "/obj/item/weapon/staff/broom" = 0.5,
+ "/obj/item/weapon/soap" = 0.4,
+ "/obj/item/weapon/material/wirerod" = 0.4,
+ "/obj/item/weapon/storage/box/donkpockets" = 0.6,
+ "/obj/item/weapon/contraband/poster" = 1.3,
+ "/obj/item/device/magnetic_lock/security" = 0.3,
+ "/obj/item/device/magnetic_lock/engineering" = 0.3
+ )
+ return pickweight(maint)
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index 0fc33344b80..5faa4c70530 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -46,6 +46,7 @@
/obj/structure/New()
..()
+ updateVisibility(src)
if(climbable)
verbs += /obj/structure/proc/climb_on
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 0418a8092b9..e0555530127 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -55,7 +55,12 @@
else
user << "It is full."
-
+/obj/structure/closet/proc/stored_weight()
+ var/content_size = 0
+ for(var/obj/item/I in src.contents)
+ if(!I.anchored)
+ content_size += Ceiling(I.w_class/2)
+ return content_size
/obj/structure/closet/alter_health()
return get_turf(src)
diff --git a/code/game/objects/structures/crates_lockers/closets/gimmick.dm b/code/game/objects/structures/crates_lockers/closets/gimmick.dm
index 1a62db2dfbf..44e173cb895 100644
--- a/code/game/objects/structures/crates_lockers/closets/gimmick.dm
+++ b/code/game/objects/structures/crates_lockers/closets/gimmick.dm
@@ -4,6 +4,7 @@
icon_state = "cabinet_closed"
icon_closed = "cabinet_closed"
icon_opened = "cabinet_open"
+ storage_capacity = 45 //such a big closet deserves a little more capacity
/obj/structure/closet/cabinet/update_icon()
if(!opened)
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/bar.dm b/code/game/objects/structures/crates_lockers/closets/secure/bar.dm
index 5fd5cfd3147..37ec5a4a51f 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/bar.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/bar.dm
@@ -7,6 +7,7 @@
icon_opened = "cabinetdetective_open"
icon_broken = "cabinetdetective_broken"
icon_off = "cabinetdetective_broken"
+ storage_capacity = 45 //such a big closet deserves a little more capacity
New()
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
index b65018356eb..1a8b64ad2d7 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
@@ -249,20 +249,22 @@
icon_opened = "cabinetdetective_open"
icon_broken = "cabinetdetective_broken"
icon_off = "cabinetdetective_broken"
+ storage_capacity = 45 //such a big closet deserves a little more capacity
New()
..()
new /obj/item/clothing/under/det(src)
new /obj/item/clothing/under/det/grey(src)
new /obj/item/clothing/under/det/black(src)
- new /obj/item/clothing/suit/storage/det_trench(src)
- new /obj/item/clothing/suit/storage/det_trench/grey(src)
+ new /obj/item/clothing/under/det/slob(src)
+ new /obj/item/clothing/suit/storage/det_suit(src)
+ new /obj/item/clothing/suit/storage/det_suit/technicolor(src)
new /obj/item/clothing/suit/storage/forensics/blue(src)
new /obj/item/clothing/suit/storage/forensics/red(src)
new /obj/item/clothing/gloves/black(src)
- new /obj/item/clothing/head/det(src)
- new /obj/item/clothing/head/det/grey(src)
- new /obj/item/clothing/shoes/laceup(src)
+ new /obj/item/clothing/head/det_hat(src)
+ new /obj/item/clothing/head/det_hat/technicolor(src)
+ new /obj/item/clothing/shoes/brown(src)
new /obj/item/weapon/storage/box/evidence(src)
new /obj/item/device/radio/headset/headset_sec(src)
new /obj/item/clothing/suit/storage/vest/detective(src)
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index 7d64d26b703..cb34fc25ba3 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -36,11 +36,20 @@
playsound(src.loc, 'sound/machines/click.ogg', 15, 1, -3)
for(var/obj/O in src)
O.forceMove(get_turf(src))
- icon_state = icon_opened
- src.opened = 1
if(climbable)
structure_shaken()
+
+ for (var/mob/M in src)
+ M.forceMove(get_turf(src))
+ if (M.stat == CONSCIOUS)
+ M.visible_message(span("danger","\The [M.name] bursts out of the [src]!"), span("danger","You burst out of the [src]!"))
+ else
+ M.visible_message(span("danger","\The [M.name] tumbles out of the [src]!"))
+
+ icon_state = icon_opened
+ src.opened = 1
+
return 1
/obj/structure/closet/crate/close()
@@ -67,6 +76,14 @@
src.opened = 0
return 1
+
+/obj/structure/closet/crate/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(istype(mover,/obj/item/projectile) && prob(50))
+ return 1
+ if(istype(mover) && mover.checkpass(PASSTABLE))
+ return 1
+ return 0
+
/obj/structure/closet/crate/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(opened)
return ..()
@@ -496,3 +513,60 @@
// new /obj/item/weapon/pestspray(src)
// new /obj/item/weapon/pestspray(src)
// new /obj/item/weapon/pestspray(src)
+
+
+
+//A crate that populates itself with randomly selected loot from randomstock.dm
+//Can be passed in a rarity value, which is used as a multiplier on the rare/uncommon chance
+//Quantity of spawns is number of discrete selections from the loot lists, default 10
+
+/obj/structure/closet/crate/loot
+ name = "unusual container"
+ desc = "A mysterious container of unknown origins. What mysteries lie within?"
+ var/rarity = 1
+ var/quantity = 10
+ var/list/spawntypes
+
+//The crate chooses its icon randomly from a number of noticeable options.
+//None of these are the standard grey crate sprite, and a few are currently unused ingame
+//This ensures that people stumbling across a lootbox will notice it's different and investigate
+ var/list/iconchoices = list(
+ "radiation" = "radiationopen",
+ "o2crate" = "o2crateopen",
+ "freezer" = "freezeropen",
+ "weaponcrate" = "weaponcrateopen",
+ "largebins" = "largebinsopen",
+ "phoroncrate" = "phoroncrateopen",
+ "trashcart" = "trashcartopen",
+ "critter" = "critteropen",
+ "largemetal" = "largemetalopen",
+ "medicalcrate" = "medicalcrateopen")
+
+
+/obj/structure/closet/crate/loot/New(var/location, var/_rarity = 1, var/_quantity = 10)
+
+ rarity = _rarity
+ quantity = _quantity
+ ..(location)
+
+
+/obj/structure/closet/crate/loot/initialize()
+ spawntypes = list("1" = STOCK_RARE_PROB*rarity, "2" = STOCK_UNCOMMON_PROB*rarity, "3" = (100 - ((STOCK_RARE_PROB*rarity) + (STOCK_UNCOMMON_PROB*rarity))))
+
+ icon_closed = pick(iconchoices)
+ icon_opened = iconchoices[icon_closed]
+ update_icon()
+ while (quantity > 0)
+ quantity --
+ var/newtype = get_spawntype()
+ spawn_stock(newtype,src)
+
+/obj/structure/closet/crate/loot/proc/get_spawntype()
+ var/stocktype = pickweight(spawntypes)
+ switch (stocktype)
+ if ("1")
+ return pickweight(random_stock_rare)
+ if ("2")
+ return pickweight(random_stock_uncommon)
+ if ("3")
+ return pickweight(random_stock_common)
\ No newline at end of file
diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm
index 71a87b18b4e..76719ebd45e 100644
--- a/code/game/objects/structures/flora.dm
+++ b/code/game/objects/structures/flora.dm
@@ -78,6 +78,15 @@
icon = 'icons/obj/plants.dmi'
icon_state = "plant-26"
+//Added random icon selection for potted plants.
+//It was silly they always used the same sprite when we have 26 sprites of them in the icon file
+/obj/structure/flora/pottedplant/random/New()
+ ..()
+ var/number = rand(1,26)
+ if (number < 10)
+ number = "0[number]"
+ icon_state = "plant-[number]"
+
//newbushes
/obj/structure/flora/ausbushes
diff --git a/code/game/objects/structures/signs.dm b/code/game/objects/structures/signs.dm
index 3638b88fd27..3ecfa6149df 100644
--- a/code/game/objects/structures/signs.dm
+++ b/code/game/objects/structures/signs.dm
@@ -23,21 +23,24 @@
/obj/structure/sign/attackby(obj/item/tool as obj, mob/user as mob) //deconstruction
if(istype(tool, /obj/item/weapon/screwdriver) && !istype(src, /obj/structure/sign/double))
user << "You unfasten the sign with your [tool]."
- var/obj/item/sign/S = new(src.loc)
- S.name = name
- S.desc = desc
- S.icon_state = icon_state
- //var/icon/I = icon('icons/obj/decals.dmi', icon_state)
- //S.icon = I.Scale(24, 24)
- S.sign_state = icon_state
- qdel(src)
+ unfasten()
else ..()
+/obj/structure/sign/proc/unfasten()
+ var/obj/item/sign/S = new(src.loc)
+ S.name = name
+ S.desc = desc
+ S.icon_state = icon_state
+ //var/icon/I = icon('icons/obj/decals.dmi', icon_state)
+ //S.icon = I.Scale(24, 24)
+ S.sign_state = icon_state
+ qdel(src)
+
/obj/item/sign
name = "sign"
desc = ""
icon = 'icons/obj/decals.dmi'
- w_class = 3 //big
+ w_class = 5 //big
var/sign_state = ""
/obj/item/sign/attackby(obj/item/tool as obj, mob/user as mob) //construction
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 24a594d52fc..b801121e62e 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -168,7 +168,7 @@
/obj/structure/window/attack_tk(mob/user as mob)
user.visible_message("Something knocks on [src].")
- playsound(loc, 'sound/effects/Glasshit.ogg', 50, 1)
+ playsound(loc, 'sound/effects/Glasshit.ogg', 60, 1)
/obj/structure/window/attack_hand(mob/user as mob)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
@@ -186,13 +186,13 @@
attack_generic(H,25)
return
- playsound(src.loc, 'sound/effects/glassknock.ogg', 80, 1)
+ playsound(src.loc, 'sound/effects/glassknock.ogg', 90, 1)
user.do_attack_animation(src)
usr.visible_message("\The [usr] bangs against \the [src]!",
"You bang against \the [src]!",
"You hear a banging sound.")
else
- playsound(src.loc, 'sound/effects/glassknock.ogg', 80, 1)
+ playsound(src.loc, 'sound/effects/glassknock.ogg', 60, 1)
usr.visible_message("[usr.name] knocks on the [src.name].",
"You knock on the [src.name].",
"You hear a knocking sound.")
@@ -207,6 +207,7 @@
take_damage(damage)
else
visible_message("\The [user] bonks \the [src] harmlessly.")
+ playsound(src.loc, 'sound/effects/Glasshit.ogg', 7, 1, -1)
user.do_attack_animation(src)
return 1
diff --git a/code/game/sound.dm b/code/game/sound.dm
index 8f27a6c471c..575df8ebe58 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -86,11 +86,11 @@ var/list/footstepfx = list("defaultstep","concretestep","grassstep","dirtstep","
var/const/FALLOFF_SOUNDS = 0.5
/mob/proc/playsound_local(var/turf/turf_source, soundin, vol as num, vary, frequency, falloff, is_global, var/usepressure = 1, var/environment = -1)
- if(!src.client || ear_deaf > 0) return
+ if(!src.client || ear_deaf > 0) return 0
if(soundin in footstepfx)
if(!(src.client.prefs.asfx_togs & ASFX_FOOTSTEPS))
- return
+ return 0
soundin = get_sfx(soundin)
@@ -120,7 +120,6 @@ var/const/FALLOFF_SOUNDS = 0.5
//This extra falloff should probably be rewritten or removed, but for now ive implemented a quick fix by only setting S.volume to vol after calculations are done
//This fix allows feeding in high volume values (>100) to make longrange sounds audible
// -Nanako
-
if (usepressure)
//sound volume falloff with pressure. Pass usepressure = 0 to disable these calculations
@@ -141,7 +140,7 @@ var/const/FALLOFF_SOUNDS = 0.5
vol *= pressure_factor
if (vol <= 0)
- return //no volume means no sound
+ return 0//no volume means no sound
S.volume = vol
var/dx = turf_source.x - T.x // Hearing from the right/left
@@ -179,6 +178,7 @@ var/const/FALLOFF_SOUNDS = 0.5
S.environment = A.sound_env
src << S
+ return S.volume
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 4523bbab66d..175344d9390 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -137,18 +137,20 @@ var/const/enterloopsanity = 100
if(M.lastarea.has_gravity == 0)
inertial_drift(M)
- var/mob/living/carbon/human/MOB = M
- if(istype(MOB) && !MOB.lying && footstep_sound)
- if(istype(MOB.shoes, /obj/item/clothing/shoes) && !MOB.shoes:silent)
- if(MOB.m_intent == "run")
- if(MOB.footstep >= 2)
- MOB.footstep = 0
- else
- MOB.footstep++
- if(MOB.footstep == 0)
- playsound(MOB, footstep_sound, 50, 1) // this will get annoying very fast. - Tell them to mute it then -_-
- else
- playsound(MOB, footstep_sound, 40, 1)
+ if (!M.buckled)
+ var/mob/living/carbon/human/MOB = M
+ if(istype(MOB) && !MOB.lying && footstep_sound)
+ if(istype(MOB.shoes, /obj/item/clothing/shoes) && !MOB.shoes:silent)
+ if(MOB.m_intent == "run")
+ playsound(MOB, footstep_sound, 70, 1)
+ else //Run and walk footsteps switched, because walk is the normal movement mode now
+ if(MOB.footstep >= 2)
+ MOB.footstep = 0
+ else
+ MOB.footstep++
+ if(MOB.footstep == 0)
+ playsound(MOB, footstep_sound, 40, 1)
+
else if(!istype(src, /turf/space))
@@ -247,7 +249,10 @@ var/const/enterloopsanity = 100
if(istype(O,/obj/effect/decal/cleanable) || istype(O,/obj/effect/overlay))
qdel(O)
if(istype(O,/obj/effect/rune))
- user << "\red No matter how well you wash, the bloody symbols remain!"
+ var/obj/effect/rune/R = O
+ // Only show message for visible runes
+ if (R.visibility)
+ user << "No matter how well you wash, the bloody symbols remain!"
else
user << "\The [source] is too dry to wash that."
source.reagents.trans_to_turf(src, 1, 10) //10 is the multiplier for the reaction effect. probably needed to wet the floor properly.
diff --git a/code/global.dm b/code/global.dm
index 049b886170e..1cca8def890 100644
--- a/code/global.dm
+++ b/code/global.dm
@@ -9,6 +9,7 @@ var/global/datum/datacore/data_core = null
var/global/list/all_areas = list()
var/global/list/machines = list()
var/global/list/processing_objects = list()
+var/global/list/processing_modifiers = list()
var/global/list/processing_power_items = list()
var/global/list/active_diseases = list()
var/global/list/med_hud_users = list() // List of all entities using a medical HUD.
diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm
index a5f9ea0e5af..19dec0a7d86 100644
--- a/code/modules/admin/verbs/pray.dm
+++ b/code/modules/admin/verbs/pray.dm
@@ -29,7 +29,7 @@
//log_admin("HELP: [key_name(src)]: [msg]")
/proc/Centcomm_announce(var/msg, var/mob/Sender, var/iamessage)
- discord_bot.send_to_cciaa("@here - Emergency message from the station: `[msg]`, sent by [Sender]!")
+ discord_bot.send_to_cciaa("Emergency message from the station: `[msg]`, sent by [Sender]!")
var/msg_cciaa = "\blue [uppertext(boss_short)][iamessage ? " IA" : ""]:[key_name(Sender, 1)] (RPLY): [msg]"
diff --git a/code/modules/cargo/randomstock.dm b/code/modules/cargo/randomstock.dm
new file mode 100644
index 00000000000..6a1cb57d114
--- /dev/null
+++ b/code/modules/cargo/randomstock.dm
@@ -0,0 +1,1889 @@
+//See Setup.dm for the configuration vars:
+//TOTAL_STOCK
+//STOCK_UNCOMMON_PROB
+//STOCK_RARE_PROB
+//STOCK_LARGE_PROB
+
+//Their values are set there
+
+/*
+At roundstart, some items will spawn in the cargo warehouse. MANY items in fact.
+
+Generally, 20-40 items of various sorts will spawn.
+
+Most of them (70-90%) will be chosen from the common items list. These items are designed to be fairly
+useful and likely to be helpful to the station. They will help cement cargo's supply role - as the
+place to go when you want to find things you lack.
+
+A smaller amount will be from the uncommon items. This is a combination of more powerful items, and
+useless but interesting curios. The uncommon category tends towards interestingness and niche use
+
+Rare items will spawn fairly infrequently, These are things that can have a significant
+effect on the round, and most of the spawnable weapons are in these categories. Many of the rare
+items are prime targets for an antag raiding cargo, or a desperate crew looking for things to fight with
+
+Large items are a more broadly weighted category with a variety of rare and common items. The main
+defining criteria of this category is that they are dense, and usually structures or machinery, and
+thus require a dedicated tile for themselves to spawn in.
+
+Many items which draw from random pools, like meds/aid/signs/figures/plushies/booze/etc,
+have been given higher probabilitiesthan those which simply spawn preset items. This encourages
+more variety, and keeps the odds of any one specific item to be about the same
+
+*/
+
+var/list/global/random_stock_common = list(
+ "toolbox" = 4,
+ "meds" = 5,
+ "steel" = 7,
+ "glass" = 2.5,
+ "wood" = 2,
+ "plastic" = 1.5,
+ "cardboard" = 1,
+ "lightreplacer" = 1,
+ "bodybag" = 2.2,
+ "lamp" = 2.4,
+ "mousetrap" = 2,
+ "donk" = 2,
+ "sterile" = 2,
+ "light" = 1.8,
+ "aid" = 4,
+ "flame" = 2,
+ "crayons" = 1.5,
+ "figure" = 1,
+ "bombsupply" = 4.5,
+ "tech" = 5,
+ "smokes" = 2,
+ "vials" = 2,
+ "smallcell" = 4,
+ "robolimb" = 2.5,
+ "circuitboard" = 2,
+ "smalloxy" = 3.2,
+ "belts" = 2,
+ "backpack" = 4.5,
+ "weldgear" = 2,
+ "inflatable" = 3,
+ "wheelchair" = 1,
+ "meson" = 1.5,
+ "beartrap" = 2,
+ "trays" = 1.8,
+ "utensil" = 2,
+ "metalfoam" = 1.5,
+ "nanopaste" = 2,
+ "gloves" = 3.3,
+ "insulated" = 1.8,
+ "scanners" = 3.2,
+ "binoculars" = 1.5,
+ "flash" = 1,
+ "maglock" = 2,
+ "luminol" = 2,
+ "cleaning" = 3.5,
+ "BDSM" = 2,
+ "charger" = 2,
+ "spacesuit" = 2,
+ "rollerbed" = 2.2,
+ "smokebombs" = 1.1,
+ "jar" = 2,
+ "uvlight" = 1.2,
+ "glasses" = 1.2,
+ "pills" = 1.2,
+ "cosmetic" = 2.2,
+ "suitcooler" = 1.2,
+ "officechair" = 1.2,
+ "booze" = 3,
+ "plant" = 3.5,
+ "bag" = 2,
+ "extinguish" = 2.2,
+ "hailer" = 1.1,
+ "target" = 2,
+ "snacks" = 3.5,
+ "oxytank" = 2.5,
+ "signs" = 4,
+ "parts" = 6,
+ "cane" = 2,
+ "warning" = 2.2,
+ "gasmask" = 2,
+ "cleanernades" = 1.5,
+ "mining" = 2,
+ "paicard" = 2,
+ "phoronsheets" = 2,
+ "nothing" = 0)
+
+var/list/global/random_stock_uncommon = list(
+ "glowshrooms" = 2,
+ "plasteel" = 3,
+ "silver" = 2,
+ "phoronglass" = 2,
+ "sandstone" = 2,
+ "marble" = 2,
+ "iron" = 2,
+ "flare" = 2,
+ "deathalarm" = 2,
+ "trackimp" = 1,
+ "flashbang" = 1,
+ "cuffs" = 1,
+ "monkey" = 2,
+ "specialcrayon" = 1.5,
+ "contraband" = 2,
+ "figure" = 4,
+ "plushie" = 4,
+ "mediumcell" = 3,
+ "chempack" = 5,
+ "robolimbs" = 3,
+ "circuitboards" = 3,
+ "jetpack" = 3,
+ "xenocostume" = 1,
+ "bible" = 1,
+ "advwelder" = 2,
+ "sord" = 1,
+ "policebaton" = 2,
+ "stunbaton" = 1,//batons spawn with no powercell
+ "watches" = 3,
+ "MMI" = 1.5,
+ "voidsuit" = 2,
+ "nightvision" = 2,
+ "violin" = 2,
+ "atmosfiresuit" = 2,
+ "pdacart" = 3,
+ "debugger" = 2,
+ "surgerykit" = 2.5,
+ "crimekit" = 1,
+ "carpet" = 2,
+ "gift" = 4,
+ "lightfloor" = 2,
+ "linenbin" = 1,
+ "coatrack" = 1,
+ "riotshield" = 2,
+ "fireaxe" = 1.5,
+ "service" = 2,
+ "robot" = 2,
+ "latexb" = 1.2,
+ "taperoll" = 1,
+ "headset" = 2,
+ "bat" = 1.2,
+ "scythe" = 1,
+ "manual" = 2,
+ "jammer" = 2,
+ "rped" = 2,
+ "briefcase" = 2,
+ "blade" = 1.5,
+ "exoquip" = 2,
+ "laserscalpel" = 1.5,
+ "electropack" = 1,
+ "nothing" = 0)
+
+var/list/global/random_stock_rare = list(
+ "gold" = 2.5,
+ "diamond" = 1.5,
+ "uranium" = 3,
+ "EMP" = 1,
+ "hypercell" = 3,
+ "combatmeds" = 3,
+ "batterer" = 1,
+ "posibrain" = 3,
+ "augmentvision" = 1.6,
+ "thermals" = 2,
+ "bsbeaker" = 3,
+ "energyshield" = 3,
+ "hardsuit" = 0.75,
+ "cluster" = 2.0,
+ "cloak" = 1,
+ "sword" = 1,
+ "ims" = 1.5,
+ "exogear" = 1.5,
+ "teleporter" = 1,
+ "voice" = 2,
+ "nothing" = 0)
+
+var/list/global/random_stock_large = list(
+ "russian" = 1,
+ "emergency" = 2,
+ "firecloset" = 2,
+ "tacticool" = 0.4,
+ "radsuit" = 3,
+ "exosuit" = 1.2,//A randomly generated exosuit in a very variable condition.
+ "EOD" = 1.5,
+ "biosuit" = 3,
+ "hydrotray" = 3,
+ "oxycanister" = 6,//Cargo should almost always have an oxycanister
+ "oxydispenser" = 5,
+ "bubbleshield" = 2,
+ "chassis" = 2,
+ "watertank" = 2,
+ "fueltank" = 2,
+ "airpump" = 1,
+ "airscrubber" = 1,
+ "generator" = 5,
+ "flasher" = 2,
+ "vendor" = 6,
+ "piano" = 2,
+ "suspension" = 2,
+ "animal" = 2.5,
+ "cablelayer" = 1,
+ "floodlight" = 3,
+ "floorlayer" = 2,
+ "heater" = 1.3,
+ "dispenser" = 2.5,
+ "jukebox" = 1.2,
+ "pipemachine" = 1.7,
+ "nothing" = 0)
+
+
+/proc/spawn_cargo_stock()
+ var/start_time = world.timeofday
+ new /datum/cargospawner()
+ admin_notice("Cargo Stock generation completed in [round(0.1*(world.timeofday-start_time),0.1)] seconds.", R_DEBUG)
+
+/datum/cargospawner
+ var/list/containers = list()
+ var/list/tables = list()
+ var/list/full_containers = list()//Used to hold references to crates we filled up
+ var/area/warehouse
+ var/list/warehouseturfs = list()
+
+ var/list/infest_mobs_minor = list(
+ "/mob/living/simple_animal/mouse" = 1,
+ "/mob/living/simple_animal/lizard" = 0.5,
+ "/mob/living/simple_animal/yithian" = 0.7,
+ "/mob/living/simple_animal/tindalos" = 0.6,
+ "/mob/living/bot/secbot" = 0.1)
+
+ var/list/infest_mobs_moderate = list(
+ "/mob/living/simple_animal/bee" = 1,
+ "/mob/living/simple_animal/hostile/diyaab" = 1,
+ "/mob/living/simple_animal/hostile/viscerator" = 1,
+ "/mob/living/simple_animal/hostile/scarybat" = 1)
+
+ var/list/infest_mobs_severe = list(
+ "/mob/living/simple_animal/hostile/giant_spider/hunter" = 1,
+ "/mob/living/simple_animal/hostile/shantak" = 0.7,
+ "/mob/living/simple_animal/hostile/bear" = 0.5,
+ "/mob/living/simple_animal/hostile/carp" = 1.5,
+ "cratey" = 1
+ )
+
+/datum/cargospawner/New()
+
+ //First lets get the reference to our warehouse
+ for(var/areapath in typesof(/area/quartermaster/storage))
+ warehouse = locate(areapath)
+ if (warehouse)
+ for (var/turf/simulated/floor/T in warehouse)
+ warehouseturfs += T
+ for (var/obj/structure/closet/crate/C in warehouse)
+ containers |= C
+ for (var/obj/structure/table/B in warehouse)
+ tables |= B
+
+ if (!warehouse || !warehouseturfs.len)
+ admin_notice("ERROR: Cargo spawner failed to locate warehouse. Terminating.", R_DEBUG)
+ qdel(src)
+ return
+
+ //First, we spawn the larger items
+ //Large objects are spawned on preset locations around cargo
+ //These locations are designated by large stock marker objects, which are manually mapped in
+ for (var/obj/effect/large_stock_marker/LSM in world)
+ if (prob(STOCK_LARGE_PROB))
+ spawn_stock(pickweight(random_stock_large), get_turf(LSM))
+ qdel(LSM)
+
+
+ //Now we spawn the smaller items
+ //These are spawned inside the cargo warehouse, in crates and on tables.
+ var/spawns = TOTAL_STOCK
+ while (spawns > 0)
+ var/atom/spawnloc = get_location()
+ var/spawntype = get_spawntype()
+ spawn_stock(spawntype, spawnloc, src)
+ spawns--
+
+ handle_infestation()
+ shuffle_items()
+
+/datum/cargospawner/proc/get_location()
+ var/cratespawn = 0
+ var/obj/structure/closet/crate/emptiest
+ if (prob(70))//We'll usually put items in crates
+ var/minweight = 99999999999//We will distribute items somewhat evenly among crates
+ //by selecting the least-filled one for each spawn
+
+ for (var/obj/structure/closet/crate/C in containers)
+ if (C.stored_weight() < minweight && C.stored_weight() < C.storage_capacity)
+ minweight = C.stored_weight()
+ emptiest = C
+ cratespawn = 1
+
+ if (cratespawn)
+ return emptiest
+ else
+ //If cratespawn is zero, we either failed to find a crate to spawn in, or didnt select
+ //crate spawning with the random check.
+ var/turf/clearest
+ var/min_items = 999999999
+ for (var/B in tables)//As with crates, we attempt to distribute items evenly. Pick least-filled
+ var/TB = get_turf(B)
+ var/numitems = 0
+ for (var/obj/item in TB)
+ numitems++
+ if (numitems <= min_items)
+ clearest = TB
+ min_items = numitems
+
+
+ if (!clearest)//This should never happen
+ clearest = get_turf(pick(tables))
+
+ return clearest
+
+
+/datum/cargospawner/proc/get_spawntype()
+ var/list/spawntypes = list("1" = STOCK_RARE_PROB, "2" = STOCK_UNCOMMON_PROB, "3" = (100 - (STOCK_RARE_PROB + STOCK_UNCOMMON_PROB)))
+ var/stocktype = pickweight(spawntypes)
+ switch (stocktype)
+ if ("1")
+ return pickweight(random_stock_rare)
+ if ("2")
+ return pickweight(random_stock_uncommon)
+ if ("3")
+ return pickweight(random_stock_common)
+
+//Minor and moderate mobs are checked per crate
+#define INFEST_PROB_MINOR 6
+#define INFEST_PROB_MODERATE 3
+
+#define INFEST_PROB_SEVERE 3//Severe is once per round, not per crate
+
+/datum/cargospawner/proc/handle_infestation()
+ for (var/obj/O in containers)
+ if (prob(INFEST_PROB_MINOR))
+ //No admin message for the minor mobs, because they are friendly and harmless
+ var/ctype = pickweight(infest_mobs_minor)
+ new ctype(O)
+ else if (prob(INFEST_PROB_MODERATE))
+ var/ctype = pickweight(infest_mobs_moderate)
+ new ctype(O)
+ msg_admin_attack("Common cargo warehouse critter [ctype] spawned inside [O.name] coords (JMP)")
+
+
+ //This is checked only once per round. ~3% chance to spawn a scary monster infesting the warehouse
+ if (prob(INFEST_PROB_SEVERE))
+ //Find a tile to spawn the thing
+ var/list/turfs = list()
+ var/turf/T
+ for (var/turf/t in warehouseturfs)
+ T = t//Failsafe incase none are clear
+ if (turf_clear(T))
+ turfs |= t
+
+ if (turfs.len)
+ T = pick(turfs)
+
+ var/ctype = pickweight(infest_mobs_severe)
+
+ if (ctype == "cratey")
+ var/obj/C = pick(containers)
+
+ var/mob/living/simple_animal/hostile/mimic/copy/cratey
+
+ cratey = new /mob/living/simple_animal/hostile/mimic/copy(C.loc, C, null)
+
+
+ //Cratey is kinda tough but slow, easy to run away from
+ cratey.name = "Cratey"
+ cratey.health = 150
+ cratey.maxHealth = 150
+ cratey.melee_damage_lower = 7
+ cratey.melee_damage_upper = 18
+ cratey.knockdown_people = 1
+ cratey.move_to_delay = 12
+
+ msg_admin_attack("Cratey spawned coords (JMP)")
+
+ else
+ new ctype(T)
+ msg_admin_attack("Rare cargo warehouse critter [ctype] spawned coords (JMP)")
+ return
+
+/datum/cargospawner/proc/shuffle_items()
+ for (var/obj/O in containers)
+ O.contents = shuffle(O.contents)
+
+ for (var/obj/a in tables)
+ var/turf/T = get_turf(a)
+ T.contents = shuffle(T.contents)
+
+
+/obj/effect/large_stock_marker
+ name = "Large Stock Marker"
+ desc = "This marks a place where a large object could spawn in cargo"
+ icon = 'icons/mob/screen1.dmi'
+ icon_state = "x3"
+
+//This function actually handles the spawning.
+//If location is a turf, it will look for crates, lockers or similar containers on that turf
+//to spawn items into, instead of spawning them on the floor
+/proc/spawn_stock(var/stock, var/atom/L, var/datum/cargospawner/CS = null)
+ //L is the location we spawn in. Using a single letter as shorthand because its written so often
+ switch(stock)
+ if ("toolbox")
+ if (prob(5))
+ new /obj/item/weapon/storage/toolbox/syndicate(L)
+ else
+ new /obj/random/toolbox(L)
+
+ if("nanopaste")
+ new /obj/item/stack/nanopaste(L)
+
+ if ("meds")//A random low level medical item
+ new /obj/random/medical(L)
+ new /obj/random/medical(L)
+ new /obj/random/medical(L)
+
+ if ("steel")
+ new /obj/item/stack/material/steel(L, 50)
+
+ if ("glass")
+ if (prob(35))
+ new /obj/item/stack/material/glass/reinforced(L, rand(10,50))
+ else
+ new /obj/item/stack/material/glass(L, 50)
+ if("wood")
+ new /obj/item/stack/material/wood(L, rand(20,50))
+ if("plastic")
+ new /obj/item/stack/material/plastic(L, rand(10,50))
+ if("cardboard")
+ new /obj/item/stack/material/cardboard(L, rand(10,50))
+
+ if ("lightreplacer")
+ //A lightreplacer and a kit of lights
+ var/obj/item/device/lightreplacer/LR
+ if (prob(5))
+ LR = new /obj/item/device/lightreplacer/advanced(L)
+ else
+ LR = new /obj/item/device/lightreplacer(L)
+ LR.uses = 0
+
+ new /obj/item/weapon/storage/box/lights/mixed(L)
+ new /obj/item/weapon/storage/box/lights/mixed(L)
+
+ if("bodybag")
+ //A bundle of bodybags or stasis bags
+ if (prob(25))
+ new /obj/item/bodybag/cryobag(L)
+ new /obj/item/bodybag/cryobag(L)
+ new /obj/item/bodybag/cryobag(L)
+ new /obj/item/bodybag/cryobag(L)
+ else
+ new /obj/item/weapon/storage/box/bodybags(L)
+
+ if ("lamp")
+ var/number = rand(1,3)
+ while (number > 0)
+ var/obj/item/device/flashlight/lamp/P
+ if (prob(50))
+ P = new /obj/item/device/flashlight/lamp/green(L)
+ else
+ P = new /obj/item/device/flashlight/lamp(L)
+ P.on = 0
+ P.update_icon()
+ number--
+
+ if("mousetrap")
+ new /obj/item/weapon/storage/box/mousetraps(L)
+ if("donk")
+ if (prob(10))
+ new /obj/item/weapon/storage/box/sinpockets(L)
+ else
+ new /obj/item/weapon/storage/box/donkpockets(L)
+ if("sterile")
+ new /obj/item/weapon/storage/box/gloves(L)
+ new /obj/item/weapon/storage/box/masks(L)
+
+ if("light")
+ new /obj/item/weapon/storage/box/lights/mixed(L)
+ if (prob(50))
+ new /obj/item/weapon/storage/box/lights/mixed(L)
+ if (prob(25))
+ new /obj/item/weapon/storage/box/lights/mixed(L)
+ if("aid")
+ new /obj/random/firstaid(L)
+ if("flame")
+ new /obj/item/weapon/storage/box/matches(L)
+ new /obj/item/weapon/flame/lighter/random(L)
+ new /obj/item/weapon/storage/fancy/candle_box(L)
+ new /obj/item/weapon/storage/fancy/candle_box(L)
+ if ("crayons")
+ new /obj/item/weapon/storage/fancy/crayons(L)
+
+ if("bombsupply")
+ new /obj/random/bomb_supply(L)
+ new /obj/random/bomb_supply(L)
+ new /obj/random/bomb_supply(L)
+ new /obj/random/bomb_supply(L)
+ if("tech")
+ new /obj/random/tech_supply(L)
+ new /obj/random/tech_supply(L)
+ new /obj/random/tech_supply(L)
+ new /obj/random/tech_supply(L)
+
+ if("smokes")
+ new /obj/item/weapon/flame/lighter/random(L)
+ if (prob(20))
+ new /obj/item/weapon/storage/fancy/cigar(L)
+ new /obj/item/weapon/storage/fancy/cigar(L)
+ else
+ if (prob(50))
+ new /obj/item/weapon/storage/fancy/cigarettes/dromedaryco(L)
+ new /obj/item/weapon/storage/fancy/cigarettes/dromedaryco(L)
+ new /obj/item/weapon/storage/fancy/cigarettes/dromedaryco(L)
+ new /obj/item/weapon/storage/fancy/cigarettes/dromedaryco(L)
+ else
+ new /obj/item/weapon/storage/fancy/cigarettes(L)
+ new /obj/item/weapon/storage/fancy/cigarettes(L)
+ new /obj/item/weapon/storage/fancy/cigarettes(L)
+ new /obj/item/weapon/storage/fancy/cigarettes(L)
+
+ if (prob(30))
+ new /obj/item/clothing/mask/smokable/pipe(L)
+
+ if("vials")
+ if (prob(20))
+ new /obj/item/weapon/storage/lockbox/vials(L)
+ else
+ new /obj/item/weapon/storage/fancy/vials(L)
+
+ if("smallcell")
+ var/number = rand(1,4)
+ while (number > 0)
+ var/type = pick(list(/obj/item/weapon/cell, /obj/item/weapon/cell/device, /obj/item/weapon/cell/apc, /obj/item/weapon/cell/high))
+ new type(L)
+ number --
+
+ //Spawns a robo limb with a random manufacturer
+ if("robolimb")
+ var/manufacturer = pick(all_robolimbs)
+ var/list/limblist = list(
+ /obj/item/robot_parts/l_arm,
+ /obj/item/robot_parts/r_arm,
+ /obj/item/robot_parts/l_leg,
+ /obj/item/robot_parts/r_leg)
+ var/type = pick(limblist)
+ new type(L, manufacturer)
+ if("circuitboard")
+ //Spawns a random circuitboard
+ //Allboards being a global list might be faster, but it didnt seem worth the extra memory
+ var/list/allboards = typesof(/obj/item/weapon/circuitboard)
+ allboards -= typesof(/obj/item/weapon/circuitboard/mecha)
+ var/type = pick(allboards)
+ new type(L)
+
+ if("smalloxy")
+ new /obj/random/smalltank(L)
+ new /obj/random/smalltank(L)
+ new /obj/random/smalltank(L)
+ if("belts")
+ new /obj/random/belt(L)
+ new /obj/random/belt(L)
+ if("backpack")
+ new /obj/random/backpack(L)
+ new /obj/random/backpack(L)
+ if("weldgear")
+ if (prob(50))
+ new /obj/item/clothing/glasses/welding(L)
+ if (prob(50))
+ new /obj/item/clothing/head/welding(L)
+ if (prob(50))
+ new /obj/item/weapon/weldpack(L)
+ if("inflatable")
+ new /obj/item/weapon/storage/briefcase/inflatable(L)
+
+ //Wheelchair is not dense so it doesnt NEED a clear tile, but it looks a little silly to
+ //have it on a crate. So we will attempt to find a clear tile around the spawnpoint.
+ //We'll put it ontop of a crate if we need to though, its not essential to find clear space
+ //In any case, we always spawn it on a turf and never in a container
+ if("wheelchair")
+ var/turf/T = get_turf(L)
+ if (!turf_clear(T))
+ for (var/turf/U in range(T,1))
+ if (turf_clear(U))
+ T = U
+ break
+ new /obj/structure/bed/chair/wheelchair(T)
+ if ("meson")
+ new /obj/item/clothing/glasses/meson(L)
+ if (prob(50))
+ new /obj/item/clothing/glasses/meson(L)
+ if ("beartrap")
+ new /obj/item/weapon/beartrap(L)
+ if (prob(50))
+ new /obj/item/weapon/beartrap(L)
+ if ("trays")
+ var/number = rand(1,7)
+ for (var/i = 0;i < number, i++)
+ new /obj/item/weapon/tray(L)
+ if ("utensil")
+ new /obj/item/weapon/storage/box/kitchen(L)
+ if ("metalfoam")
+ new /obj/item/weapon/grenade/chem_grenade/metalfoam(L)
+ new /obj/item/weapon/grenade/chem_grenade/metalfoam(L)
+ new /obj/item/weapon/grenade/chem_grenade/metalfoam(L)
+ if ("gloves")
+ var/list/allgloves = typesof(/obj/item/clothing/gloves)
+ var/number = rand(1,5)
+ while (number > 0)
+ var/gtype = pick(allgloves)
+ var/thing = new gtype(L)
+ if (!istype(thing, /obj/item/clothing/gloves/rig))//Rig gloves would be buggy
+ number--
+ else
+ qdel(thing)
+ if ("insulated")
+ new /obj/item/clothing/gloves/yellow(L)
+ new /obj/item/clothing/gloves/yellow(L)
+ if ("scanners")
+ //A random scanning device, most are useless
+ var/list/possible = list(
+ "/obj/item/device/healthanalyzer" = 5,
+ "/obj/item/device/analyzer" = 0.5,
+ "/obj/item/device/mass_spectrometer" = 0.5,
+ "/obj/item/device/mass_spectrometer/adv" = 0.5,
+ "/obj/item/device/slime_scanner" = 1,
+ "/obj/item/weapon/autopsy_scanner" = 1,
+ "/obj/item/device/robotanalyzer" = 4,
+ "/obj/machinery/disease2/diseaseanalyser" = 0.5,
+ "/obj/item/weapon/mining_scanner" = 1,
+ "/obj/item/device/ano_scanner" = 1,
+ "/obj/item/device/reagent_scanner" = 2,
+ "/obj/item/device/reagent_scanner/adv" = 2,
+ "/obj/item/weapon/barcodescanner" = 1,
+ "/obj/item/device/depth_scanner" = 1,
+ "/obj/item/device/antibody_scanner" = 0.5
+ )
+ var/number = rand(1,3)
+ while (number > 0)
+ var/stype = pickweight(possible)
+ new stype(L)
+ number--
+ if ("binoculars")
+ new /obj/item/device/binoculars(L)
+ if (prob(50))
+ new /obj/item/device/binoculars(L)
+ if ("flash")
+ new /obj/item/device/flash(L)
+ if ("BDSM")
+ if (prob(50))
+ new /obj/item/clothing/glasses/sunglasses/blindfold(L)
+ if (prob(50))
+ new /obj/item/clothing/mask/muzzle(L)
+ if (prob(30))
+ new /obj/item/clothing/suit/straight_jacket(L)
+ if ("maglock")
+ new /obj/item/device/magnetic_lock(L)
+ if ("luminol")
+ new /obj/item/weapon/reagent_containers/spray/luminol(L)
+ if ("cleaning")
+ if (prob(80))
+ new /obj/item/weapon/reagent_containers/glass/rag(L)
+ if (prob(80))
+ var/list/soaps = list(
+ /obj/item/weapon/soap,
+ /obj/item/weapon/soap/nanotrasen,
+ /obj/item/weapon/soap/deluxe,
+ /obj/item/weapon/soap/syndie
+ )
+ var/soaptype = pick(soaps)
+ new soaptype(L)
+ if (prob(80))
+ new /obj/item/weapon/mop(L)
+ if ("charger")
+ var/list/choices = list(/obj/machinery/cell_charger, /obj/machinery/recharger)
+ var/newtype = pick(choices)
+ var/obj/machinery/ma = new newtype(L)
+ ma.anchored = 0
+ if ("spacesuit")
+ new /obj/item/clothing/suit/space(L)
+ new /obj/item/clothing/head/helmet/space(L)
+ if ("rollerbed")
+ new /obj/item/roller(L)
+ if ("jar")
+ new /obj/item/glass_jar(L)
+ if ("smokebombs")
+ new /obj/item/weapon/storage/box/smokebombs(L)
+ if ("uvlight")
+ new /obj/item/device/uv_light(L)
+ if("glasses")
+ new /obj/item/weapon/storage/box/rxglasses(L)
+
+ //Spawns a bottle of pills, lower odds than the meds spawn
+ if ("pills")
+ var/list/options = list(
+ /obj/item/weapon/storage/pill_bottle/bicaridine,
+ /obj/item/weapon/storage/pill_bottle/dexalin_plus,
+ /obj/item/weapon/storage/pill_bottle/dermaline,
+ /obj/item/weapon/storage/pill_bottle/dylovene,
+ /obj/item/weapon/storage/pill_bottle/inaprovaline,
+ /obj/item/weapon/storage/pill_bottle/kelotane,
+ /obj/item/weapon/storage/pill_bottle/spaceacillin,
+ /obj/item/weapon/storage/pill_bottle/tramadol,
+ )
+ var/newtype = pick(options)
+ new newtype(L)
+
+ if ("cosmetic")
+ if (prob(50))
+ new /obj/item/weapon/lipstick/random(L)
+ else
+ new /obj/item/weapon/haircomb(L)
+ if ("suitcooler")
+ new /obj/item/device/suit_cooling_unit(L)
+ if ("officechair")
+ var/turf/T = get_turf(L)
+ if (!turf_clear(T))
+ for (var/turf/U in range(T,1))
+ if (turf_clear(U))
+ T = U
+ break
+ new /obj/structure/bed/chair/office/dark(T)
+ if ("booze")
+ if (prob(8))//Spare keg of beer
+ var/turf/T = get_turf(L)
+ if (!turf_clear(T))
+ for (var/turf/U in range(T,1))
+ if (turf_clear(U))
+ T = U
+ break
+
+ new /obj/structure/reagent_dispensers/beerkeg(T)
+ else
+ var/list/drinks = typesof(/obj/item/weapon/reagent_containers/food/drinks/bottle)
+
+ var/number = rand(1,3)
+ while (number > 0)
+ var/type = pick(drinks)
+ new type(L)
+ number--
+ if ("plant")
+ var/turf/T = get_turf(L)
+ if (!turf_clear(T))
+ for (var/turf/U in range(T,1))
+ if (turf_clear(U))
+ T = U
+ break
+ new /obj/structure/flora/pottedplant/random(T)
+ if ("bag")
+ var/list/bags = list(
+ "/obj/item/weapon/storage/bag/trash",
+ "/obj/item/weapon/storage/bag/plasticbag",
+ "/obj/item/weapon/storage/bag/ore",
+ "/obj/item/weapon/storage/bag/plants",
+ "/obj/item/weapon/storage/bag/sheetsnatcher",
+ "/obj/item/weapon/storage/bag/cash",
+ "/obj/item/weapon/storage/bag/books"
+ )
+ var/type = pick(bags)
+ new type(L)
+ if (prob(30))
+ new type(L)
+
+ if ("extinguish")
+ var/list/ext = list(
+ /obj/item/weapon/extinguisher,
+ /obj/item/weapon/extinguisher/mini)
+
+ var/number = rand(1,3)
+ while (number > 0)
+ var/type = pick(ext)
+ new type(L)
+ number--
+ if ("hailer")
+ if (prob(50))
+ new /obj/item/device/megaphone(L)
+ else
+ new /obj/item/device/hailer(L)
+
+
+ if ("taperoll")
+ if (prob(50))
+ new /obj/item/taperoll/police(L)
+ else
+ new /obj/item/taperoll/engineering(L)
+
+ //A target, for target practise
+ //Take em up to science for gun testing
+ if ("target")
+ var/turf/T = get_turf(L)
+ if (!turf_clear(T))
+ for (var/turf/U in range(T,1))
+ if (turf_clear(U))
+ T = U
+ break
+ if (prob(50))
+ new /obj/item/target(T)
+ else
+ new /obj/structure/target_stake(T)
+
+
+ if ("snacks")
+ //Snackboxes are much more likely to spawn on tables than in crates.
+ //This ensures the cargo bay will have a supply of food in an obtainable place for animals
+ //allows nymphs and mice to raid it for nutrients, and thus gives playermice more
+ //reason to infest the warehouse
+ if (CS && prob(65))
+ if (!istype(L, /turf))
+ L = get_turf(pick(CS.tables))
+
+ new /obj/item/weapon/storage/box/snack(L)
+
+ if ("oxytank")
+ new /obj/item/weapon/tank/oxygen(L)
+ new /obj/item/weapon/tank/oxygen(L)
+
+ //Parts used for machines
+ //Also includes the telecomms parts even though they're useless
+ //Because they are interesting
+ if ("parts")
+ var/list/parts = list(
+ //Low ranking parts, common
+ "/obj/item/weapon/stock_parts/console_screen" = 3,
+ "/obj/item/weapon/stock_parts/capacitor" = 3,
+ "/obj/item/weapon/stock_parts/scanning_module" = 3,
+ "/obj/item/weapon/stock_parts/manipulator" = 3,
+ "/obj/item/weapon/stock_parts/micro_laser" = 3,
+ "/obj/item/weapon/stock_parts/matter_bin" = 3,
+ //Improved parts, less common
+ "/obj/item/weapon/stock_parts/capacitor/adv" = 1,
+ "/obj/item/weapon/stock_parts/scanning_module/adv" = 1,
+ "/obj/item/weapon/stock_parts/manipulator/nano" = 1,
+ "/obj/item/weapon/stock_parts/micro_laser/high" = 1,
+ "/obj/item/weapon/stock_parts/matter_bin/adv" = 1,
+ //Top level parts, rare
+ "/obj/item/weapon/stock_parts/capacitor/super" = 0.3,
+ "/obj/item/weapon/stock_parts/scanning_module/phasic" = 0.3,
+ "/obj/item/weapon/stock_parts/manipulator/pico" = 0.3,
+ "/obj/item/weapon/stock_parts/micro_laser/ultra" = 0.3,
+ "/obj/item/weapon/stock_parts/matter_bin/super" = 0.3,
+ //Telecomms parts, useless novelties and red herrings.
+ "/obj/item/weapon/stock_parts/subspace/ansible" = 0.5,
+ "/obj/item/weapon/stock_parts/subspace/filter" = 0.5,
+ "/obj/item/weapon/stock_parts/subspace/amplifier" = 0.5,
+ "/obj/item/weapon/stock_parts/subspace/treatment" = 0.5,
+ "/obj/item/weapon/stock_parts/subspace/analyzer" = 0.5,
+ "/obj/item/weapon/stock_parts/subspace/crystal" = 0.5,
+ "/obj/item/weapon/stock_parts/subspace/transmitter" = 0.5
+ )
+
+ var/number = rand(2,5)
+ while (number > 0)
+ var/part = pickweight(parts)
+ new part(L)
+ number--
+
+
+ if ("cane")
+ if (prob(5))
+ new /obj/item/weapon/cane/concealed(L)
+ else if (prob(20))
+ new /obj/item/weapon/staff/broom(L)
+ else if (prob(30))
+ new /obj/item/weapon/staff/stick(L)
+ else
+ new /obj/item/weapon/cane(L)
+
+ if ("warning")
+ if (prob(50))
+ new /obj/item/weapon/caution(L)
+ else
+ new /obj/item/weapon/caution/cone(L)
+
+ if ("gasmask")
+ var/list/masks = list(
+ "/obj/item/clothing/mask/gas" = 10,
+ "/obj/item/clothing/mask/gas/plaguedoctor" = 1,
+ "/obj/item/clothing/mask/gas/swat" = 5,
+ "/obj/item/clothing/mask/gas/clown_hat" = 0.5,
+ "/obj/item/clothing/mask/gas/sexyclown" = 0.5,
+ "/obj/item/clothing/mask/gas/mime" = 0.5,
+ "/obj/item/clothing/mask/gas/monkeymask" = 0.5,
+ "/obj/item/clothing/mask/gas/sexymime" = 0.5,
+ "/obj/item/clothing/mask/gas/death_commando" = 2,
+ "/obj/item/clothing/mask/gas/cyborg" = 1,
+ "/obj/item/clothing/mask/gas/owl_mask" = 1
+ )
+
+ var/type = pickweight(masks)
+ new type(L)
+
+ if ("cleanernades")
+ new /obj/item/weapon/grenade/chem_grenade/cleaner(L)
+ new /obj/item/weapon/grenade/chem_grenade/cleaner(L)
+ if (prob(50))
+ new /obj/item/weapon/grenade/chem_grenade/cleaner(L)
+ new /obj/item/weapon/grenade/chem_grenade/cleaner(L)
+
+
+
+ if ("mining")
+ if (prob(50))
+ new /obj/item/weapon/shovel(L)
+ if (prob(50))
+ new /obj/item/weapon/pickaxe(L)
+ if (prob(50))
+ new /obj/item/clothing/glasses/material(L)
+ if (prob(50))
+ new /obj/item/device/flashlight/lantern(L)
+ if (prob(50))
+ new /obj/item/weapon/mining_scanner(L)
+
+ if ("paicard")
+ new /obj/item/device/paicard(L)
+
+ if ("phoronsheets")
+ new /obj/item/stack/material/phoron(L, rand(5,50))
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//Uncommon items below here
+//=============================================================
+//=============================================================
+//=============================================================
+ if ("glowshrooms")
+ new /obj/item/seeds/glowshroom(L)
+ new /obj/item/seeds/glowshroom(L)
+ new /obj/item/seeds/glowshroom(L)
+ if("plasteel")
+ new /obj/item/stack/material/plasteel(L, rand(1,30))
+ if("silver")
+ new /obj/item/stack/material/silver(L, rand(5,30))
+ if("phoronglass")
+ new /obj/item/stack/material/glass/phoronglass(L, 50)
+ if("sandstone")
+ new /obj/item/stack/material/sandstone(L, 50)
+ if("marble")
+ new /obj/item/stack/material/marble(L, 50)
+ if("iron")
+ new /obj/item/stack/material/iron(L, 50)
+ if ("flare")
+ new /obj/item/device/flashlight/flare(L)
+ new /obj/item/device/flashlight/flare(L)
+ new /obj/item/device/flashlight/flare(L)
+ if("deathalarm")
+ new /obj/item/weapon/storage/box/cdeathalarm_kit(L)
+ if("trackimp")
+ new /obj/item/weapon/storage/box/trackimp(L)
+ if("flashbang")
+ new /obj/item/weapon/storage/box/flashbangs(L)
+ if("cuffs")
+ new /obj/item/weapon/storage/box/handcuffs(L)
+ if("monkey")
+ if (prob(40))
+ var/type = pick(list(/obj/item/weapon/storage/box/monkeycubes/farwacubes,
+ /obj/item/weapon/storage/box/monkeycubes/stokcubes,
+ /obj/item/weapon/storage/box/monkeycubes/neaeracubes))
+ new type(L)
+ else
+ new /obj/item/weapon/storage/box/monkeycubes(L)
+ if("specialcrayon")
+ if (prob(50))
+ new /obj/item/weapon/pen/crayon/mime(L)
+ else
+ new /obj/item/weapon/pen/crayon/rainbow(L)
+ if("contraband")
+ var/number = rand(1,8)
+ while (number > 0)
+ new /obj/random/contraband(L)
+ number--
+ if("figure")
+ new /obj/random/action_figure(L)
+ new /obj/random/action_figure(L)
+ new /obj/random/action_figure(L)
+ if("plushie")
+ new /obj/random/plushie(L)
+ if("mediumcell")
+ var/number = rand(1,2)
+ while (number > 0)
+ var/type = pick(list(/obj/item/weapon/cell/super, /obj/item/weapon/cell/potato, /obj/item/weapon/cell/high))
+ new type(L)
+ number --
+
+ //Spawns several random chemical cartridges
+ //Can be slotted into any dispenser
+ if("chempack")
+ var/total = rand(2,6)
+ for (var/i=0,i 0)
+ var/manufacturer = pick(all_robolimbs)
+ var/type = pick(limblist)
+ new type(L, manufacturer)
+ number--
+
+ //Spawns several random circuitboards
+ if("circuitboards")
+ var/list/allboards = typesof(/obj/item/weapon/circuitboard)
+ allboards -= typesof(/obj/item/weapon/circuitboard/mecha)
+ var/number = rand(2,5)
+ while (number > 0)
+ var/type = pick(allboards)
+ new type(L)
+ number--
+
+ if("jetpack")
+ new /obj/item/weapon/tank/jetpack/void(L)
+ new /obj/item/weapon/tank/emergency_oxygen/double(L)
+
+ if ("xenocostume")
+ new /obj/item/clothing/suit/xenos(L)
+ new /obj/item/clothing/head/xenos(L)
+ if ("bible")
+ if (prob(25))
+ new /obj/item/weapon/storage/bible/booze(L)
+ else
+ new /obj/item/weapon/storage/bible(L)
+ if ("advwelder")
+ new /obj/item/weapon/weldingtool/hugetank(L)
+
+ if ("sord")
+ new /obj/item/weapon/sord(L)
+ if ("policebaton")
+ new /obj/item/weapon/melee/classic_baton(L)
+ if ("stunbaton")
+ //Batons are put into storage without their powercell
+ var/obj/item/weapon/melee/baton/B = new /obj/item/weapon/melee/baton(L)
+ B.bcell = null
+ B.update_icon()
+ if ("watches")
+ new /obj/item/clothing/gloves/watch(L)
+ new /obj/item/clothing/gloves/watch(L)
+ new /obj/item/clothing/gloves/watch(L)
+
+ if ("MMI")
+ new /obj/item/device/mmi(L)
+ if ("voidsuit")
+ new /obj/random/voidsuit(L,1)
+
+ if ("signs")
+ var/list/allsigns = typesof(/obj/structure/sign)
+ allsigns -= typesof(/obj/structure/sign/double)
+ var/number = rand(1,5)
+
+ while (number > 0)
+ var/newsign = pick(allsigns)
+ if (newsign != /obj/structure/sign)//Dont want to spawn the generic parent class
+ var/obj/structure/sign/S = new newsign(L)
+ S.unfasten()
+ number--
+
+ if ("violin")
+ new /obj/item/device/violin(L)
+ if ("nightvision")
+ new /obj/item/clothing/glasses/night(L)
+ if ("atmosfiresuit")
+ new /obj/item/clothing/head/hardhat/red/atmos(L)
+ new /obj/item/clothing/suit/fire/atmos(L)
+ if ("pdacart")
+ var/number = rand(1,4)
+ while (number > 0)
+ new /obj/random/pda_cart(L)
+ number--
+ if ("surgerykit")
+ new /obj/item/weapon/storage/firstaid/surgery(L)
+ if ("debugger")
+ new /obj/item/device/debugger(L)//No idea what this thing does, or if it works at all
+ if ("crimekit")
+ new /obj/item/weapon/storage/briefcase/crimekit(L)
+ if ("carpet")
+ new /obj/item/stack/tile/carpet(L, 50)
+ if ("gift")
+ new /obj/item/weapon/a_gift(L)
+ if ("lightfloor")
+ new /obj/item/stack/tile/light(L, 50)
+ if ("linenbin")
+ new /obj/structure/bedsheetbin(get_turf(L))
+ if ("coatrack")
+ var/turf/T = get_turf(L)
+ if (!turf_clear(T))
+ for (var/turf/U in range(T,1))
+ if (turf_clear(U))
+ T = U
+ break
+ new /obj/structure/coatrack(T)
+
+
+ if ("riotshield")
+ new /obj/item/weapon/shield/riot(L)
+ if (prob(60))
+ new /obj/item/weapon/shield/riot(L)
+ if ("fireaxe")
+ new /obj/item/weapon/material/twohanded/fireaxe(L)
+ if ("service")
+ new /obj/item/weapon/rsf(L)
+
+ //Spawns a random deactivated bot
+ if ("robot")
+ var/list/bots = list(
+ "/mob/living/bot/cleanbot" = 2,
+ "/mob/living/bot/secbot" = 0.7,
+ "/mob/living/bot/medbot" = 2,
+ "/mob/living/bot/floorbot" = 2.5,
+ "/mob/living/bot/farmbot" = 1,
+ "/mob/living/bot/secbot/ed209" = 0.3
+ )
+
+ var/type = pickweight(bots)
+ if (type == "/mob/living/bot/secbot/ed209")//ED is large and should spawn on the floor
+ L = get_turf(L)
+ if (!turf_clear(L))
+ for (var/turf/U in range(L,1))
+ if (turf_clear(U))
+ L = U
+ break
+ var/mob/living/bot/newbot = new type(L)
+ newbot.on = 0//Deactivated
+ if (prob(10))
+ newbot.Emag(null)
+ if ("latexb")
+ new /obj/item/latexballon(L)
+
+ //Random headsets for low-security department
+ //No command or sec
+ if ("headset")
+ var/list/sets = list(
+ "/obj/item/device/radio/headset/headset_eng" = 1,
+ "/obj/item/device/radio/headset/headset_rob" = 0.4,
+ "/obj/item/device/radio/headset/headset_med" = 1,
+ "/obj/item/device/radio/headset/headset_sci" = 0.8,
+ "/obj/item/device/radio/headset/headset_medsci" = 0.4,
+ "/obj/item/device/radio/headset/headset_cargo" = 1,
+ "/obj/item/device/radio/headset/headset_service" = 1
+ )
+
+ var/type = pickweight(sets)
+ new type(L)
+
+ if ("bat")
+ new /obj/item/weapon/material/twohanded/baseballbat(L)
+
+ if ("scythe")
+ new /obj/item/weapon/material/scythe(L)
+
+ //Spawns a random manual book. These are mostly outdated, inaccurate, and obsolete.
+ //This is here for novelty effect and these manuals shouldn't actually be followed
+ //Only the manuals that actually contain stuff are here. Those that just link to a wiki page are excluded
+ if ("manual")
+ var/list/manuals = list(
+ /obj/item/weapon/book/manual/excavation,
+ /obj/item/weapon/book/manual/mass_spectrometry,
+ /obj/item/weapon/book/manual/anomaly_spectroscopy,
+ /obj/item/weapon/book/manual/materials_chemistry_analysis,
+ /obj/item/weapon/book/manual/anomaly_testing,
+ /obj/item/weapon/book/manual/stasis,
+ /obj/item/weapon/book/manual/engineering_particle_accelerator,
+ /obj/item/weapon/book/manual/supermatter_engine,
+ /obj/item/weapon/book/manual/engineering_singularity_safety,
+ /obj/item/weapon/book/manual/medical_cloning,
+ /obj/item/weapon/book/manual/ripley_build_and_repair,
+ /obj/item/weapon/book/manual/research_and_development,
+ /obj/item/weapon/book/manual/robotics_cyborgs,
+ /obj/item/weapon/book/manual/medical_diagnostics_manual,
+ /obj/item/weapon/book/manual/chef_recipes,
+ /obj/item/weapon/book/manual/barman_recipes,
+ /obj/item/weapon/book/manual/detective,
+ /obj/item/weapon/book/manual/nuclear,
+ /obj/item/weapon/book/manual/atmospipes,
+ /obj/item/weapon/book/manual/evaguide
+ )
+
+ var/type = pick(manuals)
+ new type(L)
+
+ if ("jammer")
+ new /obj/item/device/radiojammer(L)
+
+ if ("rped")
+ new /obj/item/weapon/storage/part_replacer(L)
+
+
+ if ("briefcase")
+ if (prob(20))
+ new /obj/item/weapon/storage/secure/briefcase(L)
+ else
+ new /obj/item/weapon/storage/briefcase(L)
+
+ if ("blade")
+ var/list/blades = list(
+ "/obj/item/weapon/material/butterfly" = 1,
+ "/obj/item/weapon/material/butterfly/switchblade" = 1,
+ "/obj/item/weapon/material/knife/hook" = 1.5,
+ "/obj/item/weapon/material/knife/ritual" = 1.5,
+ "/obj/item/weapon/material/knife/butch" = 1,
+ "/obj/item/weapon/material/hatchet" = 1.5,
+ "/obj/item/weapon/material/hatchet/unathiknife" = 0.75,
+ "/obj/item/weapon/material/hatchet/tacknife" = 1,
+ )
+
+ var/type = pickweight(blades)
+ new type(L)
+
+ //a single random exosuit attachment from a limited list,
+ //with some of the more overpowered ones excluded
+ if ("exoquip")
+ var/list/equips = list(
+ "/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp" = 1,
+ "/obj/item/mecha_parts/mecha_equipment/tool/drill" = 1,
+ "/obj/item/mecha_parts/mecha_equipment/tool/extinguisher" = 1,
+ "/obj/item/mecha_parts/mecha_equipment/gravcatapult" = 0.8,
+ "/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster" = 1,
+ "/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster" = 0.9,
+ "/obj/item/mecha_parts/mecha_equipment/generator" = 1.5,
+ "/obj/item/mecha_parts/mecha_equipment/tool/passenger" = 1,
+ "/obj/item/mecha_parts/mecha_equipment/tool/sleeper" = 0.9,
+ "/obj/item/mecha_parts/mecha_equipment/tool/cable_layer" = 1.2,
+ "/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun" = 1
+ )
+
+ var/type = pickweight(equips)
+ new type(L)
+
+ if ("laserscalpel")
+ var/list/lasers = list(
+ "/obj/item/weapon/scalpel/laser1" = 3,
+ "/obj/item/weapon/scalpel/laser2" = 2,
+ "/obj/item/weapon/scalpel/laser3" = 1
+ )
+ var/type = pickweight(lasers)
+ new type(L)
+
+ if ("electropack")
+ new /obj/item/device/radio/electropack(L)
+
+ if (istype(L, /obj/structure/closet/crate) && prob(40))
+ var/obj/structure/closet/crate/cr = L
+ cr.rigged = 1//Boobytrapped crate, will electrocute when you attempt to open it
+ //Can be disarmed with wirecutters or ignored with insulated gloves
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//Rare items below here:
+//=============================================================
+//=============================================================
+//=============================================================
+ if("gold")
+ new /obj/item/stack/material/gold(L, rand(2,15))
+ if("diamond")
+ new /obj/item/stack/material/diamond(L, rand(1,10))
+ if("uranium")
+ new /obj/item/stack/material/uranium(L, rand(5,30))
+ if("EMP")
+ new /obj/item/weapon/storage/box/emps(L)
+ if("hypercell")
+ new /obj/item/weapon/cell/hyper(L)
+ if("combatmeds")
+ new /obj/item/weapon/storage/firstaid/combat(L)
+ if("batterer")
+ new /obj/item/device/batterer(L)
+ if("posibrain")
+ new /obj/item/device/mmi/digital/posibrain(L)
+ if("augmentvision")
+ new /obj/item/clothing/glasses/hud/security/jensenshades(L)
+ if("thermals")
+ new /obj/item/clothing/glasses/thermal(L)
+ if("bsbeaker")
+ new /obj/item/weapon/reagent_containers/glass/beaker/bluespace(L)
+ if (prob(50))
+ new /obj/item/weapon/reagent_containers/glass/beaker/bluespace(L)
+ if("energyshield")
+ new /obj/item/weapon/shield/energy(L)
+ if("cluster")
+ new /obj/item/weapon/grenade/flashbang/clusterbang(L)
+ if("cloak")
+ new /obj/item/weapon/cloaking_device(L)
+ if("sword")
+ var/list/swords = list(
+ /obj/item/weapon/material/sword,
+ /obj/item/weapon/material/sword/katana,
+ /obj/item/weapon/material/sword/rapier,
+ /obj/item/weapon/material/sword/longsword,
+ /obj/item/weapon/material/sword/trench,
+ /obj/item/weapon/material/sword/sabre
+ )
+
+ var/type = pick(swords)
+ new type(L)
+ if("ims")
+ new /obj/item/weapon/scalpel/manager(L)
+ if("hardsuit")
+ //A random RIG/hardsuit
+ //It will come with some screwy electronics and possibly need reprogramming
+ var/list/rigs = list(
+ "/obj/item/weapon/rig/unathi" = 2,
+ "/obj/item/weapon/rig/unathi/fancy" = 1,
+ "/obj/item/weapon/rig/combat" = 0.1,
+ "/obj/item/weapon/rig/ert" = 0.2,
+ "/obj/item/weapon/rig/ert/engineer" = 0.2,
+ "/obj/item/weapon/rig/ert/medical" = 0.3,
+ "/obj/item/weapon/rig/ert/security" = 0.15,
+ "/obj/item/weapon/rig/ert/assetprotection" = 0.1,
+ "/obj/item/weapon/rig/light" = 0.5,
+ "/obj/item/weapon/rig/light/hacker" = 0.8,
+ "/obj/item/weapon/rig/light/stealth" = 1.5,
+ "/obj/item/weapon/rig/merc" = 1,
+ "/obj/item/weapon/rig/industrial" = 3,
+ "/obj/item/weapon/rig/eva" = 3,
+ "/obj/item/weapon/rig/ce" = 2,
+ "/obj/item/weapon/rig/hazmat" = 4,
+ "/obj/item/weapon/rig/medical" = 4,
+ "/obj/item/weapon/rig/hazard" = 4,
+ )
+
+ var/type = pickweight(rigs)
+ var/obj/item/weapon/rig/module = new type(L)
+
+ //screw it up a bit
+ var/cnd = rand(1,100)
+ module.lose_modules(cnd)
+ module.misconfigure(cnd)
+ module.sabotage_cell()
+ module.sabotage_tank()
+
+
+ //Several random non-weapon exosuit attachments
+ if ("exogear")
+ var/list/equips = list(
+ "/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp" = 1,
+ "/obj/item/mecha_parts/mecha_equipment/tool/drill" = 1,
+ "/obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill" = 0.7,
+ "/obj/item/mecha_parts/mecha_equipment/tool/extinguisher" = 1,
+ "/obj/item/mecha_parts/mecha_equipment/tool/rcd" = 0.08,
+ "/obj/item/mecha_parts/mecha_equipment/teleporter" = 0.3,
+ "/obj/item/mecha_parts/mecha_equipment/wormhole_generator" = 0.5,
+ "/obj/item/mecha_parts/mecha_equipment/gravcatapult" = 0.8,
+ "/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster" = 1,
+ "/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster" = 0.9,
+ "/obj/item/mecha_parts/mecha_equipment/repair_droid" = 0.7,
+ "/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay" = 0.4,
+ "/obj/item/mecha_parts/mecha_equipment/generator" = 1.5,
+ "/obj/item/mecha_parts/mecha_equipment/generator/nuclear" = 0.8,
+ "/obj/item/mecha_parts/mecha_equipment/tool/safety_clamp" = 0.2,
+ "/obj/item/mecha_parts/mecha_equipment/tool/passenger" = 1,
+ "/obj/item/mecha_parts/mecha_equipment/tool/sleeper" = 0.9,
+ "/obj/item/mecha_parts/mecha_equipment/tool/cable_layer" = 1.2,
+ "/obj/item/mecha_parts/mecha_equipment/tool/syringe_gun" = 1
+ )
+
+ var/number = rand(2,5)
+ while (number > 0)
+ var/type = pickweight(equips)
+ new type(L)
+
+ if ("teleporter")
+ new /obj/item/weapon/hand_tele(L)
+
+ if ("voice")
+ new /obj/item/clothing/mask/gas/voice(L)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//Large items go below here
+//=============================================================
+//=============================================================
+//=============================================================
+ if ("oxycanister")
+ new /obj/machinery/portable_atmospherics/canister/oxygen(L)
+ if ("oxydispenser")
+ new /obj/structure/dispenser/oxygen(L)
+ if ("bubbleshield")
+ var/obj/O = new /obj/machinery/shield_gen(L)
+ var/turf/T = get_turf(O)
+ for (var/turf/U in range(O,1))
+ if (turf_clear(U))
+ T = U
+ break
+ new /obj/machinery/shield_capacitor(T)
+ if ("hydrotray")
+ new /obj/machinery/portable_atmospherics/hydroponics(L)
+ if ("watertank")
+ new /obj/structure/reagent_dispensers/watertank(L)
+ if ("fueltank")
+ new /obj/structure/reagent_dispensers/fueltank(L)
+ if ("EOD")
+ if (prob(33))
+ new /obj/structure/closet/bombclosetsecurity(L)
+ else
+ new /obj/structure/closet/bombcloset(L)
+ if ("biosuit")
+ var/list/allsuits = typesof(/obj/structure/closet/l3closet)
+ var/type = pick(allsuits)
+ new type(L)
+ if ("tacticool")
+ new /obj/structure/closet/gimmick/tacticool(L)
+ if ("emergency")
+ new /obj/structure/closet/emcloset(L)
+ if ("russian")
+ new /obj/structure/closet/gimmick/russian(L)
+ if ("firecloset")
+ new /obj/structure/closet/firecloset(L)
+ if ("radsuit")
+ new /obj/structure/closet/radiation(L)
+
+ if ("airpump")
+ var/obj/machinery/portable_atmospherics/powered/M = new /obj/machinery/portable_atmospherics/powered/pump/filled(L)
+ if (prob(60))
+ M.cell = null
+ if ("airscrubber")
+ var/obj/machinery/portable_atmospherics/powered/M = new /obj/machinery/portable_atmospherics/powered/scrubber(L)
+ if (prob(60))
+ M.cell = null
+ //Spawns a complete, but brainless robot chassis. Ready for MMI insertion
+ //It may be missing limbs, and if so cargo can probably scrounge some up in the warehouse.
+ if ("chassis")
+ var/obj/item/robot_parts/robot_suit/RS = new /obj/item/robot_parts/robot_suit(L)
+ if (prob(90))
+ RS.r_arm = new
+ if (prob(90))
+ RS.r_leg = new
+ if (prob(90))
+ RS.l_arm = new
+ if (prob(90))
+ RS.l_leg = new
+ RS.chest = new
+ RS.head = new
+ RS.updateicon()
+
+ if ("suspension")//Xenoarch suspension field generator, they need a spare
+ new /obj/machinery/suspension_gen(L)
+
+ if ("flasher")
+ new /obj/machinery/flasher/portable(L)
+ if ("vendor")
+ new /obj/random/vendor(L, 1)
+
+ if ("piano")
+ new /obj/structure/device/piano(L)
+
+ if ("cablelayer")
+ new /obj/machinery/cablelayer(L)
+
+ if ("floodlight")
+ new /obj/machinery/floodlight(L)
+
+ if ("heater")
+ new /obj/machinery/space_heater(L)
+
+ if ("generator")
+ var/list/generators = list(
+ "/obj/machinery/power/port_gen/pacman" = 1,
+ "/obj/machinery/power/port_gen/pacman/super" = 0.7,
+ "/obj/machinery/power/port_gen/pacman/mrs" = 0.5
+ )
+ var/type = pickweight(generators)
+ new type(L)
+
+ //Spawns a reagent dispenser without most of its cartridges
+ if ("dispenser")
+ var/list/dispensers = list(
+ "/obj/machinery/chemical_dispenser/bar_alc/full" = 0.6,
+ "/obj/machinery/chemical_dispenser/bar_soft/full" = 1,
+ "/obj/machinery/chemical_dispenser/full" = 0.3
+ )
+ var/type = pickweight(dispensers)
+ var/obj/machinery/chemical_dispenser/CD = new type(L)
+ CD.anchored = 0
+ for (var/cart in CD.cartridges)
+ if (prob(90))
+ CD.cartridges -= cart
+
+
+ //Spawns a random live animal crate
+ if ("animal")
+ var/list/animals = list(/obj/structure/largecrate/animal/chick,
+ /obj/structure/largecrate/animal/cat,
+ /obj/structure/largecrate/animal/goat,
+ /obj/structure/largecrate/animal/cow,
+ /obj/structure/largecrate/animal/corgi)
+ var/type = pick(animals)
+ new type(L)
+
+ if ("floorlayer")
+ new /obj/machinery/floorlayer(L)
+
+ if ("jukebox")
+ new /obj/machinery/media/jukebox(L)
+
+ if ("pipemachine")
+ if (prob(50))
+ new /obj/machinery/pipedispenser/disposal(L)
+ else
+ new /obj/machinery/pipedispenser(L)
+
+ //This will be complex
+ //Spawns a random exosuit, Probably not in good condition
+ //It may be missing a cell, have hull damage or internal damage
+ if ("exosuit")
+
+ //First up, weighted list of suits to spawn.
+ //Some of these come preloaded with modules
+ //Those which have dangerous modules have lower weights
+
+ //We may farther remove modules to mitigate it
+ var/list/randsuits = list(
+ "/obj/mecha/working/hoverpod" = 5,
+ "/obj/mecha/working/hoverpod/combatpod" = 0.5,//Comes with weapons
+ "/obj/mecha/working/hoverpod/shuttlepod" = 6,
+ "/obj/mecha/working/ripley" = 5,
+ "/obj/mecha/working/ripley/firefighter" = 6,
+ "/obj/mecha/working/ripley/deathripley" = 0.5,//has a dangerous melee weapon
+ "/obj/mecha/working/ripley/mining" = 4,
+ "/obj/mecha/medical/odysseus" = 6,
+ "/obj/mecha/medical/odysseus/loaded" = 5,
+ "/obj/mecha/combat/durand" = 1,//comes unarmed
+ "/obj/mecha/combat/gygax" = 1.5,//comes unarmed
+ "/obj/mecha/combat/gygax/dark" = 0.5,//has weapons
+ "/obj/mecha/combat/marauder" = 0.6,
+ "/obj/mecha/combat/marauder/seraph" = 0.3,
+ "/obj/mecha/combat/marauder/mauler" = 0.4,
+ "/obj/mecha/combat/phazon" = 0.1
+ )
+ var/type = pickweight(randsuits)
+ var/obj/mecha/exosuit = new type(get_turf(L))
+ //Now we determine the exosuit's condition
+ var/cnd = rand(0,100)
+ switch (cnd)
+ if (0 to 3)
+ //Perfect condition, it was well cared for and put into storage in a pristine state
+ //Nothing is done to it.
+ if (4 to 10)
+ //Poorly maintained.
+ //The internal airtank and power cell will be somewhat depleted, otherwise intact
+ var/P = rand(0,50)
+ P /= 100
+ if (exosuit.cell)//Set the cell to a random charge below 50%
+ exosuit.cell.charge = exosuit.cell.maxcharge * P
+
+ P = rand(50,100)
+ P /= 100
+ if(exosuit.internal_tank)//remove 50-100% of airtank contents
+ exosuit.internal_tank.air_contents.remove(exosuit.internal_tank.air_contents.total_moles * P)
+
+
+ if (11 to 20)
+ //Wear and tear
+ //Hull has light to moderate damage, tank and cell are depleted
+ //Any equipment will have a 25% chance to be lost
+ var/P = rand(0,30)
+ P /= 100
+ if (exosuit.cell)//Set the cell to a random charge below 50%
+ exosuit.cell.charge = exosuit.cell.maxcharge * P
+
+ P = rand(70,100)
+ P /= 100
+ if(exosuit.internal_tank)//remove 50-100% of airtank contents
+ exosuit.internal_tank.air_contents.remove(exosuit.internal_tank.air_contents.total_moles * P)
+
+ exosuit.lose_equipment(25)//Lose modules
+
+ P = rand(10,100)//Set hull integrity
+ P /= 100
+ exosuit.health = initial(exosuit.health)*P
+
+
+ if (21 to 40)
+ //Severe damage
+ //Power cell has 50% chance to be missing or is otherwise low
+ //Significant chance for internal damage
+ //Hull integrity less than half
+ //Each module has a 50% loss chance
+ //Systems may be misconfigured
+ var/P
+
+ if (prob(50))//Remove cell
+ exosuit.cell = null
+ else
+ P = rand(0,20)//or deplete it
+ P /= 100
+ if (exosuit.cell)//Set the cell to a random charge below 50%
+ exosuit.cell.charge = exosuit.cell.maxcharge * P
+
+ P = rand(80,100)
+ P /= 100//Deplete tank
+ if(exosuit.internal_tank)//remove 50-100% of airtank contents
+ exosuit.internal_tank.air_contents.remove(exosuit.internal_tank.air_contents.total_moles * P)
+
+ exosuit.lose_equipment(50)//Lose modules
+ exosuit.random_internal_damage(15)//Internal damage
+
+ P = rand(5,50)//Set hull integrity
+ P /= 100
+ exosuit.health = initial(exosuit.health)*P
+ exosuit.misconfigure_systems(15)
+
+
+ if (41 to 80)
+ //Decomissioned
+ //The exosuit is a writeoff, it was tossed into storage for later scrapping.
+ //Wasnt considered worth repairing, but you still can
+ //Power cell missing, internal tank completely drained or ruptured/
+ //65% chance for each type of internal damage
+ //90% chance to lose each equipment
+ //System settings will be randomly configured
+ var/P
+ if (prob(15))
+ exosuit.cell.rigged = 1//Powercell will explode if you use it
+ else if (prob(50))//Remove cell
+ exosuit.cell = null
+
+ if (exosuit.cell)
+ P = rand(0,20)//or deplete it
+ P /= 100
+ if (exosuit.cell)//Set the cell to a random charge below 50%
+ exosuit.cell.charge = exosuit.cell.maxcharge * P
+
+ exosuit.lose_equipment(90)//Lose modules
+ exosuit.random_internal_damage(50)//Internal damage
+
+ if (!exosuit.hasInternalDamage(MECHA_INT_TANK_BREACH))//If the tank isn't breaches
+ qdel(exosuit.internal_tank)//Then delete it
+ exosuit.internal_tank = null
+
+ P = rand(5,50)//Set hull integrity
+ P /= 100
+ exosuit.health = initial(exosuit.health)*P
+ exosuit.misconfigure_systems(45)
+
+
+ if (81 to 100)
+ //Salvage
+ //The exosuit is wrecked. Spawns a wreckage object instead of a suit
+ //Set the noexplode var so it doesn't explode, then just qdel it
+ //The destroy proc handles wreckage generation
+ exosuit.noexplode = 1
+ qdel(exosuit)
+ exosuit = null
+
+
+ //Finally, so that the exosuit seems like it's been in storage for a while
+ //We will take any malfunctions to their logical conclusion, and set the error states high
+ if (exosuit)
+ //If the tank has a breach, then there will be no air left
+ if (exosuit.hasInternalDamage(MECHA_INT_TANK_BREACH) && exosuit.internal_tank)
+ exosuit.internal_tank.air_contents.remove(exosuit.internal_tank.air_contents.total_moles)
+
+ //If there's an electrical fault, the cell will be complerely drained
+ if (exosuit.hasInternalDamage(MECHA_INT_SHORT_CIRCUIT) && exosuit.cell)
+ exosuit.cell.charge = 0
+
+
+ //Handle power or damage warnings
+ if (exosuit.pr_manage_warnings)
+ exosuit.pr_manage_warnings.process(exosuit)//Trigger them first, if they'll happen
+
+ if (exosuit.power_alert_status)
+ exosuit.pr_manage_warnings.last_power_warning = -99999999
+ //Make it go into infrequent warning state instantly
+ exosuit.pr_manage_warnings.power_warning_delay = 99999999
+ //and set the delay between warnings to a functionally infinite value
+ //so that it will shut up
+
+ if (exosuit.damage_alert_status)
+ exosuit.pr_manage_warnings.last_damage_warning = -99999999
+ exosuit.pr_manage_warnings.damage_warning_delay = 99999999
+
+ exosuit.pr_manage_warnings.process(exosuit)
+ else
+ log_debug("ERROR: Random cargo spawn failed for [stock]")
+
+
+
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index a88be8b03df..43c43973f1f 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -55,8 +55,6 @@
cmd_admin_discord_pm(href_list["discord_msg"])
return
-
-
//Logs all hrefs
if(config && config.log_hrefs && href_logfile)
href_logfile << "[time2text(world.timeofday,"hh:mm")] [src] (usr:[usr]) || [hsrc ? "[hsrc] " : ""][href] "
@@ -178,6 +176,10 @@
var/DBQuery/query = dbcon.NewQuery("INSERT INTO ss13_stats_ie (ckey, IsIE, IsEdge, EdgeHtmlVersion, TrueVersion, ActingVersion, CompatibilityMode, DateUpdated) VALUES (_ckey, _IsIE, _IsEdge, _EdgeHtmlVersion, _TrueVersion, _ActingVersion, _CompatibilityMode, NOW()) ON DUPLICATE KEY UPDATE IsIE = VALUES(IsIe), IsEdge = VALUES(IsEdge), EdgeHtmlVersion = VALUES(EdgeHtmlVersion), TrueVersion = VALUES(TrueVersion), ActingVersion = VALUES(ActingVersion), CompatibilityMode = VALUES(CompatibilityMode), DateUpdated = NOW()")
query.Execute(data)
+ if ("greeting")
+ if (server_greeting)
+ server_greeting.handle_call(href_list, src)
+
return
// Antag contest shit
@@ -291,10 +293,9 @@
send_resources()
nanomanager.send_resources(src)
- var/outdated_greeting_info = server_greeting.find_outdated_info(src)
-
- if (outdated_greeting_info)
- server_greeting.display_to_client(src, outdated_greeting_info)
+ // Server greeting shenanigans.
+ if (server_greeting.find_outdated_info(src, 1))
+ server_greeting.display_to_client(src)
// Check code/modules/admin/verbs/antag-ooc.dm for definition
add_aooc_if_necessary()
@@ -418,10 +419,7 @@
'html/images/talisman.png',
'html/bootstrap/css/bootstrap.min.css',
'html/bootstrap/js/bootstrap.min.js',
- 'html/bootstrap/js/html5shiv.min.js',
- 'html/bootstrap/js/respond.min.js',
'html/jquery/jquery-2.0.0.min.js',
- 'html/iestats/json2.min.js',
'html/iestats/ie-truth.min.js',
'icons/pda_icons/pda_atmos.png',
'icons/pda_icons/pda_back.png',
@@ -589,4 +587,7 @@
set name = "Open Greeting"
set category = "OOC"
- server_greeting.display_to_client(src, server_greeting.find_outdated_info(src))
+ // Update the information just in case.
+ server_greeting.find_outdated_info(src, 1)
+
+ server_greeting.display_to_client(src)
diff --git a/code/modules/client/preferences_notification.dm b/code/modules/client/preferences_notification.dm
index 0e347defd0e..ac42be96411 100644
--- a/code/modules/client/preferences_notification.dm
+++ b/code/modules/client/preferences_notification.dm
@@ -61,13 +61,13 @@
* Returns the HTML required to display this alert.
*/
/datum/client_notification/proc/get_html()
- var/html = "
[note_wrapper[1]]\n"
+ var/html = "
[note_wrapper[1]]"
if (!persistent)
- html += "×\n"
+ html += "×"
html += note_text
- html += "\n[note_wrapper[2]]
"
+ html += "[note_wrapper[2]]
"
return html
diff --git a/code/modules/client/preferences_sql.dm b/code/modules/client/preferences_sql.dm
index 7662f2f3a96..273b961b4cd 100644
--- a/code/modules/client/preferences_sql.dm
+++ b/code/modules/client/preferences_sql.dm
@@ -164,10 +164,10 @@
flv.robot_standard,
flv.robot_engineering,
flv.robot_construction,
- flv.robot_surgeon,
- flv.robot_crisis,
+ flv.robot_medical,
+ flv.robot_rescue,
flv.robot_miner,
- flv.robot_janitor,
+ flv.robot_custodial,
flv.robot_service,
flv.robot_clerical,
flv.robot_security,
@@ -305,10 +305,10 @@
flavour_texts_robot["Standard"] = character_query.item[33]
flavour_texts_robot["Engineering"] = character_query.item[34]
flavour_texts_robot["Construction"] = character_query.item[35]
- flavour_texts_robot["Surgeon"] = character_query.item[36]
- flavour_texts_robot["Crisis"] = character_query.item[37]
+ flavour_texts_robot["Medical"] = character_query.item[36]
+ flavour_texts_robot["Rescue"] = character_query.item[37]
flavour_texts_robot["Miner"] = character_query.item[38]
- flavour_texts_robot["Janitor"] = character_query.item[39]
+ flavour_texts_robot["Custodial"] = character_query.item[39]
flavour_texts_robot["Service"] = character_query.item[40]
flavour_texts_robot["Clerical"] = character_query.item[41]
flavour_texts_robot["Security"] = character_query.item[42]
@@ -479,10 +479,10 @@
flv.robot_standard = :robot_standard,
flv.robot_engineering = :robot_engineering,
flv.robot_construction = :robot_construction,
- flv.robot_surgeon = :robot_surgeon,
- flv.robot_crisis = :robot_crisis,
+ flv.robot_medical = :robot_medical,
+ flv.robot_rescue = :robot_rescue,
flv.robot_miner = :robot_miner,
- flv.robot_janitor = :robot_janitor,
+ flv.robot_custodial = :robot_custodial,
flv.robot_service = :robot_service,
flv.robot_clerical = :robot_clerical,
flv.robot_security = :robot_security,
@@ -526,8 +526,8 @@
current_character = text2num(get_query.item[1])
params[":char_id"] = current_character
- insert_query = dbcon.NewQuery({"INSERT INTO ss13_characters_flavour (char_id, records_employment, records_medical, records_security, records_exploit, flavour_general, flavour_head, flavour_face, flavour_eyes, flavour_torso, flavour_arms, flavour_hands, flavour_legs, flavour_feet, robot_default, robot_standard, robot_engineering, robot_construction, robot_surgeon, robot_crisis, robot_miner, robot_janitor, robot_service, robot_clerical, robot_security, robot_research)
- VALUES (:char_id, :gen_rec, :med_rec, :sec_rec, :exploit_record, :flv_general, :flv_head, :flv_face, :flv_eyes, :flv_torso, :flv_arms, :flv_hands, :flv_legs, :flv_feet, :robot_default, :robot_standard, :robot_engineering, :robot_construction, :robot_surgeon, :robot_crisis, :robot_miner, :robot_janitor, :robot_service, :robot_clerical, :robot_security, :robot_research);"})
+ insert_query = dbcon.NewQuery({"INSERT INTO ss13_characters_flavour (char_id, records_employment, records_medical, records_security, records_exploit, flavour_general, flavour_head, flavour_face, flavour_eyes, flavour_torso, flavour_arms, flavour_hands, flavour_legs, flavour_feet, robot_default, robot_standard, robot_engineering, robot_construction, robot_medical, robot_rescue, robot_miner, robot_custodial, robot_service, robot_clerical, robot_security, robot_research)
+ VALUES (:char_id, :gen_rec, :med_rec, :sec_rec, :exploit_record, :flv_general, :flv_head, :flv_face, :flv_eyes, :flv_torso, :flv_arms, :flv_hands, :flv_legs, :flv_feet, :robot_default, :robot_standard, :robot_engineering, :robot_construction, :robot_medical, :robot_rescue, :robot_miner, :robot_custodial, :robot_service, :robot_clerical, :robot_security, :robot_research);"})
insert_query.Execute(params, 1)
if (insert_query.ErrorMsg())
@@ -618,10 +618,10 @@
params[":robot_standard"] = flavour_texts_robot["Standard"]
params[":robot_engineering"] = flavour_texts_robot["Engineering"]
params[":robot_construction"] = flavour_texts_robot["Construction"]
- params[":robot_surgeon"] = flavour_texts_robot["Surgeon"]
- params[":robot_crisis"] = flavour_texts_robot["Crisis"]
+ params[":robot_medical"] = flavour_texts_robot["Medical"]
+ params[":robot_rescue"] = flavour_texts_robot["Rescue"]
params[":robot_miner"] = flavour_texts_robot["Miner"]
- params[":robot_janitor"] = flavour_texts_robot["Janitor"]
+ params[":robot_custodial"] = flavour_texts_robot["Custodial"]
params[":robot_service"] = flavour_texts_robot["Service"]
params[":robot_clerical"] = flavour_texts_robot["Clerical"]
params[":robot_security"] = flavour_texts_robot["Security"]
diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm
index 8ebc690497c..af6d66d524c 100644
--- a/code/modules/clothing/spacesuits/rig/rig.dm
+++ b/code/modules/clothing/spacesuits/rig/rig.dm
@@ -905,6 +905,53 @@
/mob/living/carbon/human/get_rig()
return back
+//Used in random rig spawning for cargo
+//Randomly deletes modules
+/obj/item/weapon/rig/proc/lose_modules(var/probability)
+ for(var/obj/item/rig_module/module in installed_modules)
+ if (probability)
+ qdel(module)
+
+
+//Fiddles with some wires to possibly make the suit malfunction a little
+//Had to use numeric literals here, the wire defines in rig_wiring.dm weren't working
+//Possibly due to being defined in a later file, or undef'd somewhere
+/obj/item/weapon/rig/proc/misconfigure(var/probability)
+ if (prob(probability))
+ wires.UpdatePulsed(1)//Fiddle with access
+ if (prob(probability))
+ wires.UpdatePulsed(2)//frustrate the AI
+ if (prob(probability))
+ wires.UpdateCut(4)//break the suit
+ if (prob(probability))
+ wires.UpdatePulsed(8)
+ if (prob(probability))
+ wires.UpdateCut(16)
+ if (prob(probability))
+ subverted = 1
+
+//Drains, rigs or removes the cell
+/obj/item/weapon/rig/proc/sabotage_cell()
+ if (!cell)
+ return
+
+ if (prob(50))
+ cell.charge = rand(0, cell.charge*0.5)
+ else if (prob(15))
+ cell.rigged = 1
+ else
+ cell = null
+
+//Depletes or removes the airtank
+/obj/item/weapon/rig/proc/sabotage_tank()
+ if (!air_supply)
+ return
+
+ if (prob(70))
+ air_supply.remove_air(air_supply.air_contents.total_moles)
+ else
+ air_supply = null
+
#undef ONLY_DEPLOY
#undef ONLY_RETRACT
#undef SEAL_DELAY
diff --git a/code/modules/clothing/spacesuits/rig/rig_wiring.dm b/code/modules/clothing/spacesuits/rig/rig_wiring.dm
index 98f8a137028..d27c86500d5 100644
--- a/code/modules/clothing/spacesuits/rig/rig_wiring.dm
+++ b/code/modules/clothing/spacesuits/rig/rig_wiring.dm
@@ -26,6 +26,13 @@
if(RIG_INTERFACE_SHOCK)
rig.electrified = mended ? 0 : -1
rig.shock(usr,100)
+ if(RIG_SYSTEM_CONTROL)
+ if(mended)
+ rig.malfunctioning = 0
+ rig.malfunction_delay = 0
+ else
+ rig.malfunctioning += 10
+ rig.malfunction_delay = 99999999999999999999
/datum/wires/rig/UpdatePulsed(var/index)
diff --git a/code/modules/clothing/spacesuits/rig/suits/station.dm b/code/modules/clothing/spacesuits/rig/suits/station.dm
index f44c5416561..c88cbd95f16 100644
--- a/code/modules/clothing/spacesuits/rig/suits/station.dm
+++ b/code/modules/clothing/spacesuits/rig/suits/station.dm
@@ -114,7 +114,6 @@
/obj/item/rig_module/vision/meson
)
-//Chief Engineer's rig. This is sort of a halfway point between the old hardsuits (voidsuits) and the rig class.
/obj/item/weapon/rig/ce
name = "advanced voidsuit control module"
@@ -130,12 +129,10 @@
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd)
-
req_access = list()
req_one_access = list()
- boot_type = null
- glove_type = null
+ glove_type = /obj/item/clothing/gloves/rig/ce
/obj/item/weapon/rig/ce/equipped
@@ -149,13 +146,8 @@
/obj/item/rig_module/vision/meson
)
- chest_type = /obj/item/clothing/suit/space/rig/ce
- boot_type = null
- glove_type = null
-
-/obj/item/clothing/suit/space/rig/ce
- heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
- body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
+/obj/item/clothing/gloves/rig/ce
+ siemens_coefficient = 0
/obj/item/weapon/rig/hazmat
@@ -169,8 +161,6 @@
helm_type = /obj/item/clothing/head/helmet/space/rig/hazmat
- helm_type = /obj/item/clothing/head/helmet/space/rig/ert
-
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/stack/flag,/obj/item/weapon/storage/box/excavation,/obj/item/weapon/pickaxe,/obj/item/device/healthanalyzer,/obj/item/device/measuring_tape,/obj/item/device/ano_scanner,/obj/item/device/depth_scanner,/obj/item/device/core_sampler,/obj/item/device/gps,/obj/item/device/beacon_locator,/obj/item/device/radio/beacon,/obj/item/weapon/pickaxe/hand,/obj/item/weapon/storage/bag/fossils)
req_access = list()
diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm
index 4f207e43aa4..01f1db51b6c 100644
--- a/code/modules/clothing/suits/jobs.dm
+++ b/code/modules/clothing/suits/jobs.dm
@@ -105,10 +105,34 @@
sprite_sheets = list("Vox" = 'icons/mob/species/vox/suit.dmi')
siemens_coefficient = 0.7
-/obj/item/clothing/suit/storage/det_trench/grey
- name = "grey trenchcoat"
+/obj/item/clothing/suit/storage/det_suit/black
icon_state = "detective2"
+/obj/item/clothing/suit/storage/det_suit/technicolor
+ desc = "A 23rd-century multi-purpose trenchcoat. It's fibres are hyper-absorbent."
+ icon = 'icons/obj/clothing/coloured_detective_coats.dmi'
+ icon_state = "suit_detective_black"
+ item_state = "suit_detective_black"
+ var/suit_color
+ contained_sprite = 1
+
+/obj/item/clothing/suit/storage/det_suit/technicolor/New()
+ if(prob(5))
+ var/list/colors = list("yellow"=2,"red"=1,"white"=1,"orange"=1,"purple"=1,"green"=1,"blue"=1 )
+ var/color = pickweight(colors)
+ icon_state = "suit_detective_[color]"
+ item_state = "suit_detective_[color]"
+ ..()
+
+/obj/item/clothing/suit/storage/det_suit/technicolor/attackby(obj/item/weapon/O as obj, mob/user as mob)
+ if(istype(O, /obj/item/weapon/reagent_containers/glass/paint))
+ var/obj/item/weapon/reagent_containers/glass/paint/P = O
+ suit_color = P.paint_type
+ user.visible_message("[user] soaks \the [src] into [P]!")
+ icon_state = "suit_detective_[suit_color]"
+ item_state = "suit_detective_[suit_color]"
+ ..()
+
//Forensics
/obj/item/clothing/suit/storage/forensics
name = "jacket"
diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm
index e8ab69a13d3..fdba7aae29b 100644
--- a/code/modules/clothing/suits/miscellaneous.dm
+++ b/code/modules/clothing/suits/miscellaneous.dm
@@ -401,4 +401,14 @@
icon_state = "black_hoodie"
item_state = "black_hoodie"
icon_open = "black_hoodie_open"
- icon_closed = "black_hoodie"
\ No newline at end of file
+ icon_closed = "black_hoodie"
+
+/obj/item/clothing/suit/storage/toggle/tracksuit
+ name = "track jacket"
+ desc = "An athletic black and white track jacket."
+ icon = 'icons/obj/tracksuit.dmi'
+ icon_state = "trackjacket"
+ item_state = "trackjacket"
+ icon_open = "trackjacket_open"
+ icon_closed = "trackjacket"
+ contained_sprite = 1
diff --git a/code/modules/clothing/under/jobs/security.dm b/code/modules/clothing/under/jobs/security.dm
index 87bf6aed753..1849d3add49 100644
--- a/code/modules/clothing/under/jobs/security.dm
+++ b/code/modules/clothing/under/jobs/security.dm
@@ -120,6 +120,31 @@
icon_state = "detective2"
desc = "A grey fedora - either the cornerstone of a detective's style or a poor attempt at looking cool, depending on the person wearing it."
+/obj/item/clothing/head/det_hat/technicolor
+ desc = "A 23rd-century fedora. It's fibres are hyper-absorbent."
+ icon = 'icons/obj/clothing/coloured_detective_coats.dmi'
+ icon_state = "hat_detective_black"
+ item_state = "hat_detective_black"
+ var/hat_color
+ contained_sprite = 1
+
+/obj/item/clothing/head/det_hat/technicolor/New()
+ if(prob(5))
+ var/list/colors = list("yellow"=2,"red"=1,"white"=1,"orange"=1,"purple"=1,"green"=1,"blue"=1 )
+ var/color = pickweight(colors)
+ icon_state = "hat_detective_[color]"
+ item_state = "hat_detective_[color]"
+ ..()
+
+obj/item/clothing/head/det_hat/technicolor/attackby(obj/item/weapon/O as obj, mob/user as mob)
+ if(istype(O, /obj/item/weapon/reagent_containers/glass/paint))
+ var/obj/item/weapon/reagent_containers/glass/paint/P = O
+ hat_color = P.paint_type
+ user.visible_message("[user] soaks \the [src] into [P]!")
+ icon_state = "hat_detective_[hat_color]"
+ item_state = "hat_detective_[hat_color]"
+ ..()
+
/*
* Head of Security
diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm
index 1c0829cff9e..4b442277569 100644
--- a/code/modules/clothing/under/miscellaneous.dm
+++ b/code/modules/clothing/under/miscellaneous.dm
@@ -525,3 +525,11 @@
icon_state = "blue_blazer"
item_state = "blue_blazer"
worn_state = "blue_blazer"
+
+/obj/item/clothing/under/track
+ name = "track pants"
+ desc = "Track pants, perfect for squatting."
+ icon = 'icons/obj/tracksuit.dmi'
+ icon_state = "trackpants"
+ item_state = "trackpants"
+ contained_sprite = 1
diff --git a/code/modules/clothing/under/syndicate.dm b/code/modules/clothing/under/syndicate.dm
index 90425c40dcc..75c28b82fb6 100644
--- a/code/modules/clothing/under/syndicate.dm
+++ b/code/modules/clothing/under/syndicate.dm
@@ -19,3 +19,11 @@
worn_state = "tactifool"
siemens_coefficient = 1
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0)
+
+/obj/item/clothing/under/syndicate/tracksuit
+ name = "tactical tracksuit"
+ desc = "A tactical ready tracksuit, perfect for stealthy operations and squatting in cold places."
+ icon = 'icons/obj/tracksuit.dmi'
+ icon_state = "fulltracksuit"
+ item_state = "fulltracksuit"
+ contained_sprite = 1
diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm
index e27162f877e..faae9240827 100644
--- a/code/modules/customitems/item_defines.dm
+++ b/code/modules/customitems/item_defines.dm
@@ -138,6 +138,7 @@ All custom items with worn sprites must follow the contained sprite system: http
user << "You close the pocket altar."
desc = "A black tin box with a symbol painted over it. It shimmers in the light."
+
/obj/item/clothing/head/det_hat/fluff/bell_hat //Brown Hat - Avery Bell - serveris6 - DONE
name = "brown hat"
desc = "A worn mid 20th century brown hat. It seems to have aged very well."
@@ -146,7 +147,6 @@ All custom items with worn sprites must follow the contained sprite system: http
item_state = "bell_hat"
contained_sprite = 1
-
/obj/item/clothing/suit/storage/det_suit/fluff/bell_coat //Pinned Brown Coat - Avery Bell - serveris6 - DONE
name = "pinned brown coat"
desc = "A worn mid 20th century brown trenchcoat. If you look closely at the breast, you can see an ID flap stitched into the leather - 'Avery Bell, Silhouette Co.'."
@@ -256,11 +256,6 @@ All custom items with worn sprites must follow the contained sprite system: http
/obj/item/weapon/contraband/poster/fluff/conservan_poster/New()
serial_number = 59
-/datum/poster/bay_59
- name = "ATLAS poster"
- desc = "ATLAS: For all of Humanity."
- icon_state = "bposter59"
-
/obj/item/weapon/flame/lighter/zippo/fluff/locke_zippo // Fire Extinguisher Zippo - Jacob Locke - completegarbage - DONE
name = "fire extinguisher lighter"
@@ -554,3 +549,76 @@ All custom items with worn sprites must follow the contained sprite system: http
icon = 'icons/obj/custom_items/akela_photo.dmi'
icon_state = "akela_photo"
w_class = 2
+
+
+/obj/item/weapon/implant/fluff/ziva_implant //Heart Condition - Ziva Ta'Kim - sierrakomodo - DONE
+ name = "heart monitor"
+ desc = "A small machine to watch upon broken hearts."
+
+/obj/item/weapon/implant/fluff/ziva_implant/implanted(mob/living/carbon/human/M as mob)
+ if (M.ckey == "sierrakomodo") //just to be sure
+ M.verbs += /mob/living/carbon/human/proc/heart_attack
+ else
+ return
+
+/mob/living/carbon/human/proc/heart_attack()
+ set category = "IC"
+ set name = "Suffer Heart Condition"
+ set desc = "HNNNNG."
+
+ if(last_special > world.time)
+ src << "Your chest still hurts badly!"
+ return
+
+ last_special = world.time + 500
+
+ var/obj/item/organ/F = src.internal_organs_by_name["heart"]
+
+ if(isnull(F))
+ return
+
+ F.damage += 5
+ src << "You feel a stabbing pain in your chest!"
+ playsound(user, 'sound/effects/Heart Beat.ogg', 20, 1)
+
+
+/obj/item/clothing/accessory/badge/fluff/caleb_badge //Worn Badge - Caleb Greene - notmegatron - DONE
+ name = "worn badge"
+ desc = "A simple gold badge denoting the wearer as Head of Security. It is worn and dulled with age, but the name, Caleb Greene, is still clearly legible."
+ icon_state = "badge"
+ icon = 'icons/obj/custom_items/caleb_badge.dmi'
+ item_state = "caleb_badge"
+ icon_state = "caleb_badge"
+ stored_name = "Caleb Greene"
+ badge_string = "NOS Apollo Head of Security"
+ contained_sprite = 1
+
+
+/obj/item/fluff/messa_pressing //Pressing of Messa's Tears - Poslan Kur'yer-Isra - jboy2000000 - DONE
+ name = "pressing of Messa's tears"
+ desc = "As Messa looked at the pain and death wrought on the world she had given life, she cried, and from her tears sprouted these leaves."
+ icon = 'icons/obj/custom_items/cat_religion.dmi'
+ icon_state = "messa"
+ w_class = 2
+
+/obj/item/fluff/srendarr_pressing //Pressing of S'Rendarr's Hand - Poslan Kur'yer-Isra - jboy2000000 - DONE
+ name = "pressing of S'Rendarr's hand"
+ desc = "As S'Rendarr watched her sister cry, she felt rage never known to her before. Her fists clashed with those who upset her sister, and from their blood came these."
+ icon = 'icons/obj/custom_items/cat_religion.dmi'
+ icon_state = "srendarr"
+ w_class = 2
+
+/obj/item/clothing/suit/chaplain_hoodie/fluff/poslan_jacket //Twin Suns Throw-over - Poslan Kur'yer-Isra - jboy2000000 - DONE
+ name = "twin suns throw-over"
+ desc = "A light black jacket, on one side of its breast is the design of a yellow sun, and on the other side there is a smaller blue sun."
+ icon = 'icons/obj/custom_items/cat_religion.dmi'
+ icon_state = "poslan_jacket"
+ item_state = "poslan_jacket"
+ contained_sprite = 1
+
+/obj/item/sign/fluff/alexis_degree //Xenonuerology Doctorate - Alexis Shaw - Tenenza - DONE
+ name = "xenonuerology degree"
+ desc = "Certification for a doctorate in Xenonuerology, made out to Alexis Shaw by the St. Grahelm University of Biesel, authenticated by watermarking."
+ icon_state = "alexis_degree"
+ sign_state = "alexis_degree"
+ w_class = 2
diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm
index 218e25e02f5..ef52f1d8012 100644
--- a/code/modules/events/event_container.dm
+++ b/code/modules/events/event_container.dm
@@ -142,8 +142,10 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Trivial News", /datum/event/trivial_news, 400),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Vermin Infestation",/datum/event/infestation, 60, list(ASSIGNMENT_JANITOR = 20, ASSIGNMENT_SECURITY = 10)),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Wallrot", /datum/event/wallrot, 75, list(ASSIGNMENT_ENGINEER = 5, ASSIGNMENT_GARDENER = 20)),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Clogged Vents", /datum/event/vent_clog, 100),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Clogged Vents", /datum/event/vent_clog, 85),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "False Alarm", /datum/event/false_alarm, 100),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Supply Drop", /datum/event/supply_drop, 80),
+
)
/datum/event_container/moderate
@@ -171,13 +173,13 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
/datum/event_container/major
severity = EVENT_LEVEL_MAJOR
available_events = list(
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Nothing", /datum/event/nothing, 80),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, "Nothing", /datum/event/nothing, 90),
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Blob", /datum/event/blob, 50, list(ASSIGNMENT_ENGINEER = 5,ASSIGNMENT_SECURITY = 5), 1),
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Carp Migration", /datum/event/carp_migration, 60, list(ASSIGNMENT_SECURITY = 10), 1),
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Meteor Wave", /datum/event/meteor_wave, 40, list(ASSIGNMENT_ENGINEER = 10),1),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Space Vines", /datum/event/spacevine, 50, list(ASSIGNMENT_ENGINEER = 10, ASSIGNMENT_GARDENER = 20), 1),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, "Space Vines", /datum/event/spacevine, 60, list(ASSIGNMENT_ENGINEER = 10, ASSIGNMENT_GARDENER = 20), 1),
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Viral Infection", /datum/event/viral_infection, 20, list(ASSIGNMENT_MEDICAL = 13), 1),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Bluespace Bears", /datum/event/bear_attack, 60, list(ASSIGNMENT_SECURITY = 10), 1)
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, "Bluespace Bears", /datum/event/bear_attack, 50, list(ASSIGNMENT_SECURITY = 10), 1)
)
//NOTE: Re added nothing option, but with fairly low weight
diff --git a/code/modules/events/infestation.dm b/code/modules/events/infestation.dm
index 624fc5ce484..d4aa75aad32 100644
--- a/code/modules/events/infestation.dm
+++ b/code/modules/events/infestation.dm
@@ -104,7 +104,7 @@
spawn_area_type = /area/quartermaster/office
locstrings[numlocs] = "the cargo disposals office"
if(LOC_CARGO)
- spawn_area_type = /area/quartermaster/storage
+ spawn_area_type = /area/quartermaster/loading
locstrings[numlocs] = "the cargo bay"
if(LOC_MEETING)
spawn_area_type = /area/bridge/meeting_room
diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm
index 5f9e969e872..808e8470153 100644
--- a/code/modules/events/spacevine.dm
+++ b/code/modules/events/spacevine.dm
@@ -1,7 +1,7 @@
/var/global/spacevines_spawned = 0
/datum/event/spacevine
- announceWhen = 10
+ announceWhen = 30
ic_name = "a biohazard"
/datum/event/spacevine/start()
diff --git a/code/modules/events/supply_drop.dm b/code/modules/events/supply_drop.dm
new file mode 100644
index 00000000000..0052d8ccc1c
--- /dev/null
+++ b/code/modules/events/supply_drop.dm
@@ -0,0 +1,34 @@
+//This mundane event spawns a random crate of loot
+
+/datum/event/supply_drop
+ var/location_name
+ var/turf/spawn_loc
+
+/datum/event/supply_drop/setup()
+ announceWhen = rand(0,80)
+
+/datum/event/supply_drop/start()
+ var/rarity = rand()*3
+ rarity = min(1, rarity)
+ var/quantity = rand(5,15)
+
+ var/area/a = random_station_area()
+ spawn_loc = a.random_space()
+ location_name = a.name
+
+ new /obj/structure/closet/crate/loot(spawn_loc, rarity, quantity)
+
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+ s.set_up(10, 0, spawn_loc)
+ s.start()
+ spawn(2)
+ qdel(s)
+
+
+/datum/event/supply_drop/announce()
+ if (prob(65))//Announce the location
+ command_announcement.Announce("Transport signature of unknown origin detected in [location_name], an object appears to have been beamed aboard [station_name()].", "Unknown Object")
+ else if (prob(25))//Announce the transport, but not the location
+ command_announcement.Announce("External transport signature of unknown origin detected aboard [station_name()], precise destination point cannot be determined, please investigate.", "Unknown Object")
+ //Otherwise, no announcement at all.
+ //Someone will randomly stumble across it, and probably quietly loot it without telling anyone
\ No newline at end of file
diff --git a/code/modules/mob/freelook/update_triggers.dm b/code/modules/mob/freelook/update_triggers.dm
index 482025426eb..c04fdd26f15 100644
--- a/code/modules/mob/freelook/update_triggers.dm
+++ b/code/modules/mob/freelook/update_triggers.dm
@@ -28,10 +28,6 @@
updateVisibility(src)
return ..()
-/obj/structure/New()
- ..()
- updateVisibility(src)
-
// EFFECTS
/obj/effect/Destroy()
diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm
index 8c3d9f62975..221b6d0cec8 100644
--- a/code/modules/mob/living/bot/bot.dm
+++ b/code/modules/mob/living/bot/bot.dm
@@ -60,6 +60,14 @@
else
return 0
+/mob/living/bot/proc/has_ui_access(mob/user)
+ if (access_scanner.allowed(user))
+ return 1
+ if (!locked)
+ return 1
+ if (isAI(user))
+ return 1
+ return 0
/mob/living/bot/attackby(var/obj/item/O, var/mob/user)
if(O.GetID())
diff --git a/code/modules/mob/living/bot/cleanbot.dm b/code/modules/mob/living/bot/cleanbot.dm
index 57bc8164b28..06302b2543b 100644
--- a/code/modules/mob/living/bot/cleanbot.dm
+++ b/code/modules/mob/living/bot/cleanbot.dm
@@ -247,8 +247,12 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
patrol_path = list()
/mob/living/bot/cleanbot/attack_hand(var/mob/user)
+ if (!has_ui_access(user) && !emagged)
+ user << "The unit's interface refuses to unlock!"
+ return
+
var/dat
- dat += "Automatic Station Cleaner v1.0
"
+ dat += "Automatic Station Cleaner v1.1
"
dat += "Status: [on ? "On" : "Off"] "
dat += "Behaviour controls are [locked ? "locked" : "unlocked"] "
dat += "Maintenance panel is [open ? "opened" : "closed"]"
@@ -259,7 +263,7 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
dat += "Odd looking screw twiddled: [screwloose ? "Yes" : "No"] "
dat += "Weird button pressed: [oddbutton ? "Yes" : "No"]"
- user << browse("Cleaner v1.0 controls[dat]", "window=autocleaner")
+ user << browse("Cleaner v1.1 controls[dat]", "window=autocleaner")
onclose(user, "autocleaner")
return
@@ -268,6 +272,11 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
return
usr.set_machine(src)
add_fingerprint(usr)
+
+ if (!has_ui_access(usr) && !emagged)
+ usr << "Insufficient permissions."
+ return
+
switch(href_list["operation"])
if("start")
if(on)
diff --git a/code/modules/mob/living/bot/farmbot.dm b/code/modules/mob/living/bot/farmbot.dm
index c5ef30a2f82..44c5f33b779 100644
--- a/code/modules/mob/living/bot/farmbot.dm
+++ b/code/modules/mob/living/bot/farmbot.dm
@@ -38,8 +38,13 @@
. = ..()
if(.)
return
+
+ if (!has_ui_access(user))
+ user << "The unit's interface refuses to unlock!"
+ return
+
var/dat = ""
- dat += "Automatic Hyrdoponic Assisting Unit v1.0
"
+ dat += "Automatic Hyrdoponic Assisting Unit v1.1
"
dat += "Status: [on ? "On" : "Off"] "
dat += "Water Tank: "
if (tank)
@@ -47,7 +52,7 @@
else
dat += "Error: Watertank not found"
dat += " Behaviour controls are [locked ? "locked" : "unlocked"]"
- if(!locked)
+ if(!locked || issilicon(usr))
dat += "Watering controls: "
dat += "Water plants : [waters_trays ? "Yes" : "No"] "
dat += "Refill watertank : [refills_water ? "Yes" : "No"] "
@@ -60,7 +65,7 @@
dat += "Remove dead plants: [removes_dead ? "Yes" : "No"] "
dat += ""
- user << browse("Farmbot v1.0 controls[dat]", "window=autofarm")
+ user << browse("Farmbot v1.1 controls[dat]", "window=autofarm")
onclose(user, "autofarm")
return
@@ -79,13 +84,18 @@
return
usr.machine = src
add_fingerprint(usr)
- if((href_list["power"]) && (access_scanner.allowed(usr)))
+
+ if (!has_ui_access(usr))
+ usr << "Insufficient permissions."
+ return
+
+ if(href_list["power"])
if(on)
turn_off()
else
turn_on()
- if(locked)
+ if(locked && !issilicon(usr))
return
if(href_list["water"])
diff --git a/code/modules/mob/living/bot/floorbot.dm b/code/modules/mob/living/bot/floorbot.dm
index 91ca86e4abc..21a4a6c619c 100644
--- a/code/modules/mob/living/bot/floorbot.dm
+++ b/code/modules/mob/living/bot/floorbot.dm
@@ -26,9 +26,13 @@
icon_state = "floorbot[on]e"
/mob/living/bot/floorbot/attack_hand(var/mob/user)
+ if (!has_ui_access(user))
+ user << "The unit's interface refuses to unlock!"
+ return
+
user.set_machine(src)
var/dat
- dat += "Automatic Station Floor Repairer v1.0
"
+ dat += "Automatic Station Floor Repairer v1.1
"
dat += "Status: [src.on ? "On" : "Off"] "
dat += "Maintenance panel is [open ? "opened" : "closed"] "
//dat += "Tiles left: [amount] "
@@ -44,7 +48,7 @@
bmode = "Disabled"
dat += "
Bridge Mode : [bmode] "
- user << browse("Repairbot v1.0 controls[dat]", "window=autorepair")
+ user << browse("Repairbot v1.1 controls[dat]", "window=autorepair")
onclose(user, "autorepair")
return
@@ -61,6 +65,11 @@
return
usr.set_machine(src)
add_fingerprint(usr)
+
+ if (!has_ui_access(usr))
+ usr << "Insufficient permissions."
+ return
+
switch(href_list["operation"])
if("start")
if (on)
diff --git a/code/modules/mob/living/bot/medbot.dm b/code/modules/mob/living/bot/medbot.dm
index c4185cfcf19..1ee7aa45363 100644
--- a/code/modules/mob/living/bot/medbot.dm
+++ b/code/modules/mob/living/bot/medbot.dm
@@ -120,8 +120,12 @@
icon_state = "medibot[on]"
/mob/living/bot/medbot/attack_hand(var/mob/user)
+ if (!has_ui_access(user))
+ user << "The unit's interface refuses to unlock!"
+ return
+
var/dat
- dat += "Automatic Medical Unit v1.0
"
+ dat += "Automatic Medical Unit v1.1
"
dat += "Status: [on ? "On" : "Off"] "
dat += "Maintenance panel is [open ? "opened" : "closed"] "
dat += "Beaker: "
@@ -152,7 +156,7 @@
dat += "The speaker switch is [vocal ? "on" : "off"]. Toggle "
- user << browse("Medibot v1.0 controls[dat]", "window=automed")
+ user << browse("Medibot v1.1 controls[dat]", "window=automed")
onclose(user, "automed")
return
@@ -178,13 +182,18 @@
return
usr.set_machine(src)
add_fingerprint(usr)
- if ((href_list["power"]) && access_scanner.allowed(usr))
+
+ if (!has_ui_access(usr))
+ usr << "Insufficient permissions."
+ return
+
+ if (href_list["power"])
if (on)
turn_off()
else
turn_on()
- else if((href_list["adj_threshold"]) && (!locked || issilicon(usr)))
+ else if(href_list["adj_threshold"] && (!locked || issilicon(usr)))
var/adjust_num = text2num(href_list["adj_threshold"])
heal_threshold += adjust_num
if(heal_threshold < 5)
@@ -192,7 +201,7 @@
if(heal_threshold > 75)
heal_threshold = 75
- else if((href_list["adj_inject"]) && (!locked || issilicon(usr)))
+ else if(href_list["adj_inject"] && (!locked || issilicon(usr)))
var/adjust_num = text2num(href_list["adj_inject"])
injection_amount += adjust_num
if(injection_amount < 5)
@@ -200,7 +209,7 @@
if(injection_amount > 15)
injection_amount = 15
- else if((href_list["use_beaker"]) && (!locked || issilicon(usr)))
+ else if(href_list["use_beaker"] && (!locked || issilicon(usr)))
use_beaker = !use_beaker
else if (href_list["eject"] && (!isnull(reagent_glass)))
@@ -210,10 +219,10 @@
else
usr << "You cannot eject the beaker because the panel is locked."
- else if ((href_list["togglevoice"]) && (!locked || issilicon(usr)))
+ else if (href_list["togglevoice"] && (!locked || issilicon(usr)))
vocal = !vocal
- else if ((href_list["declaretreatment"]) && (!locked || issilicon(usr)))
+ else if (href_list["declaretreatment"] && (!locked || issilicon(usr)))
declare_treatment = !declare_treatment
attack_hand(usr)
@@ -255,6 +264,12 @@
qdel(src)
return
+/mob/living/bot/medbot/turn_off()
+ patient = null
+ frustration = 0
+ currently_healing = 0
+ ..()
+
/mob/living/bot/medbot/proc/valid_healing_target(var/mob/living/carbon/human/H)
if(H.stat == DEAD) // He's dead, Jim
return null
diff --git a/code/modules/mob/living/bot/secbot.dm b/code/modules/mob/living/bot/secbot.dm
index 4781ccf8d71..40684be20c7 100644
--- a/code/modules/mob/living/bot/secbot.dm
+++ b/code/modules/mob/living/bot/secbot.dm
@@ -39,7 +39,7 @@
var/next_destination = "__nearest__" // This is the next beacon's ID
var/nearest_beacon // Tag of the beakon that we assume to be the closest one
- var/bot_version = 1.3
+ var/bot_version = 1.4
var/list/threat_found_sounds = new('sound/voice/bcriminal.ogg', 'sound/voice/bjustice.ogg', 'sound/voice/bfreeze.ogg')
var/list/preparing_arrest_sounds = new('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg')
@@ -76,6 +76,9 @@
set_light(0)
/mob/living/bot/secbot/attack_hand(var/mob/user)
+ if (!has_ui_access(user))
+ user << "The unit's interface refuses to unlock!"
+ return
user.set_machine(src)
var/dat
dat += "Automatic Security Unit v[bot_version]
"
@@ -100,11 +103,18 @@
usr.set_machine(src)
add_fingerprint(usr)
- if((href_list["power"]) && (access_scanner.allowed(usr)))
+ if (!has_ui_access(usr))
+ usr << "Insufficient permissions."
+ return
+
+ if(href_list["power"])
if(on)
turn_off()
else
turn_on()
+ attack_hand(usr)
+
+ if (locked && !issilicon(usr))
return
switch(href_list["operation"])
diff --git a/code/modules/mob/living/carbon/alien/diona/diona_powers.dm b/code/modules/mob/living/carbon/alien/diona/diona_powers.dm
index 02baf2734a3..708823d8811 100644
--- a/code/modules/mob/living/carbon/alien/diona/diona_powers.dm
+++ b/code/modules/mob/living/carbon/alien/diona/diona_powers.dm
@@ -229,14 +229,19 @@
src.visible_message("[src] is trying to bite [donor.name]", "\red You start biting [donor.name], you and them must stay still!")
face_atom(get_turf(donor))
- if (do_mob(src, donor, 30, needhand = 0))
+ if (do_mob(src, donor, 40, needhand = 0))
//Attempt to find the blood vessel, but don't create a fake one if its not there.
//If the target doesn't have a vessel its probably due to someone not implementing it properly, like xenos
//We'll still allow it
var/datum/reagents/vessel = donor.get_vessel(1)
var/newDNA
- vessel.remove_reagent("blood", 85, 1)//85 units of blood is enough to affect a human and make them woozy
+ var/datum/reagent/blood/B = vessel.get_master_reagent()
+ var/total_blood = B.volume
+ var/remove_amount = (total_blood - 280) * 0.3
+ if (remove_amount > 0)
+ vessel.remove_reagent("blood", remove_amount, 1)//85 units of blood is enough to affect a human and make them woozy
+ nutrition += remove_amount*0.5
var/list/data = vessel.get_data("blood")
newDNA = data["blood_DNA"]
@@ -246,7 +251,7 @@
donor.adjustBruteLoss(4)
src.visible_message("[src] sucks some blood from [donor.name]", "You extract a delicious mouthful of blood from [donor.name]!")
- nutrition += 40
+
if (newDNA in sampled_DNA)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 248440d09bd..b7b34a87380 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -1154,6 +1154,12 @@
if (src.is_diona())
setup_gestalt(1)
+ max_stamina = species.stamina
+ stamina = max_stamina
+ sprint_speed_factor = species.sprint_speed_factor
+ sprint_cost_factor = species.sprint_cost_factor
+ stamina_recovery = species.stamina_recovery
+ exhaust_threshold = species.exhaust_threshold
if(species)
return 1
else
diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm
index 4ac19363d61..e3fba24bf9f 100644
--- a/code/modules/mob/living/carbon/human/human_damage.dm
+++ b/code/modules/mob/living/carbon/human/human_damage.dm
@@ -18,7 +18,7 @@
var/clone_l = getCloneLoss()
health = maxHealth - oxy_l - tox_l - clone_l - total_burn - total_brute
-
+ update_health_display()
//TODO: fix husking
if( ((maxHealth - total_burn) < config.health_threshold_dead) && stat == DEAD)
ChangeToHusk()
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 4c4d4ffc5f9..acdbda2d244 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -159,9 +159,13 @@ emp_act
var/hit_zone = get_zone_with_miss_chance(target_zone, src)
- if(!hit_zone)
- visible_message("\The [user] misses [src] with \the [I]!")
- return null
+ if(user == src) // Attacking yourself can't miss
+ target_zone = user.zone_sel.selecting
+ if(!target_zone)
+ visible_message("\red [user] misses [src] with \the [I]!")
+ return 0
+
+ var/obj/item/organ/external/affecting = get_organ(target_zone)
if(check_shields(I.force, I, user, target_zone, "the [I.name]"))
return null
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index b1645378a34..08ae2b8022d 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -1,7 +1,6 @@
/mob/living/carbon/human/movement_delay()
var/tally = 0
-
if(species.slowdown)
tally = species.slowdown
@@ -10,8 +9,7 @@
if(embedded_flag)
handle_embedded_objects() //Moving with objects stuck in you can cause bad times.
- if(CE_SPEEDBOOST in chem_effects)
- return -1
+
var/health_deficiency = (100 - health)
if(health_deficiency >= 40) tally += (health_deficiency / 25)
@@ -49,20 +47,30 @@
if(shock_stage >= 10) tally += 3
+
if(aiming && aiming.aiming_at) tally += 5 // Iron sights make you slower, it's a well-known fact.
+ if (drowsyness) tally += 6
+
if(FAT in src.mutations)
tally += 1.5
if (bodytemperature < 283.222)
tally += (283.222 - bodytemperature) / 10 * 1.75
tally += max(2 * stance_damage, 0) //damaged/missing feet or legs is slow
-
if(mRun in mutations)
tally = 0
+ tally += move_delay_mod
+
+ if(tally > 0 && (CE_SPEEDBOOST in chem_effects))
+ tally = max(0, tally-3)
+
return (tally+config.human_delay)
+
+
+
/mob/living/carbon/human/Process_Spacemove(var/check_drift = 0)
//Can we act?
if(restrained()) return 0
diff --git a/code/modules/mob/living/carbon/human/intoxication.dm b/code/modules/mob/living/carbon/human/intoxication.dm
index 524e9f85f5e..24fae1d543d 100644
--- a/code/modules/mob/living/carbon/human/intoxication.dm
+++ b/code/modules/mob/living/carbon/human/intoxication.dm
@@ -1,7 +1,9 @@
#define AE_DIZZY 5
+#define AE_BUZZED_MIN 6
#define AE_SLURRING 15
#define AE_CONFUSION 18
#define AE_CLUMSY 22
+#define AE_BUZZED_MAX 24
#define AE_BLURRING 25
#define AE_VOMIT 40
#define AE_DROWSY 55
@@ -70,3 +72,48 @@ var/mob/living/carbon/human/alcohol_clumsy = 0
if(intoxication > AE_BLACKOUT*SR) // Pass out
paralysis = max(paralysis, 20)
sleeping = max(sleeping, 30)
+
+ if( intoxication >= AE_BUZZED_MIN && intoxication <= AE_BUZZED_MAX && !(locate(/datum/modifier/buzzed) in modifiers))
+ add_modifier(/datum/modifier/buzzed, MODIFIER_CUSTOM)
+
+
+//Pleasant feeling from being slightly drunk
+//Makes you faster and reduces sprint cost
+//Wears off if you get too drunk or too sober, a balance must be maintained
+/datum/modifier/buzzed
+
+/datum/modifier/buzzed/activate()
+ ..()
+ if (ishuman(target))
+ var/mob/living/carbon/human/H = target
+ H.move_delay_mod += -0.75
+
+ H.sprint_cost_factor += -0.1
+
+/datum/modifier/buzzed/deactivate()
+ ..()
+ if (ishuman(target))
+ var/mob/living/carbon/human/H = target
+ H.move_delay_mod -= -0.75
+
+ H.sprint_cost_factor -= -0.1
+
+/datum/modifier/buzzed/custom_validity()
+ if (ishuman(target))
+ var/mob/living/carbon/human/H = target
+ if (H.intoxication >= AE_BUZZED_MIN && H.intoxication <= AE_BUZZED_MAX)
+
+ return 1
+ return 0
+
+#undef AE_DIZZY
+#undef AE_BUZZED_MIN
+#undef AE_SLURRING
+#undef AE_CONFUSION
+#undef AE_CLUMSY
+#undef AE_BUZZED_MAX
+#undef AE_BLURRING
+#undef AE_VOMIT
+#undef AE_DROWSY
+#undef AE_OVERDOSE
+#undef AE_BLACKOUT
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 97853b40fbf..5aa7594b583 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -84,6 +84,9 @@
handle_heartbeat()
+ //Handles regenerating stamina if we have sufficient air and no oxyloss
+ handle_stamina()
+
if (is_diona())
diona_handle_light(DS)
@@ -612,7 +615,51 @@
if (temp_adj < 0)
temp_adj /= (BODYTEMP_COLD_DIVISOR * 5) //don't raise temperature as much as if we were directly exposed
else
- temp_adj /= (BODYTEMP_HEAT_DIVISOR * 5) //don't raise temperature as much as if we were directly exposed
+ failed_last_breath = 0
+ adjustOxyLoss(-3)
+
+
+
+
+ // Hot air hurts :(
+ if((breath.temperature < species.cold_level_1 || breath.temperature > species.heat_level_1) && !(COLD_RESISTANCE in mutations))
+
+ if(breath.temperature <= species.cold_level_1)
+ if(prob(20))
+ src << "You feel your face freezing and icicles forming in your lungs!"
+ else if(breath.temperature >= species.heat_level_1)
+ if(prob(20))
+ src << "You feel your face burning and a searing heat in your lungs!"
+
+ if(breath.temperature >= species.heat_level_1)
+ if(breath.temperature < species.heat_level_2)
+ apply_damage(HEAT_GAS_DAMAGE_LEVEL_1, BURN, "head", used_weapon = "Excessive Heat")
+ fire_alert = max(fire_alert, 2)
+ else if(breath.temperature < species.heat_level_3)
+ apply_damage(HEAT_GAS_DAMAGE_LEVEL_2, BURN, "head", used_weapon = "Excessive Heat")
+ fire_alert = max(fire_alert, 2)
+ else
+ apply_damage(HEAT_GAS_DAMAGE_LEVEL_3, BURN, "head", used_weapon = "Excessive Heat")
+ fire_alert = max(fire_alert, 2)
+
+ else if(breath.temperature <= species.cold_level_1)
+ if(breath.temperature > species.cold_level_2)
+ apply_damage(COLD_GAS_DAMAGE_LEVEL_1, BURN, "head", used_weapon = "Excessive Cold")
+ fire_alert = max(fire_alert, 1)
+ else if(breath.temperature > species.cold_level_3)
+ apply_damage(COLD_GAS_DAMAGE_LEVEL_2, BURN, "head", used_weapon = "Excessive Cold")
+ fire_alert = max(fire_alert, 1)
+ else
+ apply_damage(COLD_GAS_DAMAGE_LEVEL_3, BURN, "head", used_weapon = "Excessive Cold")
+ fire_alert = max(fire_alert, 1)
+
+
+ //breathing in hot/cold air also heats/cools you a bit
+ var/temp_adj = breath.temperature - bodytemperature
+ if (temp_adj < 0)
+ temp_adj /= (BODYTEMP_COLD_DIVISOR * 5) //don't raise temperature as much as if we were directly exposed
+ else
+ temp_adj /= (BODYTEMP_HEAT_DIVISOR * 5) //don't raise temperature as much as if we were directly exposed
var/relative_density = breath.total_moles / (MOLES_CELLSTANDARD * BREATH_PERCENTAGE)
temp_adj *= relative_density
@@ -1020,27 +1067,45 @@
death()
blinded = 1
silent = 0
- return 1
+ else //ALIVE. LIGHTS ARE ON
+ updatehealth() //TODO
- //UNCONSCIOUS. NO-ONE IS HOME
- if((getOxyLoss() > 50) || (health <= config.health_threshold_crit))
- Paralyse(3)
+ if(health <= config.health_threshold_dead || (species.has_organ["brain"] && !has_brain()))
+ death()
+ blinded = 1
+ silent = 0
+ return 1
- if(hallucination)
- if(hallucination >= 20)
- if(prob(3))
- fake_attack(src)
- if(!handling_hal)
- spawn handle_hallucinations() //The not boring kind!
- if(client && prob(5))
- client.dir = pick(2,4,8)
- spawn(rand(20,50))
- client.dir = 1
+ //UNCONSCIOUS. NO-ONE IS HOME
+ if((getOxyLoss() > exhaust_threshold) || (health <= config.health_threshold_crit))
+ Paralyse(3)
- hallucination = max(0, hallucination - 2)
- else
- for(var/atom/a in hallucinations)
- qdel(a)
+ if(hallucination)
+ //Machines do not hallucinate.
+ if (species.flags & IS_SYNTHETIC)
+ hallucination = 0
+ else
+ if(hallucination >= 20)
+ if(prob(3))
+ fake_attack(src)
+ if(!handling_hal)
+ spawn handle_hallucinations() //The not boring kind!
+ if(client && prob(5))
+ client.dir = pick(2,4,8)
+ var/client/C = client
+ spawn(rand(20,50))
+ if(C)
+ C.dir = 1
+
+ if(hallucination<=2)
+ hallucination = 0
+ halloss = 0
+ else
+ hallucination -= 2
+
+ else
+ for(var/atom/a in hallucinations)
+ qdel(a)
if(halloss > 100)
src << "[species.halloss_message_self]"
@@ -1230,34 +1295,74 @@
I = overlays_cache[23]
damageoverlay.overlays += I
+ if( stat == DEAD )
+ sight = SEE_TURFS|SEE_MOBS|SEE_OBJS|SEE_SELF
+ see_in_dark = 8
+ if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO
+ if(healths) healths.icon_state = "health7" //DEAD healthmeter
+ // #TODO-MERGE: Check the indentation of this file! It's awful!
if(healths)
- if (analgesic > 100)
- healths.icon_state = "health_numb"
- else
- switch(hal_screwyhud)
- if(1) healths.icon_state = "health6"
- if(2) healths.icon_state = "health7"
- else
- //switch(health - halloss)
- switch(100 - ((species.flags & NO_PAIN) ? 0 : traumatic_shock))
- if(100 to INFINITY) healths.icon_state = "health0"
- if(80 to 100) healths.icon_state = "health1"
- if(60 to 80) healths.icon_state = "health2"
- if(40 to 60) healths.icon_state = "health3"
- if(20 to 40) healths.icon_state = "health4"
- if(0 to 20) healths.icon_state = "health5"
- else healths.icon_state = "health6"
+ if(client.view != world.view) // If mob dies while zoomed in with device, unzoom them.
+ for(var/obj/item/item in contents)
+ if(item.zoom)
+ item.zoom()
+ break
- if(nutrition_icon)
- switch(nutrition)
- if(450 to INFINITY) nutrition_icon.icon_state = "nutrition0"
- if(350 to 450) nutrition_icon.icon_state = "nutrition1"
- if(250 to 350) nutrition_icon.icon_state = "nutrition2"
- if(150 to 250) nutrition_icon.icon_state = "nutrition3"
- else nutrition_icon.icon_state = "nutrition4"
+ /*
+ if(locate(/obj/item/weapon/gun/energy/sniperrifle, contents))
+ var/obj/item/weapon/gun/energy/sniperrifle/s = locate() in src
+ if(s.zoom)
+ s.zoom()
+ if(locate(/obj/item/device/binoculars, contents))
+ var/obj/item/device/binoculars/b = locate() in src
+ if(b.zoom)
+ b.zoom()
+ */
- if(pressure)
- pressure.icon_state = "pressure[pressure_alert]"
+ else
+ if(is_ventcrawling == 0) // Stops sight returning to normal if inside a vent
+ sight = species.vision_flags
+ see_in_dark = species.darksight
+ see_invisible = see_in_dark>2 ? SEE_INVISIBLE_LEVEL_ONE : SEE_INVISIBLE_LIVING
+
+ if(XRAY in mutations)
+ sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS
+ see_in_dark = 8
+ if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO
+
+ if(seer)
+ var/obj/effect/rune/R = locate() in loc
+ if(R && R.word1 == cultwords["see"] && R.word2 == cultwords["hell"] && R.word3 == cultwords["join"])
+ see_invisible = SEE_INVISIBLE_CULT
+ else
+ seer = 0
+
+ if(!seer)
+ see_invisible = SEE_INVISIBLE_LIVING
+
+ var/equipped_glasses = glasses
+ var/obj/item/weapon/rig/rig = back
+ if(istype(rig) && rig.visor)
+ if(!rig.helmet || (head && rig.helmet == head))
+ if(rig.visor && rig.visor.vision && rig.visor.active && rig.visor.vision.glasses)
+ equipped_glasses = rig.visor.vision.glasses
+ if(equipped_glasses)
+ process_glasses(equipped_glasses)
+
+
+ update_health_display()
+
+
+ if(nutrition_icon)
+ switch(nutrition)
+ if(450 to INFINITY) nutrition_icon.icon_state = "nutrition0"
+ if(350 to 450) nutrition_icon.icon_state = "nutrition1"
+ if(250 to 350) nutrition_icon.icon_state = "nutrition2"
+ if(150 to 250) nutrition_icon.icon_state = "nutrition3"
+ else nutrition_icon.icon_state = "nutrition4"
+
+ if(pressure)
+ pressure.icon_state = "pressure[pressure_alert]"
// if(rest) //Not used with new UI
// if(resting || lying || sleeping) rest.icon_state = "rest1"
@@ -1684,5 +1789,62 @@
if(XRAY in mutations)
sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS
+/mob/living/carbon/human/proc/handle_stamina()
+ if (species.stamina == -1)//If species stamina is -1, it has special mechanics which will be handled elsewhere
+ return//so quit this function
+
+ if (failed_last_breath || oxyloss > exhaust_threshold)//Can't catch our breath if we're suffocating
+ return
+
+ if (stamina != max_stamina)
+ //Any suffocation damage slows stamina regen.
+ //This includes oxyloss from low blood levels
+ var/regen = stamina_recovery * (1 - min(((oxyloss*2) / exhaust_threshold), 1))
+ if (regen > 0)
+ stamina = min(max_stamina, stamina+regen)
+ nutrition -= stamina_recovery*0.4
+ hud_used.move_intent.update_move_icon(src)
+
+/mob/living/carbon/human/proc/update_health_display()
+ if(!healths)
+ return
+
+ if (analgesic > 100)
+ healths.icon_state = "health_numb"
+ else
+ switch(hal_screwyhud)
+ if(1) healths.icon_state = "health6"
+ if(2) healths.icon_state = "health7"
+ else
+ //switch(health - halloss)
+ switch(health - traumatic_shock)
+ if(100 to INFINITY) healths.icon_state = "health0"
+ if(80 to 100) healths.icon_state = "health1"
+ if(60 to 80) healths.icon_state = "health2"
+ if(40 to 60) healths.icon_state = "health3"
+ if(20 to 40) healths.icon_state = "health4"
+ if(0 to 20) healths.icon_state = "health5"
+ else healths.icon_state = "health6"
+
+/mob/living/carbon/human/proc/update_oxy_overlay()
+ if(oxyloss)
+ var/image/I
+ switch(oxyloss)
+ if(10 to 20)
+ I = overlays_cache[11]
+ if(20 to 25)
+ I = overlays_cache[12]
+ if(25 to 30)
+ I = overlays_cache[13]
+ if(30 to 35)
+ I = overlays_cache[14]
+ if(35 to 40)
+ I = overlays_cache[15]
+ if(40 to 45)
+ I = overlays_cache[16]
+ if(45 to INFINITY)
+ I = overlays_cache[17]
+ damageoverlay.overlays += I
+
#undef HUMAN_MAX_OXYLOSS
#undef HUMAN_CRIT_MAX_OXYLOSS
diff --git a/code/modules/mob/living/carbon/human/species/outsider/skeleton.dm b/code/modules/mob/living/carbon/human/species/outsider/skeleton.dm
index abbcdf5e3bc..2b1a97405dc 100644
--- a/code/modules/mob/living/carbon/human/species/outsider/skeleton.dm
+++ b/code/modules/mob/living/carbon/human/species/outsider/skeleton.dm
@@ -54,3 +54,8 @@
"l_foot" = list("path" = /obj/item/organ/external/foot/skeleton),
"r_foot" = list("path" = /obj/item/organ/external/foot/right/skeleton)
)
+
+ stamina = 500 //Tireless automatons
+ stamina_recovery = 1
+ sprint_speed_factor = 0.3
+ exhaust_threshold = 0 //No oxyloss, so zero threshold
diff --git a/code/modules/mob/living/carbon/human/species/outsider/vox.dm b/code/modules/mob/living/carbon/human/species/outsider/vox.dm
index 50dd75474b5..6b2b9142123 100644
--- a/code/modules/mob/living/carbon/human/species/outsider/vox.dm
+++ b/code/modules/mob/living/carbon/human/species/outsider/vox.dm
@@ -16,6 +16,12 @@
smell.
Most humans will never meet a Vox raider, instead learning of this insular species through \
dealing with their traders and merchants; those that do rarely enjoy the experience."
+
+ stamina = 120 // Vox are even faster than unathi and can go longer, but recover slowly
+ sprint_speed_factor = 3
+ stamina_recovery = 1
+ sprint_cost_factor = 1.7
+
speech_sounds = list('sound/voice/shriek1.ogg')
speech_chance = 20
diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm
index 1ed28546007..78cecbec9d4 100644
--- a/code/modules/mob/living/carbon/human/species/species.dm
+++ b/code/modules/mob/living/carbon/human/species/species.dm
@@ -126,6 +126,12 @@
var/rarity_value = 1 // Relative rarity/collector value for this species.
var/ethanol_resistance = 1 // How well the mob resists alcohol, lower values get drunk faster, higher values need to drink more
+ var/stamina = 100 // The maximum stamina this species has. Determines how long it can sprint
+ var/stamina_recovery = 3 // Flat amount of stamina species recovers per proc
+ var/sprint_speed_factor = 0.65 // The percentage of bonus speed you get when sprinting. 0.4 = 40%
+ var/sprint_cost_factor = 1.0 // Multiplier on stamina cost for sprinting
+ var/exhaust_threshold = 50 // When stamina runs out, the mob takes oxyloss up til this value. Then collapses and drops to walk
+
// Determines the organs that the species spawns with and
var/list/has_organ = list( // which required-organ checks are conducted.
"heart" = /obj/item/organ/heart,
@@ -388,3 +394,33 @@
H.client.screen |= overlay
return 1
+
+/datum/species/proc/handle_sprint_cost(var/mob/living/carbon/human/H, var/cost)
+ cost *= H.sprint_cost_factor
+ if (H.stamina == -1)
+ log_debug("Error: Species with special sprint mechanics has not overridden cost function.")
+ return 0
+
+ var/remainder = 0
+ if (H.stamina > cost)
+ H.stamina -= cost
+ H.hud_used.move_intent.update_move_icon(H)
+ return 1
+ else if (H.stamina > 0)
+ remainder = cost - H.stamina
+ H.stamina = 0
+ else
+ remainder = cost
+
+ H.adjustOxyLoss(remainder*0.25)
+ H.adjustHalLoss(remainder*0.25)
+ H.updatehealth()
+ H.update_oxy_overlay()
+
+ if (H.oxyloss >= (exhaust_threshold * 0.8))
+ H.m_intent = "walk"
+ H.hud_used.move_intent.update_move_icon(H)
+ H << span("danger", "You're too exhausted to run anymore!")
+ return 0
+ H.hud_used.move_intent.update_move_icon(H)
+ return 1
diff --git a/code/modules/mob/living/carbon/human/species/station/golem.dm b/code/modules/mob/living/carbon/human/species/station/golem.dm
index efb46ed2127..ecfc88ea11b 100644
--- a/code/modules/mob/living/carbon/human/species/station/golem.dm
+++ b/code/modules/mob/living/carbon/human/species/station/golem.dm
@@ -34,6 +34,11 @@
death_message = "becomes completely motionless..."
+ stamina = 500 //Tireless automatons
+ stamina_recovery = 1
+ sprint_speed_factor = 0.3
+ exhaust_threshold = 0 //No oxyloss, so zero threshold
+
/datum/species/golem/handle_post_spawn(var/mob/living/carbon/human/H)
if(H.mind)
H.mind.assigned_role = "Golem"
diff --git a/code/modules/mob/living/carbon/human/species/station/station.dm b/code/modules/mob/living/carbon/human/species/station/station.dm
index e675a1acfce..4e0d1d4884e 100644
--- a/code/modules/mob/living/carbon/human/species/station/station.dm
+++ b/code/modules/mob/living/carbon/human/species/station/station.dm
@@ -19,6 +19,11 @@
/datum/species/human/get_bodytype()
return "Human"
+ stamina = 130 // Humans can sprint for longer than any other species
+ stamina_recovery = 5
+ sprint_speed_factor = 0.8
+ sprint_cost_factor = 0.7
+
/datum/species/unathi
name = "Unathi"
short_name = "una"
@@ -37,6 +42,10 @@
num_alternate_languages = 2
secondary_langs = list("Sinta'unathi")
name_language = "Sinta'unathi"
+ stamina = 120 // Unathi have the shortest but fastest sprint of all
+ sprint_speed_factor = 3
+ stamina_recovery = 5
+ sprint_cost_factor = 1.8
rarity_value = 3
blurb = "A heavily reptillian species, Unathi (or 'Sinta as they call themselves) hail from the \
@@ -105,6 +114,12 @@
ethanol_resistance = 0.8//Gets drunk a little faster
rarity_value = 2
+ stamina = 90 // Tajarans evolved to maintain a steady pace in the snow, sprinting wastes energy
+ stamina_recovery = 4
+ sprint_speed_factor = 0.55
+ sprint_cost_factor = 1
+
+
blurb = "The Tajaran race is a species of feline-like bipeds hailing from the planet of Ahdomai in the \
S'randarr system. They have been brought up into the space age by the Humans and Skrell, and have been \
influenced heavily by their long history of Slavemaster rule. They have a structured, clan-influenced way \
@@ -156,7 +171,6 @@
num_alternate_languages = 2
secondary_langs = list("Skrellian")
name_language = null
-
rarity_value = 3
spawn_flags = CAN_JOIN | IS_WHITELISTED
@@ -169,6 +183,10 @@
reagent_tag = IS_SKRELL
ethanol_resistance = 0.5//gets drunk faster
+ stamina = 90
+ sprint_speed_factor = 1.1 //Evolved for rapid escapes from predators
+
+
/datum/species/diona
name = "Diona"
short_name = "dio"
@@ -244,6 +262,41 @@
reagent_tag = IS_DIONA
+ stamina = -1 // Diona sprinting uses energy instead of stamina
+ sprint_speed_factor = 0.4 //Speed gained is minor
+
+
+/datum/species/diona/handle_sprint_cost(var/mob/living/carbon/human/H, var/cost)
+ var/datum/dionastats/DS = H.get_dionastats()
+
+ if (!DS)
+ return 0 //Something is very wrong
+
+ var/remainder = cost
+
+ if (H.radiation)
+ if (H.radiation > (cost*0.5))//Radiation counts as double energy
+ H.radiation -= cost*0.5
+ return 1
+ else
+ remainder = cost - (H.radiation*2)
+ H.radiation = 0
+
+ if (DS.stored_energy > remainder)
+ DS.stored_energy -= remainder
+ return 1
+ else
+ remainder -= DS.stored_energy
+ DS.stored_energy = 0
+ H.adjustHalLoss(remainder*5, 1)
+ H.updatehealth()
+ H.m_intent = "walk"
+ H.hud_used.move_intent.update_move_icon(H)
+ H << span("danger", "We have expended our energy reserves, and cannot continue to move at such a pace. We must find light!")
+ return 0
+
+
+
/datum/species/diona/can_understand(var/mob/other)
var/mob/living/carbon/alien/diona/D = other
if(istype(D))
@@ -348,6 +401,23 @@
heat_discomfort_strings = list(
"Your CPU temperature probes warn you that you are approaching critical heat levels!"
)
+ stamina = -1 // Machines use power and generate heat, stamina is not a thing
+ sprint_speed_factor = 0.85 // About as capable of speed as a human
+
+
+/datum/species/machine/handle_sprint_cost(var/mob/living/carbon/human/H, var/cost)
+ if (H.stat == CONSCIOUS)
+ H.bodytemperature += cost*1.35
+ H.nutrition -= cost*0.9
+ if (H.nutrition > 0)
+ return 1
+ else
+ H.Weaken(30)
+ H.m_intent = "walk"
+ H.hud_used.move_intent.update_move_icon(H)
+ H << span("danger", "ERROR: Power reserves depleted, emergency shutdown engaged. Backup power will come online in 60 seconds, initiate charging as primary directive.")
+ playsound(H.loc, 'sound/machines/buzz-two.ogg', 100, 0)
+ return 0
/datum/species/machine/handle_death(var/mob/living/carbon/human/H)
..()
@@ -399,6 +469,12 @@
flesh_color = "#E6E600"
base_color = "#575757"
+ stamina = 100 // Long period of sprinting, but relatively low speed gain
+ sprint_speed_factor = 0.5
+ sprint_cost_factor = 0.27
+ stamina_recovery = 1//slow recovery
+
+
inherent_verbs = list(
/mob/living/carbon/human/proc/bugbite, //weaker version of gut.
)
diff --git a/code/modules/mob/living/devour.dm b/code/modules/mob/living/devour.dm
index 43ff8e1ac83..4e7452abf5f 100644
--- a/code/modules/mob/living/devour.dm
+++ b/code/modules/mob/living/devour.dm
@@ -27,6 +27,8 @@
//This is set true if there are any mobs in this mob's contents. they will be slowly digested.
//just used as a boolean to minimise extra processing
+/mob/living/var/mob/living/devouring = null
+//If we are currently devouring a mob, a reference to it goes here.
/mob/living/proc/attempt_devour(var/mob/living/victim, var/eat_types, var/mouth_size = null)
@@ -44,6 +46,16 @@
src << "\red You can't eat yourself!"
return 0
+ if (devouring == victim)
+ src << span("danger","You're already eating that!.")
+ return
+
+ if (ishuman(src))
+ var/mob/living/carbon/human/H = src
+ var/obj/item/blocked = H.check_mouth_coverage()
+ if(blocked)
+ user << "\The [blocked] is in the way!"
+ return
//This check is exploit prevention.
//Nymphs have seperate mechanics for gaining biomass from other diona
@@ -106,6 +118,7 @@
//This function will start consuming the victim by taking bites out of them.
//Victim or attacker moving will interrupt it
//A bite will be taken every 4 seconds
+ devouring = victim
var/bite_delay = 4
var/bite_size = mouth_size * 0.5
var/num_bites_needed = (victim.mob_size*victim.mob_size)/bite_size//total bites needed to eat it from full health
@@ -141,7 +154,7 @@
var/i = 0
for (i=0;i < num_bites_needed;i++)
- if(do_mob(src, victim, bite_delay*10))
+ if(do_mob(src, victim, bite_delay*10) && devouring == victim)
face_atom(victim)
victim.adjustCloneLoss(victim_maxhealth*PEPB)
victim.adjustHalLoss(victim_maxhealth*PEPB*5)//Being eaten hurts!
@@ -153,14 +166,17 @@
src.visible_message("[src] finishes devouring [victim]","You finish devouring [victim]")
handle_devour_mess(src, victim, vessel, 1)
qdel(victim)
+ devouring = null
break
else
+ devouring = null
if (victimloc != victim.loc)
src << "[victim] moved away, you need to keep it still. Try grabbing, stunning or killing it first."
else if (ourloc != src.loc)
src << "You moved! Devouring cancelled"
else
src << "Devouring Cancelled"//reason unknown, maybe the eater got stunned?
+ //This can also happen if you start devouring something else
break
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index fd4870cdeb9..1625d85b629 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -55,3 +55,15 @@
var/datum/reagents/metabolism/ingested = null
var/underdoor //Used for mobs that can walk through maintenance hatches - drones, pais, and spiderbots
var/life_tick = 0 // The amount of life ticks that have processed on this mob.
+
+
+ //These values are duplicated from the species datum so we can handle things on a per-mob basis, allows for chemicals to affect them
+ var/stamina = 0
+ var/max_stamina = 100//Maximum stamina. We start taking oxyloss when this runs out while sprinting
+ var/sprint_speed_factor = 0.4
+ var/sprint_cost_factor = 1
+ var/stamina_recovery = 1
+ var/min_walk_delay = 0//When move intent is walk, movedelay is clamped to this value as a lower bound
+ var/exhaust_threshold = 50
+
+ var/move_delay_mod = 0//Added to move delay, used for calculating movement speeds. Provides a centralised value for modifiers to alter
diff --git a/code/modules/mob/living/living_powers.dm b/code/modules/mob/living/living_powers.dm
index b7d640a5ee8..1eb7c868f93 100644
--- a/code/modules/mob/living/living_powers.dm
+++ b/code/modules/mob/living/living_powers.dm
@@ -32,10 +32,37 @@
attempt_devour(L, eat_types, mouth_size)
-/*
-/mob/living/verb/devourverb(var/mob/living/victim)//For situations where species inherent verbs isnt suitable
- set category = "Abilities"
- set name = "Devour Creature"
- set desc = "Attempt to eat a nearby creature, swallowing it whole if small enough, or eating it piece by piece otherwise"
- attempt_devour(victim, eat_types, mouth_size)
-*/
\ No newline at end of file
+/mob/living/verb/set_walk_speed()
+ set category = "IC"
+ set name = "Adjust walk speed"
+ set desc = "Allows you to adjust your walking speed to a slower value than normal, or reset it. Does not make you faster."
+
+
+ //First a quick block of code to calculate our normal/best movedelay
+ var/delay = config.walk_speed + movement_delay()
+ var/speed = 1 / (delay/10)
+ var/newspeed
+ var/list/options = list("No limit",speed*0.95,speed*0.9,speed*0.85,speed*0.8,speed*0.7,speed*0.6,speed*0.5, "Custom")
+
+
+ var/response = input(src, "Your current walking speed is [speed] tiles per second. This menu allows you to limit it to a lower value, by applying a multiplier to that. Please choose a value, select custom to enter your own, or No limit to set your walk speed to the maximum. This menu will not make you move any faster than usual, it is only for allowing you to move at a slower pace than normal, for roleplay purposes. Values set here will not affect your sprinting speed", "Limit Walking speed") as null|anything in options
+
+ if (isnull(response))
+ return
+ else if (response == "No limit")
+ src << "Movement speed has now been set to normal, limits removed."
+ src.min_walk_delay = 0
+ return
+ else if (response == "Custom")
+ response = input(src, "Please enter the exact speed you want to walk at, in tiles per second. This value must be less than [speed] and greater than zero", "Limit Walking speed") as num
+ newspeed = response
+ else
+ newspeed = text2num(response)
+
+ if (!newspeed || newspeed >= speed || newspeed <= 0)
+ src << "Error, invalid value entered. Walk speed has not been changed"
+ return
+
+
+ src << "Walking speed has now been limited to [newspeed] tiles per second, which is [(newspeed/speed)*100]% of your normal walking speed."
+ src.min_walk_delay = (10 / newspeed)
\ No newline at end of file
diff --git a/code/modules/mob/living/silicon/pai/recruit.dm b/code/modules/mob/living/silicon/pai/recruit.dm
index b022b820234..c23416361f7 100644
--- a/code/modules/mob/living/silicon/pai/recruit.dm
+++ b/code/modules/mob/living/silicon/pai/recruit.dm
@@ -73,6 +73,7 @@ var/datum/paiController/paiController // Global handler for pAI candidates
t = input("Enter any OOC comments", "pAI OOC Comments", candidate.comments) as message
if(t)
candidate.comments = sanitize(t)
+
if("save")
candidate.savefile_save(usr)
if("load")
@@ -95,7 +96,7 @@ var/datum/paiController/paiController // Global handler for pAI candidates
p.alertUpdate()
usr << browse(null, "window=paiRecruit")
return
-
+ candidate.savefile_save(usr)
recruitWindow(usr, href_list["allow_submit"] != "0")
/datum/paiController/proc/recruitWindow(var/mob/M as mob, allowSubmit = 1)
@@ -206,18 +207,6 @@ var/datum/paiController/paiController // Global handler for pAI candidates
-
"
dat += ""
user << browse(dat, "window=keycard_auth;size=500x250")
if(screen == 2)
@@ -149,6 +151,12 @@
if("Revoke Emergency Maintenance Access")
revoke_maint_all_access()
feedback_inc("alert_keycard_auth_maintRevoke",1)
+ if("Cyborg Crisis Override")
+ cyborg_crisis_override()
+ feedback_inc("alert_keycard_auth_borgCrisis",1)
+ if("Disable Cyborg Crisis Override")
+ disable_cyborg_crisis_override()
+ feedback_inc("alert_keycard_auth_borgDisable",1)
if("Emergency Response Team")
if(is_ert_blocked())
usr << "\red All emergency response teams are dispatched and can not be called at this time."
@@ -177,3 +185,15 @@ var/global/maint_all_access = 0
if(maint_all_access && src.check_access_list(list(access_maint_tunnels)))
return 1
return ..(M)
+
+/proc/cyborg_crisis_override()
+ for(var/mob/living/silicon/robot/M in silicon_mob_list)
+ M.crisis_override = 1
+ world << "Attention!"
+ world << "Cyborg crisis override has been activated, station bound cyborgs are allowed to select the combat module during code red."
+
+/proc/disable_cyborg_crisis_override()
+ for(var/mob/living/silicon/robot/M in silicon_mob_list)
+ M.crisis_override = 0
+ world << "Attention!"
+ world << "Cyborg crisis override has been deactivated, station bound cyborgs are no longer allowed to select the combat module."
diff --git a/code/modules/vehicles/cargo_train.dm b/code/modules/vehicles/cargo_train.dm
index 62d14702ba5..46b0d1851d4 100644
--- a/code/modules/vehicles/cargo_train.dm
+++ b/code/modules/vehicles/cargo_train.dm
@@ -6,7 +6,7 @@
on = 0
powered = 1
locked = 0
-
+ move_speed = 3
load_item_visible = 1
load_offset_x = 0
mob_offset_y = 7
@@ -29,7 +29,7 @@
anchored = 0
passenger_allowed = 0
locked = 0
-
+ //move_speed = 3
load_item_visible = 1
load_offset_x = 0
load_offset_y = 4
@@ -352,15 +352,12 @@
/obj/vehicle/train/cargo/engine/update_car(var/train_length, var/active_engines)
src.train_length = train_length
src.active_engines = active_engines
-
+ move_speed = initial(move_speed) //so that engines that have been turned off don't lag behind
//Update move delay
- if(!is_train_head() || !on)
- move_delay = initial(move_delay) //so that engines that have been turned off don't lag behind
- else
- move_delay = max(0, (-car_limit * active_engines) + train_length - active_engines) //limits base overweight so you cant overspeed trains
- move_delay *= (1 / max(1, active_engines)) * 2 //overweight penalty (scaled by the number of engines)
- move_delay += config.run_speed //base reference speed
- move_delay *= 1.1 //makes cargo trains 10% slower than running when not overweight
+ if(is_train_head() && on)
+ var/remainder = (car_limit * active_engines) - (train_length - active_engines)
+ if (remainder)
+ move_speed -= 0.25 * remainder //makes cargo trains 10% slower than running when not overweight
/obj/vehicle/train/cargo/trolley/update_car(var/train_length, var/active_engines)
src.train_length = train_length
diff --git a/code/modules/vehicles/train.dm b/code/modules/vehicles/train.dm
index d6c0b934e1c..d3136822aca 100644
--- a/code/modules/vehicles/train.dm
+++ b/code/modules/vehicles/train.dm
@@ -2,7 +2,6 @@
name = "train"
dir = 4
- move_delay = 1
health = 100
maxhealth = 100
@@ -29,13 +28,19 @@
var/old_loc = get_turf(src)
if(..())
if(tow)
- tow.Move(old_loc)
+ tow.forceMove(old_loc)
return 1
else
if(lead)
unattach()
return 0
+obj/vehicle/train/forceMove()
+ var/old_loc = get_turf(src)
+ ..()
+ if(tow)
+ tow.forceMove(old_loc)
+
/obj/vehicle/train/Bump(atom/Obstacle)
if(!istype(Obstacle, /atom/movable))
return
@@ -51,7 +56,7 @@
var/mob/living/M = A
visible_message("\red [src] knocks over [M]!")
M.apply_effects(5, 5) //knock people down if you hit them
- M.apply_damages(22 / move_delay) // and do damage according to how fast the train is going
+ M.apply_damages(10 * move_speed) // and do damage according to how fast the train is going
if(istype(load, /mob/living/carbon/human))
var/mob/living/D = load
D << "\red You hit [M]!"
diff --git a/code/modules/vehicles/vehicle.dm b/code/modules/vehicles/vehicle.dm
index c7177c3a4d2..0b3e5c68e35 100644
--- a/code/modules/vehicles/vehicle.dm
+++ b/code/modules/vehicles/vehicle.dm
@@ -28,7 +28,10 @@
var/stat = 0
var/emagged = 0
var/powered = 0 //set if vehicle is powered and should use fuel when moving
- var/move_delay = 1 //set this to limit the speed of the vehicle
+
+ move_speed = 2//Expressed in tiles per second. This is used to control how fast the vehicle moves
+
+ var/move_delay//DO NOT MANUALLY SET THIS. For internal use only
var/obj/item/weapon/cell/cell
var/charge_use = 5 //set this to adjust the amount of power the vehicle uses per move
@@ -45,8 +48,22 @@
/obj/vehicle/New()
..()
//spawn the cell you want in each vehicle
+ calc_delay()
+
+/obj/vehicle/proc/calc_delay()
+ if (!move_speed || move_speed < 0)//Shouldn't happen
+ move_speed = 0
+ move_delay = 999999999
+ return 0
+
+ move_delay = (1 / move_speed) * 10 * config.vehicle_delay_multiplier
+ return 1
+
/obj/vehicle/Move()
+ if (!move_speed)
+ return 0
+
if(world.time > l_move_time + move_delay)
var/old_loc = get_turf(src)
if(on && powered && cell.charge < charge_use)
@@ -170,6 +187,7 @@
// Vehicle procs
//-------------------------------------------
/obj/vehicle/proc/turn_on()
+ calc_delay()
if(stat)
return 0
if(powered && cell.charge < charge_use)
@@ -299,6 +317,7 @@
if(ismob(C))
buckle_mob(C)
+ calc_delay()
return 1
/obj/vehicle/user_unbuckle_mob(var/mob/user)
@@ -346,7 +365,7 @@
unbuckle_mob(load)
load = null
-
+ calc_delay()
return 1
diff --git a/code/setup.dm.back b/code/setup.dm.back
index 1a5a872a239..7ed113467e4 100644
--- a/code/setup.dm.back
+++ b/code/setup.dm.back
@@ -1090,3 +1090,28 @@ var/list/be_special_flags = list(
#define RESPAWN_CREW 18000
#define RESPAWN_ANIMAL 3000
#define RESPAWN_MINISYNTH 6000
+
+// Night lighting controller times
+// The time (in ticks based on worldtime2ticks()) that various actions trigger
+#define MORNING_LIGHT_RESET 252000 // 7am or 07:00 - lighting restores to normal in morning
+#define NIGHT_LIGHT_ACTIVE 648000 // 6pm or 18:00 - night lighting mode activates
+
+//Cargo random stock vars
+//These are used in randomstock.dm
+//And also for generating random loot crates in crates.dm
+#define TOTAL_STOCK 80//The total number of items we'll spawn in cargo stock
+
+
+#define STOCK_UNCOMMON_PROB 23
+//The probability, as a percentage for each item, that we'll choose from the uncommon spawns list
+
+#define STOCK_RARE_PROB 2.8
+//The probability, as a percentage for each item, that we'll choose from the rare spawns list
+
+//If an item is not rare or uncommon, it will be chosen from the common spawns list.
+//So the probability of a common item is 100 - (uncommon + rare)
+
+
+#define STOCK_LARGE_PROB 75
+//Large items are spawned on predetermined locations.
+//For each large spawn marker, this is the chance that we will spawn there
diff --git a/config/example/config.txt b/config/example/config.txt
index 6e389dae59f..b9638ee7fd6 100644
--- a/config/example/config.txt
+++ b/config/example/config.txt
@@ -260,6 +260,9 @@ GATEWAY_DELAY 18000
## Uncomment to restrict non-admins from using humanoid alien races
USEALIENWHITELIST
+## Comment this to disable automated station night lighting
+NIGHT_LIGHTING
+
## Comment this to unrestrict the number of alien players allowed in the round. The number represents the number of alien players for every human player.
#ALIEN_PLAYER_RATIO 0.2
##Remove the # to let ghosts spin chairs
diff --git a/config/example/game_options.txt b/config/example/game_options.txt
index 614e696df2e..39866b8e4c6 100644
--- a/config/example/game_options.txt
+++ b/config/example/game_options.txt
@@ -44,18 +44,23 @@ REVIVAL_BRAIN_LIFE -1
## These values get directly added to values and totals in-game. To speed things up make the number negative, to slow things down, make the number positive.
-## These modify the run/walk speed of all mobs before the mob-specific modifiers are applied.
-RUN_SPEED 2
-WALK_SPEED 5
+## This will globally modify the movement speed of all mobs.
+## Note that it can do so unproportionally, meaning you'll lose the relationships between the different species. As such, modifying the delay multipliers below should be preferred.
+WALK_SPEED 4
+## The following two will globally affect all movement speed, while retaining the proportional relationships between the different mob's movement speeds.
+## Ideally, you want to modify these. Any value between 0 and 1 will increase speed by decreasing the movement delay, anything above 1 will slow everyone down by increasing the delay.
+WALK_DELAY_MULTIPLIER 1
+RUN_DELAY_MULTIPLIER 1
+VEHICLE_DELAY_MULTIPLIER 1
## The variables below affect the movement of specific mob types.
-HUMAN_DELAY 0
-ROBOT_DELAY 0
-MONKEY_DELAY 0
-ALIEN_DELAY 0
-METROID_DELAY 0
-ANIMAL_DELAY 0
+HUMAN_DELAY 1
+ROBOT_DELAY 1
+MONKEY_DELAY 1
+ALIEN_DELAY 1
+METROID_DELAY 1
+ANIMAL_DELAY 1
### Miscellaneous ###
diff --git a/html/bootstrap/js/html5shiv.min.js b/html/bootstrap/js/html5shiv.min.js
deleted file mode 100644
index 355afd10608..00000000000
--- a/html/bootstrap/js/html5shiv.min.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
-* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
-*/
-!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document);
\ No newline at end of file
diff --git a/html/bootstrap/js/respond.min.js b/html/bootstrap/js/respond.min.js
deleted file mode 100644
index 80a7b69dcce..00000000000
--- a/html/bootstrap/js/respond.min.js
+++ /dev/null
@@ -1,5 +0,0 @@
-/*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl
- * Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT
- * */
-
-!function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b
+
06 November 2016
+
Alberyk updated:
+
+
Ported new door crushing mechanics from baystation, now door crushing someone will push them away from the door, instead of just stunning them.
+
Opening airlocks with brain damage may be more difficult now.
+
Golems should be space-proof now.
+
Golems are a bit slower, but are more resistant to trauma.
+
New sprites for golems.
+
Added new flavors of flashlights.
+
Ported glowsticks from polaris.
+
You can now carry flashlights in your armor.
+
Wizard robes and voidsuits can now carry magic related items.
+
The chaplain hoodie, and nun robes, can now store religious objects in their suit storage.
+
New recorder, camera and lantern sprites.
+
Cult hoods have the same armor as the robes now.
+
Tweaked the weapons available to the heister in their skipjack.
+
Added a canesword, replacing the switchblade in the concealed cane.
+
Tweaked the chances of ghetto handguns malfunctioning.
+
Randomized most of the items you can find in the maintenance tunnels.
+
Added a new rare finding in xenoarchaeology.
+
Heisters found another pirate haven to continue their operations.
+
Added new poster designs.
+
Added magboots and insulated gauntlets to the chief engineer hardsuit.
+
Selecting the combat module as cyborgs now requires an event to be activated via the keycard authentication device, an upgrade from roboticis or code delta also triggers also allows it.
+
+
Bedshaped updated:
+
+
Added a button on APCs to set the area lights to a 'night-mode' which is dimmer and saves energy.
+
Added an automated system to turn 'night-mode' on in hallways between 6pm and 7am in station time.
+
New implementation of magnetic door locks, can be found in armory and eng secure storage.
+
Added hydraulic servo sounds.
+
Crew monitoring computer: Lightened the font colors of suff and tox.
+
+
Fire and Glory updated:
+
+
Added the Kneebreaker Hammer to the code, at a later date this'll become a traitor uplink item.
+
Ported our old biosuits.
+
+
LordFowl updated:
+
+
Gave detective a colourable trench-coat, solving the Dick Tracy Dilemma.
+
Wooden closets now have a slightly larger capacity, indicative of their greater size.
+
Added three new energy-based weapons, one designed purely for pest-control.
+
Added a new rare handpistol, based off of a proposed competitor to the NT Mk58.
+
Added a new pet for the Head of Security - the PTR-7 Tranquilizer Rifle.
+
Syndicate manhack delivery grenades are now available via the traitor uplink.
+
Manhacks will no longer attack anyone belonging to the 'syndicate' faction, including Heist pirates.
+
Tweaked loadout customisation whitelists, generally making them more restrictive by role.
+
Dismembered limbs no longer suffer from pixelation due to unnecessary rotation of the sprite.
+
Severed heads retain the facial features of their owner.
+
Heads impaled on spears now look like the head of their owner.
+
It is no longer possible to be older or younger than your species ought to be.
+
+
Nanako updated:
+
+
Explosions now have proper directional sounds, so you can tell the direction that something exploded in.
+
Distant explosions now cause mild screen shaking proportional to power and distance.
+
Adjusted sound volumes for several actions related to windows and airlocks.
+
Adds a major new feature: Cargo stocking. Now the cargo bay, and especially the warehouse, will come pre-stocked with a large variety of assorted junk, supplies and useful oddities, intended for distributing to whoever on the station will enjoy/use them the most.
+
Potted plants now have varied sprites instead of always being the same.
+
Small animals can now crawl over crates. Crates will now only block bullets sometimes. Also a few insects had their density fixed.
+
Clusterbangs and Floor layer machines now function properly. Maybe you'll find them in cargo...
+
Added a bountiful new event!
+
Fixed nymphs being able to kill people by repeated DNA sampling.
+
Added a new sprinting mechanic. Moving in run mode is now much faster, but limited by stamina or a special species mechanic. Sprinting works slightly differently for each species.
+
Moving in walk mode is now as fast as run used to be. Walk is the new default speed.
+
Added a walkspeed limiting feature. Use Limit Walk Speed in the IC tab, or alt+click on the walk/run button to bring up a menu. This allows limiting your walk speed very precisely to any value below normal. It can only slow you down, will not increase speed.
+
Natural recovery of suffocation damage is now slower.
+
Alcohol, caffienated drinks, and several performance enhancing drugs now have interactions with movement, sprinting and stamina.
+
Toolboxes that are full of stuff now hit much harder, but spill their contents.
+
Fixed unathi being able to eat while wearing face-covering helmets, and being able to rapidly spam devour.
+
Fixed being unable to save pAI information. It now autosaves whenever anything is entered. Save and load buttons are obsolete and removed.
+
Altered some event probabilities. And the announcement for space vines is now delayed significantly longer
+
+
OneOneThreeEight updated:
+
+
Adds back previous oldcode functionality of telescopic batons.
+
Slightly nerfed weaken() potency from telescopic baton to prevent overt stunlocking.
Fixed Alt-Clicking PDA on ground displaying wrong message.
+
Fixed smallbot controls access.
+
Fixed mice being able to open and close laptop computers.
+
+
+
29 October 2016
+
inselc updated:
+
+
Fixed welding tool not using fuel when repairing IPCs, and repairing IPCs in switched-off state.
+
Fixed Artificers healing other constructs.
+
Fixed invisible runes triggering message when trying to clean the tile they're on.
+
Added Juggernaut ability to smash machines.
+
+
13 October 2016
Alberyk updated:
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml
index 116b40cf5c3..b72a3578e69 100644
--- a/html/changelogs/.all_changelog.yml
+++ b/html/changelogs/.all_changelog.yml
@@ -3372,3 +3372,123 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
2016-10-13:
Alberyk:
- bugfix: Flashing someone should not turn them into a revolutionary anymore.
+2016-10-29:
+ inselc:
+ - bugfix: Fixed welding tool not using fuel when repairing IPCs, and repairing IPCs
+ in switched-off state.
+ - bugfix: Fixed Artificers healing other constructs.
+ - bugfix: Fixed invisible runes triggering message when trying to clean the tile
+ they're on.
+ - rscadd: Added Juggernaut ability to smash machines.
+2016-10-30:
+ inselc:
+ - bugfix: Fixed hungry Shades.
+ - bugfix: Fixed AI being able to interact with IV drips.
+ - bugfix: Fixed Alt-Clicking PDA on ground displaying wrong message.
+ - bugfix: Fixed smallbot controls access.
+ - bugfix: Fixed mice being able to open and close laptop computers.
+2016-11-06:
+ Alberyk:
+ - tweak: Ported new door crushing mechanics from baystation, now door crushing someone
+ will push them away from the door, instead of just stunning them.
+ - rscadd: Opening airlocks with brain damage may be more difficult now.
+ - tweak: Golems should be space-proof now.
+ - tweak: Golems are a bit slower, but are more resistant to trauma.
+ - imageadd: New sprites for golems.
+ - rscadd: Added new flavors of flashlights.
+ - rscadd: Ported glowsticks from polaris.
+ - rscadd: You can now carry flashlights in your armor.
+ - rscadd: Wizard robes and voidsuits can now carry magic related items.
+ - rscadd: The chaplain hoodie, and nun robes, can now store religious objects in
+ their suit storage.
+ - imageadd: New recorder, camera and lantern sprites.
+ - tweak: Cult hoods have the same armor as the robes now.
+ - tweak: Tweaked the weapons available to the heister in their skipjack.
+ - rscadd: Added a canesword, replacing the switchblade in the concealed cane.
+ - tweak: Tweaked the chances of ghetto handguns malfunctioning.
+ - rscadd: Randomized most of the items you can find in the maintenance tunnels.
+ - rscadd: Added a new rare finding in xenoarchaeology.
+ - rscadd: Heisters found another pirate haven to continue their operations.
+ - rscadd: Added new poster designs.
+ - rscadd: Added magboots and insulated gauntlets to the chief engineer hardsuit.
+ - tweak: Selecting the combat module as cyborgs now requires an event to be activated
+ via the keycard authentication device, an upgrade from roboticis or code delta
+ also triggers also allows it.
+ Bedshaped:
+ - rscadd: Added a button on APCs to set the area lights to a 'night-mode' which
+ is dimmer and saves energy.
+ - rscadd: Added an automated system to turn 'night-mode' on in hallways between
+ 6pm and 7am in station time.
+ - rscadd: New implementation of magnetic door locks, can be found in armory and
+ eng secure storage.
+ - soundadd: Added hydraulic servo sounds.
+ - tweak: 'Crew monitoring computer: Lightened the font colors of suff and tox.'
+ Fire and Glory:
+ - rscadd: Added the Kneebreaker Hammer to the code, at a later date this'll become
+ a traitor uplink item.
+ - tweak: Ported our old biosuits.
+ LordFowl:
+ - rscadd: Gave detective a colourable trench-coat, solving the Dick Tracy Dilemma.
+ - tweak: Wooden closets now have a slightly larger capacity, indicative of their
+ greater size.
+ - rscadd: Added three new energy-based weapons, one designed purely for pest-control.
+ - rscadd: Added a new rare handpistol, based off of a proposed competitor to the
+ NT Mk58.
+ - rscadd: Added a new pet for the Head of Security - the PTR-7 Tranquilizer Rifle.
+ - rscadd: Syndicate manhack delivery grenades are now available via the traitor
+ uplink.
+ - rscadd: Manhacks will no longer attack anyone belonging to the 'syndicate' faction,
+ including Heist pirates.
+ - tweak: Tweaked loadout customisation whitelists, generally making them more restrictive
+ by role.
+ - tweak: Dismembered limbs no longer suffer from pixelation due to unnecessary rotation
+ of the sprite.
+ - bugfix: Severed heads retain the facial features of their owner.
+ - bugfix: Heads impaled on spears now look like the head of their owner.
+ - bugfix: It is no longer possible to be older or younger than your species ought
+ to be.
+ Nanako:
+ - rscadd: Explosions now have proper directional sounds, so you can tell the direction
+ that something exploded in.
+ - rscadd: Distant explosions now cause mild screen shaking proportional to power
+ and distance.
+ - tweak: Adjusted sound volumes for several actions related to windows and airlocks.
+ - rscadd: 'Adds a major new feature: Cargo stocking. Now the cargo bay, and especially
+ the warehouse, will come pre-stocked with a large variety of assorted junk,
+ supplies and useful oddities, intended for distributing to whoever on the station
+ will enjoy/use them the most.'
+ - rscadd: Potted plants now have varied sprites instead of always being the same.
+ - tweak: Small animals can now crawl over crates. Crates will now only block bullets
+ sometimes. Also a few insects had their density fixed.
+ - bugfix: Clusterbangs and Floor layer machines now function properly. Maybe you'll
+ find them in cargo...
+ - rscadd: Added a bountiful new event!
+ - tweak: Fixed nymphs being able to kill people by repeated DNA sampling.
+ - rscadd: Added a new sprinting mechanic. Moving in run mode is now much faster,
+ but limited by stamina or a special species mechanic. Sprinting works slightly
+ differently for each species.
+ - tweak: Moving in walk mode is now as fast as run used to be. Walk is the new default
+ speed.
+ - rscadd: Added a walkspeed limiting feature. Use Limit Walk Speed in the IC tab,
+ or alt+click on the walk/run button to bring up a menu. This allows limiting
+ your walk speed very precisely to any value below normal. It can only slow you
+ down, will not increase speed.
+ - tweak: Natural recovery of suffocation damage is now slower.
+ - rscadd: Alcohol, caffienated drinks, and several performance enhancing drugs now
+ have interactions with movement, sprinting and stamina.
+ - rscadd: Toolboxes that are full of stuff now hit much harder, but spill their
+ contents.
+ - bugfix: Fixed unathi being able to eat while wearing face-covering helmets, and
+ being able to rapidly spam devour.
+ - bugfix: Fixed being unable to save pAI information. It now autosaves whenever
+ anything is entered. Save and load buttons are obsolete and removed.
+ - tweak: Altered some event probabilities. And the announcement for space vines
+ is now delayed significantly longer
+ OneOneThreeEight:
+ - tweak: Adds back previous oldcode functionality of telescopic batons.
+ - tweak: Slightly nerfed weaken() potency from telescopic baton to prevent overt
+ stunlocking.
+ inselc:
+ - tweak: Updated PDA Power Monitor UI.
+ - tweak: Sleeper Console now uses fancy NanoUI. Added printout feature. Added sanity
+ checks.
diff --git a/html/changelogs/Bedshaped-PR-1058.yml b/html/changelogs/Bedshaped-PR-1058.yml
deleted file mode 100644
index 2253744bb18..00000000000
--- a/html/changelogs/Bedshaped-PR-1058.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: Bedshaped
-
-delete-after: True
-
-changes:
- - tweak: "Crew monitoring computer: Lightened the font colors of suff and tox."
diff --git a/html/changelogs/LordFowl - MiscUpdate.yml b/html/changelogs/LordFowl - MiscUpdate.yml
deleted file mode 100644
index ccd771d2d48..00000000000
--- a/html/changelogs/LordFowl - MiscUpdate.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-################################
-# Example Changelog File
-#
-# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
-#
-# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
-# When it is, any changes listed below will disappear.
-#
-# Valid Prefixes:
-# bugfix
-# wip (For works in progress)
-# tweak
-# soundadd
-# sounddel
-# rscadd (general adding of nice things)
-# rscdel (general deleting of nice things)
-# imageadd
-# imagedel
-# maptweak
-# spellcheck (typo fixes)
-# experiment
-#################################
-
-# Your name.
-author: LordFowl
-
-# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
-delete-after: True
-
-# Any changes you've made. See valid prefix list above.
-# INDENT WITH TWO SPACES. NOT TABS. SPACES.
-# SCREW THIS UP AND IT WON'T WORK.
-# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
-# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
-changes:
- - rscadd: "Added three new energy-based weapons, one designed purely for pest-control."
- - rscadd: "Added a new rare handpistol, based off of a proposed competitor to the NT Mk58."
- - rscadd: "Added a new pet for the Head of Security - the PTR-7 Tranquilizer Rifle."
- - rscadd: "Syndicate manhack delivery grenades are now available via the traitor uplink."
- - rscadd: "Manhacks will no longer attack anyone belonging to the 'syndicate' faction, including Heist pirates."
- - tweak: "Tweaked loadout customisation whitelists, generally making them more restrictive by role."
- - bugfix: "It is no longer possible to be older or younger than your species ought to be."
diff --git a/html/changelogs/alberyk-PR-1000.yml b/html/changelogs/alberyk-PR-1000.yml
deleted file mode 100644
index 53c7eb16886..00000000000
--- a/html/changelogs/alberyk-PR-1000.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-author: Alberyk
-
-delete-after: True
-
-changes:
- - tweak: "Ported new door crushing mechanics from baystation, now door crushing someone will push them away from the door, instead of just stunning them."
- - rscadd: "Opening airlocks with brain damage may be more difficult now."
diff --git a/html/changelogs/alberyk-PR-1007.yml b/html/changelogs/alberyk-PR-1007.yml
deleted file mode 100644
index 2c525661219..00000000000
--- a/html/changelogs/alberyk-PR-1007.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-author: Alberyk
-
-delete-after: True
-
-changes:
- - tweak: "Golems should be space-proof now."
- - tweak: "Golems are a bit slower, but are more resistant to trauma."
- - imageadd: "New sprites for golems."
diff --git a/html/changelogs/alberyk-PR-1025.yml b/html/changelogs/alberyk-PR-1025.yml
deleted file mode 100644
index a7fb437667e..00000000000
--- a/html/changelogs/alberyk-PR-1025.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-author: Alberyk
-
-delete-after: True
-
-changes:
- - rscadd: "Added new flavors of flashlights."
- - rscadd: "Ported glowsticks from polaris."
- - rscadd: "You can now carry flashlights in your armor."
- - rscadd: "Wizard robes and voidsuits can now carry magic related items."
- - rscadd: "The chaplain hoodie, and nun robes, can now store religious objects in their suit storage."
- - imageadd: "New recorder, camera and lantern sprites."
- - tweak: "Cult hoods have the same armor as the robes now."
diff --git a/html/iestats/json2.min.js b/html/iestats/json2.min.js
deleted file mode 100644
index a766943fa40..00000000000
--- a/html/iestats/json2.min.js
+++ /dev/null
@@ -1 +0,0 @@
-"object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(a){return a<10?"0"+a:a}function this_value(){return this.valueOf()}function quote(a){return rx_escapable.lastIndex=0,rx_escapable.test(a)?'"'+a.replace(rx_escapable,function(a){var b=meta[a];return"string"==typeof b?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function str(a,b){var c,d,e,f,h,g=gap,i=b[a];switch(i&&"object"==typeof i&&"function"==typeof i.toJSON&&(i=i.toJSON(a)),"function"==typeof rep&&(i=rep.call(b,a,i)),typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";if(gap+=indent,h=[],"[object Array]"===Object.prototype.toString.apply(i)){for(f=i.length,c=0;cAurorastation Welcome Screen
-
+
@@ -46,9 +46,9 @@