Skip to content

feat: phone battery system - #151

Open
Github-Samuel wants to merge 11 commits into
mainfrom
feat/battery-system
Open

feat: phone battery system#151
Github-Samuel wants to merge 11 commits into
mainfrom
feat/battery-system

Conversation

@Github-Samuel

Copy link
Copy Markdown
Member

Replaces the cosmetic battery with a real, persistent, configurable one. Ships disabled (configs/battery.lua Enabled = false), so nothing changes for existing installs until they opt in.

What was already there

This finishes an existing seam rather than adding a feature from scratch:

  • client/main.lua:667 drained 1% per 30s while open, never persisted, and never recharged (it hit 0 and stayed there)
  • BatteryPage.tsx had a Low Power Mode toggle wired to nothing, plus invented "Battery health / Maximum Capacity 100%" rows
  • The Settings footer claimed the phone "recharges automatically over time", which was false
  • 5 lb-phone client exports were stubs and 3 server exports were silent no-ops

Design

The charge follows DataOwner. session.identity(source) or player.getIdentifier(source) resolves to the device/SIM profile under unique phones and the citizenid otherwise, so it is correct in all four ownership configurations with no second concept.

Client simulates, server checkpoints. No server threads and no per-percent writes. The client owns the tick (it is the only side that knows if the phone is open); the server stores level/charging/updated_at and derives on demand for exports and item handlers. Checkpoints happen on transitions plus a slow safety flush, roughly one write per player per few minutes rather than ~9 writes/second at 500 players.

A live client is authoritative; derive() is the fallback for owners with no live client. Without that rule a power bank used on a player who never opened their phone would write drift back into their row.

lb-phone compatibility

All 8 exports implemented plus the lb-phone:phoneDied event. Three deliberate divergences, each fixing a defect in lb's own bundle:

lb-phone here
Low Power Mode doubles the charge interval too (~23m instead of ~12m) multiplier applies to drain only
Warnings fire on battery === 20, so SetBattery(25 → 15) silently skips them fires on downward crossing
One row write per percent checkpoints on transitions

Charging

lb ships no chargers at all, so enabling battery there gives players a phone that dies and never recovers. Three affordances ship here, each independently switchable: a power bank item, a cable item plus ox_target props and radius zones, and vehicle charging. All are thin callers of chargePhone / toggleCharging, which are also exported for server owners.

Verification

  • lua tests/lua/run.lua: 301 passed, 0 failed (27 new assertions covering derive projection, clock skew, DrainWhileOffline/DrainWhenClosed, ownerKey across all four modes, rate selection, and threshold crossing)
  • npm run build (tsc + vite), lint (0 errors), test (163 passed), knip (clean)
  • luac -p on all 16 touched Lua files

Test files are intentionally not committed per repo convention.

Notes

  • config.StatusBar.BatteryStart is still read as the fallback for Battery.StartLevel, mirroring how SimTray honours UseContainers.
  • AllowEmergencyCalls needed an EmergencyNumbers list because no emergency-number concept existed; it only bites under DeadBehaviour = 'noservice', since under 'dead' the phone will not open to reach a dialler. Documented in the config comment.
  • Not yet tested in-game. Wants a live pass across the three charging sources and both dead behaviours.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant