$ {{(cash)}}
195 |$ {{(bank)}}
200 |+ {{(amount)}}
204 |- {{(amount)}}
205 |├── .editorconfig ├── .github ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml ├── actions │ └── bump-manifest-version.js ├── contributing.md ├── pull_request_template.md └── workflows │ ├── discord-commit.yml │ ├── discord-release.yml │ ├── issues-project.yml │ ├── lint.yml │ ├── release-action.yml │ └── release.yml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── README.md ├── client └── main.lua ├── config ├── client.lua ├── server.lua └── shared.lua ├── fxmanifest.lua ├── html ├── app.js ├── brand-logo.svg ├── index.html ├── responsive.css └── styles.css ├── locales ├── ar.json ├── cs.json ├── da.json ├── de.json ├── en.json ├── es.json ├── et.json ├── fa.json ├── fi.json ├── fr.json ├── ge.json ├── it.json ├── nl.json ├── pl.json ├── pt-br.json ├── pt.json ├── sk.json ├── sv.json └── tr.json ├── server └── main.lua └── stream ├── circlemap.ytd ├── minimap.gfx ├── minimap.ytd └── squaremap.ytd /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = false 6 | indent_style = space 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | indent_size = 4 10 | 11 | [*.{sql,html,css,json,js,ts,jsx,tsx}] 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | education, socio-economic status, nationality, personal appearance, race, 10 | religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team in the discord at https://discord.gg/Z6Whda5hHA. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: Create a report to help us improve or fix something 3 | labels: ['bug', 'need repro'] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thank you for taking the time to fill out a bug report! 9 | Please use our Discord Server to ask questions and receive support: https://discord.gg/Z6Whda5hHA 10 | - type: input 11 | id: summary 12 | attributes: 13 | label: Summary 14 | description: Write a short and concise description of your bug. 15 | validations: 16 | required: true 17 | - type: textarea 18 | id: repro 19 | attributes: 20 | label: Reproduction 21 | description: What did you do to make this happen? 22 | placeholder: | 23 | 1. Using ... 24 | 2. Do ... 25 | 3. Then use ... 26 | 4. See error 27 | validations: 28 | required: true 29 | - type: textarea 30 | id: expected 31 | attributes: 32 | label: Expected behavior 33 | description: What did you expect to happen? 34 | validations: 35 | required: true 36 | - type: textarea 37 | id: actual 38 | attributes: 39 | label: Actual behavior 40 | description: What actually happened? 41 | validations: 42 | required: true 43 | - type: textarea 44 | id: additional 45 | attributes: 46 | label: Additional context 47 | description: If you have any other context about the problem such as screenshots or videos, add them here. 48 | - type: input 49 | id: updated 50 | attributes: 51 | label: Current Version 52 | description: What version of the resource are you currently using? 53 | placeholder: e.g. v1.3.0, v1.4.0 54 | validations: 55 | required: true 56 | - type: input 57 | id: custom 58 | attributes: 59 | label: Custom Resources 60 | description: Are you using custom resources? Which ones? 61 | placeholder: e.g. zdiscord, qb-target 62 | validations: 63 | required: true 64 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Qbox Discord Server 4 | url: https://discord.gg/Z6Whda5hHA 5 | about: Ask questions, receive support, and discuss with the community in our Discord server. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest an idea for Qbox 3 | labels: enhancement 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Please use our Discord Server to ask questions and receive support: https://discord.gg/Z6Whda5hHA 9 | - type: textarea 10 | id: problem 11 | attributes: 12 | label: The problem 13 | description: A clear and concise description of what the problem is, or what feature you want to be implemented. 14 | placeholder: | 15 | Some examples: 16 | I'm frustrated that ... 17 | It would be nice if ... 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: solution 22 | attributes: 23 | label: Ideal solution 24 | description: A clear and concise description of what you want to happen, with as much detail as possible. 25 | validations: 26 | required: true 27 | - type: textarea 28 | id: alternatives 29 | attributes: 30 | label: Alternative solutions 31 | description: A clear and concise description of any alternative solutions or features you've considered. 32 | - type: textarea 33 | id: additional 34 | attributes: 35 | label: Additional context 36 | description: If you have any other context about the problem such as screenshots or videos, add them here. 37 | -------------------------------------------------------------------------------- /.github/actions/bump-manifest-version.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | const version = process.env.TGT_RELEASE_VERSION 4 | const newVersion = version.replace('v', '') 5 | 6 | const manifestFile = fs.readFileSync('fxmanifest.lua', {encoding: 'utf8'}) 7 | 8 | const versionStr = `version '${newVersion}'` 9 | let newFileContent = manifestFile.replace(/\bversion\s+(.*)$/gm, versionStr) 10 | 11 | if (!newFileContent.includes(versionStr)) { 12 | newFileContent = manifestFile.replace(/\bgame\s+(.*)$/gm, `game 'gta5'\n${versionStr}`); 13 | } 14 | 15 | fs.writeFileSync('fxmanifest.lua', newFileContent) -------------------------------------------------------------------------------- /.github/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Qbox 2 | 3 | Thank you for taking the time to contribute! 4 | 5 | These guidelines will help you help us in the best way possible regardless of your skill level. We ask that you try to read everything related to the way you'd like to contribute and try and use your best judgement for anything not covered. 6 | 7 | Make sure to also read our [Contributor Code of Conduct](./CODE_OF_CONDUCT.md). 8 | 9 | If you still have further questions after reading be sure to join the [Qbox Discord server][discord link]. 10 | 11 | ## Issues 12 | 13 | Open a new issue to report a bug or request a new feature or improvement. 14 | 15 | If you want to ask a question, issues are not the place to do so. Please join our [Discord server][discord link] and ask over there instead. 16 | 17 | Before opening a new issue: 18 | 19 | - [Search](https://github.com/issues?q=is%3Aissue+org%3AQbox-Project) for existing issues; maybe someone else already experienced the same problem that you're having! Duplicate issues will be closed. 20 | - Download the latest release of the resource you are opening the issue for to make sure that it wasn't already fixed. Issues that are already fixed are considered invalid and will be closed. 21 | 22 | When opening a new issue, make sure to pick the right type of form and fill it out. The more information you provide, the easier it will be for us to work on your new issue. Issues that are invalid or do not follow the correct form may be ignored or even closed. 23 | 24 | ## Pull Requests 25 | 26 | Open a new pull request to contribute code. 27 | 28 | You can use your own editor of choice, but we recommend using [VSCode](https://code.visualstudio.com/) with these extensions: 29 | 30 | - [GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) 31 | - [Lua Language Server](https://marketplace.visualstudio.com/items?itemName=sumneko.lua) 32 | - [EditorConfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) 33 | - [CfxLua IntelliSense](https://marketplace.visualstudio.com/items?itemName=overextended.cfxlua-vscode) 34 | 35 | If you are new to contributing code, you can check out and try to fix issues marked with [`good first issue`](https://github.com/issues?q=is%3Aissue+is%3Aopen+org%3AQbox-Project+label%3A%22good+first+issue%22). 36 | 37 | If you want to contribute code but don't know what to do, please check out issues marked with [`help wanted`](https://github.com/issues?q=is%3Aissue+is%3Aopen+org%3AQbox-Project+label%3A%22help+wanted%22) as those are issues that we actually *need* help with. 38 | 39 | If you want to contribute code unrelated to an existing issue, you should open an issue yourself or ask over on the [Discord server][discord link] to discuss it with our team and ask whether your change is wanted, especially if you are planning on adding new features or large designs. 40 | 41 | Before opening a pull request: 42 | 43 | - Make sure that your contribution fits our [code conventions](#code-conventions) described below. After opening a pull request your code will be checked according to them. 44 | - Make sure that your pull request is small and focused. Break it into multiple smaller pull requests if not (see [Small Pull Request Manifesto](https://github.com/PlaytikaOSS/small-pull-request-manifesto)). 45 | - It is recommended to test your changes to make sure they work and don't break existing functionality. 46 | 47 | When opening a pull request, make sure to follow the template and explain your changes. If you are trying to contribute something related to a GitHub issue, make sure to mention it as well. 48 | 49 | ## Code Conventions 50 | 51 | Below are conventions that you must follow when contributing code. 52 | 53 | ### Commit Message Conventions 54 | 55 | - The first line of a commit message must be 72 characters at most. 56 | - Commit messages and pull request titles must follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/). 57 | - Use `fix:` when patching a bug. 58 | - Use `feat:` when introducing a new feature. 59 | - Use `refactor:` when altering code without changing functionality. 60 | - Use `chore:` when your changes don't alter production code. 61 | - Append a `!` after the type/scope of the feature when you change code and introduce a breaking API change. Optionally add a footer to the bottom of your commit message separated by 2 newlines, starting with `BREAKING CHANGE:`, and explaining the breaking change. 62 | 63 | ### Lua Conventions 64 | 65 | #### General Style 66 | 67 | - Use 4 space indentation. 68 | - Prefer creating local variables over global ones. 69 | - Don't repeat yourself. If you're using the same operations in multiple different places convert them into a flexible function. 70 | - Exported functions must be properly annotated (see [LuaLS Annotations](https://luals.github.io/wiki/annotations/)). 71 | - Utilize [ox_lib](https://overextended.dev/ox_lib) to make your life easier. Prefer lib calls over native ones. 72 | - Make use of config options where it makes sense to make features optional and/or customizable. Configs should not be modified by other code. 73 | 74 | #### Optimization & Security 75 | 76 | - Consider [this Lua Performance guide](https://springrts.com/wiki/Lua_Performance). 77 | - Don't create unnecessary threads. Always try to find a better method of triggering events. 78 | - Set longer `Wait` calls in distance checking loops when the player is out of range. 79 | - Don't waste cycles; job specific loops should only run for players with that job. 80 | - When possible don't trust the client, *especially* with transactions. 81 | - Balance security and optimizations. 82 | - Use `#(vector3 - vector3)` instead of `GetDistanceBetweenCoords()`. 83 | - Use `myTable[#myTable + 1] = 'value'` instead of `table.insert(myTable, 'value')`. 84 | - Use `myTable['key'] = 'value'` instead of `table.insert(myTable, 'key', 'value')`. 85 | 86 | #### Naming 87 | 88 | - Use `camelCase` for local variables and functions. 89 | - Use `PascalCase` for global variables and functions. 90 | - Avoid acronyms as they can be confusing and context dependant. 91 | - Be descriptive; make it easy for the reader. 92 | - Booleans may be prefixed with `is`, `has`, `are`, etc. 93 | - Arrays should have plural names. 94 | 95 | ### JavaScript/TypeScript Conventions 96 | 97 | Consider following the [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html) and the [Google TypeScript Style Guide](https://google.github.io/styleguide/tsguide.html). 98 | 99 | [discord link]: https://discord.gg/Z6Whda5hHA 100 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 4 | 5 | ## Checklist 6 | 7 | 8 | 9 | - [ ] I have personally loaded this code into an updated Qbox project and checked all of its functionality. 10 | - [ ] My pull request fits the contribution guidelines & code conventions. 11 | -------------------------------------------------------------------------------- /.github/workflows/discord-commit.yml: -------------------------------------------------------------------------------- 1 | name: "Discord Commit" 2 | 3 | on: [push] 4 | 5 | jobs: 6 | report-status: 7 | if: github.event.repository.default_branch == github.ref_name 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout repository 11 | uses: actions/checkout@v4 12 | - name: Discord Webhook 13 | uses: ChatDisabled/discord-commits@main 14 | with: 15 | id: ${{ secrets.WEBHOOK_ID }} 16 | token: ${{ secrets.WEBHOOK_TOKEN }} 17 | -------------------------------------------------------------------------------- /.github/workflows/discord-release.yml: -------------------------------------------------------------------------------- 1 | name: "Discord Releases" 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | github-releases-to-discord: 9 | name: Discord Releases Changelog 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout Repository 13 | uses: actions/checkout@v4 14 | - name: Github Releases To Discord 15 | uses: SethCohen/github-releases-to-discord@v1.16.2 16 | with: 17 | webhook_url: ${{ secrets.WEBHOOK_URL }} 18 | color: "15852866" 19 | username: ${{ github.event.repository.name }} 20 | avatar_url: "https://i.imgur.com/Eh1yiLI.png" 21 | footer_timestamp: true 22 | -------------------------------------------------------------------------------- /.github/workflows/issues-project.yml: -------------------------------------------------------------------------------- 1 | name: Issues Project Management 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | add-to-project: 10 | name: Add issue to project 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Get App Token 14 | uses: actions/create-github-app-token@v1 15 | id: generate_token 16 | with: 17 | app-id: ${{ secrets.APP_ID }} 18 | private-key: ${{ secrets.PRIVATE_KEY }} 19 | - uses: actions/add-to-project@v0.5.0 20 | with: 21 | project-url: https://github.com/orgs/Qbox-project/projects/4 22 | github-token: ${{ steps.generate_token.outputs.token }} 23 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: 3 | push: 4 | paths: 5 | - '**.lua' 6 | - '.github/workflows/lint.yml' 7 | pull_request_target: 8 | paths: 9 | - '**.lua' 10 | - '.github/workflows/lint.yml' 11 | jobs: 12 | lint: 13 | name: Lint Resource 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | ref: ${{ github.event.pull_request.head.sha }} 19 | - name: Lint 20 | uses: iLLeniumStudios/fivem-lua-lint-action@v2 21 | with: 22 | capture: "junit.xml" 23 | args: "-t --formatter JUnit" 24 | extra_libs: ox_lib+mysql+qblocales+qbox+qbox_playerdata+qbox_lib 25 | - name: Generate Lint Report 26 | if: always() 27 | uses: mikepenz/action-junit-report@v4 28 | with: 29 | report_paths: "**/junit.xml" 30 | check_name: Linting Report 31 | fail_on_failure: false 32 | -------------------------------------------------------------------------------- /.github/workflows/release-action.yml: -------------------------------------------------------------------------------- 1 | name: "release-action" 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | release-action: 10 | name: "Create Release" 11 | runs-on: "ubuntu-latest" 12 | steps: 13 | - name: Checkout Repository 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | ref: ${{ github.event.repository.default_branch }} 18 | 19 | - name: Install ZIP 20 | run: sudo apt install zip 21 | 22 | - name: Bundle files 23 | run: | 24 | rm -rf ./.github ./.vscode ./.git 25 | shopt -s extglob 26 | mkdir ./${{ github.event.repository.name }} 27 | cp -r !(${{ github.event.repository.name }}) ${{ github.event.repository.name }} 28 | zip -r ./${{ github.event.repository.name }}.zip ./${{ github.event.repository.name }} 29 | 30 | - name: Get App Token 31 | uses: actions/create-github-app-token@v1 32 | id: generate_token 33 | with: 34 | app-id: ${{ secrets.APP_ID }} 35 | private-key: ${{ secrets.PRIVATE_KEY }} 36 | 37 | - name: Update CHANGELOG 38 | id: changelog 39 | uses: requarks/changelog-action@v1 40 | continue-on-error: true 41 | with: 42 | token: ${{ steps.generate_token.outputs.token }} 43 | tag: ${{ github.ref_name }} 44 | includeInvalidCommits: true 45 | useGitmojis: false 46 | writeToFile: false 47 | 48 | - name: Create Release 49 | uses: ncipollo/release-action@v1.14.0 50 | with: 51 | allowUpdates: true 52 | draft: false 53 | makeLatest: true 54 | name: ${{ github.ref_name }} 55 | tag: ${{ github.ref_name }} 56 | body: ${{ steps.changelog.outputs.changes }} 57 | artifacts: ${{ github.event.repository.name }}.zip 58 | token: ${{ steps.generate_token.outputs.token }} 59 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create New Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | required: true 8 | 9 | jobs: 10 | create-release: 11 | name: Create New Release 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Get App Token 15 | uses: actions/create-github-app-token@v1 16 | id: generate_token 17 | with: 18 | app-id: ${{ secrets.APP_ID }} 19 | private-key: ${{ secrets.PRIVATE_KEY }} 20 | 21 | - name: Checkout Repository 22 | uses: actions/checkout@v4 23 | with: 24 | token: ${{ steps.generate_token.outputs.token }} 25 | 26 | - name: Setup node 27 | uses: actions/setup-node@v4 28 | with: 29 | node-version: 20.x 30 | 31 | - name: Bump manifest version 32 | run: node .github/actions/bump-manifest-version.js 33 | env: 34 | TGT_RELEASE_VERSION: ${{ inputs.version }} 35 | 36 | - name: Push manifest change 37 | uses: EndBug/add-and-commit@v9 38 | with: 39 | add: fxmanifest.lua 40 | push: true 41 | message: 'chore: bump manifest version to ${{ inputs.version }}' 42 | tag: ${{ inputs.version }} 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore any IDE derived project configs 2 | .idea 3 | .vscode/* 4 | !.vscode/extensions.json 5 | !.vscode/settings.json 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "sumneko.lua", 4 | "overextended.cfxlua-vscode", 5 | "ihyajb.qbcore-code-snippets", 6 | "EditorConfig.EditorConfig", 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Lua.runtime.nonstandardSymbol": ["/**/", "`", "+=", "-=", "*=", "/="], 3 | "Lua.runtime.version": "Lua 5.4", 4 | "Lua.diagnostics.globals": [ 5 | "lib", 6 | "cache", 7 | "locale", 8 | "MySQL", 9 | "QBX", 10 | "qbx" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |  2 | 3 | # qbx_hud 4 | 5 | Shows UI elements such as health, hunger, thirst, etc. 6 | 7 | ## Features 8 | 9 | ### Player HUDs 10 | - Hunger 11 | - Thirst 12 | - Health 13 | - Voice Volume 14 | - Armor 15 | - Stamina 16 | - Stress 17 | - Oxygen 18 | - Parachute 19 | - Radio 20 | - Money 21 | 22 | ### Vehicle HUDs 23 | - Altitude 24 | - Speed 25 | - Nitro 26 | - Harness Health 27 | - Seatbelt Status 28 | - Engine Health 29 | - Fuel 30 | 31 | ### Settings Menu 32 | Each player can customize the look and feel of their own HUD through a settings menu. 33 | The **default key** is **"I"**, but each player can rebind this. One can also open the settings menu using **"/menu"** 34 | 35 | ## Previews 36 | 37 | ### all radials 38 |  39 | ### dead health 40 |  41 | ### engine health 42 |  43 | ### altitude 44 |  45 | ### parachute 46 |  47 | ### armed 48 |  49 | ### cinematic 50 |  51 | ### cruise 52 |  53 | ### harness 54 |  55 | ### cash 56 |  57 | ### bank 58 |  59 | ### nitro 60 |  61 | ### stamina 62 |  63 | ### oxygen 64 |  65 | ### radio (outdated color) 66 |  67 | -------------------------------------------------------------------------------- /client/main.lua: -------------------------------------------------------------------------------- 1 | local config = require 'config.client' 2 | local sharedConfig = require 'config.shared' 3 | local speedMultiplier = config.useMPH and 2.23694 or 3.6 4 | local cruiseOn = false 5 | local showAltitude = false 6 | local showSeatbelt = false 7 | local nos = 0 8 | local playerState = LocalPlayer.state 9 | local stress = playerState.stress or 0 10 | local hunger = playerState.hunger or 100 11 | local thirst = playerState.thirst or 100 12 | local cashAmount = 0 13 | local bankAmount = 0 14 | local nitroActive = 0 15 | local hp = 100 16 | local armed = false 17 | local parachute = -1 18 | local oxygen = 100 19 | local dev = false 20 | local playerDead = false 21 | local showMenu = false 22 | local showCircleB = false 23 | local showSquareB = false 24 | local CinematicHeight = 0.2 25 | local w = 0 26 | local hasWeapon = false 27 | 28 | DisplayRadar(false) 29 | 30 | local function cinematicShow(bool) 31 | SetBigmapActive(true, false) 32 | Wait(0) 33 | SetBigmapActive(false, false) 34 | if bool then 35 | for i = CinematicHeight, 0, -1.0 do 36 | Wait(10) 37 | w = i 38 | end 39 | else 40 | for i = 0, CinematicHeight, 1.0 do 41 | Wait(10) 42 | w = i 43 | end 44 | end 45 | end 46 | 47 | local function loadSettings(settings) 48 | for k, v in pairs(settings) do 49 | if k == 'isToggleMapShapeChecked' then 50 | sharedConfig.menu.isToggleMapShapeChecked = v 51 | SendNUIMessage({test = true, event = k, toggle = v}) 52 | elseif k == 'isCineamticModeChecked' then 53 | sharedConfig.menu.isCineamticModeChecked = v 54 | cinematicShow(v) 55 | SendNUIMessage({test = true, event = k, toggle = v}) 56 | elseif k == 'isChangeFPSChecked' then 57 | sharedConfig.menu[k] = v 58 | local val = v and 'Optimized' or 'Synced' 59 | SendNUIMessage({test = true, event = k, toggle = val}) 60 | else 61 | sharedConfig.menu[k] = v 62 | SendNUIMessage({test = true, event = k, toggle = v}) 63 | end 64 | end 65 | exports.qbx_core:Notify(locale('notify.hud_settings_loaded'), 'success') 66 | Wait(1000) 67 | TriggerEvent('hud:client:LoadMap') 68 | end 69 | 70 | local function saveSettings() 71 | SetResourceKvp('hudSettings', json.encode(sharedConfig.menu)) 72 | end 73 | 74 | RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() 75 | Wait(2000) 76 | local hudSettings = GetResourceKvpString('hudSettings') 77 | if hudSettings then loadSettings(json.decode(hudSettings)) end 78 | stress = QBX.PlayerData.metadata.stress 79 | hunger = QBX.PlayerData.metadata.hunger 80 | thirst = QBX.PlayerData.metadata.thirst 81 | hp = QBX.PlayerData.metadata.health 82 | end) 83 | 84 | AddEventHandler('onResourceStart', function(resourceName) 85 | if GetCurrentResourceName() ~= resourceName then return end 86 | Wait(2000) 87 | local hudSettings = GetResourceKvpString('hudSettings') 88 | if hudSettings then loadSettings(json.decode(hudSettings)) end 89 | end) 90 | 91 | -- Callbacks & Events 92 | local function settingsMenu() 93 | if showMenu then return end 94 | SetNuiFocus(true, true) 95 | SendNUIMessage({action = 'open'}) 96 | showMenu = true 97 | end 98 | 99 | lib.addKeybind({ 100 | name = 'hud_menu', 101 | description = locale('info.open_menu'), 102 | defaultKey = config.menuKey, 103 | defaultMapper = 'keyboard', 104 | onPressed = settingsMenu, 105 | }) 106 | 107 | RegisterNUICallback('closeMenu', function(_, cb) 108 | Wait(50) 109 | showMenu = false 110 | SetNuiFocus(false, false) 111 | cb('ok') 112 | end) 113 | 114 | -- Reset hud 115 | local function restartHud() 116 | exports.qbx_core:Notify(locale('notify.hud_restart'), 'error') 117 | if cache.vehicle then 118 | Wait(2600) 119 | SendNUIMessage({action = 'car', show = false}) 120 | SendNUIMessage({action = 'car', show = true}) 121 | end 122 | Wait(2600) 123 | SendNUIMessage({action = 'hudtick', show = false}) 124 | SendNUIMessage({action = 'hudtick', show = true}) 125 | Wait(2600) 126 | exports.qbx_core:Notify(locale('notify.hud_start'), 'success') 127 | end 128 | 129 | RegisterNUICallback('restartHud', function(_, cb) 130 | Wait(50) 131 | restartHud() 132 | cb('ok') 133 | end) 134 | 135 | RegisterCommand('resethud', function(_, cb) 136 | Wait(50) 137 | restartHud() 138 | cb('ok') 139 | end) 140 | 141 | RegisterNUICallback('resetStorage', function(_, cb) 142 | Wait(50) 143 | TriggerEvent('hud:client:resetStorage') 144 | cb('ok') 145 | end) 146 | 147 | RegisterNetEvent('hud:client:resetStorage', function() 148 | Wait(50) 149 | local menu = lib.callback.await('hud:server:getMenu', false) 150 | loadSettings(menu) 151 | SetResourceKvp('hudSettings', json.encode(menu)) 152 | end) 153 | 154 | RegisterNUICallback('showOutMap', function(_, cb) 155 | Wait(50) 156 | sharedConfig.menu.isOutMapChecked = not sharedConfig.menu.isOutMapChecked 157 | saveSettings() 158 | cb('ok') 159 | end) 160 | 161 | RegisterNUICallback('showOutCompass', function(_, cb) 162 | Wait(50) 163 | sharedConfig.menu.isOutCompassChecked = not sharedConfig.menu.isOutCompassChecked 164 | saveSettings() 165 | cb('ok') 166 | end) 167 | 168 | RegisterNUICallback('showFollowCompass', function(_, cb) 169 | Wait(50) 170 | sharedConfig.menu.isCompassFollowChecked = not sharedConfig.menu.isCompassFollowChecked 171 | saveSettings() 172 | cb('ok') 173 | end) 174 | 175 | RegisterNUICallback('showMapNotif', function(_, cb) 176 | Wait(50) 177 | sharedConfig.menu.isMapNotifChecked = not sharedConfig.menu.isMapNotifChecked 178 | 179 | saveSettings() 180 | cb('ok') 181 | end) 182 | 183 | RegisterNUICallback('showFuelAlert', function(_, cb) 184 | Wait(50) 185 | sharedConfig.menu.isLowFuelChecked = not sharedConfig.menu.isLowFuelChecked 186 | saveSettings() 187 | cb('ok') 188 | end) 189 | 190 | RegisterNUICallback('showCinematicNotif', function(_, cb) 191 | Wait(50) 192 | sharedConfig.menu.isCinematicNotifChecked = not sharedConfig.menu.isCinematicNotifChecked 193 | saveSettings() 194 | cb('ok') 195 | end) 196 | 197 | -- Status 198 | RegisterNUICallback('dynamicHealth', function(_, cb) 199 | Wait(50) 200 | TriggerEvent('hud:client:ToggleHealth') 201 | cb('ok') 202 | end) 203 | 204 | RegisterNetEvent('hud:client:ToggleHealth', function() 205 | Wait(50) 206 | sharedConfig.menu.isDynamicHealthChecked = not sharedConfig.menu.isDynamicHealthChecked 207 | saveSettings() 208 | end) 209 | 210 | RegisterNUICallback('dynamicArmor', function(_, cb) 211 | Wait(50) 212 | sharedConfig.menu.isDynamicArmorChecked = not sharedConfig.menu.isDynamicArmorChecked 213 | saveSettings() 214 | cb('ok') 215 | end) 216 | 217 | RegisterNUICallback('dynamicHunger', function(_, cb) 218 | Wait(50) 219 | sharedConfig.menu.isDynamicHungerChecked = not sharedConfig.menu.isDynamicHungerChecked 220 | saveSettings() 221 | cb('ok') 222 | end) 223 | 224 | RegisterNUICallback('dynamicThirst', function(_, cb) 225 | Wait(50) 226 | sharedConfig.menu.isDynamicThirstChecked = not sharedConfig.menu.isDynamicThirstChecked 227 | saveSettings() 228 | cb('ok') 229 | end) 230 | 231 | RegisterNUICallback('dynamicStress', function(_, cb) 232 | Wait(50) 233 | sharedConfig.menu.isDynamicStressChecked = not sharedConfig.menu.isDynamicStressChecked 234 | saveSettings() 235 | cb('ok') 236 | end) 237 | 238 | RegisterNUICallback('dynamicOxygen', function(_, cb) 239 | Wait(50) 240 | sharedConfig.menu.isDynamicOxygenChecked = not sharedConfig.menu.isDynamicOxygenChecked 241 | saveSettings() 242 | cb('ok') 243 | end) 244 | 245 | -- Vehicle 246 | RegisterNUICallback('changeFPS', function(_, cb) 247 | Wait(50) 248 | sharedConfig.menu.isChangeFPSChecked = not sharedConfig.menu.isChangeFPSChecked 249 | saveSettings() 250 | cb('ok') 251 | end) 252 | 253 | RegisterNUICallback('HideMap', function(_, cb) 254 | Wait(50) 255 | sharedConfig.menu.isHideMapChecked = not sharedConfig.menu.isHideMapChecked 256 | DisplayRadar(not sharedConfig.menu.isHideMapChecked) 257 | saveSettings() 258 | cb('ok') 259 | end) 260 | 261 | RegisterNetEvent('hud:client:LoadMap', function() 262 | Wait(50) 263 | -- Credit to Dalrae for the solve. 264 | local defaultAspectRatio = 1920 / 1080 -- Don't change this. 265 | local resolutionX, resolutionY = GetActiveScreenResolution() 266 | local aspectRatio = resolutionX / resolutionY 267 | local minimapOffset = 0 268 | if aspectRatio > defaultAspectRatio then 269 | minimapOffset = ((defaultAspectRatio-aspectRatio) / 3.6) - 0.008 270 | end 271 | if sharedConfig.menu.isToggleMapShapeChecked == 'square' then 272 | lib.requestStreamedTextureDict('squaremap') 273 | if sharedConfig.menu.isMapNotifChecked then 274 | exports.qbx_core:Notify(locale('notify.load_square_map'), 'inform') 275 | end 276 | SetMinimapClipType(0) 277 | AddReplaceTexture('platform:/textures/graphics', 'radarmasksm', 'squaremap', 'radarmasksm') 278 | AddReplaceTexture('platform:/textures/graphics', 'radarmask1g', 'squaremap', 'radarmasksm') 279 | -- 0.0 = nav symbol and icons left 280 | -- 0.1638 = nav symbol and icons stretched 281 | -- 0.216 = nav symbol and icons raised up 282 | SetMinimapComponentPosition('minimap', 'L', 'B', 0.0 + minimapOffset, -0.047, 0.1638, 0.183) 283 | 284 | -- icons within map 285 | SetMinimapComponentPosition('minimap_mask', 'L', 'B', 0.0 + minimapOffset, 0.0, 0.128, 0.20) 286 | 287 | -- -0.01 = map pulled left 288 | -- 0.025 = map raised up 289 | -- 0.262 = map stretched 290 | -- 0.315 = map shorten 291 | SetMinimapComponentPosition('minimap_blur', 'L', 'B', -0.01 + minimapOffset, 0.025, 0.262, 0.300) 292 | SetBlipAlpha(GetNorthRadarBlip(), 0) 293 | SetBigmapActive(true, false) 294 | SetMinimapClipType(0) 295 | Wait(50) 296 | SetBigmapActive(false, false) 297 | if sharedConfig.menu.isToggleMapBordersChecked then 298 | showCircleB = false 299 | showSquareB = true 300 | end 301 | Wait(1200) 302 | if sharedConfig.menu.isMapNotifChecked then 303 | exports.qbx_core:Notify(locale('notify.loaded_square_map'), 'success') 304 | end 305 | elseif sharedConfig.menu.isToggleMapShapeChecked == 'circle' then 306 | lib.requestStreamedTextureDict('circlemap') 307 | if sharedConfig.menu.isMapNotifChecked then 308 | exports.qbx_core:Notify(locale('notify.load_circle_map'), 'inform') 309 | end 310 | SetMinimapClipType(1) 311 | AddReplaceTexture('platform:/textures/graphics', 'radarmasksm', 'circlemap', 'radarmasksm') 312 | AddReplaceTexture('platform:/textures/graphics', 'radarmask1g', 'circlemap', 'radarmasksm') 313 | -- -0.0100 = nav symbol and icons left 314 | -- 0.180 = nav symbol and icons stretched 315 | -- 0.258 = nav symbol and icons raised up 316 | SetMinimapComponentPosition('minimap', 'L', 'B', -0.0100 + minimapOffset, -0.030, 0.180, 0.258) 317 | 318 | -- icons within map 319 | SetMinimapComponentPosition('minimap_mask', 'L', 'B', 0.200 + minimapOffset, 0.0, 0.065, 0.20) 320 | 321 | -- -0.00 = map pulled left 322 | -- 0.015 = map raised up 323 | -- 0.252 = map stretched 324 | -- 0.338 = map shorten 325 | SetMinimapComponentPosition('minimap_blur', 'L', 'B', -0.00 + minimapOffset, 0.015, 0.252, 0.338) 326 | SetBlipAlpha(GetNorthRadarBlip(), 0) 327 | SetMinimapClipType(1) 328 | SetBigmapActive(true, false) 329 | Wait(50) 330 | SetBigmapActive(false, false) 331 | if sharedConfig.menu.isToggleMapBordersChecked then 332 | showSquareB = false 333 | showCircleB = true 334 | end 335 | Wait(1200) 336 | if sharedConfig.menu.isMapNotifChecked then 337 | exports.qbx_core:Notify(locale('notify.loaded_circle_map'), 'success') 338 | end 339 | end 340 | end) 341 | 342 | RegisterNUICallback('ToggleMapShape', function(_, cb) 343 | Wait(50) 344 | if not sharedConfig.menu.isHideMapChecked then 345 | sharedConfig.menu.isToggleMapShapeChecked = sharedConfig.menu.isToggleMapShapeChecked == 'circle' and 'square' or 'circle' 346 | Wait(50) 347 | TriggerEvent('hud:client:LoadMap') 348 | end 349 | saveSettings() 350 | cb('ok') 351 | end) 352 | 353 | RegisterNUICallback('ToggleMapBorders', function(_, cb) 354 | Wait(50) 355 | sharedConfig.menu.isToggleMapBordersChecked = not sharedConfig.menu.isToggleMapBordersChecked 356 | if sharedConfig.menu.isToggleMapBordersChecked then 357 | if sharedConfig.menu.isToggleMapShapeChecked == 'square' then 358 | showSquareB = true 359 | else 360 | showCircleB = true 361 | end 362 | else 363 | showSquareB = false 364 | showCircleB = false 365 | end 366 | saveSettings() 367 | cb('ok') 368 | end) 369 | 370 | RegisterNUICallback('dynamicEngine', function(_, cb) 371 | Wait(50) 372 | sharedConfig.menu.isDynamicEngineChecked = not sharedConfig.menu.isDynamicEngineChecked 373 | saveSettings() 374 | cb('ok') 375 | end) 376 | 377 | RegisterNUICallback('dynamicNitro', function(_, cb) 378 | Wait(50) 379 | sharedConfig.menu.isDynamicNitroChecked = not sharedConfig.menu.isDynamicNitroChecked 380 | saveSettings() 381 | cb('ok') 382 | end) 383 | 384 | -- Compass 385 | RegisterNUICallback('showCompassBase', function(_, cb) 386 | Wait(50) 387 | sharedConfig.menu.isCompassShowChecked = not sharedConfig.menu.isCompassShowChecked 388 | saveSettings() 389 | cb('ok') 390 | end) 391 | 392 | RegisterNUICallback('showStreetsNames', function(_, cb) 393 | Wait(50) 394 | sharedConfig.menu.isShowStreetsChecked = not sharedConfig.menu.isShowStreetsChecked 395 | saveSettings() 396 | cb('ok') 397 | end) 398 | 399 | RegisterNUICallback('showPointerIndex', function(_, cb) 400 | Wait(50) 401 | sharedConfig.menu.isPointerShowChecked = not sharedConfig.menu.isPointerShowChecked 402 | saveSettings() 403 | cb('ok') 404 | end) 405 | 406 | RegisterNUICallback('showDegreesNum', function(_, cb) 407 | Wait(50) 408 | sharedConfig.menu.isDegreesShowChecked = not sharedConfig.menu.isDegreesShowChecked 409 | saveSettings() 410 | cb('ok') 411 | end) 412 | 413 | RegisterNUICallback('changeCompassFPS', function(_, cb) 414 | Wait(50) 415 | sharedConfig.menu.isChangeCompassFPSChecked = not sharedConfig.menu.isChangeCompassFPSChecked 416 | saveSettings() 417 | cb('ok') 418 | end) 419 | 420 | RegisterNUICallback('cinematicMode', function(_, cb) 421 | Wait(50) 422 | if sharedConfig.menu.isCineamticModeChecked then 423 | cinematicShow(false) 424 | sharedConfig.menu.isCineamticModeChecked = false 425 | if sharedConfig.menu.isCinematicNotifChecked then 426 | exports.qbx_core:Notify(locale('notify.cinematic_off'), 'error') 427 | end 428 | DisplayRadar(true) 429 | else 430 | cinematicShow(true) 431 | sharedConfig.menu.isCineamticModeChecked = true 432 | if sharedConfig.menu.isCinematicNotifChecked then 433 | exports.qbx_core:Notify(locale('notify.cinematic_on'), 'success') 434 | end 435 | end 436 | saveSettings() 437 | cb('ok') 438 | end) 439 | 440 | RegisterNetEvent('hud:client:ToggleAirHud', function() 441 | showAltitude = not showAltitude 442 | end) 443 | 444 | ---@deprecated Use statebags instead 445 | RegisterNetEvent('hud:client:UpdateNeeds', function(newHunger, newThirst) -- Triggered in qb-core 446 | hunger = newHunger 447 | thirst = newThirst 448 | end) 449 | 450 | AddStateBagChangeHandler('hunger', ('player:%s'):format(cache.serverId), function(_, _, value) 451 | hunger = value 452 | end) 453 | 454 | AddStateBagChangeHandler('thirst', ('player:%s'):format(cache.serverId), function(_, _, value) 455 | thirst = value 456 | end) 457 | 458 | ---@deprecated Use statebags instead 459 | RegisterNetEvent('hud:client:UpdateStress', function(newStress) 460 | stress = newStress 461 | end) 462 | 463 | AddStateBagChangeHandler('stress', ('player:%s'):format(cache.serverId), function(_, _, value) 464 | stress = value 465 | end) 466 | 467 | RegisterNetEvent('hud:client:ToggleShowSeatbelt', function() 468 | showSeatbelt = not showSeatbelt 469 | end) 470 | 471 | RegisterNetEvent('seatbelt:client:ToggleCruise', function() -- Triggered in smallresources 472 | cruiseOn = not cruiseOn 473 | end) 474 | 475 | ---@deprecated Use statebags instead 476 | RegisterNetEvent('hud:client:UpdateNitrous', function(_, nitroLevel, bool) 477 | nos = nitroLevel 478 | nitroActive = bool 479 | end) 480 | 481 | qbx.entityStateHandler('nitroFlames', function(veh, netId, value) 482 | local plate = qbx.string.trim(GetVehicleNumberPlateText(veh)) 483 | local cachePlate = qbx.string.trim(GetVehicleNumberPlateText(cache.vehicle)) 484 | if plate ~= cachePlate then return end 485 | nitroActive = value 486 | end) 487 | 488 | qbx.entityStateHandler('nitro', function(veh, netId, value) 489 | local plate = qbx.string.trim(GetVehicleNumberPlateText(veh)) 490 | local cachePlate = qbx.string.trim(GetVehicleNumberPlateText(cache.vehicle)) 491 | if plate ~= cachePlate then return end 492 | nos = value 493 | end) 494 | 495 | RegisterNetEvent('hud:client:UpdateHarness', function(harnessHp) 496 | hp = harnessHp 497 | end) 498 | 499 | RegisterNetEvent('qb-admin:client:ToggleDevmode', function() 500 | dev = not dev 501 | end) 502 | 503 | local function isWhitelistedWeaponArmed(weapon) 504 | if weapon then 505 | for _, v in pairs(config.weaponsArmedMode) do 506 | if weapon == v then 507 | return true 508 | end 509 | end 510 | end 511 | return false 512 | end 513 | 514 | local prevPlayerStats = {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil} 515 | 516 | local function updatePlayerHud(data) 517 | local shouldUpdate = false 518 | for k, v in pairs(data) do 519 | if prevPlayerStats[k] ~= v then 520 | shouldUpdate = true 521 | break 522 | end 523 | end 524 | prevPlayerStats = data 525 | if shouldUpdate then 526 | SendNUIMessage({ 527 | action = 'hudtick', 528 | show = data[1], 529 | dynamicHealth = data[2], 530 | dynamicArmor = data[3], 531 | dynamicHunger = data[4], 532 | dynamicThirst = data[5], 533 | dynamicStress = data[6], 534 | dynamicOxygen = data[7], 535 | dynamicEngine = data[8], 536 | dynamicNitro = data[9], 537 | health = data[10], 538 | playerDead = data[11], 539 | armor = data[12], 540 | thirst = data[13], 541 | hunger = data[14], 542 | stress = data[15], 543 | voice = data[16], 544 | radio = data[17], 545 | talking = data[18], 546 | armed = data[19], 547 | oxygen = data[20], 548 | parachute = data[21], 549 | nos = data[22], 550 | cruise = data[23], 551 | nitroActive = data[24], 552 | harness = data[25], 553 | hp = data[26], 554 | speed = data[27], 555 | engine = data[28], 556 | cinematic = data[29], 557 | dev = data[30], 558 | }) 559 | end 560 | end 561 | 562 | local prevVehicleStats = {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil} 563 | 564 | local function updateVehicleHud(data) 565 | local shouldUpdate = false 566 | local invOpen = LocalPlayer.state.invOpen 567 | for k, v in pairs(data) do 568 | if prevVehicleStats[k] ~= v then shouldUpdate = true break end 569 | end 570 | prevVehicleStats = data 571 | if shouldUpdate and not invOpen then 572 | SendNUIMessage({ 573 | action = 'car', 574 | show = data[1], 575 | isPaused = data[2], 576 | seatbelt = data[3], 577 | speed = data[4], 578 | fuel = data[5], 579 | altitude = data[6], 580 | showAltitude = data[7], 581 | showSeatbelt = data[8], 582 | showSquareB = data[9], 583 | showCircleB = data[10], 584 | }) 585 | end 586 | end 587 | 588 | local lastFuelUpdate = 0 589 | local lastFuelCheck = 0 590 | 591 | local function getFuelLevel(vehicle) 592 | local updateTick = GetGameTimer() 593 | if (updateTick - lastFuelUpdate) > 2000 then 594 | lastFuelUpdate = updateTick 595 | lastFuelCheck = math.floor(GetVehicleFuelLevel(vehicle)) 596 | end 597 | return lastFuelCheck 598 | end 599 | 600 | -- HUD Update loop 601 | 602 | CreateThread(function() 603 | local wasInVehicle = false 604 | while true do 605 | if sharedConfig.menu.isChangeFPSChecked then 606 | Wait(500) 607 | else 608 | Wait(50) 609 | end 610 | if LocalPlayer.state.isLoggedIn then 611 | local show = true 612 | local weapon = GetSelectedPedWeapon(cache.ped) 613 | -- Player hud 614 | if not isWhitelistedWeaponArmed(weapon) then 615 | if weapon ~= `WEAPON_UNARMED` then 616 | armed = true 617 | else 618 | armed = false 619 | end 620 | end 621 | playerDead = IsEntityDead(cache.ped) or QBX.PlayerData.metadata.inlaststand or QBX.PlayerData.metadata.isdead 622 | parachute = GetPedParachuteState(cache.ped) 623 | -- Stamina 624 | if not IsEntityInWater(cache.ped) then 625 | oxygen = 100 - GetPlayerSprintStaminaRemaining(cache.playerId) 626 | end 627 | -- Oxygen 628 | if IsEntityInWater(cache.ped) then 629 | oxygen = GetPlayerUnderwaterTimeRemaining(cache.playerId) * 10 630 | end 631 | -- Player hud 632 | local talking = NetworkIsPlayerTalking(cache.playerId) 633 | local voice = 0 634 | if LocalPlayer.state.proximity then 635 | voice = LocalPlayer.state.proximity.distance 636 | end 637 | if IsPauseMenuActive() then 638 | show = false 639 | end 640 | if not (cache.vehicle and not IsThisModelABicycle(cache.vehicle)) then 641 | updatePlayerHud({ 642 | show, 643 | sharedConfig.menu.isDynamicHealthChecked, 644 | sharedConfig.menu.isDynamicArmorChecked, 645 | sharedConfig.menu.isDynamicHungerChecked, 646 | sharedConfig.menu.isDynamicThirstChecked, 647 | sharedConfig.menu.isDynamicStressChecked, 648 | sharedConfig.menu.isDynamicOxygenChecked, 649 | sharedConfig.menu.isDynamicEngineChecked, 650 | sharedConfig.menu.isDynamicNitroChecked, 651 | GetEntityHealth(cache.ped) - 100, 652 | playerDead, 653 | GetPedArmour(cache.ped), 654 | thirst, 655 | hunger, 656 | stress, 657 | voice, 658 | LocalPlayer.state.radioChannel, 659 | talking, 660 | armed, 661 | oxygen, 662 | parachute, 663 | -1, 664 | cruiseOn, 665 | nitroActive, 666 | LocalPlayer.state?.harness, 667 | hp, 668 | math.ceil(GetEntitySpeed(cache.vehicle) * speedMultiplier), 669 | -1, 670 | sharedConfig.menu.isCineamticModeChecked, 671 | dev, 672 | }) 673 | end 674 | -- Vehicle hud 675 | if IsPedInAnyHeli(cache.ped) or IsPedInAnyPlane(cache.ped) then 676 | showAltitude = true 677 | showSeatbelt = false 678 | end 679 | if cache.vehicle and not IsThisModelABicycle(cache.vehicle) then 680 | if not wasInVehicle then 681 | DisplayRadar(true) 682 | end 683 | wasInVehicle = true 684 | updatePlayerHud({ 685 | show, 686 | sharedConfig.menu.isDynamicHealthChecked, 687 | sharedConfig.menu.isDynamicArmorChecked, 688 | sharedConfig.menu.isDynamicHungerChecked, 689 | sharedConfig.menu.isDynamicThirstChecked, 690 | sharedConfig.menu.isDynamicStressChecked, 691 | sharedConfig.menu.isDynamicOxygenChecked, 692 | sharedConfig.menu.isDynamicEngineChecked, 693 | sharedConfig.menu.isDynamicNitroChecked, 694 | GetEntityHealth(cache.ped) - 100, 695 | playerDead, 696 | GetPedArmour(cache.ped), 697 | thirst, 698 | hunger, 699 | stress, 700 | voice, 701 | LocalPlayer.state.radioChannel, 702 | talking, 703 | armed, 704 | oxygen, 705 | GetPedParachuteState(cache.ped), 706 | nos, 707 | cruiseOn, 708 | nitroActive, 709 | LocalPlayer.state?.harness, 710 | hp, 711 | math.ceil(GetEntitySpeed(cache.vehicle) * speedMultiplier), 712 | (GetVehicleEngineHealth(cache.vehicle) / 10), 713 | sharedConfig.menu.isCineamticModeChecked, 714 | dev, 715 | }) 716 | updateVehicleHud({ 717 | show, 718 | IsPauseMenuActive(), 719 | LocalPlayer.state?.seatbelt, 720 | math.ceil(GetEntitySpeed(cache.vehicle) * speedMultiplier), 721 | getFuelLevel(cache.vehicle), 722 | math.ceil(GetEntityCoords(cache.ped).z * 0.5), 723 | showAltitude, 724 | showSeatbelt, 725 | showSquareB, 726 | showCircleB, 727 | }) 728 | showAltitude = false 729 | showSeatbelt = true 730 | else 731 | if wasInVehicle then 732 | wasInVehicle = false 733 | SendNUIMessage({ 734 | action = 'car', 735 | show = false, 736 | seatbelt = false, 737 | cruise = false, 738 | }) 739 | cruiseOn = false 740 | end 741 | DisplayRadar(sharedConfig.menu.isOutMapChecked) 742 | end 743 | else 744 | SendNUIMessage({ 745 | action = 'hudtick', 746 | show = false 747 | }) 748 | end 749 | end 750 | end) 751 | 752 | -- Low fuel 753 | CreateThread(function() 754 | while true do 755 | if LocalPlayer.state.isLoggedIn then 756 | if cache.vehicle and not IsThisModelABicycle(GetEntityModel(cache.vehicle)) then 757 | if getFuelLevel(cache.vehicle) <= 20 then -- At 20% Fuel Left 758 | if sharedConfig.menu.isLowFuelChecked then 759 | -- Add pager sound for when fuel is low 760 | exports.qbx_core:Notify(locale('notify.low_fuel'), 'error') 761 | Wait(60000) -- repeats every 1 min until empty 762 | end 763 | end 764 | end 765 | end 766 | Wait(10000) 767 | end 768 | end) 769 | 770 | -- Money HUD 771 | 772 | RegisterNetEvent('hud:client:ShowAccounts', function(type, amount) 773 | if type == 'cash' then 774 | SendNUIMessage({ 775 | action = 'show', 776 | type = 'cash', 777 | cash = amount 778 | }) 779 | else 780 | SendNUIMessage({ 781 | action = 'show', 782 | type = 'bank', 783 | bank = amount 784 | }) 785 | end 786 | end) 787 | 788 | RegisterNetEvent('hud:client:OnMoneyChange', function(type, amount, isMinus) 789 | cashAmount = QBX.PlayerData.money.cash 790 | bankAmount = QBX.PlayerData.money.bank 791 | SendNUIMessage({ 792 | action = 'updatemoney', 793 | cash = cashAmount, 794 | bank = bankAmount, 795 | amount = amount, 796 | minus = isMinus, 797 | type = type 798 | }) 799 | end) 800 | 801 | -- Stress Gain 802 | if config.stress.enableStress then 803 | CreateThread(function() -- Speeding 804 | while true do 805 | if LocalPlayer.state.isLoggedIn then 806 | if cache.vehicle then 807 | local vehClass = GetVehicleClass(cache.vehicle) 808 | local speed = GetEntitySpeed(cache.vehicle) * speedMultiplier 809 | 810 | if vehClass ~= 13 and vehClass ~= 14 and vehClass ~= 15 and vehClass ~= 16 and vehClass ~= 21 then 811 | local stressSpeed 812 | if vehClass == 8 then 813 | stressSpeed = config.stress.minForSpeeding 814 | else 815 | stressSpeed = LocalPlayer.state?.seatbelt and config.stress.minForSpeeding or config.stress.minForSpeedingUnbuckled 816 | end 817 | if speed >= stressSpeed then 818 | TriggerServerEvent('hud:server:GainStress', math.random(1, 3)) 819 | end 820 | end 821 | end 822 | end 823 | Wait(10000) 824 | end 825 | end) 826 | end 827 | 828 | local function isWhitelistedWeaponStress(weapon) 829 | if weapon then 830 | for _, v in pairs(config.stress.whitelistedWeapons) do 831 | if weapon == v then 832 | return true 833 | end 834 | end 835 | end 836 | return false 837 | end 838 | 839 | local function startWeaponStressThread(weapon) 840 | if isWhitelistedWeaponStress(weapon) then return end 841 | hasWeapon = true 842 | 843 | CreateThread(function() 844 | while hasWeapon do 845 | if IsPedShooting(cache.ped) then 846 | if math.random() <= config.stress.chance then 847 | TriggerServerEvent('hud:server:GainStress', math.random(1, 5)) 848 | end 849 | end 850 | Wait(0) 851 | end 852 | end) 853 | end 854 | 855 | AddEventHandler('ox_inventory:currentWeapon', function(currentWeapon) 856 | hasWeapon = false 857 | Wait(0) 858 | 859 | if not currentWeapon then return end 860 | 861 | startWeaponStressThread(currentWeapon.hash) 862 | end) 863 | 864 | -- Stress Screen Effects 865 | 866 | local function getBlurIntensity(stresslevel) 867 | for _, v in pairs(config.stress.blurIntensity) do 868 | if stresslevel >= v.min and stresslevel <= v.max then 869 | return v.intensity 870 | end 871 | end 872 | return 1500 873 | end 874 | 875 | local function getEffectInterval(stresslevel) 876 | for _, v in pairs(config.stress.effectInterval) do 877 | if stresslevel >= v.min and stresslevel <= v.max then 878 | return v.timeout 879 | end 880 | end 881 | return 60000 882 | end 883 | 884 | CreateThread(function() 885 | while true do 886 | local effectInterval = getEffectInterval(stress) 887 | if stress >= 100 then 888 | local blurIntensity = getBlurIntensity(stress) 889 | local fallRepeat = math.random(2, 4) 890 | local ragdollTimeout = fallRepeat * 1750 891 | TriggerScreenblurFadeIn(1000.0) 892 | Wait(blurIntensity) 893 | TriggerScreenblurFadeOut(1000.0) 894 | 895 | if not IsPedRagdoll(cache.ped) and IsPedOnFoot(cache.ped) and not IsPedSwimming(cache.ped) then 896 | local forwardVector = GetEntityForwardVector(cache.ped) 897 | SetPedToRagdollWithFall(cache.ped, ragdollTimeout, ragdollTimeout, 1, forwardVector.x, forwardVector.y, forwardVector.z, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) 898 | end 899 | 900 | Wait(1000) 901 | for _ = 1, fallRepeat, 1 do 902 | Wait(750) 903 | DoScreenFadeOut(200) 904 | Wait(1000) 905 | DoScreenFadeIn(200) 906 | TriggerScreenblurFadeIn(1000.0) 907 | Wait(blurIntensity) 908 | TriggerScreenblurFadeOut(1000.0) 909 | end 910 | elseif stress >= config.stress.minForShaking then 911 | local blurIntensity = getBlurIntensity(stress) 912 | TriggerScreenblurFadeIn(1000.0) 913 | Wait(blurIntensity) 914 | TriggerScreenblurFadeOut(1000.0) 915 | end 916 | Wait(effectInterval) 917 | end 918 | end) 919 | 920 | local function blackBars() 921 | DrawRect(0.0, 0.0, 2.0, w, 0, 0, 0, 255) 922 | DrawRect(0.0, 1.0, 2.0, w, 0, 0, 0, 255) 923 | end 924 | 925 | CreateThread(function() 926 | while true do 927 | if w > 0 then 928 | blackBars() 929 | DisplayRadar(false) 930 | SendNUIMessage({ 931 | action = 'hudtick', 932 | show = false, 933 | }) 934 | SendNUIMessage({ 935 | action = 'car', 936 | show = false, 937 | }) 938 | end 939 | Wait(0) 940 | end 941 | end) 942 | 943 | -- Compass 944 | local prevBaseplateStats = {nil, nil, nil, nil, nil, nil, nil} 945 | 946 | local function updateBaseplateHud(data) 947 | local shouldUpdate = false 948 | for k, v in pairs(data) do 949 | if prevBaseplateStats[k] ~= v then shouldUpdate = true break end 950 | end 951 | prevBaseplateStats = data 952 | if shouldUpdate then 953 | SendNUIMessage ({ 954 | action = 'baseplate', 955 | show = data[1], 956 | street1 = data[2], 957 | street2 = data[3], 958 | showCompass = data[4], 959 | showStreets = data[5], 960 | showPointer = data[6], 961 | showDegrees = data[7], 962 | }) 963 | end 964 | end 965 | 966 | local lastCrossroadUpdate = 0 967 | local lastCrossroadCheck = {} 968 | 969 | local function getCrossroads(player) 970 | local updateTick = GetGameTimer() 971 | if updateTick - lastCrossroadUpdate > 1500 then 972 | local pos = GetEntityCoords(player) 973 | local street1, street2 = GetStreetNameAtCoord(pos.x, pos.y, pos.z) 974 | lastCrossroadUpdate = updateTick 975 | lastCrossroadCheck = {GetStreetNameFromHashKey(street1), GetStreetNameFromHashKey(street2)} 976 | end 977 | return lastCrossroadCheck 978 | end 979 | 980 | -- Compass Update loop 981 | 982 | CreateThread(function() 983 | local lastHeading = 1 984 | local heading 985 | while true do 986 | if sharedConfig.menu.isChangeCompassFPSChecked then 987 | Wait(50) 988 | else 989 | Wait(0) 990 | end 991 | local show = true 992 | local camRot = GetGameplayCamRot(0) 993 | if sharedConfig.menu.isCompassFollowChecked then 994 | heading = qbx.math.round(360.0 - ((camRot.z + 360.0) % 360.0)) 995 | else 996 | heading = qbx.math.round(360.0 - GetEntityHeading(cache.ped)) 997 | end 998 | if heading == 360 then heading = 0 end 999 | if heading ~= lastHeading then 1000 | if cache.vehicle then 1001 | local crossroads = getCrossroads(cache.ped) 1002 | SendNUIMessage ({ 1003 | action = 'update', 1004 | value = heading 1005 | }) 1006 | updateBaseplateHud({ 1007 | show, 1008 | crossroads[1], 1009 | crossroads[2], 1010 | sharedConfig.menu.isCompassShowChecked, 1011 | sharedConfig.menu.isShowStreetsChecked, 1012 | sharedConfig.menu.isPointerShowChecked, 1013 | sharedConfig.menu.isDegreesShowChecked, 1014 | }) 1015 | else 1016 | if sharedConfig.menu.isOutCompassChecked then 1017 | SendNUIMessage ({ 1018 | action = 'update', 1019 | value = heading 1020 | }) 1021 | SendNUIMessage ({ 1022 | action = 'baseplate', 1023 | show = true, 1024 | showCompass = true, 1025 | }) 1026 | else 1027 | SendNUIMessage ({ 1028 | action = 'baseplate', 1029 | show = false, 1030 | }) 1031 | end 1032 | end 1033 | end 1034 | lastHeading = heading 1035 | end 1036 | end) 1037 | 1038 | RegisterNetEvent('qbx_hud:client:showHud', function() 1039 | if cache.vehicle then 1040 | DisplayRadar(true) 1041 | updateVehicleHud({ 1042 | true, 1043 | IsPauseMenuActive(), 1044 | LocalPlayer.state?.seatbelt, 1045 | math.ceil(GetEntitySpeed(cache.vehicle) * speedMultiplier), 1046 | getFuelLevel(cache.vehicle), 1047 | math.ceil(GetEntityCoords(cache.ped).z * 0.5), 1048 | showAltitude, 1049 | showSeatbelt, 1050 | showSquareB, 1051 | showCircleB, 1052 | }) 1053 | end 1054 | end) 1055 | 1056 | RegisterNetEvent('qbx_hud:client:hideHud', function() 1057 | if cache.vehicle then 1058 | DisplayRadar(false) 1059 | SendNUIMessage({ 1060 | action = 'car', 1061 | show = false, 1062 | }) 1063 | end 1064 | end) 1065 | -------------------------------------------------------------------------------- /config/client.lua: -------------------------------------------------------------------------------- 1 | return { 2 | menuKey = 'I', -- Key to open the HUD menu 3 | useMPH = true, -- If true, speed math will be done as MPH, if false KPH will be used (YOU HAVE TO CHANGE CONTENT IN STYLES.CSS TO DISPLAY THE CORRECT TEXT) 4 | 5 | stress = { 6 | chance = 0.1, -- Percentage stress chance when shooting (0-1) 7 | minForShaking = 50, -- Minimum stress level for screen shaking 8 | minForSpeeding = 1000, -- Minimum stress level for speeding while buckled 9 | minForSpeedingUnbuckled = 50, -- Minimum stress level for speeding while unbuckled 10 | whitelistedWeapons = { -- Weapons which don't give stress 11 | `weapon_petrolcan`, 12 | `weapon_hazardcan`, 13 | `weapon_fireextinguisher`, 14 | }, 15 | blurIntensity = { -- Blur intensity for different stress levels 16 | [1] = {min = 50, max = 60, intensity = 1500}, 17 | [2] = {min = 60, max = 70, intensity = 2000}, 18 | [3] = {min = 70, max = 80, intensity = 2500}, 19 | [4] = {min = 80, max = 90, intensity = 2700}, 20 | [5] = {min = 90, max = 100, intensity = 3000}, 21 | }, 22 | effectInterval = { -- Effect interval for different stress levels 23 | [1] = {min = 50, max = 60, timeout = math.random(50000, 60000)}, 24 | [2] = {min = 60, max = 70, timeout = math.random(40000, 50000)}, 25 | [3] = {min = 70, max = 80, timeout = math.random(30000, 40000)}, 26 | [4] = {min = 80, max = 90, timeout = math.random(20000, 30000)}, 27 | [5] = {min = 90, max = 100, timeout = math.random(15000, 20000)}, 28 | }, 29 | }, 30 | 31 | weaponsArmedMode = { -- Weapons which won't trigger armed mode 32 | -- Miscellaneous 33 | `weapon_petrolcan`, 34 | `weapon_hazardcan`, 35 | `weapon_fireextinguisher`, 36 | -- Melee 37 | `weapon_dagger`, 38 | `weapon_bat`, 39 | `weapon_bottle`, 40 | `weapon_crowbar`, 41 | `weapon_flashlight`, 42 | `weapon_golfclub`, 43 | `weapon_hammer`, 44 | `weapon_hatchet`, 45 | `weapon_knuckle`, 46 | `weapon_knife`, 47 | `weapon_machete`, 48 | `weapon_switchblade`, 49 | `weapon_nightstick`, 50 | `weapon_wrench`, 51 | `weapon_battleaxe`, 52 | `weapon_poolcue`, 53 | `weapon_briefcase`, 54 | `weapon_briefcase_02`, 55 | `weapon_garbagebag`, 56 | `weapon_handcuffs`, 57 | `weapon_bread`, 58 | `weapon_stone_hatchet`, 59 | -- Throwables 60 | `weapon_grenade`, 61 | `weapon_bzgas`, 62 | `weapon_molotov`, 63 | `weapon_stickybomb`, 64 | `weapon_proxmine`, 65 | `weapon_snowball`, 66 | `weapon_pipebomb`, 67 | `weapon_ball`, 68 | `weapon_smokegrenade`, 69 | `weapon_flare`, 70 | }, 71 | } -------------------------------------------------------------------------------- /config/server.lua: -------------------------------------------------------------------------------- 1 | return { 2 | stress = { 3 | disableForLEO = true, -- If true, it will disable stress for people in the leo job type 4 | }, 5 | } -------------------------------------------------------------------------------- /config/shared.lua: -------------------------------------------------------------------------------- 1 | return { 2 | stress = { 3 | enableStress = true, -- If false, it will disable stress for everyone 4 | }, 5 | 6 | menu = { -- Don't touch 7 | isOutMapChecked = false, -- isOutMapChecked 8 | isOutCompassChecked = false, -- isOutMapChecked 9 | isCompassFollowChecked = true, -- isCompassFollowChecked 10 | isMapNotifChecked = true, -- isMapNotifChecked 11 | isLowFuelChecked = true, -- isLowFuelChecked 12 | isCinematicNotifChecked = true, -- isCinematicNotifChecked 13 | isDynamicHealthChecked = true, -- isDynamicHealthChecked 14 | isDynamicArmorChecked= true, -- isDynamicArmorChecked 15 | isDynamicHungerChecked = true, -- isDynamicHungerChecked 16 | isDynamicThirstChecked = true, -- isDynamicThirstChecked 17 | isDynamicStressChecked = true, -- isDynamicStressChecked 18 | isDynamicOxygenChecked = true, -- isDynamicOxygenChecked 19 | isChangeFPSChecked = true, -- isChangeFPSChecked 20 | isHideMapChecked = false, -- isHideMapChecked 21 | isToggleMapBordersChecked = true, -- isToggleMapBordersChecked 22 | isDynamicEngineChecked = true, -- isDynamicEngineChecked 23 | isDynamicNitroChecked = true, -- isDynamicNitroChecked 24 | isChangeCompassFPSChecked = true, -- isChangeCompassFPSChecked 25 | isCompassShowChecked = true, -- isShowCompassChecked 26 | isShowStreetsChecked = true, -- isShowStreetsChecked 27 | isPointerShowChecked = true, -- isPointerShowChecked 28 | isDegreesShowChecked = true, -- isDegreesShowChecked 29 | isCineamticModeChecked = false, -- isCineamticModeChecked 30 | isToggleMapShapeChecked = 'square', -- isToggleMapShapeChecked 31 | } 32 | } -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version 'cerulean' 2 | game 'gta5' 3 | 4 | description 'HUD for Qbox' 5 | repository 'https://github.com/Qbox-project/qbx_hud' 6 | version '0.1.0' 7 | 8 | ox_lib 'locale' 9 | 10 | shared_scripts { 11 | '@ox_lib/init.lua', 12 | '@qbx_core/modules/lib.lua', 13 | } 14 | 15 | client_scripts { 16 | '@qbx_core/modules/playerdata.lua', 17 | 'client/main.lua', 18 | } 19 | 20 | server_script 'server/main.lua' 21 | 22 | ui_page 'html/index.html' 23 | 24 | files { 25 | 'html/*', 26 | 'html/index.html', 27 | 'html/styles.css', 28 | 'html/responsive.css', 29 | 'html/app.js', 30 | 'locales/*.json', 31 | 'config/client.lua', 32 | 'config/shared.lua', 33 | } 34 | 35 | lua54 'yes' 36 | use_experimental_fxv2_oal 'yes' 37 | -------------------------------------------------------------------------------- /html/app.js: -------------------------------------------------------------------------------- 1 | const { ref, onBeforeUnmount } = Vue 2 | 3 | const app = Vue.createApp({ 4 | data: function() { 5 | return { 6 | isOutMapChecked: this.initIsOutMapChecked(), 7 | isOutCompassChecked: this.initIsOutCompassChecked(), 8 | isCompassFollowChecked: this.initIsCompassFollowChecked(), 9 | isMapNotifChecked: this.initIsMapNotifChecked(), 10 | isLowFuelChecked: this.initIsLowFuelChecked(), 11 | isCinematicNotifChecked: this.initIsCinematicNotifChecked(), 12 | isDynamicHealthChecked: this.initIsDynamicHealthChecked(), 13 | isDynamicArmorChecked: this.initIsDynamicArmorChecked(), 14 | isDynamicHungerChecked: this.initIsDynamicHungerChecked(), 15 | isDynamicThirstChecked: this.initIsDynamicThirstChecked(), 16 | isDynamicStressChecked: this.initIsDynamicStressChecked(), 17 | isDynamicOxygenChecked: this.initIsDynamicOxygenChecked(), 18 | isChangeFPSChecked: this.initIsChangeFPSChecked(), 19 | isToggleMapShapeChecked: this.initIsToggleMapShapeChecked(), 20 | isHideMapChecked: this.initIsHideMapChecked(), 21 | isToggleMapBordersChecked: this.initIsToggleMapBordersChecked(), 22 | isDynamicEngineChecked: this.initIsDynamicEngineChecked(), 23 | isDynamicNitroChecked: this.initIsDynamicNitroChecked(), 24 | isChangeCompassFPSChecked: this.initIsChangeCompassFPSChecked(), 25 | isShowCompassChecked: this.initIsShowCompassChecked(), 26 | isShowStreetsChecked: this.initIsShowStreetsChecked(), 27 | isPointerShowChecked: this.initIsPointerShowChecked(), 28 | isDegreesShowChecked: this.initIsDegreesShowChecked(), 29 | isCineamticModeChecked: this.initIsCineamticModeChecked(), 30 | }; 31 | }, 32 | setup () { 33 | const progress = ref([ 34 | { loading: false, percentage: 0 }, 35 | { loading: false, percentage: 0 }, 36 | { loading: false, percentage: 0 } 37 | ]) 38 | 39 | const intervals = [ null, null, null ] 40 | 41 | function startComputing (id) { 42 | progress.value[ id ].loading = true 43 | progress.value[ id ].percentage = 0 44 | 45 | intervals[ id ] = setInterval(() => { 46 | progress.value[ id ].percentage += Math.floor(Math.random() * 8 + 10) 47 | if (progress.value[ id ].percentage >= 100) { 48 | clearInterval(intervals[ id ]) 49 | progress.value[ id ].loading = false 50 | } 51 | }, 700) 52 | } 53 | 54 | onBeforeUnmount(() => { 55 | intervals.forEach(val => { 56 | clearInterval(val) 57 | }) 58 | }) 59 | return { 60 | framework: { 61 | plugins: [ 62 | 'LocalStorage', 63 | 'SessionStorage' 64 | ] 65 | }, 66 | tab: ref('hud'), 67 | splitterModel: ref(20), 68 | selection: ref([]), 69 | progress, 70 | startComputing, 71 | } 72 | }, 73 | watch: { 74 | isOutMapChecked: function() { 75 | localStorage.setItem("isOutMapChecked", this.isOutMapChecked); 76 | }, 77 | isOutCompassChecked: function() { 78 | localStorage.setItem("isOutCompassChecked", this.isOutCompassChecked); 79 | }, 80 | isCompassFollowChecked: function() { 81 | localStorage.setItem("isCompassFollowChecked", this.isCompassFollowChecked); 82 | }, 83 | isMapNotifChecked: function() { 84 | localStorage.setItem("isMapNotifChecked", this.isMapNotifChecked); 85 | }, 86 | isLowFuelChecked: function() { 87 | localStorage.setItem("isLowFuelChecked", this.isLowFuelChecked); 88 | }, 89 | isCinematicNotifChecked: function() { 90 | localStorage.setItem("isCinematicNotifChecked", this.isCinematicNotifChecked); 91 | }, 92 | isDynamicHealthChecked: function() { 93 | localStorage.setItem("isDynamicHealthChecked", this.isDynamicHealthChecked); 94 | }, 95 | isDynamicArmorChecked: function() { 96 | localStorage.setItem("isDynamicArmorChecked", this.isDynamicArmorChecked); 97 | }, 98 | isDynamicHungerChecked: function() { 99 | localStorage.setItem("isDynamicHungerChecked", this.isDynamicHungerChecked); 100 | }, 101 | isDynamicThirstChecked: function() { 102 | localStorage.setItem("isDynamicThirstChecked", this.isDynamicThirstChecked); 103 | }, 104 | isDynamicStressChecked: function() { 105 | localStorage.setItem("isDynamicStressChecked", this.isDynamicStressChecked); 106 | }, 107 | isDynamicOxygenChecked: function() { 108 | localStorage.setItem("isDynamicOxygenChecked", this.isDynamicOxygenChecked); 109 | }, 110 | isChangeFPSChecked: function() { 111 | localStorage.setItem("isChangeFPSChecked", this.isChangeFPSChecked); 112 | }, 113 | isToggleMapShapeChecked: function() { 114 | localStorage.setItem("isToggleMapShapeChecked", this.isToggleMapShapeChecked); 115 | }, 116 | isHideMapChecked: function() { 117 | localStorage.setItem("isHideMapChecked", this.isHideMapChecked); 118 | }, 119 | isToggleMapBordersChecked: function() { 120 | localStorage.setItem("isToggleMapBordersChecked", this.isToggleMapBordersChecked); 121 | }, 122 | isDynamicEngineChecked: function() { 123 | localStorage.setItem("isDynamicEngineChecked", this.isDynamicEngineChecked); 124 | }, 125 | isDynamicNitroChecked: function() { 126 | localStorage.setItem("isDynamicNitroChecked", this.isDynamicNitroChecked); 127 | }, 128 | isChangeCompassFPSChecked: function() { 129 | localStorage.setItem("isChangeCompassFPSChecked", this.isChangeCompassFPSChecked); 130 | }, 131 | isShowCompassChecked: function() { 132 | localStorage.setItem("isShowCompassChecked", this.isShowCompassChecked); 133 | }, 134 | isShowStreetsChecked: function() { 135 | localStorage.setItem("isShowStreetsChecked", this.isShowStreetsChecked); 136 | }, 137 | isPointerShowChecked: function() { 138 | localStorage.setItem("isPointerShowChecked", this.isPointerShowChecked); 139 | }, 140 | isDegreesShowChecked: function() { 141 | localStorage.setItem("isDegreesShowChecked", this.isDegreesShowChecked); 142 | }, 143 | isCineamticModeChecked: function() { 144 | localStorage.setItem("isCineamticModeChecked", this.isCineamticModeChecked); 145 | }, 146 | }, 147 | methods: { 148 | initIsOutMapChecked: function() { 149 | const stored = localStorage.getItem("isOutMapChecked"); 150 | if (stored === null) { 151 | return true; 152 | } else { 153 | return stored == 'true'; 154 | } 155 | }, 156 | initIsOutCompassChecked: function() { 157 | const stored = localStorage.getItem("isOutCompassChecked"); 158 | if (stored === null) { 159 | return true; 160 | } else { 161 | return stored == 'true'; 162 | } 163 | }, 164 | initIsCompassFollowChecked: function() { 165 | const stored = localStorage.getItem("isCompassFollowChecked"); 166 | if (stored === null) { 167 | return true; 168 | } else { 169 | return stored == 'true'; 170 | } 171 | }, 172 | initIsMapNotifChecked: function() { 173 | const stored = localStorage.getItem("isMapNotifChecked"); 174 | if (stored === null) { 175 | return true; 176 | } else { 177 | return stored == 'true'; 178 | } 179 | }, 180 | initIsLowFuelChecked: function() { 181 | const stored = localStorage.getItem("isLowFuelChecked"); 182 | if (stored === null) { 183 | return true; 184 | } else { 185 | return stored == 'true'; 186 | } 187 | }, 188 | initIsCinematicNotifChecked: function() { 189 | const stored = localStorage.getItem("isCinematicNotifChecked"); 190 | if (stored === null) { 191 | return true; 192 | } else { 193 | return stored == 'true'; 194 | } 195 | }, 196 | initIsDynamicHealthChecked: function() { 197 | const stored = localStorage.getItem("isDynamicHealthChecked"); 198 | if (stored === null) { 199 | return false; 200 | } else { 201 | return stored == 'true'; 202 | } 203 | }, 204 | initIsDynamicArmorChecked: function() { 205 | const stored = localStorage.getItem("isDynamicArmorChecked"); 206 | if (stored === null) { 207 | return false; 208 | } else { 209 | return stored == 'true'; 210 | } 211 | }, 212 | initIsDynamicHungerChecked: function() { 213 | const stored = localStorage.getItem("isDynamicHungerChecked"); 214 | if (stored === null) { 215 | return false; 216 | } else { 217 | return stored == 'true'; 218 | } 219 | }, 220 | initIsDynamicThirstChecked: function() { 221 | const stored = localStorage.getItem("isDynamicThirstChecked"); 222 | if (stored === null) { 223 | return false; 224 | } else { 225 | return stored == 'true'; 226 | } 227 | }, 228 | initIsDynamicStressChecked: function() { 229 | const stored = localStorage.getItem("isDynamicStressChecked"); 230 | if (stored === null) { 231 | return false; 232 | } else { 233 | return stored == 'true'; 234 | } 235 | }, 236 | initIsDynamicOxygenChecked: function() { 237 | const stored = localStorage.getItem("isDynamicOxygenChecked"); 238 | if (stored === null) { 239 | return false; 240 | } else { 241 | return stored == 'true'; 242 | } 243 | }, 244 | initIsChangeFPSChecked: function() { 245 | const stored = localStorage.getItem("isChangeFPSChecked"); 246 | if (stored === null) { 247 | return 'Optimized'; 248 | } else { 249 | return stored; 250 | } 251 | }, 252 | initIsToggleMapShapeChecked: function() { 253 | const stored = localStorage.getItem("isToggleMapShapeChecked"); 254 | if (stored === null) { 255 | return 'Circle'; 256 | } else { 257 | return stored; 258 | } 259 | }, 260 | initIsHideMapChecked: function() { 261 | const stored = localStorage.getItem("isHideMapChecked"); 262 | if (stored === null) { 263 | return true; 264 | } else { 265 | return stored == 'true'; 266 | } 267 | }, 268 | initIsToggleMapBordersChecked: function() { 269 | const stored = localStorage.getItem("isToggleMapBordersChecked"); 270 | if (stored === null) { 271 | return true; 272 | } else { 273 | return stored == 'true'; 274 | } 275 | }, 276 | initIsDynamicEngineChecked: function() { 277 | const stored = localStorage.getItem("isDynamicEngineChecked"); 278 | if (stored === null) { 279 | return false; 280 | } else { 281 | return stored == 'true'; 282 | } 283 | }, 284 | initIsDynamicNitroChecked: function() { 285 | const stored = localStorage.getItem("isDynamicNitroChecked"); 286 | if (stored === null) { 287 | return false; 288 | } else { 289 | return stored == 'true'; 290 | } 291 | }, 292 | initIsChangeCompassFPSChecked: function() { 293 | const stored = localStorage.getItem("isChangeCompassFPSChecked"); 294 | if (stored === null) { 295 | return 'Optimized'; 296 | } else { 297 | return stored; 298 | } 299 | }, 300 | initIsShowCompassChecked: function() { 301 | const stored = localStorage.getItem("isShowCompassChecked"); 302 | if (stored === null) { 303 | return true; 304 | } else { 305 | return stored == 'true'; 306 | } 307 | }, 308 | initIsShowStreetsChecked: function() { 309 | const stored = localStorage.getItem("isShowStreetsChecked"); 310 | if (stored === null) { 311 | return true; 312 | } else { 313 | return stored == 'true'; 314 | } 315 | }, 316 | initIsPointerShowChecked: function() { 317 | const stored = localStorage.getItem("isPointerShowChecked"); 318 | if (stored === null) { 319 | return true; 320 | } else { 321 | return stored == 'true'; 322 | } 323 | }, 324 | initIsDegreesShowChecked: function() { 325 | const stored = localStorage.getItem("isDegreesShowChecked"); 326 | if (stored === null) { 327 | return true; 328 | } else { 329 | return stored == 'true'; 330 | } 331 | }, 332 | initIsCineamticModeChecked: function() { 333 | const stored = localStorage.getItem("isCineamticModeChecked"); 334 | if (stored === null) { 335 | return false; 336 | } else { 337 | return stored == 'true'; 338 | } 339 | }, 340 | resetStorage: function(event) { 341 | targetId = event.currentTarget.id; 342 | localStorage.clear(); 343 | resetStorage() 344 | }, 345 | restartHud: function(event) { 346 | targetId = event.currentTarget.id; 347 | restartHud() 348 | }, 349 | showOutMap: function(event) { 350 | targetId = event.currentTarget.id; 351 | showOutMap() 352 | }, 353 | showOutCompass: function(event) { 354 | targetId = event.currentTarget.id; 355 | showOutCompass() 356 | }, 357 | showFollowCompass: function(event) { 358 | targetId = event.currentTarget.id; 359 | showFollowCompass() 360 | }, 361 | showMapNotif: function(event) { 362 | targetId = event.currentTarget.id; 363 | showMapNotif() 364 | }, 365 | showFuelAlert: function(event) { 366 | targetId = event.currentTarget.id; 367 | showFuelAlert() 368 | }, 369 | showCinematicNotif: function(event) { 370 | targetId = event.currentTarget.id; 371 | showCinematicNotif() 372 | }, 373 | dynamicHealth: function(event) { 374 | targetId = event.currentTarget.id; 375 | dynamicHealth() 376 | }, 377 | dynamicArmor: function(event) { 378 | targetId = event.currentTarget.id; 379 | dynamicArmor() 380 | }, 381 | dynamicHunger: function(event) { 382 | targetId = event.currentTarget.id; 383 | dynamicHunger() 384 | }, 385 | dynamicThirst: function(event) { 386 | targetId = event.currentTarget.id; 387 | dynamicThirst() 388 | }, 389 | dynamicStress: function(event) { 390 | targetId = event.currentTarget.id; 391 | dynamicStress() 392 | }, 393 | dynamicOxygen: function(event) { 394 | targetId = event.currentTarget.id; 395 | dynamicOxygen() 396 | }, 397 | changeFPS: function(event) { 398 | targetId = event.currentTarget.id; 399 | changeFPS() 400 | }, 401 | ToggleMapShape: function(event) { 402 | targetId = event.currentTarget.id; 403 | ToggleMapShape() 404 | }, 405 | HideMap: function(event) { 406 | targetId = event.currentTarget.id; 407 | HideMap() 408 | }, 409 | ToggleMapBorders: function(event) { 410 | targetId = event.currentTarget.id; 411 | ToggleMapBorders() 412 | }, 413 | dynamicEngine: function(event) { 414 | targetId = event.currentTarget.id; 415 | dynamicEngine() 416 | }, 417 | dynamicNitro: function(event) { 418 | targetId = event.currentTarget.id; 419 | dynamicNitro() 420 | }, 421 | changeCompassFPS: function(event) { 422 | targetId = event.currentTarget.id; 423 | changeCompassFPS() 424 | }, 425 | showCompassBase: function(event) { 426 | targetId = event.currentTarget.id; 427 | showCompassBase() 428 | }, 429 | showStreetsNames: function(event) { 430 | targetId = event.currentTarget.id; 431 | showStreetsNames() 432 | }, 433 | showPointerIndex: function(event) { 434 | targetId = event.currentTarget.id; 435 | showPointerIndex() 436 | }, 437 | showDegreesNum: function(event) { 438 | targetId = event.currentTarget.id; 439 | showDegreesNum() 440 | }, 441 | cinematicMode: function(event) { 442 | targetId = event.currentTarget.id; 443 | cinematicMode() 444 | }, 445 | }, 446 | mounted() { 447 | this.listener = window.addEventListener("message", (event) => { 448 | if (event.data.event === 'isToggleMapShapeChecked' || event.data.event === 'isChangeFPSChecked') { 449 | eval(`this.${event.data.event} = "${event.data.toggle}"`) 450 | } 451 | }); 452 | }, 453 | }) 454 | 455 | app.use(Quasar, { config: {} }) 456 | app.mount('#menu') 457 | 458 | document.onkeyup = function (data) { 459 | if (data.key == 'Escape') { 460 | closeMenu() 461 | } 462 | }; 463 | 464 | function closeMenu() { 465 | $("#openmenu").fadeOut(550); 466 | $.post(`https://${GetParentResourceName()}/closeMenu`); 467 | } 468 | function restartHud() { 469 | closeMenu() 470 | $.post(`https://${GetParentResourceName()}/restartHud`); 471 | } 472 | function resetStorage() { 473 | closeMenu() 474 | $.post(`https://${GetParentResourceName()}/resetStorage`); 475 | } 476 | function showOutMap() { 477 | $.post(`https://${GetParentResourceName()}/showOutMap`); 478 | } 479 | function showOutCompass() { 480 | $.post(`https://${GetParentResourceName()}/showOutCompass`); 481 | } 482 | function showFollowCompass() { 483 | $.post(`https://${GetParentResourceName()}/showFollowCompass`); 484 | } 485 | function showMapNotif() { 486 | $.post(`https://${GetParentResourceName()}/showMapNotif`); 487 | } 488 | function showFuelAlert() { 489 | $.post(`https://${GetParentResourceName()}/showFuelAlert`); 490 | } 491 | function showCinematicNotif() { 492 | $.post(`https://${GetParentResourceName()}/showCinematicNotif`); 493 | } 494 | function dynamicHealth() { 495 | $.post(`https://${GetParentResourceName()}/dynamicHealth`); 496 | } 497 | function dynamicArmor() { 498 | $.post(`https://${GetParentResourceName()}/dynamicArmor`); 499 | } 500 | function dynamicHunger() { 501 | $.post(`https://${GetParentResourceName()}/dynamicHunger`); 502 | } 503 | function dynamicThirst() { 504 | $.post(`https://${GetParentResourceName()}/dynamicThirst`); 505 | } 506 | function dynamicStress() { 507 | $.post(`https://${GetParentResourceName()}/dynamicStress`); 508 | } 509 | function dynamicOxygen() { 510 | $.post(`https://${GetParentResourceName()}/dynamicOxygen`); 511 | } 512 | function dynamicEngine() { 513 | $.post(`https://${GetParentResourceName()}/dynamicEngine`); 514 | } 515 | function dynamicNitro() { 516 | $.post(`https://${GetParentResourceName()}/dynamicNitro`); 517 | } 518 | function ToggleMapShape() { 519 | $.post(`https://${GetParentResourceName()}/ToggleMapShape`); 520 | } 521 | function changeFPS() { 522 | $.post(`https://${GetParentResourceName()}/changeFPS`); 523 | } 524 | function ToggleMapBorders() { 525 | $.post(`https://${GetParentResourceName()}/ToggleMapBorders`); 526 | } 527 | function HideMap() { 528 | $.post(`https://${GetParentResourceName()}/HideMap`); 529 | } 530 | function changeCompassFPS() { 531 | $.post(`https://${GetParentResourceName()}/changeCompassFPS`); 532 | } 533 | function showCompassBase() { 534 | $.post(`https://${GetParentResourceName()}/showCompassBase`); 535 | } 536 | function showStreetsNames() { 537 | $.post(`https://${GetParentResourceName()}/showStreetsNames`); 538 | } 539 | function showPointerIndex() { 540 | $.post(`https://${GetParentResourceName()}/showPointerIndex`); 541 | } 542 | function showDegreesNum() { 543 | $.post(`https://${GetParentResourceName()}/showDegreesNum`); 544 | } 545 | function cinematicMode() { 546 | $.post(`https://${GetParentResourceName()}/cinematicMode`); 547 | } 548 | 549 | $(document).ready(function () { 550 | window.addEventListener("message", function (event) { 551 | switch (event.data.action) { 552 | case "open": 553 | Open(event.data); 554 | break; 555 | } 556 | }); 557 | }); 558 | 559 | Open = function (data) { 560 | $("#openmenu").fadeIn(150); 561 | } 562 | $('.closeMenu').click(() => { 563 | closeMenu() 564 | }); 565 | 566 | // MONEY HUD 567 | 568 | const moneyHud = Vue.createApp({ 569 | data() { 570 | return { 571 | cash: 0, 572 | bank: 0, 573 | amount: 0, 574 | plus: false, 575 | minus: false, 576 | showCash: false, 577 | showBank: false, 578 | showUpdate: false, 579 | }; 580 | }, 581 | destroyed() { 582 | window.removeEventListener("message", this.listener); 583 | }, 584 | mounted() { 585 | this.listener = window.addEventListener("message", (event) => { 586 | switch (event.data.action) { 587 | case "showconstant": 588 | this.showConstant(event.data); 589 | break; 590 | case "updatemoney": 591 | this.update(event.data); 592 | break; 593 | case "show": 594 | this.showAccounts(event.data); 595 | break; 596 | } 597 | }); 598 | }, 599 | methods: { 600 | // CONFIGURE YOUR CURRENCY HERE 601 | // https://www.w3schools.com/tags/ref_language_codes.asp LANGUAGE CODES 602 | // https://www.w3schools.com/tags/ref_country_codes.asp COUNTRY CODES 603 | formatMoney(value) { 604 | const formatter = new Intl.NumberFormat("en-US", { 605 | style: "currency", 606 | currency: "USD", 607 | minimumFractionDigits: 0, 608 | }); 609 | return formatter.format(value); 610 | }, 611 | showConstant(data) { 612 | this.showCash = true; 613 | this.showBank = true; 614 | this.cash = data.cash; 615 | this.bank = data.bank; 616 | }, 617 | update(data) { 618 | this.showUpdate = true; 619 | this.amount = data.amount; 620 | this.bank = data.bank; 621 | this.cash = data.cash; 622 | this.minus = data.minus; 623 | this.plus = data.plus; 624 | if (data.type === "cash") { 625 | if (data.minus) { 626 | this.showCash = true; 627 | this.minus = true; 628 | setTimeout(() => (this.showUpdate = false), 1000); 629 | setTimeout(() => (this.showCash = false), 2000); 630 | } else { 631 | this.showCash = true; 632 | this.plus = true; 633 | setTimeout(() => (this.showUpdate = false), 1000); 634 | setTimeout(() => (this.showCash = false), 2000); 635 | } 636 | } 637 | if (data.type === "bank") { 638 | if (data.minus) { 639 | this.showBank = true; 640 | this.minus = true; 641 | setTimeout(() => (this.showUpdate = false), 1000); 642 | setTimeout(() => (this.showBank = false), 2000); 643 | } else { 644 | this.showBank = true; 645 | this.plus = true; 646 | setTimeout(() => (this.showUpdate = false), 1000); 647 | setTimeout(() => (this.showBank = false), 2000); 648 | } 649 | } 650 | }, 651 | showAccounts(data) { 652 | if (data.type === "cash" && !this.showCash) { 653 | this.showCash = true; 654 | this.cash = data.cash; 655 | setTimeout(() => (this.showCash = false), 3500); 656 | } else if (data.type === "bank" && !this.showBank) { 657 | this.showBank = true; 658 | this.bank = data.bank; 659 | setTimeout(() => (this.showBank = false), 3500); 660 | } 661 | }, 662 | }, 663 | }).mount("#money-container"); 664 | 665 | // PLAYER HUD 666 | 667 | const playerHud = { 668 | data() { 669 | return { 670 | dynamicHealth: 0, 671 | dynamicHunger: 0, 672 | dynamicThirst: 0, 673 | dynamicStress: 0, 674 | dynamicOxygen: 0, 675 | dynamicEngine: 0, 676 | dynamicNitro: 0, 677 | nos: 0, 678 | static: 100, 679 | health: 0, 680 | playerDead: 0, 681 | armor: 0, 682 | hunger: 0, 683 | thirst: 0, 684 | stress: 0, 685 | voice: 0, 686 | radio: 0, 687 | harness: 0, 688 | nitroActive: 0, 689 | cruise: 0, 690 | parachute: 0, 691 | oxygen: 0, 692 | hp: 0, 693 | armed: 0, 694 | speed: 0, 695 | engine: 0, 696 | cinematic: 0, 697 | dev: 0, 698 | show: false, 699 | talking: false, 700 | showVoice: true, 701 | showHealth: false, 702 | showArmor: true, 703 | showHunger: true, 704 | showThirst: true, 705 | showNos: true, 706 | showStress: true, 707 | showOxygen: false, 708 | showArmed: true, 709 | showEngine: false, 710 | showCruise: false, 711 | showHarness: false, 712 | showParachute: false, 713 | showDev: false, 714 | voiceIcon: "fas fa-microphone", 715 | talkingColor: "#FFFFFF", 716 | nosColor: "", 717 | engineColor: "", 718 | armorColor: "", 719 | hungerColor: "", 720 | healthColor: "", 721 | thirstColor: "", 722 | }; 723 | }, 724 | 725 | destroyed() { 726 | window.removeEventListener("message", this.listener); 727 | }, 728 | mounted() { 729 | this.listener = window.addEventListener("message", (event) => { 730 | if (event.data.action === "hudtick") { 731 | this.hudTick(event.data); 732 | } 733 | // else if(event.data.update) { 734 | // eval(event.data.action + "(" + event.data.show + ')') 735 | // } 736 | }); 737 | Config = {}; 738 | }, 739 | methods: { 740 | hudTick(data) { 741 | this.show = data.show; 742 | this.health = data.health; 743 | this.armor = data.armor; 744 | this.hunger = data.hunger; 745 | this.thirst = data.thirst; 746 | this.stress = data.stress; 747 | this.voice = data.voice; 748 | this.talking = data.talking; 749 | this.radio = data.radio; 750 | this.nos = data.nos; 751 | this.oxygen = data.oxygen; 752 | this.cruise = data.cruise; 753 | this.nitroActive = data.nitroActive; 754 | this.harness = data.harness; 755 | this.speed = data.speed; 756 | this.armed = data.armed; 757 | this.parachute = data.parachute; 758 | this.hp = data.hp*5; 759 | this.engine = data.engine; 760 | this.cinematic = data.cinematic; 761 | this.dev = data.dev; 762 | this.playerDead = data.playerDead; 763 | this.dynamicHealth = data.dynamicHealth; 764 | this.dynamicArmor = data.dynamicArmor; 765 | this.dynamicHunger = data.dynamicHunger; 766 | this.dynamicThirst = data.dynamicThirst; 767 | this.dynamicStress = data.dynamicStress; 768 | this.dynamicOxygen = data.dynamicOxygen; 769 | this.dynamicEngine = data.dynamicEngine; 770 | this.dynamicNitro = data.dynamicNitro; 771 | 772 | if (data.dynamicHealth == true) { 773 | if (data.health >= 100) { 774 | this.showHealth = false; } 775 | else{ 776 | this.showHealth = true; 777 | } 778 | } else if (data.dynamicHealth == false){ 779 | this.showHealth = true; 780 | } 781 | if (data.playerDead === false) { 782 | this.healthColor = "#3FA554"; 783 | } else { 784 | this.healthColor = "#ff0000"; 785 | this.health = 100; 786 | } 787 | 788 | if (data.dynamicArmor == true) { 789 | if (data.armor == 0) { 790 | this.showArmor = false; 791 | } else { 792 | this.showArmor = true; 793 | } 794 | } else if (data.dynamicArmor == false){ 795 | this.showArmor = true; 796 | } 797 | 798 | if (data.armor <= 0) { 799 | this.armorColor = "#FF0000"; 800 | } else { 801 | this.armorColor = "#326dbf"; 802 | } 803 | 804 | if (data.dynamicHunger == true) { 805 | if (data.hunger >= 100) { 806 | this.showHunger = false; } 807 | else{ 808 | this.showHunger = true; 809 | } 810 | } else if (data.dynamicHunger == false){ 811 | this.showHunger = true; 812 | } 813 | if (data.hunger >= 100) { 814 | this.hungerColor = "#dd6e14"; 815 | } else if(data.hunger <= 30){ 816 | this.hungerColor = "#ff0000"; 817 | } else{ 818 | this.hungerColor = "#dd6e14"; 819 | } 820 | 821 | if (data.dynamicThirst == true) { 822 | if (data.thirst >= 100) { 823 | this.showThirst = false; } 824 | else{ 825 | this.showThirst = true; 826 | } 827 | } else if (data.dynamicThirst == false){ 828 | this.showThirst = true; 829 | } 830 | if (data.thirst >= 100) { 831 | this.thirstColor = "#1a7cad"; 832 | } else if(data.thirst <= 30){ 833 | this.thirstColor = "#ff0000"; 834 | } else{ 835 | this.thirstColor = "#1a7cad"; 836 | } 837 | 838 | if (data.dynamicStress == true) { 839 | if (data.stress == 0) { 840 | this.showStress = false; 841 | } else { 842 | this.showStress = true; 843 | } 844 | } else if (data.dynamicStress == false){ 845 | this.showStress = true; 846 | } 847 | 848 | if (data.dynamicOxygen == true) { 849 | if (data.oxygen >= 100) { 850 | this.showOxygen = false; } 851 | else{ 852 | this.showOxygen = true; 853 | } 854 | } else if (data.dynamicOxygen == false){ 855 | this.showOxygen = true; 856 | } 857 | 858 | if (data.dynamicEngine == true) { 859 | if (data.engine >= 95) { 860 | this.showEngine = false; 861 | } else if (data.engine < 0){ 862 | this.showEngine = false;} else {this.showEngine = true;} 863 | } else if (data.dynamicEngine == false){ 864 | if (data.engine < 0) { 865 | this.showEngine = false;} else {this.showEngine = true;} 866 | } 867 | if (data.engine <= 45) { 868 | this.engineColor = "#ff0000"; 869 | } else if (data.engine <= 75 && data.engine >= 46 ) { 870 | this.engineColor = "#dd6e14"; 871 | } else if(data.engine<=100) { 872 | this.engineColor = "#3FA554"; 873 | } 874 | 875 | if (data.dynamicNitro == true) { 876 | if (data.nos === 0 || data.nos === undefined) { 877 | this.showNos = false; 878 | } else if (data.nos < 0){ 879 | this.showNos = false;} else {this.showNos = true;} 880 | } else if (data.dynamicNitro == false) { 881 | if (data.nos < 0){ 882 | this.showNos = false; 883 | } else {this.showNos = true;} 884 | } 885 | if (data.nitroActive) { 886 | this.nosColor = "#D64763"; 887 | } else { 888 | this.nosColor = "#FFFFFF"; 889 | } 890 | 891 | if (data.talking && data.radio) { 892 | this.talkingColor = "#D64763"; 893 | } else if (data.talking) { 894 | this.talkingColor = '#FFFF3E'; 895 | } else { 896 | this.talkingColor = "#FFFFFF"; 897 | } 898 | if (data.radio != 0 && data.radio != undefined) { 899 | this.voiceIcon = 'fas fa-headset'; 900 | } else if (data.radio == 0 || data.radio == undefined) { 901 | this.voiceIcon = 'fas fa-microphone'; 902 | } 903 | if (data.cruise === true) { 904 | this.cruise = 1; 905 | this.showCruise = true; 906 | } else { 907 | this.cruise = 0; 908 | this.showCruise = false; 909 | } 910 | 911 | if (data.harness === true) { 912 | this.showHarness = true; 913 | } else { 914 | this.showHarness = false; 915 | } 916 | if (data.armed === true) { 917 | this.showArmed = true; 918 | } else { 919 | this.showArmed = false; 920 | } 921 | 922 | if (data.parachute >= 0 ) { 923 | this.showParachute = true; 924 | } else { 925 | this.showParachute = false; 926 | } 927 | 928 | if (data.dev === true ) { 929 | this.showDev = true; 930 | } else { 931 | this.showDev = false; 932 | } 933 | 934 | if (data.isPaused === 1) { 935 | this.show = false; 936 | } 937 | }, 938 | }, 939 | }; 940 | const app2 = Vue.createApp(playerHud); 941 | app2.use(Quasar); 942 | app2.mount("#ui-container"); 943 | 944 | // VEHICLE HUD 945 | 946 | const vehHud = { 947 | data() { 948 | return { 949 | speedometer: 66, 950 | fuelgauge: 69, 951 | altitudegauge: 75, 952 | fuel: 0, 953 | speed: 0, 954 | seatbelt: 0, 955 | showSquareB: 0, 956 | show: false, 957 | showAltitude: true, 958 | showSeatbelt: true, 959 | showSquare: false, 960 | showCircle: false, 961 | seatbeltColor: "", 962 | }; 963 | }, 964 | 965 | destroyed() { 966 | window.removeEventListener("message", this.listener); 967 | }, 968 | mounted() { 969 | this.listener = window.addEventListener("message", (event) => { 970 | if (event.data.action === "car") { 971 | this.vehicleHud(event.data); 972 | } 973 | }); 974 | }, 975 | methods: { 976 | vehicleHud(data) { 977 | this.show = data.show; 978 | this.speed = data.speed; 979 | this.altitude = data.altitude; 980 | this.fuel = (data.fuel * 0.71); 981 | this.showSeatbelt = data.showSeatbelt; 982 | this.showAltitude = data.showAltitude; 983 | this.showSquareB = data.showSquareB; 984 | this.showCircleB = data.showCircleB; 985 | if (data.seatbelt === true) { 986 | this.seatbelt = 1; 987 | this.seatbeltColor = "transparent"; 988 | } else { 989 | this.seatbelt = 0; 990 | this.seatbeltColor = "#FF5100"; 991 | } 992 | if (data.showSeatbelt === true) { 993 | this.showSeatbelt = true; 994 | } else { 995 | this.showSeatbelt = false; 996 | } 997 | if (data.showAltitude === true) { 998 | this.showAltitude = true; 999 | } else { 1000 | this.showAltitude = false; 1001 | } 1002 | if (data.fuel <= 20) { 1003 | this.fuelColor = "#ff0000"; 1004 | } else if (data.fuel <= 30) { 1005 | this.fuelColor = "#dd6e14"; 1006 | } else { 1007 | this.fuelColor = "#FFFFFF"; 1008 | } 1009 | if (data.showSquareB === true) { 1010 | this.showSquare = true; 1011 | } else { 1012 | this.showSquare = false; 1013 | } 1014 | if (data.showCircleB === true) { 1015 | this.showCircle = true; 1016 | } else { 1017 | this.showCircle = false; 1018 | } 1019 | if (data.isPaused === 1) { 1020 | this.show = false; 1021 | } 1022 | }, 1023 | }, 1024 | }; 1025 | const app3 = Vue.createApp(vehHud); 1026 | app3.use(Quasar); 1027 | app3.mount("#veh-container"); 1028 | 1029 | // COMPASS HUD 1030 | 1031 | const baseplateHud = { 1032 | data() { 1033 | return { 1034 | show: false, 1035 | street1: "", 1036 | street2: "", 1037 | showCompass: true, 1038 | showStreets: true, 1039 | showPointer: true, 1040 | showDegrees: true, 1041 | }; 1042 | }, 1043 | destroyed() { 1044 | window.removeEventListener("message", this.listener); 1045 | }, 1046 | mounted() { 1047 | this.listener = window.addEventListener("message", (event) => { 1048 | if (event.data.action == "update") { 1049 | type = event.data.type 1050 | value = event.data.value 1051 | if (value !== undefined) { 1052 | $('.degrees').html(value); 1053 | bar = document.getElementsByTagName("svg")[0]; 1054 | bar.setAttribute("viewBox", ''+ (value - 90) + ' 0 180 5'); 1055 | heading = document.getElementsByTagName("svg")[1]; 1056 | heading.setAttribute("viewBox", ''+ (value - 90) + ' 0 180 1.5'); 1057 | } 1058 | } 1059 | if (event.data.action === "baseplate") { 1060 | this.baseplateHud(event.data); 1061 | } 1062 | }); 1063 | }, 1064 | methods: { 1065 | baseplateHud(data) { 1066 | this.show = data.show; 1067 | this.street1 = data.street1; 1068 | this.street2 = data.street2; 1069 | this.showCompass = data.showCompass; 1070 | this.showStreets = data.showStreets; 1071 | this.showPointer = data.showPointer; 1072 | this.showDegrees = data.showDegrees; 1073 | if (data.showCompass == true) { 1074 | this.showCompass = true; 1075 | } else { 1076 | this.showCompass = false; 1077 | } 1078 | if (data.showStreets == true) { 1079 | this.showStreets = true; 1080 | } else { 1081 | this.showStreets = false; 1082 | } 1083 | if (data.showPointer == true) { 1084 | this.showPointer = true; 1085 | } else { 1086 | this.showPointer = false; 1087 | } 1088 | if (data.showDegrees == true) { 1089 | this.showDegrees = true; 1090 | } else { 1091 | this.showDegrees = false; 1092 | } 1093 | }, 1094 | }, 1095 | }; 1096 | const app4 = Vue.createApp(baseplateHud); 1097 | app4.use(Quasar); 1098 | app4.mount("#baseplate-container"); 1099 | -------------------------------------------------------------------------------- /html/index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |$ {{(cash)}}
195 |$ {{(bank)}}
200 |+ {{(amount)}}
204 |- {{(amount)}}
205 |