diff --git a/resources/[system]/chat-theme-gtao/__resource.lua b/resources/[system]/chat-theme-gtao/__resource.lua
new file mode 100644
index 0000000..a9a6938
--- /dev/null
+++ b/resources/[system]/chat-theme-gtao/__resource.lua
@@ -0,0 +1,10 @@
+file 'style.css'
+file 'shadow.js'
+
+chat_theme 'gtao' {
+ styleSheet = 'style.css',
+ script = 'shadow.js',
+ msgTemplates = {
+ default = '{0}{1}'
+ }
+}
\ No newline at end of file
diff --git a/resources/[system]/chat-theme-gtao/shadow.js b/resources/[system]/chat-theme-gtao/shadow.js
new file mode 100644
index 0000000..55d3176
--- /dev/null
+++ b/resources/[system]/chat-theme-gtao/shadow.js
@@ -0,0 +1,74 @@
+(function() {
+var Filters = {}
+
+var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+svg.setAttribute("style", "display:block;width:0px;height:0px");
+var defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
+
+var blurFilter = document.createElementNS("http://www.w3.org/2000/svg", "filter");
+blurFilter.setAttribute("id", "svgBlurFilter");
+var feGaussianFilter = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur");
+feGaussianFilter.setAttribute("stdDeviation", "0 0");
+blurFilter.appendChild(feGaussianFilter);
+defs.appendChild(blurFilter);
+Filters._svgBlurFilter = feGaussianFilter;
+
+// Drop Shadow Filter
+var dropShadowFilter = document.createElementNS("http://www.w3.org/2000/svg", "filter");
+dropShadowFilter.setAttribute("id", "svgDropShadowFilter");
+var feGaussianFilter = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur");
+feGaussianFilter.setAttribute("in", "SourceAlpha");
+feGaussianFilter.setAttribute("stdDeviation", "3");
+dropShadowFilter.appendChild(feGaussianFilter);
+Filters._svgDropshadowFilterBlur = feGaussianFilter;
+
+var feOffset = document.createElementNS("http://www.w3.org/2000/svg", "feOffset");
+feOffset.setAttribute("dx", "0");
+feOffset.setAttribute("dy", "0");
+feOffset.setAttribute("result", "offsetblur");
+dropShadowFilter.appendChild(feOffset);
+Filters._svgDropshadowFilterOffset = feOffset;
+
+var feFlood = document.createElementNS("http://www.w3.org/2000/svg", "feFlood");
+feFlood.setAttribute("flood-color", "rgba(0,0,0,1)");
+dropShadowFilter.appendChild(feFlood);
+Filters._svgDropshadowFilterFlood = feFlood;
+
+var feComposite = document.createElementNS("http://www.w3.org/2000/svg", "feComposite");
+feComposite.setAttribute("in2", "offsetblur");
+feComposite.setAttribute("operator", "in");
+dropShadowFilter.appendChild(feComposite);
+var feComposite = document.createElementNS("http://www.w3.org/2000/svg", "feComposite");
+feComposite.setAttribute("in2", "SourceAlpha");
+feComposite.setAttribute("operator", "out");
+feComposite.setAttribute("result", "outer");
+dropShadowFilter.appendChild(feComposite);
+
+var feMerge = document.createElementNS("http://www.w3.org/2000/svg", "feMerge");
+var feMergeNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
+feMerge.appendChild(feMergeNode);
+var feMergeNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
+feMerge.appendChild(feMergeNode);
+Filters._svgDropshadowMergeNode = feMergeNode;
+dropShadowFilter.appendChild(feMerge);
+defs.appendChild(dropShadowFilter);
+svg.appendChild(defs);
+document.documentElement.appendChild(svg);
+
+const blurScale = 1;
+const scale = (document.body.clientWidth / 1280);
+
+Filters._svgDropshadowFilterBlur.setAttribute("stdDeviation",
+ 1 * blurScale + " " +
+ 1 * blurScale
+);
+Filters._svgDropshadowFilterOffset.setAttribute("dx",
+ String(Math.cos(45 * Math.PI / 180) * 1 * scale));
+Filters._svgDropshadowFilterOffset.setAttribute("dy",
+ String(Math.sin(45 * Math.PI / 180) * 1 * scale));
+Filters._svgDropshadowFilterFlood.setAttribute("flood-color",
+ 'rgba(0, 0, 0, 1)');
+Filters._svgDropshadowMergeNode.setAttribute("in",
+ "SourceGraphic");
+
+})();
\ No newline at end of file
diff --git a/resources/[system]/chat-theme-gtao/style.css b/resources/[system]/chat-theme-gtao/style.css
new file mode 100644
index 0000000..661d2ee
--- /dev/null
+++ b/resources/[system]/chat-theme-gtao/style.css
@@ -0,0 +1,116 @@
+* {
+ font-family: inherit;
+}
+
+.chat-window {
+ --size: calc((((2.7vw / 1.77777) * 1.2)) * 6);
+
+ position: absolute;
+ right: calc(1.56vw);
+ top: calc(50% - (var(--size) / 2));
+ height: var(--size) !important;
+
+ background: inherit !important;
+
+ text-align: right;
+
+ left: auto;
+
+ user-select: none;
+}
+
+@font-face {
+ font-family: 'Font2';
+ src: url(https://runtime.fivem.net/temp/ChaletLondonNineteenSixty.otf?a);
+}
+
+@font-face {
+ font-family: 'Font2_cond';
+ src: url(https://runtime.fivem.net/temp/chaletcomprime-colognesixty-webfont.ttf?a);
+}
+
+.msg {
+ font-family: Font2, sans-serif;
+ color: #fff;
+
+ font-size: calc(1.8vw / 1.77777); /* 13px in 720p, calc'd by width */
+ filter: url(#svgDropShadowFilter);
+
+ line-height: calc((2.7vw / 1.77777) * 1.2);
+
+ margin-bottom: 0;
+}
+
+.chat-messages {
+ margin: 0;
+ height: 100%;
+}
+
+.msg > span > span > b {
+ font-family: Font2_cond, sans-serif;
+ font-weight: normal;
+
+ vertical-align: baseline;
+ padding-right: 11px;
+
+ line-height: 1;
+
+ font-size: calc(2.7vw / 1.77777); /* 13px in 720p, calc'd by width */
+}
+
+.msg > span > span > span {
+ vertical-align: baseline;
+}
+
+.msg i:first-of-type {
+ font-style: normal;
+ color: #c0c0c0;
+}
+
+.chat-input {
+ position: absolute;
+ right: calc(1.56vw);
+ bottom: calc(1.56vw);
+
+ background: inherit !important;
+
+ text-align: right;
+
+ top: auto;
+ left: auto;
+
+ height: auto;
+
+ font-family: Font2, sans-serif;
+}
+
+.chat-input > div {
+ background-color: rgba(0, 0, 0, .6);
+ padding: calc(0.15625vw / 2);
+}
+
+.chat-input .prefix {
+ margin: 0;
+ margin-left: 0.7%;
+ margin-top: -0.1%;
+}
+
+.chat-input > div + div {
+ position: absolute;
+ bottom: calc(1.65vh + 0.15625vw + 0.15625vw + 0.15625vw + (0.15625vw / 2));
+ width: 99.6%;
+
+ text-align: left;
+}
+
+.suggestions {
+ border: calc(0.15625vw / 2) solid rgba(180, 180, 180, .6);
+ background: transparent;
+}
+
+textarea {
+ background: transparent;
+ border: calc(0.15625vw / 2) solid rgba(180, 180, 180, .6);
+ padding: calc(0.15625vw / 2);
+ padding-left: calc(3.5% + (0.15625vw / 2));
+}
\ No newline at end of file
diff --git a/resources/[system]/chat/cl_chat.lua b/resources/[system]/chat/cl_chat.lua
index 7173dbd..1a1eece 100644
--- a/resources/[system]/chat/cl_chat.lua
+++ b/resources/[system]/chat/cl_chat.lua
@@ -1,5 +1,7 @@
local chatInputActive = false
local chatInputActivating = false
+local chatHidden = true
+local chatLoaded = false
RegisterNetEvent('chatMessage')
RegisterNetEvent('chat:addTemplate')
@@ -132,16 +134,54 @@ local function refreshCommands()
end
end
+local function refreshThemes()
+ local themes = {}
+
+ for resIdx = 0, GetNumResources() - 1 do
+ local resource = GetResourceByFindIndex(resIdx)
+
+ if GetResourceState(resource) == 'started' then
+ local numThemes = GetNumResourceMetadata(resource, 'chat_theme')
+
+ if numThemes > 0 then
+ local themeName = GetResourceMetadata(resource, 'chat_theme')
+ local themeData = json.decode(GetResourceMetadata(resource, 'chat_theme_extra') or 'null')
+
+ if themeName and themeData then
+ themeData.baseUrl = 'nui://' .. resource .. '/'
+ themes[themeName] = themeData
+ end
+ end
+ end
+ end
+
+ SendNUIMessage({
+ type = 'ON_UPDATE_THEMES',
+ themes = themes
+ })
+end
+
AddEventHandler('onClientResourceStart', function(resName)
Wait(500)
refreshCommands()
+ refreshThemes()
+end)
+
+AddEventHandler('onClientResourceStop', function(resName)
+ Wait(500)
+
+ refreshCommands()
+ refreshThemes()
end)
RegisterNUICallback('loaded', function(data, cb)
TriggerServerEvent('chat:init');
refreshCommands()
+ refreshThemes()
+
+ chatLoaded = true
cb('ok')
end)
@@ -171,5 +211,22 @@ Citizen.CreateThread(function()
chatInputActivating = false
end
end
+
+ if chatLoaded then
+ local shouldBeHidden = false
+
+ if IsScreenFadedOut() or IsPauseMenuActive() then
+ shouldBeHidden = true
+ end
+
+ if (shouldBeHidden and not chatHidden) or (not shouldBeHidden and chatHidden) then
+ chatHidden = shouldBeHidden
+
+ SendNUIMessage({
+ type = 'ON_SCREEN_STATE_CHANGE',
+ shouldHide = shouldBeHidden
+ })
+ end
+ end
end
end)
diff --git a/resources/[system]/chat/html/App.js b/resources/[system]/chat/html/App.js
index 4b38eb4..77769e1 100644
--- a/resources/[system]/chat/html/App.js
+++ b/resources/[system]/chat/html/App.js
@@ -6,6 +6,7 @@ window.APP = {
style: CONFIG.style,
showInput: false,
showWindow: false,
+ shouldHide: true,
backingSuggestions: [],
removedSuggestions: [],
templates: CONFIG.templates,
@@ -13,6 +14,8 @@ window.APP = {
messages: [],
oldMessages: [],
oldMessagesIndex: -1,
+ tplBackups: [],
+ msgTplBackups: []
};
},
destroyed() {
@@ -48,6 +51,9 @@ window.APP = {
},
},
methods: {
+ ON_SCREEN_STATE_CHANGE({ shouldHide }) {
+ this.shouldHide = shouldHide;
+ },
ON_OPEN() {
this.showInput = true;
this.showWindow = true;
@@ -91,6 +97,85 @@ window.APP = {
this.templates[template.id] = template.html;
}
},
+ ON_UPDATE_THEMES({ themes }) {
+ this.removeThemes();
+
+ this.setThemes(themes);
+ },
+ removeThemes() {
+ for (let i = 0; i < document.styleSheets.length; i++) {
+ const styleSheet = document.styleSheets[i];
+ const node = styleSheet.ownerNode;
+
+ if (node.getAttribute('data-theme')) {
+ node.parentNode.removeChild(node);
+ }
+ }
+
+ this.tplBackups.reverse();
+
+ for (const [ elem, oldData ] of this.tplBackups) {
+ elem.innerText = oldData;
+ }
+
+ this.tplBackups = [];
+
+ this.msgTplBackups.reverse();
+
+ for (const [ id, oldData ] of this.msgTplBackups) {
+ this.templates[id] = oldData;
+ }
+
+ this.msgTplBackups = [];
+ },
+ setThemes(themes) {
+ for (const [ id, data ] of Object.entries(themes)) {
+ if (data.style) {
+ const style = document.createElement('style');
+ style.type = 'text/css';
+ style.setAttribute('data-theme', id);
+ style.appendChild(document.createTextNode(data.style));
+
+ document.head.appendChild(style);
+ }
+
+ if (data.styleSheet) {
+ const link = document.createElement('link');
+ link.rel = 'stylesheet';
+ link.type = 'text/css';
+ link.href = data.baseUrl + data.styleSheet;
+ link.setAttribute('data-theme', id);
+
+ document.head.appendChild(link);
+ }
+
+ if (data.templates) {
+ for (const [ tplId, tpl ] of Object.entries(data.templates)) {
+ const elem = document.getElementById(tplId);
+
+ if (elem) {
+ this.tplBackups.push([ elem, elem.innerText ]);
+ elem.innerText = tpl;
+ }
+ }
+ }
+
+ if (data.script) {
+ const script = document.createElement('script');
+ script.type = 'text/javascript';
+ script.src = data.baseUrl + data.script;
+
+ document.head.appendChild(script);
+ }
+
+ if (data.msgTemplates) {
+ for (const [ tplId, tpl ] of Object.entries(data.msgTemplates)) {
+ this.msgTplBackups.push([ tplId, this.templates[tplId] ]);
+ this.templates[tplId] = tpl;
+ }
+ }
+ }
+ },
warn(msg) {
this.messages.push({
args: [msg],
diff --git a/resources/[system]/chat/html/index.css b/resources/[system]/chat/html/index.css
index 96a8e3a..30f1699 100644
--- a/resources/[system]/chat/html/index.css
+++ b/resources/[system]/chat/html/index.css
@@ -103,7 +103,7 @@ textarea:focus, input:focus {
list-style-type: none;
padding: 0.5%;
padding-left: 1.4%;
- font-size: 1.1rem;
+ font-size: 1.65vh;
box-sizing: border-box;
color: white;
background-color: rgba(44, 62, 80, 1.0);
@@ -121,3 +121,7 @@ textarea:focus, input:focus {
.suggestion {
margin-bottom: 0.5%;
}
+
+.hidden {
+ display: none;
+}
\ No newline at end of file
diff --git a/resources/[system]/chat/html/index.html b/resources/[system]/chat/html/index.html
index a0c43b2..6ce65dd 100644
--- a/resources/[system]/chat/html/index.html
+++ b/resources/[system]/chat/html/index.html
@@ -18,7 +18,7 @@