mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2026-08-31 09:09:49 +01:00
Merge branch 'master' of https://github.com/Yawn-Wider/YWPolarisVore into UpstreamMergeApril2020
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
@@ -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 ..()
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]")
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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" : ""]")
|
||||
|
||||
@@ -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))
|
||||
@@ -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"
|
||||
@@ -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]
|
||||
@@ -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>"
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user