100 | {controls.map(({ input, label, disabled }, index) => (
101 |
102 | {input && renderInputs(input, classes.span)}
103 | {label && {lang.get(label)}}
104 |
105 | ))}
106 |
107 | )
108 | }
--------------------------------------------------------------------------------
/serverside/src/managers/WeaponManager.ts:
--------------------------------------------------------------------------------
1 | import { app } from "../bootstrap"
2 | import { singleton, inject, injectable, delay } from "tsyringe"
3 | import { Config } from "../core/Config"
4 | import { NotFoundNotifyError } from "../errors/PlayerErrors"
5 |
6 | /**
7 | * Class to manage the weapons
8 | */
9 | @injectable()
10 | @singleton()
11 | class WeaponManager {
12 | static readonly MAX_AMMO: number = app.getConfig().get('MAX_AMMO') || 1000
13 | static readonly MAX_WEAPON_SLOTS: number = app.getConfig().get('MAX_WEAPON_SLOTS') || 3
14 |
15 | private weapons: any
16 | private weaponsSetCategories: string[][]
17 | public readonly weaponSet: string[][]
18 |
19 | constructor(@inject(Config) readonly config: Config) {
20 |
21 | this.weaponRequest = this.weaponRequest.bind(this)
22 |
23 | this.weapons = this.config.get("WEAPONS")
24 | this.weaponsSetCategories = this.config.get('WEAPONS_SET')
25 | this.weaponSet = this.getWeaponSet()
26 | }
27 |
28 | /**
29 | * Rpc call
30 | *
31 | * Fires when the player has made the choice
32 | * @param {string[]} choice
33 | * @param {rpc.ProcedureListenerInfo} param1
34 | */
35 | weaponRequest(choice: string[], { player }: rpc.ProcedureListenerInfo): void {
36 | if (this.isCorrectChoice(choice)) {
37 | this.giveWeapons(player as PlayerMp, choice)
38 | } else {
39 | throw new NotFoundNotifyError(SHARED.MSG.ERR_WEAPON_NOT_FOUND, player as PlayerMp)
40 | }
41 | }
42 |
43 | /**
44 | * Give the weapons to the player
45 | * @param {PlayerMp} player
46 | * @param {string[]} choice - array of weapon hashes
47 | */
48 | giveWeapons(player: PlayerMp, choice: string[]) : void {
49 | player.removeAllWeapons()
50 | choice.forEach(hash => player.giveWeapon(mp.joaat(hash), WeaponManager.MAX_AMMO))
51 | }
52 |
53 | /**
54 | * Check if the choice is correct
55 | * @param {string[]} choice - array of weapon hashes
56 | */
57 | isCorrectChoice(choice: string[]) : boolean {
58 | const list = this.getList()
59 |
60 | return choice.length <= WeaponManager.MAX_WEAPON_SLOTS && choice.every(hash => list.indexOf(hash) !== -1)
61 | }
62 |
63 | /**
64 | * Get weapon list from the config
65 | */
66 | getList(): string[] {
67 | return Object
68 | .entries(this.weapons)
69 | .reduce((carry, currentValue) => {
70 | const [_, weapons]: [any, any] = currentValue
71 |
72 | return [...carry, ...weapons] as any
73 | }, [])
74 | }
75 |
76 | /**
77 | * Set the weapon list
78 | */
79 | private getWeaponSet(): string[][] {
80 | const weaponSet: string[][] = []
81 |
82 | if (Array.isArray(this.weaponsSetCategories) && typeof this.weapons === 'object') {
83 | this.weaponsSetCategories.forEach((packs, index) => {
84 | if (weaponSet[index] === undefined) weaponSet[index] = []
85 | packs.forEach(pack => {
86 | if (Array.isArray(this.weapons[pack])) weaponSet[index] = [...weaponSet[index], ...this.weapons[pack]]
87 | })
88 | })
89 | }
90 |
91 | return weaponSet
92 | }
93 | }
94 |
95 | export { WeaponManager }
--------------------------------------------------------------------------------
/cef/src/MapEditor/MapEditor.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { makeStyles } from '@material-ui/core/styles'
3 | import Paper from '@material-ui/core/Paper'
4 | import Grid from '@material-ui/core/Grid'
5 | import Header from './Header'
6 | import Body from './Body'
7 | import Footer from './Footer'
8 | import { register, callClient } from 'rage-rpc'
9 | import { RPC_DIALOG } from '../events'
10 |
11 | const useStyles = makeStyles({
12 | root: {
13 | borderTop: '10px solid black',
14 | position: 'absolute',
15 | right: '5vmin',
16 | flexGrow: 1,
17 | padding: 15,
18 | maxWidth: 460,
19 | background: 'rgba(255 255 255 / 75%)',
20 | '&:focus': {
21 | outline: 'none',
22 | },
23 | },
24 | header: {
25 | fontWeight: 600,
26 | fontSize: 24,
27 | textDecoration: 'underline',
28 | textDecorationStyle: 'dotted',
29 | textDecorationColor: 'crimson',
30 | },
31 | title: {
32 | marginTop: 15,
33 | },
34 | list: {
35 | maxHeight: 100,
36 | overflow: 'auto',
37 | }
38 | })
39 |
40 | export interface Point {
41 | name: string
42 | coord: any
43 | }
44 | export type SpawnVector = {
45 | 'ATTACKERS': Point[]
46 | 'DEFENDERS': Point[]
47 | }
48 |
49 | export interface MapEditorState {
50 | open: boolean
51 | editing: boolean
52 | path: Point[]
53 | spawn: SpawnVector
54 | team: 'ATTACKERS' | 'DEFENDERS'
55 | mapName: string
56 | focus: boolean
57 | }
58 |
59 | export const initialMapEditorState: MapEditorState = {
60 | open: false,
61 | editing: false,
62 | path: [],
63 | spawn: {
64 | 'ATTACKERS': [],
65 | 'DEFENDERS': [],
66 | },
67 | team: 'ATTACKERS',
68 | mapName: '',
69 | focus: false,
70 | }
71 |
72 | /**
73 | * The map editor form
74 | */
75 | export default function MapEditor() {
76 | const [render, setRender] = React.useState(false)
77 | const [state, setState] = React.useState