Developer

4 min readUpdated 2 weeks ago

Overview

ml_climaticvitals has client exports for reading temperature data and toggling the system, and an open/ folder for your own code. OpenServer.OnItemUsed in it is not called by the script. Vehicle climate state syncs via entity StateBags. Server-side: temperature calculation, weather sync, DB persistence, item confirmation (zero-trust, server computes all damage/stat values, never trusts client).

Client Exports

Temperature Data

  • GetPerceivedTemperature(): Returns the current perceived temperature (number, Celsius). Includes all modifiers
  • GetCurrentTemperature(): Returns the current ambient/server temperature (number, Celsius). Raw weather-based value
  • GetState(): Returns a table with the full system state:
lua
1{
2    perceived = 22.0,      -- Perceived temperature (Celsius)
3    current = 20.0,        -- Ambient temperature (Celsius)
4    weather = 'CLEAR',     -- Current weather type string
5    nearHeat = false,      -- Near a heat/cold source object
6    inVehicle = false,     -- In a vehicle with insulation
7    inInterior = false,    -- Inside an MLO/interior
8    wetness = 0.0,         -- Wetness level (0.0 to 1.0)
9    itemBuff = 0.0,        -- Active item buff modifier
10}

UI Control

  • hideweatherhud(hide): Hide or show the temperature HUD widget. true = hide, false = show. Blocked when Config.ForceShowWidget = true
  • HideUi(shouldHide): Alias for hideweatherhud
  • OpenClimateMenu(): Opens the vehicle climate control UI. Only works in a valid vehicle with the engine running

System Control

  • SetClimaticState(state): Enable or disable the entire system. true = active, false = paused. When paused: HUD hides, visual effects clear, damage stops, temperature updates stop

Buffs

  • ApplyBuff(buffAmount, durationMinutes): Apply a local temperature buff. buffAmount is degrees (positive = warm, negative = cool), durationMinutes is duration in minutes. Client-side only, does not sync to server

StateBags

Vehicle Climate State

Climate control state is stored on the vehicle entity:

lua
1Entity(vehicle).state.climateState = {
2    on = true,          -- Climate system on/off
3    temp = 21.0,        -- Target temperature
4    fan = 1,            -- Fan speed level (0-3)
5    mode = 'auto'       -- 'auto', 'heat', or 'ac'
6}

All vehicle occupants read this StateBag. Players in the vehicle send updates to the server with ml_climaticvitals:server:updateClimateState. The server validates: entity existence, player proximity (10 units), data types, and clamps temp/fan/mode to valid ranges before writing the StateBag.

Server Events

  • ml_climaticvitals:getInitialTemperature: Client requests current temperature on join
  • ml_climaticvitals:server:setWeather: Weather reporter client sends the current weather name. Accepted only from the assigned reporter
  • ml_climaticvitals:server:updateClimateState: Client sends vehicle climate changes. Server validates before writing StateBag
  • ml_climaticvitals:server:ApplyStatusDamage: Client sends condition string ('hot'/'cold'). Server computes actual hunger/thirst values from config and applies them via Bridge.SetHunger/Bridge.SetThirst. Rate-limited to 10 seconds per player
  • ml_climaticvitals:server:confirmItemUse: Client confirms item consumption after progress bar completes. Server verifies inventory, removes 1 item, applies buff via client event. Protected with GetInvokingResource() check

Client Events

  • ml_climaticvitals:updateTemperature: Server broadcasts ambient temperature and weather type
  • ml_climaticvitals:client:setAsWeatherReporter: Server assigns a client as weather reporter
  • ml_climaticvitals:client:useItem: Server triggers item consumption flow. Client shows progress bar, then confirms back to server
  • ml_climaticvitals:client:applyBuff: Server applies a buff after item confirmation. Receives buffAmount and durationMs
  • ml_climaticvitals:client:notifyItemBuff: Server sends item notification after confirmation
  • ml_climaticvitals:client:playerReady: Internal event fired when the player is fully loaded

The net event handlers refuse calls from other server resources.

Temperature Calculation

The perceived temperature formula:

lua
1basePerceived = ambient
2    + zoneModifier
3    + clothesThermalBonus
4    + temperatureObjectBonus
5    + vehicleClimateModifier
6    + vehicleInsulationBonus
7    + nightTimeMalus (-4 between 21:00-06:00)
8    + wetnessMalus (wetness * Config.Wetness.TemperatureMalus)
9    + waterMalus (Config.PlayerOnWater.TempMalus when swimming)
10    + itemBuff
11
12interiorMod = (Config.InteriorBonus.targetTemp - basePerceived) * Config.InteriorBonus.strength
13
14perceived = basePerceived + interiorMod

The interior mod pulls perceived temp towards targetTemp: warms you if it's cold, cools you if it's hot.

Weather Reporter System

One client is designated as the weather reporter and polls GetNextWeatherTypeHashName() every 5 seconds. When the reporter disconnects, the server reassigns after 1 second. Only the assigned reporter's weather updates are accepted.

Item Use Flow

  1. Server registers all Config.ItemBuffs items as usable via Bridge.BindUsableItem
  2. Player uses item, then server checks Bridge.HasItem, then triggers ml_climaticvitals:client:useItem
  3. Client shows lib.progressBar with animation/prop
  4. On progress bar completion, then client triggers ml_climaticvitals:server:confirmItemUse
  5. Server re-checks Bridge.HasItem, removes 1 item, triggers buff on client

Item is removed server-side only after client confirms progress bar completion. Cancelling the progress bar does not remove the item.

Examples

lua
1-- Client-side
2local state = exports.ml_climaticvitals:GetState()
3print(('Perceived: %.1f, Weather: %s, Wetness: %.0f%%'):format(
4    state.perceived, state.weather, state.wetness * 100
5))
lua
1-- Client-side
2exports.ml_climaticvitals:hideweatherhud(true)  -- hide
3exports.ml_climaticvitals:hideweatherhud(false) -- show
lua
1-- Client-side
2exports.ml_climaticvitals:SetClimaticState(false) -- pause
3-- ...minigame logic...
4exports.ml_climaticvitals:SetClimaticState(true)  -- resume
lua
1-- Client-side: cool the local player by 10 degrees for 5 minutes
2exports.ml_climaticvitals:ApplyBuff(-10.0, 5)