Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
-- ============================================================================
-- Nyxframework: nyx_services, nyx_properties, nyx_treasury & nyx_vehicles
-- Migration: 067_municipal_water_utility_and_hydrant_pressure.sql
-- Description: Municipal reservoirs, contamination diffusion, pump station power
-- dependencies, and fire hydrant pressure dynamics.
-- Engine: InnoDB | Charset: utf8mb4 | Collation: utf8mb4_unicode_ci
-- ============================================================================

SET FOREIGN_KEY_CHECKS = 0;

-- 1. Create municipal water reservoirs table
CREATE TABLE IF NOT EXISTS `nyx_water_reservoirs` (
`reservoir_id` VARCHAR(32) NOT NULL PRIMARY KEY,
`label` VARCHAR(64) NOT NULL,
`water_level_liters` DECIMAL(15, 2) NOT NULL DEFAULT 5000000.00,
`max_capacity_liters` DECIMAL(15, 2) NOT NULL DEFAULT 10000000.00,
`purity_percentage` DECIMAL(5, 2) NOT NULL DEFAULT 100.00,
`contamination_ppm` DECIMAL(8, 2) NOT NULL DEFAULT 0.00,
`substation_id` VARCHAR(32) NOT NULL,
`coords_x` DECIMAL(10, 4) NOT NULL DEFAULT 0.0000,
`coords_y` DECIMAL(10, 4) NOT NULL DEFAULT 0.0000,
`coords_z` DECIMAL(10, 4) NOT NULL DEFAULT 0.0000,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_res_substation` (`substation_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 2. Create municipal pump stations table
CREATE TABLE IF NOT EXISTS `nyx_water_pump_stations` (
`pump_id` VARCHAR(32) NOT NULL PRIMARY KEY,
`reservoir_id` VARCHAR(32) NOT NULL,
`substation_id` VARCHAR(32) NOT NULL,
`pump_status` ENUM('ONLINE', 'OFFLINE_POWER', 'FAULT') NOT NULL DEFAULT 'ONLINE',
`flow_rate_lps` DECIMAL(8, 2) NOT NULL DEFAULT 150.00,
`power_draw_kw` DECIMAL(8, 2) NOT NULL DEFAULT 750.00,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_pump_res` (`reservoir_id`),
INDEX `idx_pump_sub` (`substation_id`),
INDEX `idx_pump_status` (`pump_status`),
CONSTRAINT `fk_pump_reservoir` FOREIGN KEY (`reservoir_id`) REFERENCES `nyx_water_reservoirs`(`reservoir_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 3. Create municipal fire hydrants table
CREATE TABLE IF NOT EXISTS `nyx_fire_hydrants` (
`hydrant_id` VARCHAR(32) NOT NULL PRIMARY KEY,
`reservoir_id` VARCHAR(32) NOT NULL,
`coords_x` DECIMAL(10, 4) NOT NULL DEFAULT 0.0000,
`coords_y` DECIMAL(10, 4) NOT NULL DEFAULT 0.0000,
`coords_z` DECIMAL(10, 4) NOT NULL DEFAULT 0.0000,
`pressure_psi` DECIMAL(6, 2) NOT NULL DEFAULT 85.00,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`last_inspected_at` TIMESTAMP NULL DEFAULT NULL,
INDEX `idx_hydrant_res` (`reservoir_id`),
INDEX `idx_hydrant_status` (`is_active`),
CONSTRAINT `fk_hydrant_reservoir` FOREIGN KEY (`reservoir_id`) REFERENCES `nyx_water_reservoirs`(`reservoir_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 4. Create water contamination incident log table
CREATE TABLE IF NOT EXISTS `nyx_water_contamination_events` (
`event_id` BIGINT AUTO_INCREMENT PRIMARY KEY,
`reservoir_id` VARCHAR(32) NOT NULL,
`contaminant_type` VARCHAR(64) NOT NULL,
`ppm_added` DECIMAL(8, 2) NOT NULL DEFAULT 0.00,
`reported_by` VARCHAR(64) NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_contam_res` (`reservoir_id`),
INDEX `idx_contam_time` (`created_at` DESC),
CONSTRAINT `fk_contam_reservoir` FOREIGN KEY (`reservoir_id`) REFERENCES `nyx_water_reservoirs`(`reservoir_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- Seed default reservoirs
INSERT INTO `nyx_water_reservoirs`
(`reservoir_id`, `label`, `water_level_liters`, `max_capacity_liters`, `purity_percentage`, `contamination_ppm`, `substation_id`, `coords_x`, `coords_y`, `coords_z`)
VALUES
('res_vinewood_lake', 'Vinewood Hills Main Reservoir', 8500000.00, 10000000.00, 100.00, 0.00, 'grid_central_ls', 212.4500, 715.3200, 185.1000),
('res_zancudo_aquifer', 'Fort Zancudo Subterranean Aquifer', 6000000.00, 8000000.00, 99.50, 0.50, 'grid_zancudo', -2250.1000, 3210.4500, 15.2000),
('res_paleto_basin', 'Paleto Bay Coastal Basin', 4500000.00, 5000000.00, 100.00, 0.00, 'grid_paleto', -280.5000, 6210.8000, 30.0000)
ON DUPLICATE KEY UPDATE `label` = VALUES(`label`);

-- Seed default pump stations
INSERT INTO `nyx_water_pump_stations`
(`pump_id`, `reservoir_id`, `substation_id`, `pump_status`, `flow_rate_lps`, `power_draw_kw`)
VALUES
('pump_vinewood_01', 'res_vinewood_lake', 'grid_central_ls', 'ONLINE', 250.00, 1200.00),
('pump_zancudo_01', 'res_zancudo_aquifer', 'grid_zancudo', 'ONLINE', 180.00, 900.00),
('pump_paleto_01', 'res_paleto_basin', 'grid_paleto', 'ONLINE', 120.00, 600.00)
ON DUPLICATE KEY UPDATE `reservoir_id` = VALUES(`reservoir_id`);

-- Seed default fire hydrants
INSERT INTO `nyx_fire_hydrants`
(`hydrant_id`, `reservoir_id`, `coords_x`, `coords_y`, `coords_z`, `pressure_psi`, `is_active`)
VALUES
('hydrant_vinewood_01', 'res_vinewood_lake', 220.1000, 700.5000, 180.2000, 85.00, 1),
('hydrant_legion_01', 'res_vinewood_lake', 150.0000, -900.0000, 30.0000, 90.00, 1),
('hydrant_zancudo_01', 'res_zancudo_aquifer', -2200.0000, 3200.0000, 15.0000, 80.00, 1),
('hydrant_paleto_01', 'res_paleto_basin', -270.0000, 6200.0000, 30.0000, 75.00, 1)
ON DUPLICATE KEY UPDATE `reservoir_id` = VALUES(`reservoir_id`);

SET FOREIGN_KEY_CHECKS = 1;
2 changes: 1 addition & 1 deletion FiveM/nyx_core/sql/migrations/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,5 @@
"064_industrial_refining_and_functional_parts_manufacturing.sql",
"065_judicial_bail_bonds_surety_escrow_and_fugitive_recovery.sql",
"066_municipal_toll_booths_and_commercial_weigh_stations.sql",
"067_highway_degradation_and_transit_repair.sql"
"067_municipal_water_utility_and_hydrant_pressure.sql"
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
-- ============================================================================
-- Nyxframework: nyx_services, nyx_properties, nyx_treasury & nyx_vehicles
-- Migration: 067_municipal_water_utility_and_hydrant_pressure.sql
-- Description: Municipal reservoirs, contamination diffusion, pump station power
-- dependencies, and fire hydrant pressure dynamics.
-- Engine: InnoDB | Charset: utf8mb4 | Collation: utf8mb4_unicode_ci
-- ============================================================================

SET FOREIGN_KEY_CHECKS = 0;

-- 1. Create municipal water reservoirs table
CREATE TABLE IF NOT EXISTS `nyx_water_reservoirs` (
`reservoir_id` VARCHAR(32) NOT NULL PRIMARY KEY,
`label` VARCHAR(64) NOT NULL,
`water_level_liters` DECIMAL(15, 2) NOT NULL DEFAULT 5000000.00,
`max_capacity_liters` DECIMAL(15, 2) NOT NULL DEFAULT 10000000.00,
`purity_percentage` DECIMAL(5, 2) NOT NULL DEFAULT 100.00,
`contamination_ppm` DECIMAL(8, 2) NOT NULL DEFAULT 0.00,
`substation_id` VARCHAR(32) NOT NULL,
`coords_x` DECIMAL(10, 4) NOT NULL DEFAULT 0.0000,
`coords_y` DECIMAL(10, 4) NOT NULL DEFAULT 0.0000,
`coords_z` DECIMAL(10, 4) NOT NULL DEFAULT 0.0000,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_res_substation` (`substation_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 2. Create municipal pump stations table
CREATE TABLE IF NOT EXISTS `nyx_water_pump_stations` (
`pump_id` VARCHAR(32) NOT NULL PRIMARY KEY,
`reservoir_id` VARCHAR(32) NOT NULL,
`substation_id` VARCHAR(32) NOT NULL,
`pump_status` ENUM('ONLINE', 'OFFLINE_POWER', 'FAULT') NOT NULL DEFAULT 'ONLINE',
`flow_rate_lps` DECIMAL(8, 2) NOT NULL DEFAULT 150.00,
`power_draw_kw` DECIMAL(8, 2) NOT NULL DEFAULT 750.00,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_pump_res` (`reservoir_id`),
INDEX `idx_pump_sub` (`substation_id`),
INDEX `idx_pump_status` (`pump_status`),
CONSTRAINT `fk_pump_reservoir` FOREIGN KEY (`reservoir_id`) REFERENCES `nyx_water_reservoirs`(`reservoir_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 3. Create municipal fire hydrants table
CREATE TABLE IF NOT EXISTS `nyx_fire_hydrants` (
`hydrant_id` VARCHAR(32) NOT NULL PRIMARY KEY,
`reservoir_id` VARCHAR(32) NOT NULL,
`coords_x` DECIMAL(10, 4) NOT NULL DEFAULT 0.0000,
`coords_y` DECIMAL(10, 4) NOT NULL DEFAULT 0.0000,
`coords_z` DECIMAL(10, 4) NOT NULL DEFAULT 0.0000,
`pressure_psi` DECIMAL(6, 2) NOT NULL DEFAULT 85.00,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`last_inspected_at` TIMESTAMP NULL DEFAULT NULL,
INDEX `idx_hydrant_res` (`reservoir_id`),
INDEX `idx_hydrant_status` (`is_active`),
CONSTRAINT `fk_hydrant_reservoir` FOREIGN KEY (`reservoir_id`) REFERENCES `nyx_water_reservoirs`(`reservoir_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 4. Create water contamination incident log table
CREATE TABLE IF NOT EXISTS `nyx_water_contamination_events` (
`event_id` BIGINT AUTO_INCREMENT PRIMARY KEY,
`reservoir_id` VARCHAR(32) NOT NULL,
`contaminant_type` VARCHAR(64) NOT NULL,
`ppm_added` DECIMAL(8, 2) NOT NULL DEFAULT 0.00,
`reported_by` VARCHAR(64) NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_contam_res` (`reservoir_id`),
INDEX `idx_contam_time` (`created_at` DESC),
CONSTRAINT `fk_contam_reservoir` FOREIGN KEY (`reservoir_id`) REFERENCES `nyx_water_reservoirs`(`reservoir_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- Seed default reservoirs
INSERT INTO `nyx_water_reservoirs`
(`reservoir_id`, `label`, `water_level_liters`, `max_capacity_liters`, `purity_percentage`, `contamination_ppm`, `substation_id`, `coords_x`, `coords_y`, `coords_z`)
VALUES
('res_vinewood_lake', 'Vinewood Hills Main Reservoir', 8500000.00, 10000000.00, 100.00, 0.00, 'grid_central_ls', 212.4500, 715.3200, 185.1000),
('res_zancudo_aquifer', 'Fort Zancudo Subterranean Aquifer', 6000000.00, 8000000.00, 99.50, 0.50, 'grid_zancudo', -2250.1000, 3210.4500, 15.2000),
('res_paleto_basin', 'Paleto Bay Coastal Basin', 4500000.00, 5000000.00, 100.00, 0.00, 'grid_paleto', -280.5000, 6210.8000, 30.0000)
ON DUPLICATE KEY UPDATE `label` = VALUES(`label`);

-- Seed default pump stations
INSERT INTO `nyx_water_pump_stations`
(`pump_id`, `reservoir_id`, `substation_id`, `pump_status`, `flow_rate_lps`, `power_draw_kw`)
VALUES
('pump_vinewood_01', 'res_vinewood_lake', 'grid_central_ls', 'ONLINE', 250.00, 1200.00),
('pump_zancudo_01', 'res_zancudo_aquifer', 'grid_zancudo', 'ONLINE', 180.00, 900.00),
('pump_paleto_01', 'res_paleto_basin', 'grid_paleto', 'ONLINE', 120.00, 600.00)
ON DUPLICATE KEY UPDATE `reservoir_id` = VALUES(`reservoir_id`);

-- Seed default fire hydrants
INSERT INTO `nyx_fire_hydrants`
(`hydrant_id`, `reservoir_id`, `coords_x`, `coords_y`, `coords_z`, `pressure_psi`, `is_active`)
VALUES
('hydrant_vinewood_01', 'res_vinewood_lake', 220.1000, 700.5000, 180.2000, 85.00, 1),
('hydrant_legion_01', 'res_vinewood_lake', 150.0000, -900.0000, 30.0000, 90.00, 1),
('hydrant_zancudo_01', 'res_zancudo_aquifer', -2200.0000, 3200.0000, 15.0000, 80.00, 1),
('hydrant_paleto_01', 'res_paleto_basin', -270.0000, 6200.0000, 30.0000, 75.00, 1)
ON DUPLICATE KEY UPDATE `reservoir_id` = VALUES(`reservoir_id`);

SET FOREIGN_KEY_CHECKS = 1;
125 changes: 125 additions & 0 deletions FiveM/nyx_services/client/water_utility.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
--[[
Nyxframework: nyx_services
File: client/water_utility.lua
Description: Client-side spatial interactions, target hooks via nyx_interact,
and UI telemetry bridge via nyx_ui for fire hydrants and water reservoirs.
]]

Nyx = Nyx or {}

-- Initialize spatial interaction points for fire hydrants and reservoirs
local function SetupWaterUtilityTargets()
if not exports['nyx_interact'] then return end

local hydrants = GlobalState.fireHydrants or {}
for hydrantId, h in pairs(hydrants) do
if h.coords then
exports['nyx_interact']:AddTargetCoords("fire_hydrant_" .. hydrantId, vec3(h.coords.x, h.coords.y, h.coords.z), 2.5, {
options = {
{
icon = "fa-solid fa-fire-hydrant",
label = string.format("Inspect Hydrant Pressure (ID: %s)", hydrantId),
action = function()
TriggerEvent("nyx_services:client:inspectHydrant", hydrantId)
end
},
{
icon = "fa-solid fa-hose",
label = "Connect Tender Hose / Draft Water",
action = function()
TriggerEvent("nyx_services:client:draftHydrantMenu", hydrantId)
end
}
],
distance = 2.5
})
end
end

local reservoirs = GlobalState.waterReservoirs or {}
for resId, r in pairs(reservoirs) do
if r.coords then
exports['nyx_interact']:AddTargetCoords("reservoir_valve_" .. resId, vec3(r.coords.x, r.coords.y, r.coords.z), 3.0, {
options = {
{
icon = "fa-solid fa-faucet-drip",
label = string.format("Access Intake Valve (%s)", r.label or resId),
action = function()
TriggerEvent("nyx_services:client:openReservoirMenu", resId)
end
}
],
distance = 3.0
})
end
end
end

-- Event handler for inspecting hydrant status
RegisterNetEvent("nyx_services:client:inspectHydrant", function(hydrantId)
local hydrants = GlobalState.fireHydrants or {}
local h = hydrants[hydrantId]
if not h then
if exports['nyx_notify'] then
exports['nyx_notify']:SendNotification("Hydrant telemetry unavailable.", "error")
end
return
end

local statusText = string.format("Hydrant %s | Pressure: %.1f PSI | Active: %s", hydrantId, h.pressurePsi or 0.0, h.isActive and "YES" or "NO")
if exports['nyx_notify'] then
exports['nyx_notify']:SendNotification(statusText, "info")
else
print("[Nyx:WaterUtility] " .. statusText)
end
end)

-- Event handler for opening reservoir intake menu
RegisterNetEvent("nyx_services:client:openReservoirMenu", function(reservoirId)
local reservoirs = GlobalState.waterReservoirs or {}
local r = reservoirs[reservoirId]
if not r then
if exports['nyx_notify'] then
exports['nyx_notify']:SendNotification("Reservoir telemetry unavailable.", "error")
end
return
end

local statusText = string.format("Reservoir: %s | Level: %.0f/%.0f L | Purity: %.1f%% | Contam: %.2f PPM",
r.label or reservoirId, r.waterLevelLiters or 0, r.maxCapacityLiters or 0, r.purityPercentage or 100, r.contaminationPpm or 0)
if exports['nyx_notify'] then
exports['nyx_notify']:SendNotification(statusText, "info")
else
print("[Nyx:WaterUtility] " .. statusText)
end
end)

-- Event handler for opening drafting menu
RegisterNetEvent("nyx_services:client:draftHydrantMenu", function(hydrantId)
if exports['nyx_core'] and exports['nyx_core'].TriggerRPC then
exports['nyx_core']:TriggerRPC("services:draftHydrant", function(success, res)
if success and res then
local msg = string.format("Drafted %sL. Reservoir Level: %sL | Pressure: %.1f PSI | Purity: %.1f%%",
res.litersDrawn, res.remainingLevel, res.pressurePsi, res.purity)
if exports['nyx_notify'] then
exports['nyx_notify']:SendNotification(msg, "success")
end
else
local err = string.format("Failed to draft from hydrant: %s", tostring(res))
if exports['nyx_notify'] then
exports['nyx_notify']:SendNotification(err, "error")
end
end
end, hydrantId, 500.0) -- Draft 500L standard batch
end
end)

CreateThread(function()
Wait(2000)
SetupWaterUtilityTargets()
end)

RegisterNetEvent("nyx:core:client:dbReady", function()
Wait(1000)
SetupWaterUtilityTargets()
end)
5 changes: 4 additions & 1 deletion FiveM/nyx_services/fxmanifest.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ shared_scripts {
'shared/sabotage.lua',
'shared/metering.lua',
'shared/hospital_beds.lua',
'shared/chemical_labs.lua'
'shared/chemical_labs.lua',
'shared/water_utility.lua'
}

server_scripts {
Expand All @@ -36,6 +37,7 @@ server_scripts {
'server/chemlabs.lua',
'server/agriculture.lua',
'server/repair_stations.lua',
'server/water_utility.lua',
'server/main.lua',
'server/exports.lua'
}
Expand All @@ -50,6 +52,7 @@ client_scripts {
'client/beds.lua',
'client/chemlabs.lua',
'client/agriculture.lua',
'client/water_utility.lua',
'client/main.lua',
'client/exports.lua'
}
Expand Down
20 changes: 20 additions & 0 deletions FiveM/nyx_services/server/exports.lua
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,23 @@ exports("SterilizeChemicalLab", function(src, labId)
return Nyx.Services.StartLabSterilization(src, labId)
end)

-- Municipal Water Utility & Hydrant Pressure
exports("GetWaterReservoirs", function()
return Nyx.Services.WaterReservoirs or {}
end)

exports("GetFireHydrants", function()
return Nyx.Services.FireHydrants or {}
end)

exports("ContaminateWaterReservoir", function(reservoirId, contaminantType, grams, citizenId)
return Nyx.Services.ContaminateReservoir(reservoirId, contaminantType, grams, citizenId)
end)

exports("PurifyWaterReservoir", function(reservoirId, treatmentKg, chemicalEfficiency, citizenId)
return Nyx.Services.PurifyReservoir(reservoirId, treatmentKg, chemicalEfficiency, citizenId)
end)

exports("DraftFromFireHydrant", function(hydrantId, requestedLiters)
return Nyx.Services.DraftFromHydrant(hydrantId, requestedLiters)
end)
Loading
Loading