Better TankTrouble Chatbox

Redesigned chatbox meant both for power users, and those who maybe just wants something new

目前为 2024-01-16 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Better TankTrouble Chatbox
  3. // @author commander
  4. // @description Redesigned chatbox meant both for power users, and those who maybe just wants something new
  5. // @namespace https://github.com/asger-finding/tanktrouble-userscripts
  6. // @version 0.1.2
  7. // @license GPL-3.0
  8. // @match *://*.tanktrouble.com/*
  9. // @exclude *://classic.tanktrouble.com/
  10. // @run-at document-end
  11. // @grant GM_addStyle
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @require https://update.greasyfork.org/scripts/482092/1309109/TankTrouble%20Development%20Library.js
  15. // @noframes
  16. // ==/UserScript==
  17.  
  18. // TODO: chat messages history - press up to go to last sent message
  19. // TODO: whisper suggestions when pressing @ ?
  20.  
  21. GM_addStyle(`
  22. #chat {
  23. /*Move it to the bottom left*/
  24. inset: calc(100% - 30px) auto auto 34px !important;
  25. /*Disable drop shadow filter*/
  26. filter: none;
  27. -webkit-filter: none;
  28. }
  29. /*Reverse the chat flow*/
  30. #chat,
  31. #chat .content,
  32. #chat .body {
  33. display: flex;
  34. flex-direction: column-reverse;
  35. }
  36. #chat .status.button {
  37. transform: translate(7px, -18px);
  38. cursor: initial;
  39. z-index: 1;
  40. }
  41. #chat form {
  42. width: 200px;
  43. margin-left: 20px;
  44. background: #ececec;
  45. }
  46. #chat form[style*="repeating-linear-gradient"] {
  47. background: #d0d0d0 !important;
  48. }
  49. #chat:not(.open) form {
  50. display: none;
  51. }
  52. #chat textarea {
  53. left: 5px;
  54. transition: width 0s !important;
  55. }
  56. #chat .body {
  57. padding-right: 10px;
  58. border-radius: 3px;
  59. background: linear-gradient(225deg, #00000005 12px, #00000014 12px, #00000014 100%);
  60. margin-bottom: 7px;
  61. top: 0 !important;
  62. -webkit-mask-image: linear-gradient(225deg, #000000 11px, #00000000 12px, #00000000 100% ),
  63. linear-gradient(to top, #000000 70%, rgba(0, 0, 0, 0.11));
  64. }
  65. #chat .body .chatMessage svg {
  66. padding: 2px 4px 1px 4px;
  67. border-left: 2px dotted rgb(170, 170, 170);
  68. filter: drop-shadow(1px 1px 1px #00000022);
  69. }
  70. #chat .body.dragging {
  71. border: none !important;
  72. margin-left: 20px !important;
  73. }
  74. /*Rotate and align the handle to top-right*/
  75. .handle.ui-resizable-ne[src*="resizeHandleBottomRight.png"] {
  76. width: 12px;
  77. height: 12px !important;
  78. transform: translateX(6px) rotate(-90deg);
  79. z-index: 2147483647;
  80. position: sticky;
  81. left: calc(100% - 7px);
  82. top: 0;
  83. order: 0;
  84. margin-bottom: auto !important;
  85. }
  86. body:has(#chat .body.ui-resizable-resizing) .ui-resizable-handle.handle.ui-resizable-ne {
  87. display: none !important;
  88. }
  89.  
  90. /* Scrollbar */
  91. #chat .body {
  92. scrollbar-gutter: stable;
  93. scrollbar-width: thin;
  94. scrollbar-color: rgb(170, 170, 170) transparent;
  95. align-items: end;
  96. direction: rtl;
  97. pointer-events: auto;
  98. overflow-x: hidden;
  99. overflow-y: hidden;
  100. }
  101. #chat .body:hover {
  102. overflow-y: scroll;
  103. }
  104. #chat .body .chatMessage {
  105. direction: ltr;
  106. margin-left: ${(/Chrome.*Safari/u).test(navigator.userAgent) ? '3px' : '5px'};
  107. }
  108. #chat .body::-webkit-scrollbar {
  109. width: 3px;
  110. }
  111. #chat .body::-webkit-scrollbar-track {
  112. background: transparent;
  113. }
  114. #chat .body::-webkit-scrollbar-thumb {
  115. background: rgb(170, 170, 170);
  116. }
  117. `);
  118.  
  119. /**
  120. * Reconfigure the chat handle to be dragging
  121. * from the south-east direction (down)
  122. * to the north-east direction (up)
  123. */
  124. const changeHandleDirection = () => {
  125. const { resizable } = $.fn;
  126.  
  127. // Use a regular function to keep context
  128. $.fn.resizable = function(...args) {
  129. const [config] = args;
  130.  
  131. // Reassign the chat handle to be north-east facing
  132. if (config.handles) {
  133. const handle = config.handles.se;
  134. if (handle === TankTrouble.ChatBox.chatBodyResizeHandle) {
  135. handle.removeClass('ui-resizable-se')
  136. .addClass('ui-resizable-ne');
  137.  
  138. config.handles.ne = handle;
  139. delete config.handles.se;
  140.  
  141. // Set a taller chat maxHeight
  142. config.maxHeight = 650;
  143. }
  144. }
  145.  
  146. return resizable.call(this, config);
  147. };
  148. };
  149.  
  150. /**
  151. * Hook message render functions to disable jquery .show() animation
  152. * This fixes chat messages not showing up in the reversed chat order
  153. */
  154. const fixChatRendering = () => {
  155. Loader.interceptFunction(TankTrouble.ChatBox, '_renderChatMessage', (original, ...args) => {
  156. TankTrouble.ChatBox.chatBody.scrollTop(TankTrouble.ChatBox.chatBody.height());
  157.  
  158. // Set animateHeight to false
  159. args[9] = false;
  160. original(...args);
  161. });
  162.  
  163. Loader.interceptFunction(TankTrouble.ChatBox, '_renderSystemMessage', (original, ...args) => {
  164. TankTrouble.ChatBox.chatBody.scrollTop(TankTrouble.ChatBox.chatBody.height());
  165.  
  166. // Set animateHeight to false
  167. args[3] = false;
  168. original(...args);
  169. });
  170. };
  171.  
  172. /**
  173. * Prevent TankTrouble from clearing the chat when the client disconnects
  174. * Print message to chat when client switches server to separate conversations
  175. */
  176. const preventChatClear = () => {
  177. Loader.interceptFunction(TankTrouble.ChatBox, '_clearChat', (original, ...args) => {
  178. const isUnconnected = ClientManager.getClient().getState() === TTClient.STATES.UNCONNECTED;
  179.  
  180. // Void the call if the client is unconnected
  181. // when the function is invoked
  182. if (isUnconnected) return null;
  183.  
  184. return original(...args);
  185. });
  186.  
  187. Loader.interceptFunction(TankTrouble.ChatBox, '_updateStatusMessageAndAvailability', (original, ...args) => {
  188. const [systemMessageText, guestPlayerIds] = args;
  189.  
  190. // Check for welcome message
  191. // If true, print a system message
  192. if (systemMessageText === 'Welcome to TankTrouble Comms § § ') {
  193. const newServer = ClientManager.getAvailableServers()[ClientManager.multiplayerServerId];
  194. return original(`Connected to ${ newServer.name } ${ guestPlayerIds.length ? '§ ' : '' }`, guestPlayerIds);
  195. }
  196. return original(...args);
  197. });
  198. };
  199.  
  200. /**
  201. * Write the chat savestate to storage and return
  202. * @returns Promise for last savestate
  203. */
  204. const startChatSavestate = () => {
  205. Loader.interceptFunction(TankTrouble.ChatBox, 'open', (original, ...args) => {
  206. GM_setValue('chat-open', true);
  207. original(...args);
  208. });
  209. Loader.interceptFunction(TankTrouble.ChatBox, 'close', (original, ...args) => {
  210. GM_setValue('chat-open', false);
  211. original(...args);
  212. });
  213.  
  214. // Get savestate and default to chat being open
  215. return GM_getValue('chat-open', true);
  216. };
  217.  
  218. changeHandleDirection();
  219. fixChatRendering();
  220. Loader.whenContentInitialized().then(async() => {
  221. preventChatClear();
  222.  
  223. const shouldChatOpen = await startChatSavestate();
  224. if (shouldChatOpen) TankTrouble.ChatBox.open();
  225.  
  226. // Get the plain nodes for better performance
  227. // eslint-disable-next-line prefer-destructuring
  228. const chatBody = TankTrouble.ChatBox.chatBody[0];
  229. // eslint-disable-next-line prefer-destructuring
  230. const chatForm = TankTrouble.ChatBox.chatForm[0];
  231. // eslint-disable-next-line prefer-destructuring
  232. const chatInput = TankTrouble.ChatBox.chatInput[0];
  233.  
  234. // Create a mutation observer that looks for changes in the chatBody's attributes (namely width)
  235. new MutationObserver(() => {
  236. const width = Number(chatBody.offsetWidth || 220);
  237.  
  238. chatForm.style.width = `${width}px`;
  239. chatInput.style.width = `${width - 12}px`;
  240. }).observe(chatBody, {
  241. attributes: true,
  242. characterData: false
  243. });
  244.  
  245. chatForm.style.width = '220px';
  246. chatInput.style.width = `${chatForm.offsetWidth - 12}px`;
  247.  
  248. // Allow more characters in the chat input
  249. chatInput.setAttribute('maxlength', '255');
  250. });