From d64b50f550300d3a172a1ae1fcbd38567ede4459 Mon Sep 17 00:00:00 2001 From: farie82 Date: Sat, 21 Mar 2020 16:54:38 +0100 Subject: [PATCH 01/10] Fixes cling sting types not initialising (#13159) * Fixes cling sting types not initialising * syntax change --- code/game/gamemodes/changeling/evolution_menu.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/gamemodes/changeling/evolution_menu.dm b/code/game/gamemodes/changeling/evolution_menu.dm index d2b1cdae519..9c33ffaaca5 100644 --- a/code/game/gamemodes/changeling/evolution_menu.dm +++ b/code/game/gamemodes/changeling/evolution_menu.dm @@ -12,7 +12,7 @@ GLOBAL_LIST_EMPTY(sting_paths) return var/datum/changeling/changeling = usr.mind.changeling - if(!GLOB.sting_paths) + if(!GLOB.sting_paths || !GLOB.sting_paths.len) GLOB.sting_paths = init_subtypes(/datum/action/changeling) var/dat = create_menu(changeling) From aa441ebf65755b7ebe68e1e1cc8d80047f6c0cf7 Mon Sep 17 00:00:00 2001 From: AffectedArc07 Date: Sat, 21 Mar 2020 16:53:27 +0000 Subject: [PATCH 02/10] Fixes pipes not rendering (#13160) --- code/ATMOSPHERICS/datum_icon_manager.dm | 10 ---------- code/__DEFINES/colors.dm | 9 +++++++++ code/_globalvars/misc.dm | 1 + 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/code/ATMOSPHERICS/datum_icon_manager.dm b/code/ATMOSPHERICS/datum_icon_manager.dm index 23a4dab33da..2286b3523a0 100644 --- a/code/ATMOSPHERICS/datum_icon_manager.dm +++ b/code/ATMOSPHERICS/datum_icon_manager.dm @@ -6,16 +6,6 @@ // atmospherics devices. //-------------------------------------------- -#define PIPE_COLOR_GREY "#ffffff" //yes white is grey -#define PIPE_COLOR_RED "#ff0000" -#define PIPE_COLOR_BLUE "#0000ff" -#define PIPE_COLOR_CYAN "#00ffff" -#define PIPE_COLOR_GREEN "#00ff00" -#define PIPE_COLOR_YELLOW "#ffcc00" -#define PIPE_COLOR_PURPLE "#5c1ec0" - -GLOBAL_LIST_INIT(pipe_colors, list("grey" = PIPE_COLOR_GREY, "red" = PIPE_COLOR_RED, "blue" = PIPE_COLOR_BLUE, "cyan" = PIPE_COLOR_CYAN, "green" = PIPE_COLOR_GREEN, "yellow" = PIPE_COLOR_YELLOW, "purple" = PIPE_COLOR_PURPLE)) - /proc/pipe_color_lookup(var/color) for(var/C in GLOB.pipe_colors) if(color == GLOB.pipe_colors[C]) diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index 13f7777d386..2bb53c91ce4 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -99,3 +99,12 @@ #define COLOR_ASSEMBLY_LBLUE "#5D99BE" #define COLOR_ASSEMBLY_BLUE "#38559E" #define COLOR_ASSEMBLY_PURPLE "#6F6192" + +// Pipe colours +#define PIPE_COLOR_GREY "#ffffff" //yes white is grey +#define PIPE_COLOR_RED "#ff0000" +#define PIPE_COLOR_BLUE "#0000ff" +#define PIPE_COLOR_CYAN "#00ffff" +#define PIPE_COLOR_GREEN "#00ff00" +#define PIPE_COLOR_YELLOW "#ffcc00" +#define PIPE_COLOR_PURPLE "#5c1ec0" diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm index 5fd40bf86d9..32d6370f3d0 100644 --- a/code/_globalvars/misc.dm +++ b/code/_globalvars/misc.dm @@ -100,3 +100,4 @@ GLOBAL_DATUM_INIT(dbcon, /DBConnection, new) //Feedback database (New database) GLOBAL_PROTECT(dbcon) GLOBAL_LIST_EMPTY(ability_verbs) // Create-level abilities +GLOBAL_LIST_INIT(pipe_colors, list("grey" = PIPE_COLOR_GREY, "red" = PIPE_COLOR_RED, "blue" = PIPE_COLOR_BLUE, "cyan" = PIPE_COLOR_CYAN, "green" = PIPE_COLOR_GREEN, "yellow" = PIPE_COLOR_YELLOW, "purple" = PIPE_COLOR_PURPLE)) From 284b7584323ec2223cf01e41bebcea61c7a12a03 Mon Sep 17 00:00:00 2001 From: datlo Date: Sat, 21 Mar 2020 17:56:54 +0100 Subject: [PATCH 03/10] Fix grey telepathy (#13142) --- code/modules/mob/say.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 3b3f34fdb13..81d22e40a0b 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -1,5 +1,5 @@ -#define ILLEGAL_CHARACTERS_LIST list("<" = "", ">" = "", "^" = "", \ +#define ILLEGAL_CHARACTERS_LIST list("<" = "", ">" = "", \ "\[" = "", "]" = "", "{" = "", "}" = "") /mob/proc/say() From 5cb95359566434031fa78b5d757cd782d9759ad0 Mon Sep 17 00:00:00 2001 From: Aquilar <20759278+Aquilar@users.noreply.github.com> Date: Sun, 22 Mar 2020 01:00:57 +0800 Subject: [PATCH 04/10] Adds inhand sprites for the jaws of life and hand drill (#13124) --- icons/mob/inhands/items_lefthand.dmi | Bin 151695 -> 152689 bytes icons/mob/inhands/items_righthand.dmi | Bin 151143 -> 152133 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi index 3d02cfd7a58a3e738a9d13721d557f18640634d5..26cd5a1337b8bfedfdb65a1642fb166aae208e8f 100644 GIT binary patch delta 7526 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 delta 6525 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 diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi index 265230ad36988ef55e4ee7370183db01bf45bf37..78aa94f2907e719eef10903e284c12ead3275d73 100644 GIT binary patch delta 7055 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 S#}?~fjzLv*R7#bs-uyq_d%-^d delta 6057 zcmcImhc{enw4XtQAc)?3^yo$>k?18#NHSUwC0Ydq6C%N~n_5Oi();j0g?dCYSyPxw{ZVzjGCT% z?OV`qC5CrLiPk1|UnIh~j;V-d5o`^Cbxf;_;*q(mrK((LDrCvXco|TrlbF%Y4wCmm z*U&Q#z-+rV3I1ngexXjbvhrkIzg{h)RRmEX8g-ldts$>bvA=JMb4Pr3N3)EC^joWm z*s{;9#&F&=x>@y$K*maz=aW}nN^wMMMYE?rptHyk5aXYQaaD3 zuM8?u;*BXjSrg*LC<6>e5oHS`t^|Ld=@7BGp0o4!k&UY?BHsjcTgMH?G#!{AcJ$Ed zxS`P~f5Y_0p!~$R4F3(VETN&upB^2J4_5S{Li098zC=8=wyI093Muq+MDTGg&@|y( zM8cj^x&%QI#QgkV`*;ETX+P7MJYt^a1$AX8NGOH-{mr|sE@F>}b+(1pNpy;y6Zn0!Af5~n~=V3Yfli^EGj2{3T!Bue<&_*Yj(XP4vNadpL$(e ze&cs)g|U?v4fP>4KcQ?MfclyVyR%%pHl0Yw&S~rz3v`lV*=x@++=yzEdB-FD%Q0}f z!0=bFon4{Cdc!&vo$UX{w1u|GF*-6ZUtkbg7Zt=0Oza-A784m*EtzjzM;b5I{U#Ip zBG*(S-s-B?O0_L`E&Z{)U8j)?TT$wFnVc!s6P(8soA2q|ch@iU&A=Fc)u=!?E02{=TbZmD_OUmo7*Fc){ z5;ZO!L_M{FR@jWC9z8KWVsKo`z=yRZdv0LcEvgO%nq!Y@?{*+hoZo-d4i;Oa@NX3^p=CJ~k)5hr7f}Ltn?pDR+^S z+h0cVIMzFl@_D((TCBPu`)pqMWZ!0(PveD1$iWO#G6&E1z1$QQiuCs;b_8R0YXCwW zRq#NE9THjMt--GIonDAh=z{xcuCdvf(S?qu@_+)-^l2i6V>dh6kj>@}j$Xk&H}4CB zv6LXGy%;oFb?vUh?uo}+gwTG{*z$T(x&Da7^w;bVogNFmL2j+8TcJ=njRPWZopY|# z6O(L>NG|9#@PKEic3dX6L-1~RJ8^S*T=JbO;($sum#`kk*+-tc;P`>)GOqU~SYDNl zw`gYYFZwqcV}_2-tbX)Yb(EP2Z@%_NCj{t^NR!!3+&eo}l&nkizV@x=R^qN?5j>1u z{wrN~(E}`YQ6?EFK2H!xisOsL>XhZ^8S-ad9{@!=$~>Eq9$WrM_k33{$&goO=Vu&U zbRK|xn;f9E{*Wf0l+OMx-rhjZU6BRsc0FTXH@DsOMuF(Ok8!BIYhe2Wz0f@!!>eM? zbFJEI%`NG*U$h~GU2MdN>fON-%Z|d5j+8(~7v)D08K3E!iC_UeEt`#lq(SG_3rWlI z_!X5nK1p66%RjfoE=%qXXOj1=jXJ8gOOhgUa>U8nr6h4MNs$mGDSYd>E7^^cn_v^h z;Y8kNB2y!E?g*dhhOw@iisV#M4qz@YnS(85tjQh3z^KCZ?S(A{6~id8!O`tvvzNx^ zJ4qz6T<;EQ$nGB}>#lWz2@%sg?c0>UFUeC)p<|;;?T8q@>XJn4JE>5RJ0i87z&lhLf$>#xO=D8Pd=Rv)o7`9gfRJ6?B^dWQT!_sPz; zay+T%ij=A*Hq$umvP!sba?26gjnCwY8FcKT?Kj&D+fGcbu_V5c{jy>uwc>lBBYdv( zxPxu|S*hsZ(5Qd)(=*lKEDC^@F=er0gy&aNMvH}hU??ZAJA5TvcM}cv$LSEgS-#tx zI}D%vM&8jaZug1ly8DMhwNbi97w5boSQB&2Qe;|5Kp1V~X8VffZ~Q=iG-+??2hx!p zCOHUq^oX$TZ#$E=D<_K$uT;S(DFtMy*~1_Sr?)Z+tyf}VeH7)E$lQVcSUJ8x?~nyp zyObNDnx7lFDn!ssK74(}U*t`v-czKcd6Ht<_fDs}3tEb|`NVY~)BFM_aBia)79rXQikA&X9FrF%Azc-P=zHkhi|K)H(6466Y@w?3`sgo-ZLG46NB>#Ogg^zRC2E7tf{%9)t*@ci@YP zuxOj`PRgO5u7Ww{R1>N2Wc$HL{I~(x0-t`?_3AQOVTS>X&@|ar!Acs7hM?Dw&6>^1ah!l8 zbmInm>mMFm33o}D1s`OblPI`*rJcmwA^Xw%xPYt=aZ{8+Vo18L-2Htj=!e3YmK$=ASrk+gYx@KNVII|2+GJ{5@TA zFG|wov`_LVs~QnvFu`owxQVa|KH56lIIC!xIi-$ep(>fmFXsrKxE3{W4UC$&Mzkge zI82x0RN`AGANb}oBxGAmRpzACyNN1#R6otWN&4kMwimbEQ}c}xaV6eQG#>>$93}D8 zdQ(LiM&tRxjxzO?ScJ+S z3U3a<+HBnFNgG0~K%@`SeYk(#tgg#l0pL2hh$(98fKpC(uw_Qkab`bHsW+#rgCS>Zj#;ASR&z_51Ujk+7c?1_y5iMof)) z6BGF3Vq;x8o3rGD)+suW_}{RJf4n2i4dqpyPpFMv*V7`IM)as`! zb#Q_;#ZNc-ZK^`~(8Ws!+lxzsqDd(anSX6PsSR2d=w8yOaj1IH)3&Qsp~E5b#Fu#$ zQd%#*i$^0k{*^;4e}>q@)Wki6)PJGQNVP-@Yul=5>~ zgp*ntwP9WR?41vKCaco}-*IrfYI+{_VT=00q5XK(mFGc5sTvAP1+`^doa8$9*oCd- z1=~`0;D(2k^s%rQTh%IW@m4F5&wH~~6+4)gySyfpSHt^yM}OEH7W~cv`V{0x`wK}!n7tr^f4Z%&9?^#*@Pp9gPjEteRH5VzeTk00uYo*C=fL&j?(PTAm8>A|{ zW*aN)w`iYfWaT~_18J{yur(6ARX2KeF&KUK&Y+9=p~uE8L@*SZSyWUsGVI{>9^q_N zvU8_QQ&Te$O$a`2Hv(WNnhf%Vphv;bs6m;wAFDRPq)~Ohd@CNR0s_sSL!8f@U0)q@ zsmjdk>~rZB4_i?9{A4o@8g@S_2B=TD(!Er64fhudsu(ReCj61>gl>SOncCCgoFeVQ z&f$D^!M%9VJe7VMSX=A$?H9S>1^#fH==R-D8ATfH&%s`wEzC)`|9==**2#3%L5U^Jt1q3`jv>F}%6 z8G8xn+FH5F;54k#j7sX~oVPdub!q7j;?TN>*xKq~qxD*nm!72$;KS|YRc>1j$5!SW zF)~K-neU*0=wc}g>Mh+@^__5@@s-|5_@a+nfX(Yc|K2B?x=sHCeC}xpy!_UVu)hJF zpIB3;NuYi)yqaHctqUw`k^I2qKtjHcJ7d26&uO4KDd=c@y%Uv&b59#;Zxmf>$sjHivE<71PbCI*zCdUDtJBi!6l$u|gTvtlP$<-J!OP9fjXzO`xuo((vpH{~kd4Fr z)y5meE0?{$n^7=T(RqBjrzo*{kh3`o#8ZGi8VV5wfIuMG)JyPBZ{^*LgzqC#X&?9F z3^M9;7C!SUB2mj{?(9e8l@iaC*0WOC+yU#pmVa=@=U$AJw@(GUN%Oft$L4 zpjf$-_yv+(-(VFnJuj>JCe;MnCbDUEkj4OQD=`SfElJ?CExLqguQ9gaD=<)+F7C3K zHI2-?VG}=nZi%wSpSMq{!==mF+{ZXqn$7q2WK>tS<9@#DHR@xNjS>K5l^O{~q+XLS zfBAc~Tk-J6b$jDaETt)KWw6*vmjX91s2-=(LUCCSNYC)=H1BGMdfSJyv9F`KQx!P@ zirdOeYGp?Xl>a#gTB%?0YTIUyG`Y#*L~vS?va%m{QmyE2k#h+sTK6c>1|3iaS z{u6^3E3c{mAdrjFZomxg#$Tyn2*b|*svl~AsQ;rwu8{mci7(^-ah_za@9-VR`jxTW zO^u^9F-fG^X_LT1in=g9WQy9VUb$hCY02AeLnD!7BW?FcoF~cYAcHT$Q@P#F|u)YQ~OK88!Pp3&1ls6b- zI(i~0nBL$pLHoTw9FGu51$gW@oA8WkbmtNx^)mDy{E6^ac0?NsQBi*Gx>LjI4K0M* zGXZi=(&@6IDH7vm%CJ|`51(&xFiWDly9Xk1@mlFuwJ)#cqwhAuWZ(I5T~4803|$Zs ziPTRT#i88dFz0B|42717eb#}8_El+$ej6iYOKxLj`>qwrJRBSv+m7gCtPa?x$lviauAARC+aEZ zfK5?;cGm$&#q;tV^F#`RKb7Jw^O^sIcX1V zIa8J|l5fc`zU$v333xgzEI;$EAS{h47~L0VxZ_uH_zB4r-%Bz;kd5GE&r)WFJ%d>| zI^TL)Fj}oHQLRKbC?E_!sw5T7DwcHGdIOOZnPLB!LKk#`9tsW$@;|c~^*$cDcq!Er zHd$7EI-`D^ic6MCY;9vVK=&tY(2Aum2tdnmuw=)xWwWFdPxtr4ZY>8q`Q=XT5E#qtb+wv? zZk4Ijx+Q&5Gsyp=P5-qQod<^)ODAxxIKR7%%Y9WfwQ@&k*VB5QWtEd}*eZzFmW-Fx z6xS5acN)NZoouUX7c}2j*K|=+Z#b5~&sU8*ap166x2e&&3J>@|(AJKAr1NIG{mMYk zuG2R8-=xM($FBv1?bz)tUa@QlEw-5uuLG1MS8)9r{A@et!~omL^^+Wv z*(CTklGRT?1QQ5WU@F}I&!ME}PvQ}`t%`HY$#vt?TlpfFhiu|eIKsPAS9JBfgF`T8 z^}3nu#%?Z;p%mOkm0?q((~t3sx+y5=w>FUH26%^m_}BGF`8J?BPs`qSJ|Q3O zEkT6f*16I0m`|55SdOZ3Uc$Ic!-8*pycTGx)y8K#z$Heq^M|a^jsXXy5loR)RaIT# zoi?K7M=jOA>Wu}C<8%IId|#R9h;(3j+D-0shj;#J*v9#IJ{+z;hm-y!ePF@E)^vgY zh?VGi4EJ3103H%dD|}b`c9knA|83J;dH37lZW~)$aWu{OlLi;?nxmtxHaay6)26>0 zbz5|}I9%pHu!-el_(A92fFMc8<+Rhd(t-<+`;gCOxa& Date: Sat, 21 Mar 2020 21:35:42 +0000 Subject: [PATCH 05/10] Automatic merge conflict labeling (#13162) --- .github/workflows/label_merge_conflicts.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/workflows/label_merge_conflicts.yml diff --git a/.github/workflows/label_merge_conflicts.yml b/.github/workflows/label_merge_conflicts.yml new file mode 100644 index 00000000000..d2a56e2ef3d --- /dev/null +++ b/.github/workflows/label_merge_conflicts.yml @@ -0,0 +1,13 @@ +name: 'Merge Conflict Detection' +on: + push: + branches: + - master +jobs: + triage: + runs-on: ubuntu-latest + steps: + - uses: mschilde/auto-label-merge-conflicts@master + with: + CONFLICT_LABEL_NAME: 'Merge Conflict' + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From de09a6011ec2e71f26e2210c950be84f67009a84 Mon Sep 17 00:00:00 2001 From: Shawn Hind Date: Sat, 21 Mar 2020 19:03:54 -0400 Subject: [PATCH 06/10] Update cloning scan messages (#13094) * Update cloning scan messages Clarifies cloning scan messages to make them more clear for new players. * Change null subject, not human, no dna message back to original. * add xenomorph condition * Update no ghost cloning message --- code/game/machinery/computer/cloning.dm | 28 +++++++++++++------------ 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 6a8aafb364d..14a8e2b6ddc 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -358,35 +358,37 @@ return if(scan_brain && !can_brainscan()) return - if((isnull(subject)) || (!(ishuman(subject))) || (!subject.dna) || (NO_SCAN in subject.dna.species.species_traits)) - scantemp = "Error: Unable to locate valid genetic data." - SSnanoui.update_uis(src) - return + if(isnull(subject) || (!(ishuman(subject))) || (!subject.dna)) + if(isalien(subject)) + scantemp = "Error: Xenomorphs are not scannable." + SSnanoui.update_uis(src) + return + // can add more conditions for specific non-human messages here + else + scantemp = "Error: Subject species is not scannable." + SSnanoui.update_uis(src) + return if(subject.get_int_organ(/obj/item/organ/internal/brain)) var/obj/item/organ/internal/brain/Brn = subject.get_int_organ(/obj/item/organ/internal/brain) if(istype(Brn)) if(NO_SCAN in Brn.dna.species.species_traits) - scantemp = "Error: Subject's brain is incompatible." + scantemp = "Error: [subject.dna.species.name_plural] are not scannable." SSnanoui.update_uis(src) return if(!subject.get_int_organ(/obj/item/organ/internal/brain)) - scantemp = "Error: No signs of intelligence detected." + scantemp = "Error: No brain detected in subject." SSnanoui.update_uis(src) return if(subject.suiciding) - scantemp = "Error: Subject's brain is not responding to scanning stimuli." + scantemp = "Error: Subject has committed suicide and is not scannable." SSnanoui.update_uis(src) return if((!subject.ckey) || (!subject.client)) - scantemp = "Error: Mental interface failure." + scantemp = "Error: Subject's brain is not responding. Further attempts after a short delay may succeed." SSnanoui.update_uis(src) return if((NOCLONE in subject.mutations) && src.scanner.scan_level < 2) - scantemp = "Error: Mental interface failure." - SSnanoui.update_uis(src) - return - if(scan_brain && !subject.get_int_organ(/obj/item/organ/internal/brain)) - scantemp = "Error: No brain found." + scantemp = "Error: Subject has incompatible genetic mutations." SSnanoui.update_uis(src) return if(!isnull(find_record(subject.ckey))) From e691ea2b36b37422b57cc0f3a943740503dc6bf6 Mon Sep 17 00:00:00 2001 From: Ty-Omaha Date: Sat, 21 Mar 2020 19:17:03 -0400 Subject: [PATCH 07/10] Paper Letterhead (#13155) * fax letterhead 1 need to add correct paperbins on map and resize logos * tweaks * syndicate paper on syndicate base * code improvements --- .../lavaland_surface_syndicate_base1.dmm | 6 +-- _maps/map_files/cyberiad/cyberiad.dmm | 49 ++++-------------- code/modules/admin/topic.dm | 12 +++-- code/modules/client/asset_cache.dm | 3 +- code/modules/paperwork/paper.dm | 23 +++++++- code/modules/paperwork/paperbin.dm | 18 ++++++- code/modules/paperwork/photocopier.dm | 2 + icons/paper_icons/ntlogo.png | Bin 9990 -> 6673 bytes icons/paper_icons/syndielogo.png | Bin 0 -> 7734 bytes 9 files changed, 64 insertions(+), 49 deletions(-) create mode 100644 icons/paper_icons/syndielogo.png diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm index 279afa9713d..e00aedc8100 100644 --- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm +++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm @@ -1789,7 +1789,6 @@ /area/ruin/unpowered/syndicate_lava_base/cargo) "ed" = ( /obj/structure/table, -/obj/item/paper_bin, /obj/item/pen, /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, @@ -1803,6 +1802,7 @@ /obj/effect/turf_decal/tile/neutral{ dir = 8 }, +/obj/item/paper_bin/syndicate, /turf/simulated/floor/plasteel/dark, /area/ruin/unpowered/syndicate_lava_base/cargo) "ef" = ( @@ -1898,7 +1898,6 @@ "ep" = ( /obj/structure/table/reinforced, /obj/effect/decal/cleanable/dirt, -/obj/item/paper_bin, /obj/item/pen, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -1912,6 +1911,7 @@ /obj/effect/turf_decal/tile/neutral{ dir = 8 }, +/obj/item/paper_bin/syndicate, /turf/simulated/floor/plasteel/dark, /area/ruin/unpowered/syndicate_lava_base/testlab) "eq" = ( @@ -6790,8 +6790,8 @@ /area/ruin/unpowered/syndicate_lava_base/telecomms) "ov" = ( /obj/structure/table/reinforced, -/obj/item/paper_bin, /obj/item/pen, +/obj/item/paper_bin/syndicate, /turf/simulated/floor/plasteel/dark, /area/ruin/unpowered/syndicate_lava_base/telecomms) "ox" = ( diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm index 74e179255ce..7cf27593f6f 100644 --- a/_maps/map_files/cyberiad/cyberiad.dmm +++ b/_maps/map_files/cyberiad/cyberiad.dmm @@ -1619,15 +1619,12 @@ /area/security/range) "adH" = ( /obj/structure/table/wood, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/pen/multi, /obj/machinery/light{ dir = 1; on = 1 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -16077,16 +16074,13 @@ /area/lawoffice) "aAI" = ( /obj/structure/table/reinforced, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/pen, /obj/machinery/alarm{ dir = 8; pixel_x = 25; pixel_y = 0 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ tag = "icon-cult"; icon_state = "cult"; @@ -16863,11 +16857,8 @@ /area/magistrateoffice) "aCj" = ( /obj/structure/table/reinforced, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/stamp/magistrate, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/carpet, /area/magistrateoffice) "aCk" = ( @@ -21875,11 +21866,8 @@ /area/maintenance/fpmaint2) "aMO" = ( /obj/structure/table/reinforced, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/pen, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ tag = "icon-cult"; icon_state = "cult"; @@ -27795,16 +27783,13 @@ /obj/item/folder/yellow, /obj/item/stamp/ce, /obj/item/book/manual/sop_engineering, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/storage/fancy/cigarettes, /obj/item/lighter/zippo, /obj/item/pen/multi, /obj/item/enginepicker{ layer = 3.1 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "neutralfull" @@ -43355,12 +43340,9 @@ }, /area/hallway/primary/central/west) "bCG" = ( -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/pen, /obj/structure/table/wood, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/carpet, /area/bridge/meeting_room) "bCH" = ( @@ -47113,10 +47095,6 @@ /area/shuttle/constructionsite) "bJN" = ( /obj/structure/table/wood, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 10 - }, /obj/item/pen/multi/fountain, /obj/machinery/door_control{ id = "captainofficedoor"; @@ -47129,6 +47107,7 @@ /obj/item/radio/intercom{ pixel_x = -28 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/wood, /area/crew_quarters/captain) "bJO" = ( @@ -50576,15 +50555,12 @@ }, /area/toxins/lab) "bPE" = ( -/obj/item/paper_bin{ - pixel_x = 1; - pixel_y = 9 - }, /obj/structure/table/glass, /obj/item/pen/multi, /obj/item/book/manual/sop_science{ pixel_y = -14 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ dir = 2; icon_state = "cafeteria"; @@ -54151,10 +54127,6 @@ /area/hallway/primary/central/se) "bVy" = ( /obj/structure/table, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/pen/multi, /obj/item/pen/multi, /obj/item/megaphone, @@ -55112,6 +55084,7 @@ /obj/item/book/manual/sop_service, /obj/item/book/manual/sop_supply, /obj/item/book/manual/sop_command, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel, /area/crew_quarters/heads) "bXa" = ( @@ -60880,7 +60853,6 @@ /area/medical/cmo) "cfW" = ( /obj/structure/table/glass, -/obj/item/paper_bin, /obj/item/pen/multi, /obj/item/folder/white{ pixel_y = 7 @@ -60890,6 +60862,7 @@ pixel_x = 32; pixel_y = 0 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "darkblue" @@ -67468,12 +67441,12 @@ /area/blueshield) "cqS" = ( /obj/structure/table/wood, -/obj/item/paper_bin, /obj/item/flashlight/lamp/green{ pixel_x = -5; pixel_y = 12 }, /obj/item/paper/ntrep, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/carpet, /area/ntrep) "cqT" = ( diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 340acc52ef7..03b5cb09720 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -2196,9 +2196,15 @@ var/reply_to = locate(href_list["replyto"]) var/destination var/notify - - var/obj/item/paper/P = new /obj/item/paper(null) //hopefully the null loc won't cause trouble for us - + var/obj/item/paper/P + var/use_letterheard = alert("Use letterhead? If so, do not add your own header or a footer. Type and format only your actual message.",,"Nanotrasen","Syndicate", "No") + switch(use_letterheard) + if("Nanotrasen") + P = new /obj/item/paper/central_command(null) + if("Syndicate") + P = new /obj/item/paper/syndicate(null) + if("No") + P = new /obj/item/paper(null) if(!fax) var/list/departmentoptions = GLOB.alldepartments + GLOB.hidden_departments + "All Departments" destination = input(usr, "To which department?", "Choose a department", "") as null|anything in departmentoptions diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index b29482a73cc..e1e68bd0617 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -195,7 +195,8 @@ GLOBAL_LIST_EMPTY(asset_datums) "large_stamp-rep.png" = 'icons/paper_icons/large_stamp-rep.png', "large_stamp-magistrate.png"= 'icons/paper_icons/large_stamp-magistrate.png', "talisman.png" = 'icons/paper_icons/talisman.png', - "ntlogo.png" = 'icons/paper_icons/ntlogo.png' + "ntlogo.png" = 'icons/paper_icons/ntlogo.png', + "syndielogo.png" ='icons/paper_icons/syndielogo.png' ) /datum/asset/simple/chess diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 57bdc94b08c..34acdcf0983 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -21,7 +21,9 @@ max_integrity = 50 attack_verb = list("bapped") dog_fashion = /datum/dog_fashion/head + var/header //Above the main body, displayed at the top var/info //What's actually written on the paper. + var/footer //The bottom stuff before the stamp but after the body var/info_links //A different version of the paper which includes html links at fields and EOF var/stamps //The (text for the) stamps on the paper. var/fields //Amount of user created fields @@ -76,9 +78,9 @@ var/data var/stars = (!user.say_understands(null, GLOB.all_languages["Galactic Common"]) && !forceshow) || forcestars if(stars) //assuming all paper is written in common is better than hardcoded type checks - data = "[stars(info)][stamps]" + data = "[header][stars(info)][footer][stamps]" else - data = "
[infolinks ? info_links : info]
[stamps]" + data = "[header]
[infolinks ? info_links : info]
[footer][stamps]" if(view) var/datum/browser/popup = new(user, "Paper[UID()]", , paper_width, paper_height) popup.stylesheets = list() @@ -587,6 +589,23 @@ /obj/item/paper/crumpled name = "paper scrap" icon_state = "scrap" + +/obj/item/paper/syndicate + name = "paper" + header = "


" + info = "" + +/obj/item/paper/nanotrasen + name = "paper" + header = "


" + info = "" + +/obj/item/paper/central_command + name = "paper" + header ="


Nanotrasen Central Command

Official Expedited Memorandum


" + info = "" + footer = "

Failure to adhere appropriately to orders that may be contained herein is in violation of Space Law, and punishments may be administered appropriately upon return to Central Command.
The recipient(s) of this memorandum acknowledge by reading it that they are liable for any and all damages to crew or station that may arise from ignoring suggestions or advice given herein.

" + /obj/item/paper/crumpled/update_icon() return diff --git a/code/modules/paperwork/paperbin.dm b/code/modules/paperwork/paperbin.dm index 68afe9fbd12..d76765b61a7 100644 --- a/code/modules/paperwork/paperbin.dm +++ b/code/modules/paperwork/paperbin.dm @@ -10,7 +10,8 @@ pressure_resistance = 8 var/amount = 30 //How much paper is in the bin. var/list/papers = list() //List of papers put in the bin for reference. - + var/letterhead_type + /obj/item/paper_bin/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) if(amount) amount = 0 @@ -71,7 +72,10 @@ P = papers[papers.len] papers.Remove(P) else - P = new /obj/item/paper + if(letterhead_type && alert("Choose a style",,"Letterhead","Blank")=="Letterhead") + P = new letterhead_type + else + P = new /obj/item/paper if(SSholiday.holidays && SSholiday.holidays[APRIL_FOOLS]) if(prob(30)) P.info = "HONK HONK HONK HONK HONK HONK HONK
HOOOOOOOOOOOOOOOOOOOOOONK
APRIL FOOLS
" @@ -139,3 +143,13 @@ add_fingerprint(user) return + + +/obj/item/paper_bin/nanotrasen + name = "nanotrasen paper bin" + letterhead_type = /obj/item/paper/nanotrasen + +/obj/item/paper_bin/syndicate + name = "syndicate paper bin" + letterhead_type = /obj/item/paper/syndicate + diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index aa1fe064626..5ca53396253 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -187,7 +187,9 @@ /obj/machinery/photocopier/proc/copy(var/obj/item/paper/copy) var/obj/item/paper/c = new /obj/item/paper (loc) + c.header = copy.header c.info = copy.info + c.footer = copy.footer c.name = copy.name // -- Doohl c.fields = copy.fields c.stamps = copy.stamps diff --git a/icons/paper_icons/ntlogo.png b/icons/paper_icons/ntlogo.png index d5614a964eef8109b40fbe3540e494c404abb2aa..3aeae313238eca7244ca10677586d299bc27a782 100644 GIT binary patch literal 6673 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 AHvj+t literal 9990 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 D^Z3ue diff --git a/icons/paper_icons/syndielogo.png b/icons/paper_icons/syndielogo.png new file mode 100644 index 0000000000000000000000000000000000000000..6b0c4966541b5254cee30995b02ead93f22f121a GIT binary patch literal 7734 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 Date: Sun, 22 Mar 2020 00:28:20 +0100 Subject: [PATCH 08/10] Adds a new logging system and a logging view (#13115) * Super early initial commit * Why do I keep comitting this * in between * In between * Sort fix. Transfer fix. HTML and more * Scrolling + define values change * Search fixes and time input fixes * Minor tweaks. Fuel tank inclusion. Fixes * derp * Extra logging to fuel tank * minor stuff * add the message to admins for fueltanks * Don't keep mob/atom references + fixes * line fixes? * Review improvements * pois comment --- code/__DEFINES/logs.dm | 6 + code/__HELPERS/mobs.dm | 35 ++- code/__HELPERS/time.dm | 16 +- code/__HELPERS/unsorted.dm | 13 +- code/datums/datumvars.dm | 2 +- code/datums/log_record.dm | 37 +++ code/datums/log_viewer.dm | 232 ++++++++++++++++++ code/datums/mind.dm | 8 + code/datums/spell.dm | 2 +- code/datums/spells/mime.dm | 3 +- code/game/gamemodes/cult/cult.dm | 1 + .../miniantags/abduction/abduction.dm | 3 +- code/game/gamemodes/nuclear/nuclear.dm | 1 + code/game/gamemodes/revolution/revolution.dm | 3 +- code/game/gamemodes/shadowling/shadowling.dm | 3 + code/game/gamemodes/vampire/vampire.dm | 1 + code/game/gamemodes/wizard/rightandwrong.dm | 4 +- code/game/gamemodes/wizard/soulstone.dm | 5 +- code/game/gamemodes/wizard/spellbook.dm | 1 + code/game/gamemodes/wizard/wizard.dm | 1 + code/game/machinery/doors/airlock.dm | 3 + code/game/mecha/mecha.dm | 1 + .../objects/items/devices/laserpointer.dm | 1 + code/modules/admin/admin_verbs.dm | 1 + code/modules/admin/verbs/logging_view.dm | 10 + code/modules/admin/verbs/randomverbs.dm | 2 +- .../kitchen_machinery/gibber.dm | 1 + code/modules/mob/dead/observer/observer.dm | 7 +- code/modules/mob/emote.dm | 1 + code/modules/mob/living/living.dm | 1 + code/modules/mob/living/say.dm | 2 + .../mob/living/silicon/robot/update_status.dm | 1 + code/modules/mob/living/stat_states.dm | 5 +- code/modules/mob/login.dm | 1 + code/modules/mob/logout.dm | 1 + code/modules/mob/mob.dm | 13 +- code/modules/mob/mob_defines.dm | 7 +- code/modules/projectiles/guns/magic/wand.dm | 1 + code/modules/projectiles/projectile/magic.dm | 6 +- code/modules/reagents/reagent_dispenser.dm | 12 +- code/modules/recycling/sortingmachinery.dm | 1 + code/modules/spacepods/spacepod.dm | 1 + code/modules/tram/tram.dm | 1 + paradise.dme | 4 + 44 files changed, 419 insertions(+), 42 deletions(-) create mode 100644 code/__DEFINES/logs.dm create mode 100644 code/datums/log_record.dm create mode 100644 code/datums/log_viewer.dm create mode 100644 code/modules/admin/verbs/logging_view.dm diff --git a/code/__DEFINES/logs.dm b/code/__DEFINES/logs.dm new file mode 100644 index 00000000000..a8a4a457c7c --- /dev/null +++ b/code/__DEFINES/logs.dm @@ -0,0 +1,6 @@ +#define ATTACK_LOG "Attack" +#define DEFENSE_LOG "Defense" +#define CONVERSION_LOG "Conversion" +#define SAY_LOG "Say" +#define EMOTE_LOG "Emote" +#define MISC_LOG "Misc" \ No newline at end of file diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index cdbb969f817..6be2b411376 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -274,7 +274,7 @@ Proc for attack log creation, because really why not This is always put in the attack log. */ -/proc/add_attack_logs(mob/user, mob/target, what_done, custom_level) +/proc/add_attack_logs(atom/user, target, what_done, custom_level) if(islist(target)) // Multi-victim adding var/list/targets = target for(var/mob/M in targets) @@ -282,24 +282,33 @@ This is always put in the attack log. return var/user_str = key_name_log(user) + COORD(user) - var/target_str = key_name_log(target) + COORD(target) - - if(istype(user)) - user.create_attack_log("Attacked [target_str]: [what_done]") - if(istype(target)) - target.create_attack_log("Attacked by [user_str]: [what_done]") + var/target_str + if(isatom(target)) + var/atom/AT = target + target_str = key_name_log(AT) + COORD(AT) + else + target_str = target + var/mob/MU = user + var/mob/MT = target + if(istype(MU)) + MU.create_log(ATTACK_LOG, what_done, target, get_turf(user)) + MU.create_attack_log("Attacked [target_str]: [what_done]") + if(istype(MT)) + MT.create_log(DEFENSE_LOG, what_done, user, get_turf(MT)) + MT.create_attack_log("Attacked by [user_str]: [what_done]") log_attack(user_str, target_str, what_done) var/loglevel = ATKLOG_MOST if(!isnull(custom_level)) loglevel = custom_level - else if(istype(target)) - if(istype(user) && !user.ckey && !target.ckey) // Attacks between NPCs are only shown to admins with ATKLOG_ALL - loglevel = ATKLOG_ALL - else if(!user.ckey || !target.ckey || (user.ckey == target.ckey)) // Player v NPC combat is de-prioritized. Also no self-harm, nobody cares - loglevel = ATKLOG_ALMOSTALL + else if(istype(MT)) + if(istype(MU)) + if(!MU.ckey && !MT.ckey) // Attacks between NPCs are only shown to admins with ATKLOG_ALL + loglevel = ATKLOG_ALL + else if(!MU.ckey || !MT.ckey || (MU.ckey == MT.ckey)) // Player v NPC combat is de-prioritized. Also no self-harm, nobody cares + loglevel = ATKLOG_ALMOSTALL else - var/area/A = get_area(target) + var/area/A = get_area(MT) if(A && A.hide_attacklogs) loglevel = ATKLOG_ALMOSTALL if(isLivingSSD(target)) // Attacks on SSDs are shown to admins with any log level except ATKLOG_NONE. Overrides custom level diff --git a/code/__HELPERS/time.dm b/code/__HELPERS/time.dm index d13bf08c58b..340d7a3b134 100644 --- a/code/__HELPERS/time.dm +++ b/code/__HELPERS/time.dm @@ -50,10 +50,24 @@ return "[date_portion]T[time_portion]" /proc/gameTimestamp(format = "hh:mm:ss", wtime=null) - if(!wtime) + if(wtime == null) wtime = world.time return time2text(wtime - GLOB.timezoneOffset, format) +// max hh:mm:ss supported +/proc/timeStampToNum(timestamp) + var/list/splits = text2numlist(timestamp, ":") + . = 0 + var/split_len = length(splits) + for(var/i = 1 to length(splits)) + switch(split_len - i) + if(2) + . += splits[i] HOURS + if(1) + . += splits[i] MINUTES + if(0) + . += splits[i] SECONDS + /* This is used for displaying the "station time" equivelent of a world.time value Calling it with no args will give you the current time, but you can specify a world.time-based value as an argument - You can use this, for example, to do "This will expire at [station_time_at(world.time + 500)]" to display a "station time" expiration date diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index c5a77880cf0..aedc16c4162 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1895,8 +1895,15 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) #undef DELTA_CALC -// This proc gets a list of all "points of interest" (poi's) that can be used by admins to track valuable mobs or atoms (such as the nuke disk). -/proc/getpois(mobs_only=0,skip_mindless=0) +/* + * This proc gets a list of all "points of interest" (poi's) that can be used by admins to track valuable mobs or atoms (such as the nuke disk). + * @param mobs_only if set to TRUE it won't include locations to the returned list + * @param skip_mindless if set to TRUE it will skip mindless mobs + * @param force_include_bots if set to TRUE it will include bots even if skip_mindless is set to TRUE + * @param force_include_cameras if set to TRUE it will include camera eyes even if skip_mindless is set to TRUE + * @return returns a list with the found points of interest +*/ +/proc/getpois(mobs_only = FALSE, skip_mindless = FALSE, force_include_bots = FALSE, force_include_cameras = FALSE) var/list/mobs = sortmobs() var/list/names = list() var/list/pois = list() @@ -1904,7 +1911,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) for(var/mob/M in mobs) if(skip_mindless && (!M.mind && !M.ckey)) - if(!isbot(M) && !istype(M, /mob/camera/)) + if(!(force_include_bots && isbot(M)) && !(force_include_cameras && istype(M, /mob/camera))) continue if(M.client && M.client.holder && M.client.holder.fakekey) //stealthmins continue diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index 1defb9a6afa..b2b09003934 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -31,7 +31,7 @@ /datum/proc/vv_get_var(var_name) switch(var_name) - if("attack_log", "debug_log") + if("attack_log_old", "debug_log") return debug_variable(var_name, vars[var_name], 0, src, sanitize = FALSE) if("vars") return debug_variable(var_name, list(), 0, src) diff --git a/code/datums/log_record.dm b/code/datums/log_record.dm new file mode 100644 index 00000000000..9b7d52b5390 --- /dev/null +++ b/code/datums/log_record.dm @@ -0,0 +1,37 @@ +/datum/log_record + var/log_type // Type of log + var/raw_time // When did this happen? + var/what // What happened + var/who // Who did it + var/target // Who/what was targeted (can be a string) + var/turf/where // Where did it happen + +/datum/log_record/New(_log_type, _who, _what, _target, _where, _raw_time) + log_type = _log_type + + who = get_subject_text(_who) + what = _what + target = get_subject_text(_target) + if(!_where) + _where = get_turf(_who) + where = _where + if(!_raw_time) + _raw_time = world.time + raw_time = _raw_time + +/datum/log_record/proc/get_subject_text(subject) + if(ismob(subject) || isclient(subject) || istype(subject, /datum/mind)) + return key_name_admin(subject) + if(isatom(subject)) + var/atom/A = subject + return A.name + if(istype(subject, /datum)) + var/datum/D = subject + return D.type + return subject + +/proc/compare_log_record(datum/log_record/A, datum/log_record/B) + var/time_diff = A.raw_time - B.raw_time + if(!time_diff) // Same time + return cmp_text_asc(A.log_type, B.log_type) + return time_diff diff --git a/code/datums/log_viewer.dm b/code/datums/log_viewer.dm new file mode 100644 index 00000000000..70f7d832924 --- /dev/null +++ b/code/datums/log_viewer.dm @@ -0,0 +1,232 @@ +#define ALL_LOGS list(ATTACK_LOG, DEFENSE_LOG, CONVERSION_LOG, SAY_LOG, EMOTE_LOG, MISC_LOG) + +/datum/log_viewer + var/time_from = 0 + var/time_to = 4 HOURS // 4 Hours should be enough. INFINITY would screw the UI up + var/list/selected_mobs = list() // The mobs in question + var/list/selected_log_types = list() // The log types being searched for + + var/list/log_records = list() // Found and sorted records + +/datum/log_viewer/proc/clear_all() + selected_mobs.Cut() + selected_log_types.Cut() + time_from = initial(time_from) + time_to = initial(time_to) + log_records.Cut() + return + +/datum/log_viewer/proc/search() + log_records.Cut() // Empty the old results + var/list/invalid_mobs = list() + for(var/i in selected_mobs) + var/mob/M = i + if(!M || QDELETED(M)) + invalid_mobs |= M + continue + for(var/log_type in selected_log_types) + var/list/logs = M.logs[log_type] + var/len_logs = length(logs) + if(len_logs) + var/start_index = get_earliest_log_index(logs) + if(!start_index) // No log found that matches the starting time criteria + continue + var/end_index = get_latest_log_index(logs) + if(!end_index) // No log found that matches the end time criteria + continue + log_records.Add(logs.Copy(start_index, end_index + 1)) + + if(invalid_mobs.len) + to_chat(usr, "The search criteria contained invalid mobs. They have been removed from the criteria.") + for(var/i in invalid_mobs) + selected_mobs -= i // Cleanup + + log_records = sortTim(log_records, /proc/compare_log_record) + +/** Binary search like implementation to find the earliest log + * Returns the index of the earliest log using the time_from value for the given list of logs. + * It will return 0 if no log after time_from is found +*/ +/datum/log_viewer/proc/get_earliest_log_index(list/logs) + if(!time_from) + return 1 + var/start = 1 + var/end = length(logs) + var/mid + do + mid = round_down((end + start) / 2) + var/datum/log_record/L = logs[mid] + if(L.raw_time >= time_from) + end = mid + else + start = mid + while(end - start > 1) + var/datum/log_record/L = logs[end] + if(L.raw_time >= time_from) // Check if there is atleast one valid log + return end + return 0 + +/** Binary search like implementation to find the latest log + * Returns the index of the latest log using the time_to value (1 second is added to prevent rounding weirdness) for the given list of logs. + * It will return 0 if no log before time_to + 10 is found +*/ +/datum/log_viewer/proc/get_latest_log_index(list/logs) + if(world.time < time_to) + return length(logs) + + var/end = length(logs) + var/start = 1 + var/mid + var/max_time = time_to + 10 + do + mid = round((end + start) / 2 + 0.5) + var/datum/log_record/L = logs[mid] + if(L.raw_time >= max_time) + end = mid + else + start = mid + while(end - start > 1) + var/datum/log_record/L = logs[start] + if(L.raw_time < max_time) // Check if there is atleast one valid log + return start + return 0 + +/datum/log_viewer/proc/add_mob(mob/user, mob/M) + if(!M || !user) + return + selected_mobs |= M + + show_ui(user) + +/datum/log_viewer/proc/show_ui(mob/user) + var/all_log_types = ALL_LOGS + var/trStyleTop = "border-top:2px solid; border-bottom:2px solid; padding-top: 5px; padding-bottom: 5px;" + var/trStyle = "border-top:1px solid; border-bottom:1px solid; padding-top: 5px; padding-bottom: 5px;" + var/dat + dat += "" + dat += "
" + dat += "Time Search Range: [gameTimestamp(wtime = time_from)]" + dat += " To: [gameTimestamp(wtime = time_to)]" + dat += "
" + + dat += "Mobs being used:" + for(var/i in selected_mobs) + var/mob/M = i + dat += "[M.name]" + dat += "Add Mob" + dat += "Clear All Mobs" + dat += "
" + + dat += "Log Types:" + for(var/i in all_log_types) + var/log_type = i + var/enabled = (log_type in selected_log_types) + var/text + var/style + if(enabled) + text = "[log_type]" + style = "background: [get_logtype_color(i)]" + else + text = log_type + + dat += "[text]" + + dat += "
" + dat += "Clear All Settings" + dat += "Search" + dat += "
" + + // Search results + var/tdStyleTime = "width:80px; text-align:center;" + var/tdStyleType = "width:80px; text-align:center;" + var/tdStyleWho = "width:300px; text-align:center;" + var/tdStyleWhere = "width:150px; text-align:center;" + dat += "
" + dat += "" + dat += "" + for(var/i in log_records) + var/datum/log_record/L = i + var/time = gameTimestamp(wtime = L.raw_time - 9.99) // The time rounds up for some reason. Will result in weird filtering results + + dat +="\ + \ + " + + dat += "
WhenTypeWhoWhatTargetWhere
[time][L.log_type][L.who][L.what][L.target][ADMIN_COORDJMP(L.where)]
" + dat += "
" + + var/datum/browser/popup = new(user, "Log viewer", "Log viewer", 1400, 600) + popup.set_content(dat) + popup.open() + +/datum/log_viewer/Topic(href, href_list) + if(href_list["start_time"]) + var/input = input(usr, "hh:mm:ss", "Start time", "00:00:00") as text|null + if(!input) + return + var/res = timeStampToNum(input) + if(res < 0) + to_chat(usr, "'[input]' is an invalid input value.") + return + time_from = res + show_ui(usr) + return + if(href_list["end_time"]) + var/input = input(usr, "hh:mm:ss", "End time", "04:00:00") as text|null + if(!input) + return + var/res = timeStampToNum(input) + if(res < 0) + to_chat(usr, "'[input]' is an invalid input value.") + return + time_to = res + + show_ui(usr) + return + if(href_list["search"]) + search(usr) + show_ui(usr) + return + if(href_list["clear_all"]) + clear_all(usr) + show_ui(usr) + return + if(href_list["clear_mobs"]) + selected_mobs.Cut() + show_ui(usr) + return + if(href_list["add_mob"]) + var/list/mobs = getpois(TRUE, TRUE) + var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a mob: ", mobs) + A.on_close(CALLBACK(src, .proc/add_mob, usr)) + return + if(href_list["remove_mob"]) + var/mob/M = locate(href_list["remove_mob"]) + if(M) + selected_mobs -= M + show_ui(usr) + return + if(href_list["toggle_log_type"]) + var/log_type = href_list["toggle_log_type"] + if(log_type in selected_log_types) + selected_log_types -= log_type + else + selected_log_types += log_type + show_ui(usr) + return + +/datum/log_viewer/proc/get_logtype_color(log_type) + switch(log_type) + if(ATTACK_LOG) + return "darkred" + if(DEFENSE_LOG) + return "chocolate" + if(CONVERSION_LOG) + return "indigo" + if(SAY_LOG) + return "teal" + if(EMOTE_LOG) + return "deepskyblue" + if(MISC_LOG) + return "gray" + return "slategray" diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 4f176970a92..135114c11f9 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -105,6 +105,14 @@ current.mind = null leave_all_huds() //leave all the huds in the old body, so it won't get huds if somebody else enters it + for(var/log_type in current.logs) // Copy the old logs + var/list/logs = current.logs[log_type] + if(new_character.logs[log_type]) + new_character.logs[log_type] += logs.Copy() // Append the old ones + new_character.logs[log_type] = sortTim(new_character.logs[log_type], /proc/compare_log_record) // Sort them on time + else + new_character.logs[log_type] = logs.Copy() // Just copy them + SSnanoui.user_transferred(current, new_character) if(new_character.mind) //remove any mind currently in our new body's mind variable diff --git a/code/datums/spell.dm b/code/datums/spell.dm index 6b1865cf73d..30347d1e796 100644 --- a/code/datums/spell.dm +++ b/code/datums/spell.dm @@ -241,7 +241,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) before_cast(targets) invocation() if(user && user.ckey) - user.create_attack_log("[key_name(user)] cast the spell [name].") + add_attack_logs(user, null, "cast the spell [name]") spawn(0) if(charge_type == "recharge" && recharge) start_recharge() diff --git a/code/datums/spells/mime.dm b/code/datums/spells/mime.dm index 31066001270..d0c052649d9 100644 --- a/code/datums/spells/mime.dm +++ b/code/datums/spells/mime.dm @@ -141,6 +141,7 @@ else user.mind.AddSpell(S) to_chat(user, "You flip through the pages. Your understanding of the boundaries of reality increases. You can cast [spellname]!") + user.create_log(MISC_LOG, "learned the spell [spellname] ([S])") user.create_attack_log("[key_name(user)] learned the spell [spellname] ([S]).") onlearned(user) @@ -164,4 +165,4 @@ /obj/item/spellbook/oneuse/mime/greaterwall spell = /obj/effect/proc_holder/spell/targeted/forcewall/mime spellname = "Invisible Greater Wall" - desc = "It contains illustrations of the great walls of human history." + desc = "It contains illustrations of the great walls of human history." \ No newline at end of file diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index ffe67595f11..1a9dd15c65e 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -189,6 +189,7 @@ GLOBAL_LIST_EMPTY(all_cults) C.Grant(cult_mind.current) SEND_SOUND(cult_mind.current, 'sound/ambience/antag/bloodcult.ogg') cult_mind.current.create_attack_log("Has been converted to the cult!") + cult_mind.current.create_log(CONVERSION_LOG, "converted to the cult") if(jobban_isbanned(cult_mind.current, ROLE_CULTIST) || jobban_isbanned(cult_mind.current, ROLE_SYNDICATE)) replace_jobbanned_player(cult_mind.current, ROLE_CULTIST) update_cult_icons_added(cult_mind) diff --git a/code/game/gamemodes/miniantags/abduction/abduction.dm b/code/game/gamemodes/miniantags/abduction/abduction.dm index 6914ae55363..1888d0e4a1d 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction.dm @@ -267,6 +267,7 @@ SSticker.mode.abductors -= abductor_mind abductor_mind.special_role = null abductor_mind.current.create_attack_log("No longer abductor") + abductor_mind.current.create_log(CONVERSION_LOG, "No longer abductor") if(issilicon(abductor_mind.current)) to_chat(abductor_mind.current, "You have been turned into a robot! You are no longer an abductor.") else @@ -281,4 +282,4 @@ /datum/game_mode/proc/update_abductor_icons_removed(datum/mind/alien_mind) var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_ABDUCTOR] hud.leave_hud(alien_mind.current) - set_antag_hud(alien_mind.current, null) + set_antag_hud(alien_mind.current, null) \ No newline at end of file diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index 8fd5da01a2c..ad1146c0bc8 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -68,6 +68,7 @@ proc/issyndicate(mob/living/M as mob) for(var/datum/objective/nuclear/O in operative_mind.objectives) operative_mind.objectives -= O operative_mind.current.create_attack_log("No longer nuclear operative") + operative_mind.current.create_log(CONVERSION_LOG, "No longer nuclear operative") if(issilicon(operative_mind.current)) to_chat(operative_mind.current, "You have been turned into a robot! You are no longer a Syndicate operative.") else diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index f7da210b4a9..00ce24b6c46 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -244,6 +244,7 @@ rev_mind.current.Stun(5) to_chat(rev_mind.current, " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!") rev_mind.current.create_attack_log("Has been converted to the revolution!") + rev_mind.current.create_log(CONVERSION_LOG, "converted to the revolution") rev_mind.special_role = SPECIAL_ROLE_REV update_rev_icons_added(rev_mind) if(jobban_isbanned(rev_mind.current, ROLE_REV) || jobban_isbanned(rev_mind.current, ROLE_SYNDICATE)) @@ -262,7 +263,7 @@ revolutionaries -= rev_mind rev_mind.special_role = null rev_mind.current.create_attack_log("Has renounced the revolution!") - + rev_mind.current.create_log(CONVERSION_LOG, "renounced the revolution") if(beingborged) to_chat(rev_mind.current, "The frame's firmware detects and deletes your neural reprogramming! You remember nothing[remove_head ? "." : " but the name of the one who flashed you."]") message_admins("[key_name_admin(rev_mind.current)] [ADMIN_QUE(rev_mind.current,"?")] ([ADMIN_FLW(rev_mind.current,"FLW")]) has been borged while being a [remove_head ? "leader" : " member"] of the revolution.") diff --git a/code/game/gamemodes/shadowling/shadowling.dm b/code/game/gamemodes/shadowling/shadowling.dm index d190009dbb2..9cd91817af4 100644 --- a/code/game/gamemodes/shadowling/shadowling.dm +++ b/code/game/gamemodes/shadowling/shadowling.dm @@ -158,6 +158,7 @@ Made by Xhuis new_thrall_mind.special_role = SPECIAL_ROLE_SHADOWLING_THRALL update_shadow_icons_added(new_thrall_mind) new_thrall_mind.current.create_attack_log("Became a thrall") + new_thrall_mind.current.create_log(CONVERSION_LOG, "Became a thrall") new_thrall_mind.current.add_language("Shadowling Hivemind") new_thrall_mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/lesser_shadow_walk(null)) new_thrall_mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadow_vision/thrall(null)) @@ -179,6 +180,7 @@ Made by Xhuis return 0 //If there is no mind, the mind isn't a thrall, or the mind's mob isn't alive, return shadowling_thralls.Remove(thrall_mind) thrall_mind.current.create_attack_log("Dethralled") + thrall_mind.current.create_log(CONVERSION_LOG, "Dethralled") thrall_mind.special_role = null update_shadow_icons_removed(thrall_mind) for(var/obj/effect/proc_holder/spell/S in thrall_mind.spell_list) @@ -236,6 +238,7 @@ Made by Xhuis update_shadow_icons_removed(ling_mind) shadows.Remove(ling_mind) ling_mind.current.create_attack_log("Deshadowlinged") + ling_mind.current.create_log(CONVERSION_LOG, "Deshadowlinged") ling_mind.special_role = null for(var/obj/effect/proc_holder/spell/S in ling_mind.spell_list) ling_mind.RemoveSpell(S) diff --git a/code/game/gamemodes/vampire/vampire.dm b/code/game/gamemodes/vampire/vampire.dm index e0ea19b2f0a..6c22313ef6e 100644 --- a/code/game/gamemodes/vampire/vampire.dm +++ b/code/game/gamemodes/vampire/vampire.dm @@ -348,6 +348,7 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha SSticker.mode.vampires -= vampire_mind vampire_mind.special_role = null vampire_mind.current.create_attack_log("De-vampired") + vampire_mind.current.create_log(CONVERSION_LOG, "De-vampired") if(vampire_mind.vampire) vampire_mind.vampire.remove_vampire_powers() QDEL_NULL(vampire_mind.vampire) diff --git a/code/game/gamemodes/wizard/rightandwrong.dm b/code/game/gamemodes/wizard/rightandwrong.dm index 4f2d20b373a..3dbd9211399 100644 --- a/code/game/gamemodes/wizard/rightandwrong.dm +++ b/code/game/gamemodes/wizard/rightandwrong.dm @@ -109,6 +109,7 @@ GLOBAL_VAR_INIT(summon_magic_triggered, FALSE) H.mind.add_antag_datum(/datum/antagonist/survivalist/guns) H.create_attack_log("was made into a survivalist, and trusts no one!") + H.create_log(CONVERSION_LOG, "was made into a survivalist") var/gun_type = pick(GLOB.summoned_guns) var/obj/item/gun/G = new gun_type(get_turf(H)) @@ -130,6 +131,7 @@ GLOBAL_VAR_INIT(summon_magic_triggered, FALSE) H.mind.add_antag_datum(/datum/antagonist/survivalist/magic) H.create_attack_log("was made into a survivalist, and trusts no one!") + H.create_log(CONVERSION_LOG, "was made into a survivalist") var/magic_type = pick(GLOB.summoned_magic) var/lucky = FALSE @@ -166,4 +168,4 @@ GLOBAL_VAR_INIT(summon_magic_triggered, FALSE) if(summon_type == SUMMON_MAGIC) give_magic(H) else - give_guns(H) + give_guns(H) \ No newline at end of file diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm index cf0ce4843f0..634fc8f5a38 100644 --- a/code/game/gamemodes/wizard/soulstone.dm +++ b/code/game/gamemodes/wizard/soulstone.dm @@ -69,9 +69,6 @@ to_chat(user, "\"Come now, do not capture your fellow's soul.\"") return ..() - M.create_attack_log("Has had their soul captured with [src.name] by [key_name(user)]") - user.create_attack_log("Used the [src.name] to capture the soul of [key_name(M)]") - if(optional) if(!M.ckey) to_chat(user, "They have no soul!") @@ -353,7 +350,7 @@ /obj/item/soulstone/proc/get_shade_type() return /mob/living/simple_animal/shade/cult - + /obj/item/soulstone/anybody/get_shade_type() return /mob/living/simple_animal/shade diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm index f97f14a9975..0dffc0e6159 100644 --- a/code/game/gamemodes/wizard/spellbook.dm +++ b/code/game/gamemodes/wizard/spellbook.dm @@ -837,6 +837,7 @@ else user.mind.AddSpell(S) to_chat(user, "you rapidly read through the arcane book. Suddenly you realize you understand [spellname]!") + user.create_log(MISC_LOG, "learned the spell [spellname] ([S])") user.create_attack_log("[key_name(user)] learned the spell [spellname] ([S]).") onlearned(user) diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm index 84f5b665e4f..3d07ae173db 100644 --- a/code/game/gamemodes/wizard/wizard.dm +++ b/code/game/gamemodes/wizard/wizard.dm @@ -59,6 +59,7 @@ SSticker.mode.wizards -= wizard_mind wizard_mind.special_role = null wizard_mind.current.create_attack_log("De-wizarded") + wizard_mind.current.create_log(CONVERSION_LOG, "De-wizarded") wizard_mind.current.spellremove(wizard_mind.current) wizard_mind.current.faction = list("Station") if(issilicon(wizard_mind.current)) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index cc76c2ab86f..bf83e6c5d49 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -276,6 +276,7 @@ About the new airlock wires panel: if(usr) shockedby += text("\[[time_stamp()]\] - [usr](ckey:[usr.ckey])") usr.create_attack_log("Electrified the [name] at [x] [y] [z]") + add_attack_logs(usr, src, "Electrified") else shockedby += text("\[[time_stamp()]\] - EMP)") message = "The door is now electrified [duration == -1 ? "permanently" : "for [duration] second\s"]." @@ -763,6 +764,7 @@ About the new airlock wires panel: else if(activate) //electrify door for 30 seconds shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") usr.create_attack_log("Electrified the [name] at [x] [y] [z]") + add_attack_logs(usr, src, "Electrified") to_chat(usr, "The door is now electrified for thirty seconds.") electrify(30) if("electrify_permanently") @@ -774,6 +776,7 @@ About the new airlock wires panel: else if(activate) shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") usr.create_attack_log("Electrified the [name] at [x] [y] [z]") + add_attack_logs(usr, src, "Electrified") to_chat(usr, "The door is now electrified.") electrify(-1) if("open") diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index cc58d7efe7e..ec6f2c6456e 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -559,6 +559,7 @@ animal_damage = user.obj_damage animal_damage = min(animal_damage, 20*user.environment_smash) user.create_attack_log("attacked [name]") + add_attack_logs(user, src, "Attacked") attack_generic(user, animal_damage, user.melee_damage_type, "melee", play_soundeffect) return TRUE diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm index 21b71e7668c..095b1ea9456 100644 --- a/code/game/objects/items/devices/laserpointer.dm +++ b/code/game/objects/items/devices/laserpointer.dm @@ -138,6 +138,7 @@ log_admin("[key_name(user)] EMPd a camera with a laser pointer") user.create_attack_log("[key_name(user)] EMPd a camera with a laser pointer") + add_attack_logs(user, C, "EMPd with [src]") else outmsg = "You missed the lens of [C] with [src]." diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index ec197c61651..34929b52f53 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -26,6 +26,7 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list( /client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/ /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/ /client/proc/cmd_admin_check_contents, /*displays the contents of an instance*/ + /client/proc/cmd_admin_open_logging_view, /client/proc/getserverlogs, /*allows us to fetch server logs (diary) for other days*/ /client/proc/jumptocoord, /*we ghost and jump to a coordinate*/ /client/proc/Getmob, /*teleports a mob to our location*/ diff --git a/code/modules/admin/verbs/logging_view.dm b/code/modules/admin/verbs/logging_view.dm new file mode 100644 index 00000000000..2a222e21dbd --- /dev/null +++ b/code/modules/admin/verbs/logging_view.dm @@ -0,0 +1,10 @@ +GLOBAL_LIST_INIT(open_logging_views, list()) + +/client/proc/cmd_admin_open_logging_view() + set category = "Admin" + set name = "Open Logging View" + set desc = "Opens the detailed logging viewer" + + if(!GLOB.open_logging_views[usr.client.ckey]) + GLOB.open_logging_views[usr.client.ckey] = new /datum/log_viewer() + GLOB.open_logging_views[usr.client.ckey].show_ui(usr) \ No newline at end of file diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 8c68d73a390..a8136655d8f 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -885,7 +885,7 @@ Traitors and the like can also be revived with the previous role mostly intact. return to_chat(usr, text("Attack Log for []", mob)) - for(var/t in M.attack_log) + for(var/t in M.attack_log_old) to_chat(usr, t) feedback_add_details("admin_verb","ATTL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm index e0f456facec..7d17ac07720 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm @@ -285,6 +285,7 @@ else //this looks ugly but it's better than a copy-pasted startgibbing proc override occupant.create_attack_log("Was gibbed by an autogibber (\the [src])") + add_attack_logs(src, occupant, "gibbed") occupant.emote("scream") playsound(get_turf(src), 'sound/goonstation/effects/gib.ogg', 50, 1) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 0c283c60a7f..548d22a3fa3 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -53,7 +53,8 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) var/turf/T if(ismob(body)) T = get_turf(body) //Where is the body located? - attack_log = body.attack_log //preserve our attack logs by copying them to our ghost + attack_log_old = body.attack_log_old //preserve our attack logs by copying them to our ghost + logs = body.logs.Copy() var/mutable_appearance/MA = copy_appearance(body) if(body.mind && body.mind.name) @@ -431,7 +432,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp set name = "Orbit" // "Haunt" set desc = "Follow and orbit a mob." - var/list/mobs = getpois(skip_mindless=1) + var/list/mobs = getpois(FALSE, TRUE, TRUE, TRUE) var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a mob: ", mobs) A.on_close(CALLBACK(src, .proc/ManualFollow)) @@ -506,7 +507,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp set desc = "Teleport to a mob" if(isobserver(usr)) //Make sure they're an observer! - var/list/dest = getpois(mobs_only=1) //Fill list, prompt user with list + var/list/dest = getpois(mobs_only=TRUE) //Fill list, prompt user with list var/datum/async_input/A = input_autocomplete_async(usr, "Enter a mob name: ", dest) A.on_close(CALLBACK(src, .proc/jump_to_mob)) diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index 57232d74f44..5f18a27adc9 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -59,6 +59,7 @@ var/mob/living/L = src L.say_log += "EMOTE: [input]" //say log too so it is easier on admins instead of having to merge the two with timestamps etc L.emote_log += input //emote only log if an admin wants to search just for emotes they don't have to sift through the say + create_log(EMOTE_LOG, input) // TODO after #13047: Include the channel // Hearing gasp and such every five seconds is not good emotes were not global for a reason. // Maybe some people are okay with that. for(var/mob/M in GLOB.player_list) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 0f6ba5df181..30e10553d3e 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -247,6 +247,7 @@ set hidden = 1 if(InCritical()) create_attack_log("[src] has ["succumbed to death"] with [round(health, 0.1)] points of health!") + create_log(MISC_LOG, "has succumbed to death with [round(health, 0.1)] points of health") adjustOxyLoss(health - HEALTH_THRESHOLD_DEAD) // super check for weird mobs, including ones that adjust hp // we don't want to go overboard and gib them, though diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 782808f2190..eeaed10b9c5 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -276,6 +276,7 @@ proc/get_radio_key_from_channel(var/channel) //Log of what we've said, plain message, no spans or junk say_log += message + create_log(SAY_LOG, message) // TODO after #13047: Include the channel log_say(message, src) return 1 @@ -361,6 +362,7 @@ proc/get_radio_key_from_channel(var/channel) say_log += "whisper: [message]" log_whisper(message, src) + create_log(SAY_LOG, "WHISPER: [message]") var/message_range = 1 var/eavesdropping_range = 2 var/watching_range = 5 diff --git a/code/modules/mob/living/silicon/robot/update_status.dm b/code/modules/mob/living/silicon/robot/update_status.dm index 63c13f97d24..5f1519395c7 100644 --- a/code/modules/mob/living/silicon/robot/update_status.dm +++ b/code/modules/mob/living/silicon/robot/update_status.dm @@ -30,6 +30,7 @@ to_chat(ghost, "Your cyborg shell has been repaired, re-enter if you want to continue! (Verbs -> Ghost -> Re-enter corpse)") ghost << sound('sound/effects/genetics.ogg') create_attack_log("revived, trigger reason: [reason]") + create_log(MISC_LOG, "revived, trigger reason: [reason]") // diag_hud_set_status() // diag_hud_set_health() // update_health_hud() diff --git a/code/modules/mob/living/stat_states.dm b/code/modules/mob/living/stat_states.dm index 57fd6e17cbc..9ff67107abd 100644 --- a/code/modules/mob/living/stat_states.dm +++ b/code/modules/mob/living/stat_states.dm @@ -7,6 +7,7 @@ else if(stat == UNCONSCIOUS) return 0 create_attack_log("Fallen unconscious at [atom_loc_line(get_turf(src))]") + add_attack_logs(src, null, "Fallen unconscious") log_game("[key_name(src)] fell unconscious at [atom_loc_line(get_turf(src))]") stat = UNCONSCIOUS if(updating) @@ -22,6 +23,7 @@ else if(stat == CONSCIOUS) return 0 create_attack_log("Woken up at [atom_loc_line(get_turf(src))]") + add_attack_logs(src, null, "Woken up") log_game("[key_name(src)] woke up at [atom_loc_line(get_turf(src))]") stat = CONSCIOUS if(updating) @@ -45,6 +47,7 @@ if(!can_be_revived()) return 0 create_attack_log("Came back to life at [atom_loc_line(get_turf(src))]") + add_attack_logs(src, null, "Came back to life") log_game("[key_name(src)] came back to life at [atom_loc_line(get_turf(src))]") stat = CONSCIOUS GLOB.dead_mob_list -= src @@ -73,4 +76,4 @@ return 1 /mob/living/proc/check_death_method() - return TRUE + return TRUE \ No newline at end of file diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 04dfa081be1..5345bda5b94 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -5,6 +5,7 @@ computer_id = client.computer_id log_access_in(client) create_attack_log("Logged in at [atom_loc_line(get_turf(src))]") + create_log(MISC_LOG, "Logged in") if(config.log_access) for(var/mob/M in GLOB.player_list) if(M == src) continue diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index bbf477fa1a0..8bdb0d89332 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -4,6 +4,7 @@ GLOB.player_list -= src log_access_out(src) create_attack_log("Logged out at [atom_loc_line(get_turf(src))]") + create_log(MISC_LOG, "Logged out") // `holder` is nil'd out by now, so we check the `admin_datums` array directly //Only report this stuff if we are currently playing. if(GLOB.admin_datums[ckey] && SSticker && SSticker.current_state == GAME_STATE_PLAYING) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 7d1edef93cf..d93b2723024 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -22,6 +22,7 @@ for(var/datum/alternate_appearance/AA in viewing_alternate_appearances) AA.viewers -= src viewing_alternate_appearances = null + logs.Cut() ..() return QDEL_HINT_HARDDEL @@ -1257,14 +1258,20 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \ return list() //must return list or IGNORE_ACCESS /mob/proc/create_attack_log(text, collapse = TRUE) - LAZYINITLIST(attack_log) - create_log_in_list(attack_log, text, collapse, last_log) + LAZYINITLIST(attack_log_old) + create_log_in_list(attack_log_old, text, collapse, last_log) last_log = world.timeofday /mob/proc/create_debug_log(text, collapse = TRUE) LAZYINITLIST(debug_log) create_log_in_list(debug_log, text, collapse, world.timeofday) +/mob/proc/create_log(log_type, what, target = null, turf/where = get_turf(src)) + LAZYINITLIST(logs[log_type]) + var/list/log_list = logs[log_type] + var/datum/log_record/record = new(log_type, src, what, target, where, world.time) + log_list.Add(record) + /proc/create_log_in_list(list/target, text, collapse = TRUE, last_log)//forgive me code gods for this shitcode proc //this proc enables lovely stuff like an attack log that looks like this: "[18:20:29-18:20:45]21x John Smith attacked Andrew Jackson with a crowbar." //That makes the logs easier to read, but because all of this is stored in strings, weird things have to be used to get it all out. @@ -1406,4 +1413,4 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \ ///Force set the mob nutrition /mob/proc/set_nutrition(change) - nutrition = max(0, change) + nutrition = max(0, change) \ No newline at end of file diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 5d63b7b4ad9..2f074aeaa9f 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -33,8 +33,11 @@ var/computer_id = null var/lastattacker = null var/lastattacked = null - var/list/attack_log = list( ) + var/list/attack_log_old = list( ) var/list/debug_log = null + + var/list/logs = list() // Logs for each log type defined in __DEFINES/logs.dm + var/last_log = 0 var/obj/machinery/machine = null var/other_mobs = null @@ -208,4 +211,4 @@ var/list/tkgrabbed_objects = list() // Assoc list of items to TK grabs var/forced_look = null // This can either be a numerical direction or a soft object reference (UID). It makes the mob always face towards the selected thing. - var/registered_z + var/registered_z \ No newline at end of file diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm index 544ca67a361..68a7e7275a7 100644 --- a/code/modules/projectiles/guns/magic/wand.dm +++ b/code/modules/projectiles/guns/magic/wand.dm @@ -51,6 +51,7 @@ user.visible_message("[user] zaps [user.p_them()]self with [src].") playsound(user, fire_sound, 50, 1) user.create_attack_log("[key_name(user)] zapped [user.p_them()]self with a [src]") + add_attack_logs(null, user, "zapped [user.p_them()]self with a [src]") ///////////////////////////////////// //WAND OF DEATH diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index d15e6c1375d..6c464a9dca1 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -263,12 +263,14 @@ return M.create_attack_log("[key_name(M)] became [new_mob.real_name].") - new_mob.attack_log = M.attack_log + add_attack_logs(null, M, "became [new_mob.real_name]") new_mob.a_intent = INTENT_HARM if(M.mind) M.mind.transfer_to(new_mob) else + new_mob.attack_log_old = M.attack_log_old.Copy() + new_mob.logs = M.logs.Copy() new_mob.key = M.key to_chat(new_mob, "Your form morphs into that of a [randomize].") @@ -344,4 +346,4 @@ to_chat(target, "You get splatted by [src].") M.Weaken(slip_weaken) M.Stun(slip_stun) - . = ..() + . = ..() \ No newline at end of file diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index 194675a21d8..48c35415387 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -16,7 +16,7 @@ . = ..() if(. && obj_integrity > 0) if(tank_volume && (damage_flag == "bullet" || damage_flag == "laser")) - boom() + boom(FALSE, TRUE) /obj/structure/reagent_dispensers/attackby(obj/item/I, mob/user, params) if(I.is_refillable()) @@ -43,7 +43,7 @@ /obj/structure/reagent_dispensers/deconstruct(disassembled = TRUE) if(!(flags & NODECONSTRUCT)) if(!disassembled) - boom() + boom(FALSE, TRUE) else qdel(src) @@ -85,15 +85,19 @@ if(!QDELETED(src)) //wasn't deleted by the projectile's effects. if(!P.nodamage && ((P.damage_type == BURN) || (P.damage_type == BRUTE))) message_admins("[key_name_admin(P.firer)] triggered a fueltank explosion with [P.name] at [COORD(loc)] ") + add_attack_logs(P.firer, src, "shot with [P.name]") log_game("[key_name(P.firer)] triggered a fueltank explosion with [P.name] at [COORD(loc)]") investigate_log("[key_name(P.firer)] triggered a fueltank explosion with [P.name] at [COORD(loc)]", INVESTIGATE_BOMB) boom() -/obj/structure/reagent_dispensers/fueltank/boom(rigtrigger = FALSE) // Prevent case where someone who rigged the tank is blamed for the explosion when the rig isn't what triggered the explosion +/obj/structure/reagent_dispensers/fueltank/boom(rigtrigger = FALSE, log_attack = FALSE) // Prevent case where someone who rigged the tank is blamed for the explosion when the rig isn't what triggered the explosion if(rigtrigger) // If the explosion is triggered by an assembly holder message_admins("A fueltank, last rigged by [lastrigger], was triggered at [COORD(loc)]") // Then admin is informed of the last person who rigged the fuel tank log_game("A fueltank, last rigged by [lastrigger], triggered at [COORD(loc)]") + add_attack_logs(lastrigger, src, "rigged fuel tank exploded") investigate_log("A fueltank, last rigged by [lastrigger], triggered at [COORD(loc)]", INVESTIGATE_BOMB) + if(log_attack) + add_attack_logs(usr, src, "blew up", ATKLOG_FEW) if(reagents) reagents.set_reagent_temp(1000) //uh-oh qdel(src) @@ -140,6 +144,7 @@ if(istype(H.a_left, /obj/item/assembly/igniter) || istype(H.a_right, /obj/item/assembly/igniter)) msg_admin_attack("[key_name_admin(user)] rigged [src.name] with [I.name] for explosion (JMP)", ATKLOG_FEW) log_game("[key_name(user)] rigged [src.name] with [I.name] for explosion at [COORD(loc)]") + add_attack_logs(user, src, "rigged fuel tank") investigate_log("[key_name(user)] rigged [src.name] with [I.name] for explosion at [COORD(loc)]", INVESTIGATE_BOMB) lastrigger = "[key_name(user)]" @@ -163,6 +168,7 @@ obj/structure/reagent_dispensers/fueltank/welder_act(mob/user, obj/item/I) user.visible_message("[user] catastrophically fails at refilling [user.p_their()] [I]!", "That was stupid of you.") message_admins("[key_name_admin(user)] triggered a fueltank explosion at [COORD(loc)]") log_game("[key_name(user)] triggered a fueltank explosion at [COORD(loc)]") + add_attack_logs(user, src, "hit with lit welder") investigate_log("[key_name(user)] triggered a fueltank explosion at [COORD(loc)]", INVESTIGATE_BOMB) boom() else diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index 7d2be6953c6..92245d83acb 100755 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -221,6 +221,7 @@ user.visible_message("[user] wraps [target].") user.create_attack_log("Has used [name] on [target]") + add_attack_logs(user, target, "used [name]") if(amount <= 0 && !src.loc) //if we used our last wrapping paper, drop a cardboard tube new /obj/item/c_tube( get_turf(user) ) diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm index 5b0be539578..7b154ef2a25 100644 --- a/code/modules/spacepods/spacepod.dm +++ b/code/modules/spacepods/spacepod.dm @@ -216,6 +216,7 @@ deal_damage(damage) visible_message("[user] [user.attacktext] [src]!") user.create_attack_log("attacked [src.name]") + add_attack_logs(user, src, "attacked") return TRUE /obj/spacepod/attack_alien(mob/user) diff --git a/code/modules/tram/tram.dm b/code/modules/tram/tram.dm index 109e820345b..ea43f3fd659 100644 --- a/code/modules/tram/tram.dm +++ b/code/modules/tram/tram.dm @@ -243,6 +243,7 @@ qdel(src) src.visible_message("[M] has [M.attacktext] [src]!") M.create_attack_log("attacked [src.name]") + add_attack_logs(M, src, "attacked") /obj/tram/bullet_act(var/obj/item/projectile/proj) if(prob(proj.damage)) diff --git a/paradise.dme b/paradise.dme index 952567f4894..5ef4e7db728 100644 --- a/paradise.dme +++ b/paradise.dme @@ -46,6 +46,7 @@ #include "code\__DEFINES\language.dm" #include "code\__DEFINES\layers.dm" #include "code\__DEFINES\lighting.dm" +#include "code\__DEFINES\logs.dm" #include "code\__DEFINES\machines.dm" #include "code\__DEFINES\math.dm" #include "code\__DEFINES\MC.dm" @@ -263,6 +264,8 @@ #include "code\datums\gas_mixture.dm" #include "code\datums\holocall.dm" #include "code\datums\hud.dm" +#include "code\datums\log_record.dm" +#include "code\datums\log_viewer.dm" #include "code\datums\mind.dm" #include "code\datums\mixed.dm" #include "code\datums\mutable_appearance.dm" @@ -1213,6 +1216,7 @@ #include "code\modules\admin\verbs\gimmick_team.dm" #include "code\modules\admin\verbs\honksquad.dm" #include "code\modules\admin\verbs\infiltratorteam_syndicate.dm" +#include "code\modules\admin\verbs\logging_view.dm" #include "code\modules\admin\verbs\map_template_loadverb.dm" #include "code\modules\admin\verbs\mapping.dm" #include "code\modules\admin\verbs\massmodvar.dm" From bdafa40b406b5049d17f15a66ea932e7c22bdf8f Mon Sep 17 00:00:00 2001 From: Mitchs98 Date: Sat, 21 Mar 2020 19:14:54 -0500 Subject: [PATCH 09/10] Advanced Scanner print-outs now show if an organ is dead. (#13138) * Diagnosis * minor oopsie * fixes the fix --- code/game/machinery/adv_med.dm | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index 6b6f3087ad2..9d96ec94b99 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -425,6 +425,7 @@ var/AN = "" var/open = "" var/infected = "" + var/dead = "" var/robot = "" var/imp = "" var/bled = "" @@ -439,6 +440,8 @@ splint = "Splinted:" if(e.status & ORGAN_BROKEN) AN = "[e.broken_description]:" + if(e.status & ORGAN_DEAD) + dead = "DEAD:" if(e.is_robotic()) robot = "Robotic:" if(e.open) @@ -454,9 +457,9 @@ infected = "Acute Infection:" if(INFECTION_LEVEL_TWO + 200 to INFECTION_LEVEL_TWO + 300) infected = "Acute Infection+:" - if(INFECTION_LEVEL_TWO + 300 to INFECTION_LEVEL_TWO + 400) + if(INFECTION_LEVEL_TWO + 300 to INFECTION_LEVEL_TWO + 399) infected = "Acute Infection++:" - if(INFECTION_LEVEL_THREE to INFINITY) + if(INFECTION_LEVEL_TWO + 400 to INFINITY) infected = "Septic:" var/unknown_body = 0 @@ -467,11 +470,14 @@ imp += "Unknown body present:" if(!AN && !open && !infected & !imp) AN = "None:" - dat += "[e.name][e.burn_dam][e.brute_dam][robot][bled][AN][splint][open][infected][imp][internal_bleeding][lung_ruptured]" + dat += "[e.name][e.burn_dam][e.brute_dam][robot][bled][AN][splint][open][infected][imp][internal_bleeding][lung_ruptured][dead]" dat += "" for(var/obj/item/organ/internal/i in occupant.internal_organs) var/mech = i.desc var/infection = "None" + var/dead = "" + if(i.status & ORGAN_DEAD) + dead = "DEAD:" switch(i.germ_level) if(1 to INFECTION_LEVEL_ONE + 200) infection = "Mild Infection:" @@ -483,11 +489,13 @@ infection = "Acute Infection:" if(INFECTION_LEVEL_TWO + 200 to INFECTION_LEVEL_TWO + 300) infection = "Acute Infection+:" - if(INFECTION_LEVEL_TWO + 300 to INFINITY) + if(INFECTION_LEVEL_TWO + 300 to INFECTION_LEVEL_TWO + 399) infection = "Acute Infection++:" + if(INFECTION_LEVEL_TWO + 400 to INFINITY) + infection = "Septic:" dat += "" - dat += "[i.name]N/A[i.damage][infection]:[mech]" + dat += "[i.name]N/A[i.damage][infection]:[mech][dead]" dat += "" dat += "" if(occupant.disabilities & BLIND) From 63ea21b20e16a1476c7eaf43c69bf6c8a14a937b Mon Sep 17 00:00:00 2001 From: SteelSlayer <42044220+SteelSlayer@users.noreply.github.com> Date: Sat, 21 Mar 2020 19:21:12 -0500 Subject: [PATCH 10/10] Adds support for multiple callback checks for the do_after and incapacitated procs (#13057) * do_after refactor Update slime.dm fix a comment if you read this you get a cookie! * some little tweaks Changes a TRUE to FALSE in a comment, got confused there. Removes the code that checks for non-callbacks in the extra_checks list and removes them. It should be up to the dev to not add non-callbacks to this list. I'd rather have it runtime instead * check_for_true_callbacks * CRLF to LF Co-authored-by: SteelSlayer --- code/__HELPERS/mobs.dm | 53 ++++++++++++++----- code/game/machinery/doors/airlock.dm | 4 +- .../objects/items/tools/tool_behaviour.dm | 4 +- code/game/objects/structures/window.dm | 10 ++-- .../mob/living/carbon/human/species/slime.dm | 8 +-- code/modules/mob/living/update_status.dm | 10 +++- code/modules/mob/status_procs.dm | 3 ++ 7 files changed, 64 insertions(+), 28 deletions(-) diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 6be2b411376..ff127fd3a48 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -355,24 +355,35 @@ This is always put in the attack log. if(progress) qdel(progbar) -/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null) +/* Use this proc when you want to have code under it execute after a delay, and ensure certain conditions are met during that delay... + * Such as the user not being interrupted via getting stunned or by moving off the tile they're currently on. + * + * Example usage: + * + * if(do_after(user, 50, target = sometarget, extra_checks = list(callback_check1, callback_check2))) + * do_stuff() + * + * This will create progress bar that lasts for 5 seconds. If the user doesn't move or otherwise do something that would cause the checks to fail in those 5 seconds, do_stuff() would execute. + * The Proc returns TRUE upon success (the progress bar reached the end), or FALSE upon failure (the user moved or some other check failed) + */ +/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1, list/extra_checks = list(), use_default_checks = TRUE) if(!user) - return 0 + return FALSE var/atom/Tloc = null if(target) Tloc = target.loc var/atom/Uloc = user.loc - var/drifting = 0 + var/drifting = FALSE if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 + drifting = TRUE var/holding = user.get_active_hand() - var/holdingnull = 1 //User's hand started out empty, check for an empty hand + var/holdingnull = TRUE //User's hand started out empty, check for an empty hand if(holding) - holdingnull = 0 //Users hand started holding something, check to see if it's still holding that + holdingnull = FALSE //Users hand started holding something, check to see if it's still holding that var/datum/progressbar/progbar if(progress) @@ -380,22 +391,29 @@ This is always put in the attack log. var/endtime = world.time + delay var/starttime = world.time - . = 1 + . = TRUE + + // By default, checks for weakness and stunned get added to the extra_checks list. + // Setting `use_default_checks` to FALSE means that you don't want the do_after to check for these statuses, or that you will be supplying your own checks. + if(use_default_checks) + extra_checks += CALLBACK(user, /mob.proc/IsWeakened) + extra_checks += CALLBACK(user, /mob.proc/IsStunned) + while(world.time < endtime) sleep(1) if(progress) progbar.update(world.time - starttime) if(drifting && !user.inertia_dir) - drifting = 0 + drifting = FALSE Uloc = user.loc - if(!user || user.stat || user.IsWeakened() || user.stunned || (!drifting && user.loc != Uloc)|| (extra_checks && !extra_checks.Invoke())) - . = 0 + if(!user || user.stat || (!drifting && user.loc != Uloc) || check_for_true_callbacks(extra_checks)) + . = FALSE break if(Tloc && (!target || Tloc != target.loc)) - . = 0 + . = FALSE break if(needhand) @@ -403,14 +421,21 @@ This is always put in the attack log. //i.e the hand is used to pull some item/tool out of the construction if(!holdingnull) if(!holding) - . = 0 + . = FALSE break if(user.get_active_hand() != holding) - . = 0 + . = FALSE break if(progress) qdel(progbar) +// Upon any of the callbacks in the list returning TRUE, the proc will return TRUE. +/proc/check_for_true_callbacks(list/extra_checks) + for(var/datum/callback/CB in extra_checks) + if(CB.Invoke()) + return TRUE + return FALSE + #define DOAFTERONCE_MAGIC "Magic~~" GLOBAL_LIST_INIT(do_after_once_tracker, list()) /proc/do_after_once(mob/user, delay, needhand = 1, atom/target = null, progress = 1, attempt_cancel_message = "Attempt cancelled.") @@ -423,7 +448,7 @@ GLOBAL_LIST_INIT(do_after_once_tracker, list()) to_chat(user, "[attempt_cancel_message]") return FALSE GLOB.do_after_once_tracker[cache_key] = TRUE - . = do_after(user, delay, needhand, target, progress, extra_checks = CALLBACK(GLOBAL_PROC, .proc/do_after_once_checks, cache_key)) + . = do_after(user, delay, needhand, target, progress, extra_checks = list(CALLBACK(GLOBAL_PROC, .proc/do_after_once_checks, cache_key))) GLOB.do_after_once_tracker[cache_key] = FALSE /proc/do_after_once_checks(cache_key) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index bf83e6c5d49..692024899a2 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1003,7 +1003,7 @@ About the new airlock wires panel: "You begin [welded ? "unwelding":"welding"] the airlock...", \ "You hear welding.") - if(I.use_tool(src, user, 40, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/weld_checks, I, user))) + if(I.use_tool(src, user, 40, volume = I.tool_volume, extra_checks = list(CALLBACK(src, .proc/weld_checks, I, user)))) if(!density && !welded) return welded = !welded @@ -1014,7 +1014,7 @@ About the new airlock wires panel: user.visible_message("[user] is welding the airlock.", \ "You begin repairing the airlock...", \ "You hear welding.") - if(I.use_tool(src, user, 40, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/weld_checks, I, user))) + if(I.use_tool(src, user, 40, volume = I.tool_volume, extra_checks = list(CALLBACK(src, .proc/weld_checks, I, user)))) obj_integrity = max_integrity stat &= ~BROKEN user.visible_message("[user.name] has repaired [src].", \ diff --git a/code/game/objects/items/tools/tool_behaviour.dm b/code/game/objects/items/tools/tool_behaviour.dm index c937f80c30d..80b00dcd1ed 100644 --- a/code/game/objects/items/tools/tool_behaviour.dm +++ b/code/game/objects/items/tools/tool_behaviour.dm @@ -16,11 +16,11 @@ var/datum/callback/tool_check = CALLBACK(src, .proc/tool_check_callback, user, target, amount, extra_checks) if(ismob(target)) - if(!do_mob(user, target, delay, extra_checks=tool_check)) + if(!do_mob(user, target, delay, extra_checks = list(tool_check))) return else - if(!do_after(user, delay, target=target, extra_checks=tool_check)) + if(!do_after(user, delay, target=target, extra_checks = list(tool_check))) return else // Invoke the extra checks once, just in case. diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 9faab45cf97..8fe46ca052d 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -254,7 +254,7 @@ GLOBAL_LIST_INIT(wcCommon, pick(list("#379963", "#0d8395", "#58b5c3", "#49e46e", if(!can_be_reached(user)) return to_chat(user, "You begin to lever the window [state == WINDOW_OUT_OF_FRAME ? "into":"out of"] the frame...") - if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) + if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = list(CALLBACK(src, .proc/check_state_and_anchored, state, anchored)))) return state = (state == WINDOW_OUT_OF_FRAME ? WINDOW_IN_FRAME : WINDOW_OUT_OF_FRAME) to_chat(user, "You pry the window [state == WINDOW_IN_FRAME ? "into":"out of"] the frame.") @@ -268,20 +268,20 @@ GLOBAL_LIST_INIT(wcCommon, pick(list("#379963", "#0d8395", "#58b5c3", "#49e46e", if(reinf) if(state == WINDOW_SCREWED_TO_FRAME || state == WINDOW_IN_FRAME) to_chat(user, "You begin to [state == WINDOW_SCREWED_TO_FRAME ? "unscrew the window from":"screw the window to"] the frame...") - if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) + if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = list(CALLBACK(src, .proc/check_state_and_anchored, state, anchored)))) return state = (state == WINDOW_IN_FRAME ? WINDOW_SCREWED_TO_FRAME : WINDOW_IN_FRAME) to_chat(user, "You [state == WINDOW_IN_FRAME ? "unfasten the window from":"fasten the window to"] the frame.") else if(state == WINDOW_OUT_OF_FRAME) to_chat(user, "You begin to [anchored ? "unscrew the frame from":"screw the frame to"] the floor...") - if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) + if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = list(CALLBACK(src, .proc/check_state_and_anchored, state, anchored)))) return anchored = !anchored update_nearby_icons() to_chat(user, "You [anchored ? "fasten the frame to":"unfasten the frame from"] the floor.") else //if we're not reinforced, we don't need to check or update state to_chat(user, "You begin to [anchored ? "unscrew the window from":"screw the window to"] the floor...") - if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_anchored, anchored))) + if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = list(CALLBACK(src, .proc/check_anchored, anchored)))) return anchored = !anchored air_update_turf(TRUE) @@ -297,7 +297,7 @@ GLOBAL_LIST_INIT(wcCommon, pick(list("#379963", "#0d8395", "#58b5c3", "#49e46e", if(!can_be_reached(user)) return TOOL_ATTEMPT_DISMANTLE_MESSAGE - if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) + if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = list(CALLBACK(src, .proc/check_state_and_anchored, state, anchored)))) return var/obj/item/stack/sheet/G = new glass_type(user.loc, glass_amount) G.add_fingerprint(user) diff --git a/code/modules/mob/living/carbon/human/species/slime.dm b/code/modules/mob/living/carbon/human/species/slime.dm index d9fa1ccbf3c..622e0919b45 100644 --- a/code/modules/mob/living/carbon/human/species/slime.dm +++ b/code/modules/mob/living/carbon/human/species/slime.dm @@ -146,11 +146,13 @@ return var/limb_select = input(H, "Choose a limb to regrow", "Limb Regrowth") as null|anything in missing_limbs + if(!limb_select) // If the user hit cancel on the popup, return + return var/chosen_limb = missing_limbs[limb_select] H.visible_message("[H] begins to hold still and concentrate on [H.p_their()] missing [limb_select]...", "You begin to focus on regrowing your missing [limb_select]... (This will take [round(SLIMEPERSON_REGROWTHDELAY/10)] seconds, and you must hold still.)") - if(do_after(H, SLIMEPERSON_REGROWTHDELAY, needhand = 0, target = H)) - if(H.incapacitated()) + if(do_after(H, SLIMEPERSON_REGROWTHDELAY, FALSE, H, extra_checks = list(CALLBACK(H, /mob.proc/IsStunned)), use_default_checks = FALSE)) // Override the check for weakness, only check for stunned + if(H.incapacitated(ignore_lying = TRUE, extra_checks = list(CALLBACK(H, /mob.proc/IsStunned)), use_default_checks = FALSE)) // Override the check for weakness, only check for stunned to_chat(H, "You cannot regenerate missing limbs in your current state.") return @@ -196,4 +198,4 @@ #undef SLIMEPERSON_HUNGERCOST #undef SLIMEPERSON_MINHUNGER -#undef SLIMEPERSON_REGROWTHDELAY +#undef SLIMEPERSON_REGROWTHDELAY \ No newline at end of file diff --git a/code/modules/mob/living/update_status.dm b/code/modules/mob/living/update_status.dm index 20cc969119f..d24301b0dd0 100644 --- a/code/modules/mob/living/update_status.dm +++ b/code/modules/mob/living/update_status.dm @@ -65,8 +65,14 @@ return !(IsWeakened() || paralysis || stat || (status_flags & FAKEDEATH)) // Whether the mob is capable of actions or not -/mob/living/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, ignore_lying = FALSE) - if(stat || paralysis || stunned || IsWeakened() || (!ignore_restraints && restrained()) || (!ignore_lying && lying)) +/mob/living/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, ignore_lying = FALSE, list/extra_checks = list(), use_default_checks = TRUE) + // By default, checks for weakness and stunned get added to the extra_checks list. + // Setting `use_default_checks` to FALSE means that you don't want it checking for these statuses or you are supplying your own checks. + if(use_default_checks) + extra_checks += CALLBACK(src, /mob.proc/IsWeakened) + extra_checks += CALLBACK(src, /mob.proc/IsStunned) + + if(stat || paralysis || (!ignore_restraints && restrained()) || (!ignore_lying && lying) || check_for_true_callbacks(extra_checks)) return TRUE // wonderful proc names, I know - used to check whether the blur overlay diff --git a/code/modules/mob/status_procs.dm b/code/modules/mob/status_procs.dm index 0aeb9beef84..b61ce171194 100644 --- a/code/modules/mob/status_procs.dm +++ b/code/modules/mob/status_procs.dm @@ -173,6 +173,9 @@ /mob/proc/Stun() return +/mob/proc/IsStunned() + return stunned + /mob/proc/SetStunned() return