Immersion

Graffiti Tags

Persistent wall tags players write themselves, flush against any wall, feeding a gang territory system. Nothing to stream.

Version
1.0.0
Resource
SF-Graffiti
Version 1.0.0 Resource SF-Graffiti Frameworks ESX ยท QBCore ยท QBX ยท Standalone Bundled languages 5 Store page View

Overview#

The player writes it

Their own text, or one of eighteen motifs. Nine fonts, seven effects, a colour, an outline, a size โ€” with a live preview.

Flat on any wall

Aim, and the tag appears in transparency where you point. Wheel rotates, arrows resize, E sprays.

Nothing to stream

No .ytd, no .ydr. Tags are rendered in HTML and projected into the world โ€” which is what makes them endlessly customisable.

Persistent

Everything is kept in the database and comes back after a restart.

Covering a rival

Tagging close to an existing tag replaces it, for a configurable number of charges. Covering your own gang is refused.

Cleaning

With a remover in hand, E scrubs a tag off. Optionally paid, optionally reserved to certain jobs โ€” a city-cleaning job in two settings.

Gang territories

Each zone counts tags by gang. The gang holding the most controls it, with coloured blips on the map.

Exports and an event

Read zone ownership and statistics, and react to a capture from your own scripts.

Dependencies#

ResourceRequiredWhat for
ox_libRequiredInterface and shared helpers
oxmysqlRequiredTag storage โ€” the table is created on its own

Installation#

  1. Drop SF-Graffiti into your resources folder.
  2. Declare the two items โ€” see ITEMS.md.
  3. Add the line below to your server.cfg, after ox_lib, oxmysql and your framework.
  4. Adjust Config.Zones to your own map, then set Config.Debug = false.
server.cfg
ensure ox_lib
ensure oxmysql
ensure SF-Graffiti

Configuration#

SettingDefaultWhat it does
Config.Fonts / Effects / Motifs / Colors9 / 7 / 18The creative catalogue offered in the editor
Config.Sizeโ€”Minimum and maximum size, and the height/width ratio
Config.MinSpacingโ€”How close you must be to cover an existing tag
Config.CoverCost2Charges spent to cover a rival tag
Config.CleanRewardโ€”Optional pay for scrubbing a tag off
Config.CleanJobsโ€”Which jobs may be paid for cleaning
Config.MaxRendered15Cannot be raised โ€” it is the capacity of the atlas
Config.MaxTotal500Global ceiling on stored tags
Config.Zonesโ€”Territories, with Config.MinTagsToOwn and Config.GangBlipColors

Commands#

CommandWhat it does
/tagPlace a tag โ€” requires a spray can
/territoiresGang influence, zone by zone
/cleartags [all](admin) Purge 20 m around you, or the whole map
/sf_graffiti_debug(console) Bridges, loaded tags, zone owners
/graffiti_admin(Config.Debug) Why this player is admin, or is not

Exports#

server
exports['SF-Graffiti']:GetZoneOwner('grove')   --> 'ballas' | nil
exports['SF-Graffiti']:GetZoneStats('grove')   --> { owner=, total=, gangs={...} }
exports['SF-Graffiti']:GetAllZones()

AddEventHandler('sf-graffiti:server:zoneCaptured', function(zoneId, newGang, oldGang)
    -- territory income, gang alert, Discord notification...
end)

Good to know#

Config.MaxRendered is the number of tags drawn at the same time, not the number stored. Beyond it, the furthest give way to the nearest. It cannot go above 15 โ€” that is the capacity of the atlas, not a cautious default.
Config.MaxTotal exists because a map covered edge to edge eventually weighs on the database and on syncing new players.
The server trusts nothing: wall position and normal re-validated, size and rotation clamped, colour checked against the palette, text sanitised and truncated, and the item consumed before the write. A modified client cannot tag at range, tag for free, or inject content into the interface.