├── fxmanifest.lua
├── README.md
├── nui
├── style.css
├── index.html
└── script.js
└── client
└── camera.lua
/fxmanifest.lua:
--------------------------------------------------------------------------------
1 | fx_version "cerulean"
2 | game "gta5"
3 | lua54 "yes"
4 |
5 | version "1.0.0"
6 | name "cuchi_guide"
7 | author "Cu-chi"
8 | description "Camera editor"
9 |
10 | client_scripts {
11 | "client/camera.lua"
12 | }
13 |
14 | ui_page "nui/index.html"
15 |
16 | files {
17 | "nui/*"
18 | }
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # cuchi_cameraEditor
2 | Easily create a camera with settings
3 | 
4 |
5 | # Tutorial
6 | Use the `createCam` command to start. Then copy the camera settings and use it in your code or wherever you want.
7 |
--------------------------------------------------------------------------------
/nui/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
6 | h1 {
7 | margin: 10px;
8 | }
9 |
10 | button {
11 | outline: none;
12 | font-family: Arial, Helvetica, sans-serif;
13 | color: #0D0B09;
14 | background-color: #cdc3b1;
15 | border-radius: 4px;
16 | font-size: medium;
17 | }
18 |
19 | button:active {
20 | opacity: 75%;
21 | }
22 |
23 | #blured {
24 | background-color: #cdc3b1;
25 | width: 100%;
26 | height: 100%;
27 | position: absolute;
28 | filter: blur(120px);
29 | z-index: -1;
30 | }
31 |
32 | .indicator {
33 | outline: none;
34 | background-color: #0D0B09;
35 | border: solid 2px #cdc3b1;
36 | border-radius: 4px;
37 | color: #F1EFEC;
38 | font-family: Arial, Helvetica, sans-serif;
39 | font-size: medium;
40 | width: 3em;
41 | text-align: center;
42 | }
43 |
44 | #container {
45 | position: absolute;
46 | display: none;
47 | flex-direction: column;
48 | align-items: center;
49 | background-color: #0D0B09;
50 | border-radius: 8px;
51 | padding: 10px;
52 | margin: 40px;
53 | gap: 5px;
54 | font-family: Arial, Helvetica, sans-serif;
55 | color: #F1EFEC;
56 | }
57 |
58 | .category {
59 | display: flex;
60 | gap: 10px;
61 | justify-content: center;
62 | align-items: center;
63 | }
64 |
--------------------------------------------------------------------------------
/nui/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Cuchi Guide Camera
8 |
9 |
10 |
11 |
12 |
Camera Editor
13 |
14 |
Rotate X
15 |
16 |
17 |
18 |
0
19 |
20 |
21 |
Rotate Y
22 |
23 |
24 |
25 |
0
26 |
27 |
28 |
Rotate Z
29 |
30 |
31 |
32 |
0
33 |
34 |
35 |
36 |
Position X
37 |
38 |
39 |
40 |
0
41 |
42 |
43 |
Position Y
44 |
45 |
46 |
47 |
0
48 |
49 |
50 |
Position Z
51 |
52 |
53 |
54 |
0
55 |
56 |
57 |
58 |
FOV
59 |
60 |
61 |
62 |
0
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/client/camera.lua:
--------------------------------------------------------------------------------
1 | local creatingCamera = false
2 | local camera
3 | local rotation
4 | local cameraPosition
5 | local FOV = 45.0
6 |
7 | function CreateCamera(coords)
8 | if not creatingCamera then
9 | creatingCamera = true
10 |
11 | cameraPosition = {
12 | x = tonumber(string.format("%.f", coords.x)) + 0.0,
13 | y = tonumber(string.format("%.f", coords.y)) + 0.0,
14 | z = tonumber(string.format("%.f", coords.z)) + 0.0
15 | }
16 |
17 | rotation = {
18 | x = 0.0,
19 | y = 0.0,
20 | z = 0.0
21 | }
22 |
23 | SendNUIMessage({
24 | show = true,
25 | pos = cameraPosition,
26 | rot = rotation
27 | })
28 |
29 | SetNuiFocus(true, true)
30 |
31 | while creatingCamera do
32 | camera = CreateCamWithParams("DEFAULT_SCRIPTED_CAMERA", cameraPosition.x, cameraPosition.y, cameraPosition.z, rotation.x, rotation.y, rotation.z, FOV, true, 2)
33 | RenderScriptCams(true, false, 0, false, false)
34 | SetFocusPosAndVel(cameraPosition.x, cameraPosition.y, cameraPosition.z, 0.0, 0.0, 0.0)
35 |
36 | Wait(0)
37 |
38 | DestroyCam(camera, true)
39 | end
40 | else
41 | creatingCamera = false
42 | DestroyCam(camera, true)
43 | RenderScriptCams(false, false, 0, false, false)
44 | SetFocusEntity(PlayerPedId())
45 | SendNUIMessage({ show = false, })
46 | SetNuiFocus(false, false)
47 | end
48 | end
49 |
50 | RegisterCommand("createCam", function()
51 | local playerPed = PlayerPedId()
52 | local playerPosition = GetEntityCoords(playerPed)
53 | CreateCamera(playerPosition)
54 | end, true)
55 |
56 | RegisterNUICallback("rotation", function(body, resultCallback)
57 | if body.axis == "x" then
58 | rotation.x += body.factor*1
59 | elseif body.axis == "y" then
60 | rotation.y += body.factor*1
61 | elseif body.axis == "z" then
62 | rotation.z += body.factor*1
63 | end
64 | resultCallback("ok")
65 | end)
66 |
67 | RegisterNUICallback("setRotation", function(body, resultCallback)
68 | local value = (tonumber(body.value) or 0.0) + 0.0
69 | if body.axis == "x" then
70 | rotation.x = value
71 | elseif body.axis == "y" then
72 | rotation.y = value
73 | elseif body.axis == "z" then
74 | rotation.z = value
75 | end
76 | resultCallback("ok")
77 | end)
78 |
79 | RegisterNUICallback("position", function(body, resultCallback)
80 | cameraPosition[body.axis] += body.factor*1
81 | resultCallback("ok")
82 | end)
83 |
84 | RegisterNUICallback("setPosition", function(body, resultCallback)
85 | local value = tonumber(body.value) or 0.0
86 | cameraPosition[body.axis] = value + 0.0
87 | resultCallback("ok")
88 | end)
89 |
90 | RegisterNUICallback("FOV", function(body, resultCallback)
91 | FOV += body.factor*1
92 | if FOV < 0 then
93 | FOV = 0.0
94 | elseif FOV > 130 then
95 | FOV = 130.0
96 | end
97 | resultCallback("ok")
98 | end)
99 |
100 | RegisterNUICallback("resetFOV", function(_, resultCallback)
101 | FOV = 45.0
102 | resultCallback("ok")
103 | end)
104 |
105 | RegisterNUICallback("close", function(_, resultCallback)
106 | creatingCamera = false
107 | DestroyCam(camera, true)
108 | RenderScriptCams(false, false, 0, false, false)
109 |
110 | SendNUIMessage({ show = false })
111 | SetNuiFocus(false, false)
112 | SetFocusEntity(PlayerPedId())
113 |
114 | resultCallback("ok")
115 | end)
116 |
117 | AddEventHandler("onResourceStop", function(resource)
118 | if resource == GetCurrentResourceName() then
119 | SetFocusEntity(PlayerPedId())
120 | end
121 | end)
122 |
--------------------------------------------------------------------------------
/nui/script.js:
--------------------------------------------------------------------------------
1 | const resName = GetParentResourceName();
2 | let rotations = { x: 0, y: 0, z: 0 };
3 | let defaultPositions = { x: 0, y: 0, z: 0 };
4 | let positions = { x: 0, y: 0, z: 0 };
5 | let FOV = 45;
6 | let fovInterval;
7 |
8 | window.addEventListener("message", (event) => {
9 | let data = event.data;
10 | let camCreatorElem = document.getElementById("container");
11 | if (data.show) {
12 | camCreatorElem.style.display = "flex";
13 | rotations = { x: 0, y: 0, z: 0 };
14 | defaultPositions = data.pos;
15 | positions = { ...defaultPositions };
16 | for (val of ["x", "y", "z"]) {
17 | document.getElementById("rot-"+val).innerText = rotations[val];
18 | document.getElementById("pos-"+val).innerText = positions[val];
19 | }
20 |
21 | document.getElementById("fov").innerText = FOV;
22 | }
23 | else {
24 | camCreatorElem.style.display = "none";
25 | }
26 | });
27 |
28 | window.addEventListener("keyup", (e) => {
29 | if (e.key === "Escape" || e.key === "Backspace") {
30 | fetch("https://"+resName+"/close", {
31 | method: "POST",
32 | body: null
33 | });
34 | }
35 | })
36 |
37 | for (val of ["x", "y", "z"]) {
38 | let value = val
39 | let interval;
40 |
41 | let positiveElem = document.getElementById("p-rot-"+value)
42 | positiveElem.onmousedown = () => {
43 | interval = setInterval(function() {
44 | fetch("https://"+resName+"/rotation", {
45 | method: "POST",
46 | body: JSON.stringify({
47 | axis: value,
48 | factor: 1
49 | })
50 | });
51 | rotations[value]++;
52 | if (rotations[value] % 360 === 0) rotations[value] = 0;
53 | document.getElementById("rot-"+value).innerText = rotations[value];
54 | }, 25);
55 | };
56 |
57 | positiveElem.onmouseup = () => clearInterval(interval);
58 | positiveElem.onmouseleave = () => clearInterval(interval);
59 |
60 | let negativeElem = document.getElementById("n-rot-"+value)
61 | negativeElem.onmousedown = () => {
62 | interval = setInterval(function() {
63 | fetch("https://"+resName+"/rotation", {
64 | method: "POST",
65 | body: JSON.stringify({
66 | axis: value,
67 | factor: -1
68 | })
69 | });
70 | rotations[value]--;
71 | if (rotations[value] % 360 === 0) rotations[value] = 0;
72 | document.getElementById("rot-"+value).innerText = rotations[value];
73 | }, 25);
74 | };
75 |
76 | negativeElem.onmouseup = () => clearInterval(interval);
77 | negativeElem.onmouseleave = () => clearInterval(interval);
78 |
79 | document.getElementById("r-rot-"+value).onclick = () => {
80 | rotations[value] = 0;
81 | document.getElementById("rot-"+value).innerText = rotations[value];
82 |
83 | fetch("https://"+resName+"/setRotation", {
84 | method: "POST",
85 | body: JSON.stringify({
86 | axis: value,
87 | value: 0
88 | })
89 | });
90 | };
91 |
92 | positiveElem = document.getElementById("p-pos-"+value)
93 | positiveElem.onmousedown = () => {
94 | interval = setInterval(function() {
95 | fetch("https://"+resName+"/position", {
96 | method: "POST",
97 | body: JSON.stringify({
98 | axis: value,
99 | factor: 1
100 | })
101 | });
102 | positions[value]++;
103 | document.getElementById("pos-"+value).innerText = positions[value];
104 | }, 25);
105 | };
106 |
107 | positiveElem.onmouseup = () => clearInterval(interval);
108 | positiveElem.onmouseleave = () => clearInterval(interval);
109 |
110 | negativeElem = document.getElementById("n-pos-"+value);
111 | negativeElem.onmousedown = () => {
112 | interval = setInterval(function() {
113 | fetch("https://"+resName+"/position", {
114 | method: "POST",
115 | body: JSON.stringify({
116 | axis: value,
117 | factor: -1
118 | })
119 | });
120 | positions[value]--;
121 | document.getElementById("pos-"+value).innerText = positions[value];
122 | }, 25);
123 | };
124 |
125 | negativeElem.onmouseup = () => clearInterval(interval);
126 | negativeElem.onmouseleave = () => clearInterval(interval);
127 |
128 | document.getElementById("r-pos-"+value).onclick = () => {
129 | positions[value] = defaultPositions[value];
130 | document.getElementById("pos-"+value).innerText = positions[value]
131 |
132 | fetch("https://"+resName+"/setPosition", {
133 | method: "POST",
134 | body: JSON.stringify({
135 | axis: value,
136 | value: positions[value]
137 | })
138 | });
139 | };
140 | }
141 |
142 | FOVPositiveElem = document.getElementById("p-fov");
143 | FOVPositiveElem.onmousedown = () => {
144 | fovInterval = setInterval(function() {
145 | fetch("https://"+resName+"/FOV", {
146 | method: "POST",
147 | body: JSON.stringify({
148 | factor: 1
149 | })
150 | });
151 | FOV++;
152 | if (FOV > 130) FOV = 130;
153 | document.getElementById("fov").innerText = FOV;
154 | }, 25);
155 | };
156 |
157 | FOVPositiveElem.onmouseup = () => clearInterval(fovInterval);
158 | FOVPositiveElem.onmouseleave = () => clearInterval(fovInterval);
159 |
160 | FOVNegativeElem = document.getElementById("n-fov");
161 | FOVNegativeElem.onmousedown = () => {
162 | fovInterval = setInterval(function() {
163 | fetch("https://"+resName+"/FOV", {
164 | method: "POST",
165 | body: JSON.stringify({
166 | factor: -1
167 | })
168 | });
169 | FOV--;
170 | if (FOV < 0) FOV = 0;
171 | document.getElementById("fov").innerText = FOV;
172 | }, 25);
173 | };
174 |
175 | FOVNegativeElem.onmouseup = () => clearInterval(fovInterval);
176 | FOVNegativeElem.onmouseleave = () => clearInterval(fovInterval);
177 |
178 | document.getElementById("r-fov").onclick = () => {
179 | document.getElementById("fov").innerText = 45;
180 | FOV = 45;
181 | fetch("https://"+resName+"/resetFOV", {
182 | method: "POST",
183 | body: null
184 | });
185 | };
186 |
187 | const copyToClipboard = (text) => {
188 | const element = document.createElement("textarea");
189 | element.value = text;
190 | document.body.appendChild(element);
191 | element.select();
192 | document.execCommand("copy");
193 | document.body.removeChild(element);
194 | };
195 |
196 | document.getElementById("copy").onclick = () => {
197 | copyToClipboard(`CreateCamWithParams("DEFAULT_SCRIPTED_CAMERA", ${positions.x}.0, ${positions.y}.0, ${positions.z}.0, ${rotations.x}.0, ${rotations.y}.0, ${rotations.z}.0, ${FOV}.0, true, 2)`);
198 | };
199 |
--------------------------------------------------------------------------------