Skip to content

Commit 44b45d2

Browse files
committed
Initial public release
0 parents  commit 44b45d2

21 files changed

Lines changed: 651 additions & 0 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.idea

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2021 Project Error
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
19+
OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<p align="center">
2+
<a href="https://projecterror.dev/" rel="noopener" target="_blank"><img width="150" src="https://i.tasoagc.dev/c1pD" alt="Material-UI logo"></a></p>
3+
</p>
4+
<h1 align="center">Project Error Basic Loading</h1>
5+
6+
<div align="center">
7+
A very basic, yet still robust and extendable, loading screen.
8+
</div>
9+
10+
<div align="center">
11+
12+
[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/project-error/pe-basicloading/master/LICENSE)
13+
![Discord](https://img.shields.io/discord/791854454760013827?label=Our%20Discord)
14+
</div>
15+
16+
<div align="center">
17+
![preview](https://i.imgur.com/aivxpfx.gif)
18+
</div>
19+
20+
## Features
21+
* Customizable tip section that can be switched through
22+
* Markdown support for tip section content. Also allows for navigation using the **Left** and **Right** arrow keys
23+
* Customizable animated backgrounds that smoothly transition
24+
* CSS/JS cursor implementation, allowing for its use while loading
25+
26+
## Quick Start
27+
1. Download the latest release from [here](https://github.com/project-error/pe-basicloading/releases/)
28+
2. Unzip and drag the `pe-basicloading` folder into your `resources` directory
29+
3. Add `ensure pe-basicloading` to your `server.cfg`
30+
31+
*Note: This resource uses the `MANUAL_SHUTDOWN` feature. You can learn more about that [here]()*
32+
33+
### Customize
34+
35+
### Adding Backgrounds
36+
37+
Adding or replacing images is very simple.
38+
39+
1. First ensure that the image is in the `web/backgrounds` folder.
40+
2. Then open the `web/config.js` file and add the exact filename for your image
41+
to the `BACKGROUND_IMAGES` array.
42+
43+
📁 **web/config.js**
44+
```js
45+
// An array of image files that are available in the `bg` folder
46+
export const BACKGROUND_IMAGES = [
47+
"1.jpg",
48+
"2.jpg",
49+
"3.jpg",
50+
"4.jpg",
51+
"5.jpg",
52+
"6.jpg",
53+
"7.jpg",
54+
"8.jpg",
55+
"YourNewImage.jpg"
56+
]
57+
```
58+
59+
### Adding Tips
60+
This loadscreen employs dynamic tooltips as one of its main features. These are
61+
defined in the `web/config.js` file under the `LOADSCREEN_TIPS` array.
62+
63+
The data structure of tooltips are fairly simple, they are defined with a `title`
64+
and a `content` property. Here's a simple example.
65+
66+
```
67+
{
68+
title: 'My Tooltip Title',
69+
content: 'This is the markdown compatible content that will be shown on screen'
70+
}
71+
```
72+
73+
**Markdown Compatible Content**
74+
75+
An important thing to note with **Tips** is that the content property is **markdown compatible**.
76+
Meaning you can employ the same syntax that you might be comfortable with while for example using Discord.
77+
78+
**Structure in Config**
79+
```
80+
{
81+
title: 'Markdown Render',
82+
content: '**This is bold**. But this is not. [This is a link!](https://github.com/project-error/pe-basicloading/#)'
83+
}
84+
```
85+
**Rendered Output**
86+
![img](https://i.tasoagc.dev/SB6A)
87+
88+
The markdown parser utilized is fully compliant with the original `Markdown.pl` spec and around 60% compliant
89+
with `CommonMarkdown` spec. `Github Flavored Markdown` can also be enabled in the `config.js`.
90+
91+
## Script Integration
92+
93+
This loading screen can be implemented directly into scripts easily. As a standalone script, it will wait
94+
for the `spawnPlayer` event to be triggered by spawnmanager before shutting down. If you wish to control its
95+
behavior directly, use the following convars and exports
96+
97+
**Convars**
98+
99+
`pe-basicloading:disableAutoShutdown`
100+
* Description: Controls whether script should auto shutdown loading on `playerSpawned` event
101+
* Usage: `set pe-basicloading:disableAutoShutdown 1`
102+
103+
**Exports**
104+
105+
`shutdown`
106+
* Description: Will shutdown the loading frame and cleanup.
107+
* Usage: `exports[pe-basicloading]:shutdown()`
108+
109+
## License
110+
[Licensed under MIT](https://opensource.org/licenses/MIT)

client.lua

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
local disableManualShutdown = GetConvarInt('pe-basicloading:disableAutoShutdown', 0) == 1
2+
3+
local function shutdownHandler()
4+
CreateThread(function()
5+
SendLoadingScreenMessage(json.encode({
6+
fullyLoaded = true
7+
}))
8+
Wait(5000)
9+
ShutdownLoadingScreenNui(true)
10+
end)
11+
end
12+
13+
if not disableManualShutdown then
14+
AddEventHandler('playerSpawned', shutdownHandler)
15+
end
16+
17+
exports('shutdown', shutdownHandler)

docs/markdown.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Enabled Markdown
2+

fxmanifest.lua

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
fx_version 'cerulean'
2+
game 'gta5'
3+
4+
author 'Project Error'
5+
description 'A basic yet robust and extendable loading screen'
6+
version '1.0.0'
7+
repository 'https://github.com/project-error/pe-basicloading'
8+
9+
loadscreen 'web/index.html'
10+
11+
client_script 'client.lua'
12+
13+
loadscreen_manual_shutdown 'yes'
14+
15+
files {
16+
"config.json",
17+
"web/**/*"
18+
}

web/assets/images/cursor.svg

Lines changed: 4 additions & 0 deletions
Loading

web/assets/markdown_parser.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import {ENABLE_GFM_MARKDOWN, LOADSCREEN_TIPS} from "../config.js";
2+
import {cleanUrl} from "./utils.js";
3+
4+
// We enable all settings for now
5+
const md = window.markdownit({
6+
html: true,
7+
linkify: true,
8+
typographer: true,
9+
gfm: ENABLE_GFM_MARKDOWN
10+
})
11+
12+
window.__openUrl = (url) => window.invokeNative ? window.invokeNative('openUrl', url) : window.open(url)
13+
14+
const renderer = {
15+
link(href, title, text) {
16+
href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
17+
if (href === null) {
18+
return text;
19+
}
20+
// let out = `<a href="${escape(href)}"`;
21+
let out = `<a`
22+
if (title) {
23+
out += ` title="${title}"`;
24+
}
25+
// NUI Specific
26+
out += ` onclick="window.__openUrl('${href}')"`
27+
out += `>${text}</a>`;
28+
return out;
29+
}
30+
}
31+
32+
marked.use({ renderer })
33+
34+
export const parsedMdTips = LOADSCREEN_TIPS.map(({title, content}) => {
35+
const renderedMarkup = marked(content)
36+
return {title, content: renderedMarkup}
37+
})

web/assets/script.js

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import {
2+
TIP_CHANGE_INTERVAL,
3+
BACKGROUND_CHANGE_INTERVAL,
4+
BACKGROUND_IMAGES,
5+
ENABLE_CURSOR
6+
} from '../config.js'
7+
8+
import { parsedMdTips } from "./markdown_parser.js";
9+
10+
/**
11+
* @typedef {Object} TooltipObject
12+
* @property {string} title - The title of the tooltip
13+
* @property {string} content - The HTML contents
14+
**/
15+
16+
const headerEl = $('#tip-header')
17+
const contentEl = $('#tip-content')
18+
const hintHelpTxtEl = $('#hint-help-text')
19+
const containerEL = $('#container')
20+
const bgImgEl = $('#bgImg')
21+
const spinnerEl = $('#spinner')
22+
23+
24+
// We store the current tipInterval if any here
25+
let tipInterval = null
26+
27+
// We store the current tipIndex here
28+
let currentTipIndex
29+
30+
/**
31+
* Set the current tooltip
32+
* @param tooltipObj {TooltipObject} - The tooltip object
33+
* @param index {number} - The array index for this particular tooltip object
34+
**/
35+
const setCurrentTip = ({title, content}, index) => {
36+
headerEl.fadeOut("fast", () => {
37+
headerEl.html(title)
38+
headerEl.fadeIn("fast")
39+
})
40+
contentEl.fadeOut("fast", () => {
41+
contentEl.html(content)
42+
contentEl.fadeIn("fast")
43+
})
44+
hintHelpTxtEl.text(`Browse Tips ${index + 1}/${parsedMdTips.length}`)
45+
currentTipIndex = index
46+
47+
if (tipInterval) {
48+
clearInterval(tipInterval)
49+
}
50+
51+
tipInterval = setInterval(() => showNextTip(), TIP_CHANGE_INTERVAL)
52+
}
53+
54+
/**
55+
* Handle the left arrow "prev" tooltip
56+
**/
57+
const showPrevTip = () => {
58+
const prevTipIdx = currentTipIndex - 1
59+
const finalTipIdx = parsedMdTips.length - 1
60+
61+
clearInterval(tipInterval)
62+
63+
if (prevTipIdx < 0) {
64+
const tipObj = parsedMdTips[finalTipIdx]
65+
setCurrentTip(tipObj, finalTipIdx)
66+
} else {
67+
const tipObj = parsedMdTips[prevTipIdx]
68+
setCurrentTip(tipObj, prevTipIdx)
69+
}
70+
}
71+
72+
/**
73+
* Handle the right arrow "next" tooltip
74+
**/
75+
const showNextTip = () => {
76+
const nextTipIdx = currentTipIndex + 1
77+
78+
if (nextTipIdx > parsedMdTips.length - 1) {
79+
const tipObj = parsedMdTips[0]
80+
setCurrentTip(tipObj, 0)
81+
} else {
82+
const tipObj = parsedMdTips[nextTipIdx]
83+
setCurrentTip(tipObj, nextTipIdx)
84+
}
85+
}
86+
87+
/**
88+
* Find random tooltip and set it as current
89+
**/
90+
const setRandomTip = () => {
91+
const randomTipIndex = Math.floor(Math.random() * parsedMdTips.length)
92+
const randomTipObj = parsedMdTips[randomTipIndex]
93+
setCurrentTip(randomTipObj, randomTipIndex)
94+
}
95+
96+
/**
97+
* Fade out the whole loading screen container
98+
**/
99+
const fadeoutLoadingScreen = () => {
100+
containerEL.fadeOut('slow')
101+
}
102+
103+
let currentBgIdx = 0
104+
105+
/**
106+
* Start the interval for background changes
107+
**/
108+
const startBackgroundInterval = () => {
109+
setInterval(() => {
110+
111+
const nextImgIdx = currentBgIdx + 1
112+
const imgIdx = (nextImgIdx > BACKGROUND_IMAGES.length - 1) ? 0 : nextImgIdx
113+
114+
const bgFileName = BACKGROUND_IMAGES[imgIdx]
115+
116+
bgImgEl.css("background-image", `url(backgrounds/${bgFileName})`)
117+
118+
currentBgIdx = imgIdx
119+
}, BACKGROUND_CHANGE_INTERVAL)
120+
}
121+
122+
/* Listeners */
123+
124+
window.addEventListener('DOMContentLoaded', () => {
125+
startBackgroundInterval()
126+
setRandomTip()
127+
spinnerEl.fadeIn()
128+
})
129+
130+
window.addEventListener('keydown', (e) => {
131+
if (e.code === 'ArrowLeft') {
132+
showPrevTip()
133+
} else if (e.code === 'ArrowRight') {
134+
showNextTip()
135+
}
136+
})
137+
138+
window.addEventListener('message', (e) => {
139+
// We get this from the client scripts
140+
if (e.data.fullyLoaded) {
141+
return fadeoutLoadingScreen()
142+
}
143+
})
144+
145+
if (ENABLE_CURSOR) {
146+
const cursor = document.getElementById('cursor')
147+
148+
cursor.style.visibility = 'visible'
149+
150+
window.addEventListener("mousemove", e => {
151+
// These offsets are related to the size of the SVG
152+
// for now this is fine
153+
const x = e.pageX - cursor.offsetWidth + 20
154+
const y = e.pageY - 2
155+
156+
cursor.style.left = `${x}px`
157+
cursor.style.top = `${y}px`
158+
})
159+
160+
}

0 commit comments

Comments
 (0)