Files
Bubberstation/code/modules/holodeck/computer.dm
TheVekter f80105ed21 Adds proper logging to Holodeck safeties and programs (#58149)
* Adds proper logging to Holodeck safeties and programs

* a more elegant solution
2021-04-05 23:29:28 +02:00

408 lines
15 KiB
Plaintext

/*
Map Template Holodeck
Holodeck finds the location of mapped_start_area and loads offline_program in it on LateInitialize. It then passes its program templates to Holodeck.js in the form of program_cache and emag_programs. when a user selects a program the
ui calls load_program() with the id of the selected program.
load_program() -> map_template/load() on map_template/holodeck.
holodeck map templates:
1. have an update_blacklist that doesnt allow placing on non holofloors (except for engine floors so you can repair it)
2. has should_place_on_top = FALSE, so that the baseturfs list doesnt pull a kilostation oom crash
3. has returns_created = TRUE, so that SSatoms gives the map template a list of spawned atoms
all the fancy flags and shit are added to holodeck objects in finish_spawn()
Easiest way to add new holodeck programs:
1. Define new map template datums in code/modules/holodeck/holodeck_map_templates, make sure they have the access flags
of the holodeck you want them to be able to load, for the onstation holodeck the flag is STATION_HOLODECK.
2. Create the new map templates in _maps/templates (remember theyre 9x10, and make sure they have area/noop or else it will fuck with linked)
all turfs in holodeck programs MUST be of type /turf/open/floor/holofloor, OR /turf/open/floor/engine, or they will block future programs!
Note: if youre looking at holodeck code because you want to see how returns_created is handled so that templates return a list of atoms
created from them: make sure you handle that list correctly! Either copy them by value and delete them or reference it and handle qdel'ing
and clear when youre done! if you dont i will use :newspaper2: on you
*/
#define HOLODECK_CD 2 SECONDS
#define HOLODECK_DMG_CD 5 SECONDS
/obj/machinery/computer/holodeck
name = "holodeck control console"
desc = "A computer used to control a nearby holodeck."
icon_screen = "holocontrol"
idle_power_usage = 10
active_power_usage = 50
//new vars
///what area type this holodeck loads into. linked turns into the nearest instance of this area
var/area/mapped_start_area = /area/holodeck/rec_center
///the currently used map template
var/datum/map_template/holodeck/template
///bottom left corner of the loading room, used for placing
var/turf/bottom_left
///if TRUE the holodeck is busy spawning another simulation and should immediately stop loading the newest one
var/spawning_simulation = FALSE
//old vars
///the area that this holodeck loads templates into, used for power and deleting holo objects that leave it
var/area/holodeck/linked
///what program is loaded right now or is about to be loaded
var/program = "holodeck_offline"
var/last_program
///the default program loaded by this holodeck when spawned and when deactivated
var/offline_program = "holodeck_offline"
///stores all of the unrestricted holodeck map templates that this computer has access to
var/list/program_cache
///stores all of the restricted holodeck map templates that this computer has access to
var/list/emag_programs
///subtypes of this (but not this itself) are loadable programs
var/program_type = /datum/map_template/holodeck
///every holo object created by the holodeck goes in here to track it
var/list/spawned = list()
var/list/effects = list() //like above, but for holo effects
///TRUE if the holodeck is using extra power because of a program, FALSE otherwise
var/active = FALSE
///increases the holodeck cooldown if TRUE, causing the holodeck to take longer to allow loading new programs
var/damaged = FALSE
//creates the timer that determines if another program can be manually loaded
COOLDOWN_DECLARE(holodeck_cooldown)
/obj/machinery/computer/holodeck/Initialize(mapload)
..()
return INITIALIZE_HINT_LATELOAD
/obj/machinery/computer/holodeck/LateInitialize()//from here linked is populated and the program list is generated. its also set to load the offline program
linked = GLOB.areas_by_type[mapped_start_area]
bottom_left = locate(linked.x, linked.y, src.z)
var/area/computer_area = get_area(src)
if(istype(computer_area, /area/holodeck))
log_mapping("Holodeck computer cannot be in a holodeck, This would cause circular power dependency.")
qdel(src)
return
// the following is necessary for power reasons
if(!linked)
log_world("No matching holodeck area found")
qdel(src)
return
else if (!offline_program)
stack_trace("Holodeck console created without an offline program")
qdel(src)
return
else
linked.linked = src
var/area/my_area = get_area(src)
if(my_area)
linked.power_usage = my_area.power_usage
else
linked.power_usage = list(AREA_USAGE_LEN)
COOLDOWN_START(src, holodeck_cooldown, HOLODECK_CD)
generate_program_list()
load_program(offline_program,TRUE)
///adds all programs that this holodeck has access to, and separates the restricted and unrestricted ones
/obj/machinery/computer/holodeck/proc/generate_program_list()
for(var/typekey in subtypesof(program_type))
var/datum/map_template/holodeck/program = typekey
var/list/info_this = list("id" = initial(program.template_id), "name" = initial(program.name))
if(initial(program.restricted))
LAZYADD(emag_programs, list(info_this))
else
LAZYADD(program_cache, list(info_this))
/obj/machinery/computer/holodeck/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Holodeck", name)
ui.open()
/obj/machinery/computer/holodeck/ui_data(mob/user)
var/list/data = list()
data["default_programs"] = program_cache
if(obj_flags & EMAGGED)
data["emagged"] = TRUE
data["emag_programs"] = emag_programs
data["program"] = program
data["can_toggle_safety"] = issilicon(user) || isAdminGhostAI(user)
return data
/obj/machinery/computer/holodeck/ui_act(action, params)
. = ..()
if(.)
return
. = TRUE
switch(action)
if("load_program")
var/program_to_load = params["id"]
var/list/checked = program_cache.Copy()
if (obj_flags & EMAGGED)
checked |= emag_programs
var/valid = FALSE //dont tell security about this
for (var/prog in checked)//checks if program_to_load is any one of the loadable programs, if it isnt then it rejects it
var/list/check_list = prog
if (check_list["id"] == program_to_load)
valid = TRUE
break
if (!valid)
return FALSE
//load the map_template that program_to_load represents
if(program_to_load)
load_program(program_to_load)
if("safety")
if((obj_flags & EMAGGED) && program)
emergency_shutdown()
nerf(obj_flags & EMAGGED,FALSE)
obj_flags ^= EMAGGED
say("Safeties reset. Restarting...")
log_game("[key_name(usr)] disabled Holodeck safeties.")
///this is what makes the holodeck not spawn anything on broken tiles (space and non engine plating / non holofloors)
/datum/map_template/holodeck/update_blacklist(turf/placement, list/input_blacklist)
for (var/_turf in get_affected_turfs(placement))
var/turf/possible_blacklist = _turf
if (possible_blacklist.holodeck_compatible)
continue
input_blacklist += possible_blacklist
///loads the template whose id string it was given ("offline_program" loads datum/map_template/holodeck/offline)
/obj/machinery/computer/holodeck/proc/load_program(map_id, force = FALSE, add_delay = TRUE)
if (program == map_id)
return
if (!is_operational)//load_program is called once with a timer (in toggle_power) we dont want this to load anything if its off
map_id = offline_program
force = TRUE
if (!force && (!COOLDOWN_FINISHED(src, holodeck_cooldown) || spawning_simulation))
say("ERROR. Recalibrating projection apparatus.")
return
if(spawning_simulation)
return
if (add_delay)
COOLDOWN_START(src, holodeck_cooldown, (damaged ? HOLODECK_CD + HOLODECK_DMG_CD : HOLODECK_CD))
if (damaged && floorcheck())
damaged = FALSE
spawning_simulation = TRUE
active = (map_id != offline_program)
use_power = active + IDLE_POWER_USE
program = map_id
//clear the items from the previous program
for (var/_item in spawned)
var/obj/holo_item = _item
derez(holo_item)
for (var/_effect in effects)
var/obj/effect/holodeck_effect/holo_effect = _effect
effects -= holo_effect
holo_effect.deactivate(src)
//makes sure that any time a holoturf is inside a baseturf list (e.g. if someone put a wall over it) its set to the OFFLINE turf
//so that you cant bring turfs from previous programs into other ones (like putting the plasma burn turf into lounge for example)
for (var/turf/closed/holo_turf in linked)
for (var/_baseturf in holo_turf.baseturfs)
if (ispath(_baseturf, /turf/open/floor/holofloor))
holo_turf.baseturfs -= _baseturf
holo_turf.baseturfs += /turf/open/floor/holofloor/plating
template = SSmapping.holodeck_templates[map_id]
template.load(bottom_left) //this is what actually loads the holodeck simulation into the map
if(template.restricted)
log_game("[key_name(usr)] loaded a restricted Holodeck program: [program].")
message_admins("[ADMIN_LOOKUPFLW(usr)] loaded a restricted Holodeck program: [program].")
spawned = template.created_atoms //populate the spawned list with the atoms belonging to the holodeck
if(istype(template, /datum/map_template/holodeck/thunderdome1218) && !SSshuttle.shuttle_purchase_requirements_met[SHUTTLE_UNLOCK_MEDISIM])
say("Special note from \"1218 AD\" developer: I see you too are interested in the REAL dark ages of humanity! I've made this program also unlock some interesting shuttle designs on any communication console around. Have fun!")
SSshuttle.shuttle_purchase_requirements_met[SHUTTLE_UNLOCK_MEDISIM] = TRUE
nerf(!(obj_flags & EMAGGED))
finish_spawn()
///finalizes objects in the spawned list
/obj/machinery/computer/holodeck/proc/finish_spawn()
//this is used for holodeck effects (like spawners). otherwise they dont do shit
//holo effects are taken out of the spawned list and added to the effects list
//turfs and overlay objects are taken out of the spawned list
//objects get resistance flags added to them
for (var/atom/atoms in spawned)
RegisterSignal(atoms, COMSIG_PARENT_PREQDELETED, .proc/remove_from_holo_lists)
atoms.flags_1 |= HOLOGRAM_1
if (isholoeffect(atoms))//activates holo effects and transfers them from the spawned list into the effects list
var/obj/effect/holodeck_effect/holo_effect = atoms
effects += holo_effect
spawned -= holo_effect
var/atom/active_effect = holo_effect.activate(src)
if(istype(active_effect) || islist(active_effect))
spawned += active_effect // we want mobs or objects spawned via holoeffects to be tracked as objects
continue
if (isobj(atoms))
var/obj/holo_object = atoms
holo_object.resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
if (isstructure(holo_object))
holo_object.flags_1 |= NODECONSTRUCT_1
continue
if (ismachinery(holo_object))
var/obj/machinery/machines = holo_object
machines.flags_1 |= NODECONSTRUCT_1
machines.power_change()
if(istype(machines, /obj/machinery/button))
var/obj/machinery/button/buttons = machines
buttons.setup_device()
spawning_simulation = FALSE
///this qdels holoitems that should no longer exist for whatever reason
/obj/machinery/computer/holodeck/proc/derez(obj/object, silent = TRUE, forced = FALSE)
spawned -= object
if(!object)
return
UnregisterSignal(object, COMSIG_PARENT_PREQDELETED)
var/turf/target_turf = get_turf(object)
for(var/c in object) //make sure that things inside of a holoitem are moved outside before destroying it
var/atom/movable/object_contents = c
object_contents.forceMove(target_turf)
if(!silent)
visible_message("<span class='notice'>[object] fades away!</span>")
qdel(object)
/obj/machinery/computer/holodeck/proc/remove_from_holo_lists(datum/to_remove, _forced)
spawned -= to_remove
UnregisterSignal(to_remove, COMSIG_PARENT_PREQDELETED)
/obj/machinery/computer/holodeck/process(delta_time)
if(damaged && DT_PROB(5, delta_time))
for(var/turf/holo_turf in linked)
if(DT_PROB(2.5, delta_time))
do_sparks(2, 1, holo_turf)
return
. = ..()
if(!. || program == offline_program)//we dont need to scan the holodeck if the holodeck is offline
return
if(!floorcheck()) //if any turfs in the floor of the holodeck are broken
emergency_shutdown()
damaged = TRUE
visible_message("The holodeck overloads!")
for(var/turf/holo_turf in linked)
if(prob(30))
do_sparks(2, 1, holo_turf)
SSexplosions.lowturf += holo_turf
holo_turf.hotspot_expose(1000,500,1)
if(!(obj_flags & EMAGGED))
for(var/item in spawned)
if(!(get_turf(item) in linked))
derez(item)
for(var/_effect in effects)
var/obj/effect/holodeck_effect/holo_effect = _effect
holo_effect.tick()
active_power_usage = 50 + spawned.len * 3 + effects.len * 5
/obj/machinery/computer/holodeck/proc/toggle_power(toggleOn = FALSE)
if(active == toggleOn)
return
if(toggleOn)
if(last_program && (last_program != offline_program))
addtimer(CALLBACK(src,.proc/load_program, last_program, TRUE), 25)
active = TRUE
else
last_program = program
load_program(offline_program, TRUE)
active = FALSE
/obj/machinery/computer/holodeck/power_change()
. = ..()
INVOKE_ASYNC(src, .proc/toggle_power, !machine_stat)
///shuts down the holodeck and force loads the offline_program
/obj/machinery/computer/holodeck/proc/emergency_shutdown()
last_program = program
active = FALSE
load_program(offline_program, TRUE)
///returns TRUE if the entire floor of the holodeck is intact, returns FALSE if any are broken
/obj/machinery/computer/holodeck/proc/floorcheck()
for(var/turf/holo_floor in linked)
if(isspaceturf(holo_floor))
return FALSE
if(!holo_floor.intact)
return FALSE
return TRUE
///changes all weapons in the holodeck to do stamina damage if set
/obj/machinery/computer/holodeck/proc/nerf(nerf_this, is_loading = TRUE)
if (!nerf_this && is_loading)
return
for(var/obj/item/to_be_nerfed in spawned)
to_be_nerfed.damtype = nerf_this ? STAMINA : initial(to_be_nerfed.damtype)
for(var/to_be_nerfed in effects)
var/obj/effect/holodeck_effect/holo_effect = to_be_nerfed
holo_effect.safety(nerf_this)
/obj/machinery/computer/holodeck/emag_act(mob/user)
if(obj_flags & EMAGGED)
return
if(!LAZYLEN(emag_programs))
to_chat(user, "[src] does not seem to have a card swipe port. It must be an inferior model.")
return
playsound(src, "sparks", 75, TRUE)
obj_flags |= EMAGGED
to_chat(user, "<span class='warning'>You vastly increase projector power and override the safety and security protocols.</span>")
say("Warning. Automatic shutoff and derezzing protocols have been corrupted. Please call Nanotrasen maintenance and do not use the simulator.")
log_game("[key_name(user)] emagged the Holodeck Control Console")
nerf(!(obj_flags & EMAGGED),FALSE)
/obj/machinery/computer/holodeck/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
return
emergency_shutdown()
/obj/machinery/computer/holodeck/ex_act(severity, target)
emergency_shutdown()
return ..()
/obj/machinery/computer/holodeck/Destroy()
emergency_shutdown()
if(linked)
linked.linked = null
linked.power_usage = list(AREA_USAGE_LEN)
return ..()
/obj/machinery/computer/holodeck/blob_act(obj/structure/blob/B)
emergency_shutdown()
return ..()
#undef HOLODECK_CD
#undef HOLODECK_DMG_CD