mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-09 07:57:50 +00:00
* Pref code refactor * Empty database reference * Unit testing SQLite * Everything else * Disable unit testing. * Equivalent * more robust unit tests
534 lines
16 KiB
Plaintext
534 lines
16 KiB
Plaintext
/obj/effect/mark
|
|
var/mark = ""
|
|
icon = 'icons/misc/mark.dmi'
|
|
icon_state = "blank"
|
|
anchored = 1
|
|
mouse_opacity = 0
|
|
|
|
/obj/effect/begin
|
|
name = "begin"
|
|
icon = 'icons/obj/stationobjs.dmi'
|
|
icon_state = "begin"
|
|
anchored = 1.0
|
|
|
|
/*
|
|
* This item is completely unused, but removing it will break something in R&D and Radio code causing PDA and Ninja code to fail on compile
|
|
*/
|
|
|
|
// Holy shit someone make this a datum already
|
|
/obj/effect/datacore
|
|
name = "datacore"
|
|
var/medical[] = list()
|
|
var/general[] = list()
|
|
var/security[] = list()
|
|
//This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character().
|
|
var/locked[] = list()
|
|
|
|
// Finds a record in 'general' based on the name, then finds a record in 'which' based on the ID
|
|
// Returns the record if it finds one, null otherwise
|
|
/obj/effect/datacore/proc/find_record_by_name(var/target_name, var/list/which)
|
|
for (var/datum/data/record/E in general)
|
|
if (E.fields["name"] == target_name)
|
|
for (var/datum/data/record/R in which)
|
|
if (R.fields["id"] == E.fields["id"])
|
|
return R
|
|
return null
|
|
|
|
/obj/effect/datacore/proc/find_record_by_dna(var/target_dna, var/list/which)
|
|
for (var/datum/data/record/E in which)
|
|
if (E.fields["b_dna"] == target_dna)
|
|
return E
|
|
return null
|
|
|
|
/obj/effect/datacore/proc/find_general_record_by_name(var/target_name)
|
|
for(var/datum/data/record/E in general)
|
|
if(E.fields["name"] == target_name)
|
|
return E
|
|
return null
|
|
|
|
/obj/effect/datacore/proc/find_medical_record_by_name(var/target_name)
|
|
return find_record_by_name(target_name, medical)
|
|
|
|
/obj/effect/datacore/proc/find_medical_record_by_dna(var/target_dna)
|
|
return find_record_by_dna(target_dna, medical)
|
|
|
|
/obj/effect/datacore/proc/find_security_record_by_name(var/target_name)
|
|
return find_record_by_name(target_name, security)
|
|
|
|
/obj/effect/datacore/proc/get_manifest(monochrome, OOC)
|
|
var/list/heads = new()
|
|
var/list/sec = new()
|
|
var/list/eng = new()
|
|
var/list/med = new()
|
|
var/list/sci = new()
|
|
var/list/cgo = new()
|
|
var/list/civ = new()
|
|
var/list/bot = new()
|
|
var/list/misc = new()
|
|
var/list/isactive = new()
|
|
var/dat = {"
|
|
<head><style>
|
|
.manifest {border-collapse:collapse;}
|
|
.manifest td, th {border:1px solid [monochrome?"black":"#DEF; background-color:white; color:black"]; padding:.25em}
|
|
.manifest th {height: 2em; [monochrome?"border-top-width: 3px":"background-color: #48C; color:white"]}
|
|
.manifest tr.head th { [monochrome?"border-top-width: 1px":"background-color: #488;"] }
|
|
.manifest td:first-child {text-align:right}
|
|
.manifest tr.alt td {[monochrome?"border-top-width: 2px":"background-color: #DEF"]}
|
|
</style></head>
|
|
<table class="manifest" width='350px'>
|
|
<tr class='head'><th>Name</th><th>Rank</th><th>Activity</th></tr>
|
|
"}
|
|
var/even = 0
|
|
// sort mobs
|
|
if(OOC)
|
|
for(var/mob/living/silicon/ai/dooropener in mob_list)
|
|
if(dooropener.parent) //Hide shunted AI APC Copies from view.
|
|
continue
|
|
bot[dooropener.name] = "AI"
|
|
isactive[dooropener.name] = (dooropener.client && dooropener.client.inactivity <= 10 * 60 * 10) ? "Active" : "Inactive"
|
|
for(var/mob/living/silicon/robot/tincan in mob_list)
|
|
if(isMoMMI(tincan))
|
|
continue
|
|
if(tincan.syndicate)
|
|
continue
|
|
bot[tincan.name] = tincan.braintype
|
|
isactive[tincan.name] = (tincan.client && tincan.client.inactivity <= 10 * 60 * 10) ? "Active" : "Inactive"
|
|
for(var/datum/data/record/t in sortRecord(data_core.general))
|
|
var/name = t.fields["name"]
|
|
var/rank = t.fields["rank"]
|
|
var/real_rank = t.fields["real_rank"]
|
|
if(OOC)
|
|
var/active = 0
|
|
var/SSD = 0
|
|
for(var/mob/M in player_list)
|
|
if(M.real_name == name)
|
|
if(!M.client)
|
|
SSD = 1
|
|
break
|
|
if(M.client && M.client.inactivity <= 10 * 60 * 10)
|
|
active = 1
|
|
break
|
|
isactive[name] = (SSD ? "SSD" : (active ? "Active" : "Inactive"))
|
|
else
|
|
isactive[name] = t.fields["p_stat"]
|
|
//cael - to prevent multiple appearances of a player/job combination, add a continue after each line
|
|
var/department = 0
|
|
if((real_rank in command_positions) || (t.fields["override_dept"] == "Command"))
|
|
heads[name] = rank
|
|
department = 1
|
|
if((real_rank in security_positions) || (t.fields["override_dept"] == "Security"))
|
|
sec[name] = rank
|
|
department = 1
|
|
if((real_rank in engineering_positions) || (t.fields["override_dept"] == "Engineering"))
|
|
eng[name] = rank
|
|
department = 1
|
|
if((real_rank in medical_positions) || (t.fields["override_dept"] == "Medical"))
|
|
med[name] = rank
|
|
department = 1
|
|
if((real_rank in science_positions) || (t.fields["override_dept"] == "Science"))
|
|
sci[name] = rank
|
|
department = 1
|
|
if((real_rank in cargo_positions) || (t.fields["override_dept"] == "Cargo"))
|
|
cgo[name] = rank
|
|
department = 1
|
|
if((real_rank in civilian_positions) || (t.fields["override_dept"] == "Civilian"))
|
|
civ[name] = rank
|
|
department = 1
|
|
if(!OOC && (real_rank in nonhuman_positions))
|
|
bot[name] = rank
|
|
department = 1
|
|
if(!department && !(name in heads))
|
|
misc[name] = rank
|
|
if(heads.len > 0)
|
|
dat += "<tr><th colspan=3>Heads</th></tr>"
|
|
for(name in heads)
|
|
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[heads[name]]</td><td>[isactive[name]]</td></tr>"
|
|
even = !even
|
|
|
|
if(sec.len > 0)
|
|
dat += "<tr><th colspan=3>Security</th></tr>"
|
|
for(name in sec)
|
|
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[sec[name]]</td><td>[isactive[name]]</td></tr>"
|
|
even = !even
|
|
|
|
if(eng.len > 0)
|
|
dat += "<tr><th colspan=3>Engineering</th></tr>"
|
|
for(name in eng)
|
|
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[eng[name]]</td><td>[isactive[name]]</td></tr>"
|
|
even = !even
|
|
|
|
if(med.len > 0)
|
|
dat += "<tr><th colspan=3>Medical</th></tr>"
|
|
for(name in med)
|
|
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[med[name]]</td><td>[isactive[name]]</td></tr>"
|
|
even = !even
|
|
|
|
if(sci.len > 0)
|
|
dat += "<tr><th colspan=3>Science</th></tr>"
|
|
for(name in sci)
|
|
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[sci[name]]</td><td>[isactive[name]]</td></tr>"
|
|
even = !even
|
|
|
|
if(cgo.len > 0)
|
|
dat += "<tr><th colspan=3>Cargo</th></tr>"
|
|
for(name in cgo)
|
|
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[cgo[name]]</td><td>[isactive[name]]</td></tr>"
|
|
even = !even
|
|
|
|
if(civ.len > 0)
|
|
dat += "<tr><th colspan=3>Civilian</th></tr>"
|
|
for(name in civ)
|
|
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[civ[name]]</td><td>[isactive[name]]</td></tr>"
|
|
even = !even
|
|
|
|
// misc guys
|
|
if(misc.len > 0)
|
|
dat += "<tr><th colspan=3>Miscellaneous</th></tr>"
|
|
for(name in misc)
|
|
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[misc[name]]</td><td>[isactive[name]]</td></tr>"
|
|
even = !even
|
|
|
|
if(bot.len > 0)
|
|
dat += "<tr><th colspan=3>Silicon</th></tr>"
|
|
for(name in bot)
|
|
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[bot[name]]</td><td>[isactive[name]]</td></tr>"
|
|
even = !even
|
|
|
|
dat += "</table>"
|
|
dat = replacetext(dat, "\n", "") // so it can be placed on paper correctly
|
|
dat = replacetext(dat, "\t", "")
|
|
return dat
|
|
|
|
/* Predicting the manifest is much less intense than building a real one.
|
|
We don't care about names, DNA, accounts, activity, any of that. We're just gonna loop through high job prefs.*/
|
|
|
|
/datum/controller/occupations/proc/display_prediction()
|
|
var/list/heads = new()
|
|
var/list/sec = new()
|
|
var/list/eng = new()
|
|
var/list/med = new()
|
|
var/list/sci = new()
|
|
var/list/cgo = new()
|
|
var/list/civ = new()
|
|
var/list/bot = new()
|
|
var/list/misc = new()
|
|
predict_manifest()
|
|
if(!crystal_ball.len)
|
|
return "No prediction has been made!" //This only gets shown the first time ever. If everyone unreadies it's blank.
|
|
var/dat = {"
|
|
<head><head><style>
|
|
.manifest {border-collapse:collapse;}
|
|
.manifest td, th {border:1px solid #DEF; background-color:white; color:black; padding:.25em}
|
|
.manifest th {height: 2em; background-color: #48C; color:white}
|
|
.manifest tr.head th {background-color: #488}
|
|
.manifest td:first-child {text-align:right}
|
|
.manifest tr.alt td {background-color: #DEF}
|
|
</style></head>
|
|
<table class="manifest" width='350px'>
|
|
<tr class='head'><th>Rank</th><th>Quantity</th></tr>
|
|
"}
|
|
|
|
var/color = 0
|
|
for(var/job in crystal_ball)
|
|
if(!crystal_ball[job])
|
|
continue //If 0, skip
|
|
var/department = 0
|
|
if(job in command_positions)
|
|
heads[job] = crystal_ball[job]
|
|
department = 1
|
|
if(job in security_positions)
|
|
sec[job] = crystal_ball[job]
|
|
department = 1
|
|
if(job in engineering_positions)
|
|
eng[job] = crystal_ball[job]
|
|
department = 1
|
|
if(job in medical_positions)
|
|
med[job] = crystal_ball[job]
|
|
department = 1
|
|
if(job in science_positions)
|
|
sci[job] = crystal_ball[job]
|
|
department = 1
|
|
if(job in cargo_positions)
|
|
cgo[job] = crystal_ball[job]
|
|
department = 1
|
|
if(job in civilian_positions)
|
|
civ[job] = crystal_ball[job]
|
|
department = 1
|
|
if(job in nonhuman_positions)
|
|
bot[job] = crystal_ball[job]
|
|
department = 1
|
|
if(!department && !(job in heads))
|
|
misc[job] = crystal_ball[job]
|
|
|
|
if(heads.len > 0)
|
|
dat += "<tr><th colspan=3>Heads</th></tr>"
|
|
for(var/job in heads)
|
|
dat += "<tr[color ? " class='alt'" : ""]><td>[job]</td><td>[crystal_ball[job]]</td></tr>"
|
|
color = !color
|
|
|
|
if(sec.len > 0)
|
|
dat += "<tr><th colspan=3>Security</th></tr>"
|
|
for(var/job in sec)
|
|
dat += "<tr[color ? " class='alt'" : ""]><td>[job]</td><td>[crystal_ball[job]]</td></tr>"
|
|
color = !color
|
|
|
|
if(eng.len > 0)
|
|
dat += "<tr><th colspan=3>Engineering</th></tr>"
|
|
for(var/job in eng)
|
|
dat += "<tr[color ? " class='alt'" : ""]><td>[job]</td><td>[crystal_ball[job]]</td></tr>"
|
|
color = !color
|
|
|
|
if(med.len > 0)
|
|
dat += "<tr><th colspan=3>Medical</th></tr>"
|
|
for(var/job in med)
|
|
dat += "<tr[color ? " class='alt'" : ""]><td>[job]</td><td>[crystal_ball[job]]</td></tr>"
|
|
color = !color
|
|
|
|
if(sci.len > 0)
|
|
dat += "<tr><th colspan=3>Science</th></tr>"
|
|
for(var/job in sci)
|
|
dat += "<tr[color ? " class='alt'" : ""]><td>[job]</td><td>[crystal_ball[job]]</td></tr>"
|
|
color = !color
|
|
|
|
if(cgo.len > 0)
|
|
dat += "<tr><th colspan=3>Cargo</th></tr>"
|
|
for(var/job in cgo)
|
|
dat += "<tr[color ? " class='alt'" : ""]><td>[job]</td><td>[crystal_ball[job]]</td></tr>"
|
|
color = !color
|
|
|
|
if(civ.len > 0)
|
|
dat += "<tr><th colspan=3>Civilian</th></tr>"
|
|
for(var/job in civ)
|
|
dat += "<tr[color ? " class='alt'" : ""]><td>[job]</td><td>[crystal_ball[job]]</td></tr>"
|
|
color = !color
|
|
|
|
// misc guys
|
|
if(misc.len > 0)
|
|
dat += "<tr><th colspan=3>Miscellaneous</th></tr>"
|
|
for(var/job in misc)
|
|
dat += "<tr[color ? " class='alt'" : ""]><td>[job]</td><td>[crystal_ball[job]]</td></tr>"
|
|
color = !color
|
|
|
|
if(bot.len > 0)
|
|
dat += "<tr><th colspan=3>Silicon</th></tr>"
|
|
for(var/job in bot)
|
|
dat += "<tr[color ? " class='alt'" : ""]><td>[job]</td><td>[crystal_ball[job]]</td></tr>"
|
|
color = !color
|
|
|
|
dat += "</table>"
|
|
|
|
return dat
|
|
|
|
/datum/controller/occupations/proc/predict_manifest()
|
|
crystal_ball = list("AI" = 0, "Cyborg" = 0, "Captain" = 0, "Head of Personnel" = 0, "Head of Security" = 0, "Chief Engineer" = 0, "Chief Medical Officer" = 0, "Research Director" = 0)
|
|
//We always want to list these first, the rest can be random for all we care.
|
|
for(var/mob/new_player/player in player_list)
|
|
if(!player.ready)
|
|
continue
|
|
|
|
var/list/jobs = player.client.prefs.get_pref(/datum/preference_setting/assoc_list_setting/jobs)
|
|
|
|
for(var/job in jobs)
|
|
if((jobs[job] == JOB_PREF_HIGH) && GetJob(job))
|
|
crystal_ball[job] += 1
|
|
|
|
/*
|
|
We can't just insert in HTML into the nanoUI so we need the raw data to play with.
|
|
Instead of creating this list over and over when someone leaves their PDA open to the page
|
|
we'll only update it when it changes. The PDA_Manifest global list is zeroed out upon any change
|
|
using /obj/effect/datacore/proc/manifest_inject( ), or manifest_insert( )
|
|
*/
|
|
|
|
var/global/list/PDA_Manifest = list()
|
|
|
|
/obj/effect/datacore/proc/get_manifest_json()
|
|
if(PDA_Manifest.len)
|
|
return PDA_Manifest
|
|
var/heads[0]
|
|
var/sec[0]
|
|
var/eng[0]
|
|
var/med[0]
|
|
var/sci[0]
|
|
var/cgo[0]
|
|
var/civ[0]
|
|
var/bot[0]
|
|
var/misc[0]
|
|
for(var/datum/data/record/t in data_core.general)
|
|
var/name = sanitize(t.fields["name"])
|
|
var/rank = sanitize(t.fields["rank"])
|
|
var/real_rank = t.fields["real_rank"]
|
|
var/isactive = t.fields["p_stat"]
|
|
var/department = 0
|
|
var/depthead = 0 // Department Heads will be placed at the top of their lists. Too bad all the procs that get the manifest call get_manifest(), which can't do this without a rewrite.
|
|
if(real_rank in command_positions)
|
|
heads[++heads.len] = list("name" = name, "rank" = rank, "active" = isactive)
|
|
department = 1
|
|
depthead = 1
|
|
if(rank=="Captain" && heads.len != 1)
|
|
heads.Swap(1,heads.len)
|
|
|
|
if(real_rank in security_positions)
|
|
sec[++sec.len] = list("name" = name, "rank" = rank, "active" = isactive)
|
|
department = 1
|
|
if(depthead && sec.len != 1)
|
|
sec.Swap(1,sec.len)
|
|
|
|
if(real_rank in engineering_positions)
|
|
eng[++eng.len] = list("name" = name, "rank" = rank, "active" = isactive)
|
|
department = 1
|
|
if(depthead && eng.len != 1)
|
|
eng.Swap(1,eng.len)
|
|
|
|
if(real_rank in medical_positions)
|
|
med[++med.len] = list("name" = name, "rank" = rank, "active" = isactive)
|
|
department = 1
|
|
if(depthead && med.len != 1)
|
|
med.Swap(1,med.len)
|
|
|
|
if(real_rank in science_positions)
|
|
sci[++sci.len] = list("name" = name, "rank" = rank, "active" = isactive)
|
|
department = 1
|
|
if(depthead && sci.len != 1)
|
|
sci.Swap(1,sci.len)
|
|
|
|
if(real_rank in cargo_positions)
|
|
cgo[++cgo.len] = list("name" = name, "rank" = rank, "active" = isactive)
|
|
department = 1
|
|
if(depthead && cgo.len != 1)
|
|
cgo.Swap(1,cgo.len)
|
|
|
|
if(real_rank in civilian_positions)
|
|
civ[++civ.len] = list("name" = name, "rank" = rank, "active" = isactive)
|
|
department = 1
|
|
if(depthead && civ.len != 1)
|
|
civ.Swap(1,civ.len)
|
|
|
|
if(real_rank in nonhuman_positions)
|
|
bot[++bot.len] = list("name" = name, "rank" = rank, "active" = isactive)
|
|
department = 1
|
|
|
|
if(!department && !(name in heads))
|
|
misc[++misc.len] = list("name" = name, "rank" = rank, "active" = isactive)
|
|
|
|
|
|
PDA_Manifest = list(\
|
|
"heads" = heads,\
|
|
"sec" = sec,\
|
|
"eng" = eng,\
|
|
"med" = med,\
|
|
"sci" = sci,\
|
|
"cgo" = cgo,\
|
|
"civ" = civ,\
|
|
"bot" = bot,\
|
|
"misc" = misc\
|
|
)
|
|
return PDA_Manifest
|
|
|
|
/obj/effect/list_container
|
|
name = "list container"
|
|
|
|
/obj/effect/list_container/mobl
|
|
name = "mobl"
|
|
var/master = null
|
|
|
|
var/list/container = list( )
|
|
|
|
/obj/effect/projection
|
|
name = "Projection"
|
|
desc = "This looks like a projection of something."
|
|
anchored = 1.0
|
|
|
|
|
|
/obj/effect/shut_controller
|
|
name = "shut controller"
|
|
var/moving = null
|
|
var/list/parts = list( )
|
|
|
|
/obj/machinery/showcase
|
|
name = "Showcase"
|
|
icon = 'icons/obj/stationobjs.dmi'
|
|
icon_state = "showcase_1"
|
|
desc = "A stand with the empty body of a cyborg bolted to it."
|
|
density = 1
|
|
anchored = 1
|
|
machine_flags = WRENCHMOVE
|
|
|
|
/obj/item/mouse_drag_pointer = MOUSE_ACTIVE_POINTER
|
|
|
|
/obj/item/weapon/beach_ball
|
|
icon = 'icons/misc/beach.dmi'
|
|
icon_state = "ball"
|
|
name = "beach ball"
|
|
item_state = "beachball"
|
|
density = 0
|
|
anchored = 0
|
|
w_class = W_CLASS_TINY
|
|
force = 0.0
|
|
throwforce = 0.0
|
|
throw_speed = 1
|
|
throw_range = 20
|
|
flags = FPRINT
|
|
siemens_coefficient = 1
|
|
|
|
/obj/item/weapon/beach_ball/afterattack(atom/target as mob|obj|turf|area, mob/user as mob)
|
|
if(user.drop_item(src))
|
|
src.throw_at(target, throw_range, throw_speed)
|
|
|
|
/obj/effect/stop
|
|
var/victim = null
|
|
icon_state = "empty"
|
|
name = "Geas"
|
|
desc = "You can't resist."
|
|
// name = ""
|
|
|
|
/obj/effect/stop/Uncross(atom/movable/mover)
|
|
if(victim == mover)
|
|
return 0
|
|
return 1
|
|
|
|
/obj/effect/stop/sleeping
|
|
var/sleeptime
|
|
icon_state = "empty"
|
|
name = "Sleepy time"
|
|
var/datum/mind/owner
|
|
var/ignored_type
|
|
var/spell/aoe_turf/fall/ourspell
|
|
invisibility = 100
|
|
var/theworld
|
|
ignoreinvert = 1
|
|
|
|
/obj/effect/stop/sleeping/New(loc, ourtime, mind, var/spell/aoe_turf/fall/F, theworld, var/ignored)
|
|
..()
|
|
sleeptime = ourtime
|
|
owner = mind
|
|
ourspell = F
|
|
src.theworld = theworld
|
|
if(ignored)
|
|
ignored_type = ignored
|
|
|
|
/obj/effect/stop/sleeping/Crossed(atom/movable/A)
|
|
if(!(A.flags & TIMELESS) && sleeptime > world.time)
|
|
if(!ignored_type || !istype(A,ignored_type))
|
|
if(ismob(A))
|
|
var/mob/living/L = A
|
|
if(L.mind != owner)
|
|
if(L.client)
|
|
L.client.move_delayer.next_allowed = sleeptime //So we don't need to check timestopped in client/move
|
|
if(!L.stat)
|
|
L.playsound_local(src, theworld == 1 ? 'sound/effects/theworld2.ogg' : 'sound/effects/fall2.ogg', 100, 0, 0, 0, 0)
|
|
//L.Paralyse(round(((sleeptime - world.time)/10)/2, 1))
|
|
//L.update_canmove()
|
|
if(!(L in ourspell.affected))
|
|
invertcolor(L)
|
|
ourspell.affected += L
|
|
ourspell.recursive_timestop(L)
|
|
else
|
|
if(!(A in ourspell.affected))
|
|
invertcolor(A)
|
|
ourspell.affected += A
|
|
ourspell.recursive_timestop(A)
|
|
|
|
|
|
/obj/effect/spawner
|
|
name = "object spawner"
|