esx_christmas
esx_christmas is the official ESX Legacy Christmas present hunt with timed spawns, tiered cash and item rewards, claim limits and built-in anti-abuse checks.
esx_christmas is a seasonal ESX Legacy resource that spawns a landmark and a few presents at a configured location on a timer. Players walk up to a present, press E and receive cash and items depending on how early they opened it. It lives in the separate ESX-Legacy-Seasonal repository, not in [esx_addons].
Overview
| Resource | esx_christmas (version 1.0.0) |
| Repository | esx-framework/ESX-Legacy-Seasonal, folder [esx_seasonal] |
| Side | Server state, client props and prompts |
| Dependencies | es_extended in fxmanifest.lua; the shared scripts also load @ox_lib/init.lua; the prompt needs esx_textui |
| Database | None; event state lives in memory and resets on restart |
| Locales | en only (locales/en.lua) |
The manifest only lists es_extended under dependencies, but it loads @ox_lib/init.lua as a shared script. Start ox_lib before this resource, see ox_lib.
Installation
Copy esx_christmas into your resources folder and start it after its dependencies:
ensure ox_lib
ensure es_extended
ensure esx_textui
ensure esx_christmasThe resource streams its own props (stream/*, with this_is_a_map 'yes' and a .ytyp file), so no extra map resource is needed.
How it works
- A server thread calls
StartCycleimmediately on start and then everyConfig.EventIntervalseconds. - A cycle first clears the previous locations (if
DespawnLocationOnCycleEndis on), then picksConfig.MaxLocationsPerCycleentries fromConfig.Locations, randomly whenUseRandomLocationis on, and broadcasts them to all clients. - Each client spawns the landmark, an optional hint prop and one present prop per
PresentCoordsentry as local, non-networked objects. A present shows the text UI "Press E to open the present" within 2.0 units. - Pressing E sends
esx_christmas:server:claim. The server validates the claim, picks the reward tier from the claim order at that location and pays the player. - All clients get the claim notification, the present disappears and, when every present at a location is taken, the whole location is removed.
Configuration
All keys are in config/main.lua.
| Key | Default | Description |
|---|---|---|
Config.Enabled | true | Master switch; when false, cycles do nothing |
Config.Debug | true | Prints [ESX_Christmas] debug lines on server and client |
Config.EventInterval | 600 | Seconds between cycles; 0 or lower disables the cycle thread |
Config.MaxLocationsPerCycle | 1 | Locations activated per cycle |
Config.UseRandomLocation | true | Pick locations randomly instead of in list order |
Config.GlobalMaxClaims | 20 | Claim cap across all locations until counters reset; nil or 0 disables it |
Config.PerPlayerMaxClaimsPerCycle | 3 | Claim cap per player (by ESX identifier); nil or 0 disables it |
Config.UseTierScaling | true | Use Config.Tiers by claim order; when false everyone gets the default tier |
Config.DefaultTierName | "Common" | Tier used when no tier matches the claim order |
Config.DespawnPresentOnClaim | true | Remove a present for all players when it is opened |
Config.DespawnLocationWhenAllClaimed | true | Remove the location once all its presents are taken |
Config.DespawnLocationOnCycleEnd | true | Clear locations and reset claim counters when a new cycle starts |
Config.UseHints | true | Spawn a hint prop next to the landmark |
Config.Account | "bank" | Account for cash rewards; nil pays cash in hand via addMoney |
Config.MinClaimInterval | 2 | Minimum seconds between claims (and sync requests) per player; 0 disables the check |
Config.MaxViolations | 5 | Violations before the kick |
Config.KickOnMaxViolations | true | Drop the player at MaxViolations |
Config.MaxClaimDistance | 5.0 | Max distance in units between player and present on the server; 0 disables the check |
Config.Locale | GetConvar("esx:locale", "en") | Locale for notifications |
Locations
Config.Locations = {
{
Name = "Legion Square",
LandmarkCoords = vec3(203.525284, -941.169250, 29.55),
LandmarkModel = "sp_medium_christmas_santa",
PresentCoords = {
vec3(199.437363, -935.485718, 29.678345),
vec3(199.318680, -953.432983, 29.088623),
vec3(175.002197, -950.268127, 29.088623),
},
HintModel = "sp_christmas_snowman"
}
}| Field | Type | Description |
|---|---|---|
Name | string | Label used in debug output |
LandmarkCoords | vector3 | Where the landmark prop spawns |
LandmarkModel | string? | Fixed landmark model; when empty a random one from Config.Props.LandmarkModels is used |
PresentCoords | vector3[] | One present per entry; this count is the number of claims at the location |
HintModel | string? | Hint prop, spawned 2 units to the side of the landmark when UseHints is on |
A hint model only shows its floating text when it has an entry in Config.Hints, which maps the model name to a locale key (xmas_hint_snowman, xmas_hint_deer ship by default).
Props
Config.Props lists the model names the resource may use: LandmarkModels, PresentModels (picked randomly per present), HintModels and ExtraDecorModels. The shipped names (sp_*, prop_xmas_*) come from the files in stream/. In the current code only LandmarkModels (server, random landmark) and PresentModels (client, random present) are read. HintModels and ExtraDecorModels are not used anywhere, so a hint prop comes from HintModel in the location. Custom models you add must be streamed by this or another resource.
Tiers and rewards
Config.Tiers = {
{
Name = "Legendary",
ClaimOrder = { 1 },
Rewards = {
{ Type = "cash", Amount = 10000 },
{ Type = "item", Name = "armor", Amount = 1 },
{ Type = "item", Name = "christmas_ticket", Amount = 1 }
}
},
-- Epic: ClaimOrder = { 2 }, Common: ClaimOrder = { 3 }
}The first present opened at a location pays the Legendary tier, the second Epic, the third Common. ClaimOrder lists the claim numbers a tier applies to, so { 3, 4, 5 } stretches a tier. Rewards use xPlayer.addAccountMoney(Config.Account, amount) for cash and xPlayer.addInventoryItem(name, amount) for item, see xPlayer.
Events
Client to server (both are validated on the server):
| Event | Arguments | Purpose |
|---|---|---|
esx_christmas:server:claim | locationId, presentIndex | Open a present |
esx_christmas:server:requestSync | none | Ask for the active locations; sent once after the player loads |
Server to client: setLocations, removeLocation, clearLocations, despawnPresent, onClaim, presentAlreadyClaimed, cannotClaim and notify, all prefixed with esx_christmas:client:. They only drive props and notifications.
Common issues
- Rewards do nothing or throw errors. The default item names are not defined in the official
legacy.sqlor addons. Add rows to theitemstable or changeConfig.Tiers, see Add items. - Debug spam in the console.
Config.Debugistrueby default. Set it tofalsefor production. - Claim limits never reset. The global and per-player counters are only reset when a cycle clears the locations. With
DespawnLocationOnCycleEnd = falsethey keep growing until a restart. - No "Press E" prompt. The prompt uses
ESX.TextUI, which only works whileesx_textuiis running. Without it the present can still be opened with E, but nothing is shown. - Only English text. Only
locales/en.luaships. Add a file inlocales/for other languages.
Related
Frequently asked questions
Independent documentation
Last updated on
esx_weather
esx_weather syncs weather and time per zone (Los Santos, Sandy Shores, Paleto Bay) on ESX Legacy, with an admin panel, persistence and sync toggle exports.
Create a Job
How to create a job in ESX Legacy step by step, with jobs and job_grades SQL, ESX.CreateJob at runtime, /refreshjobs, /setjob and an esx_society boss menu.