├── .gitignore
├── web
├── src
│ ├── vite-env.d.ts
│ ├── store
│ │ ├── locale.ts
│ │ ├── imagepath.ts
│ │ ├── items.ts
│ │ ├── index.ts
│ │ ├── contextMenu.ts
│ │ ├── tooltip.ts
│ │ └── inventory.ts
│ ├── typings
│ │ ├── index.ts
│ │ ├── item.ts
│ │ ├── dnd.ts
│ │ ├── inventory.ts
│ │ ├── state.ts
│ │ └── slot.ts
│ ├── components
│ │ ├── utils
│ │ │ ├── Divider.tsx
│ │ │ ├── KeyPress.tsx
│ │ │ ├── transitions
│ │ │ │ ├── Fade.tsx
│ │ │ │ └── SlideUp.tsx
│ │ │ ├── icons
│ │ │ │ └── ClockIcon.tsx
│ │ │ ├── Tooltip.tsx
│ │ │ ├── WeightBar.tsx
│ │ │ ├── DragPreview.tsx
│ │ │ └── ItemNotifications.tsx
│ │ └── inventory
│ │ │ ├── LeftInventory.tsx
│ │ │ ├── RightInventory.tsx
│ │ │ ├── index.tsx
│ │ │ ├── InventoryGrid.tsx
│ │ │ ├── InventoryControl.tsx
│ │ │ ├── InventoryHotbar.tsx
│ │ │ └── UsefulControls.tsx
│ ├── utils
│ │ ├── misc.ts
│ │ ├── setClipboard.ts
│ │ ├── debugData.ts
│ │ └── fetchNui.ts
│ ├── dnd
│ │ ├── onUse.ts
│ │ ├── onGive.ts
│ │ ├── onCraft.ts
│ │ ├── onBuy.ts
│ │ └── onDrop.ts
│ ├── reducers
│ │ ├── index.ts
│ │ ├── swapSlots.ts
│ │ ├── stackSlots.ts
│ │ ├── moveSlots.ts
│ │ ├── setupInventory.ts
│ │ └── refreshSlots.ts
│ ├── hooks
│ │ ├── useDebounce.ts
│ │ ├── useKeyPress.ts
│ │ ├── useQueue.ts
│ │ ├── useIntersection.ts
│ │ ├── useExitListener.ts
│ │ └── useNuiEvent.ts
│ ├── thunks
│ │ ├── craftItem.ts
│ │ ├── buyItem.ts
│ │ └── validateItems.ts
│ ├── main.tsx
│ └── App.tsx
├── images
│ ├── key.png
│ ├── ammo-9.png
│ ├── armour.png
│ ├── burger.png
│ ├── carkey.png
│ ├── donut.png
│ ├── fries.png
│ ├── meth.png
│ ├── money.png
│ ├── oldkey.png
│ ├── phone.png
│ ├── radio.png
│ ├── sprunk.png
│ ├── trash.png
│ ├── water.png
│ ├── weed.png
│ ├── ziptie.png
│ ├── ammo-22.png
│ ├── ammo-38.png
│ ├── ammo-44.png
│ ├── ammo-45.png
│ ├── ammo-50.png
│ ├── ammo-emp.png
│ ├── at_grip.png
│ ├── bandage.png
│ ├── card_id.png
│ ├── cocaine.png
│ ├── garbage.png
│ ├── lockpick.png
│ ├── medikit.png
│ ├── mustard.png
│ ├── panties.png
│ ├── paperbag.png
│ ├── WEAPON_BALL.png
│ ├── WEAPON_BAT.png
│ ├── WEAPON_GAS.PNG
│ ├── WEAPON_MG.png
│ ├── WEAPON_RPG.png
│ ├── WEAPON_SMG.png
│ ├── advancedkit.png
│ ├── ammo-flare.png
│ ├── ammo-laser.png
│ ├── ammo-musket.png
│ ├── ammo-rifle.png
│ ├── ammo-rifle2.png
│ ├── ammo-rocket.png
│ ├── ammo-sniper.png
│ ├── at_barrel.png
│ ├── at_scope_nv.png
│ ├── black_money.png
│ ├── card_bank.png
│ ├── cigarette.png
│ ├── parachute.png
│ ├── pizza_ham.png
│ ├── scrapmetal.png
│ ├── trash_bread.png
│ ├── trash_can.png
│ ├── trash_chips.png
│ ├── usb_black.png
│ ├── WEAPON_BOTTLE.png
│ ├── WEAPON_BREAD.PNG
│ ├── WEAPON_BZGAS.png
│ ├── WEAPON_DAGGER.PNG
│ ├── WEAPON_FLARE.png
│ ├── WEAPON_HAMMER.png
│ ├── WEAPON_KNIFE.png
│ ├── WEAPON_MUSKET.png
│ ├── WEAPON_PISTOL.png
│ ├── WEAPON_WRENCH.png
│ ├── ammo-beanbag.png
│ ├── ammo-firework.PNG
│ ├── ammo-grenade.png
│ ├── ammo-railgun.png
│ ├── ammo-shotgun.png
│ ├── at_clip_drum.png
│ ├── at_flashlight.png
│ ├── at_muzzle_fat.png
│ ├── at_scope_holo.png
│ ├── at_suppressor.png
│ ├── pizza_ham_box.png
│ ├── trash_burger.png
│ ├── WEAPON_APPISTOL.png
│ ├── WEAPON_BATTLEAXE.png
│ ├── WEAPON_BRIEFCASE.PNG
│ ├── WEAPON_CANDYCANE.png
│ ├── WEAPON_COMBATMG.png
│ ├── WEAPON_COMBATPDW.png
│ ├── WEAPON_CROWBAR.png
│ ├── WEAPON_DBSHOTGUN.png
│ ├── WEAPON_FIREWORK.png
│ ├── WEAPON_FLAREGUN.png
│ ├── WEAPON_GOLFCLUB.png
│ ├── WEAPON_GRENADE.png
│ ├── WEAPON_GUSENBERG.png
│ ├── WEAPON_HANDCUFFS.PNG
│ ├── WEAPON_HATCHET.png
│ ├── WEAPON_HAZARDCAN.png
│ ├── WEAPON_KNUCKLE.png
│ ├── WEAPON_MACHETE.png
│ ├── WEAPON_MICROSMG.png
│ ├── WEAPON_MINIGUN.png
│ ├── WEAPON_MINISMG.png
│ ├── WEAPON_MOLOTOV.png
│ ├── WEAPON_PETROLCAN.png
│ ├── WEAPON_PIPEBOMB.png
│ ├── WEAPON_PISTOL50.png
│ ├── WEAPON_PISTOLXM3.png
│ ├── WEAPON_POOLCUE.png
│ ├── WEAPON_PROXMINE.png
│ ├── WEAPON_RAILGUN.png
│ ├── WEAPON_RAYPISTOL.png
│ ├── WEAPON_REVOLVER.png
│ ├── WEAPON_SMG_MK2.png
│ ├── WEAPON_SNOWBALL.png
│ ├── WEAPON_SNSPISTOL.png
│ ├── WEAPON_STUNGUN.png
│ ├── WEAPON_TECPISTOL.png
│ ├── ammo-heavysniper.png
│ ├── at_clip_extended.png
│ ├── at_muzzle_bell.png
│ ├── at_muzzle_flat.png
│ ├── at_muzzle_heavy.png
│ ├── at_muzzle_split.png
│ ├── at_scope_large.png
│ ├── at_scope_medium.png
│ ├── at_scope_small.png
│ ├── at_scope_thermal.png
│ ├── burger_chicken.png
│ ├── pizza_ham_slice.png
│ ├── WEAPON_ACIDPACKAGE.PNG
│ ├── WEAPON_ASSAULTSMG.png
│ ├── WEAPON_AUTOSHOTGUN.png
│ ├── WEAPON_DIGISCANNER.png
│ ├── WEAPON_EMPLAUNCHER.png
│ ├── WEAPON_FLASHLIGHT.png
│ ├── WEAPON_GARBAGEBAG.PNG
│ ├── WEAPON_HEAVYPISTOL.png
│ ├── WEAPON_HEAVYRIFLE.png
│ ├── WEAPON_HEAVYSNIPER.png
│ ├── WEAPON_NIGHTSTICK.png
│ ├── WEAPON_PISTOL_MK2.png
│ ├── WEAPON_PUMPSHOTGUN.png
│ ├── WEAPON_RAILGUNXM3.png
│ ├── WEAPON_RAYCARBINE.png
│ ├── WEAPON_RAYMINIGUN.png
│ ├── WEAPON_SNIPERRIFLE.png
│ ├── WEAPON_STICKYBOMB.png
│ ├── WEAPON_SWITCHBLADE.png
│ ├── at_clip_extended2.png
│ ├── at_muzzle_slanted.png
│ ├── at_muzzle_squared.png
│ ├── at_muzzle_tactical.png
│ ├── at_scope_advanced.png
│ ├── cigarettes_redwood.png
│ ├── WEAPON_ADVANCEDRIFLE.png
│ ├── WEAPON_ASSAULTRIFLE.png
│ ├── WEAPON_ASSAULTSHOTGUN.png
│ ├── WEAPON_BRIEFCASE_02.PNG
│ ├── WEAPON_BULLPUPRIFLE.png
│ ├── WEAPON_BULLPUPSHOTGUN.png
│ ├── WEAPON_CARBINERIFLE.png
│ ├── WEAPON_CERAMICPISTOL.png
│ ├── WEAPON_COMBATMG_MK2.png
│ ├── WEAPON_COMBATPISTOL.png
│ ├── WEAPON_COMBATSHOTGUN.png
│ ├── WEAPON_COMPACTRIFLE.png
│ ├── WEAPON_DOUBLEACTION.png
│ ├── WEAPON_FERTILIZERCAN.png
│ ├── WEAPON_GADGETPISTOL.png
│ ├── WEAPON_HEAVYSHOTGUN.png
│ ├── WEAPON_HOMINGLAUNCHER.png
│ ├── WEAPON_MACHINEPISTOL.png
│ ├── WEAPON_MARKSMANPISTOL.png
│ ├── WEAPON_MARKSMANRIFLE.png
│ ├── WEAPON_METALDETECTOR.png
│ ├── WEAPON_MILITARYRIFLE.png
│ ├── WEAPON_NAVYREVOLVER.png
│ ├── WEAPON_PRECISIONRIFLE.png
│ ├── WEAPON_REVOLVER_MK2.png
│ ├── WEAPON_SAWNOFFSHOTGUN.png
│ ├── WEAPON_SMOKEGRENADE.png
│ ├── WEAPON_SNSPISTOL_MK2.png
│ ├── WEAPON_SPECIALCARBINE.png
│ ├── WEAPON_STONE_HATCHET.png
│ ├── WEAPON_VINTAGEPISTOL.png
│ ├── at_muzzle_precision'.png
│ ├── WEAPON_ASSAULTRIFLE_MK2.png
│ ├── WEAPON_BULLPUPRIFLE_MK2.png
│ ├── WEAPON_CARBINERIFLE_MK2.png
│ ├── WEAPON_COMPACTLAUNCHER.png
│ ├── WEAPON_FIREEXTINGUISHER.png
│ ├── WEAPON_GRENADELAUNCHER.png
│ ├── WEAPON_HEAVYSNIPER_MK2.png
│ ├── WEAPON_PUMPSHOTGUN_MK2.png
│ ├── WEAPON_MARKSMANRIFLE_MK2.png
│ ├── WEAPON_SPECIALCARBINE_MK2.png
│ ├── WEAPON_GRENADELAUNCHER_SMOKE.PNG
│ └── readme.md
├── tsconfig.node.json
├── .prettierrc
├── .gitignore
├── vite.config.ts
├── index.html
├── tsconfig.json
├── LICENSE
└── package.json
├── .github
├── FUNDING.yml
├── actions
│ └── bump-manifest-version.js
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
└── workflows
│ ├── codeql-analysis.yml
│ └── release.yml
├── .vscode
└── extensions.json
├── data
├── licenses.lua
├── animations.lua
├── evidence.lua
├── crafting.lua
├── stashes.lua
└── vehicles.lua
├── .editorconfig
├── modules
├── interface
│ └── client.lua
├── bridge
│ ├── qbx
│ │ └── client.lua
│ ├── ox
│ │ ├── client.lua
│ │ └── server.lua
│ ├── nd
│ │ └── client.lua
│ ├── esx
│ │ └── client.lua
│ ├── server.lua
│ └── client.lua
├── utils
│ └── server.lua
├── pefcl
│ └── server.lua
├── items
│ ├── containers.lua
│ └── shared.lua
├── crafting
│ └── client.lua
└── hooks
│ └── server.lua
├── fxmanifest.lua
├── CONTRIBUTING.md
├── locales
├── zh-cn.json
├── zh-tw.json
├── ko.json
├── ar.json
├── sv.json
├── hr.json
├── sl.json
├── ru.json
├── lt.json
├── pt-br.json
└── sr.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | modules/logs/**/
2 | .cfg
3 | node_modules
4 | .idea
--------------------------------------------------------------------------------
/web/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
(events: DebugEvent
[], timer = 1000): void => {
16 | if (import.meta.env.DEV && isEnvBrowser()) {
17 | for (const event of events) {
18 | setTimeout(() => {
19 | window.dispatchEvent(
20 | new MessageEvent('message', {
21 | data: {
22 | action: event.action,
23 | data: event.data,
24 | },
25 | })
26 | );
27 | }, timer);
28 | }
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/web/src/store/contextMenu.ts:
--------------------------------------------------------------------------------
1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit';
2 | import { SlotWithItem } from '../typings';
3 |
4 | interface ContextMenuState {
5 | coords: {
6 | x: number;
7 | y: number;
8 | } | null;
9 | item: SlotWithItem | null;
10 | }
11 |
12 | const initialState: ContextMenuState = {
13 | coords: null,
14 | item: null,
15 | };
16 |
17 | export const contextMenuSlice = createSlice({
18 | name: 'contextMenu',
19 | initialState,
20 | reducers: {
21 | openContextMenu(state, action: PayloadAction<{ item: SlotWithItem; coords: { x: number; y: number } }>) {
22 | state.coords = action.payload.coords;
23 | state.item = action.payload.item;
24 | },
25 | closeContextMenu(state) {
26 | state.coords = null;
27 | },
28 | },
29 | });
30 |
31 | export const { openContextMenu, closeContextMenu } = contextMenuSlice.actions;
32 |
33 | export default contextMenuSlice.reducer;
34 |
--------------------------------------------------------------------------------
/web/src/hooks/useKeyPress.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect, useState } from 'react';
2 |
3 | export const useKeyPress = (targetKey: KeyboardEvent['key']) => {
4 | const [keyPressed, setKeyPressed] = useState(false);
5 |
6 | const keyToggler = useCallback(
7 | (toggle: boolean) =>
8 | ({ key }: KeyboardEvent) => {
9 | if (key === targetKey) {
10 | setKeyPressed(toggle);
11 | }
12 | },
13 | [targetKey]
14 | );
15 |
16 | const downHandler = keyToggler(true);
17 | const upHandler = keyToggler(false);
18 |
19 | useEffect(() => {
20 | window.addEventListener('keydown', downHandler);
21 | window.addEventListener('keyup', upHandler);
22 |
23 | return () => {
24 | window.removeEventListener('keydown', downHandler);
25 | window.removeEventListener('keyup', upHandler);
26 | };
27 | }, [downHandler, upHandler]);
28 |
29 | return keyPressed;
30 | };
31 |
32 | export default useKeyPress;
33 |
--------------------------------------------------------------------------------
/web/src/hooks/useQueue.ts:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 |
3 | export interface QueueMethods {inventory.label}
34 | {weight / 1000}/{inventory.maxWeight / 1000}kg
35 |
44 | {item.weight > 0
45 | ? item.weight >= 1000
46 | ? `${(item.weight / 1000).toLocaleString('en-us', {
47 | minimumFractionDigits: 2,
48 | })}kg `
49 | : `${item.weight.toLocaleString('en-us', {
50 | minimumFractionDigits: 0,
51 | })}g `
52 | : ''}
53 | {item.count ? item.count.toLocaleString('en-us') + `x` : ''} {Locale.ui_usefulcontrols || 'Useful controls'}
49 | RMB
50 |
54 | ALT + LMB
55 |
59 | CTRL + LMB
60 |
64 | SHIFT + Drag
65 |
69 | CTRL + SHIFT + LMB
70 | {props.item.text}
51 | {Locale.ui_rmb}
52 |
56 | {Locale.ui_alt_lmb}
57 |
61 | {Locale.ui_ctrl_lmb}
62 |
66 | {Locale.ui_shift_drag}
67 |
71 | {Locale.ui_ctrl_shift_lmb}
72 |