Merge branch 'master' of https://github.com/Yawn-Wider/YWPolarisVore into UpstreamMergeApril2020

This commit is contained in:
Erik
2020-04-11 16:53:35 -07:00
2323 changed files with 1758903 additions and 156537 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ SUBSYSTEM_DEF(ai)
// var/mob/living/L = currentrun[currentrun.len]
var/datum/ai_holder/A = currentrun[currentrun.len]
--currentrun.len
if(!A || QDELETED(A)) // Doesn't exist or won't exist soon.
if(!A || QDELETED(A) || A.busy) // Doesn't exist or won't exist soon or not doing it this tick
continue
if(times_fired % 4 == 0 && A.holder.stat != DEAD)
A.handle_strategicals()
+1 -1
View File
@@ -38,7 +38,7 @@ SUBSYSTEM_DEF(air)
current_cycle = 0
var/simulated_turf_count = 0
for(var/turf/simulated/S in turfs)
for(var/turf/simulated/S in world)
simulated_turf_count++
S.update_air_properties()
CHECK_TICK
+45
View File
@@ -0,0 +1,45 @@
// We manually initialize the alarm handlers instead of looping over all existing types
// to make it possible to write: camera_alarm.triggerAlarm() rather than SSalarm.managers[datum/alarm_handler/camera].triggerAlarm() or a variant thereof.
/var/global/datum/alarm_handler/atmosphere/atmosphere_alarm = new()
/var/global/datum/alarm_handler/camera/camera_alarm = new()
/var/global/datum/alarm_handler/fire/fire_alarm = new()
/var/global/datum/alarm_handler/motion/motion_alarm = new()
/var/global/datum/alarm_handler/power/power_alarm = new()
SUBSYSTEM_DEF(alarm)
name = "Alarm"
wait = 2 SECONDS
priority = FIRE_PRIORITY_ALARM
init_order = INIT_ORDER_ALARM
var/list/datum/alarm/all_handlers
var/tmp/list/currentrun = null
var/static/list/active_alarm_cache = list()
/datum/controller/subsystem/alarm/Initialize()
all_handlers = list(atmosphere_alarm, camera_alarm, fire_alarm, motion_alarm, power_alarm)
. = ..()
/datum/controller/subsystem/alarm/fire(resumed = FALSE)
if(!resumed)
src.currentrun = all_handlers.Copy()
active_alarm_cache.Cut()
var/list/currentrun = src.currentrun // Cache for sanic speed
while (currentrun.len)
var/datum/alarm_handler/AH = currentrun[currentrun.len]
currentrun.len--
AH.process()
active_alarm_cache += AH.alarms
if (MC_TICK_CHECK)
return
/datum/controller/subsystem/alarm/proc/active_alarms()
return active_alarm_cache.Copy()
/datum/controller/subsystem/alarm/proc/number_of_active_alarms()
return active_alarm_cache.len
/datum/controller/subsystem/alarm/stat_entry()
..("[number_of_active_alarms()] alarm\s")
+17
View File
@@ -0,0 +1,17 @@
SUBSYSTEM_DEF(assets)
name = "Assets"
init_order = INIT_ORDER_ASSETS
flags = SS_NO_FIRE
var/list/cache = list()
var/list/preload = list()
/datum/controller/subsystem/assets/Initialize(timeofday)
for(var/type in typesof(/datum/asset) - list(/datum/asset, /datum/asset/simple))
var/datum/asset/A = new type()
A.register()
preload = cache.Copy() //don't preload assets generated during the round
for(var/client/C in GLOB.clients)
addtimer(CALLBACK(GLOBAL_PROC, .proc/getFilesSlow, C, preload, FALSE), 10)
return ..()
+2 -2
View File
@@ -8,9 +8,9 @@ SUBSYSTEM_DEF(atoms)
init_order = INIT_ORDER_ATOMS
flags = SS_NO_FIRE
var/initialized = INITIALIZATION_INSSATOMS
var/static/initialized = INITIALIZATION_INSSATOMS
// var/list/created_atoms // This is never used, so don't bother. ~Leshana
var/old_initialized
var/static/old_initialized
var/list/late_loaders
var/list/created_atoms
+87
View File
@@ -0,0 +1,87 @@
SUBSYSTEM_DEF(chat)
name = "Chat"
flags = SS_TICKER
wait = 1 // SS_TICKER means this runs every tick
priority = FIRE_PRIORITY_CHAT
init_order = INIT_ORDER_CHAT
var/list/msg_queue = list()
/datum/controller/subsystem/chat/Initialize(timeofday)
init_vchat()
..()
/datum/controller/subsystem/chat/fire()
var/list/msg_queue = src.msg_queue // Local variable for sanic speed.
for(var/i in msg_queue)
var/client/C = i
var/list/messages = msg_queue[C]
msg_queue -= C
if (C)
C << output(jsEncode(messages), "htmloutput:putmessage")
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/chat/stat_entry()
..("C:[msg_queue.len]")
/datum/controller/subsystem/chat/proc/queue(target, time, message, handle_whitespace = TRUE)
if(!target || !message)
return
if(!istext(message))
stack_trace("to_chat called with invalid input type")
return
// Currently to_chat(world, ...) gets sent individually to each client. Consider.
if(target == world)
target = GLOB.clients
//Some macros remain in the string even after parsing and fuck up the eventual output
var/original_message = message
message = replacetext(message, "\n", "<br>")
message = replacetext(message, "\improper", "")
message = replacetext(message, "\proper", "")
if(isnull(time))
time = world.time
var/list/messageStruct = list("time" = time, "message" = message);
if(islist(target))
for(var/I in target)
var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible
if(!C)
return
if(!C?.chatOutput || C.chatOutput.broken) //A player who hasn't updated his skin file.
//Send it to the old style output window.
DIRECT_OUTPUT(C, original_message)
continue
// // Client still loading, put their messages in a queue - Actually don't, logged already in database.
// if(!C.chatOutput.loaded && C.chatOutput.message_queue && islist(C.chatOutput.message_queue))
// C.chatOutput.message_queue[++C.chatOutput.message_queue.len] = messageStruct
// continue
LAZYINITLIST(msg_queue[C])
msg_queue[C][++msg_queue[C].len] = messageStruct
else
var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible
if(!C)
return
if(!C?.chatOutput || C.chatOutput.broken) //A player who hasn't updated his skin file.
DIRECT_OUTPUT(C, original_message)
return
// // Client still loading, put their messages in a queue - Actually don't, logged already in database.
// if(!C.chatOutput.loaded && C.chatOutput.message_queue && islist(C.chatOutput.message_queue))
// C.chatOutput.message_queue[++C.chatOutput.message_queue.len] = messageStruct
// return
LAZYINITLIST(msg_queue[C])
msg_queue[C][++msg_queue[C].len] = messageStruct
+22 -4
View File
@@ -1,6 +1,8 @@
SUBSYSTEM_DEF(events)
name = "Events"
wait = 20
wait = 2 SECONDS
var/tmp/list/currentrun = null
var/list/datum/event/active_events = list()
var/list/datum/event/finished_events = list()
@@ -11,23 +13,37 @@ SUBSYSTEM_DEF(events)
var/datum/event_meta/new_event = new
/datum/controller/subsystem/events/Initialize()
allEvents = typesof(/datum/event) - /datum/event
event_containers = list(
EVENT_LEVEL_MUNDANE = new/datum/event_container/mundane,
EVENT_LEVEL_MODERATE = new/datum/event_container/moderate,
EVENT_LEVEL_MAJOR = new/datum/event_container/major
)
allEvents = typesof(/datum/event) - /datum/event
if(global.using_map.use_overmap)
GLOB.overmap_event_handler.create_events(global.using_map.overmap_z, global.using_map.overmap_size, global.using_map.overmap_event_areas)
return ..()
/datum/controller/subsystem/events/fire(resumed)
for(var/datum/event/E in active_events)
if (!resumed)
src.currentrun = active_events.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while (currentrun.len)
var/datum/event/E = currentrun[currentrun.len]
currentrun.len--
if(E.processing_active)
E.process()
if (MC_TICK_CHECK)
return
for(var/i = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR)
var/list/datum/event_container/EC = event_containers[i]
EC.process()
/datum/controller/subsystem/events/stat_entry()
..("E:[active_events.len]")
/datum/controller/subsystem/events/Recover()
if(SSevents.active_events)
active_events |= SSevents.active_events
@@ -35,6 +51,8 @@ SUBSYSTEM_DEF(events)
finished_events |= SSevents.finished_events
/datum/controller/subsystem/events/proc/event_complete(var/datum/event/E)
active_events -= E
if(!E.event_meta || !E.severity) // datum/event is used here and there for random reasons, maintaining "backwards compatibility"
log_debug("Event of '[E.type]' with missing meta-data has completed.")
return
@@ -50,7 +68,7 @@ SUBSYSTEM_DEF(events)
log_debug("Event '[EM.name]' has completed at [stationtime2text()].")
/datum/controller/subsystem/events/proc/delay_events(var/severity, var/delay)
var/list/datum/event_container/EC = event_containers[severity]
var/datum/event_container/EC = event_containers[severity]
EC.next_event_time += delay
/datum/controller/subsystem/events/proc/RoundEnd()
+57 -35
View File
@@ -1,42 +1,64 @@
SUBSYSTEM_DEF(inactivity)
name = "AFK Kick"
wait = 600
flags = SS_BACKGROUND | SS_NO_TICK_CHECK
name = "Inactivity"
wait = 1 MINUTE
flags = SS_NO_INIT | SS_BACKGROUND
var/tmp/list/client_list
var/number_kicked = 0
/datum/controller/subsystem/inactivity/fire()
if(config.kick_inactive)
for(var/i in GLOB.clients)
var/client/C = i
if(C.is_afk(config.kick_inactive MINUTES) && !C.holder) // VOREStation Edit - Allow admins to idle
to_chat(C,"<span class='warning'>You have been inactive for more than [config.kick_inactive] minute\s and have been disconnected.</span>")
var/information
/datum/controller/subsystem/inactivity/fire(resumed = FALSE)
if (!config.kick_inactive)
can_fire = FALSE
return
if (!resumed)
client_list = GLOB.clients.Copy()
if(C.mob)
if(ishuman(C.mob))
var/job
var/mob/living/carbon/human/H = C.mob
var/datum/data/record/R = find_general_record("name", H.real_name)
if(R)
job = R.fields["real_rank"]
if(!job && H.mind)
job = H.mind.assigned_role
if(!job && H.job)
job = H.job
if(job)
information = " while [job]."
while(client_list.len)
var/client/C = client_list[client_list.len]
client_list.len--
if(C.is_afk(config.kick_inactive MINUTES) && can_kick(C))
to_chat(C, "<span class='warning'>You have been inactive for more than [config.kick_inactive] minute\s and have been disconnected.</span>")
else if(issilicon(C.mob))
information = " while a silicon."
if(isAI(C.mob))
var/mob/living/silicon/ai/A = C.mob
empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(A.loc)
global_announcer.autosay("[A] has been moved to intelligence storage.", "Artificial Intelligence Oversight")
A.clear_client()
information = " while an AI."
var/information
if(C.mob)
if(ishuman(C.mob))
var/job
var/mob/living/carbon/human/H = C.mob
var/datum/data/record/R = find_general_record("name", H.real_name)
if(R)
job = R.fields["real_rank"]
if(!job && H.mind)
job = H.mind.assigned_role
if(!job && H.job)
job = H.job
if(job)
information = " while [job]."
var/adminlinks
adminlinks = " (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[C.mob.x];Y=[C.mob.y];Z=[C.mob.z]'>JMP</a>|<A HREF='?_src_=holder;cryoplayer=\ref[C.mob]'>CRYO</a>)"
else if(isobserver(C.mob))
information = " while a ghost."
log_and_message_admins("being kicked for AFK[information][adminlinks]", C.mob)
else if(issilicon(C.mob))
information = " while a silicon."
if(isAI(C.mob))
var/mob/living/silicon/ai/A = C.mob
empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(A.loc)
global_announcer.autosay("[A] has been moved to intelligence storage.", "Artificial Intelligence Oversight")
A.clear_client()
information = " while an AI."
qdel(C)
var/adminlinks
adminlinks = " (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[C.mob.x];Y=[C.mob.y];Z=[C.mob.z]'>JMP</a>|<A HREF='?_src_=holder;cryoplayer=\ref[C.mob]'>CRYO</a>)"
log_and_message_admins("being kicked for AFK[information][adminlinks]", C.mob)
qdel(C)
number_kicked++
if (MC_TICK_CHECK)
return
/datum/controller/subsystem/inactivity/stat_entry()
..("Kicked: [number_kicked]")
/datum/controller/subsystem/inactivity/proc/can_kick(var/client/C)
if(C.holder) return FALSE //VOREStation Add - Don't kick admins.
return TRUE
+142
View File
@@ -0,0 +1,142 @@
SUBSYSTEM_DEF(job)
name = "Job"
init_order = INIT_ORDER_JOB
flags = SS_NO_FIRE
var/list/occupations = list() //List of all jobs
var/list/datum/job/name_occupations = list() //Dict of all jobs, keys are titles
var/list/type_occupations = list() //Dict of all jobs, keys are types
var/list/department_datums = list()
var/debug_messages = FALSE
/datum/controller/subsystem/job/Initialize(timeofday)
if(!department_datums.len)
setup_departments()
if(!occupations.len)
setup_occupations()
return ..()
/datum/controller/subsystem/job/proc/setup_occupations(faction = "Station")
occupations = list()
var/list/all_jobs = subtypesof(/datum/job)
if(!all_jobs.len)
to_chat(world, span("warning", "Error setting up jobs, no job datums found"))
return FALSE
for(var/J in all_jobs)
var/datum/job/job = new J()
if(!job)
continue
if(job.faction != faction)
continue
occupations += job
name_occupations[job.title] = job
type_occupations[J] = job
if(LAZYLEN(job.departments))
add_to_departments(job)
sortTim(occupations, /proc/cmp_job_datums)
for(var/D in department_datums)
var/datum/department/dept = department_datums[D]
sortTim(dept.jobs, /proc/cmp_job_datums, TRUE)
sortTim(dept.primary_jobs, /proc/cmp_job_datums, TRUE)
return TRUE
/datum/controller/subsystem/job/proc/add_to_departments(datum/job/J)
// Adds to the regular job lists in the departments, which allow multiple departments for a job.
for(var/D in J.departments)
var/datum/department/dept = LAZYACCESS(department_datums, D)
if(!istype(dept))
job_debug_message("Job '[J.title]' is defined as being inside department '[D]', but it does not exist.")
continue
dept.jobs[J.title] = J
// Now for the 'primary' department for a job, which is defined as being the first department in the list for a job.
// This results in no duplicates, which can be useful in some situations.
if(LAZYLEN(J.departments))
var/primary_department = J.departments[1]
var/datum/department/dept = LAZYACCESS(department_datums, primary_department)
if(!istype(dept))
job_debug_message("Job '[J.title]' has their primary department be '[primary_department]', but it does not exist.")
else
dept.primary_jobs[J.title] = J
/datum/controller/subsystem/job/proc/setup_departments()
for(var/t in subtypesof(/datum/department))
var/datum/department/D = new t()
department_datums[D.name] = D
sortTim(department_datums, /proc/cmp_department_datums, TRUE)
/datum/controller/subsystem/job/proc/get_all_department_datums()
var/list/dept_datums = list()
for(var/D in department_datums)
dept_datums += department_datums[D]
return dept_datums
/datum/controller/subsystem/job/proc/get_job(rank)
if(!occupations.len)
setup_occupations()
return name_occupations[rank]
/datum/controller/subsystem/job/proc/get_job_type(jobtype)
if(!occupations.len)
setup_occupations()
return type_occupations[jobtype]
// Determines if a job title is inside of a specific department.
// Useful to replace the old `if(job_title in command_positions)` code.
/datum/controller/subsystem/job/proc/is_job_in_department(rank, target_department_name)
var/datum/department/D = LAZYACCESS(department_datums, target_department_name)
if(istype(D))
return LAZYFIND(D.jobs, rank) ? TRUE : FALSE
return FALSE
// Returns a list of all job names in a specific department.
/datum/controller/subsystem/job/proc/get_job_titles_in_department(target_department_name)
var/datum/department/D = LAZYACCESS(department_datums, target_department_name)
if(istype(D))
var/list/job_titles = list()
for(var/J in D.jobs)
job_titles += J
return job_titles
job_debug_message("Was asked to get job titles for a non-existant department '[target_department_name]'.")
return list()
// Returns a reference to the primary department datum that a job is in.
// Can receive job datum refs, typepaths, or job title strings.
/datum/controller/subsystem/job/proc/get_primary_department_of_job(datum/job/J)
if(!istype(J, /datum/job))
if(ispath(J))
J = get_job_type(J)
else if(istext(J))
J = get_job(J)
if(!istype(J))
job_debug_message("Was asked to get department for job '[J]', but input could not be resolved into a job datum.")
return
if(!LAZYLEN(J.departments))
return
var/primary_department = J.departments[1]
var/datum/department/dept = LAZYACCESS(department_datums, primary_department)
if(!istype(dept))
job_debug_message("Job '[J.title]' has their primary department be '[primary_department]', but it does not exist.")
return
return department_datums[primary_department]
// Someday it might be good to port code/game/jobs/job_controller.dm to here and clean it up.
/datum/controller/subsystem/job/proc/job_debug_message(message)
if(debug_messages)
log_debug("JOB DEBUG: [message]")
+29 -30
View File
@@ -1,30 +1,29 @@
// Handles map-related tasks, mostly here to ensure it does so after the MC initializes.
SUBSYSTEM_DEF(mapping)
name = "Mapping"
init_order = INIT_ORDER_MAPPING
flags = SS_NO_FIRE
var/list/map_templates = list()
var/dmm_suite/maploader = null
/datum/controller/subsystem/mapping/Initialize(timeofday)
if(subsystem_initialized)
return
world.max_z_changed() // This is to set up the player z-level list, maxz hasn't actually changed (probably)
maploader = new()
load_map_templates()
if(config.generate_map)
// Map-gen is still very specific to the map, however putting it here should ensure it loads in the correct order.
if(using_map.perform_map_generation())
using_map.refresh_mining_turfs()
/datum/controller/subsystem/mapping/proc/load_map_templates()
for(var/T in subtypesof(/datum/map_template))
var/datum/map_template/template = T
if(!(initial(template.mappath))) // If it's missing the actual path its probably a base type or being used for inheritence.
continue
template = new T()
map_templates[template.name] = template
return TRUE
// Handles map-related tasks, mostly here to ensure it does so after the MC initializes.
SUBSYSTEM_DEF(mapping)
name = "Mapping"
init_order = INIT_ORDER_MAPPING
flags = SS_NO_FIRE
var/list/map_templates = list()
var/dmm_suite/maploader = null
/datum/controller/subsystem/mapping/Initialize(timeofday)
if(subsystem_initialized)
return
world.max_z_changed() // This is to set up the player z-level list, maxz hasn't actually changed (probably)
maploader = new()
load_map_templates()
if(config.generate_map)
// Map-gen is still very specific to the map, however putting it here should ensure it loads in the correct order.
using_map.perform_map_generation()
/datum/controller/subsystem/mapping/proc/load_map_templates()
for(var/T in subtypesof(/datum/map_template))
var/datum/map_template/template = T
if(!(initial(template.mappath))) // If it's missing the actual path its probably a base type or being used for inheritence.
continue
template = new T()
map_templates[template.name] = template
return TRUE
+1 -2
View File
@@ -27,8 +27,7 @@ SUBSYSTEM_DEF(mapping)
if(config.generate_map)
// Map-gen is still very specific to the map, however putting it here should ensure it loads in the correct order.
if(using_map.perform_map_generation())
using_map.refresh_mining_turfs()
using_map.perform_map_generation()
loadEngine()
preloadShelterTemplates()
+1 -32
View File
@@ -1,41 +1,17 @@
SUBSYSTEM_DEF(nanoui)
name = "NanoUI"
wait = 5
flags = SS_NO_INIT
// a list of current open /nanoui UIs, grouped by src_object and ui_key
var/list/open_uis = list()
// a list of current open /nanoui UIs, not grouped, for use in processing
var/list/processing_uis = list()
// a list of asset filenames which are to be sent to the client on user logon
var/list/asset_files = list()
/datum/controller/subsystem/nanoui/Initialize()
var/list/nano_asset_dirs = list(\
"nano/css/",\
"nano/images/",\
"nano/images/status_icons/",\
"nano/images/modular_computers/",\
"nano/js/",\
"nano/templates/"\
)
var/list/filenames = null
for (var/path in nano_asset_dirs)
filenames = flist(path)
for(var/filename in filenames)
if(copytext(filename, length(filename)) != "/") // filenames which end in "/" are actually directories, which we want to ignore
if(fexists(path + filename))
asset_files.Add(fcopy_rsc(path + filename)) // add this file to asset_files for sending to clients when they connect
.=..()
for(var/i in GLOB.clients)
send_resources(i)
/datum/controller/subsystem/nanoui/Recover()
if(SSnanoui.open_uis)
open_uis |= SSnanoui.open_uis
if(SSnanoui.processing_uis)
processing_uis |= SSnanoui.processing_uis
if(SSnanoui.asset_files)
asset_files |= SSnanoui.asset_files
/datum/controller/subsystem/nanoui/stat_entry()
return ..("[processing_uis.len] UIs")
@@ -44,10 +20,3 @@ SUBSYSTEM_DEF(nanoui)
for(var/thing in processing_uis)
var/datum/nanoui/UI = thing
UI.process()
//Sends asset files to a client, called on client/New()
/datum/controller/subsystem/nanoui/proc/send_resources(client)
if(!subsystem_initialized)
return
for(var/file in asset_files)
client << browse_rsc(file) // send the file to the client
+5 -7
View File
@@ -36,15 +36,13 @@ SUBSYSTEM_DEF(persist)
// Try and detect job and department of mob
var/datum/job/J = detect_job(M)
if(!istype(J) || !J.department || !J.timeoff_factor)
if(!istype(J) || !J.pto_type || !J.timeoff_factor)
if (MC_TICK_CHECK)
return
continue
// Do not collect useless PTO
var/department_earning = J.department
if(J.department == "Command")
department_earning = "Civilian"
var/department_earning = J.pto_type
clear_unused_pto(M)
// Update client whatever
@@ -90,6 +88,6 @@ SUBSYSTEM_DEF(persist)
/datum/controller/subsystem/persist/proc/clear_unused_pto(var/mob/M)
var/client/C = M.client
LAZYINITLIST(C.department_hours)
if(C.department_hours["Command"])
C.department_hours["Command"] = null
C.department_hours.Remove("Command")
if(C.department_hours[DEPARTMENT_COMMAND])
C.department_hours[DEPARTMENT_COMMAND] = null
C.department_hours.Remove(DEPARTMENT_COMMAND)
+1 -1
View File
@@ -24,7 +24,7 @@ SUBSYSTEM_DEF(planets)
..()
/datum/controller/subsystem/planets/proc/createPlanets()
var/list/planet_datums = subtypesof(/datum/planet)
var/list/planet_datums = using_map.planet_datums_to_make
for(var/P in planet_datums)
var/datum/planet/NP = new P()
planets.Add(NP)
@@ -0,0 +1,20 @@
//
// Bellies subsystem - Process vore bellies
//
PROCESSING_SUBSYSTEM_DEF(bellies)
name = "Bellies"
wait = 6 SECONDS
flags = SS_KEEP_TIMING|SS_NO_INIT
runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME
/datum/controller/subsystem/processing/bellies/Recover()
log_debug("[name] subsystem Recover().")
if(SSbellies.current_thing)
log_debug("current_thing was: (\ref[SSbellies.current_thing])[SSbellies.current_thing]([SSbellies.current_thing.type]) - currentrun: [SSbellies.currentrun.len] vs total: [SSbellies.processing.len]")
var/list/old_processing = SSbellies.processing.Copy()
for(var/datum/D in old_processing)
if(!isbelly(D))
log_debug("[name] subsystem Recover() found inappropriate item in list: [D.type]")
if(CHECK_BITFIELD(D.datum_flags, DF_ISPROCESSING))
processing |= D
@@ -8,6 +8,14 @@ PROCESSING_SUBSYSTEM_DEF(chemistry)
var/list/chemical_reagents = list()
/datum/controller/subsystem/processing/chemistry/Recover()
log_debug("[name] subsystem Recover().")
if(SSchemistry.current_thing)
log_debug("current_thing was: (\ref[SSchemistry.current_thing])[SSchemistry.current_thing]([SSchemistry.current_thing.type]) - currentrun: [SSchemistry.currentrun.len] vs total: [SSchemistry.processing.len]")
var/list/old_processing = SSchemistry.processing.Copy()
for(var/datum/D in old_processing)
if(CHECK_BITFIELD(D.datum_flags, DF_ISPROCESSING))
processing |= D
chemical_reactions = SSchemistry.chemical_reactions
chemical_reagents = SSchemistry.chemical_reagents
@@ -4,3 +4,12 @@ PROCESSING_SUBSYSTEM_DEF(fastprocess)
name = "Fast Processing"
wait = 2
stat_tag = "FP"
/datum/controller/subsystem/processing/fastprocess/Recover()
log_debug("[name] subsystem Recover().")
if(SSfastprocess.current_thing)
log_debug("current_thing was: (\ref[SSfastprocess.current_thing])[SSfastprocess.current_thing]([SSfastprocess.current_thing.type]) - currentrun: [SSfastprocess.currentrun.len] vs total: [SSfastprocess.processing.len]")
var/list/old_processing = SSfastprocess.processing.Copy()
for(var/datum/D in old_processing)
if(CHECK_BITFIELD(D.datum_flags, DF_ISPROCESSING))
processing |= D
@@ -3,3 +3,14 @@ PROCESSING_SUBSYSTEM_DEF(obj)
priority = FIRE_PRIORITY_OBJ
flags = SS_NO_INIT
wait = 20
/datum/controller/subsystem/processing/obj/Recover()
log_debug("[name] subsystem Recover().")
if(SSobj.current_thing)
log_debug("current_thing was: (\ref[SSobj.current_thing])[SSobj.current_thing]([SSobj.current_thing.type]) - currentrun: [SSobj.currentrun.len] vs total: [SSobj.processing.len]")
var/list/old_processing = SSobj.processing.Copy()
for(var/datum/D in old_processing)
if(!isobj(D))
log_debug("[name] subsystem Recover() found inappropriate item in list: [D.type]")
if(CHECK_BITFIELD(D.datum_flags, DF_ISPROCESSING))
processing |= D
@@ -13,6 +13,16 @@ SUBSYSTEM_DEF(processing)
var/debug_last_thing
var/debug_original_process_proc // initial() does not work with procs
var/datum/current_thing
/datum/controller/subsystem/processing/Recover()
log_debug("[name] subsystem Recover().")
if(SSprocessing.current_thing)
log_debug("current_thing was: (\ref[SSprocessing.current_thing])[SSprocessing.current_thing]([SSprocessing.current_thing.type]) - currentrun: [SSprocessing.currentrun.len] vs total: [SSprocessing.processing.len]")
var/list/old_processing = SSprocessing.processing.Copy()
for(var/datum/D in old_processing)
if(CHECK_BITFIELD(D.datum_flags, DF_ISPROCESSING))
processing |= D
/datum/controller/subsystem/processing/stat_entry()
..("[stat_tag]:[processing.len]")
@@ -24,16 +34,19 @@ SUBSYSTEM_DEF(processing)
var/list/current_run = currentrun
while(current_run.len)
var/datum/thing = current_run[current_run.len]
current_thing = current_run[current_run.len]
current_run.len--
if(QDELETED(thing))
processing -= thing
else if(thing.process(wait) == PROCESS_KILL)
if(QDELETED(current_thing))
processing -= current_thing
else if(current_thing.process(wait) == PROCESS_KILL)
// fully stop so that a future START_PROCESSING will work
STOP_PROCESSING(src, thing)
STOP_PROCESSING(src, current_thing)
if (MC_TICK_CHECK)
current_thing = null
return
current_thing = null
/datum/controller/subsystem/processing/proc/toggle_debug()
if(!check_rights(R_DEBUG))
return
@@ -8,6 +8,15 @@ PROCESSING_SUBSYSTEM_DEF(projectiles)
var/global_pixel_speed = 2
var/global_iterations_per_move = 16
/datum/controller/subsystem/processing/projectiles/Recover()
log_debug("[name] subsystem Recover().")
if(SSprojectiles.current_thing)
log_debug("current_thing was: (\ref[SSprojectiles.current_thing])[SSprojectiles.current_thing]([SSprojectiles.current_thing.type]) - currentrun: [SSprojectiles.currentrun.len] vs total: [SSprojectiles.processing.len]")
var/list/old_processing = SSprojectiles.processing.Copy()
for(var/datum/D in old_processing)
if(CHECK_BITFIELD(D.datum_flags, DF_ISPROCESSING))
processing |= D
/datum/controller/subsystem/processing/projectiles/proc/set_pixel_speed(new_speed)
global_pixel_speed = new_speed
for(var/i in processing)
@@ -1,3 +1,14 @@
PROCESSING_SUBSYSTEM_DEF(turfs)
name = "Turf Processing"
wait = 20
/datum/controller/subsystem/processing/turfs/Recover()
log_debug("[name] subsystem Recover().")
if(SSturfs.current_thing)
log_debug("current_thing was: (\ref[SSturfs.current_thing])[SSturfs.current_thing]([SSturfs.current_thing.type]) - currentrun: [SSturfs.currentrun.len] vs total: [SSturfs.processing.len]")
var/list/old_processing = SSturfs.processing.Copy()
for(var/datum/D in old_processing)
if(!isturf(D))
log_debug("[name] subsystem Recover() found inappropriate item in list: [D.type]")
if(CHECK_BITFIELD(D.datum_flags, DF_ISPROCESSING))
processing |= D
+148 -53
View File
@@ -1,6 +1,8 @@
//
// SSshuttles subsystem - Handles initialization and processing of shuttles.
//
// Also handles initialization and processing of overmap sectors.
//
// This global variable exists for legacy support so we don't have to rename every shuttle_controller to SSshuttles yet.
var/global/datum/controller/subsystem/shuttles/shuttle_controller
@@ -13,71 +15,164 @@ SUBSYSTEM_DEF(shuttles)
flags = SS_KEEP_TIMING|SS_NO_TICK_CHECK
runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME
var/list/shuttles = list() // Maps shuttle tags to shuttle datums, so that they can be looked up.
var/list/process_shuttles = list() // Simple list of shuttles, for processing
var/list/current_run = list() // Shuttles remaining to process this fire() tick
var/list/docks_init_callbacks // List of callbacks to run when we finish setting up shuttle docks.
var/docks_initialized = FALSE
var/overmap_halted = FALSE // Whether ships can move on the overmap; used for adminbus.
var/list/ships = list() // List of all ships.
var/list/shuttles = list() // Maps shuttle tags to shuttle datums, so that they can be looked up.
var/list/process_shuttles = list() // Simple list of shuttles, for processing
var/list/registered_shuttle_landmarks = list() // Maps shuttle landmark tags to instances
var/last_landmark_registration_time // world.time of most recent addition to registered_shuttle_landmarks
var/list/shuttle_logs = list() // (Not Implemented) Keeps records of shuttle movement, format is list(datum/shuttle = datum/shuttle_log)
var/list/shuttle_areas = list() // All the areas of all shuttles.
var/list/docking_registry = list() // Docking controller tag -> docking controller program, mostly for init purposes.
var/list/landmarks_awaiting_sector = list() // Stores automatic landmarks that are waiting for a sector to finish loading.
var/list/landmarks_still_needed = list() // Stores landmark_tags that need to be assigned to the sector (landmark_tag = sector) when registered.
var/list/shuttles_to_initialize // A queue for shuttles to initialize at the appropriate time.
var/list/sectors_to_initialize // Used to find all sector objects at the appropriate time.
var/block_init_queue = TRUE // Block initialization of new shuttles/sectors
var/tmp/list/current_run // Shuttles remaining to process this fire() tick
/datum/controller/subsystem/shuttles/PreInit()
global.shuttle_controller = src // TODO - Remove this! Change everything to point at SSshuttles intead
/datum/controller/subsystem/shuttles/Initialize(timeofday)
global.shuttle_controller = src
setup_shuttle_docks()
for(var/I in docks_init_callbacks)
var/datum/callback/cb = I
cb.InvokeAsync()
LAZYCLEARLIST(docks_init_callbacks)
docks_init_callbacks = null
last_landmark_registration_time = world.time
// Find all declared shuttle datums and initailize them. (Okay, queue them for initialization a few lines further down)
for(var/shuttle_type in subtypesof(/datum/shuttle)) // This accounts for most shuttles, though away maps can queue up more.
var/datum/shuttle/shuttle = shuttle_type
if(initial(shuttle.category) == shuttle_type)
continue // Its an "abstract class" datum, not for a real shuttle.
if(!initial(shuttle.defer_initialisation)) // Skip if it asks not to be initialized at startup.
LAZYDISTINCTADD(shuttles_to_initialize, shuttle_type)
block_init_queue = FALSE
process_init_queues()
return ..()
/datum/controller/subsystem/shuttles/fire(resumed = 0)
do_process_shuttles(resumed)
/datum/controller/subsystem/shuttles/stat_entry()
var/msg = list()
msg += "AS:[shuttles.len]|"
msg += "PS:[process_shuttles.len]|"
..(jointext(msg, null))
/datum/controller/subsystem/shuttles/proc/do_process_shuttles(resumed = 0)
if (!resumed)
src.current_run = process_shuttles.Copy()
var/list/current_run = src.current_run // Cache for sanic speed
while(current_run.len)
var/datum/shuttle/S = current_run[current_run.len]
current_run.len--
if(istype(S) && !QDELETED(S))
if(istype(S, /datum/shuttle/ferry)) // Ferry shuttles get special treatment
var/datum/shuttle/ferry/F = S
if(F.process_state || F.always_process)
F.process()
else
S.process()
else
var/list/working_shuttles = src.current_run // Cache for sanic speed
while(working_shuttles.len)
var/datum/shuttle/S = working_shuttles[working_shuttles.len]
working_shuttles.len--
if(!istype(S) || QDELETED(S))
error("Bad entry in SSshuttles.process_shuttles - [log_info_line(S)] ")
process_shuttles -= S
continue
// NOTE - In old system, /datum/shuttle/ferry was processed only if (F.process_state || F.always_process)
if(S.process_state && (S.process(wait, times_fired, src) == PROCESS_KILL))
process_shuttles -= S
if(MC_TICK_CHECK)
return
// This should be called after all the machines and radio frequencies have been properly initialized
/datum/controller/subsystem/shuttles/proc/setup_shuttle_docks()
// Find all declared shuttle datums and initailize them.
for(var/shuttle_type in subtypesof(/datum/shuttle))
var/datum/shuttle/shuttle = shuttle_type
if(initial(shuttle.category) == shuttle_type)
continue
/datum/controller/subsystem/shuttles/proc/process_init_queues()
if(block_init_queue)
return
initialize_shuttles()
initialize_sectors()
// Initializes all shuttles in shuttles_to_initialize
/datum/controller/subsystem/shuttles/proc/initialize_shuttles()
var/list/shuttles_made = list()
for(var/shuttle_type in shuttles_to_initialize)
var/shuttle = initialize_shuttle(shuttle_type)
if(shuttle)
shuttles_made += shuttle
hook_up_motherships(shuttles_made)
shuttles_to_initialize = null
/datum/controller/subsystem/shuttles/proc/initialize_sectors()
for(var/sector in sectors_to_initialize)
initialize_sector(sector)
sectors_to_initialize = null
/datum/controller/subsystem/shuttles/proc/register_landmark(shuttle_landmark_tag, obj/effect/shuttle_landmark/shuttle_landmark)
if (registered_shuttle_landmarks[shuttle_landmark_tag])
CRASH("Attempted to register shuttle landmark with tag [shuttle_landmark_tag], but it is already registered!")
if (istype(shuttle_landmark))
registered_shuttle_landmarks[shuttle_landmark_tag] = shuttle_landmark
last_landmark_registration_time = world.time
var/obj/effect/overmap/visitable/O = landmarks_still_needed[shuttle_landmark_tag]
if(O) //These need to be added to sectors, which we handle.
try_add_landmark_tag(shuttle_landmark_tag, O)
landmarks_still_needed -= shuttle_landmark_tag
else if(istype(shuttle_landmark, /obj/effect/shuttle_landmark/automatic)) //These find their sector automatically
O = map_sectors["[shuttle_landmark.z]"]
O ? O.add_landmark(shuttle_landmark, shuttle_landmark.shuttle_restricted) : (landmarks_awaiting_sector += shuttle_landmark)
/datum/controller/subsystem/shuttles/proc/get_landmark(var/shuttle_landmark_tag)
return registered_shuttle_landmarks[shuttle_landmark_tag]
//Checks if the given sector's landmarks have initialized; if so, registers them with the sector, if not, marks them for assignment after they come in.
//Also adds automatic landmarks that were waiting on their sector to spawn.
/datum/controller/subsystem/shuttles/proc/initialize_sector(obj/effect/overmap/visitable/given_sector)
given_sector.populate_sector_objects() // This is a late init operation that sets up the sector's map_z and does non-overmap-related init tasks.
for(var/landmark_tag in given_sector.initial_generic_waypoints)
if(!try_add_landmark_tag(landmark_tag, given_sector))
landmarks_still_needed[landmark_tag] = given_sector // Landmark isn't registered yet, queue it to be added once it is.
for(var/shuttle_name in given_sector.initial_restricted_waypoints)
for(var/landmark_tag in given_sector.initial_restricted_waypoints[shuttle_name])
if(!try_add_landmark_tag(landmark_tag, given_sector))
landmarks_still_needed[landmark_tag] = given_sector // Landmark isn't registered yet, queue it to be added once it is.
var/landmarks_to_check = landmarks_awaiting_sector.Copy()
for(var/thing in landmarks_to_check)
var/obj/effect/shuttle_landmark/automatic/landmark = thing
if(landmark.z in given_sector.map_z)
given_sector.add_landmark(landmark, landmark.shuttle_restricted)
landmarks_awaiting_sector -= landmark
// Attempts to add a landmark instance with a sector (returns false if landmark isn't registered yet)
/datum/controller/subsystem/shuttles/proc/try_add_landmark_tag(landmark_tag, obj/effect/overmap/visitable/given_sector)
var/obj/effect/shuttle_landmark/landmark = get_landmark(landmark_tag)
if(!landmark)
return
if(landmark.landmark_tag in given_sector.initial_generic_waypoints)
given_sector.add_landmark(landmark)
. = 1
for(var/shuttle_name in given_sector.initial_restricted_waypoints)
if(landmark.landmark_tag in given_sector.initial_restricted_waypoints[shuttle_name])
given_sector.add_landmark(landmark, shuttle_name)
. = 1
/datum/controller/subsystem/shuttles/proc/initialize_shuttle(var/shuttle_type)
var/datum/shuttle/shuttle = shuttle_type
if(initial(shuttle.category) != shuttle_type) // Skip if its an "abstract class" datum
shuttle = new shuttle()
shuttle.init_docking_controllers()
shuttle.dock() //makes all shuttles docked to something at round start go into the docked state
CHECK_TICK
shuttle_areas |= shuttle.shuttle_area
log_debug("Initialized shuttle [shuttle] ([shuttle.type])")
return shuttle
// Historical note: No need to call shuttle.init_docking_controllers(), controllers register themselves
// and shuttles fetch refs in New(). Shuttles also dock() themselves in new if they want.
for(var/obj/machinery/embedded_controller/C in machines)
if(istype(C.program, /datum/computer/file/embedded_program/docking))
C.program.tag = null //clear the tags, 'cause we don't need 'em anymore
docks_initialized = TRUE
// TODO - Leshana to hook up more of this when overmap is ported.
/datum/controller/subsystem/shuttles/proc/hook_up_motherships(shuttles_list)
for(var/datum/shuttle/S in shuttles_list)
if(S.mothershuttle && !S.motherdock)
var/datum/shuttle/mothership = shuttles[S.mothershuttle]
if(mothership)
S.motherdock = S.current_location.landmark_tag
mothership.shuttle_area |= S.shuttle_area
else
error("Shuttle [S] was unable to find mothership [mothership]!")
// Register a callback that will be invoked once the shuttles have been initialized
/datum/controller/subsystem/shuttles/proc/OnDocksInitialized(datum/callback/cb)
if(!docks_initialized)
LAZYADD(docks_init_callbacks, cb)
else
cb.InvokeAsync()
// Admin command to halt/resume overmap
/datum/controller/subsystem/shuttles/proc/toggle_overmap(new_setting)
if(overmap_halted == new_setting)
return
overmap_halted = !overmap_halted
for(var/ship in ships)
var/obj/effect/overmap/visitable/ship/ship_effect = ship
overmap_halted ? ship_effect.halt() : ship_effect.unhalt()
/datum/controller/subsystem/shuttles/stat_entry()
..("Shuttles:[process_shuttles.len]/[shuttles.len], Ships:[ships.len], L:[registered_shuttle_landmarks.len][overmap_halted ? ", HALT" : ""]")
+135
View File
@@ -0,0 +1,135 @@
//Exists to handle a few global variables that change enough to justify this. Technically a parallax, but it exhibits a skybox effect.
SUBSYSTEM_DEF(skybox)
name = "Space skybox"
init_order = INIT_ORDER_SKYBOX
flags = SS_NO_FIRE
var/static/list/skybox_cache = list()
var/static/list/dust_cache = list()
var/static/list/speedspace_cache = list()
var/static/list/mapedge_cache = list()
var/static/list/phase_shift_by_x = list()
var/static/list/phase_shift_by_y = list()
/datum/controller/subsystem/skybox/PreInit()
//Static
for (var/i in 0 to 25)
var/image/im = image('icons/turf/space_dust.dmi', "[i]")
im.plane = DUST_PLANE
im.alpha = 128 //80
im.blend_mode = BLEND_ADD
dust_cache["[i]"] = im
//Moving
for (var/i in 0 to 14)
// NORTH/SOUTH
var/image/im = image('icons/turf/space_dust_transit.dmi', "speedspace_ns_[i]")
im.plane = DUST_PLANE
im.blend_mode = BLEND_ADD
speedspace_cache["NS_[i]"] = im
// EAST/WEST
im = image('icons/turf/space_dust_transit.dmi', "speedspace_ew_[i]")
im.plane = DUST_PLANE
im.blend_mode = BLEND_ADD
speedspace_cache["EW_[i]"] = im
//Over-the-edge images
for (var/dir in alldirs)
var/image/I = image('icons/turf/space.dmi', "white")
var/matrix/M = matrix()
var/horizontal = (dir & (WEST|EAST))
var/vertical = (dir & (NORTH|SOUTH))
M.Scale(horizontal ? 8 : 1, vertical ? 8 : 1)
I.transform = M
I.appearance_flags = KEEP_APART | TILE_BOUND
I.plane = SPACE_PLANE
I.layer = 0
if(dir & NORTH)
I.pixel_y = 112
else if(dir & SOUTH)
I.pixel_y = -112
if(dir & EAST)
I.pixel_x = 112
else if(dir & WEST)
I.pixel_x = -112
mapedge_cache["[dir]"] = I
//Shuffle some lists
phase_shift_by_x = get_cross_shift_list(15)
phase_shift_by_y = get_cross_shift_list(15)
. = ..()
/datum/controller/subsystem/skybox/Initialize()
. = ..()
/datum/controller/subsystem/skybox/proc/get_skybox(z)
if(!skybox_cache["[z]"])
skybox_cache["[z]"] = generate_skybox(z)
if(global.using_map.use_overmap)
var/obj/effect/overmap/visitable/O = map_sectors["[z]"]
if(istype(O))
for(var/zlevel in O.map_z)
skybox_cache["[zlevel]"] = skybox_cache["[z]"]
return skybox_cache["[z]"]
/datum/controller/subsystem/skybox/proc/generate_skybox(z)
var/datum/skybox_settings/settings = global.using_map.get_skybox_datum(z)
var/image/res = image(settings.icon)
res.appearance_flags = KEEP_TOGETHER
var/image/base = image(settings.icon, settings.icon_state)
base.color = settings.color
if(settings.use_stars)
var/image/stars = image(settings.icon, settings.star_state)
stars.appearance_flags = RESET_COLOR
base.overlays += stars
res.overlays += base
if(global.using_map.use_overmap && settings.use_overmap_details)
var/obj/effect/overmap/visitable/O = map_sectors["[z]"]
if(istype(O))
var/image/overmap = image(settings.icon)
overmap.overlays += O.generate_skybox()
for(var/obj/effect/overmap/visitable/other in O.loc)
if(other != O)
overmap.overlays += other.get_skybox_representation()
overmap.appearance_flags = RESET_COLOR
res.overlays += overmap
// Allow events to apply custom overlays to skybox! (Awesome!)
for(var/datum/event/E in SSevents.active_events)
if(E.has_skybox_image && E.isRunning && (z in E.affecting_z))
res.overlays += E.get_skybox_image()
return res
/datum/controller/subsystem/skybox/proc/rebuild_skyboxes(var/list/zlevels)
for(var/z in zlevels)
skybox_cache["[z]"] = generate_skybox(z)
for(var/client/C)
C.update_skybox(1)
// Settings datum that maps can override to play with their skyboxes
/datum/skybox_settings
var/icon = 'icons/skybox/skybox.dmi' //Path to our background. Lets us use anything we damn well please. Skyboxes need to be 736x736
var/icon_state = "dyable"
var/color
var/random_color = FALSE
var/use_stars = TRUE
var/star_icon = 'icons/skybox/skybox.dmi'
var/star_state = "stars"
var/use_overmap_details = TRUE //Do we try to draw overmap visitables in our sector on the map?
/datum/skybox_settings/New()
..()
if(random_color)
color = rgb(rand(0,255), rand(0,255), rand(0,255))
+187
View File
@@ -0,0 +1,187 @@
// This holds all the code needed to manage and use a SQLite database.
// It is merely a file sitting inside the data directory, as opposed to a full fledged DB service,
// however this makes it a lot easier to test, and it is natively supported by BYOND.
SUBSYSTEM_DEF(sqlite)
name = "SQLite"
init_order = INIT_ORDER_SQLITE
flags = SS_NO_FIRE
var/database/sqlite_db = null
/datum/controller/subsystem/sqlite/Initialize(timeofday)
connect()
if(sqlite_db)
init_schema(sqlite_db)
return ..()
/datum/controller/subsystem/sqlite/proc/connect()
if(!config.sqlite_enabled)
return
if(!sqlite_db)
sqlite_db = new("data/sqlite/sqlite.db") // The path has to be hardcoded or BYOND silently fails.
if(!sqlite_db)
to_world_log("Failed to load or create a SQLite database.")
log_debug("ERROR: SQLite database is active in config but failed to load.")
else
to_world_log("Sqlite database connected.")
// Makes the tables, if they do not already exist in the sqlite file.
/datum/controller/subsystem/sqlite/proc/init_schema(database/sqlite_object)
// Feedback table.
// Note that this is for direct feedback from players using the in-game feedback system and NOT for stat tracking.
// Player ckeys are not stored in this table as a unique key due to a config option to hash the keys to encourage more honest feedback.
/*
* id - Primary unique key to ID a specific piece of feedback.
NOT used to id people submitting feedback.
* author - The person who submitted it. Will be the ckey, or a hash of the ckey,
if both the config supports it, and the user wants it.
* topic - A specific category to organize feedback under. Options are defined in the config file.
* content - What the author decided to write to the staff. Limited to MAX_FEEDBACK_LENGTH.
* datetime - When the author submitted their feedback, acts as a timestamp.
*/
var/database/query/init_schema = new(
{"
CREATE TABLE IF NOT EXISTS [SQLITE_TABLE_FEEDBACK]
(
`[SQLITE_FEEDBACK_COLUMN_ID]` INTEGER NOT NULL UNIQUE,
`[SQLITE_FEEDBACK_COLUMN_AUTHOR]` TEXT NOT NULL,
`[SQLITE_FEEDBACK_COLUMN_TOPIC]` TEXT NOT NULL,
`[SQLITE_FEEDBACK_COLUMN_CONTENT]` TEXT NOT NULL,
`[SQLITE_FEEDBACK_COLUMN_DATETIME]` TEXT NOT NULL,
PRIMARY KEY(`[SQLITE_FEEDBACK_COLUMN_ID]`)
);
"}
)
init_schema.Execute(sqlite_object)
sqlite_check_for_errors(init_schema, "Feedback table creation")
// Add more schemas below this if the SQLite DB gets expanded for things like persistant news, polls, bans, deaths, etc.
// General error checking for SQLite.
// Returns true if something went wrong. Also writes a log.
// The desc parameter should be unique for each call, to make it easier to track down where the error occured.
/datum/controller/subsystem/sqlite/proc/sqlite_check_for_errors(var/database/query/query_used, var/desc)
if(query_used && query_used.ErrorMsg())
log_debug("SQLite Error: [desc] : [query_used.ErrorMsg()]")
return TRUE
return FALSE
/************
* Feedback *
************/
// Inserts data into the feedback table in a painless manner.
// Returns TRUE if no issues happened, FALSE otherwise.
/datum/controller/subsystem/sqlite/proc/insert_feedback(author, topic, content, database/sqlite_object)
if(!author || !topic || !content)
CRASH("One or more parameters was invalid.")
// Sanitize everything to avoid sneaky stuff.
var/sqlite_author = sql_sanitize_text(ckey(lowertext(author)))
var/sqlite_content = sql_sanitize_text(content)
var/sqlite_topic = sql_sanitize_text(topic)
var/database/query/query = new(
"INSERT INTO [SQLITE_TABLE_FEEDBACK] (\
[SQLITE_FEEDBACK_COLUMN_AUTHOR], \
[SQLITE_FEEDBACK_COLUMN_TOPIC], \
[SQLITE_FEEDBACK_COLUMN_CONTENT], \
[SQLITE_FEEDBACK_COLUMN_DATETIME]) \
\
VALUES (\
?,\
?,\
?,\
datetime('now'))",
sqlite_author,
sqlite_topic,
sqlite_content
)
query.Execute(sqlite_object)
return !sqlite_check_for_errors(query, "Insert Feedback")
/datum/controller/subsystem/sqlite/proc/can_submit_feedback(client/C)
if(!config.sqlite_enabled)
return FALSE
if(config.sqlite_feedback_min_age && !is_old_enough(C))
return FALSE
if(config.sqlite_feedback_cooldown > 0 && get_feedback_cooldown(C.key, config.sqlite_feedback_cooldown, sqlite_db) > 0)
return FALSE
return TRUE
// Returns TRUE if the player is 'old' enough, according to the config.
/datum/controller/subsystem/sqlite/proc/is_old_enough(client/C)
if(get_player_age(C.key) < config.sqlite_feedback_min_age)
return FALSE
return TRUE
// Returns how many days someone has to wait, to submit more feedback, or 0 if they can do so right now.
/datum/controller/subsystem/sqlite/proc/get_feedback_cooldown(player_ckey, cooldown, database/sqlite_object)
player_ckey = sql_sanitize_text(ckey(lowertext(player_ckey)))
var/potential_hashed_ckey = sql_sanitize_text(md5(player_ckey + SSsqlite.get_feedback_pepper()))
// First query is to get the most recent time the player has submitted feedback.
var/database/query/query = new({"
SELECT [SQLITE_FEEDBACK_COLUMN_DATETIME]
FROM [SQLITE_TABLE_FEEDBACK]
WHERE [SQLITE_FEEDBACK_COLUMN_AUTHOR] == ? OR [SQLITE_FEEDBACK_COLUMN_AUTHOR] == ?
ORDER BY [SQLITE_FEEDBACK_COLUMN_DATETIME]
DESC LIMIT 1;
"},
player_ckey,
potential_hashed_ckey
)
query.Execute(sqlite_object)
sqlite_check_for_errors(query, "Rate Limited Check 1")
// It is possible this is their first time, so there won't be a next row.
if(query.NextRow()) // If this is true, the user has submitted feedback at least once.
var/list/row_data = query.GetRowData()
var/last_submission_datetime = row_data[SQLITE_FEEDBACK_COLUMN_DATETIME]
// Now we have the datetime, we need to do something to compare it.
// Second query is to calculate the difference between two datetimes.
// This is done on the SQLite side because parsing datetimes with BYOND is probably a bad idea.
query = new(
"SELECT julianday('now') - julianday(?) \
AS 'datediff';",
last_submission_datetime
)
query.Execute(sqlite_object)
sqlite_check_for_errors(query, "Rate Limited Check 2")
query.NextRow()
row_data = query.GetRowData()
var/date_diff = row_data["datediff"]
// Now check if it's too soon to give more feedback.
if(text2num(date_diff) < cooldown) // Too soon.
return round(cooldown - date_diff, 0.1)
return 0.0
// A Pepper is like a Salt but only one exists and is supposed to be outside of a database.
// If the file is properly protected, it can only be viewed/copied by sys-admins generating a log, which is much more conspicious than accessing/copying a DB.
// This stops mods/admins/etc from guessing the author by shoving names in an MD5 hasher until they pick the right one.
// Don't use this for things needing actual security.
/datum/controller/subsystem/sqlite/proc/get_feedback_pepper()
var/pepper_file = file2list("config/sqlite_feedback_pepper.txt")
var/pepper = null
for(var/line in pepper_file)
if(!line)
continue
if(length(line) == 0)
continue
else if(copytext(line, 1, 2) == "#")
continue
else
pepper = line
break
return pepper
/datum/controller/subsystem/sqlite/CanProcCall(procname)
return procname != "get_feedback_pepper"
+397
View File
@@ -0,0 +1,397 @@
//Supply packs are in /code/datums/supplypacks
//Computers are in /code/game/machinery/computer/supply.dm
SUBSYSTEM_DEF(supply)
name = "Supply"
wait = 20 SECONDS
priority = FIRE_PRIORITY_SUPPLY
//Initializes at default time
flags = SS_NO_TICK_CHECK
//supply points
var/points = 50
var/points_per_process = 1.0 // Processes every 20 seconds, so this is 3 per minute
var/points_per_slip = 2
var/points_per_money = 0.02 // 1 point for $50
//control
var/ordernum
var/list/shoppinglist = list() // Approved orders
var/list/supply_pack = list() // All supply packs
var/list/exported_crates = list() // Crates sent from the station
var/list/order_history = list() // History of orders, showing edits made by users
var/list/adm_order_history = list() // Complete history of all orders, for admin use
var/list/adm_export_history = list() // Complete history of all crates sent back on the shuttle, for admin use
//shuttle movement
var/movetime = 1200
var/datum/shuttle/autodock/ferry/supply/shuttle
var/list/material_points_conversion = list( // Any materials not named in this list are worth 0 points
"phoron" = 5,
"platinum" = 5
)
/datum/controller/subsystem/supply/Initialize()
ordernum = rand(1,9000)
// build master supply list
for(var/typepath in subtypesof(/datum/supply_pack))
var/datum/supply_pack/P = new typepath()
if(P.name)
supply_pack[P.name] = P
else
qdel(P)
// TODO - Auto-build material_points_conversion from material datums
. = ..()
// Supply shuttle ticker - handles supply point regeneration. Just add points over time.
/datum/controller/subsystem/supply/fire()
points += points_per_process
/datum/controller/subsystem/supply/stat_entry()
..("Points: [points]")
//To stop things being sent to CentCom which should not be sent to centcomm. Recursively checks for these types.
/datum/controller/subsystem/supply/proc/forbidden_atoms_check(atom/A)
if(isliving(A))
return 1
if(istype(A,/obj/item/weapon/disk/nuclear))
return 1
if(istype(A,/obj/machinery/nuclearbomb))
return 1
if(istype(A,/obj/item/device/radio/beacon))
return 1
if(istype(A,/obj/item/device/perfect_tele_beacon)) //VOREStation Addition: Translocator beacons
return 1 //VOREStation Addition: Translocator beacons
for(var/atom/B in A.contents)
if(.(B))
return 1
//Selling
/datum/controller/subsystem/supply/proc/sell()
// Loop over each area in the supply shuttle
for(var/area/subarea in shuttle.shuttle_area)
callHook("sell_shuttle", list(subarea));
for(var/atom/movable/MA in subarea)
if(MA.anchored)
continue
var/datum/exported_crate/EC = new /datum/exported_crate()
EC.name = "\proper[MA.name]"
EC.value = 0
EC.contents = list()
var/base_value = 0
// Must be in a crate!
if(istype(MA,/obj/structure/closet/crate))
var/obj/structure/closet/crate/CR = MA
callHook("sell_crate", list(CR, subarea))
points += CR.points_per_crate
if(CR.points_per_crate)
base_value = CR.points_per_crate
var/find_slip = 1
for(var/atom/A in CR)
EC.contents[++EC.contents.len] = list(
"object" = "\proper[A.name]",
"value" = 0,
"quantity" = 1
)
// Sell manifests
if(find_slip && istype(A,/obj/item/weapon/paper/manifest))
var/obj/item/weapon/paper/manifest/slip = A
if(!slip.is_copy && slip.stamped && slip.stamped.len) //yes, the clown stamp will work. clown is the highest authority on the station, it makes sense
points += points_per_slip
EC.contents[EC.contents.len]["value"] = points_per_slip
find_slip = 0
continue
// Sell phoron and platinum
if(istype(A, /obj/item/stack))
var/obj/item/stack/P = A
if(material_points_conversion[P.get_material_name()])
EC.contents[EC.contents.len]["value"] = P.get_amount() * material_points_conversion[P.get_material_name()]
EC.contents[EC.contents.len]["quantity"] = P.get_amount()
EC.value += EC.contents[EC.contents.len]["value"]
//Sell spacebucks
if(istype(A, /obj/item/weapon/spacecash))
var/obj/item/weapon/spacecash/cashmoney = A
EC.contents[EC.contents.len]["value"] = cashmoney.worth * points_per_money
EC.contents[EC.contents.len]["quantity"] = cashmoney.worth
EC.value += EC.contents[EC.contents.len]["value"]
// Make a log of it, but it wasn't shipped properly, and so isn't worth anything
else
EC.contents = list(
"error" = "Error: Product was improperly packaged. Payment rendered null under terms of agreement."
)
exported_crates += EC
points += EC.value
EC.value += base_value
// Duplicate the receipt for the admin-side log
var/datum/exported_crate/adm = new()
adm.name = EC.name
adm.value = EC.value
adm.contents = deepCopyList(EC.contents)
adm_export_history += adm
qdel(MA)
/datum/controller/subsystem/supply/proc/get_clear_turfs()
var/list/clear_turfs = list()
for(var/area/subarea in shuttle.shuttle_area)
for(var/turf/T in subarea)
if(T.density)
continue
var/occupied = 0
for(var/atom/A in T.contents)
if(!A.simulated)
continue
occupied = 1
break
if(!occupied)
clear_turfs += T
return clear_turfs
//Buying
/datum/controller/subsystem/supply/proc/buy()
var/list/shoppinglist = list()
for(var/datum/supply_order/SO in order_history)
if(SO.status == SUP_ORDER_APPROVED)
shoppinglist += SO
if(!shoppinglist.len)
return
var/orderedamount = shoppinglist.len
var/list/clear_turfs = get_clear_turfs()
for(var/datum/supply_order/SO in shoppinglist)
if(!clear_turfs.len)
break
var/i = rand(1,clear_turfs.len)
var/turf/pickedloc = clear_turfs[i]
clear_turfs.Cut(i,i+1)
SO.status = SUP_ORDER_SHIPPED
var/datum/supply_pack/SP = SO.object
var/obj/A = new SP.containertype(pickedloc)
A.name = "[SP.containername] [SO.comment ? "([SO.comment])":"" ]"
//supply manifest generation begin
var/obj/item/weapon/paper/manifest/slip
if(!SP.contraband)
slip = new /obj/item/weapon/paper/manifest(A)
slip.is_copy = 0
slip.info = "<h3>[command_name()] Shipping Manifest</h3><hr><br>"
slip.info +="Order #[SO.ordernum]<br>"
slip.info +="Destination: [station_name()]<br>"
slip.info +="[orderedamount] PACKAGES IN THIS SHIPMENT<br>"
slip.info +="CONTENTS:<br><ul>"
//spawn the stuff, finish generating the manifest while you're at it
if(SP.access)
if(isnum(SP.access))
A.req_access = list(SP.access)
else if(islist(SP.access) && SP.one_access)
var/list/L = SP.access // access var is a plain var, we need a list
A.req_one_access = L.Copy()
A.req_access.Cut()
else if(islist(SP.access) && !SP.one_access)
var/list/L = SP.access
A.req_access = L.Copy()
else
log_debug("<span class='danger'>Supply pack with invalid access restriction [SP.access] encountered!</span>")
var/list/contains
if(istype(SP,/datum/supply_pack/randomised))
var/datum/supply_pack/randomised/SPR = SP
contains = list()
if(SPR.contains.len)
for(var/j=1,j<=SPR.num_contained,j++)
contains += pick(SPR.contains)
else
contains = SP.contains
for(var/typepath in contains)
if(!typepath)
continue
var/number_of_items = max(1, contains[typepath])
for(var/j = 1 to number_of_items)
var/atom/B2 = new typepath(A)
if(slip)
slip.info += "<li>[B2.name]</li>" //add the item to the manifest
//manifest finalisation
if(slip)
slip.info += "</ul><br>"
slip.info += "CHECK CONTENTS AND STAMP BELOW THE LINE TO CONFIRM RECEIPT OF GOODS<hr>"
return
// Will attempt to purchase the specified order, returning TRUE on success, FALSE on failure
/datum/controller/subsystem/supply/proc/approve_order(var/datum/supply_order/O, var/mob/user)
// Not enough points to purchase the crate
if(points <= O.object.cost)
return FALSE
// Based on the current model, there shouldn't be any entries in order_history, requestlist, or shoppinglist, that aren't matched in adm_order_history
var/datum/supply_order/adm_order
for(var/datum/supply_order/temp in adm_order_history)
if(temp.ordernum == O.ordernum)
adm_order = temp
break
var/idname = "*None Provided*"
if(ishuman(user))
var/mob/living/carbon/human/H = user
idname = H.get_authentification_name()
else if(issilicon(user))
idname = user.real_name
// Update order status
O.status = SUP_ORDER_APPROVED
O.approved_by = idname
O.approved_at = stationdate2text() + " - " + stationtime2text()
// Update admin-side mirror
adm_order.status = SUP_ORDER_APPROVED
adm_order.approved_by = idname
adm_order.approved_at = stationdate2text() + " - " + stationtime2text()
// Deduct cost
points -= O.object.cost
return TRUE
// Will deny the specified order. Only useful if the order is currently requested, but available at any status
/datum/controller/subsystem/supply/proc/deny_order(var/datum/supply_order/O, var/mob/user)
// Based on the current model, there shouldn't be any entries in order_history, requestlist, or shoppinglist, that aren't matched in adm_order_history
var/datum/supply_order/adm_order
for(var/datum/supply_order/temp in adm_order_history)
if(temp.ordernum == O.ordernum)
adm_order = temp
break
var/idname = "*None Provided*"
if(ishuman(user))
var/mob/living/carbon/human/H = user
idname = H.get_authentification_name()
else if(issilicon(user))
idname = user.real_name
// Update order status
O.status = SUP_ORDER_DENIED
O.approved_by = idname
O.approved_at = stationdate2text() + " - " + stationtime2text()
// Update admin-side mirror
adm_order.status = SUP_ORDER_DENIED
adm_order.approved_by = idname
adm_order.approved_at = stationdate2text() + " - " + stationtime2text()
return
// Will deny all requested orders
/datum/controller/subsystem/supply/proc/deny_all_pending(var/mob/user)
for(var/datum/supply_order/O in order_history)
if(O.status == SUP_ORDER_REQUESTED)
deny_order(O, user)
// Will delete the specified order from the user-side list
/datum/controller/subsystem/supply/proc/delete_order(var/datum/supply_order/O, var/mob/user)
// Making sure they know what they're doing
if(alert(user, "Are you sure you want to delete this record? If it has been approved, cargo points will NOT be refunded!", "Delete Record","No","Yes") == "Yes")
if(alert(user, "Are you really sure? There is no way to recover the order once deleted.", "Delete Record", "No", "Yes") == "Yes")
log_admin("[key_name(user)] has deleted supply order \ref[O] [O] from the user-side order history.")
order_history -= O
return
// Will generate a new, requested order, for the given supply pack type
/datum/controller/subsystem/supply/proc/create_order(var/datum/supply_pack/S, var/mob/user, var/reason)
var/datum/supply_order/new_order = new()
var/datum/supply_order/adm_order = new() // Admin-recorded order must be a separate copy in memory, or user-made edits will corrupt it
var/idname = "*None Provided*"
if(ishuman(user))
var/mob/living/carbon/human/H = user
idname = H.get_authentification_name()
else if(issilicon(user))
idname = user.real_name
new_order.ordernum = ++ordernum // Ordernum is used to track the order between the playerside list of orders and the adminside list
new_order.index = new_order.ordernum // Index can be fabricated, or falsified. Ordernum is a permanent marker used to track the order
new_order.object = S
new_order.name = S.name
new_order.cost = S.cost
new_order.ordered_by = idname
new_order.comment = reason
new_order.ordered_at = stationdate2text() + " - " + stationtime2text()
new_order.status = SUP_ORDER_REQUESTED
adm_order.ordernum = new_order.ordernum
adm_order.index = new_order.index
adm_order.object = new_order.object
adm_order.name = new_order.name
adm_order.cost = new_order.cost
adm_order.ordered_by = new_order.ordered_by
adm_order.comment = new_order.comment
adm_order.ordered_at = new_order.ordered_at
adm_order.status = new_order.status
order_history += new_order
adm_order_history += adm_order
// Will delete the specified export receipt from the user-side list
/datum/controller/subsystem/supply/proc/delete_export(var/datum/exported_crate/E, var/mob/user)
// Making sure they know what they're doing
if(alert(user, "Are you sure you want to delete this record?", "Delete Record","No","Yes") == "Yes")
if(alert(user, "Are you really sure? There is no way to recover the receipt once deleted.", "Delete Record", "No", "Yes") == "Yes")
log_admin("[key_name(user)] has deleted export receipt \ref[E] [E] from the user-side export history.")
exported_crates -= E
return
// Will add an item entry to the specified export receipt on the user-side list
/datum/controller/subsystem/supply/proc/add_export_item(var/datum/exported_crate/E, var/mob/user)
var/new_name = input(user, "Name", "Please enter the name of the item.") as null|text
if(!new_name)
return
var/new_quantity = input(user, "Name", "Please enter the quantity of the item.") as null|num
if(!new_quantity)
return
var/new_value = input(user, "Name", "Please enter the value of the item.") as null|num
if(!new_value)
return
E.contents[++E.contents.len] = list(
"object" = new_name,
"quantity" = new_quantity,
"value" = new_value
)
/datum/exported_crate
var/name
var/value
var/list/contents
/datum/supply_order
var/ordernum // Unfabricatable index
var/index // Fabricatable index
var/datum/supply_pack/object = null
var/cost // Cost of the supply pack (Fabricatable) (Changes not reflected when purchasing supply packs, this is cosmetic only)
var/name // Name of the supply pack datum (Fabricatable)
var/ordered_by = null // Who requested the order
var/comment = null // What reason was given for the order
var/approved_by = null // Who approved the order
var/ordered_at // Date and time the order was requested at
var/approved_at // Date and time the order was approved at
var/status // [Requested, Accepted, Denied, Shipped]
+10 -10
View File
@@ -104,7 +104,7 @@ SUBSYSTEM_DEF(vote)
else
factor = 1.4
choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor)
world << "<font color='purple'>Crew Transfer Factor: [factor]</font>"
to_world("<font color='purple'>Crew Transfer Factor: [factor]</font>")
greatest_votes = max(choices["Initiate Crew Transfer"], choices["Extend the Shift"]) //VOREStation Edit
. = list() // Get all options with that many votes and return them in a list
@@ -166,10 +166,10 @@ SUBSYSTEM_DEF(vote)
if(mode == VOTE_GAMEMODE) //fire this even if the vote fails.
if(!round_progressing)
round_progressing = 1
world << "<font color='red'><b>The round will start soon.</b></font>"
to_world("<font color='red'><b>The round will start soon.</b></font>")
if(restart)
world << "World restarting due to vote..."
to_world("World restarting due to vote...")
feedback_set_details("end_error", "restart vote")
if(blackbox)
blackbox.save_all_data_to_sql()
@@ -191,7 +191,7 @@ SUBSYSTEM_DEF(vote)
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, automatic = FALSE, time = config.vote_period)
if(!mode)
if(started_time != null && !(check_rights(R_ADMIN) || automatic))
if(started_time != null && !(check_rights(R_ADMIN|R_EVENT) || automatic))
var/next_allowed_time = (started_time + config.vote_delay)
if(next_allowed_time > world.time)
return 0
@@ -213,12 +213,12 @@ SUBSYSTEM_DEF(vote)
additional_text.Add("<td align = 'center'>[M.required_players]</td>")
gamemode_names["secret"] = "Secret"
if(VOTE_CREW_TRANSFER)
if(!check_rights(R_ADMIN|R_MOD, 0)) // The gods care not for the affairs of the mortals
if(!check_rights(R_ADMIN|R_MOD|R_EVENT, 0)) // The gods care not for the affairs of the mortals
if(get_security_level() == "red" || get_security_level() == "delta")
initiator_key << "The current alert status is too high to call for a crew transfer!"
to_chat(initiator_key, "The current alert status is too high to call for a crew transfer!")
return 0
if(ticker.current_state <= GAME_STATE_SETTING_UP)
initiator_key << "The crew transfer button has been disabled!"
to_chat(initiator_key, "The crew transfer button has been disabled!")
return 0
question = "Your PDA beeps with a message from Central. Would you like an additional hour to finish ongoing projects?" //Yawn Wider Edit //CHOMP EDIT: Changed to 'one' hour.
choices.Add("Initiate Crew Transfer", "Extend the Shift") //VOREStation Edit
@@ -252,13 +252,13 @@ SUBSYSTEM_DEF(vote)
log_vote(text)
world << "<font color='purple'><b>[text]</b>\nType <b>vote</b> or click <a href='?src=\ref[src]'>here</a> to place your votes.\nYou have [config.vote_period / 10] seconds to vote.</font>"
to_world("<font color='purple'><b>[text]</b>\nType <b>vote</b> or click <a href='?src=\ref[src]'>here</a> to place your votes.\nYou have [config.vote_period / 10] seconds to vote.</font>")
if(vote_type == VOTE_CREW_TRANSFER || vote_type == VOTE_GAMEMODE || vote_type == VOTE_CUSTOM)
world << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3)
if(mode == VOTE_GAMEMODE && round_progressing)
round_progressing = 0
world << "<font color='red'><b>Round start has been delayed.</b></font>"
to_world("<font color='red'><b>Round start has been delayed.</b></font>")
time_remaining = round(config.vote_period / 10)
return 1
@@ -269,7 +269,7 @@ SUBSYSTEM_DEF(vote)
return
var/admin = FALSE
if(C.holder)
if(C.holder.rights & R_ADMIN)
if(C.holder.rights & R_ADMIN|R_EVENT)
admin = TRUE
. = "<html><head><title>Voting Panel</title></head><body>"
+4 -1
View File
@@ -30,7 +30,7 @@ SUBSYSTEM_DEF(xenoarch)
. = ..()
/datum/controller/subsystem/xenoarch/proc/SetupXenoarch()
for(var/turf/simulated/mineral/M in turfs)
for(var/turf/simulated/mineral/M in world)
if(!M.density || M.z in using_map.xenoarch_exempt_levels)
continue
@@ -99,6 +99,9 @@ SUBSYSTEM_DEF(xenoarch)
if(isnull(M.artifact_find) && digsite != DIGSITE_GARDEN && digsite != DIGSITE_ANIMAL)
artifact_spawning_turfs.Add(archeo_turf)
//Larger maps will convince byond this is an infinite loop, so let go for a second
CHECK_TICK
//create artifact machinery
var/num_artifacts_spawn = rand(ARTIFACTSPAWNNUM_LOWER, ARTIFACTSPAWNNUM_UPPER)
while(artifact_spawning_turfs.len > num_artifacts_spawn)