Configuration
Every option in STG Vanaheim Arms, from stores, economy and blips to restock runs, the shooting range, weapon licenses, ranks, the weapon catalog and license hooks.
Most settings live in stg-vanaheimarms/shared/config.lua. Notifications are in shared/functions.lua, the weapon catalog in shared/weapons.lua and the license hooks in server/editable/license.lua. These files stay editable in the escrow version. Restart the resource after a change.
Language
Config.Locale = 'en'Available: en, de, fr, es, pt, it, nl, pl, sv, no, fi, ar, he, jp, zh. To change a text, edit the matching file in stg-vanaheimarms/locales/. The license exam questions and the NPC speech lines are in the same files.
Debug
Config.Debug = falsePrints shelf, placement and spawn details to the F8 console. Leave it off on a live server.
Target system
Config.Target = falseProp
Type
Store doors always use the floating prompt with E, whatever this is set to.
Economy
Config.WithdrawTax = 0.05
Config.EmployeeCut = 0.05
Config.BlockDuplicateWeapon = true
Config.MaxPurchaseAmount = 100
Config.ShowIdleStaff = trueProp
Type
Customers pay from their bank account and the money goes into the store safe.
Blips
Config.UniformBlipName = true
Config.BlipName = '[Selling] Ammu-Nation'
Config.Blips = {
unowned = { sprite = 110, color = 1, scale = 0.8 },
owned = { sprite = 110, color = 2, scale = 0.8 },
}Prop
Type
Once an owner renames the store, the blip shows the new name.
Stores
The script ships with 9 stores. The key, for example legion, is how the store is saved in the database, so never rename it once the server is live.
| Key | Store | Sale price | Range |
|---|---|---|---|
legion | Legion Ammu-Nation | $250,000 | Yes |
littleseoul | Little Seoul Ammu-Nation | $200,000 | No |
popularst | Popular St Ammu-Nation | $200,000 | Yes |
sandyshores | Sandy Shores Ammu-Nation | $200,000 | No |
paleto | Paleto Bay Ammu-Nation | $200,000 | No |
powerst | Power St Ammu-Nation | $200,000 | No |
palomino | Palomino Fwy Ammu-Nation | $200,000 | No |
route68 | Route 68 Ammu-Nation | $200,000 | No |
vespucci | Vespucci Blvd Ammu-Nation | $200,000 | No |
Legion uses every option:
Config.Shops = {
['legion'] = {
label = 'Legion Ammu-Nation',
salePrice = 250000,
placeSpawn = vec3(20.1263, -1105.2902, 29.7921),
inspectSpot = vec4(19.892, -1105.210, 29.527, -319.59),
license = vec4(23.65, -1108.38, 29.8, 70.38),
ped = {
model = 's_m_m_ammucountry',
coords = vec4(10.23, -1110.9, 29.8, 296.79),
scenario = 'WORLD_HUMAN_STAND_IMPATIENT',
},
doors = {
{ model = -8873588, coords = vec3(18.57, -1115.50, 29.95), group = 'main' },
{ model = 97297972, coords = vec3(16.13, -1114.61, 29.95), group = 'main' },
{ model = 825720073, coords = vec3(14.42, -1106.95, 29.94) },
},
range = {
attendant = { model = 's_m_y_ammucity_01', coords = vec4(8.83, -1099.38, 29.80, 161.07) },
shootSpot = vec4(13.60, -1097.16, 29.83, 338.83),
targets = {
vec4(15.52, -1079.03, 29.80, 160.85),
vec4(17.03, -1079.61, 29.80, 159.44),
vec4(21.11, -1081.15, 29.80, 163.93),
vec4(22.56, -1081.95, 29.80, 161.37),
},
door = { model = 452874391, coords = vec3(6.82, -1098.21, 29.95) },
},
},
-- ...
}Prop
Type
The range block:
Prop
Type
A store with a license spot but no range still runs the shooting drill: the player is moved to the first store that has a range for the drill and brought back after it.
The Popular St range attendant and target positions are estimates. Check them in-game and adjust them if they do not line up.
Speech bubbles
Config.SpeechBubble = {
enabled = true,
viewDistance = 12.0,
triggerDistance = 14.0,
greetDistance = 8.0,
duration = 5000,
cooldown = 20000,
checkInterval = 5000,
}Lines float above the store clerk, the range attendant and the license examiner.
Prop
Type
Restock mission
Config.Mission = {
DailyLimit = 5,
Pickup = vec4(249.73, -3110.94, 5.79, 3.02),
Weapons = {
['weapon_pistol'] = 1500,
['weapon_carbinerifle'] = 16000,
['armour'] = 1200,
['ammo-9'] = 500,
-- ...
},
}Members with stock permission order from the management menu. The order is paid from the safe, then they collect the cargo at the supplier, load it into a vehicle and carry it to the store clerk. Delivered stock goes to the store warehouse, ready to be placed on a shelf.
Prop
Type
Shooting range
Config.Range = {
dailyWage = 100,
entryFee = 500,
chargeMode = 'ammo',
pricePerMag = 250,
pricePerMinute = 300,
timeLimit = 120,
}The range opens when the owner hires the attendant. Players shoot with the weapon in their hands and pick Endless, Time Attack or Versus 2P. Every store keeps its own leaderboard, and all fees go into the safe.
Prop
Type
Weapon licenses
Config.License = {
pedModel = 's_m_y_cop_01',
dailyWage = 100,
licenses = {
{
key = 'weapon',
label = 'Weapon License',
licenseType = 'weapon',
licenseItem = nil,
examFee = 2500,
passScore = 75,
drillWeapon = 'weapon_pistol',
drillTarget = 350,
drillTime = 60,
},
{
key = 'heavy_weapon',
label = 'Heavy Weapon License',
licenseType = 'heavy_weapon',
licenseItem = nil,
examFee = 5000,
passScore = 80,
drillWeapon = 'weapon_carbinerifle',
drillTarget = 450,
drillTime = 60,
},
},
}The license desk opens when the owner hires the examiner. Players take the written exam first, then the shooting drill. Passing both grants the license.
Prop
Type
Each license entry:
Prop
Type
Purchase licenses
Config.PurchaseLicense = {
['Handguns'] = 'weapon',
['Submachine Guns'] = 'weapon',
['Shotguns'] = 'heavy_weapon',
['Assault Rifles'] = 'heavy_weapon',
['Sniper Rifles'] = 'heavy_weapon',
['Machine Guns'] = 'heavy_weapon',
['Heavy Weapons'] = 'heavy_weapon',
['Throwables'] = 'heavy_weapon',
['Melee'] = false,
['Armor'] = false,
['Ammo'] = false,
}License a player needs to buy from each weapon category. The value must match a licenseType above. Set a category to false to sell it without a license.
Ranks and permissions
Config.DefaultRanks = {
{ name = 'Owner', level = 100, salary = 0, isDefault = true, owner = true,
permissions = { manage = true, hire = true, manageStock = true, manageMembers = true, withdraw = true, viewFinance = true } },
{ name = 'Manager', level = 50, salary = 1000, isDefault = true, owner = false,
permissions = { manage = true, hire = true, manageStock = true, manageMembers = true, withdraw = false, viewFinance = true } },
{ name = 'Employee', level = 10, salary = 500, isDefault = true, owner = false,
permissions = { manage = false, hire = false, manageStock = true, manageMembers = false, withdraw = false, viewFinance = false } },
}Ranks every store starts with. In the management menu, members with Manage Members can add custom ranks, rename ranks, change salaries and toggle permissions, so these only set the starting point. The starting ranks cannot be deleted, the owner rank always holds every permission and new members join on the lowest rank.
Prop
Type
Config.PermissionList = {
{ key = 'manage', label = 'Manage Business', desc = 'Rename the business and change its settings.' },
{ key = 'hire', label = 'Hire & Fire NPC Staff', desc = 'Open or close the range and license services.' },
{ key = 'manageStock', label = 'Manage Stock', desc = 'Place weapons on shelves, restock and use the warehouse.' },
{ key = 'manageMembers', label = 'Manage Members', desc = 'Invite, remove and change the rank of members.' },
{ key = 'withdraw', label = 'Withdraw Money', desc = 'Withdraw money from the business safe.' },
{ key = 'viewFinance', label = 'View Finances', desc = 'See sales, transactions and business statistics.' },
}Toggles shown in the rank editor. You can change label and desc, but keep the keys.
Config.PermissionMap = {
manageStock = 'manageStock',
deposit = 'manageStock',
hire = 'hire',
viewSales = 'viewFinance',
withdraw = 'withdraw',
rename = 'manage',
editShelves = 'manage',
lockDoors = 'manageStock',
manage = 'manage',
manageMembers = 'manageMembers',
viewFinance = 'viewFinance',
}Links each action in the store to the permission it needs. For example, deposit and lockDoors need Manage Stock by default. Change the value on the right to move an action to another permission, for example lockDoors = 'manage'.
Config.EmployeeGrades = { ... }
Config.Permissions = { ... }Older settings that the management menu no longer reads. Leave them as they are.
Business
Config.Business = {
SellbackRate = 0.6,
PayrollIntervalMinutes = 60,
}Prop
Type
Weapon catalog
Every item the store can sell is defined in shared/weapons.lua. Most GTA weapons, the vest and 12 ammo types are already there, so adding a weapon to Config.Mission.Weapons is usually enough.
Config.Weapons = {
['weapon_carbinerifle'] = { label = 'Carbine Rifle', category = 'Assault Rifles', prop = 'w_ar_carbinerifle', stats = { damage = 36, fireRate = 74, accuracy = 68, range = 80, clip = 30 } },
['armour'] = { label = 'Bulletproof Vest', category = 'Armor', prop = 'prop_armour_pickup', type = 'item' },
['ammo-9'] = { label = '9mm', category = 'Ammo', prop = 'v_ret_gc_ammo5', type = 'item', bundle = 50 },
-- ...
}Prop
Type
The key is the inventory item the customer receives. The store image is loaded from web/assets/img/weapons/<key>.png for weapons and web/assets/img/items/<key>.png for items.
Notifications
In shared/functions.lua:
Config.Functions.CustomNotify = false
Config.Functions.Notify = function(notifyType, message, title)
if GetResourceState('stg-hud') == 'started' then
exports['stg-hud']:Notify({ type = notifyType == 'inform' and 'info' or notifyType, title = title, message = message, duration = 5000 })
return
end
lib.notify({ type = notifyType, description = message })
end- CustomNotify:
falseuses the built-in notifications.truesends every notification throughConfig.Functions.Notify. - Notify: replace the body to use your own notification system. By default it uses
stg-hudwhen it is running, otherwiseox_lib.
License hooks
Config.Functions.OnLicenseReceived in shared/functions.lua runs on the player's screen right after they earn a license. By default it refreshes stg-walletv2 cards when that resource is running. Use it to refresh your own ID card or wallet resource.
Config.Functions.OnLicenseReceived = function(licenseType)
if GetResourceState('stg-walletv2') == 'started' then
TriggerEvent('stg-walletv2:cardsRefreshing')
return
end
endserver/editable/license.lua connects the store to your license system. Edit it if your server stores licenses somewhere else.
- GiveLicense(source, licenseType): runs when a player passes the exam and the drill. By default it uses
esx_licenseon ESX andmetadata.licenceson QBCore and QBOX. - HasLicense(source, licenseType): runs before a purchase and before an exam, and must return
trueorfalse. - CanStartExam(source, data): runs before the written exam and before the drill.
data.examTypeis'written'or'drill'. Returnfalseand an optional message to block it, for example to keep the drill for police only.
On ESX, the default HasLicense lets every purchase through when esx_license is not running.
Still having trouble?
Open a ticket on our Discord. Our team answers around 15 hours a day.