mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-06-23 23:24:55 +01:00
7305d12d29
* Nightvision Rework (In the name of color) (#73094) Relies on #72886 for some render relay expansion I use for light_mask stuff. Hello bestie! Night vision pissed me off, so I've come to burn this place to the ground. Two sections to discuss here. First we'll talk about see_in_dark and why I hate it, second we'll discuss the lighting plane and how we brighten it, plus introducing color to the party. https://www.byond.com/docs/ref/#/mob/var/see_in_dark See in dark lets us control how far away from us a turf can be before we hide it/its contents if it's dark (not got luminosity set) We currently set it semi inconsistently to provide nightvision to mobs. The trouble is stuff that produces light != stuff that sets luminosity. The worst case of this can be seen by walking out of escape on icebox, where you'll see this  Snow draws above the lighting plane, so the snow will intermittently draw, depending on see_in_dark and the luminosity from tracking lights. This would in theory be solvable by modifying the area, but the same problem applies across many things in the codebase. As things currently stand, to be emissive you NEED to have a light on your tile. People are bad at this, and honestly it's a bit much to expect of them. An emissive overlay on a canister shouldn't need an element or something and a list on turfs to manage it. This gets worse when you factor in the patterns I'm using to avoid drawing lights above nothing, which leads to lights that should show, but are misoffset because their parent pixel offsets. It's silly. We do it so we can have things like mesons without just handing out night vision, but even there the effect of just hiding objects and mobs looks baddddddd when moving. It's always bothered me. I'll complain about mesons more later, but really just like, they're too bright as it is. I'm proposing here that rather then manually hiding stuff based off distance from the player, we can instead show/hide using just the lighting plane. This means things like mesons are gonna get dimmer, but that's fine because they suck. It does have some side effects, things like view() on mobs won't hide stuff in darkness, but that's fine because none actually thinks about view like that, I think. Oh and I added a case to prevent examining stuff that's in darkness, and not right next to you when you don't have enough nightvision, to match the old behavior `see_in_dark` gave us. Now I'd like to go on a mild tangent about color, please bare with me You ever walk around with mesons on when there's a fire going, or an ethereal or firelocks down. You notice how there isn't really much color to our lights? Doesn't that suck? It's because the way we go about brighting lighting is by making everything on the lighting plane transparent. This is fine for brightening things, but it ends up looking kinda crummy in the end and leads to really washed out colors that should be bright. Playing engineer or miner gets fucking depressing. The central idea of this pr, that everything else falls out of, is instead of making the plane more transparent, we can use color matrixes to make things AT LEAST x bright. https://www.byond.com/docs/ref/#/{notes}/color-matrix Brief recap for color matrixes, fully expanded they're a set of 20 different values in a list Units generally scale 0-1 as multipliers, though since it's multiplication in order to make an rgb(1,1,1) pixel fullbright you would need to use 255s. A "unit matrix" for color looks like this: ``` list(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 ) ``` The first four rows are how much each r, g, b and a impact r, g, b and well a. So a first row of `(1, 0, 0, 0)` means 1 unit of r results in 1 unit of r. and 0 units of green, blue and alpha, and so on. A first row of `(0, 1, 0, 0)` would make 1 red component into 1 green component, and leave red, blue and alpha alone, shifting any red of whatever it's applied to a green. Using these we can essentially color transform our world. It's a fun tool. But there's more. That last row there doesn't take a variable input like the others. Instead, it ADDS some fraction of 255 to red, green, blue and alpha. So a fifth row of `(1, 0, 0, 0)` would make every pixel as red as it could possibly be. This is what we're going to exploit here. You see all these values accept negative multipliers, so we can lower colors down instead of raising them up! The key idea is using color matrix filters https://www.byond.com/docs/ref/#/{notes}/filters/color to chain these operations together. Pulling alllll the way back, we want to brighten darkness without affecting brighter colors. Lower rgb values are darker, higher ones are brighter. This relationship isn't really linear because of suffering reasons, but it's good enough for this. Let's try chaining some matrixes on the lighting plane, which is bright where fullbright, and dark where dark. Take a list like this ``` list(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, -0.2, -0.2, -0.2, 0 ) ``` That would darken the lighting a bit, but negative values will get rounded to 0 A subsequent raising by the same amount ``` list(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0.2, 0.2, 0.2, 0 ) ``` Will essentially threshold our brightness at that value. This ensures we aren't washing out colors when we make things brighter, while leaving higher values unaffected since they basically just had a constant subtracted and then readded. You may have noticed, we gain access to individual color components here. This means not only can we darken and lighten by thresholds, we can COLOR those thresholds. ``` list(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0.1, 0.2, 0.1, 0 ) ``` Something like the above, if applied with its inverse, would tint the darkness green. The delta between the different scalars will determine how vivid the color is, and the actual value will impact the brightness. Something that's always bothered me about nightvision is it's just greyscale for the most part, there isn't any color to it. There was an old idea of coloring the game plane to match their lenses, but if you've ever played with the colorblind quirk you know that gets headachey really fast. So instead of that, lets color just the darkness that these glasses produce. It provides some reminder that you're wearing them, instead of just being something you forget about while playing, and provides a reason to use flashlights and such since they can give you a clearer, less tinted view of things while retaining the ability to look around things. I've so far applied this pattern to JUST headwear for humans (also those mining wisps) I'm planning on furthering it to mobs that use nightvision, but I wanted to get this up cause I don't wanna pr it the day before the freeze. Mesons are green, sec night vision is red, thermals orange, etc. I think the effect this gives is really really nice. I've tuned most things to work for the station, though mesons works for lavaland for obvious reasons. I've tuned things significantly darker then we have them set currently, since I really hate flat lighting and this system suffers when interacting with it. My goal with these is to give you a rough idea of what's around you, without a good eye for detail. That's the difference between say, mesons, and night vision. One helps you see outlines, the other gives you detail and prevents missing someone in the darkness. It's hard to balance this precisely because of different colored backgrounds (looking at you icebox) More can be done on this front in future but I'm quite happy with things as of now I have since expanded to all uses of nightvision, coloring most all of them. Along the way I turned some toggleable nightvision into just one level. Fullbright sucks, and I'd rather just have one "good" value. I've kept it for a few cases, mostly eyes you rip out of mobs. Impacted mobs are nightmares, aliens, zombies, revenants, states and sort of stands. I've done a pass on all mobs and items that impact nightvision and added what I thought was the right level of color to them. This includes stuff like blobs and shuttle control consoles As with glasses much of this was around reducing vision, though I kept it stronger here, since many of these mobs rely on it for engaging with the game <details> <summary> Technical Changes </summary> filter transitions. Found this when testing this pr, seemed silly. This avoids dumbass overlay lighting lighting up wallmounts. We switch modes if some turfflags are set, to accomplish the same thing with more overhead, and support showing things through the darkness. Also fixes a bug where you'd only get one fullscreen object per mob, so opening and closing a submap would take it away Also also fixes the lighting backdrop not actually spanning the screen. It doesn't actually do anything anymore because of the fullscreen light we have, but just in case that's unsued. Needs cleanup in future. color with a sprite This is to support the above We relay this plane to lighting mask so openspace can like, have lighting vision goggles and such Side affect of removing see_in_dark. This logic is a bit weak atm, needs some work. It's a dupe of the nightvision action button, and newly redundant since I've removed all uses of it trasnparent won't render These sucked Also transparent stuff should never render, if it does you'll get white blobs which suck </details> Videos! (Github doesn't like using a summary here I'm sorry) <details> Demonstration of ghost lighting, and color https://user-images.githubusercontent.com/58055496/215693983-99e00f9e-7214-4cf4-a76a-6e669a8a1103.mp4 Engi-glass mesons and walking in maint (Potentially overtuned, yellow is hard) https://user-images.githubusercontent.com/58055496/215695978-26e7dc45-28aa-4285-ae95-62ea3d79860f.mp4 Diagnostic nightvision goggles and see_in_dark not hiding emissives https://user-images.githubusercontent.com/58055496/215692233-115b4094-1099-4393-9e94-db2088d834f3.mp4 Sec nightvision (I just think it looks neat) https://user-images.githubusercontent.com/58055496/215692269-bc08335e-0223-49c3-9faf-d2d7b22fe2d2.mp4 Medical nightvision goggles and other colors https://user-images.githubusercontent.com/58055496/215692286-0ba3de6a-b1d5-4aed-a6eb-c32794ea45da.mp4 Miner mesons and mobs hiding in lavaland (This is basically the darkest possible environment) https://user-images.githubusercontent.com/58055496/215696327-26958b69-0e1c-4412-9298-4e9e68b3df68.mp4 Thermal goggles and coloring displayed mobs https://user-images.githubusercontent.com/58055496/215692710-d2b101f3-7922-498c-918c-9b528d181430.mp4 </details> I think it's pretty, and see_in_dark sucks butt. <!-- If your PR modifies aspects of the game that can be concretely observed by players or admins you should add a changelog. If your change does NOT meet this description, remove this section. Be sure to properly mark your PRs to prevent unnecessary GBP loss. You can read up on GBP and it's effects on PRs in the tgstation guides for contributors. Please note that maintainers freely reserve the right to remove and add tags should they deem it appropriate. You can attempt to finagle the system all you want, but it's best to shoot for clear communication right off the bat. --> 🆑 add: The darkness that glasses and hud goggles that impact your nightvision (think mesons, nightvision goggles, etc) lighten is now tinted to match the glasses. S pretty IMO, and hopefully it helps with forgetting you're wearing X. balance: Nightvision is darker. I think bright looks bad, and things like mesons do way too much balance: Mesons (and mobs in general) no longer have a static distance you can see stuff in the dark. If a tile is lit, you can now see it. fix: Nightvision no longer dims colored lights, instead simply thresholding off bits of darkness that are dimmer then some level. /🆑 * modular edits * see_in_dark * [MIRROR] Adds a unit test to detect double stacked lights [MDB IGNORE] (#19564) * Adds a unit test to detect double stacked lights * we really need to get that night vision pr done * lints fixes --------- Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Co-authored-by: Paxilmaniac <paxilmaniac@gmail.com> * Update augments_eyes.dm * Update augments_eyes.dm * eeee --------- Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Co-authored-by: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Co-authored-by: Paxilmaniac <paxilmaniac@gmail.com> Co-authored-by: Gandalf <9026500+Gandalf2k15@users.noreply.github.com>
473 lines
24 KiB
Plaintext
473 lines
24 KiB
Plaintext
// Admin Tab - Game Verbs
|
|
|
|
/datum/admins/proc/show_player_panel(mob/M in GLOB.mob_list)
|
|
set category = "Admin.Game"
|
|
set name = "Show Player Panel"
|
|
set desc="Edit player (respawn, ban, heal, etc)"
|
|
|
|
if(!check_rights())
|
|
return
|
|
|
|
log_admin("[key_name(usr)] checked the individual player panel for [key_name(M)][isobserver(usr)?"":" while in game"].")
|
|
|
|
if(!M)
|
|
to_chat(usr, span_warning("You seem to be selecting a mob that doesn't exist anymore."), confidential = TRUE)
|
|
return
|
|
|
|
var/body = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Options for [M.key]</title></head>"
|
|
body += "<body>Options panel for <b>[M]</b>"
|
|
if(M.client)
|
|
body += " played by <b>[M.client]</b> "
|
|
body += "\[<A href='?_src_=holder;[HrefToken()];editrights=[(GLOB.admin_datums[M.client.ckey] || GLOB.deadmins[M.client.ckey]) ? "rank" : "add"];key=[M.key]'>[M.client.holder ? M.client.holder.rank_names() : "Player"]</A>\]"
|
|
if(CONFIG_GET(flag/use_exp_tracking))
|
|
body += "\[<A href='?_src_=holder;[HrefToken()];getplaytimewindow=[REF(M)]'>" + M.client.get_exp_living(FALSE) + "</a>\]"
|
|
|
|
if(isnewplayer(M))
|
|
body += " <B>Hasn't Entered Game</B> "
|
|
else
|
|
body += " \[<A href='?_src_=holder;[HrefToken()];revive=[REF(M)]'>Heal</A>\] "
|
|
|
|
if(M.ckey)
|
|
body += "<br>\[<A href='?_src_=holder;[HrefToken()];ppbyckey=[M.ckey];ppbyckeyorigmob=[REF(M)]'>Find Updated Panel</A>\]"
|
|
|
|
if(M.client)
|
|
body += "<br>\[<b>First Seen:</b> [M.client.player_join_date]\]\[<b>Byond account registered on:</b> [M.client.account_join_date]\]"
|
|
body += "<br><br><b>CentCom Galactic Ban DB: </b> "
|
|
if(CONFIG_GET(string/centcom_ban_db))
|
|
body += "<a href='?_src_=holder;[HrefToken()];centcomlookup=[M.client.ckey]'>Search</a>"
|
|
else
|
|
body += "<i>Disabled</i>"
|
|
body += "<br><br><b>Show related accounts by:</b> "
|
|
body += "\[ <a href='?_src_=holder;[HrefToken()];showrelatedacc=cid;client=[REF(M.client)]'>CID</a> | "
|
|
body += "<a href='?_src_=holder;[HrefToken()];showrelatedacc=ip;client=[REF(M.client)]'>IP</a> \]"
|
|
var/full_version = "Unknown"
|
|
if(M.client.byond_version)
|
|
full_version = "[M.client.byond_version].[M.client.byond_build ? M.client.byond_build : "xxx"]"
|
|
body += "<br>\[<b>Byond version:</b> [full_version]\]<br>"
|
|
|
|
|
|
body += "<br><br>\[ "
|
|
body += "<a href='?_src_=vars;[HrefToken()];Vars=[REF(M)]'>VV</a> - "
|
|
if(M.mind)
|
|
body += "<a href='?_src_=holder;[HrefToken()];traitor=[REF(M)]'>TP</a> - "
|
|
body += "<a href='?_src_=holder;[HrefToken()];skill=[REF(M)]'>SKILLS</a> - "
|
|
else
|
|
body += "<a href='?_src_=holder;[HrefToken()];initmind=[REF(M)]'>Init Mind</a> - "
|
|
if (iscyborg(M))
|
|
body += "<a href='?_src_=holder;[HrefToken()];borgpanel=[REF(M)]'>BP</a> - "
|
|
body += "<a href='?priv_msg=[M.ckey]'>PM</a> - "
|
|
body += "<a href='?_src_=holder;[HrefToken()];subtlemessage=[REF(M)]'>SM</a> - "
|
|
if (ishuman(M) && M.mind)
|
|
body += "<a href='?_src_=holder;[HrefToken()];HeadsetMessage=[REF(M)]'>HM</a> - "
|
|
body += "<a href='?_src_=holder;[HrefToken()];adminplayerobservefollow=[REF(M)]'>FLW</a> - "
|
|
//Default to client logs if available
|
|
var/source = LOGSRC_MOB
|
|
if(M.ckey)
|
|
source = LOGSRC_CKEY
|
|
body += "<a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_src=[source]'>LOGS</a>\] <br>"
|
|
|
|
body += "<b>Mob type</b> = [M.type]<br><br>"
|
|
|
|
body += "<A href='?_src_=holder;[HrefToken()];boot2=[REF(M)]'>Kick</A> | "
|
|
if(M.client)
|
|
body += "<A href='?_src_=holder;[HrefToken()];newbankey=[M.key];newbanip=[M.client.address];newbancid=[M.client.computer_id]'>Ban</A> | "
|
|
else
|
|
body += "<A href='?_src_=holder;[HrefToken()];newbankey=[M.key]'>Ban</A> | "
|
|
|
|
body += "<A href='?_src_=holder;[HrefToken()];showmessageckey=[M.ckey]'>Notes | Messages | Watchlist</A> | "
|
|
if(M.client)
|
|
body += "| <A href='?_src_=holder;[HrefToken()];sendtoprison=[REF(M)]'>Prison</A> | "
|
|
body += "\ <A href='?_src_=holder;[HrefToken()];sendbacktolobby=[REF(M)]'>Send back to Lobby</A> | "
|
|
var/muted = M.client.prefs.muted
|
|
body += "<br><b>Mute: </b> "
|
|
body += "\[<A href='?_src_=holder;[HrefToken()];mute=[M.ckey];mute_type=[MUTE_IC]'><font color='[(muted & MUTE_IC)?"red":"blue"]'>IC</font></a> | "
|
|
body += "<A href='?_src_=holder;[HrefToken()];mute=[M.ckey];mute_type=[MUTE_OOC]'><font color='[(muted & MUTE_OOC)?"red":"blue"]'>OOC</font></a> | "
|
|
body += "<A href='?_src_=holder;[HrefToken()];mute=[M.ckey];mute_type=[MUTE_PRAY]'><font color='[(muted & MUTE_PRAY)?"red":"blue"]'>PRAY</font></a> | "
|
|
body += "<A href='?_src_=holder;[HrefToken()];mute=[M.ckey];mute_type=[MUTE_ADMINHELP]'><font color='[(muted & MUTE_ADMINHELP)?"red":"blue"]'>ADMINHELP</font></a> | "
|
|
//Skyrat Addition Begin - LOOC muting again.
|
|
body += "<A href='?_src_=holder;[HrefToken()];mute=[M.ckey];mute_type=[MUTE_DEADCHAT]'><font color='[(muted & MUTE_DEADCHAT)?"red":"blue"]'>DEADCHAT</font></a> | "
|
|
body += "<A href='?_src_=holder;[HrefToken()];mute=[M.ckey];mute_type=[MUTE_LOOC]'><font color='[(muted & MUTE_LOOC)?"red":"blue"]'>LOOC</font></a>\]"
|
|
//Skyrat Addition End - LOOC muting again.
|
|
body += "(<A href='?_src_=holder;[HrefToken()];mute=[M.ckey];mute_type=[MUTE_ALL]'><font color='[(muted & MUTE_ALL)?"red":"blue"]'>toggle all</font></a>)"
|
|
|
|
body += "<br><br>"
|
|
body += "<A href='?_src_=holder;[HrefToken()];jumpto=[REF(M)]'><b>Jump to</b></A> | "
|
|
body += "<A href='?_src_=holder;[HrefToken()];getmob=[REF(M)]'>Get</A> | "
|
|
body += "<A href='?_src_=holder;[HrefToken()];sendmob=[REF(M)]'>Send To</A>"
|
|
|
|
body += "<br><br>"
|
|
body += "<A href='?_src_=holder;[HrefToken()];traitor=[REF(M)]'>Traitor panel</A> | "
|
|
body += "<A href='?_src_=holder;[HrefToken()];narrateto=[REF(M)]'>Narrate to</A> | "
|
|
body += "<A href='?_src_=holder;[HrefToken()];subtlemessage=[REF(M)]'>Subtle message</A> | "
|
|
body += "<A href='?_src_=holder;[HrefToken()];playsoundto=[REF(M)]'>Play sound to</A> | "
|
|
body += "<A href='?_src_=holder;[HrefToken()];languagemenu=[REF(M)]'>Language Menu</A>"
|
|
|
|
if(M.client)
|
|
if(!isnewplayer(M))
|
|
body += "<br><br>"
|
|
body += "<b>Transformation:</b><br>"
|
|
if(isobserver(M))
|
|
body += "<b>Ghost</b> | "
|
|
else
|
|
body += "<A href='?_src_=holder;[HrefToken()];simplemake=observer;mob=[REF(M)]'>Make Ghost</A> | "
|
|
|
|
if(ishuman(M) && !ismonkey(M))
|
|
body += "<b>Human</b> | "
|
|
else
|
|
body += "<A href='?_src_=holder;[HrefToken()];simplemake=human;mob=[REF(M)]'>Make Human</A> | "
|
|
|
|
if(ismonkey(M))
|
|
body += "<b>Monkey</b> | "
|
|
else
|
|
body += "<A href='?_src_=holder;[HrefToken()];simplemake=monkey;mob=[REF(M)]'>Make Monkey</A> | "
|
|
|
|
if(iscyborg(M))
|
|
body += "<b>Cyborg</b> | "
|
|
else
|
|
body += "<A href='?_src_=holder;[HrefToken()];simplemake=robot;mob=[REF(M)]'>Make Cyborg</A> | "
|
|
|
|
if(isAI(M))
|
|
body += "<b>AI</b>"
|
|
else
|
|
body += "<A href='?_src_=holder;[HrefToken()];makeai=[REF(M)]'>Make AI</A>"
|
|
|
|
body += "<br><br>"
|
|
body += "<b>Other actions:</b>"
|
|
body += "<br>"
|
|
if(!isnewplayer(M))
|
|
body += "<A href='?_src_=holder;[HrefToken()];forcespeech=[REF(M)]'>Forcesay</A> | "
|
|
body += "<A href='?_src_=holder;[HrefToken()];applyquirks=[REF(M)]'>Apply Client Quirks</A> | "
|
|
body += "<A href='?_src_=holder;[HrefToken()];tdome1=[REF(M)]'>Thunderdome 1</A> | "
|
|
body += "<A href='?_src_=holder;[HrefToken()];tdome2=[REF(M)]'>Thunderdome 2</A> | "
|
|
body += "<A href='?_src_=holder;[HrefToken()];tdomeadmin=[REF(M)]'>Thunderdome Admin</A> | "
|
|
body += "<A href='?_src_=holder;[HrefToken()];tdomeobserve=[REF(M)]'>Thunderdome Observer</A> | "
|
|
body += "<A href='?_src_=holder;[HrefToken()];admincommend=[REF(M)]'>Commend Behavior</A> | "
|
|
|
|
body += "<br>"
|
|
body += "</body></html>"
|
|
|
|
usr << browse(body, "window=adminplayeropts-[REF(M)];size=550x515")
|
|
SSblackbox.record_feedback("tally", "admin_verb", 1, "Player Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
|
|
|
/client/proc/cmd_admin_godmode(mob/M in GLOB.mob_list)
|
|
set category = "Admin.Game"
|
|
set name = "Godmode"
|
|
if(!check_rights(R_ADMIN))
|
|
return
|
|
|
|
M.status_flags ^= GODMODE
|
|
to_chat(usr, span_adminnotice("Toggled [(M.status_flags & GODMODE) ? "ON" : "OFF"]"), confidential = TRUE)
|
|
|
|
log_admin("[key_name(usr)] has toggled [key_name(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]")
|
|
var/msg = "[key_name_admin(usr)] has toggled [ADMIN_LOOKUPFLW(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]"
|
|
message_admins(msg)
|
|
admin_ticket_log(M, msg)
|
|
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Godmode", "[M.status_flags & GODMODE ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
|
|
|
/*
|
|
If a guy was gibbed and you want to revive him, this is a good way to do so.
|
|
Works kind of like entering the game with a new character. Character receives a new mind if they didn't have one.
|
|
Traitors and the like can also be revived with the previous role mostly intact.
|
|
/N */
|
|
/client/proc/respawn_character()
|
|
set category = "Admin.Game"
|
|
set name = "Respawn Character"
|
|
set desc = "Respawn a person that has been gibbed/dusted/killed. They must be a ghost for this to work and preferably should not have a body to go back into."
|
|
if(!check_rights(R_ADMIN))
|
|
return
|
|
|
|
var/input = ckey(input(src, "Please specify which key will be respawned.", "Key", ""))
|
|
if(!input)
|
|
return
|
|
|
|
var/mob/dead/observer/G_found
|
|
for(var/mob/dead/observer/G in GLOB.player_list)
|
|
if(G.ckey == input)
|
|
G_found = G
|
|
break
|
|
|
|
if(!G_found)//If a ghost was not found.
|
|
to_chat(usr, "<font color='red'>There is no active key like that in the game or the person is not currently a ghost.</font>", confidential = TRUE)
|
|
return
|
|
|
|
if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something
|
|
//check if they were a monkey
|
|
if(findtext(G_found.real_name,"monkey"))
|
|
if(tgui_alert(usr,"This character appears to have been a monkey. Would you like to respawn them as such?",,list("Yes","No")) == "Yes")
|
|
var/mob/living/carbon/human/species/monkey/new_monkey = new
|
|
SSjob.SendToLateJoin(new_monkey)
|
|
G_found.mind.transfer_to(new_monkey) //be careful when doing stuff like this! I've already checked the mind isn't in use
|
|
new_monkey.key = G_found.key
|
|
to_chat(new_monkey, "You have been fully respawned. Enjoy the game.", confidential = TRUE)
|
|
var/msg = span_adminnotice("[key_name_admin(usr)] has respawned [new_monkey.key] as a filthy monkey.")
|
|
message_admins(msg)
|
|
admin_ticket_log(new_monkey, msg)
|
|
return //all done. The ghost is auto-deleted
|
|
|
|
|
|
//Ok, it's not a monkey. So, spawn a human.
|
|
var/mob/living/carbon/human/new_character = new//The mob being spawned.
|
|
SSjob.SendToLateJoin(new_character)
|
|
|
|
var/datum/record/locked/record_found //Referenced to later to either randomize or not randomize the character.
|
|
if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something
|
|
record_found = find_record(G_found.name, locked_only = TRUE)
|
|
|
|
if(record_found)//If they have a record we can determine a few things.
|
|
new_character.real_name = record_found.name
|
|
new_character.gender = record_found.gender
|
|
new_character.age = record_found.age
|
|
var/datum/dna/found_dna = record_found.dna_ref
|
|
new_character.hardset_dna(found_dna.unique_identity, record_found.dna_string, null, record_found.name, record_found.blood_type, new record_found.species, found_dna.features)
|
|
else
|
|
new_character.randomize_human_appearance()
|
|
new_character.dna.update_dna_identity()
|
|
|
|
new_character.name = new_character.real_name
|
|
|
|
if(G_found.mind && !G_found.mind.active)
|
|
G_found.mind.transfer_to(new_character) //be careful when doing stuff like this! I've already checked the mind isn't in use
|
|
else
|
|
new_character.mind_initialize()
|
|
if(is_unassigned_job(new_character.mind.assigned_role))
|
|
new_character.mind.set_assigned_role(SSjob.GetJobType(SSjob.overflow_role))
|
|
|
|
new_character.key = G_found.key
|
|
|
|
/*
|
|
The code below functions with the assumption that the mob is already a traitor if they have a special role.
|
|
So all it does is re-equip the mob with powers and/or items. Or not, if they have no special role.
|
|
If they don't have a mind, they obviously don't have a special role.
|
|
*/
|
|
|
|
//Two variables to properly announce later on.
|
|
var/admin = key_name_admin(src)
|
|
var/player_key = G_found.key
|
|
|
|
//Now for special roles and equipment.
|
|
var/datum/antagonist/traitor/traitordatum = new_character.mind.has_antag_datum(/datum/antagonist/traitor)
|
|
if(traitordatum)
|
|
SSjob.EquipRank(new_character, new_character.mind.assigned_role, new_character.client)
|
|
new_character.mind.give_uplink(silent = TRUE, antag_datum = traitordatum)
|
|
|
|
switch(new_character.mind.special_role)
|
|
if(ROLE_WIZARD)
|
|
new_character.forceMove(pick(GLOB.wizardstart))
|
|
var/datum/antagonist/wizard/A = new_character.mind.has_antag_datum(/datum/antagonist/wizard,TRUE)
|
|
A.equip_wizard()
|
|
if(ROLE_SYNDICATE)
|
|
new_character.forceMove(pick(GLOB.nukeop_start))
|
|
var/datum/antagonist/nukeop/N = new_character.mind.has_antag_datum(/datum/antagonist/nukeop,TRUE)
|
|
N.equip_op()
|
|
if(ROLE_NINJA)
|
|
var/list/ninja_spawn = list()
|
|
for(var/obj/effect/landmark/carpspawn/L in GLOB.landmarks_list)
|
|
ninja_spawn += L
|
|
var/datum/antagonist/ninja/ninjadatum = new_character.mind.has_antag_datum(/datum/antagonist/ninja)
|
|
ninjadatum.equip_space_ninja()
|
|
if(ninja_spawn.len)
|
|
new_character.forceMove(pick(ninja_spawn))
|
|
|
|
else//They may also be a cyborg or AI.
|
|
switch(new_character.mind.assigned_role.type)
|
|
if(/datum/job/cyborg)//More rigging to make em' work and check if they're traitor.
|
|
new_character = new_character.Robotize(TRUE)
|
|
if(/datum/job/ai)
|
|
new_character = new_character.AIize()
|
|
else
|
|
if(!traitordatum) // Already equipped there.
|
|
SSjob.EquipRank(new_character, new_character.mind.assigned_role, new_character.client)//Or we simply equip them.
|
|
|
|
//Announces the character on all the systems, based on the record.
|
|
if(!record_found && (new_character.mind.assigned_role.job_flags & JOB_CREW_MEMBER))
|
|
//Power to the user!
|
|
if(tgui_alert(new_character,"Warning: No data core entry detected. Would you like to announce the arrival of this character by adding them to various databases, such as medical records?",,list("No","Yes")) == "Yes")
|
|
GLOB.manifest.inject(new_character, src) // SKYRAT EDIT CHANGE - ALTERNATIVE_JOB_TITLES - Original: GLOB.manifest.inject(new_character)
|
|
|
|
if(tgui_alert(new_character,"Would you like an active AI to announce this character?",,list("No","Yes")) == "Yes")
|
|
announce_arrival(new_character, new_character.mind.assigned_role.title)
|
|
|
|
var/msg = span_adminnotice("[admin] has respawned [player_key] as [new_character.real_name].")
|
|
message_admins(msg)
|
|
admin_ticket_log(new_character, msg)
|
|
|
|
to_chat(new_character, "You have been fully respawned. Enjoy the game.", confidential = TRUE)
|
|
|
|
SSblackbox.record_feedback("tally", "admin_verb", 1, "Respawn Character") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
|
return new_character
|
|
|
|
/client/proc/cmd_admin_list_open_jobs()
|
|
set category = "Admin.Game"
|
|
set name = "Manage Job Slots"
|
|
|
|
if(!check_rights(R_ADMIN))
|
|
return
|
|
holder.manage_free_slots()
|
|
SSblackbox.record_feedback("tally", "admin_verb", 1, "Manage Job Slots") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
|
|
|
/datum/admins/proc/manage_free_slots()
|
|
if(!check_rights())
|
|
return
|
|
var/datum/browser/browser = new(usr, "jobmanagement", "Manage Free Slots", 520)
|
|
var/list/dat = list()
|
|
var/count = 0
|
|
|
|
if(!SSjob.initialized)
|
|
tgui_alert(usr, "You cannot manage jobs before the job subsystem is initialized!")
|
|
return
|
|
|
|
if(SSlag_switch.measures[DISABLE_NON_OBSJOBS])
|
|
dat += "<div class='notice red' style='font-size: 125%'>Lag Switch \"Disable non-observer late joining\" is ON. Only Observers may join!</div>"
|
|
|
|
dat += "<table>"
|
|
|
|
for(var/datum/job/job as anything in SSjob.joinable_occupations)
|
|
count++
|
|
var/J_title = html_encode(job.title)
|
|
var/J_opPos = html_encode(job.total_positions - (job.total_positions - job.current_positions))
|
|
var/J_totPos = html_encode(job.total_positions)
|
|
dat += "<tr><td>[J_title]:</td> <td>[J_opPos]/[job.total_positions < 0 ? " (unlimited)" : J_totPos]"
|
|
|
|
dat += "</td>"
|
|
dat += "<td>"
|
|
if(job.total_positions >= 0)
|
|
dat += "<A href='?src=[REF(src)];[HrefToken()];customjobslot=[job.title]'>Custom</A> | "
|
|
dat += "<A href='?src=[REF(src)];[HrefToken()];addjobslot=[job.title]'>Add 1</A> | "
|
|
if(job.total_positions > job.current_positions)
|
|
dat += "<A href='?src=[REF(src)];[HrefToken()];removejobslot=[job.title]'>Remove</A> | "
|
|
else
|
|
dat += "Remove | "
|
|
dat += "<A href='?src=[REF(src)];[HrefToken()];unlimitjobslot=[job.title]'>Unlimit</A></td>"
|
|
else
|
|
dat += "<A href='?src=[REF(src)];[HrefToken()];limitjobslot=[job.title]'>Limit</A></td>"
|
|
|
|
browser.height = min(100 + count * 20, 650)
|
|
browser.set_content(dat.Join())
|
|
browser.open()
|
|
|
|
/client/proc/toggle_view_range()
|
|
set category = "Admin.Game"
|
|
set name = "Change View Range"
|
|
set desc = "switches between 1x and custom views"
|
|
|
|
if(view_size.getView() == view_size.default)
|
|
view_size.setTo(input("Select view range:", "FUCK YE", 7) in list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,37) - 7)
|
|
else
|
|
view_size.resetToDefault(getScreenSize(prefs.read_preference(/datum/preference/toggle/widescreen)))
|
|
|
|
log_admin("[key_name(usr)] changed their view range to [view].")
|
|
//message_admins("\blue [key_name_admin(usr)] changed their view range to [view].") //why? removed by order of XSI
|
|
|
|
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Change View Range", "[view]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
|
|
|
/client/proc/toggle_combo_hud()
|
|
set category = "Admin.Game"
|
|
set name = "Toggle Combo HUD"
|
|
set desc = "Toggles the Admin Combo HUD (antag, sci, med, eng)"
|
|
|
|
if(!check_rights(R_ADMIN))
|
|
return
|
|
|
|
if (combo_hud_enabled)
|
|
disable_combo_hud()
|
|
else
|
|
enable_combo_hud()
|
|
|
|
to_chat(usr, "You toggled your admin combo HUD [combo_hud_enabled ? "ON" : "OFF"].", confidential = TRUE)
|
|
message_admins("[key_name_admin(usr)] toggled their admin combo HUD [combo_hud_enabled ? "ON" : "OFF"].")
|
|
log_admin("[key_name(usr)] toggled their admin combo HUD [combo_hud_enabled ? "ON" : "OFF"].")
|
|
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Combo HUD", "[combo_hud_enabled ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
|
|
|
/client/proc/enable_combo_hud()
|
|
if (combo_hud_enabled)
|
|
return
|
|
|
|
combo_hud_enabled = TRUE
|
|
|
|
for (var/hudtype in list(DATA_HUD_SECURITY_ADVANCED, DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC_ADVANCED))
|
|
var/datum/atom_hud/atom_hud = GLOB.huds[hudtype]
|
|
atom_hud.show_to(mob)
|
|
|
|
for (var/datum/atom_hud/alternate_appearance/basic/antagonist_hud/antag_hud in GLOB.active_alternate_appearances)
|
|
antag_hud.show_to(mob)
|
|
|
|
mob.lighting_cutoff = mob.default_lighting_cutoff()
|
|
mob.update_sight()
|
|
|
|
/client/proc/disable_combo_hud()
|
|
if (!combo_hud_enabled)
|
|
return
|
|
|
|
combo_hud_enabled = FALSE
|
|
|
|
for (var/hudtype in list(DATA_HUD_SECURITY_ADVANCED, DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC_ADVANCED))
|
|
var/datum/atom_hud/atom_hud = GLOB.huds[hudtype]
|
|
atom_hud.hide_from(mob)
|
|
|
|
for (var/datum/atom_hud/alternate_appearance/basic/antagonist_hud/antag_hud in GLOB.active_alternate_appearances)
|
|
antag_hud.hide_from(mob)
|
|
|
|
mob.lighting_cutoff = mob.default_lighting_cutoff()
|
|
mob.update_sight()
|
|
|
|
/datum/admins/proc/show_traitor_panel(mob/target_mob in GLOB.mob_list)
|
|
set category = "Admin.Game"
|
|
set desc = "Edit mobs's memory and role"
|
|
set name = "Show Traitor Panel"
|
|
var/datum/mind/target_mind = target_mob.mind
|
|
if(!target_mind)
|
|
to_chat(usr, "This mob has no mind!", confidential = TRUE)
|
|
return
|
|
if(!istype(target_mob) && !istype(target_mind))
|
|
to_chat(usr, "This can only be used on instances of type /mob and /mind", confidential = TRUE)
|
|
return
|
|
target_mind.traitor_panel()
|
|
SSblackbox.record_feedback("tally", "admin_verb", 1, "Traitor Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
|
|
|
/datum/admins/proc/show_skill_panel(target)
|
|
set category = "Admin.Game"
|
|
set desc = "Edit mobs's experience and skill levels"
|
|
set name = "Show Skill Panel"
|
|
var/datum/mind/target_mind
|
|
if(ismob(target))
|
|
var/mob/target_mob = target
|
|
target_mind = target_mob.mind
|
|
else if (istype(target, /datum/mind))
|
|
target_mind = target
|
|
else
|
|
to_chat(usr, "This can only be used on instances of type /mob and /mind", confidential = TRUE)
|
|
return
|
|
var/datum/skill_panel/SP = new(usr, target_mind)
|
|
SP.ui_interact(usr)
|
|
|
|
/datum/admins/proc/show_lag_switch_panel()
|
|
set category = "Admin.Game"
|
|
set name = "Show Lag Switches"
|
|
set desc="Display the controls for drastic lag mitigation measures."
|
|
|
|
if(!SSlag_switch.initialized)
|
|
to_chat(usr, span_notice("The Lag Switch subsystem has not yet been initialized."))
|
|
return
|
|
if(!check_rights())
|
|
return
|
|
|
|
var/list/dat = list("<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Lag Switches</title></head><body><h2><B>Lag (Reduction) Switches</B></h2>")
|
|
dat += "Automatic Trigger: <a href='?_src_=holder;[HrefToken()];change_lag_switch_option=TOGGLE_AUTO'><b>[SSlag_switch.auto_switch ? "On" : "Off"]</b></a><br/>"
|
|
dat += "Population Threshold: <a href='?_src_=holder;[HrefToken()];change_lag_switch_option=NUM'><b>[SSlag_switch.trigger_pop]</b></a><br/>"
|
|
dat += "Slowmode Cooldown (toggle On/Off below): <a href='?_src_=holder;[HrefToken()];change_lag_switch_option=SLOWCOOL'><b>[SSlag_switch.slowmode_cooldown/10] seconds</b></a><br/>"
|
|
dat += "<br/><b>SET ALL MEASURES: <a href='?_src_=holder;[HrefToken()];change_lag_switch=ALL_ON'>ON</a> | <a href='?_src_=holder;[HrefToken()];change_lag_switch=ALL_OFF'>OFF</a></b><br/>"
|
|
dat += "<br/>Disable ghosts zoom and t-ray verbs (except staff): <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_GHOST_ZOOM_TRAY]'><b>[SSlag_switch.measures[DISABLE_GHOST_ZOOM_TRAY] ? "On" : "Off"]</b></a><br/>"
|
|
dat += "Disable late joining: <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_NON_OBSJOBS]'><b>[SSlag_switch.measures[DISABLE_NON_OBSJOBS] ? "On" : "Off"]</b></a><br/>"
|
|
dat += "<br/>============! MAD GHOSTS ZONE !============<br/>"
|
|
dat += "Disable deadmob keyLoop (except staff, informs dchat): <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_DEAD_KEYLOOP]'><b>[SSlag_switch.measures[DISABLE_DEAD_KEYLOOP] ? "On" : "Off"]</b></a><br/>"
|
|
dat += "==========================================<br/>"
|
|
dat += "<br/><b>Measures below can be bypassed with a <abbr title='TRAIT_BYPASS_MEASURES'><u>special trait</u></abbr></b><br/>"
|
|
dat += "Slowmode say verb (informs world): <a href='?_src_=holder;[HrefToken()];change_lag_switch=[SLOWMODE_SAY]'><b>[SSlag_switch.measures[SLOWMODE_SAY] ? "On" : "Off"]</b></a><br/>"
|
|
dat += "Disable runechat: <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_RUNECHAT]'><b>[SSlag_switch.measures[DISABLE_RUNECHAT] ? "On" : "Off"]</b></a> - <span style='font-size:80%'>trait applies to speaker</span><br/>"
|
|
dat += "Disable examine icons: <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_USR_ICON2HTML]'><b>[SSlag_switch.measures[DISABLE_USR_ICON2HTML] ? "On" : "Off"]</b></a> - <span style='font-size:80%'>trait applies to examiner</span><br/>"
|
|
dat += "Disable parallax: <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_PARALLAX]'><b>[SSlag_switch.measures[DISABLE_PARALLAX] ? "On" : "Off"]</b></a> - <span style='font-size:80%'>trait applies to character</span><br />"
|
|
dat += "Disable footsteps: <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_FOOTSTEPS]'><b>[SSlag_switch.measures[DISABLE_FOOTSTEPS] ? "On" : "Off"]</b></a> - <span style='font-size:80%'>trait applies to character</span><br />"
|
|
dat += "Disable character creator: <a href='?_src_=holder;[HrefToken()];change_lag_switch=[DISABLE_CREATOR]'><b>[SSlag_switch.measures[DISABLE_CREATOR] ? "On" : "Off"]</b></a> - <span style='font-size:80%'>trait applies to all</span><br />" // SKRYAT EDIT ADDITION
|
|
dat += "</body></html>"
|
|
usr << browse(dat.Join(), "window=lag_switch_panel;size=420x480")
|