├── fxmanifest.lua ├── README.md ├── html ├── index.html ├── script.js └── style.css └── client.lua /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version 'cerulean' 2 | -- Renzu Lock pick game 3 | -- MADE BY Renzuzu 4 | game 'gta5' 5 | 6 | lua54 'on' 7 | -- is_cfxv2 'yes' 8 | -- use_fxv2_oal 'true' 9 | 10 | ui_page { 11 | 'html/index.html', 12 | } 13 | 14 | client_scripts { 15 | "client.lua" 16 | } 17 | 18 | files { 19 | 'html/index.html', 20 | 'html/script.js', 21 | 'html/style.css', 22 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # renzu_lockgame 2 | # Simple Lockpicking Game 3 | 4 | # sample 5 | ``` 6 | local options = { 7 | dict = "veh@break_in@0h@p_m_one@", 8 | name = "low_force_entry_ds", 9 | flag = 1, 10 | } 11 | local success = exports.renzu_lockgame:CreateGame(10,options) 12 | 13 | if success then 14 | print("success") 15 | else 16 | print("try again later") 17 | end 18 | ``` 19 | -------------------------------------------------------------------------------- /html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Renzu LockGame 7 | 8 | 9 | 10 |
11 |
Level:
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /client.lua: -------------------------------------------------------------------------------- 1 | local open = false 2 | local done = false 3 | local result = nil 4 | RegisterNUICallback('close', function(data, cb) 5 | Wait(200) 6 | open = false 7 | SendNUIMessage({type = "reset", content = true}) 8 | SetNuiFocus(false,false) 9 | SetNuiFocusKeepInput(false) 10 | done = true 11 | result = data.result or false 12 | Wait(2000) 13 | done = false 14 | end) 15 | 16 | function CreateGame(level,option, hide) 17 | if option == nil then 18 | option = { 19 | dict = "veh@break_in@0h@p_m_one@", 20 | name = "low_force_entry_ds", 21 | flag = 0 22 | } 23 | end 24 | local t = { 25 | level = level, 26 | hideshackle = hide or false 27 | } 28 | SetNuiFocus(true,false) 29 | local playinganimation = false 30 | SendNUIMessage({type = "create", table = t}) 31 | CreateThread(function() 32 | FreezeEntityPosition(PlayerPedId(),true) 33 | local player = PlayerPedId() 34 | if option.scenario ~= nil then 35 | TaskStartScenarioInPlace(player, option.scenario, 0, true) 36 | playinganimation = true 37 | else 38 | if option.dict ~= nil and option.name ~= nil then 39 | 40 | if option.flag == nil then 41 | option.flag = 1 42 | end 43 | playinganimation = true 44 | RequestAnimDict( option.dict ) 45 | while not HasAnimDictLoaded(option.dict) do Citizen.Wait(0) end 46 | RequestAnimDict( option.dict ) 47 | local player = PlayerPedId() 48 | while not HasAnimDictLoaded(option.dict) do Citizen.Wait(0) end 49 | while result == nil do Wait(1000) TaskPlayAnim(player,option.dict, option.name, 2.0, 2.0, -1, option.flag, 1, false, false, false) end 50 | end 51 | end 52 | end) 53 | while result == nil do Wait(100) end 54 | Wait(1500) 55 | local player = PlayerPedId() 56 | FreezeEntityPosition(PlayerPedId(),false) 57 | local res = result 58 | result = nil 59 | done = true 60 | ClearPedTasks(player) 61 | Wait(500) 62 | done = false 63 | return res 64 | end 65 | 66 | exports('CreateGame', function(seconds,fa,o,hide) 67 | return CreateGame(seconds,fa,o,hide) 68 | end) 69 | 70 | RegisterCommand('lockgame', function(source, args, rawCommand) -- demo 71 | local o = { 72 | --scenario = 'WORLD_HUMAN_AA_COFFEE', 73 | dict = "veh@break_in@0h@p_m_one@", 74 | name = "low_force_entry_ds", 75 | flag = 1, 76 | } 77 | local prog = exports.renzu_lockgame:CreateGame(10,o) 78 | end) -------------------------------------------------------------------------------- /html/script.js: -------------------------------------------------------------------------------- 1 | // config 2 | baseSpeed = 0.01; 3 | notchWidth = 0.1; 4 | linePosition = 0 5 | notchPosition = 0 6 | clockwise = 0 7 | //end config 8 | 9 | function setColor(color) { 10 | $("#shackle, #lock").css({"border-color": 'rgb(44 52 66)'}); 11 | } 12 | function newNotch() { 13 | notchPosition = (((Math.random() * 0.75 * Math.PI) + 0.25 * Math.PI) * ((clockwise * 2) - 1)) + linePosition; 14 | $("#notch-dial").css({"-webkit-transform": "rotate(" + notchPosition + "rad)"}); 15 | $("#notch-dial").css({"-moz-transform": "rotate(" + notchPosition + "rad)"}); 16 | $("#notch-dial").css({"transform": "rotate(" + notchPosition + "rad)"}); 17 | $("#notch").show(); 18 | $("#notch").toggleClass("appear-b"); 19 | } 20 | function setStatus(newStatus) { 21 | status = newStatus; 22 | switch (newStatus) { 23 | case "start": 24 | if (level < 10) setColor("green"); 25 | else if (level < 20) setColor("violet"); 26 | else if (level < 30) setColor("orange"); 27 | else if (level < 40) setColor("blue"); 28 | else setColor("olive"); 29 | setCount(level); 30 | linePosition = 0; 31 | clockwise = true; 32 | newNotch(); 33 | $("body").removeClass("fail"); 34 | break; 35 | case "move": 36 | break; 37 | case "fail": 38 | close() 39 | document.getElementById("loadingbar").style.display = 'none'; 40 | window.location.reload(false); 41 | $("#notch").hide(); 42 | $("body").addClass("fail"); 43 | break; 44 | case "win": 45 | $("#notch").hide(); 46 | $("body").addClass("next"); 47 | $("#shackle").addClass("unlock"); 48 | close(true) 49 | setTimeout(function() { 50 | // setLevel(level + 1); 51 | // setStatus("start"); 52 | $("#shackle").removeClass("unlock"); 53 | document.getElementById("loadingbar").style.display = 'none'; 54 | window.location.reload(false); 55 | }, 1000); 56 | setTimeout(function() { 57 | $("body").removeClass("next"); 58 | }, 2000); 59 | break; 60 | } 61 | } 62 | function setCount(newCount) { 63 | count = newCount; 64 | $("#lock").text(count); 65 | } 66 | function setLevel(newLevel) { 67 | level = newLevel; 68 | $("#level").text(level); 69 | } 70 | function click() { 71 | switch (status) { 72 | case "start": 73 | setStatus("move") 74 | break; 75 | case "move": 76 | if (Math.abs(Math.atan2(Math.sin(linePosition - notchPosition), Math.cos(linePosition - notchPosition))) < notchWidth) { 77 | setCount(count - 1); 78 | if (count == 0) { 79 | setStatus("win"); 80 | } 81 | else { 82 | clockwise = !clockwise; 83 | newNotch(); 84 | } 85 | } 86 | else { 87 | setStatus("fail"); 88 | } 89 | break; 90 | case "fail": 91 | setStatus("start"); 92 | break; 93 | case "win": 94 | setStatus("start"); 95 | break; 96 | } 97 | } 98 | function step() { 99 | if (status == "move") linePosition += baseSpeed * (clockwise * 2 - 1); 100 | $("#line-dial").css({"-webkit-transform": "rotate(" + linePosition + "rad)"}); 101 | $("#line-dial").css({"-moz-transform": "rotate(" + linePosition + "rad)"}); 102 | $("#line-dial").css({"transform": "rotate(" + linePosition + "rad)"}); 103 | if ((Math.atan2(Math.sin(linePosition - notchPosition), Math.cos(linePosition - notchPosition))) * (clockwise * 2 - 1) > notchWidth) setStatus("fail"); 104 | window.requestAnimationFrame(step); 105 | } 106 | 107 | function close(result) { 108 | var xhr = new XMLHttpRequest(); 109 | xhr.open("POST", 'https://renzu_lockgame/close', true); 110 | xhr.setRequestHeader('Content-Type', 'application/json'); 111 | xhr.send(JSON.stringify({result:result})); 112 | } 113 | window.requestAnimationFrame(step); 114 | window.addEventListener("mousedown", click); 115 | window.addEventListener("touchstart", function(event) { 116 | event.preventDefault(); 117 | click(); 118 | }); 119 | window.addEventListener("keydown", click); 120 | 121 | var current = undefined 122 | window.addEventListener('message', function (table) { 123 | let event = table.data; 124 | if (event.type == 'create') { 125 | if (event.table.hideshackle) { 126 | document.getElementById("shackle").style.display = 'none'; 127 | } 128 | document.getElementById("loadingbar").style.display = 'block'; 129 | setLevel(event.table.level || 10) 130 | setStatus("start"); 131 | } 132 | if (event.type == 'reset') { 133 | document.getElementById("loadingbar").style.display = 'none'; 134 | window.location.reload(false); 135 | } 136 | }); 137 | 138 | $(document).on('keydown', function(event) { 139 | switch(event.keyCode) { 140 | case 27: // ESC 141 | setTimeout(function(){ window.location.reload(false); }, 500); 142 | close() 143 | break; 144 | case 9: // TAB 145 | break; 146 | case 17: // TAB 147 | break; 148 | } 149 | }); -------------------------------------------------------------------------------- /html/style.css: -------------------------------------------------------------------------------- 1 | $line: hsl(343, 80%, 54%); 2 | $notch: hsl(48, 83%, 59%); 3 | $fail: hsl(0, 64%, 61%); 4 | @-webkit-keyframes appear-a { 5 | from { 6 | transform: scale(0); 7 | } 8 | to { 9 | transform: scale(1); 10 | } 11 | } 12 | @keyframes appear-a { 13 | from { 14 | transform: scale(0); 15 | } 16 | to { 17 | transform: scale(1); 18 | } 19 | } 20 | @-webkit-keyframes appear-b { 21 | from { 22 | transform: scale(0); 23 | } 24 | to { 25 | transform: scale(1); 26 | } 27 | } 28 | @keyframes appear-b { 29 | from { 30 | transform: scale(0); 31 | } 32 | to { 33 | transform: scale(1); 34 | } 35 | } 36 | @-webkit-keyframes fail { 37 | 0% { 38 | transform: rotate(0); 39 | } 40 | 25% { 41 | transform: rotate(-2deg); 42 | } 43 | 50% { 44 | transform: rotate(0); 45 | } 46 | 75% { 47 | transform: rotate(2deg); 48 | } 49 | 100% { 50 | transform: rotate(0); 51 | } 52 | } 53 | @keyframes fail { 54 | 0% { 55 | transform: rotate(0); 56 | } 57 | 25% { 58 | transform: rotate(-2deg); 59 | } 60 | 50% { 61 | transform: rotate(0); 62 | } 63 | 75% { 64 | transform: rotate(2deg); 65 | } 66 | 100% { 67 | transform: rotate(0); 68 | } 69 | } 70 | @-webkit-keyframes next { 71 | 0% { 72 | transform: translateX(0); 73 | } 74 | 25% { 75 | transform: translateX(0); 76 | } 77 | 49.8% { 78 | opacity: 1; 79 | } 80 | 49.9% { 81 | opacity: 0; 82 | transform: translateX(-100%); 83 | } 84 | 50.1% { 85 | opacity: 0; 86 | transform: translateX(100%); 87 | } 88 | 50.2% { 89 | opacity: 1; 90 | } 91 | 75% { 92 | transform: translateX(0); 93 | } 94 | 100% { 95 | transform: translateX(0); 96 | } 97 | } 98 | @keyframes next { 99 | 0% { 100 | transform: translateX(0); 101 | } 102 | 25% { 103 | transform: translateX(0); 104 | } 105 | 49.8% { 106 | opacity: 1; 107 | } 108 | 49.9% { 109 | opacity: 0; 110 | transform: translateX(-100%); 111 | } 112 | 50.1% { 113 | opacity: 0; 114 | transform: translateX(100%); 115 | } 116 | 50.2% { 117 | opacity: 1; 118 | } 119 | 75% { 120 | transform: translateX(0); 121 | } 122 | 100% { 123 | transform: translateX(0); 124 | } 125 | } 126 | @-webkit-keyframes unlock { 127 | from { 128 | transform: translateY(0); 129 | } 130 | to { 131 | transform: translateY(-64px); 132 | } 133 | } 134 | @keyframes unlock { 135 | from { 136 | transform: translateY(0); 137 | } 138 | to { 139 | transform: translateY(-64px); 140 | } 141 | } 142 | html, body { 143 | height: 100%; 144 | margin: 0; 145 | } 146 | 147 | body { 148 | display: flex; 149 | flex-direction: column; 150 | align-items: center; 151 | justify-content: center; 152 | font-family: sans-serif; 153 | min-width: 352px; 154 | transition: background-color 0.5s; 155 | overflow: hidden; 156 | } 157 | 158 | * { 159 | -webkit-user-select: none; 160 | -moz-user-select: none; 161 | -ms-user-select: none; 162 | user-select: none; 163 | } 164 | 165 | .fail { 166 | -webkit-animation: fail 0.5s ease-in-out; 167 | animation: fail 0.5s ease-in-out; 168 | background: #db5c5c !important; 169 | } 170 | 171 | .next { 172 | -webkit-animation: next 2s ease-in-out; 173 | animation: next 2s ease-in-out; 174 | } 175 | 176 | .unlock { 177 | -webkit-animation: unlock 0.5s ease-in-out forwards; 178 | animation: unlock 0.5s ease-in-out forwards; 179 | } 180 | 181 | #label { 182 | text-transform: uppercase; 183 | font-size: 36px; 184 | height: 128px; 185 | color: rgba(255, 255, 255, 0.5); 186 | } 187 | 188 | #shackle { 189 | height: 128px; 190 | width: 92px; 191 | border: 48px solid; 192 | border-bottom-color: transparent !important; 193 | border-radius: 92px 92px 0 0; 194 | margin-bottom: -64px; 195 | opacity: 0.6; 196 | margin: 0 auto -64px auto; 197 | } 198 | 199 | #lock { 200 | height: 192px; 201 | width: 192px; 202 | border: 48px solid; 203 | border-radius: 50%; 204 | line-height: 192px; 205 | text-align: center; 206 | font-size: 128px; 207 | color: rgba(255, 255, 255, 0.5); 208 | } 209 | 210 | .dial { 211 | position: absolute; 212 | height: 288px; 213 | width: 288px; 214 | display: flex; 215 | flex-direction: column; 216 | align-items: center; 217 | justify-content: top; 218 | } 219 | 220 | #line { 221 | margin: 6px; 222 | height: 36px; 223 | width: 12px; 224 | border-radius: 4px; 225 | background: #d8d8d8; 226 | } 227 | 228 | #notch { 229 | margin: 10px; 230 | height: 28px; 231 | width: 28px; 232 | border-radius: 50%; 233 | background: #319ddc; 234 | } 235 | 236 | .appear-a { 237 | -webkit-animation: appear-a 0.1s; 238 | animation: appear-a 0.1s; 239 | } 240 | 241 | .appear-b { 242 | -webkit-animation: appear-b 0.1s !important; 243 | animation: appear-b 0.1s !important; 244 | } --------------------------------------------------------------------------------