mirror of
https://github.com/citizenfx/cfx-server-data.git
synced 2025-12-12 06:14:09 +01:00
reorganize resource directories
This commit is contained in:
@@ -1,18 +0,0 @@
|
||||
description 'output lists for game content'
|
||||
|
||||
SetResourceInfo('uiPage', 'client/html/index.html')
|
||||
|
||||
client_script 'client/channelfeed.lua'
|
||||
|
||||
export 'printTo'
|
||||
export 'addChannel'
|
||||
export 'removeChannel'
|
||||
|
||||
files
|
||||
{
|
||||
'client/html/index.html',
|
||||
'client/html/feed.js',
|
||||
'client/html/feed.css',
|
||||
'client/fonts/roboto-regular.ttf',
|
||||
'client/fonts/roboto-condensed.ttf',
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
local eventBuffer = {}
|
||||
|
||||
AddUIHandler('getNew', function(data, cb)
|
||||
local localBuf = eventBuffer
|
||||
eventBuffer = {}
|
||||
|
||||
cb(localBuf)
|
||||
end)
|
||||
|
||||
function printTo(channel, data)
|
||||
table.insert(eventBuffer, {
|
||||
meta = 'print',
|
||||
channel = channel,
|
||||
data = data
|
||||
})
|
||||
|
||||
PollUI()
|
||||
end
|
||||
|
||||
function addChannel(id, options)
|
||||
if not options.template then
|
||||
return
|
||||
end
|
||||
|
||||
options.id = id
|
||||
|
||||
table.insert(eventBuffer, {
|
||||
meta = 'addChannel',
|
||||
data = options
|
||||
})
|
||||
|
||||
PollUI()
|
||||
end
|
||||
|
||||
function removeChannel(id)
|
||||
table.insert(eventBuffer, {
|
||||
meta = 'removeChannel',
|
||||
data = id
|
||||
})
|
||||
end
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,118 +0,0 @@
|
||||
(function() {
|
||||
var getLock = 0;
|
||||
|
||||
var channels = {};
|
||||
|
||||
var zoomLevel = '100%';
|
||||
|
||||
$(function()
|
||||
{
|
||||
zoomLevel = Math.round(($(window).height() / 720) * 100) + '%'; // yay dynamic typing
|
||||
});
|
||||
|
||||
function refetchData()
|
||||
{
|
||||
getLock = 0;
|
||||
|
||||
$.get('http://channelfeed/getNew', function(data)
|
||||
{
|
||||
if (getLock > 1)
|
||||
{
|
||||
setTimeout(refetchData, 50);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
getLock++;
|
||||
|
||||
data.forEach(function(item)
|
||||
{
|
||||
switch (item.meta)
|
||||
{
|
||||
case 'print':
|
||||
var channel = item.channel;
|
||||
|
||||
if (!(channel in channels))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
channel = channels[channel];
|
||||
|
||||
var elem = $($.Mustache.render(item.channel, item.data, { method: channel.method })).appendTo(channel.$elem);
|
||||
|
||||
setTimeout(function()
|
||||
{
|
||||
elem.fadeOut(400, function()
|
||||
{
|
||||
elem.remove();
|
||||
});
|
||||
}, 7500);
|
||||
|
||||
break;
|
||||
|
||||
case 'addChannel':
|
||||
var channel = item.data;
|
||||
|
||||
if (channel.id in channels)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
channel.$elem = $('<div></div>').attr('id', 'channel-' + channel.id).appendTo('#channels');
|
||||
|
||||
if (channel.styles !== undefined)
|
||||
{
|
||||
channel.$elem.css(channel.styles);
|
||||
}
|
||||
|
||||
channel.$elem = $('<div></div>').css('zoom', zoomLevel).appendTo(channel.$elem);
|
||||
|
||||
if (channel.styleUrl !== undefined)
|
||||
{
|
||||
$('<link>').appendTo('head').attr({ type: 'text/css', rel: 'stylesheet' }).attr('href', channel.styleUrl);
|
||||
}
|
||||
|
||||
$.Mustache.add(channel.id, channel.template);
|
||||
|
||||
channels[channel.id] = channel;
|
||||
|
||||
break;
|
||||
case 'removeChannel':
|
||||
var channelId = item.data;
|
||||
|
||||
if (channelId in channels)
|
||||
{
|
||||
channel.$elem.parent().remove();
|
||||
|
||||
delete channels[channelId];
|
||||
}
|
||||
|
||||
break;
|
||||
case 'clear':
|
||||
var channel = item.channel;
|
||||
|
||||
if (!(channel in channels))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
channel = channels[channel];
|
||||
|
||||
channel.$elem.html();
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('message', function(event)
|
||||
{
|
||||
if (event.data.type != 'poll')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
refetchData();
|
||||
});
|
||||
})();
|
||||
@@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="nui://game/ui/jquery.js" type="text/javascript"></script>
|
||||
<script src="nui://game/ui/mustache.js" type="text/javascript"></script>
|
||||
<script src="nui://game/ui/jquery.mustache.js" type="text/javascript"></script>
|
||||
<script src="feed.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="channels">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
10
resources/[gameplay]/chat-theme-gtao/__resource.lua
Normal file
10
resources/[gameplay]/chat-theme-gtao/__resource.lua
Normal file
@@ -0,0 +1,10 @@
|
||||
file 'style.css'
|
||||
file 'shadow.js'
|
||||
|
||||
chat_theme 'gtao' {
|
||||
styleSheet = 'style.css',
|
||||
script = 'shadow.js',
|
||||
msgTemplates = {
|
||||
default = '<b>{0}</b><span>{1}</span>'
|
||||
}
|
||||
}
|
||||
74
resources/[gameplay]/chat-theme-gtao/shadow.js
Normal file
74
resources/[gameplay]/chat-theme-gtao/shadow.js
Normal file
@@ -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");
|
||||
|
||||
})();
|
||||
116
resources/[gameplay]/chat-theme-gtao/style.css
Normal file
116
resources/[gameplay]/chat-theme-gtao/style.css
Normal file
@@ -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));
|
||||
}
|
||||
1
resources/[gameplay]/chat/README.md
Normal file
1
resources/[gameplay]/chat/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# Chat
|
||||
26
resources/[gameplay]/chat/__resource.lua
Normal file
26
resources/[gameplay]/chat/__resource.lua
Normal file
@@ -0,0 +1,26 @@
|
||||
description 'chat management stuff'
|
||||
|
||||
ui_page 'html/index.html'
|
||||
|
||||
client_script 'cl_chat.lua'
|
||||
server_script 'sv_chat.lua'
|
||||
|
||||
files {
|
||||
'html/index.html',
|
||||
'html/index.css',
|
||||
'html/config.default.js',
|
||||
'html/config.js',
|
||||
'html/App.js',
|
||||
'html/Message.js',
|
||||
'html/Suggestions.js',
|
||||
'html/vendor/vue.2.3.3.min.js',
|
||||
'html/vendor/flexboxgrid.6.3.1.min.css',
|
||||
'html/vendor/animate.3.5.2.min.css',
|
||||
'html/vendor/latofonts.css',
|
||||
'html/vendor/fonts/LatoRegular.woff2',
|
||||
'html/vendor/fonts/LatoRegular2.woff2',
|
||||
'html/vendor/fonts/LatoLight2.woff2',
|
||||
'html/vendor/fonts/LatoLight.woff2',
|
||||
'html/vendor/fonts/LatoBold.woff2',
|
||||
'html/vendor/fonts/LatoBold2.woff2',
|
||||
}
|
||||
232
resources/[gameplay]/chat/cl_chat.lua
Normal file
232
resources/[gameplay]/chat/cl_chat.lua
Normal file
@@ -0,0 +1,232 @@
|
||||
local chatInputActive = false
|
||||
local chatInputActivating = false
|
||||
local chatHidden = true
|
||||
local chatLoaded = false
|
||||
|
||||
RegisterNetEvent('chatMessage')
|
||||
RegisterNetEvent('chat:addTemplate')
|
||||
RegisterNetEvent('chat:addMessage')
|
||||
RegisterNetEvent('chat:addSuggestion')
|
||||
RegisterNetEvent('chat:addSuggestions')
|
||||
RegisterNetEvent('chat:removeSuggestion')
|
||||
RegisterNetEvent('chat:clear')
|
||||
|
||||
-- internal events
|
||||
RegisterNetEvent('__cfx_internal:serverPrint')
|
||||
|
||||
RegisterNetEvent('_chat:messageEntered')
|
||||
|
||||
--deprecated, use chat:addMessage
|
||||
AddEventHandler('chatMessage', function(author, color, text)
|
||||
local args = { text }
|
||||
if author ~= "" then
|
||||
table.insert(args, 1, author)
|
||||
end
|
||||
SendNUIMessage({
|
||||
type = 'ON_MESSAGE',
|
||||
message = {
|
||||
color = color,
|
||||
multiline = true,
|
||||
args = args
|
||||
}
|
||||
})
|
||||
end)
|
||||
|
||||
AddEventHandler('__cfx_internal:serverPrint', function(msg)
|
||||
print(msg)
|
||||
|
||||
SendNUIMessage({
|
||||
type = 'ON_MESSAGE',
|
||||
message = {
|
||||
templateId = 'print',
|
||||
multiline = true,
|
||||
args = { msg }
|
||||
}
|
||||
})
|
||||
end)
|
||||
|
||||
AddEventHandler('chat:addMessage', function(message)
|
||||
SendNUIMessage({
|
||||
type = 'ON_MESSAGE',
|
||||
message = message
|
||||
})
|
||||
end)
|
||||
|
||||
AddEventHandler('chat:addSuggestion', function(name, help, params)
|
||||
SendNUIMessage({
|
||||
type = 'ON_SUGGESTION_ADD',
|
||||
suggestion = {
|
||||
name = name,
|
||||
help = help,
|
||||
params = params or nil
|
||||
}
|
||||
})
|
||||
end)
|
||||
|
||||
AddEventHandler('chat:addSuggestions', function(suggestions)
|
||||
for _, suggestion in ipairs(suggestions) do
|
||||
SendNUIMessage({
|
||||
type = 'ON_SUGGESTION_ADD',
|
||||
suggestion = suggestion
|
||||
})
|
||||
end
|
||||
end)
|
||||
|
||||
AddEventHandler('chat:removeSuggestion', function(name)
|
||||
SendNUIMessage({
|
||||
type = 'ON_SUGGESTION_REMOVE',
|
||||
name = name
|
||||
})
|
||||
end)
|
||||
|
||||
AddEventHandler('chat:addTemplate', function(id, html)
|
||||
SendNUIMessage({
|
||||
type = 'ON_TEMPLATE_ADD',
|
||||
template = {
|
||||
id = id,
|
||||
html = html
|
||||
}
|
||||
})
|
||||
end)
|
||||
|
||||
AddEventHandler('chat:clear', function(name)
|
||||
SendNUIMessage({
|
||||
type = 'ON_CLEAR'
|
||||
})
|
||||
end)
|
||||
|
||||
RegisterNUICallback('chatResult', function(data, cb)
|
||||
chatInputActive = false
|
||||
SetNuiFocus(false)
|
||||
|
||||
if not data.canceled then
|
||||
local id = PlayerId()
|
||||
|
||||
--deprecated
|
||||
local r, g, b = 0, 0x99, 255
|
||||
|
||||
if data.message:sub(1, 1) == '/' then
|
||||
ExecuteCommand(data.message:sub(2))
|
||||
else
|
||||
TriggerServerEvent('_chat:messageEntered', GetPlayerName(id), { r, g, b }, data.message)
|
||||
end
|
||||
end
|
||||
|
||||
cb('ok')
|
||||
end)
|
||||
|
||||
local function refreshCommands()
|
||||
if GetRegisteredCommands then
|
||||
local registeredCommands = GetRegisteredCommands()
|
||||
|
||||
local suggestions = {}
|
||||
|
||||
for _, command in ipairs(registeredCommands) do
|
||||
if IsAceAllowed(('command.%s'):format(command.name)) then
|
||||
table.insert(suggestions, {
|
||||
name = '/' .. command.name,
|
||||
help = ''
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
TriggerEvent('chat:addSuggestions', suggestions)
|
||||
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)
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
SetTextChatEnabled(false)
|
||||
SetNuiFocus(false)
|
||||
|
||||
while true do
|
||||
Wait(0)
|
||||
|
||||
if not chatInputActive then
|
||||
if IsControlPressed(0, 245) --[[ INPUT_MP_TEXT_CHAT_ALL ]] then
|
||||
chatInputActive = true
|
||||
chatInputActivating = true
|
||||
|
||||
SendNUIMessage({
|
||||
type = 'ON_OPEN'
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
if chatInputActivating then
|
||||
if not IsControlPressed(0, 245) then
|
||||
SetNuiFocus(true)
|
||||
|
||||
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)
|
||||
255
resources/[gameplay]/chat/html/App.js
Normal file
255
resources/[gameplay]/chat/html/App.js
Normal file
@@ -0,0 +1,255 @@
|
||||
window.APP = {
|
||||
template: '#app_template',
|
||||
name: 'app',
|
||||
data() {
|
||||
return {
|
||||
style: CONFIG.style,
|
||||
showInput: false,
|
||||
showWindow: false,
|
||||
shouldHide: true,
|
||||
backingSuggestions: [],
|
||||
removedSuggestions: [],
|
||||
templates: CONFIG.templates,
|
||||
message: '',
|
||||
messages: [],
|
||||
oldMessages: [],
|
||||
oldMessagesIndex: -1,
|
||||
tplBackups: [],
|
||||
msgTplBackups: []
|
||||
};
|
||||
},
|
||||
destroyed() {
|
||||
clearInterval(this.focusTimer);
|
||||
window.removeEventListener('message', this.listener);
|
||||
},
|
||||
mounted() {
|
||||
post('http://chat/loaded', JSON.stringify({}));
|
||||
this.listener = window.addEventListener('message', (event) => {
|
||||
const item = event.data || event.detail; //'detail' is for debuging via browsers
|
||||
if (this[item.type]) {
|
||||
this[item.type](item);
|
||||
}
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
messages() {
|
||||
if (this.showWindowTimer) {
|
||||
clearTimeout(this.showWindowTimer);
|
||||
}
|
||||
this.showWindow = true;
|
||||
this.resetShowWindowTimer();
|
||||
|
||||
const messagesObj = this.$refs.messages;
|
||||
this.$nextTick(() => {
|
||||
messagesObj.scrollTop = messagesObj.scrollHeight;
|
||||
});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
suggestions() {
|
||||
return this.backingSuggestions.filter((el) => this.removedSuggestions.indexOf(el.name) <= -1);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
ON_SCREEN_STATE_CHANGE({ shouldHide }) {
|
||||
this.shouldHide = shouldHide;
|
||||
},
|
||||
ON_OPEN() {
|
||||
this.showInput = true;
|
||||
this.showWindow = true;
|
||||
if (this.showWindowTimer) {
|
||||
clearTimeout(this.showWindowTimer);
|
||||
}
|
||||
this.focusTimer = setInterval(() => {
|
||||
if (this.$refs.input) {
|
||||
this.$refs.input.focus();
|
||||
} else {
|
||||
clearInterval(this.focusTimer);
|
||||
}
|
||||
}, 100);
|
||||
},
|
||||
ON_MESSAGE({ message }) {
|
||||
this.messages.push(message);
|
||||
},
|
||||
ON_CLEAR() {
|
||||
this.messages = [];
|
||||
this.oldMessages = [];
|
||||
this.oldMessagesIndex = -1;
|
||||
},
|
||||
ON_SUGGESTION_ADD({ suggestion }) {
|
||||
const duplicateSuggestion = this.backingSuggestions.find(a => a.name == suggestion.name);
|
||||
if (duplicateSuggestion) {
|
||||
if(suggestion.help || suggestion.params) {
|
||||
duplicateSuggestion.help = suggestion.help || "";
|
||||
duplicateSuggestion.params = suggestion.params || [];
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!suggestion.params) {
|
||||
suggestion.params = []; //TODO Move somewhere else
|
||||
}
|
||||
this.backingSuggestions.push(suggestion);
|
||||
},
|
||||
ON_SUGGESTION_REMOVE({ name }) {
|
||||
if(this.removedSuggestions.indexOf(name) <= -1) {
|
||||
this.removedSuggestions.push(name);
|
||||
}
|
||||
},
|
||||
ON_TEMPLATE_ADD({ template }) {
|
||||
if (this.templates[template.id]) {
|
||||
this.warn(`Tried to add duplicate template '${template.id}'`)
|
||||
} else {
|
||||
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],
|
||||
template: '^3<b>CHAT-WARN</b>: ^0{0}',
|
||||
});
|
||||
},
|
||||
clearShowWindowTimer() {
|
||||
clearTimeout(this.showWindowTimer);
|
||||
},
|
||||
resetShowWindowTimer() {
|
||||
this.clearShowWindowTimer();
|
||||
this.showWindowTimer = setTimeout(() => {
|
||||
if (!this.showInput) {
|
||||
this.showWindow = false;
|
||||
}
|
||||
}, CONFIG.fadeTimeout);
|
||||
},
|
||||
keyUp() {
|
||||
this.resize();
|
||||
},
|
||||
keyDown(e) {
|
||||
if (e.which === 38 || e.which === 40) {
|
||||
e.preventDefault();
|
||||
this.moveOldMessageIndex(e.which === 38);
|
||||
} else if (e.which == 33) {
|
||||
var buf = document.getElementsByClassName('chat-messages')[0];
|
||||
buf.scrollTop = buf.scrollTop - 100;
|
||||
} else if (e.which == 34) {
|
||||
var buf = document.getElementsByClassName('chat-messages')[0];
|
||||
buf.scrollTop = buf.scrollTop + 100;
|
||||
}
|
||||
},
|
||||
moveOldMessageIndex(up) {
|
||||
if (up && this.oldMessages.length > this.oldMessagesIndex + 1) {
|
||||
this.oldMessagesIndex += 1;
|
||||
this.message = this.oldMessages[this.oldMessagesIndex];
|
||||
} else if (!up && this.oldMessagesIndex - 1 >= 0) {
|
||||
this.oldMessagesIndex -= 1;
|
||||
this.message = this.oldMessages[this.oldMessagesIndex];
|
||||
} else if (!up && this.oldMessagesIndex - 1 === -1) {
|
||||
this.oldMessagesIndex = -1;
|
||||
this.message = '';
|
||||
}
|
||||
},
|
||||
resize() {
|
||||
const input = this.$refs.input;
|
||||
input.style.height = '5px';
|
||||
input.style.height = `${input.scrollHeight + 2}px`;
|
||||
},
|
||||
send(e) {
|
||||
if(this.message !== '') {
|
||||
post('http://chat/chatResult', JSON.stringify({
|
||||
message: this.message,
|
||||
}));
|
||||
this.oldMessages.unshift(this.message);
|
||||
this.oldMessagesIndex = -1;
|
||||
this.hideInput();
|
||||
} else {
|
||||
this.hideInput(true);
|
||||
}
|
||||
},
|
||||
hideInput(canceled = false) {
|
||||
if (canceled) {
|
||||
post('http://chat/chatResult', JSON.stringify({ canceled }));
|
||||
}
|
||||
this.message = '';
|
||||
this.showInput = false;
|
||||
clearInterval(this.focusTimer);
|
||||
this.resetShowWindowTimer();
|
||||
},
|
||||
},
|
||||
};
|
||||
86
resources/[gameplay]/chat/html/Message.js
Normal file
86
resources/[gameplay]/chat/html/Message.js
Normal file
@@ -0,0 +1,86 @@
|
||||
Vue.component('message', {
|
||||
template: '#message_template',
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
textEscaped() {
|
||||
let s = this.template ? this.template : this.templates[this.templateId];
|
||||
|
||||
if (this.template) {
|
||||
//We disable templateId since we are using a direct raw template
|
||||
this.templateId = -1;
|
||||
}
|
||||
|
||||
//This hack is required to preserve backwards compatability
|
||||
if (this.templateId == CONFIG.defaultTemplateId
|
||||
&& this.args.length == 1) {
|
||||
s = this.templates[CONFIG.defaultAltTemplateId] //Swap out default template :/
|
||||
}
|
||||
|
||||
s = s.replace(/{(\d+)}/g, (match, number) => {
|
||||
const argEscaped = this.args[number] != undefined ? this.escape(this.args[number]) : match
|
||||
if (number == 0 && this.color) {
|
||||
//color is deprecated, use templates or ^1 etc.
|
||||
return this.colorizeOld(argEscaped);
|
||||
}
|
||||
return argEscaped;
|
||||
});
|
||||
return this.colorize(s);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
colorizeOld(str) {
|
||||
return `<span style="color: rgb(${this.color[0]}, ${this.color[1]}, ${this.color[2]})">${str}</span>`
|
||||
},
|
||||
colorize(str) {
|
||||
let s = "<span>" + (str.replace(/\^([0-9])/g, (str, color) => `</span><span class="color-${color}">`)) + "</span>";
|
||||
|
||||
const styleDict = {
|
||||
'*': 'font-weight: bold;',
|
||||
'_': 'text-decoration: underline;',
|
||||
'~': 'text-decoration: line-through;',
|
||||
'=': 'text-decoration: underline line-through;',
|
||||
'r': 'text-decoration: none;font-weight: normal;',
|
||||
};
|
||||
|
||||
const styleRegex = /\^(\_|\*|\=|\~|\/|r)(.*?)(?=$|\^r|<\/em>)/;
|
||||
while (s.match(styleRegex)) { //Any better solution would be appreciated :P
|
||||
s = s.replace(styleRegex, (str, style, inner) => `<em style="${styleDict[style]}">${inner}</em>`)
|
||||
}
|
||||
return s.replace(/<span[^>]*><\/span[^>]*>/g, '');
|
||||
},
|
||||
escape(unsafe) {
|
||||
return String(unsafe)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
},
|
||||
},
|
||||
props: {
|
||||
templates: {
|
||||
type: Object,
|
||||
},
|
||||
args: {
|
||||
type: Array,
|
||||
},
|
||||
template: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
templateId: {
|
||||
type: String,
|
||||
default: CONFIG.defaultTemplateId,
|
||||
},
|
||||
multiline: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
color: { //deprecated
|
||||
type: Array,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
44
resources/[gameplay]/chat/html/Suggestions.js
Normal file
44
resources/[gameplay]/chat/html/Suggestions.js
Normal file
@@ -0,0 +1,44 @@
|
||||
Vue.component('suggestions', {
|
||||
template: '#suggestions_template',
|
||||
props: ['message', 'suggestions'],
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
currentSuggestions() {
|
||||
if (this.message === '') {
|
||||
return [];
|
||||
}
|
||||
const currentSuggestions = this.suggestions.filter((s) => {
|
||||
if (!s.name.startsWith(this.message)) {
|
||||
const suggestionSplitted = s.name.split(' ');
|
||||
const messageSplitted = this.message.split(' ');
|
||||
for (let i = 0; i < messageSplitted.length; i += 1) {
|
||||
if (i >= suggestionSplitted.length) {
|
||||
return i < suggestionSplitted.length + s.params.length;
|
||||
}
|
||||
if (suggestionSplitted[i] !== messageSplitted[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}).slice(0, CONFIG.suggestionLimit);
|
||||
|
||||
currentSuggestions.forEach((s) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
s.disabled = !s.name.startsWith(this.message);
|
||||
|
||||
s.params.forEach((p, index) => {
|
||||
const wType = (index === s.params.length - 1) ? '.' : '\\S';
|
||||
const regex = new RegExp(`${s.name} (?:\\w+ ){${index}}(?:${wType}*)$`, 'g');
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
p.disabled = this.message.match(regex) == null;
|
||||
});
|
||||
});
|
||||
return currentSuggestions;
|
||||
},
|
||||
},
|
||||
methods: {},
|
||||
});
|
||||
19
resources/[gameplay]/chat/html/config.default.js
Normal file
19
resources/[gameplay]/chat/html/config.default.js
Normal file
@@ -0,0 +1,19 @@
|
||||
// DO NOT EDIT THIS FILE
|
||||
// Copy it to `config.js` and edit it
|
||||
window.CONFIG = {
|
||||
defaultTemplateId: 'default', //This is the default template for 2 args1
|
||||
defaultAltTemplateId: 'defaultAlt', //This one for 1 arg
|
||||
templates: { //You can add static templates here
|
||||
'default': '<b>{0}</b>: {1}',
|
||||
'defaultAlt': '{0}',
|
||||
'print': '<pre>{0}</pre>',
|
||||
'example:important': '<h1>^2{0}</h1>'
|
||||
},
|
||||
fadeTimeout: 7000,
|
||||
suggestionLimit: 5,
|
||||
style: {
|
||||
background: 'rgba(52, 73, 94, 0.7)',
|
||||
width: '38%',
|
||||
height: '22%',
|
||||
}
|
||||
};
|
||||
127
resources/[gameplay]/chat/html/index.css
Normal file
127
resources/[gameplay]/chat/html/index.css
Normal file
@@ -0,0 +1,127 @@
|
||||
.color-0{color: #ffffff;}
|
||||
.color-1{color: #ff4444;}
|
||||
.color-2{color: #99cc00;}
|
||||
.color-3{color: #ffbb33;}
|
||||
.color-4{color: #0099cc;}
|
||||
.color-5{color: #33b5e5;}
|
||||
.color-6{color: #aa66cc;}
|
||||
.color-8{color: #cc0000;}
|
||||
.color-9{color: #cc0068;}
|
||||
|
||||
* {
|
||||
font-family: 'Lato', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.no-grow {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
em {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
#app {
|
||||
font-family: 'Lato', Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.chat-window {
|
||||
position: absolute;
|
||||
top: 1.5%;
|
||||
left: 0.8%;
|
||||
width: 38%;
|
||||
height: 22%;
|
||||
max-width: 1000px;
|
||||
background-color: rgba(52, 73, 94, 0.7);
|
||||
-webkit-animation-duration: 2s;
|
||||
}
|
||||
|
||||
|
||||
.chat-messages {
|
||||
position: relative;
|
||||
height: 95%;
|
||||
font-size: 1.8vh;
|
||||
margin: 1%;
|
||||
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
|
||||
.chat-input {
|
||||
font-size: 1.65vh;
|
||||
position: absolute;
|
||||
|
||||
top: 23.8%;
|
||||
left: 0.8%;
|
||||
width: 38%;
|
||||
max-width: 1000px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.prefix {
|
||||
font-size: 1.8vh;
|
||||
position: absolute;
|
||||
margin-top: 0.5%;
|
||||
left: 0.208%;
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-size: 1.65vh;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
padding: 1%;
|
||||
padding-left: 3.5%;
|
||||
color: white;
|
||||
background-color: rgba(44, 62, 80, 1.0);
|
||||
width: 100%;
|
||||
border-width: 0;
|
||||
height: 3.15%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
textarea:focus, input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.msg {
|
||||
margin-bottom: 0.28%;
|
||||
}
|
||||
|
||||
.multiline {
|
||||
margin-left: 4%;
|
||||
text-indent: -1.2rem;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.suggestions {
|
||||
list-style-type: none;
|
||||
padding: 0.5%;
|
||||
padding-left: 1.4%;
|
||||
font-size: 1.65vh;
|
||||
box-sizing: border-box;
|
||||
color: white;
|
||||
background-color: rgba(44, 62, 80, 1.0);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.help {
|
||||
color: #b0bbbd;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
color: #b0bbbd;
|
||||
}
|
||||
|
||||
.suggestion {
|
||||
margin-bottom: 0.5%;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
117
resources/[gameplay]/chat/html/index.html
Normal file
117
resources/[gameplay]/chat/html/index.html
Normal file
@@ -0,0 +1,117 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<link href="vendor/latofonts.css" rel="stylesheet">
|
||||
<link href="vendor/flexboxgrid.6.3.1.min.css" rel="stylesheet"></link>
|
||||
<link href="vendor/animate.3.5.2.min.css" rel="stylesheet"></link>
|
||||
<link href="index.css" rel="stylesheet"></link>
|
||||
|
||||
<script src="vendor/vue.2.3.3.min.js" type="text/javascript"></script>
|
||||
<script src="config.default.js" type="text/javascript"></script>
|
||||
<script src="config.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
<!-- App Template -->
|
||||
<script type="text/x-template" id="app_template">
|
||||
<div id="app">
|
||||
<div class="chat-window" :style="this.style" :class="{ 'fadeOut animated': !showWindow, 'hidden': shouldHide }">
|
||||
<div class="chat-messages" ref="messages">
|
||||
<message v-for="msg in messages"
|
||||
:templates="templates"
|
||||
:multiline="msg.multiline"
|
||||
:args="msg.args"
|
||||
:color="msg.color"
|
||||
:template="msg.template"
|
||||
:template-id="msg.templateId"
|
||||
:key="msg">
|
||||
</message>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-input" v-show="showInput">
|
||||
<div>
|
||||
<span class="prefix">➤</span>
|
||||
<textarea v-model="message"
|
||||
ref="input"
|
||||
type="text"
|
||||
autofocus
|
||||
spellcheck="false"
|
||||
@keyup.esc="hideInput"
|
||||
@keyup="keyUp"
|
||||
@keydown="keyDown"
|
||||
@keypress.enter.prevent="send">
|
||||
</textarea>
|
||||
</div>
|
||||
<suggestions :message="message" :suggestions="suggestions">
|
||||
</suggestions>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<!-- Message Template -->
|
||||
<script type="text/x-template" id="message_template">
|
||||
<div class="msg" :class="{ multiline }">
|
||||
<span v-html="textEscaped"></span>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<!-- Suggestions Template -->
|
||||
<script type="text/x-template" id="suggestions_template">
|
||||
<div class="suggestions-wrap" v-show="currentSuggestions.length > 0">
|
||||
<ul class="suggestions">
|
||||
<li class="suggestion" v-for="s in currentSuggestions">
|
||||
<p>
|
||||
<span :class="{ 'disabled': s.disabled }">
|
||||
{{s.name}}
|
||||
</span>
|
||||
<span class="param"
|
||||
v-for="(p, index) in s.params"
|
||||
:class="{ 'disabled': p.disabled }">
|
||||
[{{p.name}}]
|
||||
</span>
|
||||
</p>
|
||||
<small class="help">
|
||||
<template v-if="!s.disabled">
|
||||
{{s.help}}
|
||||
</template>
|
||||
<template v-for="p in s.params" v-if="!p.disabled">
|
||||
{{p.help}}
|
||||
</template>
|
||||
</small>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script type="text/javascript" src="./Suggestions.js"></script>
|
||||
<script type="text/javascript" src="./Message.js"></script>
|
||||
<script type="text/javascript" src="./App.js"></script>
|
||||
|
||||
<!-- Main Entry -->
|
||||
<script type="text/javascript">
|
||||
window.post = (url, data) => {
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('POST', url, true);
|
||||
request.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||
request.send(data);
|
||||
}
|
||||
|
||||
const instance = new Vue({
|
||||
el: '#app',
|
||||
render: h => h(APP),
|
||||
});
|
||||
|
||||
window.emulate = (type, detail = {}) => {
|
||||
detail.type = type;
|
||||
window.dispatchEvent(new CustomEvent('message', {
|
||||
detail,
|
||||
}));
|
||||
};
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
11
resources/[gameplay]/chat/html/vendor/animate.3.5.2.min.css
vendored
Normal file
11
resources/[gameplay]/chat/html/vendor/animate.3.5.2.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
resources/[gameplay]/chat/html/vendor/flexboxgrid.6.3.1.min.css
vendored
Normal file
1
resources/[gameplay]/chat/html/vendor/flexboxgrid.6.3.1.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
resources/[gameplay]/chat/html/vendor/fonts/LatoBold.woff2
vendored
Normal file
BIN
resources/[gameplay]/chat/html/vendor/fonts/LatoBold.woff2
vendored
Normal file
Binary file not shown.
BIN
resources/[gameplay]/chat/html/vendor/fonts/LatoBold2.woff2
vendored
Normal file
BIN
resources/[gameplay]/chat/html/vendor/fonts/LatoBold2.woff2
vendored
Normal file
Binary file not shown.
BIN
resources/[gameplay]/chat/html/vendor/fonts/LatoLight.woff2
vendored
Normal file
BIN
resources/[gameplay]/chat/html/vendor/fonts/LatoLight.woff2
vendored
Normal file
Binary file not shown.
BIN
resources/[gameplay]/chat/html/vendor/fonts/LatoLight2.woff2
vendored
Normal file
BIN
resources/[gameplay]/chat/html/vendor/fonts/LatoLight2.woff2
vendored
Normal file
Binary file not shown.
BIN
resources/[gameplay]/chat/html/vendor/fonts/LatoRegular.woff2
vendored
Normal file
BIN
resources/[gameplay]/chat/html/vendor/fonts/LatoRegular.woff2
vendored
Normal file
Binary file not shown.
BIN
resources/[gameplay]/chat/html/vendor/fonts/LatoRegular2.woff2
vendored
Normal file
BIN
resources/[gameplay]/chat/html/vendor/fonts/LatoRegular2.woff2
vendored
Normal file
Binary file not shown.
48
resources/[gameplay]/chat/html/vendor/latofonts.css
vendored
Normal file
48
resources/[gameplay]/chat/html/vendor/latofonts.css
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Lato Light'), local('Lato-Light'), url(fonts/LatoLight.woff2);
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Lato Light'), local('Lato-Light'), url(fonts/LatoLight2.woff2);
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Lato Regular'), local('Lato-Regular'), url(fonts/LatoRegular.woff2);
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Lato Regular'), local('Lato-Regular'), url(fonts/LatoRegular2.woff2);
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Lato Bold'), local('Lato-Bold'), url(fonts/LatoBold.woff2);
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Lato Bold'), local('Lato-Bold'), url(fonts/LatoBold2.woff2);
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
|
||||
}
|
||||
8
resources/[gameplay]/chat/html/vendor/vue.2.3.3.min.js
vendored
Normal file
8
resources/[gameplay]/chat/html/vendor/vue.2.3.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
79
resources/[gameplay]/chat/sv_chat.lua
Normal file
79
resources/[gameplay]/chat/sv_chat.lua
Normal file
@@ -0,0 +1,79 @@
|
||||
RegisterServerEvent('chat:init')
|
||||
RegisterServerEvent('chat:addTemplate')
|
||||
RegisterServerEvent('chat:addMessage')
|
||||
RegisterServerEvent('chat:addSuggestion')
|
||||
RegisterServerEvent('chat:removeSuggestion')
|
||||
RegisterServerEvent('_chat:messageEntered')
|
||||
RegisterServerEvent('chat:clear')
|
||||
RegisterServerEvent('__cfx_internal:commandFallback')
|
||||
|
||||
AddEventHandler('_chat:messageEntered', function(author, color, message)
|
||||
if not message or not author then
|
||||
return
|
||||
end
|
||||
|
||||
TriggerEvent('chatMessage', source, author, message)
|
||||
|
||||
if not WasEventCanceled() then
|
||||
TriggerClientEvent('chatMessage', -1, author, { 255, 255, 255 }, message)
|
||||
end
|
||||
|
||||
print(author .. '^7: ' .. message .. '^7')
|
||||
end)
|
||||
|
||||
AddEventHandler('__cfx_internal:commandFallback', function(command)
|
||||
local name = GetPlayerName(source)
|
||||
|
||||
TriggerEvent('chatMessage', source, name, '/' .. command)
|
||||
|
||||
if not WasEventCanceled() then
|
||||
TriggerClientEvent('chatMessage', -1, name, { 255, 255, 255 }, '/' .. command)
|
||||
end
|
||||
|
||||
CancelEvent()
|
||||
end)
|
||||
|
||||
-- player join messages
|
||||
AddEventHandler('chat:init', function()
|
||||
TriggerClientEvent('chatMessage', -1, '', { 255, 255, 255 }, '^2* ' .. GetPlayerName(source) .. ' joined.')
|
||||
end)
|
||||
|
||||
AddEventHandler('playerDropped', function(reason)
|
||||
TriggerClientEvent('chatMessage', -1, '', { 255, 255, 255 }, '^2* ' .. GetPlayerName(source) ..' left (' .. reason .. ')')
|
||||
end)
|
||||
|
||||
RegisterCommand('say', function(source, args, rawCommand)
|
||||
TriggerClientEvent('chatMessage', -1, (source == 0) and 'console' or GetPlayerName(source), { 255, 255, 255 }, rawCommand:sub(5))
|
||||
end)
|
||||
|
||||
-- command suggestions for clients
|
||||
local function refreshCommands(player)
|
||||
if GetRegisteredCommands then
|
||||
local registeredCommands = GetRegisteredCommands()
|
||||
|
||||
local suggestions = {}
|
||||
|
||||
for _, command in ipairs(registeredCommands) do
|
||||
if IsPlayerAceAllowed(player, ('command.%s'):format(command.name)) then
|
||||
table.insert(suggestions, {
|
||||
name = '/' .. command.name,
|
||||
help = ''
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
TriggerClientEvent('chat:addSuggestions', player, suggestions)
|
||||
end
|
||||
end
|
||||
|
||||
AddEventHandler('chat:init', function()
|
||||
refreshCommands(source)
|
||||
end)
|
||||
|
||||
AddEventHandler('onServerResourceStart', function(resName)
|
||||
Wait(500)
|
||||
|
||||
for _, player in ipairs(GetPlayers()) do
|
||||
refreshCommands(player)
|
||||
end
|
||||
end)
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
server_script 'irc.lua'
|
||||
@@ -1,4 +0,0 @@
|
||||
local reflection = clr.System.Reflection
|
||||
local assembly = reflection.Assembly.LoadFrom('resources/[gameplay]/irc/ChatSharp.dll')
|
||||
|
||||
dofile('resources/[gameplay]/irc/irc_run.lua')
|
||||
@@ -1,91 +0,0 @@
|
||||
local chatSharp = clr.ChatSharp
|
||||
|
||||
local client = chatSharp.IrcClient('irc.rizon.net', chatSharp.IrcUser('citimate', 'mateyate'), false)
|
||||
|
||||
-- temporary workaround for connections that never triggered playerActivated but triggered playerDropped
|
||||
local activatedPlayers = {}
|
||||
|
||||
client.ConnectionComplete:add(function(s : object, e : System.EventArgs) : void
|
||||
client:JoinChannel('#meow')
|
||||
end)
|
||||
|
||||
-- why is 'received' even misspelled here?
|
||||
client.ChannelMessageRecieved:add(function(s : object, e : ChatSharp.Events.PrivateMessageEventArgs) : void
|
||||
local msg = e.PrivateMessage
|
||||
|
||||
TriggerClientEvent('chatMessage', -1, msg.User.Nick, { 0, 0x99, 255 }, msg.Message)
|
||||
end)
|
||||
|
||||
AddEventHandler('playerActivated', function()
|
||||
client:SendMessage('* ' .. GetPlayerName(source) .. '(' .. GetPlayerGuid(source) .. '@' .. GetPlayerEP(source) .. ') joined the server', '#fourdeltaone')
|
||||
table.insert(activatedPlayers, GetPlayerGuid(source))
|
||||
end)
|
||||
|
||||
AddEventHandler('playerDropped', function()
|
||||
-- find out if this connection ever triggered playerActivated
|
||||
for index,guid in pairs(activatedPlayers) do
|
||||
if guid == playerGuid then
|
||||
-- show player dropping connection in chat
|
||||
client:SendMessage('* ' .. GetPlayerName(source) .. '(' .. GetPlayerGuid(source) .. '@' .. GetPlayerEP(source) .. ') left the server', '#fourdeltaone')
|
||||
table.remove(activatedPlayers, index)
|
||||
return
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
AddEventHandler('chatMessage', function(source, name, message)
|
||||
print('hey there ' .. name)
|
||||
|
||||
local displayMessage = gsub(message, '^%d', '')
|
||||
|
||||
-- ignore zero-length messages
|
||||
if string.len(displayMessage) == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
-- ignore chat messages that are actually commands
|
||||
if string.sub(displayMessage, 1, 1) == "/" then
|
||||
return
|
||||
end
|
||||
|
||||
client:SendMessage('[' .. tostring(GetPlayerName(source)) .. ']: ' .. displayMessage, '#fourdeltaone')
|
||||
end)
|
||||
|
||||
AddEventHandler('onPlayerKilled', function(playerId, attackerId, reason, position)
|
||||
local player = GetPlayerByServerId(playerId)
|
||||
local attacker = GetPlayerByServerId(attackerId)
|
||||
|
||||
local reasonString = 'killed'
|
||||
|
||||
if reason == 0 or reason == 56 or reason == 1 or reason == 2 then
|
||||
reasonString = 'meleed'
|
||||
elseif reason == 3 then
|
||||
reasonString = 'knifed'
|
||||
elseif reason == 4 or reason == 6 or reason == 18 or reason == 51 then
|
||||
reasonString = 'bombed'
|
||||
elseif reason == 5 or reason == 19 then
|
||||
reasonString = 'burned'
|
||||
elseif reason == 7 or reason == 9 then
|
||||
reasonString = 'pistoled'
|
||||
elseif reason == 10 or reason == 11 then
|
||||
reasonString = 'shotgunned'
|
||||
elseif reason == 12 or reason == 13 or reason == 52 then
|
||||
reasonString = 'SMGd'
|
||||
elseif reason == 14 or reason == 15 or reason == 20 then
|
||||
reasonString = 'assaulted'
|
||||
elseif reason == 16 or reason == 17 then
|
||||
reasonString = 'sniped'
|
||||
elseif reason == 49 or reason == 50 then
|
||||
reasonString = 'ran over'
|
||||
end
|
||||
|
||||
client:SendMessage('* ' .. attacker.name .. ' ' .. reasonString .. ' ' .. player.name, '#fourdeltaone')
|
||||
end)
|
||||
|
||||
client:ConnectAsync()
|
||||
|
||||
AddEventHandler('onResourceStop', function(name)
|
||||
if name == GetInvokingResource() then
|
||||
client:Quit('Resource stopping.')
|
||||
end
|
||||
end)
|
||||
@@ -1,5 +0,0 @@
|
||||
dependency 'obituary'
|
||||
|
||||
description 'death messages using the obituary resource'
|
||||
|
||||
client_script 'deathmessages.lua'
|
||||
@@ -1,42 +0,0 @@
|
||||
AddEventHandler('onPlayerDied', function(playerId, reason, position)
|
||||
local player = GetPlayerByServerId(playerId)
|
||||
|
||||
if player then
|
||||
exports.obituary:printObituary('<b>%s</b> died.', player.name)
|
||||
end
|
||||
end)
|
||||
|
||||
AddEventHandler('onPlayerKilled', function(playerId, attackerId, reason, position)
|
||||
local player = GetPlayerByServerId(playerId)
|
||||
local attacker = GetPlayerByServerId(attackerId)
|
||||
|
||||
local reasonString = 'killed'
|
||||
|
||||
if reason == 0 or reason == 56 or reason == 1 or reason == 2 then
|
||||
reasonString = 'meleed'
|
||||
elseif reason == 3 then
|
||||
reasonString = 'knifed'
|
||||
elseif reason == 4 or reason == 6 or reason == 18 or reason == 51 then
|
||||
reasonString = 'bombed'
|
||||
elseif reason == 5 or reason == 19 then
|
||||
reasonString = 'burned'
|
||||
elseif reason == 7 or reason == 9 then
|
||||
reasonString = 'pistoled'
|
||||
elseif reason == 10 or reason == 11 then
|
||||
reasonString = 'shotgunned'
|
||||
elseif reason == 12 or reason == 13 or reason == 52 then
|
||||
reasonString = 'SMGd'
|
||||
elseif reason == 14 or reason == 15 or reason == 20 then
|
||||
reasonString = 'assaulted'
|
||||
elseif reason == 16 or reason == 17 then
|
||||
reasonString = 'sniped'
|
||||
elseif reason == 49 or reason == 50 then
|
||||
reasonString = 'ran over'
|
||||
end
|
||||
|
||||
echo("obituary-deaths: onPlayerKilled\n")
|
||||
|
||||
if player and attacker then
|
||||
exports.obituary:printObituary('<b>%s</b> %s <b>%s</b>.', attacker.name, reasonString, player.name)
|
||||
end
|
||||
end)
|
||||
@@ -1,7 +0,0 @@
|
||||
dependency 'channelfeed'
|
||||
|
||||
client_script 'obituary.lua'
|
||||
|
||||
export 'printObituary'
|
||||
|
||||
files 'obituary.css'
|
||||
@@ -1,46 +0,0 @@
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
src: url('nui://channelfeed/client/fonts/roboto-condensed.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
src: url('nui://channelfeed/client/fonts/roboto-regular.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
#channel-obituary
|
||||
{
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#channel-obituary > div
|
||||
{
|
||||
width: 100%;
|
||||
|
||||
font-family: 'Roboto';
|
||||
}
|
||||
|
||||
#channel-obituary div.item
|
||||
{
|
||||
background-color: rgba(50, 50, 50, .6);
|
||||
padding: 4px;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
border-radius: 4px;
|
||||
color: #eee;
|
||||
margin-bottom: 3px;
|
||||
|
||||
font-size: 65%;
|
||||
}
|
||||
|
||||
#channel-obituary div.item b
|
||||
{
|
||||
font-family: 'Roboto Condensed';
|
||||
font-size: 125%;
|
||||
font-weight: normal;
|
||||
color: #09f;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
AddEventHandler('onClientResourceStart', function(name)
|
||||
if name == GetCurrentResource() then
|
||||
local x, y = GetHudPosition('HUD_RADAR')
|
||||
local w, h = GetHudSize('HUD_RADAR')
|
||||
|
||||
x = x - 0.01
|
||||
w = w + 0.02
|
||||
|
||||
if GetIsWidescreen() then
|
||||
x = x / 1.333
|
||||
w = w / 1.333
|
||||
end
|
||||
|
||||
exports.channelfeed:addChannel('obituary', {
|
||||
method = 'append',
|
||||
styleUrl = 'nui://obituary/obituary.css',
|
||||
styles = { -- temporary
|
||||
left = tostring(x * 100) .. '%',
|
||||
bottom = 'calc(' .. tostring((1 - y) * 100) .. '% + 10px)',
|
||||
width = tostring(w * 100) .. '%'
|
||||
},
|
||||
template = '<div class="item">{{{text}}}</div>'
|
||||
})
|
||||
end
|
||||
end)
|
||||
|
||||
function printObituary(format, ...)
|
||||
local args = table.pack(...)
|
||||
|
||||
for i = 1, args.n do
|
||||
if type(args[i]) == 'string' then
|
||||
args[i] = args[i]:gsub('<', '<')
|
||||
end
|
||||
end
|
||||
|
||||
echo("obituary: printObituary\n")
|
||||
|
||||
exports.channelfeed:printTo('obituary', {
|
||||
text = string.format(format, table.unpack(args))
|
||||
})
|
||||
end
|
||||
|
||||
--[[AddEventHandler('chatMessage', function(name, color, message)
|
||||
exports.channelfeed:printTo('obituary', {
|
||||
text = message:gsub('<', '<')
|
||||
})
|
||||
end)]]
|
||||
|
||||
AddEventHandler('onClientResourceStop', function()
|
||||
-- todo: remove channel
|
||||
end)
|
||||
Reference in New Issue
Block a user