How to learn Lua
I know the basics of Lua but don't know how to proceed. Any tips?
https://redd.it/1d9u0l1
@r_lua
I know the basics of Lua but don't know how to proceed. Any tips?
https://redd.it/1d9u0l1
@r_lua
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
Wanted to share my Lua game engine with VS Code breakpoint debugging (Made in C++)
The Editor
The engine could be used as learning material for the beginners on this forum. If you're doing a C++/OpenGL/Lua engine, feel free to have a look. It should be fairly straight-forward to compile and run a template project.
Feature Set, TL;DR
Editor with all kinds of tools.
Works on all desktop platforms (Win, Linux, Mac) and browsers (WebGL 2 / WebAssembly).
PBR Renderer (OpenGL ES 3.0), point lights, sun light, skybox, MSAA, material editor...
Lua Scripting for systems or components, with breakpoint debugging in VS Code.
Object/Component System (like Unity), support C++ components or Lua components.
Serialization (save/load) of all the things (scene, materials, prefabs...)
In-Game User Interface
Multi-threaded animation system, root motion, etc
Audio
Multi-threaded job system
3D physics (bullet3): rigidbodies, raycasts, etc
Networking: scene sync, events, client/server architecture, multiplayer debug tools, UDP, etc
If anyone has questions, please reach out :D
GitHub link: https://github.com/mormert/jle
YouTube demo video: https://youtu.be/2GiqLXTfKg4/
https://redd.it/1da7151
@r_lua
The Editor
The engine could be used as learning material for the beginners on this forum. If you're doing a C++/OpenGL/Lua engine, feel free to have a look. It should be fairly straight-forward to compile and run a template project.
Feature Set, TL;DR
Editor with all kinds of tools.
Works on all desktop platforms (Win, Linux, Mac) and browsers (WebGL 2 / WebAssembly).
PBR Renderer (OpenGL ES 3.0), point lights, sun light, skybox, MSAA, material editor...
Lua Scripting for systems or components, with breakpoint debugging in VS Code.
Object/Component System (like Unity), support C++ components or Lua components.
Serialization (save/load) of all the things (scene, materials, prefabs...)
In-Game User Interface
Multi-threaded animation system, root motion, etc
Audio
Multi-threaded job system
3D physics (bullet3): rigidbodies, raycasts, etc
Networking: scene sync, events, client/server architecture, multiplayer debug tools, UDP, etc
If anyone has questions, please reach out :D
GitHub link: https://github.com/mormert/jle
YouTube demo video: https://youtu.be/2GiqLXTfKg4/
https://redd.it/1da7151
@r_lua
good lua ide
I am needing a lua ide for making apps but from what I am looking for I need the noscript bar to be gone in some of my apps as I need to make a more custom one I would use C# and windows forms but its not as suggested for what I am doing if there is an ide anyone knows of please let me know
https://redd.it/1daj5vb
@r_lua
I am needing a lua ide for making apps but from what I am looking for I need the noscript bar to be gone in some of my apps as I need to make a more custom one I would use C# and windows forms but its not as suggested for what I am doing if there is an ide anyone knows of please let me know
https://redd.it/1daj5vb
@r_lua
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
How can a save the scene if ive edited it while the game was runnig?
Trying to figure out preemptible how to save a setup if i forgot i pressed run and made alot of progress
https://redd.it/1dasm3t
@r_lua
Trying to figure out preemptible how to save a setup if i forgot i pressed run and made alot of progress
https://redd.it/1dasm3t
@r_lua
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
LUA help for Mac
im on Mac and a download vs code and im trying to make a folder with the file main.lua on pages and it says its a restricted file pls help ;-;
https://redd.it/1dayt7r
@r_lua
im on Mac and a download vs code and im trying to make a folder with the file main.lua on pages and it says its a restricted file pls help ;-;
https://redd.it/1dayt7r
@r_lua
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
this gang noscript wont work
Config.Gangs = {
["ebk"\] = {
Management = vec3(459.9863, -1775.6997, 29.0699),
VehicleSpawn = vec3(482.1591, -1778.6711, 28.5374),
},
["Bloods"\] = {
Management = vec3(-266.9, -961.13, 31.22),
VehicleSpawn = vec3(326.594849, -2033.116211, 20.936781),
},
},
},
--------------------------------------------------
CreateThread(function()
for Key, Value in next, Config.Zones do
local GangTurf = lib.points.new({
coords = Value.Location,
distance = 3
})
--------------------------------------------------
CreateThread(function()
for zone, location in pairs(Config.Zones) do
local zoneCoords = location.Location
local blip = AddBlipForCoord(zoneCoords)
SetBlipSprite(blip, 310)
SetBlipDisplay(blip, 4)
SetBlipScale(blip, 0.8)
SetBlipColour(blip, 27)
SetBlipAsShortRange(blip, true)
BeginTextCommandSetBlipName("STRING")
AddTextComponentString(string.format("\~HUD_COLOUR_PURPLE\~Turf\~w\~ | %s", tostring(location.Label)))
EndTextCommandSetBlipName(blip)
local mapBlip = AddBlipForRadius(zoneCoords.x, zoneCoords.y, zoneCoords.z, location.radius)
SetBlipHighDetail(mapBlip, true)
SetBlipColour(mapBlip, 27)
SetBlipAlpha(mapBlip, 80)
SetBlipDisplay(mapBlip, 2)
end
return
end)
--------------------------------------------------
CreateThread(function()
for Key, Value in next, Config.Gangs do
local GangManagement = lib.points.new({
coords = Value.Management,
distance = 2.0
})
https://preview.redd.it/k7euluza8d5d1.png?width=1335&format=png&auto=webp&s=8982d913c40652c7349de254c5da24a77e6e0097
--------------------------------------------------
https://redd.it/1db5cv9
@r_lua
Config.Gangs = {
["ebk"\] = {
Management = vec3(459.9863, -1775.6997, 29.0699),
VehicleSpawn = vec3(482.1591, -1778.6711, 28.5374),
},
["Bloods"\] = {
Management = vec3(-266.9, -961.13, 31.22),
VehicleSpawn = vec3(326.594849, -2033.116211, 20.936781),
},
},
},
--------------------------------------------------
CreateThread(function()
for Key, Value in next, Config.Zones do
local GangTurf = lib.points.new({
coords = Value.Location,
distance = 3
})
--------------------------------------------------
CreateThread(function()
for zone, location in pairs(Config.Zones) do
local zoneCoords = location.Location
local blip = AddBlipForCoord(zoneCoords)
SetBlipSprite(blip, 310)
SetBlipDisplay(blip, 4)
SetBlipScale(blip, 0.8)
SetBlipColour(blip, 27)
SetBlipAsShortRange(blip, true)
BeginTextCommandSetBlipName("STRING")
AddTextComponentString(string.format("\~HUD_COLOUR_PURPLE\~Turf\~w\~ | %s", tostring(location.Label)))
EndTextCommandSetBlipName(blip)
local mapBlip = AddBlipForRadius(zoneCoords.x, zoneCoords.y, zoneCoords.z, location.radius)
SetBlipHighDetail(mapBlip, true)
SetBlipColour(mapBlip, 27)
SetBlipAlpha(mapBlip, 80)
SetBlipDisplay(mapBlip, 2)
end
return
end)
--------------------------------------------------
CreateThread(function()
for Key, Value in next, Config.Gangs do
local GangManagement = lib.points.new({
coords = Value.Management,
distance = 2.0
})
https://preview.redd.it/k7euluza8d5d1.png?width=1335&format=png&auto=webp&s=8982d913c40652c7349de254c5da24a77e6e0097
--------------------------------------------------
https://redd.it/1db5cv9
@r_lua
Lua 5.3.0, os.time(), and year 2038
I'm using Lua 5.3.0 for a project and someone on my team raised concerns about the year 2038 issue. If I set the date to Jan 19.2038 22:30:00, I get the following error when I run os.time() from Lua: "time result cannot be represented in this Lua instalation" (the misspelling of 'installation' was apparently fixed in 5.3.1).
os.date() seems to work correctly, though.
Does a newer version of Lua have a fix for os.time(), or do I need to rework my code to utilize os.date() instead?
https://redd.it/1dcpotn
@r_lua
I'm using Lua 5.3.0 for a project and someone on my team raised concerns about the year 2038 issue. If I set the date to Jan 19.2038 22:30:00, I get the following error when I run os.time() from Lua: "time result cannot be represented in this Lua instalation" (the misspelling of 'installation' was apparently fixed in 5.3.1).
os.date() seems to work correctly, though.
Does a newer version of Lua have a fix for os.time(), or do I need to rework my code to utilize os.date() instead?
https://redd.it/1dcpotn
@r_lua
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
What would be the most optimized way to add commas to big numbers?
I'm doing a mod for a game and for more dynamic score display I have a function that makes it quickly catch up to the real hidden score over 5 seconds and is called 20 times/s, but since the number eventually reaches up to 10mil I want to add commas for better readability. Solutions I've found on the internet run too slow and it takes 15 or more seconds to catch up instead.
https://redd.it/1dck6u9
@r_lua
I'm doing a mod for a game and for more dynamic score display I have a function that makes it quickly catch up to the real hidden score over 5 seconds and is called 20 times/s, but since the number eventually reaches up to 10mil I want to add commas for better readability. Solutions I've found on the internet run too slow and it takes 15 or more seconds to catch up instead.
https://redd.it/1dck6u9
@r_lua
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
Majordome v4 released
Majordome is an events based automation tool using timers and MQTT messages arrival. Your application itself is a galaxy of small Lua tasks (as AWS' Lambda is doing).
Technically, Majordome is a C++ massive multitasking/multithreading framework, providing all the tools needed to orchestrate those tasks in an efficient and resources conservative way. Tasks are running in stateless environments, but data can be shared among them using MQTT messaging, data collection and shared variable.
This new version provides :
- ability to extend using loadable module
- a comprehensive documentation
- migration to Séléné v7
https://redd.it/1dc29hm
@r_lua
Majordome is an events based automation tool using timers and MQTT messages arrival. Your application itself is a galaxy of small Lua tasks (as AWS' Lambda is doing).
Technically, Majordome is a C++ massive multitasking/multithreading framework, providing all the tools needed to orchestrate those tasks in an efficient and resources conservative way. Tasks are running in stateless environments, but data can be shared among them using MQTT messaging, data collection and shared variable.
This new version provides :
- ability to extend using loadable module
- a comprehensive documentation
- migration to Séléné v7
https://redd.it/1dc29hm
@r_lua
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
Using the LuaSocket library inside DeSmuME
Hi, I'm trying to use the LuaSocket library ([docs](https://lunarmodules.github.io/luasocket/), [repo](https://github.com/lunarmodules/luasocket)). Since I'm working within the DeSmuME Lua noscripting environment, I'm restricted to the following:
* Can't use LuaRocks installer
* Lua 5.1
* 32-bit libraries
I found [this discussion](https://github.com/lunarmodules/luasocket/issues/283), the guy has the same use case as me (but for 64-bit) but didn't explain exactly what he did. I'm on Windows 11. I have to use 32-bit (x86) DeSmuME as my noscript uses another library that requires it.
I found [this repo](https://luarocks.org/modules/luasocket/luasocket/2.0.2-1) containing a 32-bit LuaSocket source. The 'win32-x86' link on that page downloads a file that extracts to a collection of .lua files and some .dll files. Previously when I've needed to import a library I just 'require' something in my Lua noscript but I don't know what I should do here. Probably there are some other steps too, but I barely know anything about this so I'm a bit stuck! :(
Screenshot of contents of the above
[File directory of the win32-x86 folder in the above link](https://preview.redd.it/pzfm3ulwhl5d1.png?width=263&format=png&auto=webp&s=d5f834746f91f3dfb711b12708c10ba052ba3a95)
Would anyone be able to explain how I can install this library for my situation? Thanks for any guidance.
https://redd.it/1dc1gwm
@r_lua
Hi, I'm trying to use the LuaSocket library ([docs](https://lunarmodules.github.io/luasocket/), [repo](https://github.com/lunarmodules/luasocket)). Since I'm working within the DeSmuME Lua noscripting environment, I'm restricted to the following:
* Can't use LuaRocks installer
* Lua 5.1
* 32-bit libraries
I found [this discussion](https://github.com/lunarmodules/luasocket/issues/283), the guy has the same use case as me (but for 64-bit) but didn't explain exactly what he did. I'm on Windows 11. I have to use 32-bit (x86) DeSmuME as my noscript uses another library that requires it.
I found [this repo](https://luarocks.org/modules/luasocket/luasocket/2.0.2-1) containing a 32-bit LuaSocket source. The 'win32-x86' link on that page downloads a file that extracts to a collection of .lua files and some .dll files. Previously when I've needed to import a library I just 'require' something in my Lua noscript but I don't know what I should do here. Probably there are some other steps too, but I barely know anything about this so I'm a bit stuck! :(
Screenshot of contents of the above
[File directory of the win32-x86 folder in the above link](https://preview.redd.it/pzfm3ulwhl5d1.png?width=263&format=png&auto=webp&s=d5f834746f91f3dfb711b12708c10ba052ba3a95)
Would anyone be able to explain how I can install this library for my situation? Thanks for any guidance.
https://redd.it/1dc1gwm
@r_lua
lunarmodules.github.io
LuaSocket: Network support for the Lua language
The LuaSocket Homepage
Rotating a Drone Blade
Hi there, I'm trying to program a drone blade to rotate (around the z-axis) in Visionary Render using Lua, but I have no experience in how to code a rotating assembly. The blade is locked in all position directions and in x and y rotation directions. I need help writing a noscript to execute when a button is pressed that then starts turning the blades (doesn't have to be fast, just has to look like they're turning).
Any help would be greatly appreciated :)
(I've tried looking through the help pages on the Virtalis website to no avail)
https://redd.it/1de2uzq
@r_lua
Hi there, I'm trying to program a drone blade to rotate (around the z-axis) in Visionary Render using Lua, but I have no experience in how to code a rotating assembly. The blade is locked in all position directions and in x and y rotation directions. I need help writing a noscript to execute when a button is pressed that then starts turning the blades (doesn't have to be fast, just has to look like they're turning).
Any help would be greatly appreciated :)
(I've tried looking through the help pages on the Virtalis website to no avail)
https://redd.it/1de2uzq
@r_lua
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
Need Help with FiveM Script - AI Responds in Console but Not in UI
Hi everyone,
I'm working on a FiveM noscript that integrates an AI-powered NPC chat system using QBCore. The goal is for players to be able to approach NPCs, press "E" to interact, and have a conversation where both the player's messages and the NPC's AI-generated responses appear in a UI chat window.
The issue I'm facing is that while the AI responses are correctly generated and logged in the console, they do not appear in the UI in the game. The player's messages show up in the UI chat window, but the NPC's responses are missing.
Here's a brief overview of my setup:
1. **Server Script (**`server.lua`**):** Handles the AI API request and sends the response back to the client.
2. **Client Script (**`client.lua`**):** Sends the player's message to the server, receives the AI response, and sends it to the NUI.
3. **UI Scripts (**`index.html`**,** `style.css`**,** `noscript.js`**):** Manages the chat window and displays messages.
I tried everything but nothing worked.
If you wanna test it guys, just create an access token on huggingface and add it to the config.lua.
here is my Code:
--fxmanifest.lua
fx_version 'cerulean'
game 'gta5'
author 'Revo'
denoscription 'Interactive NPCs with LLM'
version '1.0.0'
shared_noscript 'config.lua'
client_noscripts {
'client/client.lua'
}
server_noscripts {
'server/server.lua'
}
files {
'ui/index.html',
'ui/style.css',
'ui/noscript.js'
}
ui_page 'ui/index.html'
--fxmanifest.lua
fx_version 'cerulean'
game 'gta5'
author 'Revo'
denoscription 'Interactive NPCs with LLM'
version '1.0.0'
shared_noscript 'config.lua'
client_noscripts {
'client/client.lua'
}
server_noscripts {
'server/server.lua'
}
files {
'ui/index.html',
'ui/style.css',
'ui/noscript.js'
}
ui_page 'ui/index.html'
--config.lua
Config = {}
Config.HuggingFaceAPIKey = "API-Key"
Config.ModelEndpoint = "https://api-inference.huggingface.co/models/facebook/blenderbot-400M-distill"
--server.lua
local json = require('json')
RegisterNetEvent('InteractiveNPCS:RequestLLMResponse')
AddEventHandler('InteractiveNPCS:RequestLLMResponse', function(text)
print("Received text from client: " .. text)
local apiKey = Config.HuggingFaceAPIKey
local endpoint = Config.ModelEndpoint
PerformHttpRequest(endpoint, function(err, responseText, headers)
if err == 200 then
print("Received response from LLM API: " .. tostring(responseText))
local response = json.decode(responseText)
local reply = response and response[1] and response[1].generated_text or "Sorry, I don't understand."
print("Sending response to client: " .. reply)
TriggerClientEvent('InteractiveNPCS:ReceiveLLMResponse', source, reply)
else
print("Error from LLM API: " .. tostring(err))
print("Response text: " .. tostring(responseText))
TriggerClientEvent('InteractiveNPCS:ReceiveLLMResponse', source, "Sorry, something went wrong.")
end
end, 'POST', json.encode({
inputs = text
}), {
["Authorization"] = "Bearer " .. apiKey,
["Content-Type"] = "application/json"
})
end)
--client.lua
local function showChat()
SetNuiFocus(true, true)
SendNUIMessage({
type = "show"
})
end
local function closeChat()
SetNuiFocus(false, false)
SendNUIMessage({
type = "close"
})
end
local function getClosestPed(coords)
local handle, ped = FindFirstPed()
local success
local closestPed = nil
local closestDistance = -1
Hi everyone,
I'm working on a FiveM noscript that integrates an AI-powered NPC chat system using QBCore. The goal is for players to be able to approach NPCs, press "E" to interact, and have a conversation where both the player's messages and the NPC's AI-generated responses appear in a UI chat window.
The issue I'm facing is that while the AI responses are correctly generated and logged in the console, they do not appear in the UI in the game. The player's messages show up in the UI chat window, but the NPC's responses are missing.
Here's a brief overview of my setup:
1. **Server Script (**`server.lua`**):** Handles the AI API request and sends the response back to the client.
2. **Client Script (**`client.lua`**):** Sends the player's message to the server, receives the AI response, and sends it to the NUI.
3. **UI Scripts (**`index.html`**,** `style.css`**,** `noscript.js`**):** Manages the chat window and displays messages.
I tried everything but nothing worked.
If you wanna test it guys, just create an access token on huggingface and add it to the config.lua.
here is my Code:
--fxmanifest.lua
fx_version 'cerulean'
game 'gta5'
author 'Revo'
denoscription 'Interactive NPCs with LLM'
version '1.0.0'
shared_noscript 'config.lua'
client_noscripts {
'client/client.lua'
}
server_noscripts {
'server/server.lua'
}
files {
'ui/index.html',
'ui/style.css',
'ui/noscript.js'
}
ui_page 'ui/index.html'
--fxmanifest.lua
fx_version 'cerulean'
game 'gta5'
author 'Revo'
denoscription 'Interactive NPCs with LLM'
version '1.0.0'
shared_noscript 'config.lua'
client_noscripts {
'client/client.lua'
}
server_noscripts {
'server/server.lua'
}
files {
'ui/index.html',
'ui/style.css',
'ui/noscript.js'
}
ui_page 'ui/index.html'
--config.lua
Config = {}
Config.HuggingFaceAPIKey = "API-Key"
Config.ModelEndpoint = "https://api-inference.huggingface.co/models/facebook/blenderbot-400M-distill"
--server.lua
local json = require('json')
RegisterNetEvent('InteractiveNPCS:RequestLLMResponse')
AddEventHandler('InteractiveNPCS:RequestLLMResponse', function(text)
print("Received text from client: " .. text)
local apiKey = Config.HuggingFaceAPIKey
local endpoint = Config.ModelEndpoint
PerformHttpRequest(endpoint, function(err, responseText, headers)
if err == 200 then
print("Received response from LLM API: " .. tostring(responseText))
local response = json.decode(responseText)
local reply = response and response[1] and response[1].generated_text or "Sorry, I don't understand."
print("Sending response to client: " .. reply)
TriggerClientEvent('InteractiveNPCS:ReceiveLLMResponse', source, reply)
else
print("Error from LLM API: " .. tostring(err))
print("Response text: " .. tostring(responseText))
TriggerClientEvent('InteractiveNPCS:ReceiveLLMResponse', source, "Sorry, something went wrong.")
end
end, 'POST', json.encode({
inputs = text
}), {
["Authorization"] = "Bearer " .. apiKey,
["Content-Type"] = "application/json"
})
end)
--client.lua
local function showChat()
SetNuiFocus(true, true)
SendNUIMessage({
type = "show"
})
end
local function closeChat()
SetNuiFocus(false, false)
SendNUIMessage({
type = "close"
})
end
local function getClosestPed(coords)
local handle, ped = FindFirstPed()
local success
local closestPed = nil
local closestDistance = -1
repeat
local pedCoords = GetEntityCoords(ped)
local distance = #(coords - pedCoords)
if closestDistance == -1 or distance < closestDistance then
closestPed = ped
closestDistance = distance
end
success, ped = FindNextPed(handle)
until not success
EndFindPed(handle)
return closestPed
end
RegisterNUICallback('closeChat', function(data, cb)
closeChat()
cb('ok')
end)
RegisterNUICallback('sendMessage', function(data, cb)
local text = data.text
print("Client: Sending message - " .. text)
local playerPed = PlayerPedId()
local coords = GetEntityCoords(playerPed)
local closestPed = getClosestPed(coords)
if closestPed then
TriggerServerEvent('InteractiveNPCS:RequestLLMResponse', text)
else
SendNUIMessage({
type = "npcReply",
text = "No one is around to talk to."
})
end
cb('ok')
end)
Citizen.CreateThread(function()
while true do
Citizen.Wait(0)
if IsControlJustReleased(0, 38) then -- E key
local playerPed = PlayerPedId()
local coords = GetEntityCoords(playerPed)
local closestPed = getClosestPed(coords)
if closestPed then
showChat()
end
end
end
end)
RegisterNetEvent('InteractiveNPCS:ReceiveLLMResponse')
AddEventHandler('InteractiveNPCS:ReceiveLLMResponse', function(response)
print("Client: Received response - " .. response)
SendNUIMessage({
type = "npcReply",
text = response
})
end)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<noscript>Interactive NPC Chat</noscript>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="chatContainer">
<div id="messagesContainer"></div>
<div id="inputContainer">
<input type="text" id="inputMessage" placeholder="Type a message..." />
<button id="sendButton">Send</button>
<button id="closeButton">X</button>
</div>
</div>
<noscript src="noscript.js"></noscript>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<noscript>Interactive NPC Chat</noscript>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="chatContainer">
<div id="messagesContainer"></div>
<div id="inputContainer">
<input type="text" id="inputMessage" placeholder="Type a message..." />
<button id="sendButton">Send</button>
<button id="closeButton">X</button>
</div>
</div>
<noscript src="noscript.js"></noscript>
</body>
</html>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: flex-end;
height: 100vh;
background: transparent;
}
#chatContainer {
position: fixed;
bottom: 10px;
width: 90%;
max-width: 600px;
background: rgba(0, 0, 0, 0.8);
padding: 10px;
border-radius: 10px;
color: white;
display: none;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}
#messagesContainer {
max-height: 200px;
overflow-y: auto;
margin-bottom: 10px;
padding: 5px;
border: 1px solid #444;
border-radius: 5px;
background: rgba(0, 0, 0, 0.6);
}
#inputContainer {
local pedCoords = GetEntityCoords(ped)
local distance = #(coords - pedCoords)
if closestDistance == -1 or distance < closestDistance then
closestPed = ped
closestDistance = distance
end
success, ped = FindNextPed(handle)
until not success
EndFindPed(handle)
return closestPed
end
RegisterNUICallback('closeChat', function(data, cb)
closeChat()
cb('ok')
end)
RegisterNUICallback('sendMessage', function(data, cb)
local text = data.text
print("Client: Sending message - " .. text)
local playerPed = PlayerPedId()
local coords = GetEntityCoords(playerPed)
local closestPed = getClosestPed(coords)
if closestPed then
TriggerServerEvent('InteractiveNPCS:RequestLLMResponse', text)
else
SendNUIMessage({
type = "npcReply",
text = "No one is around to talk to."
})
end
cb('ok')
end)
Citizen.CreateThread(function()
while true do
Citizen.Wait(0)
if IsControlJustReleased(0, 38) then -- E key
local playerPed = PlayerPedId()
local coords = GetEntityCoords(playerPed)
local closestPed = getClosestPed(coords)
if closestPed then
showChat()
end
end
end
end)
RegisterNetEvent('InteractiveNPCS:ReceiveLLMResponse')
AddEventHandler('InteractiveNPCS:ReceiveLLMResponse', function(response)
print("Client: Received response - " .. response)
SendNUIMessage({
type = "npcReply",
text = response
})
end)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<noscript>Interactive NPC Chat</noscript>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="chatContainer">
<div id="messagesContainer"></div>
<div id="inputContainer">
<input type="text" id="inputMessage" placeholder="Type a message..." />
<button id="sendButton">Send</button>
<button id="closeButton">X</button>
</div>
</div>
<noscript src="noscript.js"></noscript>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<noscript>Interactive NPC Chat</noscript>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="chatContainer">
<div id="messagesContainer"></div>
<div id="inputContainer">
<input type="text" id="inputMessage" placeholder="Type a message..." />
<button id="sendButton">Send</button>
<button id="closeButton">X</button>
</div>
</div>
<noscript src="noscript.js"></noscript>
</body>
</html>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: flex-end;
height: 100vh;
background: transparent;
}
#chatContainer {
position: fixed;
bottom: 10px;
width: 90%;
max-width: 600px;
background: rgba(0, 0, 0, 0.8);
padding: 10px;
border-radius: 10px;
color: white;
display: none;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}
#messagesContainer {
max-height: 200px;
overflow-y: auto;
margin-bottom: 10px;
padding: 5px;
border: 1px solid #444;
border-radius: 5px;
background: rgba(0, 0, 0, 0.6);
}
#inputContainer {
display: flex;
align-items: center;
}
#inputMessage {
flex: 1;
padding: 10px;
margin-right: 10px;
border-radius: 5px;
border: none;
}
button {
padding: 10px 15px;
background: #3498db;
border: none;
border-radius: 5px;
color: white;
cursor: pointer;
margin-left: 5px;
}
button:hover {
background: #2980b9;
}
.chat-message.npc {
color: #ffcc00;
}
.chat-message.user {
color: #ffffff;
}
console.log("UI: Script loaded");
document.getElementById("sendButton").addEventListener("click", sendMessage);
document.getElementById("closeButton").addEventListener("click", closeChat);
function closeChat() {
console.log("UI: Closing chat");
fetch(`https://${GetParentResourceName()}/closeChat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
}).then(resp => resp.json()).then(resp => {
if (resp === 'ok') {
document.getElementById('chatContainer').style.display = 'none';
}
});
}
function sendMessage() {
const text = document.getElementById('inputMessage').value;
console.log("UI: Sending message - " + text);
// Add user's message to the chat
addMessageToChat("User", text);
fetch(`https://${GetParentResourceName()}/sendMessage`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ text })
}).then(resp => resp.json()).then(resp => {
if (resp === 'ok') {
document.getElementById('inputMessage').value = '';
}
});
}
function addMessageToChat(sender, message) {
const messagesContainer = document.getElementById('messagesContainer');
const messageElement = document.createElement('div');
messageElement.className = `chat-message ${sender.toLowerCase()}`;
messageElement.innerText = `${sender}: ${message}`;
messagesContainer.appendChild(messageElement);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
window.addEventListener('message', (event) => {
console.log("UI: Received message - " + JSON.stringify(event.data));
if (event.data.type === 'show') {
console.log("UI: Showing chat");
document.getElementById('inputMessage').value = '';
document.getElementById('messagesContainer').innerHTML = ''; // Clear previous messages
document.getElementById('chatContainer').style.display = 'block';
} else if (event.data.type === 'close') {
console.log("UI: Closing chat");
document.getElementById('chatContainer').style.display = 'none';
} else if (event.data.type === 'npcReply') {
console.log("UI: NPC replied with text - " + event.data.text);
// Add NPC's message to the chat
addMessageToChat("Random NPC", event.data.text);
}
});
https://redd.it/1deawfo
@r_lua
align-items: center;
}
#inputMessage {
flex: 1;
padding: 10px;
margin-right: 10px;
border-radius: 5px;
border: none;
}
button {
padding: 10px 15px;
background: #3498db;
border: none;
border-radius: 5px;
color: white;
cursor: pointer;
margin-left: 5px;
}
button:hover {
background: #2980b9;
}
.chat-message.npc {
color: #ffcc00;
}
.chat-message.user {
color: #ffffff;
}
console.log("UI: Script loaded");
document.getElementById("sendButton").addEventListener("click", sendMessage);
document.getElementById("closeButton").addEventListener("click", closeChat);
function closeChat() {
console.log("UI: Closing chat");
fetch(`https://${GetParentResourceName()}/closeChat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
}).then(resp => resp.json()).then(resp => {
if (resp === 'ok') {
document.getElementById('chatContainer').style.display = 'none';
}
});
}
function sendMessage() {
const text = document.getElementById('inputMessage').value;
console.log("UI: Sending message - " + text);
// Add user's message to the chat
addMessageToChat("User", text);
fetch(`https://${GetParentResourceName()}/sendMessage`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ text })
}).then(resp => resp.json()).then(resp => {
if (resp === 'ok') {
document.getElementById('inputMessage').value = '';
}
});
}
function addMessageToChat(sender, message) {
const messagesContainer = document.getElementById('messagesContainer');
const messageElement = document.createElement('div');
messageElement.className = `chat-message ${sender.toLowerCase()}`;
messageElement.innerText = `${sender}: ${message}`;
messagesContainer.appendChild(messageElement);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
window.addEventListener('message', (event) => {
console.log("UI: Received message - " + JSON.stringify(event.data));
if (event.data.type === 'show') {
console.log("UI: Showing chat");
document.getElementById('inputMessage').value = '';
document.getElementById('messagesContainer').innerHTML = ''; // Clear previous messages
document.getElementById('chatContainer').style.display = 'block';
} else if (event.data.type === 'close') {
console.log("UI: Closing chat");
document.getElementById('chatContainer').style.display = 'none';
} else if (event.data.type === 'npcReply') {
console.log("UI: NPC replied with text - " + event.data.text);
// Add NPC's message to the chat
addMessageToChat("Random NPC", event.data.text);
}
});
https://redd.it/1deawfo
@r_lua
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
Could someone help me out with patching this mod?
I am new to lua coding, and I'm stuck on patching this mod. I'd like to patch "targetLevel = 0.0 + targetLevel - ((UKO.randomDecimal(maxToLose / 10)) / 10 \* targetLevel)" to make it remove a consistent percentage value. Instead of removing a random value like it does currently!
if hasConfig then
local oldTargetLevel = 0.0 + targetLevel
if chanceToLose ~= 0 and UKO.random(chanceToLose) then
targetLevel = 0.0 + targetLevel - ((UKO.randomDecimal(maxToLose / 10)) / 10 * targetLevel)
if targetLevel == oldTargetLevel and oldTargetLevel ~= 0 then --if we somehow rolled no change, not even a fractional loss, and we're not at zero..
targetLevel = targetLevel - UKO.randomDecimal(1) --you're not getting away that easily..
end
print("UKO: Randomized loss has left the new target at "..targetLevel)
if (oldTargetLevel - targetLevel > 0) then
if affectedPerks ~= "" then
affectedPerks = affectedPerks..", "
end
affectedPerks = affectedPerks..perkName
end
end
else
print("UKO: Skipped unconfigured skill \""..perkName.."\".")
end
https://redd.it/1dei5hy
@r_lua
I am new to lua coding, and I'm stuck on patching this mod. I'd like to patch "targetLevel = 0.0 + targetLevel - ((UKO.randomDecimal(maxToLose / 10)) / 10 \* targetLevel)" to make it remove a consistent percentage value. Instead of removing a random value like it does currently!
if hasConfig then
local oldTargetLevel = 0.0 + targetLevel
if chanceToLose ~= 0 and UKO.random(chanceToLose) then
targetLevel = 0.0 + targetLevel - ((UKO.randomDecimal(maxToLose / 10)) / 10 * targetLevel)
if targetLevel == oldTargetLevel and oldTargetLevel ~= 0 then --if we somehow rolled no change, not even a fractional loss, and we're not at zero..
targetLevel = targetLevel - UKO.randomDecimal(1) --you're not getting away that easily..
end
print("UKO: Randomized loss has left the new target at "..targetLevel)
if (oldTargetLevel - targetLevel > 0) then
if affectedPerks ~= "" then
affectedPerks = affectedPerks..", "
end
affectedPerks = affectedPerks..perkName
end
end
else
print("UKO: Skipped unconfigured skill \""..perkName.."\".")
end
https://redd.it/1dei5hy
@r_lua
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
Please help
Alright so, here's the code:
-- Patrol Points
local patrolPoints = {
{ x = 600, y = 400 }, -- Starting point or first patrol point
{ x = 700, y = 400 }, -- Second patrol point
{ x = 600, y = 500 }, -- Third patrol point, adjust as needed
}
local currentPatrolIndex = 1 -- Index to track current patrol point
-- Sight Range parameters
local sightDistance = 250 -- Distance in pixels
local sightAngle = math.rad(60) -- Sight angle in radians (60 degrees)
-- Player properties
player = {
x = 25,
y = 25,
speed = 125,
width = 25,
height = 25,
hasKey = false,
targetX = 50,
targetY = 50
}
-- Enemy properties
enemy = {
x = 600,
y = 400,
width = 25,
height = 25,
speed = 75,
shootCooldown = 1,
shootTimer = 1,
sightDistance = 250,
sightAngle = math.rad(60)
}
-- Key properties
key = {
x = 300,
y = 200,
width = 15,
height = 15,
collected = false
}
-- Door properties
door = {
x = 900,
y = 600,
width = 50,
height = 80
}
-- Game state
gameState = "playing"
flashTimer = 0
CongratsSound = love.audio.newSource("CONGRATS.mp3", "static")
KeyCollected = love.audio.newSource("KeyCollected.mp3", "static")
GameOver = love.audio.newSource("GameOver.mp3", "static")
soundPlayed = false -- Ensure sound plays once
-- Table to store click effects
clickEffects = {}
-- Key collected message properties
keyCollectedMessage = {
text = "Key collected!",
alpha = 0, -- Initial alpha value set to 0
fadeSpeed = 0.5 -- Speed of fading
}
-- Button properties
buttonWidth = 200
buttonHeight = 50
-- Position of buttons
exitButtonX = (love.graphics.getWidth() - buttonWidth) / 2
exitButtonY = love.graphics.getHeight() / 2 - 50
tryAgainButtonX = (love.graphics.getWidth() - buttonWidth) / 2
tryAgainButtonY = love.graphics.getHeight() / 2 + 50
-- Projectile definition
projectiles = {}
local function isPlayerInSightRange()
local dx = player.x - enemy.x
local dy = player.y - enemy.y
local distance = math.sqrt(dx dx + dy dy)
return distance <= enemy.sightDistance
end
-- Function to draw the sight range cone
local function drawSightRange()
if isPlayerInSightRange() then
-- Calculate the center of the cone around the enemy
local centerX = enemy.x + enemy.width / 2
local centerY = enemy.y + enemy.height / 2
-- Calculate the direction vector from enemy to player
local dx = player.x - enemy.x
local dy = player.y - enemy.y
local angleToPlayer = math.atan2(dy, dx)
-- Calculate the angles defining the cone
local startAngle = angleToPlayer - enemy.sightAngle / 2
local endAngle = angleToPlayer + enemy.sightAngle / 2
-- Calculate the forward vector based on enemy's direction
local forwardVectorX = math.cos(angleToPlayer)
local forwardVectorY = math.sin(angleToPlayer)
-- Calculate the position of the cone's apex (focus in front of the enemy)
local apexX = enemy.x + enemy.width / 2 + forwardVectorX enemy.sightDistance
local apexY = enemy.y + enemy.height / 2 + forwardVectorY enemy.sightDistance
-- Set the color and opacity of the sight range cone
love.graphics.setColor(1, 0, 0, 0.3) -- Red color with 30% opacity
-- Draw the sight range cone centered around the enemy's apex
love.graphics.arc("fill", centerX, centerY, enemy.sightDistance, startAngle, endAngle)
end
end
function love.load()
love.window.setTitle("Puzzle Game")
love.graphics.setBackgroundColor(0.1, 0.1, 0.1)
love.window.setMode(1024, 768)
gameState = 'playing'
end
function love.update(dt)
if gameState == "playing" then
updatePlayerPosition(dt)
updateEnemyPosition(dt)
checkKeyCollision()
checkDoorCollision()
checkEnemyCollision()
updateClickEffects(dt)
updateKeyCollectedMessage(dt)
updateProjectiles(dt)
elseif gameState == "won" then
Alright so, here's the code:
-- Patrol Points
local patrolPoints = {
{ x = 600, y = 400 }, -- Starting point or first patrol point
{ x = 700, y = 400 }, -- Second patrol point
{ x = 600, y = 500 }, -- Third patrol point, adjust as needed
}
local currentPatrolIndex = 1 -- Index to track current patrol point
-- Sight Range parameters
local sightDistance = 250 -- Distance in pixels
local sightAngle = math.rad(60) -- Sight angle in radians (60 degrees)
-- Player properties
player = {
x = 25,
y = 25,
speed = 125,
width = 25,
height = 25,
hasKey = false,
targetX = 50,
targetY = 50
}
-- Enemy properties
enemy = {
x = 600,
y = 400,
width = 25,
height = 25,
speed = 75,
shootCooldown = 1,
shootTimer = 1,
sightDistance = 250,
sightAngle = math.rad(60)
}
-- Key properties
key = {
x = 300,
y = 200,
width = 15,
height = 15,
collected = false
}
-- Door properties
door = {
x = 900,
y = 600,
width = 50,
height = 80
}
-- Game state
gameState = "playing"
flashTimer = 0
CongratsSound = love.audio.newSource("CONGRATS.mp3", "static")
KeyCollected = love.audio.newSource("KeyCollected.mp3", "static")
GameOver = love.audio.newSource("GameOver.mp3", "static")
soundPlayed = false -- Ensure sound plays once
-- Table to store click effects
clickEffects = {}
-- Key collected message properties
keyCollectedMessage = {
text = "Key collected!",
alpha = 0, -- Initial alpha value set to 0
fadeSpeed = 0.5 -- Speed of fading
}
-- Button properties
buttonWidth = 200
buttonHeight = 50
-- Position of buttons
exitButtonX = (love.graphics.getWidth() - buttonWidth) / 2
exitButtonY = love.graphics.getHeight() / 2 - 50
tryAgainButtonX = (love.graphics.getWidth() - buttonWidth) / 2
tryAgainButtonY = love.graphics.getHeight() / 2 + 50
-- Projectile definition
projectiles = {}
local function isPlayerInSightRange()
local dx = player.x - enemy.x
local dy = player.y - enemy.y
local distance = math.sqrt(dx dx + dy dy)
return distance <= enemy.sightDistance
end
-- Function to draw the sight range cone
local function drawSightRange()
if isPlayerInSightRange() then
-- Calculate the center of the cone around the enemy
local centerX = enemy.x + enemy.width / 2
local centerY = enemy.y + enemy.height / 2
-- Calculate the direction vector from enemy to player
local dx = player.x - enemy.x
local dy = player.y - enemy.y
local angleToPlayer = math.atan2(dy, dx)
-- Calculate the angles defining the cone
local startAngle = angleToPlayer - enemy.sightAngle / 2
local endAngle = angleToPlayer + enemy.sightAngle / 2
-- Calculate the forward vector based on enemy's direction
local forwardVectorX = math.cos(angleToPlayer)
local forwardVectorY = math.sin(angleToPlayer)
-- Calculate the position of the cone's apex (focus in front of the enemy)
local apexX = enemy.x + enemy.width / 2 + forwardVectorX enemy.sightDistance
local apexY = enemy.y + enemy.height / 2 + forwardVectorY enemy.sightDistance
-- Set the color and opacity of the sight range cone
love.graphics.setColor(1, 0, 0, 0.3) -- Red color with 30% opacity
-- Draw the sight range cone centered around the enemy's apex
love.graphics.arc("fill", centerX, centerY, enemy.sightDistance, startAngle, endAngle)
end
end
function love.load()
love.window.setTitle("Puzzle Game")
love.graphics.setBackgroundColor(0.1, 0.1, 0.1)
love.window.setMode(1024, 768)
gameState = 'playing'
end
function love.update(dt)
if gameState == "playing" then
updatePlayerPosition(dt)
updateEnemyPosition(dt)
checkKeyCollision()
checkDoorCollision()
checkEnemyCollision()
updateClickEffects(dt)
updateKeyCollectedMessage(dt)
updateProjectiles(dt)
elseif gameState == "won" then
Please help
Alright so, here's the code:
-- Patrol Points
local patrolPoints = {
{ x = 600, y = 400 }, -- Starting point or first patrol point
{ x = 700, y = 400 }, -- Second patrol point
{ x = 600, y = 500 }, -- Third patrol point, adjust as needed
}
local currentPatrolIndex = 1 -- Index to track current patrol point
-- Sight Range parameters
local sightDistance = 250 -- Distance in pixels
local sightAngle = math.rad(60) -- Sight angle in radians (60 degrees)
-- Player properties
player = {
x = 25,
y = 25,
speed = 125,
width = 25,
height = 25,
hasKey = false,
targetX = 50,
targetY = 50
}
-- Enemy properties
enemy = {
x = 600,
y = 400,
width = 25,
height = 25,
speed = 75,
shootCooldown = 1,
shootTimer = 1,
sightDistance = 250,
sightAngle = math.rad(60)
}
-- Key properties
key = {
x = 300,
y = 200,
width = 15,
height = 15,
collected = false
}
-- Door properties
door = {
x = 900,
y = 600,
width = 50,
height = 80
}
-- Game state
gameState = "playing"
flashTimer = 0
CongratsSound = love.audio.newSource("CONGRATS.mp3", "static")
KeyCollected = love.audio.newSource("KeyCollected.mp3", "static")
GameOver = love.audio.newSource("GameOver.mp3", "static")
soundPlayed = false -- Ensure sound plays once
-- Table to store click effects
clickEffects = {}
-- Key collected message properties
keyCollectedMessage = {
text = "Key collected!",
alpha = 0, -- Initial alpha value set to 0
fadeSpeed = 0.5 -- Speed of fading
}
-- Button properties
buttonWidth = 200
buttonHeight = 50
-- Position of buttons
exitButtonX = (love.graphics.getWidth() - buttonWidth) / 2
exitButtonY = love.graphics.getHeight() / 2 - 50
tryAgainButtonX = (love.graphics.getWidth() - buttonWidth) / 2
tryAgainButtonY = love.graphics.getHeight() / 2 + 50
-- Projectile definition
projectiles = {}
local function isPlayerInSightRange()
local dx = player.x - enemy.x
local dy = player.y - enemy.y
local distance = math.sqrt(dx * dx + dy * dy)
return distance <= enemy.sightDistance
end
-- Function to draw the sight range cone
local function drawSightRange()
if isPlayerInSightRange() then
-- Calculate the center of the cone around the enemy
local centerX = enemy.x + enemy.width / 2
local centerY = enemy.y + enemy.height / 2
-- Calculate the direction vector from enemy to player
local dx = player.x - enemy.x
local dy = player.y - enemy.y
local angleToPlayer = math.atan2(dy, dx)
-- Calculate the angles defining the cone
local startAngle = angleToPlayer - enemy.sightAngle / 2
local endAngle = angleToPlayer + enemy.sightAngle / 2
-- Calculate the forward vector based on enemy's direction
local forwardVectorX = math.cos(angleToPlayer)
local forwardVectorY = math.sin(angleToPlayer)
-- Calculate the position of the cone's apex (focus in front of the enemy)
local apexX = enemy.x + enemy.width / 2 + forwardVectorX * enemy.sightDistance
local apexY = enemy.y + enemy.height / 2 + forwardVectorY * enemy.sightDistance
-- Set the color and opacity of the sight range cone
love.graphics.setColor(1, 0, 0, 0.3) -- Red color with 30% opacity
-- Draw the sight range cone centered around the enemy's apex
love.graphics.arc("fill", centerX, centerY, enemy.sightDistance, startAngle, endAngle)
end
end
function love.load()
love.window.setTitle("Puzzle Game")
love.graphics.setBackgroundColor(0.1, 0.1, 0.1)
love.window.setMode(1024, 768)
gameState = 'playing'
end
function love.update(dt)
if gameState == "playing" then
updatePlayerPosition(dt)
updateEnemyPosition(dt)
checkKeyCollision()
checkDoorCollision()
checkEnemyCollision()
updateClickEffects(dt)
updateKeyCollectedMessage(dt)
updateProjectiles(dt)
elseif gameState == "won" then
Alright so, here's the code:
-- Patrol Points
local patrolPoints = {
{ x = 600, y = 400 }, -- Starting point or first patrol point
{ x = 700, y = 400 }, -- Second patrol point
{ x = 600, y = 500 }, -- Third patrol point, adjust as needed
}
local currentPatrolIndex = 1 -- Index to track current patrol point
-- Sight Range parameters
local sightDistance = 250 -- Distance in pixels
local sightAngle = math.rad(60) -- Sight angle in radians (60 degrees)
-- Player properties
player = {
x = 25,
y = 25,
speed = 125,
width = 25,
height = 25,
hasKey = false,
targetX = 50,
targetY = 50
}
-- Enemy properties
enemy = {
x = 600,
y = 400,
width = 25,
height = 25,
speed = 75,
shootCooldown = 1,
shootTimer = 1,
sightDistance = 250,
sightAngle = math.rad(60)
}
-- Key properties
key = {
x = 300,
y = 200,
width = 15,
height = 15,
collected = false
}
-- Door properties
door = {
x = 900,
y = 600,
width = 50,
height = 80
}
-- Game state
gameState = "playing"
flashTimer = 0
CongratsSound = love.audio.newSource("CONGRATS.mp3", "static")
KeyCollected = love.audio.newSource("KeyCollected.mp3", "static")
GameOver = love.audio.newSource("GameOver.mp3", "static")
soundPlayed = false -- Ensure sound plays once
-- Table to store click effects
clickEffects = {}
-- Key collected message properties
keyCollectedMessage = {
text = "Key collected!",
alpha = 0, -- Initial alpha value set to 0
fadeSpeed = 0.5 -- Speed of fading
}
-- Button properties
buttonWidth = 200
buttonHeight = 50
-- Position of buttons
exitButtonX = (love.graphics.getWidth() - buttonWidth) / 2
exitButtonY = love.graphics.getHeight() / 2 - 50
tryAgainButtonX = (love.graphics.getWidth() - buttonWidth) / 2
tryAgainButtonY = love.graphics.getHeight() / 2 + 50
-- Projectile definition
projectiles = {}
local function isPlayerInSightRange()
local dx = player.x - enemy.x
local dy = player.y - enemy.y
local distance = math.sqrt(dx * dx + dy * dy)
return distance <= enemy.sightDistance
end
-- Function to draw the sight range cone
local function drawSightRange()
if isPlayerInSightRange() then
-- Calculate the center of the cone around the enemy
local centerX = enemy.x + enemy.width / 2
local centerY = enemy.y + enemy.height / 2
-- Calculate the direction vector from enemy to player
local dx = player.x - enemy.x
local dy = player.y - enemy.y
local angleToPlayer = math.atan2(dy, dx)
-- Calculate the angles defining the cone
local startAngle = angleToPlayer - enemy.sightAngle / 2
local endAngle = angleToPlayer + enemy.sightAngle / 2
-- Calculate the forward vector based on enemy's direction
local forwardVectorX = math.cos(angleToPlayer)
local forwardVectorY = math.sin(angleToPlayer)
-- Calculate the position of the cone's apex (focus in front of the enemy)
local apexX = enemy.x + enemy.width / 2 + forwardVectorX * enemy.sightDistance
local apexY = enemy.y + enemy.height / 2 + forwardVectorY * enemy.sightDistance
-- Set the color and opacity of the sight range cone
love.graphics.setColor(1, 0, 0, 0.3) -- Red color with 30% opacity
-- Draw the sight range cone centered around the enemy's apex
love.graphics.arc("fill", centerX, centerY, enemy.sightDistance, startAngle, endAngle)
end
end
function love.load()
love.window.setTitle("Puzzle Game")
love.graphics.setBackgroundColor(0.1, 0.1, 0.1)
love.window.setMode(1024, 768)
gameState = 'playing'
end
function love.update(dt)
if gameState == "playing" then
updatePlayerPosition(dt)
updateEnemyPosition(dt)
checkKeyCollision()
checkDoorCollision()
checkEnemyCollision()
updateClickEffects(dt)
updateKeyCollectedMessage(dt)
updateProjectiles(dt)
elseif gameState == "won" then
flashTimer = flashTimer + dt
-- Clear clickEffects when game is won
clickEffects = {}
elseif gameState == "lost" then
-- Handle game over state
end
end
function love.draw()
if gameState == "playing" then
drawGame()
drawSightRange() -- Draw the sight range cone
-- Debug information
love.graphics.setColor(1, 1, 1) -- Set color to white
love.graphics.setFont(love.graphics.newFont(18)) -- Set font size to 18
love.graphics.print("Your position: (" .. player.x .. ", " .. player.y .. ")", 10, 30)
elseif gameState == "won" then
drawWinningScreen()
if not soundPlayed then
love.audio.play(CongratsSound)
soundPlayed = true
end
elseif gameState == "lost" then
drawGameOverScreen()
if not soundPlayed then
love.audio.play(GameOver)
soundPlayed = true
end
end
-- Draw click effects list
love.graphics.setColor(1, 1, 1) -- Set color to white
love.graphics.setFont(love.graphics.newFont(18)) -- Set font size to 18
local xOffset = love.graphics.getWidth() - 400 -- Offset from the right side of the window
local yOffset = 50 -- Initial offset for y-coordinate (lowered slightly)
local lineHeight = 20 -- Height of each line in the list
for i, effect in ipairs(clickEffects) do
-- Calculate the alpha value for the text based on the alpha value of the ripple
local alpha = 1 - (effect.radius / 500)
if alpha > 0 then
local text = string.format("Effect %d: x = %d, y = %d, radius = %d", i, effect.x, effect.y, effect.radius)
love.graphics.setColor(1, 1, 1, alpha) -- Set color to white with fading alpha
love.graphics.print(text, xOffset, yOffset) -- Draw text
yOffset = yOffset + lineHeight -- Increase yOffset for next text
else
-- Remove the corresponding text entry when alpha drops below 0
table.remove(clickEffects, i)
end
end
-- Draw click effects
for _, effect in ipairs(clickEffects) do
local alpha = 1 - (effect.radius / 500) -- Calculate alpha based on radius
love.graphics.setColor(0.2, 0.6, 1, alpha) -- Set color to blue with fading alpha
love.graphics.circle("line", effect.x, effect.y, effect.radius)
end
-- Draw projectiles
drawProjectiles()
end
function love.touchpressed(id, x, y, dx, dy, pressure)
if gameState == "lost" then
-- Check if the touch is within the exit button bounds
if x >= exitButtonX and x <= exitButtonX + buttonWidth and y >= exitButtonY and y <= exitButtonY + buttonHeight then
love.event.quit()
end
-- Check if the touch is within the try again button bounds
if x >= tryAgainButtonX and x <= tryAgainButtonX + buttonWidth and y >= tryAgainButtonY and y <= tryAgainButtonY + buttonHeight then
resetGame()
end
else
-- Handle touch for other game states
player.targetX = x - player.width / 2
player.targetY = y - player.height / 2
-- Create a new click effect
local newEffect = {
x = x,
y = y,
radius = 0
}
table.insert(clickEffects, newEffect)
end
end
function checkCollision(a, b)
return a.x < b.x + b.width and
b.x < a.x + a.width and
a.y < b.y + b.height and
b.y < a.y + a.height
end
function updatePlayerPosition(dt)
local dx = player.targetX - player.x
local dy = player.targetY - player.y
local distance = math.sqrt(dx * dx + dy * dy)
if distance > player.speed * dt then
player.x = player.x + (dx / distance) * player.speed * dt
player.y = player.y + (dy / distance) * player.speed * dt
else
player.x = player.targetX
player.y = player.targetY
end
end
-- Modify updateEnemyPosition function
function updateEnemyPosition(dt)
if gameState ~= "playing" then
return -- Do not update enemy
-- Clear clickEffects when game is won
clickEffects = {}
elseif gameState == "lost" then
-- Handle game over state
end
end
function love.draw()
if gameState == "playing" then
drawGame()
drawSightRange() -- Draw the sight range cone
-- Debug information
love.graphics.setColor(1, 1, 1) -- Set color to white
love.graphics.setFont(love.graphics.newFont(18)) -- Set font size to 18
love.graphics.print("Your position: (" .. player.x .. ", " .. player.y .. ")", 10, 30)
elseif gameState == "won" then
drawWinningScreen()
if not soundPlayed then
love.audio.play(CongratsSound)
soundPlayed = true
end
elseif gameState == "lost" then
drawGameOverScreen()
if not soundPlayed then
love.audio.play(GameOver)
soundPlayed = true
end
end
-- Draw click effects list
love.graphics.setColor(1, 1, 1) -- Set color to white
love.graphics.setFont(love.graphics.newFont(18)) -- Set font size to 18
local xOffset = love.graphics.getWidth() - 400 -- Offset from the right side of the window
local yOffset = 50 -- Initial offset for y-coordinate (lowered slightly)
local lineHeight = 20 -- Height of each line in the list
for i, effect in ipairs(clickEffects) do
-- Calculate the alpha value for the text based on the alpha value of the ripple
local alpha = 1 - (effect.radius / 500)
if alpha > 0 then
local text = string.format("Effect %d: x = %d, y = %d, radius = %d", i, effect.x, effect.y, effect.radius)
love.graphics.setColor(1, 1, 1, alpha) -- Set color to white with fading alpha
love.graphics.print(text, xOffset, yOffset) -- Draw text
yOffset = yOffset + lineHeight -- Increase yOffset for next text
else
-- Remove the corresponding text entry when alpha drops below 0
table.remove(clickEffects, i)
end
end
-- Draw click effects
for _, effect in ipairs(clickEffects) do
local alpha = 1 - (effect.radius / 500) -- Calculate alpha based on radius
love.graphics.setColor(0.2, 0.6, 1, alpha) -- Set color to blue with fading alpha
love.graphics.circle("line", effect.x, effect.y, effect.radius)
end
-- Draw projectiles
drawProjectiles()
end
function love.touchpressed(id, x, y, dx, dy, pressure)
if gameState == "lost" then
-- Check if the touch is within the exit button bounds
if x >= exitButtonX and x <= exitButtonX + buttonWidth and y >= exitButtonY and y <= exitButtonY + buttonHeight then
love.event.quit()
end
-- Check if the touch is within the try again button bounds
if x >= tryAgainButtonX and x <= tryAgainButtonX + buttonWidth and y >= tryAgainButtonY and y <= tryAgainButtonY + buttonHeight then
resetGame()
end
else
-- Handle touch for other game states
player.targetX = x - player.width / 2
player.targetY = y - player.height / 2
-- Create a new click effect
local newEffect = {
x = x,
y = y,
radius = 0
}
table.insert(clickEffects, newEffect)
end
end
function checkCollision(a, b)
return a.x < b.x + b.width and
b.x < a.x + a.width and
a.y < b.y + b.height and
b.y < a.y + a.height
end
function updatePlayerPosition(dt)
local dx = player.targetX - player.x
local dy = player.targetY - player.y
local distance = math.sqrt(dx * dx + dy * dy)
if distance > player.speed * dt then
player.x = player.x + (dx / distance) * player.speed * dt
player.y = player.y + (dy / distance) * player.speed * dt
else
player.x = player.targetX
player.y = player.targetY
end
end
-- Modify updateEnemyPosition function
function updateEnemyPosition(dt)
if gameState ~= "playing" then
return -- Do not update enemy
position if game state is not playing
end
if isPlayerInSightRange() then
-- Player is in sight range, engage behavior (e.g., attack or pursue)
enemy.shootTimer = enemy.shootTimer + dt
if enemy.shootTimer >= enemy.shootCooldown then
-- Shoot or perform action towards the player
local dx = player.x - enemy.x
local dy = player.y - enemy.y
local distance = math.sqrt(dx * dx + dy * dy)
if distance > 0 then
local directionX = dx / distance
local directionY = dy / distance
spawnProjectile(enemy.x + enemy.width / 2, enemy.y + enemy.height / 2, {x = directionX, y = directionY})
end
enemy.shootTimer = 0
end
-- Move towards player
local dx = player.x - enemy.x
local dy = player.y - enemy.y
local distance = math.sqrt(dx * dx + dy * dy)
if distance > 0 then
local directionX = dx / distance
local directionY = dy / distance
enemy.x = enemy.x + directionX * enemy.speed * dt
enemy.y = enemy.y + directionY * enemy.speed * dt
end
-- Update enemy's sight cone direction based on movement
enemy.directionX = dx
enemy.directionY = dy
else
-- Player is not in sight range, idle behavior (patrol)
local targetPoint = patrolPoints[currentPatrolIndex]
local dx = targetPoint.x - enemy.x
local dy = targetPoint.y - enemy.y
local distance = math.sqrt(dx * dx + dy * dy)
if distance > 0 then
local directionX = dx / distance
local directionY = dy / distance
enemy.x = enemy.x + directionX * enemy.speed * dt
enemy.y = enemy.y + directionY * enemy.speed * dt
end
-- Update enemy's sight cone direction based on movement
enemy.directionX = dx
enemy.directionY = dy
-- Check if reached the current patrol point
if distance < 5 then
currentPatrolIndex = currentPatrolIndex % #patrolPoints + 1
end
end
end
function checkKeyCollision()
if not key.collected and checkCollision(player, key) then
key.collected = true
player.hasKey = true
love.audio.play(KeyCollected)
keyCollectedMessage.alpha = 1 -- Reset alpha to 1 when key is collected
end
end
function checkDoorCollision()
if player.hasKey and checkCollision(player, door) then
gameState = "won"
end
end
function checkEnemyCollision()
if checkCollision(player, enemy) then
gameState = "lost"
end
end
function updateClickEffects(dt)
-- Update click effects
for i = #clickEffects, 1, -1 do
clickEffects[i].radius = clickEffects[i].radius + 600 * dt -- Adjust the speed of expansion
local maxRadius = 500 -- Adjust the maximum radius as needed
if clickEffects[i].radius > maxRadius then
clickEffects[i].radius = maxRadius -- Cap the radius to the maximum value
end
end
end
function updateKeyCollectedMessage(dt)
-- Update key collected message alpha
if key.collected then
keyCollectedMessage.alpha = keyCollectedMessage.alpha - dt * keyCollectedMessage.fadeSpeed
if keyCollectedMessage.alpha <= 0 then
keyCollectedMessage.alpha = 0
end
else
keyCollectedMessage.alpha = 0 -- Set alpha to 0 if the key is not collected
end
end
function drawGame()
-- Draw player
love.graphics.setColor(0.2, 0.6, 1)
love.graphics.rectangle("fill", player.x, player.y, player.width, player.height)
-- Draw enemy
love.graphics.setColor(1, 0, 0)
love.graphics.rectangle("fill", enemy.x, enemy.y, enemy.width, enemy.height)
-- Draw key if not collected
if not key.collected then
love.graphics.setColor(1, 1, 0)
end
if isPlayerInSightRange() then
-- Player is in sight range, engage behavior (e.g., attack or pursue)
enemy.shootTimer = enemy.shootTimer + dt
if enemy.shootTimer >= enemy.shootCooldown then
-- Shoot or perform action towards the player
local dx = player.x - enemy.x
local dy = player.y - enemy.y
local distance = math.sqrt(dx * dx + dy * dy)
if distance > 0 then
local directionX = dx / distance
local directionY = dy / distance
spawnProjectile(enemy.x + enemy.width / 2, enemy.y + enemy.height / 2, {x = directionX, y = directionY})
end
enemy.shootTimer = 0
end
-- Move towards player
local dx = player.x - enemy.x
local dy = player.y - enemy.y
local distance = math.sqrt(dx * dx + dy * dy)
if distance > 0 then
local directionX = dx / distance
local directionY = dy / distance
enemy.x = enemy.x + directionX * enemy.speed * dt
enemy.y = enemy.y + directionY * enemy.speed * dt
end
-- Update enemy's sight cone direction based on movement
enemy.directionX = dx
enemy.directionY = dy
else
-- Player is not in sight range, idle behavior (patrol)
local targetPoint = patrolPoints[currentPatrolIndex]
local dx = targetPoint.x - enemy.x
local dy = targetPoint.y - enemy.y
local distance = math.sqrt(dx * dx + dy * dy)
if distance > 0 then
local directionX = dx / distance
local directionY = dy / distance
enemy.x = enemy.x + directionX * enemy.speed * dt
enemy.y = enemy.y + directionY * enemy.speed * dt
end
-- Update enemy's sight cone direction based on movement
enemy.directionX = dx
enemy.directionY = dy
-- Check if reached the current patrol point
if distance < 5 then
currentPatrolIndex = currentPatrolIndex % #patrolPoints + 1
end
end
end
function checkKeyCollision()
if not key.collected and checkCollision(player, key) then
key.collected = true
player.hasKey = true
love.audio.play(KeyCollected)
keyCollectedMessage.alpha = 1 -- Reset alpha to 1 when key is collected
end
end
function checkDoorCollision()
if player.hasKey and checkCollision(player, door) then
gameState = "won"
end
end
function checkEnemyCollision()
if checkCollision(player, enemy) then
gameState = "lost"
end
end
function updateClickEffects(dt)
-- Update click effects
for i = #clickEffects, 1, -1 do
clickEffects[i].radius = clickEffects[i].radius + 600 * dt -- Adjust the speed of expansion
local maxRadius = 500 -- Adjust the maximum radius as needed
if clickEffects[i].radius > maxRadius then
clickEffects[i].radius = maxRadius -- Cap the radius to the maximum value
end
end
end
function updateKeyCollectedMessage(dt)
-- Update key collected message alpha
if key.collected then
keyCollectedMessage.alpha = keyCollectedMessage.alpha - dt * keyCollectedMessage.fadeSpeed
if keyCollectedMessage.alpha <= 0 then
keyCollectedMessage.alpha = 0
end
else
keyCollectedMessage.alpha = 0 -- Set alpha to 0 if the key is not collected
end
end
function drawGame()
-- Draw player
love.graphics.setColor(0.2, 0.6, 1)
love.graphics.rectangle("fill", player.x, player.y, player.width, player.height)
-- Draw enemy
love.graphics.setColor(1, 0, 0)
love.graphics.rectangle("fill", enemy.x, enemy.y, enemy.width, enemy.height)
-- Draw key if not collected
if not key.collected then
love.graphics.setColor(1, 1, 0)
love.graphics.rectangle("fill", key.x, key.y, key.width, key.height)
end
-- Draw door
love.graphics.setColor(0.6, 0.3, 0.1)
love.graphics.rectangle("fill", door.x, door.y, door.width, door.height)
-- Draw key collected indicator
if key.collected then
love.graphics.setColor(1, 1, 0, keyCollectedMessage.alpha)
love.graphics.setFont(love.graphics.newFont(24)) -- Set font size to 24
love.graphics.printf(keyCollectedMessage.text, 0, love.graphics.getHeight() - 50, love.graphics.getWidth(), "center")
end
end
function drawWinningScreen()
local r = math.abs(math.sin(flashTimer * 2))
local g = math.abs(math.sin(flashTimer * 2 + math.pi / 2))
local b = math.abs(math.sin(flashTimer * 2 + math.pi))
love.graphics.setColor(r, g, b)
love.graphics.setFont(love.graphics.newFont(72))
love.graphics.printf("Congratulations!", 0, love.graphics.getHeight() / 2 - 36, love.graphics.getWidth(), "center")
projectiles = {}
clickEffects = {}
end
function drawGameOverScreen()
-- Draw game over text
love.graphics.setColor(1, 0, 0)
love.graphics.setFont(love.graphics.newFont(36))
love.graphics.printf("Game Over", 0, love.graphics.getHeight() / 3, love.graphics.getWidth(), "center")
-- Clear Effects
clickEffects = {}
-- Projectiles
projectiles = {}
-- Draw buttons
love.graphics.setColor(0.5, 0.5, 0.5)
love.graphics.rectangle("fill", exitButtonX, exitButtonY, buttonWidth, buttonHeight)
love.graphics.rectangle("fill", tryAgainButtonX, tryAgainButtonY, buttonWidth, buttonHeight)
-- Draw button text
love.graphics.setColor(1, 1, 1)
love.graphics.setFont(love.graphics.newFont(24))
love.graphics.printf("Exit", exitButtonX, exitButtonY + (buttonHeight - love.graphics.getFont():getHeight()) / 2, buttonWidth, "center")
love.graphics.printf("Try Again", tryAgainButtonX, tryAgainButtonY + (buttonHeight - love.graphics.getFont():getHeight()) / 2, buttonWidth, "center")
end
function resetGame()
-- Reset player position
player.x = 50
player.y = 50
player.hasKey = false
player.targetX = 50
player.targetY = 50
-- Reset key position and state
key.x = 300
key.y = 200
key.collected = false
-- Reset enemy position
enemy.x = 600
enemy.y = 400
-- Reset game state
gameState = "playing"
-- Reset sound played flag
soundPlayed = false
end
-- Projectile functions
function spawnProjectile(x, y, direction)
local newProjectile = {
x = x,
y = y,
direction = direction,
speed = 350, -- Adjust as needed
size = 5,
active = true
}
table.insert(projectiles, newProjectile)
end
function updateProjectiles(dt)
for i = #projectiles, 1, -1 do
local proj = projectiles[i]
proj.x = proj.x + proj.direction.x * proj.speed * dt
proj.y = proj.y + proj.direction.y * proj.speed * dt
-- Check collision with player
if checkCollision(player, proj) then
gameState = "lost"
table.remove(projectiles, i) -- Remove projectile upon collision
end
-- Check if projectile is out of bounds
if proj.x < 0 or proj.x > love.graphics.getWidth() or
proj.y < 0 or proj.y > love.graphics.getHeight() then
table.remove(projectiles, i) -- Remove projectile if out of bounds
end
end
end
function drawProjectiles()
love.graphics.setColor(1, 0, 0) -- Red ;)
for _, proj in ipairs(projectiles) do
love.graphics.circle("fill", proj.x, proj.y, proj.size)
end
end
function checkCollision(a, b)
if b.size then
-- If b is a projectile
return a.x < b.x + b.size and
b.x < a.x + a.width and
a.y < b.y + b.size and
b.y < a.y + a.height
else
-- For other objects like player, enemy, key, door
return a.x < b.x + b.width and
b.x < a.x + a.width and
a.y < b.y + b.height and
end
-- Draw door
love.graphics.setColor(0.6, 0.3, 0.1)
love.graphics.rectangle("fill", door.x, door.y, door.width, door.height)
-- Draw key collected indicator
if key.collected then
love.graphics.setColor(1, 1, 0, keyCollectedMessage.alpha)
love.graphics.setFont(love.graphics.newFont(24)) -- Set font size to 24
love.graphics.printf(keyCollectedMessage.text, 0, love.graphics.getHeight() - 50, love.graphics.getWidth(), "center")
end
end
function drawWinningScreen()
local r = math.abs(math.sin(flashTimer * 2))
local g = math.abs(math.sin(flashTimer * 2 + math.pi / 2))
local b = math.abs(math.sin(flashTimer * 2 + math.pi))
love.graphics.setColor(r, g, b)
love.graphics.setFont(love.graphics.newFont(72))
love.graphics.printf("Congratulations!", 0, love.graphics.getHeight() / 2 - 36, love.graphics.getWidth(), "center")
projectiles = {}
clickEffects = {}
end
function drawGameOverScreen()
-- Draw game over text
love.graphics.setColor(1, 0, 0)
love.graphics.setFont(love.graphics.newFont(36))
love.graphics.printf("Game Over", 0, love.graphics.getHeight() / 3, love.graphics.getWidth(), "center")
-- Clear Effects
clickEffects = {}
-- Projectiles
projectiles = {}
-- Draw buttons
love.graphics.setColor(0.5, 0.5, 0.5)
love.graphics.rectangle("fill", exitButtonX, exitButtonY, buttonWidth, buttonHeight)
love.graphics.rectangle("fill", tryAgainButtonX, tryAgainButtonY, buttonWidth, buttonHeight)
-- Draw button text
love.graphics.setColor(1, 1, 1)
love.graphics.setFont(love.graphics.newFont(24))
love.graphics.printf("Exit", exitButtonX, exitButtonY + (buttonHeight - love.graphics.getFont():getHeight()) / 2, buttonWidth, "center")
love.graphics.printf("Try Again", tryAgainButtonX, tryAgainButtonY + (buttonHeight - love.graphics.getFont():getHeight()) / 2, buttonWidth, "center")
end
function resetGame()
-- Reset player position
player.x = 50
player.y = 50
player.hasKey = false
player.targetX = 50
player.targetY = 50
-- Reset key position and state
key.x = 300
key.y = 200
key.collected = false
-- Reset enemy position
enemy.x = 600
enemy.y = 400
-- Reset game state
gameState = "playing"
-- Reset sound played flag
soundPlayed = false
end
-- Projectile functions
function spawnProjectile(x, y, direction)
local newProjectile = {
x = x,
y = y,
direction = direction,
speed = 350, -- Adjust as needed
size = 5,
active = true
}
table.insert(projectiles, newProjectile)
end
function updateProjectiles(dt)
for i = #projectiles, 1, -1 do
local proj = projectiles[i]
proj.x = proj.x + proj.direction.x * proj.speed * dt
proj.y = proj.y + proj.direction.y * proj.speed * dt
-- Check collision with player
if checkCollision(player, proj) then
gameState = "lost"
table.remove(projectiles, i) -- Remove projectile upon collision
end
-- Check if projectile is out of bounds
if proj.x < 0 or proj.x > love.graphics.getWidth() or
proj.y < 0 or proj.y > love.graphics.getHeight() then
table.remove(projectiles, i) -- Remove projectile if out of bounds
end
end
end
function drawProjectiles()
love.graphics.setColor(1, 0, 0) -- Red ;)
for _, proj in ipairs(projectiles) do
love.graphics.circle("fill", proj.x, proj.y, proj.size)
end
end
function checkCollision(a, b)
if b.size then
-- If b is a projectile
return a.x < b.x + b.size and
b.x < a.x + a.width and
a.y < b.y + b.size and
b.y < a.y + a.height
else
-- For other objects like player, enemy, key, door
return a.x < b.x + b.width and
b.x < a.x + a.width and
a.y < b.y + b.height and
b.y < a.y + a.height
end
end
It's so hard to implement a shooting feature for the player, probably 'cause it will share a touch with the movement and move and shoot where you clicked. It's really hard to explain LOL, but I just want to be able to implement a move and shoot independently feature. Any suggestions? Thanks in advance.
Edit: I just realised how butchered the code looks on reddit, I don't know how to properly write code snippets though :(
https://redd.it/1delu9f
@r_lua
end
end
It's so hard to implement a shooting feature for the player, probably 'cause it will share a touch with the movement and move and shoot where you clicked. It's really hard to explain LOL, but I just want to be able to implement a move and shoot independently feature. Any suggestions? Thanks in advance.
Edit: I just realised how butchered the code looks on reddit, I don't know how to properly write code snippets though :(
https://redd.it/1delu9f
@r_lua
Reddit
From the lua community on Reddit
Explore this post and more from the lua community