Skip to content

Model 2DFX effects

Every lamp post, neon sign and steam vent in San Andreas carries 2DFX: small effect records baked into the model by RenderWare. They are what makes a streetlight glow and a manhole steam. MTA never exposed them.

Neon does, with 13 client functions that read, edit, add and remove those records.

The clip runs test-resources/2dfx-showcase. There is no custom DFF, TXD, shader or texture in it. The lamp posts are stock model 1226 and every texture name comes from GTA itself; the whole show is native 2DFX types driven from Lua.

This API is model-level, not object-level. It matches GTA’s RenderWare 2DFX plugin, where the effect belongs to the model rather than to a placed instance.

Change a light on model 1226 and every lamp post using model 1226 changes. That is not a limitation to work around, it is the lever: the showcase splits its lamp posts across twelve runtime models so one Lua call drives five real lamp posts at once, and the two sides can run opposite colour waves.

If you need per-object effects, give the objects their own runtime models first.

TypeWhat it isProperties
lightA corona, point light and shadow. The workhorse.15, listed below
particleA named native particle emittername only
roadsignUp to four lines of generated text on a boardsize, rotation, flags, color, text1 to text4
escalatorA moving escalator surfacebottom, top, end, direction
sun_glareA sun glare spritenone, position only

All fifteen are required when adding a light:

PropertyTypeNotes
drawDistancefloatMust be 0 or greater
lightRangefloatPoint light radius, 0 or greater
coronaSizefloat0 or greater
shadowSizefloat0 or greater
shadowMultiplierint0 to 255
showModestringBlink behavior, see below
coronaReflectionboolWet-road reflection
flareTypeint0 or 1
flagstableEffect flags
shadowDistanceint-128 to 127
offsetVector3Each axis -128 to 127
colorcolor
coronaNamestringTexture name, cannot be empty
shadowNamestringTexture name, cannot be empty

showMode accepts default, on_off_at_5, warnlight, trafficlight, traincrosslight, random_flashing, random_at_wet_weather, at_rain_only and lines. These are GTA’s own blink modes, so a light can behave like a real traffic light without a Lua timer.

addModel2DFX(1337, 0, 0, 1.2, "light", {
drawDistance = 100, lightRange = 12, coronaSize = 1.5, shadowSize = 4,
shadowMultiplier = 200, showMode = "trafficlight", coronaReflection = true,
flareType = 0, flags = {}, shadowDistance = 0, offset = { 0, 0, 0 },
color = tocolor(255, 60, 60), coronaName = "coronastar", shadowName = "shad_exp",
})
FunctionPurpose
addModel2DFXAdd a resource-owned effect to a model
removeModel2DFXRemove an effect, including an original GTA one
restoreModel2DFXPut a removed original effect back
resetModel2DFXEffectsRoll a model back to its original state
getModel2DFXCountCount effects, with or without custom ones
getModel2DFXTypeRead one effect’s type
getModel2DFXEffectsDump every effect on a model
getModel2DFXPosition / setModel2DFXPosition / resetModel2DFXPositionRead, move and restore an effect’s position
getModel2DFXProperty / setModel2DFXProperty / resetModel2DFXPropertyRead, change and restore one property

getModel2DFXCount(model, false) counts only the model’s original effects, which is how a resource finds a native effect index to edit without tripping over its own additions.

Custom effects belong to the resource that created them. Overrides and removals of original GTA effects are stacked per resource, so two resources touching the same model do not silently clobber each other, and stopping one rolls back only its own layer.

Stopping a resource removes its custom effects and undoes its overrides. The regression harness proves this the honest way: it deliberately leaves effects alive on exit, and the next run starts by asserting the previous run’s effects are gone.

Adding or removing an effect changes the model, so already-streamed instances need to be rebuilt before the change is visible. A targeted model restream is cheap. engineRestreamWorld() is global and causes a noticeable streaming hitch.

The showcase never calls it during the recorded timeline, and the regression harness deliberately excludes it from its automatic run and puts it behind an explicit command instead. Treat it as a setup-time tool, not something to call while players are moving.

Issue additions in sequence rather than while a model is still being rebuilt.

  • Client-side only. No server element, no synchronization.
  • Model IDs are 0 to 65535.
  • Particle name holds 1 to 23 characters. Native storage is char[24], so 24 characters would leave no room for the terminator.
  • Roadsign lines are at most 16 characters each, across text1 to text4.
  • Light offset and shadowDistance are signed bytes, so -128 to 127.
  • Effects are model-level, so there is no way to change one placed object without giving it its own model.

test-resources/2dfx-test prints PASS / FAIL / SKIP in chat and to outputDebugString. It covers resource-stop cleanup from a previous run, rejecting a missing flags field without crashing, rejecting 24-character particle names, adding light, particle and roadsign effects, custom property set/get/reset, getModel2DFXCount(model, false) excluding custom effects, modifying and resetting a native light on model 1226, and removing then restoring a native effect while waiting for the targeted restream to settle.

/2dfxrestream and /2dfxstress [count] cover the global restream path explicitly, up to 50 cycles, verifying the custom count and type stay stable.

test-resources/2dfx-showcase is the cinematic: about 60 lamp posts, 12 independently controlled model groups, native blink modes, live colour and corona mutations, particle gates, generated roadsign text and a sun-glare cluster. Neither resource is auto-deployed; copy them into the server resources directory first.

Implementation: 27e3972e7.