๐ญmad-thoughts
Turn ๐ฌ notifications into ๐ narratives.
Link
Introduction
A thought bubble notification system designed for immersive roleplay.
In traditional FiveM servers, notifications often break immersion with meta-gaming information: "Not enough police online" or "You don't have the required skill level." These messages force players to acknowledge game mechanics rather than staying in character.
mad-thoughts transforms this experience by displaying notifications as the character's inner thoughts. When a player encounters a locked door they can't pick, instead of seeing "Lockpicking skill too low," they'll think: "This lock looks too complex for me..."
Preview
Watch the preview video to see mad-thoughts in action, or view the screenshots below.
Features & Benefits
๐ญ Immersive Thought Bubbles: Displays character thoughts above the player's head
๐จ Rich Customization: Icons, colors, and timing options
๐ Scenario System: Automatic thoughts based on gameplay situations
๐ฌ Message Variety: Random selection prevents repetition
๐ Location Awareness: Thoughts trigger in specific areas
๐ Sequential Thoughts: Create story-driven thought chains
๐ Server-Wide Thoughts: Send thoughts to all connected players
๐ป Developer-Friendly: Simple exports for easy implementation
โญ Perfect For:
Skill check feedback that doesn't break immersion
Subtle environmental storytelling
Replacing meta-gaming notifications with in-character thoughts
Adding personality and depth to player characters
Creating a more cinematic and immersive experience
Transform your server's notification system from immersion-breaking text to character-driven thoughts. Your players will feel more connected to their characters, and your world will feel more authentic and alive.
Scenarios & Locations
The real power of mad-thoughts comes from automatic triggers that create thoughts based on specific conditions. These features transform passive notifications into immersive character experiences. While the resource includes several pre-built examples, you're encouraged to create ones tailored to your server.
Scenario-Based Thoughts
Scenarios are condition-driven thoughts that monitor your player's state and environment. When specific conditions are met, they automatically trigger appropriate thoughts. Common scenario types include:
Health & Status: Low health, stress levels, character needs
Environmental: Weather conditions, time of day, underwater
Action-Based: Swimming, running, combat situations
Character Needs: Hunger, thirst, fatigue
Scenarios use custom check functions to determine when to trigger, making them highly flexible for any roleplay situation.
Location-Based Thoughts
Location triggers create area-specific thoughts that enhance environmental storytelling. Players automatically think contextually appropriate thoughts when entering designated areas.
Location triggers support both pre-configured areas (set in config.lua
) and dynamic registration and destruction, giving you complete control over when and where thoughts appear.
Circular Areas
Perfect for simple zones around points of interest:
Hospitals and police stations
Beaches, mountains, and landmarks
Buildings and neighborhoods
General area triggers
Polygon Zones
Ideal for complex, custom-shaped areas:
Detailed building interiors
Irregularly shaped districts
Precise zone boundaries
Custom map areas and routes
How to Use
Basic Thoughts
-- Basic notification
exports['mad-thoughts']:thought("I can't do this right now...")
-- With duration (10 seconds)
exports['mad-thoughts']:thought("I wonder what that means...", 10)
-- With icon
exports['mad-thoughts']:thought("Something doesn't feel right about this...", 5, "fas fa-question-circle")
-- With icon and color
exports['mad-thoughts']:thought("I need to get out of here!", 8, "fas fa-exclamation", "#e74c3c")
Parameters
message: The text content of the thought (string)
duration: How long the thought displays in seconds (number, default: 5)
icon: Font Awesome icon class (string, default: "fas fa-comment-dots")
color: Hex color code for the icon and border (string, default: "#F2F2F2")
Using predefined types
exports['mad-thoughts']:info("I just remembered where I left my keys...", 5)
exports['mad-thoughts']:success("I finally figured it out!", 5)
exports['mad-thoughts']:warning("This doesn't look safe...", 5)
exports['mad-thoughts']:error("I've made a terrible mistake...", 5)
Predefined types use the following icons and colors:
info: "fas fa-info-circle" with blue color (#2B78FC)
success: "fas fa-check-circle" with green color (#06CE6B)
warning: "fas fa-exclamation-triangle" with orange color (#FB8607)
error: "fas fa-times-circle" with red color (#fe2436)
Sequential Thoughts
Display multiple thoughts in sequence:
exports['mad-thoughts']:thoughtChain({
{message = "That's strange...", duration = 3},
{message = "I wonder if anyone is here...", duration = 4, icon = "fas fa-question"},
{message = "Better be careful.", duration = 3, color = "#e74c3c"}
}, 4000)
Parameters
thoughts: Array of thought objects, each with message, duration, icon, and color
delay: Time between thoughts in milliseconds (default: 3s)
initialDelay: Optional delay before starting the sequence (default: 0s)
Dynamic Scenario and Location Triggers
The system allows you to dynamically create and remove thought triggers. This is especially useful for:
Scenario and location triggers that appear only during certain jobs, heists, or events
Temporary areas that should trigger thoughts for a limited time
Dynamic story elements that change based on server state
Creating Custom Scenarios
Scenarios automatically trigger thoughts based on conditions you define. Each scenario includes a check function that determines when to trigger the thought.
Scenario Registration
exports['mad-thoughts']:registerScenario('myResourceScenario', {
check = function(ped)
return IsPedAimingFromCover(ped)
end,
message = "Ready, aim, fire...",
duration = 6,
icon = "fas fa-crosshairs",
color = "#FF0000",
cooldown = 30
})
Parameters
check: Function that returns true when the scenario should trigger
message: The thought text (string or array for random selection)
duration: How long the thought displays (seconds)
icon: Font Awesome icon class
color: Hex color code for styling
cooldown: Minimum seconds between triggers
Creating Custom Locations
Location triggers create thoughts when players enter specific areas. The system supports both circular areas and complex polygon zones.
Circular Zones Registration
Define a radius around a point to create your zone where the thought will trigger:
exports['mad-thoughts']:registerLocation('myResourceLocation', {
coords = vector3(176.52, -954.48, 30.09),
distance = 20.0,
message = "This place looks familiar...",
duration = 5,
icon = "fas fa-building",
color = "#3498db",
cooldown = 300
})
Parameters
coords: Center point coordinates (vector3)
distance: Trigger radius in game units
message: The thought text (string or array)
duration: Display duration in seconds
icon: Font Awesome icon class
color: Hex color code
cooldown: Seconds between triggers
Polygon Zone Registration
For complex, custom-shaped areas, use polygon zones:
exports['mad-thoughts']:registerPolygonZone('myResourceZone', {
points = {
vector3(447.9, -998.8, 25.8),
vector3(450.3, -998.2, 25.8),
vector3(449.9, -995.5, 25.8),
vector3(447.2, -995.6, 25.8),
vector3(446.3, -997.9, 25.8),
},
thickness = 4.0,
message = "This custom zone feels different...",
duration = 5,
icon = "fas fa-draw-polygon",
color = "#9b59b6",
cooldown = 300
})
Parameters
points: Array of vector3 coordinates defining the polygon shape
thickness: Vertical height of the zone (optional, default: 4.0)
message: The thought text (string or array)
duration: Display duration in seconds
icon: Font Awesome icon class
color: Hex color code
cooldown: Seconds between triggers
Note: Polygon zones require all Z coordinates to be on the same plane. The system automatically handles this requirement:
When mixed Z coordinates are detected (e.g., some points at 25.0, others at 30.0), the system automatically calculates the average Z value
Setting up Polygon Zones
You can use /zone poly
to easily setup new zones. Read the documentation for the zone-creator here.
Removing Custom Triggers
Clean up your custom scenarios and locations when they're no longer needed:
-- Remove a scenario you previously registered
exports['mad-thoughts']:removeScenario('myResourceScenario')
-- Remove a circular location you previously registered
exports['mad-thoughts']:removeLocation('myResourceLocation')
-- Remove a polygon zone you previously registered
exports['mad-thoughts']:removePolygonZone('myResourceZone')
Server-Wide Thoughts
You can send thoughts to all connected players:
From a Client Script
-- Send a thought to all players on the server
exports['mad-thoughts']:sendThoughtToAll(
"Everyone should see this message!", -- message
5, -- duration in seconds
"fas fa-bullhorn", -- icon
"#E67E22" -- color (orange)
)
From a Server Script
-- Send a thought directly from a server script
exports['mad-thoughts']:sendThoughtToAll(
"Server announcement as a thought", -- message
5, -- duration in seconds
"fas fa-server", -- icon
"#3498db" -- color (blue)
)
Via Command
-- Basic usage
/globalthought "This is a global announcement"
-- With custom duration (in seconds)
/globalthought "This is a global announcement" "10"
-- With custom icon
/globalthought "This is a global announcement" "10" "fa-solid fa-bell"
-- With custom color
/globalthought "This is a global announcement" "10" "fa-solid fa-bell" "#FF5733"
This command requires the group.admin permission.
Player-Specific Thoughts
You can send thoughts to specific players:
From a Client Script
-- Send a thought to a specific player
exports['mad-thoughts']:sendThoughtToPlayer(
1, -- target player's server ID
"This message is only for you", -- message
5, -- duration in seconds
"fas fa-user-secret", -- icon
"#9B59B6" -- color (purple)
)
From a Server Script
-- Send a thought directly from a server script
exports['mad-thoughts']:sendThoughtToPlayer(
1, -- target player's server ID
"This message is only for you", -- message
5, -- duration in seconds
"fas fa-envelope", -- icon
"#3498db" -- color (blue)
)
Via Command
-- Basic usage
/playerthought 1 "This is a private thought"
-- With custom duration (in seconds)
/playerthought 1 "This is a private thought" "10"
-- With custom icon
/playerthought 1 "This is a private thought" "10" "fa-solid fa-crown"
-- With custom color
/playerthought 1 "This is a private thought" "10" "fa-solid fa-crown" "#9B59B6"
This command requires the group.admin permission.
Examples
Making NPCs More Immersive
-- Example: When player approaches a mission NPC
exports['mad-thoughts']:thought("I wonder if I should talk to this stranger...", 5)
Skill Check Feedback
-- Example: Instead of "Lockpicking failed"
exports['mad-thoughts']:error("I can't seem to get these tumblers right...", 4)
Environmental Storytelling
-- Example: When entering an abandoned building
exports['mad-thoughts']:thoughtChain({
{message = "This place gives me the creeps...", duration = 4},
{message = "Feels like someone's watching me...", duration = 3, icon = "fas fa-eye"}
}, 3000)
Proximity
-- Example: Send a thought to nearby players
local function notifyNearbyPlayers(message, duration, icon, color)
local myCoords = GetEntityCoords(cache.ped)
local players = GetActivePlayers()
for _, playerId in ipairs(players) do
if playerId ~= PlayerId() then
local targetPed = GetPlayerPed(playerId)
local targetCoords = GetEntityCoords(targetPed)
local distance = #(myCoords - targetCoords)
if distance <= 10.0 then
exports['mad-thoughts']:sendThoughtToPlayer(
GetPlayerServerId(playerId),
message or "This guy looks suspicious",
duration or 5,
icon or "fas fa-exclamation",
color or "#e74c3c"
)
end
end
end
end
-- Example: When a player discovers something important
RegisterNetEvent('discovered')
AddEventHandler('discovered', function()
-- Thought for the player who made the discovery
exports['mad-thoughts']:thought("What's this strange artifact?", 5)
-- Send a different thought to nearby players
notifyNearbyPlayers(
"Did someone just find something?",
5,
"fas fa-question-circle",
"#9B59B6"
)
end)
Group Coordination
-- Example: Coordinating with specific team members
RegisterNetEvent('heist:beginOperation')
AddEventHandler('heist:beginOperation', function(teamMembers)
for role, playerId in pairs(teamMembers) do
if role == "hacker" then
exports['mad-thoughts']:sendThoughtToPlayer(
playerId,
"I need to focus on bypassing the security system...",
5,
"fas fa-laptop-code",
"#3498db"
)
elseif role == "lookout" then
exports['mad-thoughts']:sendThoughtToPlayer(
playerId,
"I should keep an eye out for guards...",
5,
"fas fa-eye",
"#f39c12"
)
elseif role == "driver" then
exports['mad-thoughts']:sendThoughtToPlayer(
playerId,
"Engine's running. Ready for a quick getaway...",
5,
"fas fa-car",
"#2ecc71"
)
end
end
end)
Dependencies
Installation
Ensure you have the required dependency
Place the folder in your server's resources directory
Add
start mad-thoughts
to your server.cfgConfigure scenarios & locations in the
config.lua
file to match your server's needs
Configuration
The resource comes with a comprehensive config.lua
file that includes:
Ready-to-use templates for scenarios and locations
Example circular and polygon locations for popular map areas
Example scenarios for common situations (underwater, low health, weather conditions)
Detailed comments explaining every configuration option

Debug
The resource includes debug visualizations to help you set up and test your location triggers:
Enable debug mode in your config.lua
:
Debug = true, -- boolean (true/false | default: false)
When debug mode is enabled, you'll see:
Visual indicators showing location trigger zones as red spheres
Location IDs displayed above each zone
This is particularly useful when:
Setting up new location triggers to ensure they're in the right place
Testing the detection radius of your locations

Support
Join our Discord community for support, regular updates and to request new features.
Escrow Protection
This resource is protected by FiveM's escrow system to safeguard against unauthorised distribution. The core functionality is secured while maintaining full customisability through the extensive configuration system.
Despite the security measures:
All configuration options remain fully editable.
The UI remains fully editable (html, css, js)
Server owners retain complete control over all settings.
No functionality is limited by the protection system.
This approach ensures you receive a premium, leak-protected resource while maintaining the freedom to customise it for your server's unique needs.
Last updated