From f60b0e610b88d38c7225be23d26864df48db748a Mon Sep 17 00:00:00 2001 From: ZomgPonies Date: Sat, 30 Nov 2013 06:39:04 -0500 Subject: [PATCH] AntagHUD for admins and ghosts (who cannot respawn) --- code/controllers/configuration.dm | 9 +- code/modules/admin/admin_verbs.dm | 4 +- code/modules/admin/topic.dm | 7 +- code/modules/admin/verbs/randomverbs.dm | 83 ++++++++++ code/modules/mob/dead/observer/observer.dm | 153 ++++++++++++++++++ .../mob/living/carbon/brain/posibrain.dm | 4 +- .../modules/mob/living/silicon/pai/recruit.dm | 4 +- code/modules/mob/mob_defines.dm | 2 + code/modules/mob/new_player/new_player.dm | 2 + icons/mob/hud.dmi | Bin 3320 -> 2965 bytes 10 files changed, 263 insertions(+), 5 deletions(-) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index c6b0edcff49..200dccdfe7a 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -43,7 +43,8 @@ var/Tickcomp = 0 var/socket_talk = 0 // use socket_talk to communicate with other processes var/list/resource_urls = null - + var/antag_hud_allowed = 0 // Ghosts can turn on Antagovision to see a HUD of who is the bad guys this round. + var/antag_hud_restricted = 0 // Ghosts that turn on Antagovision cannot rejoin the round. var/list/mode_names = list() var/list/modes = list() // allowed modes var/list/votable_modes = list() // votable modes @@ -384,6 +385,12 @@ if("tickcomp") Tickcomp = 1 + if("allow_antag_hud") + config.antag_hud_allowed = 1 + + if("antag_hud_restricted") + config.antag_hud_restricted = 1 + if("humans_need_surnames") humans_need_surnames = 1 diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 423c322d28b..11b62690948 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -120,7 +120,9 @@ var/list/admin_verbs_server = list( /datum/admins/proc/toggle_space_ninja, /client/proc/toggle_random_events, /client/proc/check_customitem_activity, - /client/proc/delbook + /client/proc/delbook, + /client/proc/toggle_antagHUD_use, + /client/proc/toggle_antagHUD_restrictions ) var/list/admin_verbs_debug = list( /client/proc/cmd_admin_list_open_jobs, diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 776ac5c9db2..6b3957657f8 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -568,7 +568,7 @@ //Non-Human (Green) counter = 0 jobs += "" - jobs += "" + jobs += "" for(var/jobPos in nonhuman_positions) if(!jobPos) continue var/datum/job/job = job_master.GetJob(jobPos) @@ -591,6 +591,11 @@ else jobs += "" + if(jobban_isbanned(M, "AntagHUD")) + jobs += "" + else + jobs += "" + jobs += "
Non-human Positions
Non-human Positions
pAIAntagHUDAntagHUD
" //Antagonist (Orange) diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 2777dbeeea8..9439f3e36f9 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -408,6 +408,89 @@ Traitors and the like can also be revived with the previous role mostly intact. feedback_add_details("admin_verb","RSPCH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return new_character +/client/proc/get_ghosts(var/notify = 0,var/what = 2) + // what = 1, return ghosts ass list. + // what = 2, return mob list + + var/list/mobs = list() + var/list/ghosts = list() + var/list/sortmob = sortAtom(mob_list) // get the mob list. + /var/any=0 + for(var/mob/dead/observer/M in sortmob) + mobs.Add(M) //filter it where it's only ghosts + any = 1 //if no ghosts show up, any will just be 0 + if(!any) + if(notify) + src << "There doesn't appear to be any ghosts for you to select." + return + + for(var/mob/M in mobs) + var/name = M.name + ghosts[name] = M //get the name of the mob for the popup list + if(what==1) + return ghosts + else + return mobs + +/client/proc/toggle_antagHUD_use() + set category = "Server" + set name = "Toggle antagHUD usage" + set desc = "Toggles antagHUD usage for observers" + + if(!holder) + src << "Only administrators may use this command." + var/action="" + if(config.antag_hud_allowed) + for(var/mob/dead/observer/g in get_ghosts()) + if(!g.client.holder) //Remove the verb from non-admin ghosts + g.verbs -= /mob/dead/observer/verb/toggle_antagHUD + if(g.antagHUD) + g.antagHUD = 0 // Disable it on those that have it enabled + g.has_enabled_antagHUD = 2 // We'll allow them to respawn + g << "\red The Administrator has disabled AntagHUD " + config.antag_hud_allowed = 0 + src << "\red AntagHUD usage has been disabled" + action = "disabled" + else + for(var/mob/dead/observer/g in get_ghosts()) + if(!g.client.holder) // Add the verb back for all non-admin ghosts + g.verbs += /mob/dead/observer/verb/toggle_antagHUD + g << "\blue The Administrator has enabled AntagHUD " // Notify all observers they can now use AntagHUD + config.antag_hud_allowed = 1 + action = "enabled" + src << "\blue AntagHUD usage has been enabled" + + + log_admin("[key_name(usr)] has [action] antagHUD usage for observers") + message_admins("Admin [key_name_admin(usr)] has [action] antagHUD usage for observers", 1) + + +/client/proc/toggle_antagHUD_restrictions() + set category = "Server" + set name = "Toggle antagHUD Restrictions" + set desc = "Restricts players that have used antagHUD from being able to join this round." + if(!holder) + src << "Only administrators may use this command." + var/action="" + if(config.antag_hud_restricted) + for(var/mob/dead/observer/g in get_ghosts()) + g << "\blue The administrator has lifted restrictions on joining the round if you use AntagHUD" + action = "lifted restrictions" + config.antag_hud_restricted = 0 + src << "\blue AntagHUD restrictions have been lifted" + else + for(var/mob/dead/observer/g in get_ghosts()) + g << "\red The administrator has placed restrictions on joining the round if you use AntagHUD" + g << "\red Your AntagHUD has been disabled, you may choose to re-enabled it but will be under restrictions " + g.antagHUD = 0 + g.has_enabled_antagHUD = 0 + action = "placed restrictions" + config.antag_hud_restricted = 1 + src << "\red AntagHUD restrictions have been enabled" + + log_admin("[key_name(usr)] has [action] on joining the round if they use AntagHUD") + message_admins("Admin [key_name_admin(usr)] has [action] on joining the round if they use AntagHUD", 1) + /client/proc/cmd_admin_add_freeform_ai_law() set category = "Fun" set name = "Add Custom AI law" diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 095b0539b37..027b2c9082a 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -18,6 +18,7 @@ //Note that this is not a reliable way to determine if admins started as observers, since they change mobs a lot. universal_speak = 1 var/atom/movable/following = null + var/medHUD = 0 /mob/dead/observer/New(mob/body) sight |= SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF see_invisible = SEE_INVISIBLE_OBSERVER @@ -69,12 +70,123 @@ Transfer_mind is there to check if mob is being deleted/not going to have a body Works together with spawning an observer, noted above. */ + +/mob/dead/observer/Life() + ..() + if(!loc) return + if(!client) return 0 + + + if(client.images.len) + for(var/image/hud in client.images) + if(copytext(hud.icon_state,1,4) == "hud") + client.images.Remove(hud) + if(antagHUD) + var/list/target_list = list() + for(var/mob/living/target in oview(src)) + if( target.mind&&(target.mind.special_role||issilicon(target)) ) + target_list += target + if(target_list.len) + assess_targets(target_list, src) + if(medHUD) + process_medHUD(src) + + +// Direct copied from medical HUD glasses proc, used to determine what health bar to put over the targets head. +/mob/dead/proc/RoundHealth(var/health) + switch(health) + if(100 to INFINITY) + return "health100" + if(70 to 100) + return "health80" + if(50 to 70) + return "health60" + if(30 to 50) + return "health40" + if(18 to 30) + return "health25" + if(5 to 18) + return "health10" + if(1 to 5) + return "health1" + if(-99 to 0) + return "health0" + else + return "health-100" + return "0" + + +// Pretty much a direct copy of Medical HUD stuff, except will show ill if they are ill instead of also checking for known illnesses. + +/mob/dead/proc/process_medHUD(var/mob/M) + var/client/C = M.client + var/image/holder + for(var/mob/living/carbon/human/patient in oview(M)) + var/foundVirus = 0 + if(patient.virus2.len) + foundVirus = 1 + if(!C) return + holder = patient.hud_list[HEALTH_HUD] + if(patient.stat == 2) + holder.icon_state = "hudhealth-100" + else + holder.icon_state = "hud[RoundHealth(patient.health)]" + C.images += holder + + holder = patient.hud_list[STATUS_HUD] + if(patient.stat == 2) + holder.icon_state = "huddead" + else if(patient.status_flags & XENO_HOST) + holder.icon_state = "hudxeno" + else if(foundVirus) + holder.icon_state = "hudill" + else + holder.icon_state = "hudhealthy" + C.images += holder + + +/mob/dead/proc/assess_targets(list/target_list, mob/dead/observer/U) + var/icon/tempHud = 'icons/mob/hud.dmi' + for(var/mob/living/target in target_list) + if(iscarbon(target)) + switch(target.mind.special_role) + if("traitor","Syndicate") + U.client.images += image(tempHud,target,"hudsyndicate") + if("Revolutionary") + U.client.images += image(tempHud,target,"hudrevolutionary") + if("Head Revolutionary") + U.client.images += image(tempHud,target,"hudheadrevolutionary") + if("Cultist") + U.client.images += image(tempHud,target,"hudcultist") + if("Changeling") + U.client.images += image(tempHud,target,"hudchangeling") + if("Wizard","Fake Wizard") + U.client.images += image(tempHud,target,"hudwizard") + if("Hunter","Sentinel","Drone","Queen") + U.client.images += image(tempHud,target,"hudalien") + if("Death Commando") + U.client.images += image(tempHud,target,"huddeathsquad") + if("Ninja") + U.client.images += image(tempHud,target,"hudninja") + else//If we don't know what role they have but they have one. + U.client.images += image(tempHud,target,"hudunknown1") + else//If the silicon mob has no law datum, no inherent laws, or a law zero, add them to the hud. + var/mob/living/silicon/silicon_target = target + if(!silicon_target.laws||(silicon_target.laws&&(silicon_target.laws.zeroth||!silicon_target.laws.inherent.len))||silicon_target.mind.special_role=="traitor") + if(isrobot(silicon_target))//Different icons for robutts and AI. + U.client.images += image(tempHud,silicon_target,"hudmalborg") + else + U.client.images += image(tempHud,silicon_target,"hudmalai") + return 1 + /mob/proc/ghostize(var/can_reenter_corpse = 1) if(key) var/mob/dead/observer/ghost = new(src) //Transfer safety to observer spawning proc. ghost.can_reenter_corpse = can_reenter_corpse ghost.timeofdeath = src.timeofdeath //BS12 EDIT ghost.key = key + if(!ghost.client.holder && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed. + ghost.verbs -= /mob/dead/observer/verb/toggle_antagHUD // Poor guys, don't know what they are missing! return ghost /* @@ -164,6 +276,47 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp mind.current.key = key return 1 +/mob/dead/observer/verb/toggle_medHUD() + set category = "Ghost" + set name = "Toggle MedicHUD" + set desc = "Toggles Medical HUD allowing you to see how everyone is doing" + if(!client) + return + if(medHUD) + medHUD = 0 + src << "\blue Medical HUD Disabled" + else + medHUD = 1 + src << "\blue Medical HUD Enabled" + +/mob/dead/observer/verb/toggle_antagHUD() + set category = "Ghost" + set name = "Toggle AntagHUD" + set desc = "Toggles AntagHUD allowing you to see who is the antagonist" + if(!config.antag_hud_allowed && !client.holder) + src << "\red Admins have disabled this for this round." + return + if(!client) + return + var/mob/dead/observer/M = src + if(jobban_isbanned(M, "AntagHUD")) + src << "\red You have been banned from using this feature" + return + if(config.antag_hud_restricted && !M.has_enabled_antagHUD &&!client.holder) + var/response = alert(src, "If you turn this on, you will not be able to take any part in the round.","Are you sure you want to turn this feature on?","Yes","No") + if(response == "No") return + M.can_reenter_corpse = 0 + if(M in respawnable_list) + respawnable_list -= M + if(!M.has_enabled_antagHUD && !client.holder) + M.has_enabled_antagHUD = 1 + if(M.antagHUD) + M.antagHUD = 0 + src << "\blue AntagHUD Disabled" + else + M.antagHUD = 1 + src << "\blue AntagHUD Enabled" + /mob/dead/observer/proc/dead_tele() set category = "Ghost" set name = "Teleport" diff --git a/code/modules/mob/living/carbon/brain/posibrain.dm b/code/modules/mob/living/carbon/brain/posibrain.dm index f8c014278c2..d078a854ba8 100644 --- a/code/modules/mob/living/carbon/brain/posibrain.dm +++ b/code/modules/mob/living/carbon/brain/posibrain.dm @@ -26,7 +26,9 @@ spawn(600) reset_search() proc/request_player() - for(var/mob/dead/observer/O in player_list) + for(var/mob/O in respawnable_list) + if(O.has_enabled_antagHUD == 1 && config.antag_hud_restricted) + continue if(jobban_isbanned(O, "pAI")) continue if(O.client) diff --git a/code/modules/mob/living/silicon/pai/recruit.dm b/code/modules/mob/living/silicon/pai/recruit.dm index 3928ebb2080..05536c0d176 100644 --- a/code/modules/mob/living/silicon/pai/recruit.dm +++ b/code/modules/mob/living/silicon/pai/recruit.dm @@ -190,7 +190,9 @@ var/datum/paiController/paiController // Global handler for pAI candidates user << browse(dat, "window=findPai") proc/requestRecruits() - for(var/mob/dead/observer/O in player_list) + for(var/mob/O in respawnable_list) + if(O.has_enabled_antagHUD == 1 && config.antag_hud_restricted) + continue if(jobban_isbanned(O, "pAI")) continue if(asked.Find(O.key)) diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 8af29e57db9..ca463db09bc 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -160,6 +160,8 @@ var/move_on_shuttle = 1 // Can move on the shuttle. + var/has_enabled_antagHUD = 0 + var/antagHUD = 0 //Generic list for proc holders. Only way I can see to enable certain verbs/procs. Should be modified if needed. var/proc_holder_list[] = list()//Right now unused. //Also unlike the spell list, this would only store the object in contents, not an object in itself. diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index de86597e26a..29efa7398cd 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -133,6 +133,8 @@ client.prefs.real_name = random_name(client.prefs.gender) observer.real_name = client.prefs.real_name observer.name = observer.real_name + if(!client.holder && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed. + observer.verbs -= /mob/dead/observer/verb/toggle_antagHUD // Poor guys, don't know what they are missing! observer.key = key respawnable_list += observer del(src) diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi index 66aab85d81218b614d4283b311bd18bda52bec21..9853c59275590275b2cf394aacd1f5f52d26f0c8 100644 GIT binary patch delta 2673 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 Fe*kNY^V$Fa delta 3031 zcmZ9Mc|6mPAIFEy5!uMsmD)%dm0MJXxvkvy9XTc;5zF<-T*)<|Bq2vBk@$|>`pQvS zGdD9b_qjx7xqf~B`~G_W^?tnndOjcT*ZZ9#vLjZFWCP^oRXn_IV*_wWsgD&E(YB7B zAh%_mszWG@AB94xsi_$l7}z`cT)ler)~#DO9L~wf36D`TGBUb%@1E^_0XD~jD3deg z6}66zjvx#m{k zMZPXFuwgB_Mm*mCLM&p;8>_Tc-KzV+`0q};N17P3N2vY9QjSa4AX-yiGcw6S+6N6c z+fY$J)C~rpc_DIG>LSsdN+qxO+=02rMc)j}v%R*YhXS%OIRSSC&cJg$k9Mc{%o(KY zHwW51>u+E98ENCGuSlL1SQmMjddrvh?TEhw(CXYCwB`M2wEq-~-n>UDO$gTOGw!9;NJ1(}9@O_o(EH4}-yY}O*(^~F;D>M}ld z^+uOVu7cQTN=a^$nMBwSIr;AzejB;E=~ci({gn+e$@Lo=W6(#Bw}yC-np`?#x7<`O zbx*{Fl9M-TrwVAkstz_uE?w<%RW-by0;PR?x zSrrA6_UE&v*iQg0qr62d_$HfjD1q%k_Nlc^ydo7b}7P31AJmS-a!K_DJI6MeJ|E`ROWzWecRfuu5*U!2nxm^kYJyV&<>B4ff` z+3)+UB&Thqj*n z|Adh10g2YQ>?}w6`Wmm5o+-4|ZT*FG8nVBnR;ck1KaYBxZgS*T_Ri+YvuJYzh z@X75sx){UGYptKN*X(uK8^1#)hTk>4-dUqG+x;Cs!L58uzNZIY<*v?&f3KljPCxod zOqHU>ZQulWjCFLsy*o+!`5z_4v00k7%JR-p@*>?93U_Z@UT^l9($12SXH;3x)$Qz3 z9UVO)bJg9Bc3?yko1YsnWOmn+gn|RY6L3MAZHRDZW5Li`s|HzaryuxI8WfYbvuj0P z=kZQ@92FCY?&?fK+!rjj%2YO{0{cZ?#_ksLv}5C}Y_`&C@p^CQU=uN~J^m6+HT_p; z45#enb|eS47DwP4T&d)lBX&s+lP4S^Tu2rhETZ_sVoq6nRL-Lp@A7wj4Nqt@{7(MI zrBg}cSTokIIoR-X~x_sayl0(j&2TgUx`6CAJWH1ao2YRQG%J%h{?Jksz^Z>e zqt)6fKlwCl*SFNU_FzvlcqVT_9r~Uo(!VVf0Ax@ni+`F=9NhM&VMP$yXn<*;g{HH{ zf3o(^dhQxVuvu~Us{qIh5HWfL-4Oi~BIFKQgV=DkjzxLaGf&EQe5xOzWEx^>|Kf$*Ky#6) z3^q%|CXhrTer_)ML8`2L>h0FIy!`BPcD_s%EB5LAe!Qc3gM+v>Wz;HiTpzJBmU<-wu|(cuK~zgcU8 zgZ20#rzbFFWVwCHomZRyWXQ9c_QsfI05==5Qh?D_8}R)Z=8z)y6> z?Pg}SKDiai-xs_Uk3TmDog)y?_89xB?0CLgv>9~;hsOxr#8Jb(9+@5}B}*x69?-kl z%`-lI*}|zP!v&AG5Sl&Sqw>*HEOh?wuyW8Ex8BtXv@Dn+@PrJn<9v2;yY+1UKd`eD z*zrn)_slS%cGPj8hIt+}$|rZuf%!q50OW}niT-JRZDNc@5~_#7=UT0?a8xSMgSpLQ zUV=n^8wECZ6at>lNG*i;-ez{g4fL9{_f-{7-if4md@M9#hcB#VM(@?>P?!3koZRQ$ z_h7;Nr|)NPMc{Le6$eWHTH*aNbowbwLtL5fUAa*KKagHr2>mo7d!3Tify~hs#2Jer zCB9wYm^J+n5?@n7J=MrDAiW&cf@Wm2YOz=YGC-1_3W0x>hif#6)%_hQ$wu=fH%{D3 ziSQ!ODLQb~gY?NPVn=LH$ZBq40Zpq1&w=h(hQ>Ng zx6I0PXZEn3rL!^wc3E(`DQ8*vBVf2igwiDaX8TPq30FP?tU^w1F-=cO@UC)2bDF4O z2gQW;^8y7akjB|}K}?`8IHO^$B+f~+P>Lkh z6