├── .editorconfig ├── .github └── workflows │ └── glualint.yml ├── LICENSE ├── README.md ├── addon.json ├── data_static └── gminimap │ ├── gm_abandoned_ascot_mall.json │ ├── gm_abyssalplain.json │ ├── gm_aistruct.json │ ├── gm_aknevo.json │ ├── gm_amber_metro.json │ ├── gm_ame.json │ ├── gm_assault_sandbox.json │ ├── gm_bigcity.json │ ├── gm_bigcity_improved.json │ ├── gm_bigcity_improved_lite.json │ ├── gm_bigcity_winter_day.json │ ├── gm_boreas.json │ ├── gm_boreas_night.json │ ├── gm_br_complex.json │ ├── gm_br_pitfalls.json │ ├── gm_cinema_v2.json │ ├── gm_construct.json │ ├── gm_copper9.json │ ├── gm_excess_construct_13.json │ ├── gm_genesis.json │ ├── gm_susquehanna.json │ ├── gm_thebox_17.json │ ├── rp_nycity.json │ ├── rp_nycity_day.json │ ├── rp_oviscity_gmc5.json │ ├── rp_rockford_v2b.json │ ├── rp_rudmerge.json │ ├── rp_rudmerge_day.json │ ├── rp_southside.json │ └── rp_southside_day.json ├── glualint.json ├── lua ├── autorun │ └── sh_gminimap.lua ├── gminimap │ ├── client │ │ ├── blips.lua │ │ ├── config.lua │ │ ├── landmarks.lua │ │ ├── main.lua │ │ ├── npcs.lua │ │ ├── players.lua │ │ ├── radar.lua │ │ ├── utils.lua │ │ ├── vgui │ │ │ └── radar.lua │ │ └── world.lua │ └── server │ │ └── main.lua ├── includes │ └── modules │ │ ├── styled_draw_utils.lua │ │ ├── styled_theme.lua │ │ └── styled_theme_tabbed_frame.lua └── lambdaplayers │ └── extaddon │ └── client │ └── gminimap_blips.lua ├── materials └── gminimap │ ├── blips │ ├── anchor.png │ ├── boost.png │ ├── bullets.png │ ├── car.png │ ├── chat.png │ ├── chip.png │ ├── computer.png │ ├── cross.png │ ├── default.png │ ├── diamond.png │ ├── download.png │ ├── drone.png │ ├── fast_forward.png │ ├── file.png │ ├── fire.png │ ├── globe.png │ ├── gun.png │ ├── heart.png │ ├── home.png │ ├── jerry_can.png │ ├── jet.png │ ├── joystick.png │ ├── key.png │ ├── lab.png │ ├── npc_default.png │ ├── office.png │ ├── pause.png │ ├── phone.png │ ├── plane.png │ ├── play_next.png │ ├── question.png │ ├── return.png │ ├── rocket.png │ ├── satellite.png │ ├── search.png │ ├── sports_car.png │ ├── stairs.png │ ├── star.png │ ├── sword.png │ ├── tank.png │ ├── tweak.png │ └── user.png │ ├── dead.png │ ├── gminimap.png │ ├── heading.png │ └── player.png └── resource └── localization ├── en └── gminimap.properties ├── es-ES └── gminimap.properties ├── pt-br └── gminimap.properties ├── ru └── gminimap.properties ├── tr └── gminimap.properties └── uk └── gminimap.properties /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.lua] 4 | indent_style = space 5 | indent_size = 4 -------------------------------------------------------------------------------- /.github/workflows/glualint.yml: -------------------------------------------------------------------------------- 1 | name: GLuaLint 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | pull_request: 8 | branches: [ main ] 9 | 10 | # Allows running this workflow from the Actions tab 11 | workflow_dispatch: 12 | 13 | jobs: 14 | Lint: 15 | uses: FPtje/GLuaFixer/.github/workflows/glualint.yml@master 16 | with: 17 | config: "./glualint.json" 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 StyledStrike 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## GMinimap 2 | 3 | [![GLuaLint](https://github.com/StyledStrike/gmod-gminimap/actions/workflows/glualint.yml/badge.svg)](https://github.com/FPtje/GLuaFixer) 4 | [![Workshop Page](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-steam-workshop.jross.me%2F3024317004%2Fsubscriptions-text)](https://steamcommunity.com/sharedfiles/filedetails/?id=3024317004) 5 | 6 | A customizable minimap for your game HUD. 7 | 8 | - Customizable position, size, health/armor, colors and other goodies 9 | - Bookmark your favorite locations with custom blips 10 | - Shows other nearby players 11 | - Map of the terrain, with minimum impact to performance 12 | - Supports custom blips and custom icons (including direct links to images on the internet) 13 | 14 | ## For Developers 15 | 16 | ### API Functions 17 | 18 | Functions and examples for using GMinimap with Lua are available [here](https://github.com/StyledStrike/gmod-gminimap/wiki/). 19 | 20 | ### Server console variables 21 | 22 | #### `gminimap_player_blips_max_distance ` 23 | 24 | Limits how far players can see other players on the map. Set to 0 to disable player blips. 25 | 26 | #### `gminimap_npc_blips_max_distance ` 27 | 28 | Limits how far players can see NPCs on the map. Set to 0 to disable NPC blips. 29 | 30 | #### `gminimap_force_x ` 31 | 32 | Force the X position of the minimap on all players (between 0 and 1). Set to -1 to disable this. 33 | 34 | #### `gminimap_force_y ` 35 | 36 | Force the Y position of the minimap on all players (between 0 and 1). Set to -1 to disable this. 37 | 38 | #### `gminimap_force_w ` 39 | 40 | Force the width of the minimap on all players (between 0 and 1). Set to -1 to disable this. 41 | 42 | #### `gminimap_force_h ` 43 | 44 | Force the height of the minimap on all players (between 0 and 1). Set to -1 to disable this. 45 | 46 | ## Disclaimer 47 | 48 | This addon bundles some icons from [flat-icons by Game2rise](https://opengameart.org/content/flat-icons). 49 | -------------------------------------------------------------------------------- /addon.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "GMinimap", 3 | "type": "ServerContent", 4 | "tags": ["fun", "realism"], 5 | "ignore": [ 6 | ".git*", 7 | "*.txt", 8 | "*.md", 9 | "glualint.json", 10 | ".editorconfig", 11 | "LICENSE" 12 | ] 13 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_abandoned_ascot_mall.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseZoomRatio": 15, 3 | "top": 2000.0, 4 | "bottom": -10.0, 5 | "layers": [ 6 | { 7 | "bottom": -2000.0, 8 | "top": -5.0 9 | } 10 | ], 11 | "triggers": [ 12 | { 13 | "layerIndex": 1, 14 | 15 | "ax": -3944, 16 | "ay": -3324, 17 | "az": -448, 18 | 19 | "bx": 4132, 20 | "by": 7768, 21 | "bz": -8 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_abyssalplain.json: -------------------------------------------------------------------------------- 1 | { 2 | "bottom": -1000, 3 | "top": 16800 4 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_aistruct.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseZoomRatio": 10, 3 | "bottom": 100, 4 | "top": 3000 5 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_aknevo.json: -------------------------------------------------------------------------------- 1 | { 2 | "bottom": -800, 3 | "top": 8000, 4 | "layers": [ 5 | { 6 | "bottom": -5000.0, 7 | "top": -8.0 8 | } 9 | ], 10 | "triggers": [ 11 | { 12 | "layerIndex": 1, 13 | 14 | "ax": -14332, 15 | "ay": -14368, 16 | "az": -1824, 17 | 18 | "bx": 14312, 19 | "by": 14332, 20 | "bz": -8 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_amber_metro.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseZoomRatio": 10 3 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_ame.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseZoomRatio": 10 3 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_assault_sandbox.json: -------------------------------------------------------------------------------- 1 | { 2 | "top": 1000.0, 3 | "bottom": -2000.0, 4 | "layers": [ 5 | { 6 | "bottom": -2000.0, 7 | "top": -499.0 8 | } 9 | ], 10 | "triggers": [ 11 | { 12 | "layerIndex": 1, 13 | 14 | "ax": 5004, 15 | "ay": 3560, 16 | "az": -960, 17 | 18 | "bx": 6444, 19 | "by": 4960, 20 | "bz": -480 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_bigcity.json: -------------------------------------------------------------------------------- 1 | { 2 | "top": 3000.0, 3 | "bottom": -12000.0, 4 | "layers": [ 5 | { 6 | "bottom": -12000.0, 7 | "top": -11200.0 8 | }, 9 | { 10 | "bottom": -12000.0, 11 | "top": -10900.0 12 | } 13 | ], 14 | "triggers": [ 15 | { 16 | "layerIndex": 1, 17 | "ax": 5120, "ay": -6144, "az": -11292, 18 | "bx": 6560, "by": -4612, "bz": -11168 19 | }, 20 | { 21 | "layerIndex": 2, 22 | "ax": 8736, "ay": 9472, "az": -11264, 23 | "bx": 9696, "by": 11264, "bz": -10904 24 | }, 25 | { 26 | "layerIndex": 2, 27 | "ax": 8432, "ay": 9488, "az": -11264, 28 | "bx": 8736, "by": 9712, "bz": -11016 29 | }, 30 | { 31 | "layerIndex": 2, 32 | "ax": 9696, "ay": 9488, "az": -11264, 33 | "bx": 10000, "by": 9712, "bz": -11016 34 | }, 35 | { 36 | "layerIndex": 2, 37 | "ax": 10384, "ay": 784, "az": -11136, 38 | "bx": 11568, "by": 2416, "bz": -10752 39 | }, 40 | { 41 | "layerIndex": 2, 42 | "ax": 10404, "ay": 5408, "az": -11136, 43 | "bx": 12000, "by": 6880, "bz": -10896 44 | }, 45 | { 46 | "layerIndex": 2, 47 | "ax": 11408, "ay": -8928, "az": -11136, 48 | "bx": 13024, "by": -6944, "bz": -10880 49 | }, 50 | { 51 | "layerIndex": 2, 52 | "ax": -9140, "ay": 4560, "az": -11136, 53 | "bx": -8032, "by": 10496, "bz": -10944 54 | }, 55 | 56 | { 57 | "layerIndex": 2, 58 | "ax": -13120, "ay": -4108, "az": -11144, 59 | "bx": -11220, "by": -3072, "bz": -10896 60 | }, 61 | { 62 | "layerIndex": 2, 63 | "ax": -9728, "ay": -5504, "az": -11136, 64 | "bx": -7680, "by": -2720, "bz": -10944 65 | } 66 | ] 67 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_bigcity_improved.json: -------------------------------------------------------------------------------- 1 | { 2 | "top": 3000.0, 3 | "bottom": -12000.0, 4 | "layers": [ 5 | { 6 | "bottom": -12000.0, 7 | "top": -10900.0 8 | } 9 | ], 10 | "triggers": [ 11 | { 12 | "layerIndex": 1, 13 | "ax": 8736, "ay": 9472, "az": -11264, 14 | "bx": 9696, "by": 11264, "bz": -10904 15 | }, 16 | { 17 | "layerIndex": 1, 18 | "ax": 8432, "ay": 9488, "az": -11264, 19 | "bx": 8736, "by": 9712, "bz": -11016 20 | }, 21 | { 22 | "layerIndex": 1, 23 | "ax": 9696, "ay": 9488, "az": -11264, 24 | "bx": 10000, "by": 9712, "bz": -11016 25 | }, 26 | { 27 | "layerIndex": 1, 28 | "ax": 10384, "ay": 784, "az": -11136, 29 | "bx": 11568, "by": 2416, "bz": -10752 30 | }, 31 | { 32 | "layerIndex": 1, 33 | "ax": 10404, "ay": 5408, "az": -11136, 34 | "bx": 12000, "by": 6880, "bz": -10896 35 | }, 36 | { 37 | "layerIndex": 1, 38 | "ax": 11408, "ay": -8928, "az": -11136, 39 | "bx": 13024, "by": -6944, "bz": -10880 40 | }, 41 | { 42 | "layerIndex": 1, 43 | "ax": -9140, "ay": 4560, "az": -11136, 44 | "bx": -8032, "by": 10496, "bz": -10944 45 | }, 46 | 47 | { 48 | "layerIndex": 1, 49 | "ax": -13120, "ay": -4108, "az": -11144, 50 | "bx": -11220, "by": -3072, "bz": -10896 51 | }, 52 | { 53 | "layerIndex": 1, 54 | "ax": -9728, "ay": -5504, "az": -11136, 55 | "bx": -7680, "by": -2720, "bz": -10944 56 | } 57 | ] 58 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_bigcity_improved_lite.json: -------------------------------------------------------------------------------- 1 | { 2 | "top": 3000.0, 3 | "bottom": -12000.0, 4 | "layers": [ 5 | { 6 | "bottom": -12000.0, 7 | "top": -11200.0 8 | }, 9 | { 10 | "bottom": -12000.0, 11 | "top": -10900.0 12 | } 13 | ], 14 | "triggers": [ 15 | { 16 | "layerIndex": 1, 17 | "ax": 5120, "ay": -6144, "az": -11292, 18 | "bx": 6560, "by": -4612, "bz": -11168 19 | }, 20 | { 21 | "layerIndex": 2, 22 | "ax": 8736, "ay": 9472, "az": -11264, 23 | "bx": 9696, "by": 11264, "bz": -10904 24 | }, 25 | { 26 | "layerIndex": 2, 27 | "ax": 8432, "ay": 9488, "az": -11264, 28 | "bx": 8736, "by": 9712, "bz": -11016 29 | }, 30 | { 31 | "layerIndex": 2, 32 | "ax": 9696, "ay": 9488, "az": -11264, 33 | "bx": 10000, "by": 9712, "bz": -11016 34 | }, 35 | { 36 | "layerIndex": 2, 37 | "ax": 10384, "ay": 784, "az": -11136, 38 | "bx": 11568, "by": 2416, "bz": -10752 39 | }, 40 | { 41 | "layerIndex": 2, 42 | "ax": 10404, "ay": 5408, "az": -11136, 43 | "bx": 12000, "by": 6880, "bz": -10896 44 | }, 45 | { 46 | "layerIndex": 2, 47 | "ax": 11408, "ay": -8928, "az": -11136, 48 | "bx": 13024, "by": -6944, "bz": -10880 49 | }, 50 | { 51 | "layerIndex": 2, 52 | "ax": -9140, "ay": 4560, "az": -11136, 53 | "bx": -8032, "by": 10496, "bz": -10944 54 | }, 55 | 56 | { 57 | "layerIndex": 2, 58 | "ax": -13120, "ay": -4108, "az": -11144, 59 | "bx": -11220, "by": -3072, "bz": -10896 60 | }, 61 | { 62 | "layerIndex": 2, 63 | "ax": -9728, "ay": -5504, "az": -11136, 64 | "bx": -7680, "by": -2720, "bz": -10944 65 | } 66 | ] 67 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_bigcity_winter_day.json: -------------------------------------------------------------------------------- 1 | { 2 | "top": 3000.0, 3 | "bottom": -12000.0, 4 | "layers": [ 5 | { 6 | "bottom": -12000.0, 7 | "top": -11200.0 8 | }, 9 | { 10 | "bottom": -12000.0, 11 | "top": -10900.0 12 | } 13 | ], 14 | "triggers": [ 15 | { 16 | "layerIndex": 1, 17 | "ax": 5120, "ay": -6144, "az": -11292, 18 | "bx": 6560, "by": -4612, "bz": -11168 19 | }, 20 | { 21 | "layerIndex": 2, 22 | "ax": 8736, "ay": 9472, "az": -11264, 23 | "bx": 9696, "by": 11264, "bz": -10904 24 | }, 25 | { 26 | "layerIndex": 2, 27 | "ax": 8432, "ay": 9488, "az": -11264, 28 | "bx": 8736, "by": 9712, "bz": -11016 29 | }, 30 | { 31 | "layerIndex": 2, 32 | "ax": 9696, "ay": 9488, "az": -11264, 33 | "bx": 10000, "by": 9712, "bz": -11016 34 | }, 35 | { 36 | "layerIndex": 2, 37 | "ax": 10384, "ay": 784, "az": -11136, 38 | "bx": 11568, "by": 2416, "bz": -10752 39 | }, 40 | { 41 | "layerIndex": 2, 42 | "ax": 10404, "ay": 5408, "az": -11136, 43 | "bx": 12000, "by": 6880, "bz": -10896 44 | }, 45 | { 46 | "layerIndex": 2, 47 | "ax": 11408, "ay": -8928, "az": -11136, 48 | "bx": 13024, "by": -6944, "bz": -10880 49 | }, 50 | { 51 | "layerIndex": 2, 52 | "ax": -9140, "ay": 4560, "az": -11136, 53 | "bx": -8032, "by": 10496, "bz": -10944 54 | }, 55 | 56 | { 57 | "layerIndex": 2, 58 | "ax": -13120, "ay": -4108, "az": -11144, 59 | "bx": -11220, "by": -3072, "bz": -10896 60 | }, 61 | { 62 | "layerIndex": 2, 63 | "ax": -9728, "ay": -5504, "az": -11136, 64 | "bx": -7680, "by": -2720, "bz": -10944 65 | } 66 | ] 67 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_boreas.json: -------------------------------------------------------------------------------- 1 | { 2 | "bottom": -11000, 3 | "top": 5000, 4 | "layers": [ 5 | { 6 | "bottom": -9000.0, 7 | "top": -5900.0 8 | }, 9 | { 10 | "bottom": -9100.0, 11 | "top": -6800.0 12 | } 13 | ], 14 | "triggers": [ 15 | { 16 | "layerIndex": 1, 17 | "ax": -3072, "ay": 3208, "az": -6400, "bx": 1024, "by": 3840, "bz": -5964 18 | }, 19 | { 20 | "layerIndex": 1, 21 | "ax": -544, "ay": 5248, "az": -6772, "bx": 616, "by": 6160, "bz": -6112 22 | }, 23 | { 24 | "layerIndex": 1, 25 | "ax": -372, "ay": 5420, "az": -6112, "bx": 508, "by": 5880, "bz": -5200 26 | }, 27 | { 28 | "layerIndex": 1, 29 | "ax": -672, "ay": 5248, "az": -6264, "bx": -428, "by": 5488, "bz": -5188 30 | }, 31 | { 32 | "layerIndex": 1, 33 | "ax": -972, "ay": 4284, "az": -6444, "bx": -52, "by": 5248, "bz": -5320 34 | }, 35 | { 36 | "layerIndex": 1, 37 | "ax": -672, "ay": 3840, "az": -7212, "bx": 4760, "by": 6996, "bz": -5952 38 | }, 39 | { 40 | "layerIndex": 1, 41 | "ax": -1044, "ay": 2160, "az": -6732, "bx": 4760, "by": 3840, "bz": -5952 42 | }, 43 | { 44 | "layerIndex": 1, 45 | "ax": -3136, "ay": 2796, "az": -6192, "bx": -1024, "by": 3236, "bz": -5956 46 | }, 47 | { 48 | "layerIndex": 1, 49 | "ax": -16, "ay": 728, "az": -6888, "bx": 1592, "by": 2160, "bz": -6268 50 | }, 51 | { 52 | "layerIndex": 1, 53 | "ax": 692, "ay": -7420, "az": -7524, "bx": 1016, "by": 740, "bz": -6648 54 | }, 55 | { 56 | "layerIndex": 2, 57 | "ax": 760, "ay": -9400, "az": -7688, "bx": 1016, "by": -7396, "bz": -7308 58 | }, 59 | { 60 | "layerIndex": 2, 61 | "ax": 760, "ay": -9972, "az": -7736, "bx": 1004, "by": -9352, "bz": -7488 62 | }, 63 | { 64 | "layerIndex": 2, 65 | "ax": 760, "ay": -14872, "az": -8064, "bx": 1548, "by": -9924, "bz": -7564 66 | } 67 | ] 68 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_boreas_night.json: -------------------------------------------------------------------------------- 1 | { 2 | "bottom": -11000, 3 | "top": 5000, 4 | "layers": [ 5 | { 6 | "bottom": -9000.0, 7 | "top": -5900.0 8 | }, 9 | { 10 | "bottom": -9100.0, 11 | "top": -6800.0 12 | } 13 | ], 14 | "triggers": [ 15 | { 16 | "layerIndex": 1, 17 | "ax": -3072, "ay": 3208, "az": -6400, "bx": 1024, "by": 3840, "bz": -5964 18 | }, 19 | { 20 | "layerIndex": 1, 21 | "ax": -544, "ay": 5248, "az": -6772, "bx": 616, "by": 6160, "bz": -6112 22 | }, 23 | { 24 | "layerIndex": 1, 25 | "ax": -372, "ay": 5420, "az": -6112, "bx": 508, "by": 5880, "bz": -5200 26 | }, 27 | { 28 | "layerIndex": 1, 29 | "ax": -672, "ay": 5248, "az": -6264, "bx": -428, "by": 5488, "bz": -5188 30 | }, 31 | { 32 | "layerIndex": 1, 33 | "ax": -972, "ay": 4284, "az": -6444, "bx": -52, "by": 5248, "bz": -5320 34 | }, 35 | { 36 | "layerIndex": 1, 37 | "ax": -672, "ay": 3840, "az": -7212, "bx": 4760, "by": 6996, "bz": -5952 38 | }, 39 | { 40 | "layerIndex": 1, 41 | "ax": -1044, "ay": 2160, "az": -6732, "bx": 4760, "by": 3840, "bz": -5952 42 | }, 43 | { 44 | "layerIndex": 1, 45 | "ax": -3136, "ay": 2796, "az": -6192, "bx": -1024, "by": 3236, "bz": -5956 46 | }, 47 | { 48 | "layerIndex": 1, 49 | "ax": -16, "ay": 728, "az": -6888, "bx": 1592, "by": 2160, "bz": -6268 50 | }, 51 | { 52 | "layerIndex": 1, 53 | "ax": 692, "ay": -7420, "az": -7524, "bx": 1016, "by": 740, "bz": -6648 54 | }, 55 | { 56 | "layerIndex": 2, 57 | "ax": 760, "ay": -9400, "az": -7688, "bx": 1016, "by": -7396, "bz": -7308 58 | }, 59 | { 60 | "layerIndex": 2, 61 | "ax": 760, "ay": -9972, "az": -7736, "bx": 1004, "by": -9352, "bz": -7488 62 | }, 63 | { 64 | "layerIndex": 2, 65 | "ax": 760, "ay": -14872, "az": -8064, "bx": 1548, "by": -9924, "bz": -7564 66 | } 67 | ] 68 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_br_complex.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseZoomRatio": 10, 3 | "bottom": -5000, 4 | "top": 2000 5 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_br_pitfalls.json: -------------------------------------------------------------------------------- 1 | { 2 | "top": 2000, 3 | "bottom": -3000, 4 | "layers": [ 5 | { 6 | "bottom": -7000.0, 7 | "top": -2000.0 8 | } 9 | ], 10 | "triggers": [ 11 | { 12 | "layerIndex": 1, 13 | 14 | "ax": -9964, 15 | "ay": -9936, 16 | "az": -6232, 17 | 18 | "bx": 10868, 19 | "by": 10024, 20 | "bz": -3232 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_cinema_v2.json: -------------------------------------------------------------------------------- 1 | { 2 | "top": 334, 3 | "bottom": -5000, 4 | "baseZoomRatio": 10 5 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_construct.json: -------------------------------------------------------------------------------- 1 | { 2 | "bottom": -2000, 3 | "top": 3000, 4 | "baseZoomRatio": 10 5 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_copper9.json: -------------------------------------------------------------------------------- 1 | { 2 | "bottom": -2000, 3 | "top": 730 4 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_excess_construct_13.json: -------------------------------------------------------------------------------- 1 | { 2 | "bottom": -4000, 3 | "top": 3000 4 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "bottom": -9000, 3 | "top": 1000 4 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_susquehanna.json: -------------------------------------------------------------------------------- 1 | { 2 | "top": 1200.0, 3 | "bottom": -500.0, 4 | "layers": [ 5 | { 6 | "bottom": 1200.0, 7 | "top": 3500.0 8 | } 9 | ], 10 | "triggers": [ 11 | { 12 | "layerIndex": 1, 13 | "ax": -15360, "ay": -15360, "az": 1376, 14 | "bx": 15344, "by": 15324, "bz": 4480 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /data_static/gminimap/gm_thebox_17.json: -------------------------------------------------------------------------------- 1 | { 2 | "bottom": -15000, 3 | "top": -8000, 4 | "layers": [ 5 | { 6 | "bottom": 12000, 7 | "top": 16000 8 | }, 9 | { 10 | "bottom": -1600, 11 | "top": 1500 12 | }, 13 | { 14 | "bottom": -8000, 15 | "top": -5000 16 | } 17 | ], 18 | "triggers": [ 19 | { 20 | "layerIndex": 1, 21 | "ax": -16852, "ay": -17224, "az": 12328, 22 | "bx": 4352, "by": 1452, "bz": 15712 23 | }, 24 | { 25 | "layerIndex": 2, 26 | "ax": -13240, "ay": 6896, "az": -532, 27 | "bx": -7584, "by": 13236, "bz": 392 28 | }, 29 | { 30 | "layerIndex": 3, 31 | "ax": 9320, "ay": 6176, "az": -7136, 32 | "bx": 13232, "by": 8816, "bz": -5984 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /data_static/gminimap/rp_nycity.json: -------------------------------------------------------------------------------- 1 | { 2 | "bottom": -2000, 3 | "top": 8000, 4 | "layers": [ 5 | { 6 | "bottom": -4000, 7 | "top": -6 8 | } 9 | ], 10 | "triggers": [ 11 | { 12 | "layerIndex": 1, 13 | "ax": -10356, "ay": -15880, "az": -388, 14 | "bx": -4172, "by": 14740, "bz": -20 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /data_static/gminimap/rp_nycity_day.json: -------------------------------------------------------------------------------- 1 | { 2 | "bottom": -2000, 3 | "top": 8000, 4 | "layers": [ 5 | { 6 | "bottom": -4000, 7 | "top": -6 8 | } 9 | ], 10 | "triggers": [ 11 | { 12 | "layerIndex": 1, 13 | "ax": -10356, "ay": -15880, "az": -388, 14 | "bx": -4172, "by": 14740, "bz": -20 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /data_static/gminimap/rp_oviscity_gmc5.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseZoomRatio": 15, 3 | "bottom": -2000, 4 | "top": 4000 5 | } -------------------------------------------------------------------------------- /data_static/gminimap/rp_rockford_v2b.json: -------------------------------------------------------------------------------- 1 | { 2 | "bottom": -2000, 3 | "top": 3000 4 | } -------------------------------------------------------------------------------- /data_static/gminimap/rp_rudmerge.json: -------------------------------------------------------------------------------- 1 | { 2 | "bottom": -1000, 3 | "top": 6000 4 | } -------------------------------------------------------------------------------- /data_static/gminimap/rp_rudmerge_day.json: -------------------------------------------------------------------------------- 1 | { 2 | "bottom": -1000, 3 | "top": 6000 4 | } -------------------------------------------------------------------------------- /data_static/gminimap/rp_southside.json: -------------------------------------------------------------------------------- 1 | { 2 | "bottom": -1500, 3 | "top": 3000, 4 | "layers": [ 5 | { 6 | "bottom": -5000.0, 7 | "top": -143.0 8 | } 9 | ], 10 | "triggers": [ 11 | { 12 | "layerIndex": 1, 13 | "ax": -3048, "ay": 912, "az": -1100, 14 | "bx": 5708, "by": 8484, "bz": -120 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /data_static/gminimap/rp_southside_day.json: -------------------------------------------------------------------------------- 1 | { 2 | "bottom": -1500, 3 | "top": 3000, 4 | "layers": [ 5 | { 6 | "bottom": -5000.0, 7 | "top": -143.0 8 | } 9 | ], 10 | "triggers": [ 11 | { 12 | "layerIndex": 1, 13 | "ax": -3048, "ay": 912, "az": -1100, 14 | "bx": 5708, "by": 8484, "bz": -120 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /glualint.json: -------------------------------------------------------------------------------- 1 | { 2 | "lint_maxScopeDepth": 7, 3 | "lint_syntaxErrors": true, 4 | "lint_syntaxInconsistencies": true, 5 | "lint_deprecated": true, 6 | "lint_trailingWhitespace": true, 7 | "lint_whitespaceStyle": true, 8 | "lint_beginnerMistakes": true, 9 | "lint_emptyBlocks": true, 10 | "lint_shadowing": true, 11 | "lint_gotos": true, 12 | "lint_goto_identifier": true, 13 | "lint_doubleNegations": true, 14 | "lint_redundantIfStatements": true, 15 | "lint_redundantParentheses": true, 16 | "lint_duplicateTableKeys": true, 17 | "lint_profanity": true, 18 | "lint_unusedVars": true, 19 | "lint_unusedParameters": true, 20 | "lint_unusedLoopVars": true, 21 | "lint_inconsistentVariableStyle": false, 22 | "lint_spaceBeforeComma": true, 23 | "lint_spaceAfterComma": true, 24 | "lint_spaceBetweenParens": true, 25 | "lint_spaceBetweenBrackets": true, 26 | "lint_spaceBetweenBraces": true, 27 | "lint_ignoreFiles": [], 28 | 29 | "prettyprint_spaceBetweenParens": true, 30 | "prettyprint_spaceBetweenBrackets": false, 31 | "prettyprint_spaceBetweenBraces": true, 32 | "prettyprint_spaceAfterLabel": true, 33 | "prettyprint_spaceBeforeComma": false, 34 | "prettyprint_spaceAfterComma": true, 35 | "prettyprint_semicolons": false, 36 | "prettyprint_cStyle": false, 37 | "prettyprint_rejectInvalidCode": false, 38 | "prettyprint_spaceEmptyParens": false, 39 | "prettyprint_spaceEmptyBraces": false, 40 | "prettyprint_removeRedundantParens":true, 41 | "prettyprint_minimizeParens": true, 42 | "prettyprint_assumeOperatorAssociativity": true, 43 | "prettyprint_indentation": " ", 44 | "log_format": "auto" 45 | } -------------------------------------------------------------------------------- /lua/autorun/sh_gminimap.lua: -------------------------------------------------------------------------------- 1 | GMinimap = GMinimap or {} 2 | 3 | if CLIENT then 4 | GMinimap.DATA_DIR = "gminimap/" 5 | end 6 | 7 | CreateConVar( "gminimap_player_blips_max_distance", "8000", FCVAR_ARCHIVE + FCVAR_REPLICATED + FCVAR_NOTIFY, 8 | "Limits how far players can see other players on the map. Set to 0 to disable player blips.", 0, 50000 ) 9 | 10 | CreateConVar( "gminimap_npc_blips_max_distance", "3000", FCVAR_ARCHIVE + FCVAR_REPLICATED + FCVAR_NOTIFY, 11 | "Limits how far players can see NPCs on the map. Set to 0 to disable NPC blips.", 0, 50000 ) 12 | 13 | CreateConVar( "gminimap_force_x", "-1", FCVAR_ARCHIVE + FCVAR_REPLICATED + FCVAR_NOTIFY, 14 | "Force the X position of the minimap on all players. Set to -1 to disable this.", -1, 1 ) 15 | 16 | CreateConVar( "gminimap_force_y", "-1", FCVAR_ARCHIVE + FCVAR_REPLICATED + FCVAR_NOTIFY, 17 | "Force the Y position of the minimap on all players. Set to -1 to disable this.", -1, 1 ) 18 | 19 | CreateConVar( "gminimap_force_w", "-1", FCVAR_ARCHIVE + FCVAR_REPLICATED + FCVAR_NOTIFY, 20 | "Force the width of the minimap on all players. Set to -1 to disable this.", -1, 1 ) 21 | 22 | CreateConVar( "gminimap_force_h", "-1", FCVAR_ARCHIVE + FCVAR_REPLICATED + FCVAR_NOTIFY, 23 | "Force the height of the minimap on all players. Set to -1 to disable this.", -1, 1 ) 24 | 25 | function GMinimap.Print( str, ... ) 26 | MsgC( Color( 42, 180, 0 ), "[GMinimap] ", color_white, string.format( str, ... ), "\n" ) 27 | end 28 | 29 | local UP = Vector( 0, 0, 1 ) 30 | local DOWN = Vector( 0, 0, -1 ) 31 | 32 | function GMinimap.TraceLineWorld( pos, dir, dist ) 33 | return util.TraceLine( { 34 | start = pos, 35 | endpos = pos + dir * dist, 36 | filter = ents.GetAll(), 37 | mask = MASK_SOLID_BRUSHONLY, 38 | collisiongroup = COLLISION_GROUP_WORLD, 39 | ignoreworld = false 40 | } ) 41 | end 42 | 43 | function GMinimap.GetHeightsAround( pos, dist ) 44 | -- Try to find the ceiling 45 | local tr = GMinimap.TraceLineWorld( pos, UP, dist ) 46 | local top = tr.Hit and tr.HitPos[3] or pos[3] + dist 47 | 48 | -- Try to find the floor 49 | tr = GMinimap.TraceLineWorld( pos, DOWN, dist ) 50 | local bottom = tr.Hit and tr.HitPos[3] or pos[3] - dist 51 | 52 | return top, bottom 53 | end 54 | 55 | if SERVER then 56 | include( "gminimap/server/main.lua" ) 57 | 58 | AddCSLuaFile( "includes/modules/styled_draw_utils.lua" ) 59 | AddCSLuaFile( "includes/modules/styled_theme.lua" ) 60 | AddCSLuaFile( "includes/modules/styled_theme_tabbed_frame.lua" ) 61 | 62 | AddCSLuaFile( "gminimap/client/utils.lua" ) 63 | AddCSLuaFile( "gminimap/client/blips.lua" ) 64 | AddCSLuaFile( "gminimap/client/config.lua" ) 65 | AddCSLuaFile( "gminimap/client/landmarks.lua" ) 66 | AddCSLuaFile( "gminimap/client/main.lua" ) 67 | AddCSLuaFile( "gminimap/client/npcs.lua" ) 68 | AddCSLuaFile( "gminimap/client/players.lua" ) 69 | AddCSLuaFile( "gminimap/client/radar.lua" ) 70 | AddCSLuaFile( "gminimap/client/world.lua" ) 71 | 72 | AddCSLuaFile( "gminimap/client/vgui/radar.lua" ) 73 | end 74 | 75 | if CLIENT then 76 | require( "styled_draw_utils" ) 77 | require( "styled_theme" ) 78 | require( "styled_theme_tabbed_frame" ) 79 | 80 | include( "gminimap/client/utils.lua" ) 81 | include( "gminimap/client/blips.lua" ) 82 | include( "gminimap/client/config.lua" ) 83 | include( "gminimap/client/landmarks.lua" ) 84 | include( "gminimap/client/main.lua" ) 85 | include( "gminimap/client/npcs.lua" ) 86 | include( "gminimap/client/players.lua" ) 87 | include( "gminimap/client/radar.lua" ) 88 | include( "gminimap/client/world.lua" ) 89 | 90 | include( "gminimap/client/vgui/radar.lua" ) 91 | end 92 | -------------------------------------------------------------------------------- /lua/gminimap/client/blips.lua: -------------------------------------------------------------------------------- 1 | GMinimap.blips = GMinimap.blips or {} 2 | 3 | function GMinimap:FindBlipByID( id ) 4 | for _, b in ipairs( self.blips ) do 5 | if id == b.id then return b end 6 | end 7 | end 8 | 9 | function GMinimap:RemoveAllBlips() 10 | table.Empty( self.blips ) 11 | end 12 | 13 | function GMinimap:RemoveBlipById( id ) 14 | for i, b in ipairs( self.blips ) do 15 | if b.id == id then 16 | table.remove( self.blips, i ) 17 | break 18 | end 19 | end 20 | end 21 | 22 | function GMinimap:RemoveBlipByParent( ent ) 23 | for i, b in ipairs( self.blips ) do 24 | if b.parent == ent then 25 | table.remove( self.blips, i ) 26 | end 27 | end 28 | end 29 | 30 | local function CheckOptionalType( v, n, t ) 31 | if v then 32 | assert( type( v ) == t, "Blip " .. n .. " must be a " .. t .. "!" ) 33 | end 34 | end 35 | 36 | function GMinimap:AddBlip( params ) 37 | params = params or {} 38 | 39 | assert( type( params ) == "table", "Blip parameters must be a table!" ) 40 | 41 | if params.parent then 42 | assert( IsValid( params.parent ), "Blip parent is not a valid entity!" ) 43 | end 44 | 45 | CheckOptionalType( params.id, "id", "string" ) 46 | CheckOptionalType( params.position, "position", "Vector" ) 47 | CheckOptionalType( params.angle, "angle", "Angle" ) 48 | CheckOptionalType( params.scale, "scale", "number" ) 49 | 50 | CheckOptionalType( params.indicateAlt, "indicateAlt", "boolean" ) 51 | CheckOptionalType( params.indicateAng, "indicateAng", "boolean" ) 52 | CheckOptionalType( params.lockIconAng, "lockIconAng", "boolean" ) 53 | 54 | CheckOptionalType( params.icon, "icon", "string" ) 55 | CheckOptionalType( params.alpha, "alpha", "number" ) 56 | CheckOptionalType( params.color, "color", "table" ) 57 | 58 | if params.id then 59 | local b = self:FindBlipByID( params.id ) 60 | 61 | -- Update existing blip with the same ID 62 | if b then 63 | b.parent = params.parent 64 | b.position = params.position or Vector() 65 | b.angle = params.angle or Angle() 66 | b.scale = params.scale or 1 67 | 68 | b.indicateAlt = params.indicateAlt 69 | b.indicateAng = params.indicateAng 70 | b.lockIconAng = params.lockIconAng 71 | 72 | b.icon = params.icon 73 | b.alpha = params.alpha or 255 74 | b.color = params.color or Color( 255, 255, 255 ) 75 | 76 | return b, b.id 77 | end 78 | end 79 | 80 | -- Create a new blip 81 | local blip = { 82 | parent = params.parent, 83 | position = params.position or Vector(), 84 | angle = params.angle or Angle(), 85 | scale = params.scale or 1, 86 | 87 | indicateAlt = params.indicateAlt, 88 | indicateAng = params.indicateAng, 89 | lockIconAng = params.lockIconAng, 90 | 91 | icon = params.icon, 92 | alpha = params.alpha or 255, 93 | color = params.color or Color( 255, 255, 255 ) 94 | } 95 | 96 | blip.id = params.id or string.format( "%p", blip ) 97 | 98 | self.blips[#self.blips + 1] = blip 99 | 100 | return blip, blip.id 101 | end 102 | 103 | local Angle = Angle 104 | 105 | local function GetHeading( ent ) 106 | if not ent:IsPlayer() then 107 | return Angle( 0, ent:GetAngles().y, 0 ) 108 | end 109 | 110 | local yaw = 0 111 | 112 | if ent:Alive() then 113 | if ent:InVehicle() then 114 | yaw = ent:GetForward():Angle().y 115 | else 116 | yaw = ent:EyeAngles().y 117 | end 118 | end 119 | 120 | return Angle( 0, yaw, 0 ) 121 | end 122 | 123 | local ScrH = ScrH 124 | local IsValid = IsValid 125 | local RealTime = RealTime 126 | 127 | local SetMaterial = render.SetMaterial 128 | local SetColorMaterial = render.SetColorMaterial 129 | 130 | local DrawFilledCircle = SDrawUtils.DrawFilledCircle 131 | local DrawTexturedRectRotated = SDrawUtils.DrawTexturedRectRotated 132 | local URLTexturedRectRotated = SDrawUtils.URLTexturedRectRotated 133 | local Rad, Sin, Cos, Abs = math.rad, math.sin, math.cos, math.abs 134 | 135 | local matArrow = Material( "gminimap/heading.png", "smooth ignorez" ) 136 | local colorBlack = Color( 0, 0, 0, 255 ) 137 | 138 | function GMinimap:DrawBlips( radar ) 139 | local diameter = ScrH() * self.Config.blipBaseSize 140 | local radius = diameter * 0.5 141 | local altSize = diameter * 0.8 142 | local angSize = diameter * 0.7 143 | local angDist = diameter * 0.55 144 | 145 | local i = #self.blips 146 | local blink = ( RealTime() % 1 ) > 0.5 147 | local b, x, y, yaw, zdiff, rad 148 | 149 | while i > 0 do 150 | b = self.blips[i] 151 | 152 | -- Convert the blip world position to pixels relative to the radar 153 | x, y, yaw = radar:WorldToLocal( b.position, b.angle ) 154 | 155 | -- How much higher/lower is this blip relative to the radar origin? 156 | zdiff = radar.origin.z - b.position.z 157 | 158 | colorBlack.a = b.alpha 159 | b.color.a = b.alpha 160 | 161 | if b.icon then 162 | URLTexturedRectRotated( b.icon, x, y, diameter * b.scale, diameter * b.scale, b.lockIconAng and 0 or -yaw, b.color ) 163 | else 164 | local r = radius * b.scale 165 | 166 | SetColorMaterial() 167 | DrawFilledCircle( r, x, y, colorBlack ) 168 | DrawFilledCircle( r * 0.8, x, y, b.color ) 169 | end 170 | 171 | if blink and b.indicateAlt and Abs( zdiff ) > 200 then 172 | SetMaterial( matArrow ) 173 | DrawTexturedRectRotated( x, y, altSize * b.scale, altSize * b.scale, zdiff > 0 and 180 or 0, colorBlack ) 174 | end 175 | 176 | if b.indicateAng then 177 | rad = Rad( yaw + 180 ) 178 | x = x - Sin( rad ) * angDist * b.scale 179 | y = y + Cos( rad ) * angDist * b.scale 180 | 181 | SetMaterial( matArrow ) 182 | DrawTexturedRectRotated( x, y, angSize * b.scale, angSize * b.scale, -yaw, colorBlack ) 183 | end 184 | 185 | if b.parent then 186 | if IsValid( b.parent ) then 187 | b.position = b.parent:GetPos() 188 | b.angle = GetHeading( b.parent ) 189 | else 190 | self.Print( "Blip #%d no longer has a valid parent, removing...", i ) 191 | table.remove( self.blips, i ) 192 | end 193 | end 194 | 195 | i = i - 1 196 | end 197 | end 198 | -------------------------------------------------------------------------------- /lua/gminimap/client/config.lua: -------------------------------------------------------------------------------- 1 | local Config = GMinimap.Config or {} 2 | 3 | GMinimap.Config = Config 4 | 5 | function Config:Reset() 6 | self.enable = true 7 | self.toggleKey = KEY_NONE 8 | self.expandKey = KEY_N 9 | 10 | self.zoom = 1 11 | self.pivotOffset = 0.75 12 | self.lockRotation = false 13 | self.blipBaseSize = 0.015 14 | 15 | -- Sizes are relative to the screen height 16 | self.width = 0.35 17 | self.height = 0.12 18 | 19 | -- `x` and `y` are numbers between 0 and 1, and they 20 | -- take the width/height into consideration. This means that, for example, 21 | -- when y is 0.0, the top of the radar aligns with the top of the screen, whereas 22 | -- when y is 1.0, the bottom of the radar aligns with the bottom of the screen. 23 | self.x = 0.5 24 | self.y = 0.01 25 | 26 | self.borderColor = Color( 20, 20, 20, 255 ) 27 | self.borderThickness = 2 28 | 29 | -- Terrain 30 | self.terrainColor = Color( 255, 255, 255 ) 31 | self.terrainBrightness = 0 32 | self.terrainColorMult = 0.75 33 | self.terrainColorInv = 0 34 | self.terrainLighting = true 35 | 36 | -- Health/armor 37 | self.hideDefaultHealth = false 38 | self.showCustomHealth = false 39 | self.healthHeight = 10 40 | 41 | self.healthColor = Color( 78, 160, 76, 255 ) 42 | self.lowhealthColor = Color( 203, 66, 80, 255 ) 43 | self.armorColor = Color( 70, 144, 180 ) 44 | end 45 | 46 | function Config:Load() 47 | self:Reset() 48 | 49 | GMinimap.EnsureDataDir() 50 | 51 | local data = GMinimap.FromJSON( GMinimap.LoadDataFile( "config.json" ) ) 52 | local SetNumber, SetBool, SetColor = GMinimap.SetNumber, GMinimap.SetBool, GMinimap.SetColor 53 | 54 | SetBool( self, "enable", data.enable ) 55 | SetNumber( self, "toggleKey", data.toggleKey, KEY_NONE, BUTTON_CODE_LAST, self.toggleKey ) 56 | SetNumber( self, "expandKey", data.expandKey, KEY_NONE, BUTTON_CODE_LAST, self.expandKey ) 57 | SetNumber( self, "zoom", data.zoom, 0.5, 1.5, self.zoom ) 58 | SetNumber( self, "pivotOffset", data.pivotOffset, 0, 1, self.pivotOffset ) 59 | SetBool( self, "lockRotation", data.lockRotation ) 60 | 61 | SetNumber( self, "x", data.x, 0, 1, self.x ) 62 | SetNumber( self, "y", data.y, 0, 1, self.y ) 63 | SetNumber( self, "width", data.width, 0, 1, self.width ) 64 | SetNumber( self, "height", data.height, 0, 1, self.height ) 65 | 66 | SetColor( self, "borderColor", data.borderR, data.borderG, data.borderB ) 67 | SetNumber( self, "borderThickness", data.borderThickness, 0, 16, self.borderThickness ) 68 | 69 | SetColor( self, "terrainColor", data.terrainR, data.terrainG, data.terrainB ) 70 | SetNumber( self, "terrainBrightness", data.terrainBrightness, -1, 1, self.terrainBrightness ) 71 | SetNumber( self, "terrainColorMult", data.terrainColorMult, 0, 2, self.terrainColorMult ) 72 | SetNumber( self, "terrainColorInv", data.terrainColorInv, 0, 1, self.terrainColorInv ) 73 | SetBool( self, "terrainLighting", data.terrainLighting ) 74 | 75 | SetBool( self, "hideDefaultHealth", data.hideDefaultHealth ) 76 | SetBool( self, "showCustomHealth", data.showCustomHealth ) 77 | SetNumber( self, "healthHeight", data.healthHeight, 2, 32, self.healthHeight ) 78 | 79 | SetColor( self, "healthColor", data.healthR, data.healthG, data.healthB ) 80 | SetColor( self, "lowhealthColor", data.lowhealthR, data.lowhealthG, data.lowhealthB ) 81 | SetColor( self, "armorColor", data.armorR, data.armorG, data.armorB ) 82 | end 83 | 84 | function Config:Save( immediate ) 85 | if not immediate then 86 | -- Avoid spamming the file system 87 | timer.Remove( "GMinimap.SaveConfigDelay" ) 88 | timer.Create( "GMinimap.SaveConfigDelay", 0.5, 1, function() 89 | self:Save( true ) 90 | end ) 91 | 92 | return 93 | end 94 | 95 | local data = GMinimap.ToJSON( { 96 | enable = self.enable, 97 | toggleKey = self.toggleKey, 98 | expandKey = self.expandKey, 99 | 100 | zoom = self.zoom, 101 | pivotOffset = self.pivotOffset, 102 | lockRotation = self.lockRotation, 103 | 104 | x = self.x, 105 | y = self.y, 106 | width = self.width, 107 | height = self.height, 108 | 109 | borderR = self.borderColor.r, 110 | borderG = self.borderColor.g, 111 | borderB = self.borderColor.b, 112 | borderThickness = self.borderThickness, 113 | 114 | terrainR = self.terrainColor.r, 115 | terrainG = self.terrainColor.g, 116 | terrainB = self.terrainColor.b, 117 | 118 | terrainBrightness = self.terrainBrightness, 119 | terrainColorMult = self.terrainColorMult, 120 | terrainColorInv = self.terrainColorInv, 121 | terrainLighting = self.terrainLighting, 122 | 123 | hideDefaultHealth = self.hideDefaultHealth, 124 | showCustomHealth = self.showCustomHealth, 125 | healthHeight = self.healthHeight, 126 | 127 | healthR = self.healthColor.r, 128 | healthG = self.healthColor.g, 129 | healthB = self.healthColor.b, 130 | 131 | lowhealthR = self.lowhealthColor.r, 132 | lowhealthG = self.lowhealthColor.g, 133 | lowhealthB = self.lowhealthColor.b, 134 | 135 | armorR = self.armorColor.r, 136 | armorG = self.armorColor.g, 137 | armorB = self.armorColor.b 138 | }, true ) 139 | 140 | GMinimap.SaveDataFile( "config.json", data ) 141 | end 142 | 143 | Config:Load() 144 | 145 | function Config:SetupPanel( parent ) 146 | parent:Clear() 147 | 148 | local L = GMinimap.GetLanguageText 149 | 150 | local function OnChangeConfig() 151 | self:Save() 152 | hook.Run( "OnGMinimapConfigChange" ) 153 | end 154 | 155 | local IGNORE_BIND_KEYS = { 156 | [MOUSE_LEFT] = true, 157 | [MOUSE_RIGHT] = true 158 | } 159 | 160 | local function OnBindChange( binder, keyNum, configKey, title ) 161 | if IGNORE_BIND_KEYS[keyNum] then 162 | binder:SetValue( self[configKey] ) 163 | Derma_Message( L( "invalid_bind" ):format( input.GetKeyName( keyNum ) ), L( title ), L"ok" ) 164 | 165 | return 166 | end 167 | 168 | self[configKey] = keyNum 169 | self:Save() 170 | end 171 | 172 | ----- General settings 173 | StyledTheme.CreateFormHeader( parent, L"title", 0 ) 174 | 175 | -- Toggle minimap 176 | StyledTheme.CreateFormToggle( parent, L"enable", self.enable, function( checked ) 177 | self.enable = checked 178 | 179 | if checked then 180 | GMinimap:Activate() 181 | else 182 | GMinimap:Deactivate() 183 | end 184 | 185 | self:Save() 186 | end ) 187 | 188 | -- Reset settings 189 | StyledTheme.CreateFormButton( parent, L"reset", function() 190 | Derma_Query( L"reset_query", L"reset", L"yes", function() 191 | self:Reset() 192 | self:Save() 193 | 194 | GMinimap:Activate() 195 | 196 | timer.Simple( 0, function() 197 | self:SetupPanel( parent ) 198 | end ) 199 | end, L"no" ) 200 | end ) 201 | 202 | local binderToggle = StyledTheme.CreateFormBinder( parent, L"toggle_key", self.toggleKey ) 203 | binderToggle.OnChange = function( _, num ) 204 | OnBindChange( binderToggle, num, "toggleKey", "toggle_key" ) 205 | end 206 | 207 | local binderExpand = StyledTheme.CreateFormBinder( parent, L"expand_key", self.expandKey ) 208 | binderExpand.OnChange = function( _, num ) 209 | OnBindChange( binderExpand, num, "expandKey", "expand_key" ) 210 | end 211 | 212 | ----- Radar properties 213 | StyledTheme.CreateFormHeader( parent, L"radar" ) 214 | 215 | -- Lock rotation 216 | StyledTheme.CreateFormToggle( parent, L"lock_rotation", self.lockRotation, function( checked ) 217 | self.lockRotation = checked 218 | OnChangeConfig() 219 | end ) 220 | 221 | -- Radar zoom 222 | StyledTheme.CreateFormSlider( parent, L"zoom", self.zoom, 0.5, 1.5, 2, function( value ) 223 | self.zoom = value 224 | OnChangeConfig() 225 | end ) 226 | 227 | -- Radar pivot offset 228 | StyledTheme.CreateFormSlider( parent, L"pivot_offset", self.pivotOffset, 0, 1, 2, function( value ) 229 | self.pivotOffset = value 230 | OnChangeConfig() 231 | end ) 232 | 233 | -- Radar position/size 234 | local forceX = GetConVar( "gminimap_force_x" ):GetFloat() 235 | local forceY = GetConVar( "gminimap_force_y" ):GetFloat() 236 | local forceW = GetConVar( "gminimap_force_w" ):GetFloat() 237 | local forceH = GetConVar( "gminimap_force_h" ):GetFloat() 238 | 239 | if forceX >= 0 or forceY >= 0 or forceW >= 0 or forceH >= 0 then 240 | StyledTheme.CreateFormLabel( parent, L"forced_config" ):SetContentAlignment( 5 ) 241 | end 242 | 243 | StyledTheme.CreateFormSlider( parent, "X", self.x, 0, 1, 3, function( value ) 244 | self.x = value 245 | OnChangeConfig() 246 | end ):SetEnabled( forceX < 0 ) 247 | 248 | StyledTheme.CreateFormSlider( parent, "Y", self.y, 0, 1, 3, function( value ) 249 | self.y = value 250 | OnChangeConfig() 251 | end ):SetEnabled( forceY < 0 ) 252 | 253 | StyledTheme.CreateFormSlider( parent, L"width", self.width, 0.1, 0.5, 2, function( value ) 254 | self.width = value 255 | OnChangeConfig() 256 | end ):SetEnabled( forceW < 0 ) 257 | 258 | StyledTheme.CreateFormSlider( parent, L"height", self.height, 0.1, 0.5, 2, function( value ) 259 | self.height = value 260 | OnChangeConfig() 261 | end ):SetEnabled( forceH < 0 ) 262 | 263 | -- Radar border 264 | StyledTheme.CreateFormSlider( parent, L"border_thickness", self.borderThickness, 0, 16, 0, function( value ) 265 | self.borderThickness = value 266 | OnChangeConfig() 267 | end ) 268 | 269 | StyledTheme.CreateFormLabel( parent, L"border_color" ) 270 | 271 | GMinimap.CreateColorPicker( parent, self.borderColor, function( color ) 272 | self.borderColor = color 273 | OnChangeConfig() 274 | end ) 275 | 276 | ----- Terrain properties 277 | StyledTheme.CreateFormHeader( parent, L"terrain" ) 278 | 279 | -- Terrain lighting 280 | StyledTheme.CreateFormToggle( parent, L"terrain_lighting", self.terrainLighting, function( checked ) 281 | self.terrainLighting = checked 282 | OnChangeConfig() 283 | end ) 284 | 285 | -- Terrain brightness 286 | StyledTheme.CreateFormSlider( parent, L"terrain_brightness", self.terrainBrightness, -1, 1, 1, function( value ) 287 | self.terrainBrightness = value 288 | OnChangeConfig() 289 | end ) 290 | 291 | -- Terrain saturation 292 | StyledTheme.CreateFormSlider( parent, L"terrain_saturation", self.terrainColorMult, 0, 2, 1, function( value ) 293 | self.terrainColorMult = value 294 | OnChangeConfig() 295 | end ) 296 | 297 | -- Terrain color inversion 298 | StyledTheme.CreateFormSlider( parent, L"terrain_color_inv", self.terrainColorInv, 0, 1, 1, function( value ) 299 | self.terrainColorInv = value 300 | OnChangeConfig() 301 | end ) 302 | 303 | -- Terrain tint 304 | StyledTheme.CreateFormLabel( parent, L"terrain_color" ) 305 | 306 | GMinimap.CreateColorPicker( parent, self.terrainColor, function( color ) 307 | self.terrainColor = color 308 | OnChangeConfig() 309 | end ) 310 | 311 | ----- Health/armor properties 312 | StyledTheme.CreateFormHeader( parent, L"health_armor" ) 313 | 314 | -- Toggle custom health/armor 315 | StyledTheme.CreateFormToggle( parent, L"health_show_custom", self.showCustomHealth, function( checked ) 316 | self.showCustomHealth = checked 317 | OnChangeConfig() 318 | end ) 319 | 320 | -- Toggle hide default health/armor 321 | StyledTheme.CreateFormToggle( parent, L"health_hide_default", self.hideDefaultHealth, function( checked ) 322 | self.hideDefaultHealth = checked 323 | OnChangeConfig() 324 | end ) 325 | 326 | -- Health/armor height 327 | StyledTheme.CreateFormSlider( parent, L"health_height", self.healthHeight, 2, 32, 0, function( value ) 328 | self.healthHeight = value 329 | OnChangeConfig() 330 | end ) 331 | 332 | -- Health color 333 | StyledTheme.CreateFormLabel( parent, L"health_color" ) 334 | 335 | GMinimap.CreateColorPicker( parent, self.healthColor, function( color ) 336 | self.healthColor = color 337 | OnChangeConfig() 338 | end ) 339 | 340 | -- Low health color 341 | StyledTheme.CreateFormLabel( parent, L"low_health_color" ) 342 | 343 | GMinimap.CreateColorPicker( parent, self.lowhealthColor, function( color ) 344 | self.lowhealthColor = color 345 | OnChangeConfig() 346 | end ) 347 | 348 | -- Armor color 349 | StyledTheme.CreateFormLabel( parent, L"armor_color" ) 350 | 351 | GMinimap.CreateColorPicker( parent, self.armorColor, function( color ) 352 | self.armorColor = color 353 | OnChangeConfig() 354 | end ) 355 | end 356 | -------------------------------------------------------------------------------- /lua/gminimap/client/landmarks.lua: -------------------------------------------------------------------------------- 1 | local Landmarks = GMinimap.Landmarks or {} 2 | 3 | GMinimap.Landmarks = Landmarks 4 | 5 | Landmarks.items = Landmarks.items or {} 6 | Landmarks.blips = Landmarks.blips or {} 7 | 8 | function Landmarks:GetSaveFileName() 9 | return string.format( "landmarks_%s.json", game.GetMap() ) 10 | end 11 | 12 | function Landmarks:Save( immediate ) 13 | if not immediate then 14 | -- Avoid spamming the file system 15 | timer.Remove( "GMinimap.SaveLandmarksDelay" ) 16 | timer.Create( "GMinimap.SaveLandmarksDelay", 0.5, 1, function() 17 | self:Save( true ) 18 | end ) 19 | 20 | return 21 | end 22 | 23 | local data = GMinimap.ToJSON( self.items, true ) 24 | GMinimap.SaveDataFile( self:GetSaveFileName(), data ) 25 | end 26 | 27 | function Landmarks:Load() 28 | GMinimap.EnsureDataDir() 29 | 30 | local rawData = GMinimap.LoadDataFile( self:GetSaveFileName() ) 31 | if not rawData then return end 32 | 33 | local data = GMinimap.FromJSON( rawData ) 34 | if not table.IsSequential( data ) then return end 35 | 36 | table.Empty( self.items ) 37 | 38 | local SetNumber = GMinimap.SetNumber 39 | 40 | for i, v in ipairs( data ) do 41 | local landmark = {} 42 | 43 | if isstring( v.label ) then 44 | landmark.label = v.label 45 | end 46 | 47 | if isstring( v.icon ) then 48 | landmark.icon = v.icon 49 | end 50 | 51 | SetNumber( landmark, "x", v.x, -50000, 50000, 0 ) 52 | SetNumber( landmark, "y", v.y, -50000, 50000, 0 ) 53 | SetNumber( landmark, "scale", v.scale, 0.5, 3, 1 ) 54 | 55 | SetNumber( landmark, "r", v.r, 0, 255, 255 ) 56 | SetNumber( landmark, "g", v.g, 0, 255, 255 ) 57 | SetNumber( landmark, "b", v.b, 0, 255, 255 ) 58 | 59 | self.items[i] = landmark 60 | end 61 | 62 | self:Update() 63 | end 64 | 65 | function Landmarks:Update() 66 | for i, _ in ipairs( self.blips ) do 67 | GMinimap:RemoveBlipById( "gminimap_landmark_" .. i ) 68 | end 69 | 70 | for i, l in ipairs( self.items ) do 71 | self.blips[i] = GMinimap:AddBlip( { 72 | id = "gminimap_landmark_" .. i, 73 | icon = l.icon, 74 | color = Color( l.r, l.g, l.b ), 75 | lockIconAng = true, 76 | position = Vector( l.x, l.y, 0 ), 77 | scale = l.scale 78 | } ) 79 | end 80 | end 81 | 82 | function Landmarks:SetupPanel( parent ) 83 | parent:Clear() 84 | 85 | local L = GMinimap.GetLanguageText 86 | local ApplyTheme = StyledTheme.Apply 87 | local ScaleSize = StyledTheme.ScaleSize 88 | 89 | local radar = vgui.Create( "GMinimap_Radar", parent ) 90 | radar:SetOrigin( LocalPlayer():GetPos() ) 91 | radar:SetRatio( GMinimap.World.baseZoomRatio ) 92 | radar:AddZoomSlider() 93 | radar:Dock( FILL ) 94 | 95 | local helpLabel1 = vgui.Create( "DLabel", radar ) 96 | helpLabel1:SetText( "#gminimap.landmarks_help2" ) 97 | ApplyTheme( helpLabel1 ) 98 | helpLabel1:SizeToContents() 99 | 100 | local helpLabel2 = vgui.Create( "DLabel", radar ) 101 | helpLabel2:SetText( "#gminimap.landmarks_help1" ) 102 | ApplyTheme( helpLabel2 ) 103 | helpLabel2:SizeToContents() 104 | 105 | local OriginalPerformLayout = radar.PerformLayout 106 | 107 | radar.PerformLayout = function( s, w, h ) 108 | OriginalPerformLayout( s, w, h ) 109 | 110 | local spacing = ScaleSize( 6 ) 111 | 112 | helpLabel1:SetWide( w ) 113 | helpLabel2:SetWide( w ) 114 | helpLabel1:SetPos( spacing, spacing ) 115 | helpLabel2:SetPos( spacing, helpLabel1:GetTall() + spacing * 2 ) 116 | end 117 | 118 | local function OnLandmarksChanged() 119 | self:Save() 120 | self:Update() 121 | end 122 | 123 | local SetEditingLine, StopEditing 124 | 125 | ----- Landmarks list ----- 126 | local landmarksPanel = vgui.Create( "DPanel", parent ) 127 | landmarksPanel:SetWide( ScaleSize( 240 ) ) 128 | landmarksPanel:Dock( LEFT ) 129 | landmarksPanel:DockMargin( 0, 0, ScaleSize( 4 ), 0 ) 130 | ApplyTheme( landmarksPanel ) 131 | 132 | -- Landmarks header 133 | local landmarksHeader = vgui.Create( "DLabel", landmarksPanel ) 134 | landmarksHeader:SetText( game.GetMap() ) 135 | landmarksHeader:SetContentAlignment( 5 ) 136 | landmarksHeader:Dock( TOP ) 137 | landmarksHeader:DockMargin( 0, ScaleSize( 6 ), 0, ScaleSize( 6 ) ) 138 | ApplyTheme( landmarksHeader ) 139 | landmarksHeader:SizeToContents() 140 | 141 | -- Landmarks list 142 | local landmarkList = vgui.Create( "DScrollPanel", landmarksPanel ) 143 | landmarkList:Dock( FILL ) 144 | ApplyTheme( landmarkList ) 145 | 146 | -- Workaround for `URLTexturedRectRotated` not obeying panel clipping 147 | landmarkList.OriginalPaint = landmarkList.Paint 148 | landmarkList.Paint = function( s, w, h ) 149 | s:OriginalPaint( w, h ) 150 | 151 | local x, y = s:LocalToScreen( 0, 0 ) 152 | render.SetScissorRect( x, y, x + w, y + h, true ) 153 | end 154 | 155 | landmarkList.PaintOver = function() 156 | render.SetScissorRect( 0, 0, 0, 0, false ) 157 | end 158 | 159 | local function ClearSelection() 160 | for _, line in ipairs( landmarkList.pnlCanvas:GetChildren() ) do 161 | line.isChecked = false 162 | end 163 | end 164 | 165 | local function SelectLine( index, bringToView ) 166 | ClearSelection() 167 | 168 | for _, line in ipairs( landmarkList.pnlCanvas:GetChildren() ) do 169 | if line._landmarkIndex == index then 170 | SetEditingLine( line ) 171 | 172 | if bringToView then 173 | local landmark = self.items[index] 174 | radar:SetOrigin( Vector( landmark.x, landmark.y, 0 ) ) 175 | radar:SetRatio( radar.minRatio ) 176 | end 177 | 178 | return 179 | end 180 | end 181 | 182 | StopEditing() 183 | end 184 | 185 | local function GetLabel( landmark ) 186 | return landmark.label or math.Round( landmark.x, 2 ) .. " / " .. math.Round( landmark.y, 2 ) 187 | end 188 | 189 | local function PaintLine( s, w, h ) 190 | local x, y = s:LocalToScreen( 0, 0 ) 191 | local size = h * 0.8 192 | SDrawUtils.URLTexturedRectRotated( s._landmarkIcon, x + w - h * 0.5, y + h * 0.5, size, size, 0, s._landmarkColor ) 193 | end 194 | 195 | local function OnClickLine( s ) 196 | SelectLine( s._landmarkIndex, true ) 197 | end 198 | 199 | local function RemoveLandmarkByLine( s ) 200 | local index = s._landmarkIndex 201 | local str = L ( "landmark_remove_query" ) .. "\n\n" .. GetLabel( self.items[index] ) 202 | 203 | Derma_Query( str, L"landmark_remove", L"yes", function() 204 | StopEditing() 205 | table.remove( self.items, index ) 206 | 207 | landmarkList.UpdateLandmarks() 208 | OnLandmarksChanged() 209 | end, "#gminimap.no" ) 210 | end 211 | 212 | local function AddLine( index, landmark ) 213 | local line = vgui.Create( "DButton", landmarkList ) 214 | line:SetText( GetLabel( landmark ) ) 215 | line:SetContentAlignment( 4 ) 216 | line:SetTextInset( ScaleSize( 6 ), 0 ) 217 | line:Dock( TOP ) 218 | line:DockMargin( 0, 0, 0, ScaleSize( 4 ) ) 219 | 220 | ApplyTheme( line ) 221 | line:SetTall( ScaleSize( 32 ) ) 222 | 223 | line._landmarkIndex = index 224 | line._landmarkIcon = landmark.icon 225 | line._landmarkColor = Color( landmark.r, landmark.g, landmark.b ) 226 | line.isToggle = true 227 | line.isChecked = false 228 | 229 | line.PaintOver = PaintLine 230 | line.DoClick = OnClickLine 231 | line.DoRightClick = RemoveLandmarkByLine 232 | end 233 | 234 | landmarkList.UpdateLandmarks = function() 235 | StopEditing() 236 | landmarkList:Clear() 237 | 238 | for i, v in ipairs( self.items ) do 239 | AddLine( i, v ) 240 | end 241 | end 242 | 243 | radar.OnRightClickPosition = function( _, pos ) 244 | local index = #self.items + 1 245 | 246 | local landmark = { 247 | x = pos.x, 248 | y = pos.y, 249 | icon = "gminimap/blips/star.png", 250 | r = 255, 251 | g = 255, 252 | b = 255, 253 | scale = 1.5 254 | } 255 | 256 | self.items[index] = landmark 257 | 258 | ClearSelection() 259 | OnLandmarksChanged() 260 | 261 | AddLine( index, landmark ) 262 | SelectLine( index ) 263 | end 264 | 265 | ----- Landmark editor ----- 266 | local editing 267 | 268 | local function CreateMenuPanel( class, w, h ) 269 | local pnl = vgui.Create( class ) 270 | pnl:SetSize( w, h ) 271 | 272 | local m = DermaMenu() 273 | m:AddPanel( pnl ) 274 | m:SetPaintBackground( false ) 275 | m:Open( gui.MouseX() + 8, gui.MouseY() + 10 ) 276 | 277 | return pnl 278 | end 279 | 280 | local landmarkEditor = vgui.Create( "DScrollPanel", landmarksPanel ) 281 | landmarkEditor:SetTall( 0 ) 282 | landmarkEditor:Dock( BOTTOM ) 283 | landmarkEditor:DockMargin( 0, ScaleSize( 4 ), 0, 0 ) 284 | ApplyTheme( landmarkEditor ) 285 | 286 | local removeButton = vgui.Create( "DButton", landmarkEditor ) 287 | removeButton:SetText( L"landmark_remove" ) 288 | removeButton:Dock( TOP ) 289 | removeButton:DockMargin( 0, 0, 0, ScaleSize( 4 ) ) 290 | ApplyTheme( removeButton ) 291 | 292 | removeButton.DoClick = function() 293 | RemoveLandmarkByLine( editing.line ) 294 | end 295 | 296 | -- Landmark label 297 | StyledTheme.CreateFormHeader( landmarkEditor, L"landmark_label", 0 ) 298 | 299 | local editLabelEntry = vgui.Create( "DTextEntry", landmarkEditor ) 300 | editLabelEntry:Dock( TOP ) 301 | editLabelEntry:SetUpdateOnType( true ) 302 | ApplyTheme( editLabelEntry ) 303 | 304 | editLabelEntry.OnChange = function( s ) 305 | if not editing then return end 306 | 307 | local label = string.Trim( s:GetValue() ) 308 | 309 | editing.landmark.label = Either( label == "", nil, label ) 310 | editing.line:SetText( GetLabel( editing.landmark ) ) 311 | 312 | OnLandmarksChanged() 313 | end 314 | 315 | -- Landmark icon 316 | StyledTheme.CreateFormHeader( landmarkEditor, L"landmark_icon" ) 317 | 318 | local editIconPanel = vgui.Create( "DPanel", landmarkEditor ) 319 | editIconPanel:Dock( TOP ) 320 | editIconPanel:DockMargin( 0, ScaleSize( 4 ), 0, ScaleSize( 6 ) ) 321 | editIconPanel:SetPaintBackground( false ) 322 | 323 | local function OnSelectIcon( path ) 324 | if not editing then return end 325 | 326 | editing.landmark.icon = path 327 | editing.line._landmarkIcon = path 328 | 329 | OnLandmarksChanged() 330 | end 331 | 332 | local selBuiltinButton = vgui.Create( "DButton", editIconPanel ) 333 | selBuiltinButton:SetText( L"builtin_icons" ) 334 | selBuiltinButton:Dock( FILL ) 335 | ApplyTheme( selBuiltinButton ) 336 | selBuiltinButton:SetFont( "StyledTheme_Tiny" ) 337 | 338 | selBuiltinButton.DoClick = function() 339 | local iconsPanel = CreateMenuPanel( "DScrollPanel", ScaleSize( 300 ), ScaleSize( 280 ) ) 340 | iconsPanel:SetPaintBackground( true ) 341 | 342 | local spacing = ScaleSize( 6 ) 343 | 344 | local iconLayout = vgui.Create( "DIconLayout", iconsPanel ) 345 | iconLayout:SetSpaceX( spacing ) 346 | iconLayout:SetSpaceY( spacing ) 347 | iconLayout:SetBorder( spacing ) 348 | iconLayout:Dock( FILL ) 349 | 350 | local function OnClickIcon( s ) 351 | OnSelectIcon( s:GetImage() ) 352 | CloseDermaMenus() 353 | end 354 | 355 | local iconList = file.Find( "materials/gminimap/blips/*.png", "GAME" ) 356 | 357 | for _, v in ipairs( iconList ) do 358 | local btn = iconLayout:Add( "DImageButton" ) 359 | 360 | btn:SetOnViewMaterial( "gminimap/blips/" .. v ) 361 | btn:SetTooltip( btn:GetImage() ) 362 | btn:SetSize( 32, 32 ) 363 | btn:SetStretchToFit( true ) 364 | btn.DoClick = OnClickIcon 365 | end 366 | 367 | iconLayout:Layout() 368 | end 369 | 370 | local selSilkButton = vgui.Create( "DButton", editIconPanel ) 371 | selSilkButton:SetText( L"gmod_icons" ) 372 | selSilkButton:Dock( RIGHT ) 373 | selSilkButton:DockMargin( 2, 0, 0, 0 ) 374 | ApplyTheme( selSilkButton ) 375 | selSilkButton:SetFont( "StyledTheme_Tiny" ) 376 | 377 | selSilkButton.DoClick = function() 378 | local iconBrowser = CreateMenuPanel( "DIconBrowser", ScaleSize( 300 ), ScaleSize( 280 ) ) 379 | 380 | iconBrowser.OnChange = function( s ) 381 | OnSelectIcon( s:GetSelectedIcon() ) 382 | CloseDermaMenus() 383 | end 384 | end 385 | 386 | local editScaleSlider = StyledTheme.CreateFormSlider( landmarkEditor, L"scale", 1, 0.5, 2, 2, function( value ) 387 | if not editing then return end 388 | 389 | editing.landmark.scale = value 390 | OnLandmarksChanged() 391 | end ) 392 | 393 | editScaleSlider.PerformLayout = function( s ) 394 | s.Label:SizeToContents() 395 | end 396 | 397 | -- Landmark icon color 398 | StyledTheme.CreateFormHeader( landmarkEditor, L"landmark_icon_color" ) 399 | 400 | local editColor = GMinimap.CreateColorPicker( landmarkEditor, nil, function( c ) 401 | if not editing then return end 402 | 403 | editing.landmark.r = c.r 404 | editing.landmark.g = c.g 405 | editing.landmark.b = c.b 406 | editing.line._landmarkColor = Color( c.r, c.g, c.b ) 407 | 408 | OnLandmarksChanged() 409 | end ) 410 | 411 | editColor:SetTall( ScaleSize( 240 ) ) 412 | 413 | ----- Define the editing functions 414 | 415 | StopEditing = function() 416 | editing = nil 417 | landmarkEditor:SizeTo( -1, 0, 0.2, 0, 0.5 ) 418 | editLabelEntry:SetPlaceholderText( "" ) 419 | editLabelEntry:SetValue( "" ) 420 | end 421 | 422 | StopEditing() 423 | 424 | SetEditingLine = function( line ) 425 | if not line then 426 | StopEditing() 427 | return 428 | end 429 | 430 | if editing and line == editing.line then 431 | StopEditing() 432 | return 433 | end 434 | 435 | timer.Simple( editing == nil and 0.5 or 0, function() 436 | if IsValid( landmarkList ) then 437 | landmarkList:ScrollToChild( line ) 438 | end 439 | end ) 440 | 441 | landmarkEditor:SizeTo( -1, ScaleSize( 300 ), 0.2, 0, 0.3 ) 442 | line.isChecked = true 443 | 444 | editing = { 445 | line = line, 446 | landmark = self.items[line._landmarkIndex] 447 | } 448 | 449 | editLabelEntry:SetPlaceholderText( GetLabel( editing.landmark ) ) 450 | editLabelEntry:SetValue( editing.landmark.label or "" ) 451 | editScaleSlider:SetValue( editing.landmark.scale ) 452 | editColor:SetColor( Color( editing.landmark.r, editing.landmark.g, editing.landmark.b ) ) 453 | end 454 | 455 | landmarkList.UpdateLandmarks() 456 | end 457 | 458 | Landmarks:Load() 459 | -------------------------------------------------------------------------------- /lua/gminimap/client/main.lua: -------------------------------------------------------------------------------- 1 | concommand.Add( 2 | "gminimap", 3 | function() GMinimap:OpenFrame() end, 4 | nil, 5 | "Opens the GMinimap menu." 6 | ) 7 | 8 | if engine.ActiveGamemode() == "sandbox" then 9 | list.Set( 10 | "DesktopWindows", 11 | "GMinimapDesktopIcon", 12 | { 13 | title = GMinimap.GetLanguageText( "title" ), 14 | icon = "materials/gminimap/gminimap.png", 15 | init = function() GMinimap:OpenFrame() end 16 | } 17 | ) 18 | end 19 | 20 | hook.Add( "AddToolMenuCategories", "GMinimap.AddConfigCategory", function() 21 | spawnmenu.AddToolCategory( "Utilities", "GMinimap", "#gminimap.title" ) 22 | end ) 23 | 24 | hook.Add( "PopulateToolMenu", "GMinimap.AddConfigMenu", function() 25 | spawnmenu.AddToolMenuOption( "Utilities", "GMinimap", "GMinimap_Config", "#gminimap.configure", "", "", function( panel ) 26 | panel:ClearControls() 27 | panel:Button( "#gminimap.title", "gminimap" ) 28 | end ) 29 | end ) 30 | 31 | net.Receive( "gminimap.force_cvar_changed", function() 32 | GMinimap:UpdateLayout() 33 | end ) 34 | 35 | hook.Add( "OnGMinimapConfigChange", "GMinimap.Update", function() 36 | GMinimap:UpdateLayout() 37 | end ) 38 | 39 | hook.Add( "InitPostEntity", "GMinimap.Init", function() 40 | if GMinimap.Config.enable then 41 | GMinimap:Activate() 42 | end 43 | end ) 44 | 45 | function GMinimap:CloseFrame() 46 | if IsValid( self.frame ) then 47 | self.frame:Close() 48 | end 49 | end 50 | 51 | function GMinimap:OpenFrame( tabIndex ) 52 | if IsValid( self.frame ) then 53 | self:CloseFrame() 54 | return 55 | end 56 | 57 | local L = GMinimap.GetLanguageText 58 | 59 | local frame = vgui.Create( "Styled_TabbedFrame" ) 60 | frame:SetTitle( L"title" ) 61 | frame:SetIcon( "gminimap/gminimap.png" ) 62 | frame:SetSize( StyledTheme.ScaleSize( 1000 ), StyledTheme.ScaleSize( 700 ) ) 63 | frame:Center() 64 | frame:MakePopup() 65 | 66 | frame.OnClose = function() 67 | self.frame = nil 68 | end 69 | 70 | self.frame = frame 71 | 72 | local landmarksPanel = frame:AddTab( "icon16/map.png", L"landmarks", "Panel" ) 73 | local settingsPanel = frame:AddTab( "icon16/cog.png", L"configure_minimap" ) 74 | 75 | self.Landmarks:SetupPanel( landmarksPanel ) 76 | self.Config:SetupPanel( settingsPanel ) 77 | 78 | if tabIndex then 79 | frame:SetActiveTabByIndex( tabIndex ) 80 | end 81 | end 82 | 83 | --[[ 84 | All of the code below is for the built-in minimap 85 | ]] 86 | 87 | -- Enable on autorefresh, used for development. 88 | if IsValid( LocalPlayer() ) and GMinimap.Config.enable then 89 | timer.Simple( 0, function() GMinimap:Activate() end ) 90 | end 91 | 92 | function GMinimap:Activate() 93 | if self.panel then 94 | self:Deactivate() 95 | end 96 | 97 | self.panel = vgui.Create( "GMinimap_Radar" ) 98 | self.panel:ParentToHUD() 99 | self.radar = self.panel.radar 100 | 101 | self.panel.PerformLayout = function() end 102 | 103 | self.panel.Paint = function( _, w, h ) 104 | self:DrawMinimap( w, h ) 105 | end 106 | 107 | self:UpdateLayout() 108 | 109 | hook.Add( "StartChat", "GMinimap.DetectOpenChat", function() 110 | if self.isExpanded then 111 | self.isExpanded = false 112 | self:UpdateLayout() 113 | end 114 | end ) 115 | 116 | hook.Add( "Think", "GMinimap.AutoSwitchLayers", function() 117 | self.World:CheckTriggers() 118 | end ) 119 | end 120 | 121 | function GMinimap:Deactivate() 122 | hook.Remove( "StartChat", "GMinimap.DetectOpenChat" ) 123 | hook.Remove( "HUDShouldDraw", "GMinimap.HideHUDItems" ) 124 | hook.Remove( "Think", "GMinimap.AutoSwitchLayers" ) 125 | 126 | if IsValid( self.panel ) then 127 | self.panel:Remove() 128 | end 129 | 130 | self.panel = nil 131 | self.bar = nil 132 | end 133 | 134 | function GMinimap:UpdateLayout() 135 | if not self.panel then return end 136 | 137 | local config = self.Config 138 | local screenW, screenH = ScrW(), ScrH() 139 | 140 | local forceX = GetConVar( "gminimap_force_x" ):GetFloat() 141 | local forceY = GetConVar( "gminimap_force_y" ):GetFloat() 142 | 143 | local forceW = GetConVar( "gminimap_force_w" ):GetFloat() 144 | local forceH = GetConVar( "gminimap_force_h" ):GetFloat() 145 | 146 | if forceX < 0 then forceX = config.x end 147 | if forceY < 0 then forceY = config.y end 148 | 149 | if forceW < 0 then forceW = config.width end 150 | if forceH < 0 then forceH = config.height end 151 | 152 | local expandedSize = math.max( forceW, forceH ) * 1.5 153 | 154 | local w = self.isExpanded and expandedSize or forceW 155 | local h = self.isExpanded and expandedSize or forceH 156 | 157 | w = math.Round( screenH * w ) 158 | h = math.Round( screenH * h ) 159 | 160 | local x = math.Round( ( screenW * forceX ) - ( w * forceX ) ) 161 | local y = math.Round( ( screenH * forceY ) - ( h * forceY ) ) 162 | 163 | if config.showCustomHealth then 164 | self.bar = { 165 | w = ( w * 0.5 ) - self.Config.borderThickness, 166 | h = config.healthHeight, 167 | 168 | hColor = config.healthColor, 169 | hColorBg = SDrawUtils.ModifyColorBrightness( config.healthColor, 0.2 ), 170 | 171 | hlowColor = config.lowhealthColor, 172 | hlowColorBg = SDrawUtils.ModifyColorBrightness( config.lowhealthColor, 0.2 ), 173 | 174 | aColor = config.armorColor, 175 | aColorBg = SDrawUtils.ModifyColorBrightness( config.armorColor, 0.2 ), 176 | } 177 | else 178 | self.bar = nil 179 | end 180 | 181 | local baseRatio = self.World.baseZoomRatio 182 | 183 | baseRatio = baseRatio + baseRatio * ( 1 - config.zoom ) 184 | 185 | self.radar.color = config.terrainColor 186 | self.radar.ratio = Either( self.isExpanded, baseRatio, baseRatio * 0.8 ) 187 | self.radar.pivotMultY = Either( self.isExpanded, 0.5, config.pivotOffset ) 188 | 189 | local margin = self.Config.borderThickness 190 | local marginBottom = config.showCustomHealth and ( 1 + config.healthHeight + margin * 2 ) or margin * 2 191 | 192 | self.radar:SetHeights( self.World:GetHeights() ) 193 | self.radar:SetDimensions( x + margin, y + margin, w - margin * 2, h - marginBottom ) 194 | self.panel:SetPos( x, y ) 195 | self.panel:SetSize( w, h ) 196 | 197 | if config.hideDefaultHealth then 198 | local dontDraw = { 199 | ["CHudHealth"] = true, 200 | ["CHudBattery"] = true 201 | } 202 | 203 | hook.Add( "HUDShouldDraw", "GMinimap.HideHUDItems", function( name ) 204 | if dontDraw[name] then return false end 205 | end ) 206 | else 207 | hook.Remove( "HUDShouldDraw", "GMinimap.HideHUDItems" ) 208 | end 209 | end 210 | 211 | function GMinimap:OnButtonPressed( button ) 212 | if button == self.Config.toggleKey then 213 | self.Config.enable = not self.Config.enable 214 | 215 | if self.Config.enable then 216 | self:Activate() 217 | else 218 | self:Deactivate() 219 | end 220 | 221 | elseif button == self.Config.expandKey then 222 | self.isExpanded = not self.isExpanded 223 | self:UpdateLayout() 224 | end 225 | end 226 | 227 | local LocalPlayer = LocalPlayer 228 | 229 | hook.Add( "PlayerButtonDown", "GMinimap.DetectHotkeys", function( ply, button ) 230 | if IsFirstTimePredicted() and ply == LocalPlayer() then 231 | GMinimap:OnButtonPressed( button ) 232 | end 233 | end ) 234 | 235 | -- Workaround for key hooks that only run serverside on single-player 236 | if game.SinglePlayer() then 237 | net.Receive( "gminimap.key", function() 238 | GMinimap:OnButtonPressed( net.ReadUInt( 8 ) ) 239 | end ) 240 | end 241 | 242 | local IsValid = IsValid 243 | local Clamp = math.Clamp 244 | local SetColor = surface.SetDrawColor 245 | local DrawRect = surface.DrawRect 246 | 247 | function GMinimap:DrawMinimap( w, h ) 248 | SetColor( self.Config.borderColor:Unpack() ) 249 | DrawRect( 0, 0, w, h ) 250 | 251 | self.radar.origin = EyePos() 252 | self.radar.rotation = Angle( 0, self.Config.lockRotation and 0 or EyeAngles().y, 0 ) 253 | self.radar:Draw() 254 | self:DrawBlips( self.radar ) 255 | 256 | local b = self.bar 257 | if not b then return end 258 | 259 | local user = LocalPlayer() 260 | if not IsValid( user ) then return end 261 | 262 | local margin = self.Config.borderThickness 263 | local x, y = margin, h - b.h - margin 264 | 265 | -- Health bar 266 | local health = Clamp( user:Health() / user:GetMaxHealth(), 0, 1 ) 267 | local lowHealth = health < 0.35 268 | 269 | if lowHealth then 270 | b.hlowColor.a = 255 * ( 1 - math.fmod( RealTime(), 0.7 ) ) 271 | end 272 | 273 | SetColor( lowHealth and b.hlowColorBg or b.hColorBg ) 274 | DrawRect( x, y, b.w, b.h ) 275 | 276 | SetColor( lowHealth and b.hlowColor or b.hColor ) 277 | DrawRect( x, y, b.w * health, b.h ) 278 | 279 | -- Armor bar 280 | local armor = Clamp( user:Armor() / user:GetMaxArmor(), 0, 1 ) 281 | 282 | SetColor( b.aColorBg ) 283 | DrawRect( x + b.w + 1, y, b.w, b.h ) 284 | 285 | SetColor( b.aColor ) 286 | DrawRect( x + b.w + 1, y, b.w * armor, b.h ) 287 | end 288 | -------------------------------------------------------------------------------- /lua/gminimap/client/npcs.lua: -------------------------------------------------------------------------------- 1 | local SQUAD_COLORS = { 2 | ["none"] = Color( 255, 250, 150 ), 3 | ["enemy"] = Color( 255, 0, 0 ), 4 | ["ally"] = Color( 0, 110, 255 ) 5 | } 6 | 7 | local active = {} 8 | local nearby = {} 9 | 10 | local function UpdateNPCBlip( ent, alpha ) 11 | local id = "npc_" .. ent:EntIndex() 12 | nearby[id] = true 13 | 14 | if active[id] then 15 | active[id].alpha = alpha 16 | return 17 | end 18 | 19 | local color 20 | local class = ent:GetClass() 21 | 22 | if IsEnemyEntityName( class ) then 23 | color = SQUAD_COLORS.enemy 24 | 25 | elseif IsFriendEntityName( class ) then 26 | color = SQUAD_COLORS.ally 27 | end 28 | 29 | active[id] = GMinimap:AddBlip( { 30 | id = id, 31 | position = ent:GetPos(), 32 | icon = "gminimap/blips/npc_default.png", 33 | color = color or SQUAD_COLORS.none, 34 | parent = ent, 35 | scale = 0.8, 36 | indicateAlt = true, 37 | lockIconAng = true, 38 | alpha = alpha 39 | } ) 40 | end 41 | 42 | local IsValid = IsValid 43 | local LocalPlayer = LocalPlayer 44 | local FindByClass = ents.FindByClass 45 | 46 | local cvarMaxDist = GetConVar( "gminimap_npc_blips_max_distance" ) 47 | 48 | timer.Create( "GMinimap.UpdateNPCBlips", 0.5, 0, function() 49 | local localPly = LocalPlayer() 50 | if not IsValid( localPly ) then return end 51 | 52 | local maxDist = cvarMaxDist:GetFloat() 53 | 54 | if maxDist <= 0 then 55 | for id, _ in pairs( active ) do 56 | active[id] = nil 57 | GMinimap:RemoveBlipById( id ) 58 | end 59 | 60 | return 61 | end 62 | 63 | local origin = localPly:GetPos() 64 | local minDist = maxDist * 0.75 65 | 66 | maxDist = maxDist * maxDist 67 | minDist = minDist * minDist 68 | nearby = {} 69 | 70 | local alpha, dist 71 | 72 | for _, ent in ipairs( FindByClass( "npc_*" ) ) do 73 | -- Make sure this is a NPC (to filter out things like npc_grenade_frag) 74 | if IsValid( ent ) and ent:IsNPC() and ent:Health() >= 0 then 75 | dist = origin:DistToSqr( ent:GetPos() ) 76 | 77 | if dist < maxDist then 78 | alpha = 1 79 | 80 | if dist > minDist then 81 | alpha = ( maxDist - dist ) / ( maxDist - minDist ) 82 | end 83 | 84 | UpdateNPCBlip( ent, alpha * 255 ) 85 | end 86 | end 87 | end 88 | 89 | -- Clear all other blips for invalid / far away NPCs 90 | for id, _ in pairs( active ) do 91 | if not nearby[id] then 92 | active[id] = nil 93 | GMinimap:RemoveBlipById( id ) 94 | end 95 | end 96 | end ) 97 | -------------------------------------------------------------------------------- /lua/gminimap/client/players.lua: -------------------------------------------------------------------------------- 1 | function GMinimap:SetOnlySameTeamBlips( sameTeamOnly ) 2 | self.sameTeamOnly = sameTeamOnly 3 | end 4 | 5 | --[[ 6 | Clientside player blip functions 7 | ]] 8 | 9 | local type = type 10 | local PlayerMeta = FindMetaTable( "Player" ) 11 | 12 | function PlayerMeta:SetBlipIcon( path ) 13 | if path then 14 | assert( type( path ) == "string", "Player blip icon path must be a string or nil!" ) 15 | end 16 | 17 | self._GMinimapBlipIcon = path 18 | end 19 | 20 | function PlayerMeta:SetBlipScale( scale ) 21 | if scale then 22 | assert( type( scale ) == "number", "Player blip scale must be a number or nil!" ) 23 | end 24 | 25 | self._GMinimapBlipScale = scale 26 | end 27 | 28 | function PlayerMeta:SetBlipColor( color ) 29 | if color and color.r and color.g and color.b then 30 | self._GMinimapBlipColor = Color( color.r, color.g, color.b ) 31 | else 32 | self._GMinimapBlipColor = nil 33 | end 34 | end 35 | 36 | --[[ 37 | Player blips handler 38 | ]] 39 | 40 | local icons = { 41 | dead = "gminimap/dead.png", 42 | vehicle = "gminimap/blips/car.png" 43 | } 44 | 45 | local colors = { 46 | default = Color( 255, 255, 255 ), 47 | dead = Color( 0, 0, 0 ) 48 | } 49 | 50 | local scales = { 51 | vehicle = 1.25 52 | } 53 | 54 | local function GetState( ply ) 55 | if not ply:Alive() then 56 | return "dead" 57 | end 58 | 59 | if ply:InVehicle() then 60 | return "vehicle" 61 | end 62 | 63 | return "default" 64 | end 65 | 66 | local function GetIcon( ply, state ) 67 | if ply._GMinimapBlipIcon then 68 | return ply._GMinimapBlipIcon, ply._GMinimapBlipScale, true 69 | end 70 | 71 | if icons[state] then 72 | return icons[state], ply._GMinimapBlipScale or scales[state], true 73 | end 74 | 75 | return nil, ply._GMinimapBlipScale 76 | end 77 | 78 | local function GetColor( ply, state ) 79 | if ply._GMinimapBlipColor then 80 | return ply._GMinimapBlipColor 81 | end 82 | 83 | if colors[state] then 84 | return colors[state] 85 | end 86 | 87 | return colors.default 88 | end 89 | 90 | local IsValid = IsValid 91 | local LocalPlayer = LocalPlayer 92 | 93 | local localBlip 94 | local cvarMaxDist = GetConVar( "gminimap_player_blips_max_distance" ) 95 | local playerBlips = GMinimap.playerBlips or {} 96 | 97 | GMinimap.playerBlips = playerBlips 98 | 99 | timer.Create( "GMinimap.UpdatePlayerBlips", 0.1, 0, function() 100 | local localPly = LocalPlayer() 101 | if not IsValid( localPly ) then return end 102 | 103 | -- Create/update local player blip 104 | if localBlip then 105 | local icon, scale = GetIcon( localPly ) 106 | 107 | localBlip.icon = icon or "gminimap/player.png" 108 | localBlip.color = GetColor( localPly ) 109 | localBlip.scale = scale or 1.25 110 | else 111 | localBlip = GMinimap:AddBlip( { 112 | id = "local_player", 113 | parent = localPly 114 | } ) 115 | end 116 | 117 | -- Create/update local blips from other players 118 | local teamId = GMinimap.sameTeamOnly and localPly:Team() or nil 119 | local defaultMaxDist = cvarMaxDist:GetFloat() 120 | local origin = localPly:GetPos() 121 | 122 | local id, dist, alpha, b 123 | local canSee, minDist, maxDist 124 | 125 | for _, ply in ipairs( player.GetAll() ) do 126 | if ply == localPly then continue end 127 | 128 | id = "player_" .. ply:UserID() 129 | dist = origin:Distance( ply:GetPos() ) 130 | 131 | if ply:IsDormant() then 132 | canSee, maxDist = false, 0 133 | else 134 | canSee, maxDist = hook.Run( "CanSeePlayerBlip", ply ) 135 | maxDist = maxDist or defaultMaxDist 136 | end 137 | 138 | if canSee == nil and teamId then 139 | canSee = teamId == ply:Team() 140 | end 141 | 142 | if maxDist <= 0 then 143 | canSee = false 144 | end 145 | 146 | if canSee == false or dist > maxDist then 147 | if playerBlips[id] then 148 | playerBlips[id] = nil 149 | GMinimap:RemoveBlipById( id ) 150 | end 151 | 152 | continue 153 | end 154 | 155 | alpha = 1 156 | minDist = maxDist * 0.75 157 | 158 | if dist > minDist then 159 | alpha = ( maxDist - dist ) / ( maxDist - minDist ) 160 | end 161 | 162 | b = playerBlips[id] 163 | 164 | if b then 165 | local state = GetState( ply ) 166 | local icon, scale, lockAng = GetIcon( ply, state ) 167 | 168 | b.indicateAlt = state ~= "dead" 169 | b.indicateAng = state == "default" 170 | b.lockIconAng = lockAng and ( state ~= "localplayer" ) 171 | 172 | b.icon = icon 173 | b.color = GetColor( ply, state ) 174 | b.scale = scale or 0.9 175 | b.alpha = alpha * 255 176 | else 177 | playerBlips[id] = GMinimap:AddBlip( { 178 | id = id, 179 | parent = ply, 180 | alpha = 0 181 | } ) 182 | end 183 | end 184 | end ) 185 | -------------------------------------------------------------------------------- /lua/gminimap/client/radar.lua: -------------------------------------------------------------------------------- 1 | local Radar = {} 2 | 3 | Radar.__index = Radar 4 | 5 | function GMinimap.CreateRadar() 6 | local id, rt = SDrawUtils.AllocateRT() 7 | 8 | local instance = { 9 | -- Radar position/size 10 | x = 0, 11 | y = 0, 12 | w = 128, 13 | h = 128, 14 | 15 | -- Terrain render target 16 | rtId = id, 17 | rt = rt, 18 | 19 | -- Terrain layers and blips rotate around this pivot position. 20 | -- This is calculated based on the width/height. 21 | pivotX = 64, 22 | pivotY = 64, 23 | 24 | -- `pivotY` is set to `h * pivotMultY` 25 | pivotMultY = 0.5, 26 | 27 | origin = Vector(), -- Center of the radar plane, relative to the world 28 | rotation = Angle(), -- Rotation of the radar plane, relative to the world 29 | ratio = 50, -- Unit-to-pixel ratio (used for zooming) 30 | 31 | area = 5000, -- Width/length of the map area (source units) 32 | bottom = -1000, -- Min. height of the map area (source units) 33 | top = 1000, -- Max. height of the map area (source units) 34 | 35 | -- These are used internally to capture the map area when needed 36 | lastGridX = -1, 37 | lastGridY = -1, 38 | lastCapturePos = Vector(), 39 | color = Color( 255, 255, 255 ), 40 | voidColor = Color( 40, 40, 40 ) 41 | } 42 | 43 | return setmetatable( instance, Radar ) 44 | end 45 | 46 | function Radar:Destroy() 47 | SDrawUtils.FreeRT( self.rtId ) 48 | self.rtId = nil 49 | self.rt = nil 50 | end 51 | 52 | function Radar:SetDimensions( x, y, w, h ) 53 | self.x, self.y = x, y 54 | self.w, self.h = w, h 55 | self:UpdateLayout() 56 | end 57 | 58 | function Radar:SetArea( area ) 59 | self.area = area 60 | self:ResetCapture() 61 | end 62 | 63 | function Radar:SetHeights( bottom, top ) 64 | self.bottom = bottom or self.bottom 65 | self.top = top or self.top 66 | self:ResetCapture() 67 | end 68 | 69 | function Radar:ResetCapture() 70 | self.lastGridX = -1 71 | self.lastGridY = -1 72 | end 73 | 74 | function Radar:UpdateLayout() 75 | self.pivotX = self.w * 0.5 76 | self.pivotY = self.h * self.pivotMultY 77 | self:SetArea( math.max( self.w, self.h ) * self.ratio ) 78 | end 79 | 80 | local Clamp = math.Clamp 81 | local WorldToLocal = WorldToLocal 82 | 83 | --- Converts a 3D world position to a 2D pixel position, 84 | --- relative to the radar's origin and rotation. 85 | function Radar:WorldToLocal( pos, ang ) 86 | -- Make pos relative to the radar plane 87 | pos, ang = WorldToLocal( pos, ang, self.origin, self.rotation ) 88 | 89 | -- Convert source units to pixels 90 | local x, y = pos.y / self.ratio, pos.x / self.ratio 91 | 92 | -- Position relative to the radar pivot 93 | x, y = self.x + self.pivotX - x, self.y + self.pivotY - y 94 | 95 | -- Keep it inside 96 | x, y = Clamp( x, self.x, self.x + self.w ), Clamp( y, self.y, self.y + self.h ) 97 | 98 | return x, y, -ang.y 99 | end 100 | 101 | local LocalToWorld = LocalToWorld 102 | 103 | --- Converts a 2D pixel position to a 3D world position, 104 | --- taking the radar's origin and rotation into consideration. 105 | function Radar:LocalToWorld( x, y ) 106 | local pos = Vector( self.pivotY - y, self.pivotX - x, 0 ) * self.ratio 107 | pos = LocalToWorld( pos, Angle(), self.origin, self.rotation ) 108 | pos.z = 0 109 | 110 | return pos 111 | end 112 | 113 | local terrainMat = CreateMaterial( "GMinimap_TerrainMaterial", "UnlitGeneric", { 114 | ["$nolod"] = 1, 115 | ["$ignorez"] = 1, 116 | ["$vertexcolor"] = 1 117 | } ) 118 | 119 | local yawAng = Angle() 120 | 121 | local Round, Max = math.Round, math.max 122 | local PushFilterMin, PushFilterMag = render.PushFilterMin, render.PushFilterMag 123 | local PopFilterMin, PopFilterMag = render.PopFilterMin, render.PopFilterMag 124 | 125 | local SetMaterial = render.SetMaterial 126 | local SetScissorRect = render.SetScissorRect 127 | local DrawTexturedRectRotated = SDrawUtils.DrawTexturedRectRotated 128 | 129 | local function Grid( n, res ) 130 | return Round( n / res ) * res 131 | end 132 | 133 | function Radar:Draw() 134 | if not self.rtId then return end 135 | 136 | local x, y = self.x, self.y 137 | local w, h = self.w, self.h 138 | local origin = self.origin 139 | local yaw = self.rotation[2] 140 | local size = Max( w, h ) 141 | 142 | -- Convert the origin to a position on the grid, based on the area size 143 | local gridPos = Vector( 144 | Grid( origin[1], self.area * 0.5 ), 145 | Grid( origin[2], self.area * 0.5 ), 146 | 0 147 | ) 148 | 149 | -- Capture the terrain if the grid position has changed since last frame 150 | if self.lastGridX ~= gridPos[1] or self.lastGridY ~= gridPos[2] then 151 | self.lastGridX = gridPos[1] 152 | self.lastGridY = gridPos[2] 153 | 154 | local hookId = "GMinimap.Capture_" .. self.rtId 155 | 156 | hook.Add( "PreRender", hookId, function() 157 | hook.Remove( "PreRender", hookId ) 158 | 159 | self.lastCapturePos = gridPos 160 | self:Capture( gridPos ) 161 | end ) 162 | end 163 | 164 | -- When the origin moves away from out last captured position, 165 | -- calculate how much we need to move the terrain texture to compensate. 166 | local offset = Vector( 167 | ( ( origin[2] - self.lastCapturePos[2] ) / self.area ) * size, 168 | ( ( origin[1] - self.lastCapturePos[1] ) / self.area ) * size, 169 | 0 170 | ) 171 | 172 | yawAng[2] = yaw 173 | offset:Rotate( yawAng ) 174 | 175 | SetScissorRect( x, y, x + w, y + h, true ) 176 | 177 | PushFilterMin( 3 ) 178 | PushFilterMag( 3 ) 179 | 180 | terrainMat:SetTexture( "$basetexture", self.rt ) 181 | SetMaterial( terrainMat ) 182 | DrawTexturedRectRotated( x + self.pivotX + offset[1], y + self.pivotY + offset[2], size * 2, size * 2, -yaw, self.color ) 183 | 184 | PopFilterMin() 185 | PopFilterMag() 186 | 187 | SetScissorRect( 0, 0, 0, 0, false ) 188 | end 189 | 190 | local function NoDrawFunc() return true end 191 | 192 | local OriginalEyePos = GMinimap.OriginalEyePos or EyePos 193 | GMinimap.OriginalEyePos = OriginalEyePos 194 | 195 | function Radar:Capture( origin ) 196 | if self.capturing then return end 197 | 198 | self.capturing = true 199 | origin.z = self.top 200 | 201 | local hookId = "GMinimap.Capture_" .. self.rtId 202 | local Config = GMinimap.Config 203 | 204 | hook.Add( "PreDrawSkyBox", hookId, NoDrawFunc ) 205 | hook.Add( "PrePlayerDraw", hookId, NoDrawFunc ) 206 | hook.Add( "PreDrawViewModel", hookId, NoDrawFunc ) 207 | 208 | local lastEyePos = EyePos() 209 | 210 | EyePos = function() 211 | return lastEyePos 212 | end 213 | 214 | local haloFunc = hook.GetTable()["PostDrawEffects"]["RenderHalos"] 215 | 216 | if haloFunc then 217 | hook.Remove( "PostDrawEffects", "RenderHalos" ) 218 | end 219 | 220 | render.PushRenderTarget( self.rt, 0, 0, 1024, 1024 ) 221 | render.SetStencilEnable( false ) 222 | render.SetLightingMode( Config.terrainLighting and 0 or 1 ) 223 | render.OverrideAlphaWriteEnable( false ) 224 | render.SetColorMaterial() 225 | 226 | render.Clear( self.voidColor.r, self.voidColor.g, self.voidColor.b, 255, true, true ) 227 | 228 | PushFilterMin( 1 ) 229 | PushFilterMag( 1 ) 230 | 231 | local offset = 50000 232 | local oldPanelClip = DisableClipping( true ) 233 | 234 | render.RenderView( { 235 | origin = origin + Vector( 0, 0, offset ), 236 | angles = Angle( 90, 0, 0 ), 237 | x = 0, 238 | y = 0, 239 | w = 1024, 240 | h = 1024, 241 | znear = offset, 242 | zfar = self.top - self.bottom + offset, 243 | drawhud = false, 244 | drawmonitors = false, 245 | drawviewmodel = false, 246 | dopostprocess = false, 247 | viewid = 2, -- VIEW_MONITOR 248 | 249 | ortho = { 250 | top = -self.area, 251 | left = -self.area, 252 | right = self.area, 253 | bottom = self.area 254 | } 255 | } ) 256 | 257 | DisableClipping( oldPanelClip ) 258 | 259 | render.SetLightingMode( 0 ) 260 | 261 | DrawColorModify( { 262 | ["$pp_colour_addr"] = 0, 263 | ["$pp_colour_addg"] = 0, 264 | ["$pp_colour_addb"] = 0, 265 | ["$pp_colour_mulr"] = 0, 266 | ["$pp_colour_mulg"] = 0, 267 | ["$pp_colour_mulb"] = 0, 268 | ["$pp_colour_brightness"] = Config.terrainBrightness, 269 | ["$pp_colour_contrast"] = 1, 270 | ["$pp_colour_colour"] = Config.terrainColorMult, 271 | ["$pp_colour_inv"] = Config.terrainColorInv 272 | } ) 273 | 274 | PopFilterMin() 275 | PopFilterMag() 276 | render.PopRenderTarget() 277 | 278 | hook.Remove( "PreDrawSkyBox", hookId ) 279 | hook.Remove( "PrePlayerDraw", hookId ) 280 | hook.Remove( "PreDrawViewModel", hookId ) 281 | 282 | if haloFunc then 283 | hook.Add( "PostDrawEffects", "RenderHalos", haloFunc ) 284 | end 285 | 286 | EyePos = OriginalEyePos 287 | 288 | self.capturing = false 289 | end 290 | -------------------------------------------------------------------------------- /lua/gminimap/client/utils.lua: -------------------------------------------------------------------------------- 1 | function GMinimap.GetLanguageText( id ) 2 | return language.GetPhrase( "gminimap." .. id ):Trim() 3 | end 4 | 5 | function GMinimap.ValidateNumber( v, min, max, default ) 6 | return math.Clamp( tonumber( v ) or default or 0, min, max ) 7 | end 8 | 9 | function GMinimap.SetNumber( t, k, v, min, max, default ) 10 | if v ~= nil then 11 | t[k] = GMinimap.ValidateNumber( v, min, max, default ) 12 | end 13 | end 14 | 15 | function GMinimap.SetBool( t, k, v ) 16 | if v ~= nil then 17 | t[k] = tobool( v ) 18 | end 19 | end 20 | 21 | function GMinimap.SetColor( t, k, r, g, b ) 22 | if r or g or b then 23 | t[k] = Color( 24 | GMinimap.ValidateNumber( r, 0, 255, 255 ), 25 | GMinimap.ValidateNumber( g, 0, 255, 255 ), 26 | GMinimap.ValidateNumber( b, 0, 255, 255 ), 27 | 255 28 | ) 29 | end 30 | end 31 | 32 | function GMinimap.ToJSON( tbl, prettyPrint ) 33 | return util.TableToJSON( tbl, prettyPrint ) 34 | end 35 | 36 | function GMinimap.FromJSON( str ) 37 | if not str or str == "" then 38 | return {} 39 | end 40 | 41 | return util.JSONToTable( str ) or {} 42 | end 43 | 44 | function GMinimap.EnsureDataDir() 45 | if not file.Exists( GMinimap.DATA_DIR, "DATA" ) then 46 | file.CreateDir( GMinimap.DATA_DIR ) 47 | end 48 | end 49 | 50 | function GMinimap.LoadDataFile( path ) 51 | return file.Read( GMinimap.DATA_DIR .. path, "DATA" ) 52 | end 53 | 54 | function GMinimap.SaveDataFile( path, data ) 55 | GMinimap.Print( "%s: writing %s", path, string.NiceSize( string.len( data ) ) ) 56 | GMinimap.EnsureDataDir() 57 | 58 | file.Write( GMinimap.DATA_DIR .. path, data ) 59 | end 60 | 61 | function GMinimap.CreateColorPicker( parent, default, callback ) 62 | local picker = vgui.Create( "DColorMixer", parent ) 63 | picker:SetTall( 150 ) 64 | picker:Dock( TOP ) 65 | picker:SetPalette( true ) 66 | picker:SetAlphaBar( false ) 67 | picker:SetWangs( true ) 68 | 69 | if default then 70 | picker:SetColor( default ) 71 | end 72 | 73 | picker.ValueChanged = function( _, color ) 74 | callback( Color( color.r, color.g, color.b ) ) 75 | end 76 | 77 | return picker 78 | end 79 | -------------------------------------------------------------------------------- /lua/gminimap/client/vgui/radar.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | A wrapper panel for the Radar class. 3 | ]] 4 | 5 | local Radar = {} 6 | 7 | function Radar:Init() 8 | self:SetCursor( "hand" ) 9 | 10 | local baseRatio = GMinimap.World.baseZoomRatio 11 | 12 | self.minRatio = baseRatio * 0.2 13 | self.maxRatio = baseRatio 14 | 15 | self.radar = GMinimap.CreateRadar() 16 | self:SetHeights( GMinimap.World:GetHeights() ) 17 | 18 | hook.Add( "OnGMinimapConfigChange", self, function() 19 | self.radar:UpdateLayout() 20 | end ) 21 | end 22 | 23 | function Radar:OnRemove() 24 | hook.Remove( "OnGMinimapConfigChange", self ) 25 | 26 | self.radar:Destroy() 27 | self.radar = nil 28 | end 29 | 30 | function Radar:SetOrigin( origin ) 31 | self.radar.origin = origin 32 | end 33 | 34 | function Radar:SetRotation( ang ) 35 | self.radar.rotation[2] = ang 36 | end 37 | 38 | function Radar:SetRatio( ratio ) 39 | self.radar.ratio = ratio 40 | self.radar:UpdateLayout() 41 | 42 | if self.slider then 43 | self.slider:SetValue( ratio ) 44 | end 45 | end 46 | 47 | function Radar:SetHeights( bottom, top ) 48 | self.radar:SetHeights( bottom, top ) 49 | end 50 | 51 | function Radar:AddZoomSlider() 52 | if self.slider then return end 53 | 54 | self.slider = vgui.Create( "DSlider", self ) 55 | self.slider:SetSize( 100, 16 ) 56 | self.slider:SetLockY( 0.5 ) 57 | self.slider:SetTrapInside( true ) 58 | 59 | self.slider.Paint = function( _, w ) 60 | surface.SetDrawColor( 120, 120, 120, 255 ) 61 | surface.DrawRect( 5, 7, w - 10, 1 ) 62 | 63 | surface.SetDrawColor( 30, 30, 30, 255 ) 64 | surface.DrawRect( 5, 8, w - 10, 1 ) 65 | end 66 | 67 | self.slider.SetValue = function( s, value ) 68 | s:SetSlideX( math.Remap( value, self.minRatio, self.maxRatio, 1, 0 ) ) 69 | end 70 | 71 | self.slider.TranslateValues = function( _, x, y ) 72 | self.radar.ratio = math.Remap( x, 1, 0, self.minRatio, self.maxRatio ) 73 | self.radar:UpdateLayout() 74 | 75 | return x, y 76 | end 77 | end 78 | 79 | function Radar:PerformLayout( w, h ) 80 | self.radar:SetDimensions( 0, 0, w, h ) 81 | 82 | if self.slider then 83 | self.slider:SetPos( w - self.slider:GetWide() - 4, h - self.slider:GetTall() - 4 ) 84 | end 85 | end 86 | 87 | function Radar:Paint() 88 | local x, y = self:LocalToScreen( 0, 0 ) 89 | 90 | self.radar.x = x 91 | self.radar.y = y 92 | self.radar:Draw() 93 | 94 | GMinimap:DrawBlips( self.radar ) 95 | 96 | if self._originStart then 97 | x, y = input.GetCursorPos() 98 | 99 | local diffX = ( self._mouseStartX - x ) * self.radar.ratio 100 | local diffY = ( self._mouseStartY - y ) * self.radar.ratio 101 | 102 | self.radar.origin[2] = self._originStart[2] - diffX 103 | self.radar.origin[1] = self._originStart[1] - diffY 104 | end 105 | end 106 | 107 | function Radar:OnMousePressed( keyCode ) 108 | self:MouseCapture( true ) 109 | 110 | local x, y = input.GetCursorPos() 111 | 112 | if keyCode == MOUSE_LEFT then 113 | self._originStart = Vector( self.radar.origin[1], self.radar.origin[2], 0 ) 114 | self._mouseStartX = x 115 | self._mouseStartY = y 116 | else 117 | x, y = self:ScreenToLocal( x, y ) 118 | self:OnRightClickPosition( self.radar:LocalToWorld( x, y ) ) 119 | end 120 | end 121 | 122 | function Radar:OnMouseReleased() 123 | self:MouseCapture( false ) 124 | self._originStart = nil 125 | self._mouseStartX = nil 126 | self._mouseStartY = nil 127 | end 128 | 129 | function Radar:OnMouseWheeled( delta ) 130 | local ratio = math.Clamp( self.radar.ratio - delta * 5, self.minRatio, self.maxRatio ) 131 | 132 | self.radar.ratio = ratio 133 | self.radar:UpdateLayout() 134 | 135 | if self.slider then 136 | self.slider:SetValue( ratio ) 137 | end 138 | end 139 | 140 | function Radar:OnRightClickPosition( _pos ) end 141 | 142 | vgui.Register( "GMinimap_Radar", Radar, "DPanel" ) 143 | -------------------------------------------------------------------------------- /lua/gminimap/client/world.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | This file manages map-specific settings, such as 3 | terrain layers, base zoom ratio, world size, etc. 4 | ]] 5 | local World = GMinimap.World or {} 6 | 7 | GMinimap.World = World 8 | 9 | function World:Reset() 10 | self.baseZoomRatio = 50 11 | 12 | -- World heights, set when the data file exists. 13 | self.bottom = nil 14 | self.top = nil 15 | 16 | -- List of vertical slices of the map 17 | self.layers = {} 18 | 19 | -- Places on the world that activate layers 20 | self.triggers = {} 21 | 22 | -- Trigger check variables 23 | self.triggerCount = 0 24 | self.lastTriggerIndex = 0 25 | self.triggerLayerIndex = 0 26 | self.activeLayerIndex = 0 27 | 28 | local world = game.GetWorld() 29 | if not IsValid( world ) then return end 30 | if not world.GetModelBounds then return end 31 | 32 | local mins, maxs = world:GetModelBounds() 33 | 34 | -- World heights, used when the data file does not exist. 35 | -- Assume it's the whole map if we haven't received 36 | -- accurate dimensions from the server yet. 37 | if not self.serverBottom then 38 | self.serverBottom = mins.z 39 | self.serverTop = maxs.z 40 | end 41 | 42 | -- Try to figure out the best zoom level for this map. 43 | local sizeX = maxs.x + math.abs( mins.x ) 44 | local sizeY = maxs.y + math.abs( mins.y ) 45 | local avg = ( sizeX + sizeY ) * 0.5 46 | 47 | self.baseZoomRatio = ( avg / 25000 ) * 50 48 | end 49 | 50 | World:Reset() 51 | 52 | --- Load current map settings from `data_static/` if it exists. 53 | function World:LoadFromFile() 54 | self:Reset() 55 | 56 | local path = "data_static/gminimap/" .. game.GetMap() .. ".json" 57 | local data = file.Read( path, "GAME" ) 58 | 59 | if not data then 60 | GMinimap.Print( "Map settings file '%s' does not exist, using defaults.", path ) 61 | return 62 | end 63 | 64 | data = GMinimap.FromJSON( data ) 65 | 66 | local SetNumber = GMinimap.SetNumber 67 | 68 | SetNumber( self, "bottom", data.bottom, -50000, 50000, nil ) 69 | SetNumber( self, "top", data.top, -50000, 50000, nil ) 70 | SetNumber( self, "baseZoomRatio", data.baseZoomRatio, 1, 100, self.baseZoomRatio ) 71 | 72 | if type( data.layers ) == "table" and table.IsSequential( data.layers ) then 73 | for i, l in ipairs( data.layers ) do 74 | local layer = {} 75 | 76 | SetNumber( layer, "bottom", l.bottom, -50000, 50000, nil ) 77 | SetNumber( layer, "top", l.top, -50000, 50000, nil ) 78 | 79 | self.layers[i] = layer 80 | end 81 | else 82 | GMinimap.Print( "Map settings has no layers." ) 83 | end 84 | 85 | local function SortVectors( ax, ay, az, bx, by, bz ) 86 | return 87 | math.min( ax, bx ), 88 | math.min( ay, by ), 89 | math.min( az, bz ), 90 | 91 | math.max( ax, bx ), 92 | math.max( ay, by ), 93 | math.max( az, bz ) 94 | end 95 | 96 | if type( data.triggers ) == "table" and table.IsSequential( data.triggers ) then 97 | for i, trigger in ipairs( data.triggers ) do 98 | local t = {} 99 | 100 | SetNumber( t, "layerIndex", trigger.layerIndex, 0, 1000, 1 ) 101 | 102 | SetNumber( t, "ax", trigger.ax, -50000, 50000, 0 ) 103 | SetNumber( t, "ay", trigger.ay, -50000, 50000, 0 ) 104 | SetNumber( t, "az", trigger.az, -50000, 50000, 0 ) 105 | 106 | SetNumber( t, "bx", trigger.bx, -50000, 50000, 0 ) 107 | SetNumber( t, "by", trigger.by, -50000, 50000, 0 ) 108 | SetNumber( t, "bz", trigger.bz, -50000, 50000, 0 ) 109 | 110 | t.ax, t.ay, t.az, t.bx, t.by, t.bz = SortVectors( 111 | t.ax, t.ay, t.az, 112 | t.bx, t.by, t.bz 113 | ) 114 | 115 | self.triggers[i] = t 116 | end 117 | 118 | self.triggerCount = #self.triggers 119 | else 120 | GMinimap.Print( "Map settings has no layer triggers." ) 121 | end 122 | 123 | GMinimap.Print( "Loaded map settings file: %s", path ) 124 | end 125 | 126 | function World:GetHeights() 127 | return 128 | self.layerBottom or self.bottom or self.serverBottom or -5000, 129 | self.layerTop or self.top or self.serverTop or 5000 130 | end 131 | 132 | function World:GetHeightsNoLayer() 133 | return 134 | self.bottom or self.serverBottom or -5000, 135 | self.top or self.serverTop or 5000 136 | end 137 | 138 | local function IsWithinTrigger( v, t ) 139 | if v[1] < t.ax or v[1] > t.bx then return false end 140 | if v[2] < t.ay or v[2] > t.by then return false end 141 | if v[3] < t.az or v[3] > t.bz then return false end 142 | 143 | return true 144 | end 145 | 146 | function World:SetActiveLayer( index ) 147 | local layer = self.layers[index] 148 | 149 | if layer then 150 | self.layerTop = layer.top 151 | self.layerBottom = layer.bottom 152 | else 153 | self.layerTop = nil 154 | self.layerBottom = nil 155 | end 156 | 157 | GMinimap:UpdateLayout() 158 | end 159 | 160 | function World:CheckTriggers() 161 | if self.triggerCount == 0 then return end 162 | 163 | local index = self.lastTriggerIndex + 1 164 | 165 | -- We've iterated over all triggers, time to check 166 | if index > self.triggerCount then 167 | index = 1 168 | 169 | if self.triggerLayerIndex ~= self.activeLayerIndex then 170 | self.activeLayerIndex = self.triggerLayerIndex 171 | self:SetActiveLayer( self.activeLayerIndex ) 172 | end 173 | 174 | self.triggerLayerIndex = 0 175 | end 176 | 177 | self.lastTriggerIndex = index 178 | 179 | -- Do one trigger check per frame 180 | local checkPos = LocalPlayer():EyePos() 181 | 182 | if IsWithinTrigger( checkPos, self.triggers[index] ) then 183 | self.triggerLayerIndex = self.triggers[index].layerIndex 184 | end 185 | end 186 | 187 | net.Receive( "gminimap.world_heights", function() 188 | World.serverBottom = net.ReadFloat() 189 | World.serverTop = net.ReadFloat() 190 | World.activeLayerIndex = 0 191 | World:SetActiveLayer( 0 ) 192 | 193 | GMinimap.Print( "Received world heights from server: %f, %f", World.serverBottom, World.serverTop ) 194 | end ) 195 | 196 | hook.Add( "InitPostEntity", "GMinimap.SetupWorld", function() 197 | World:LoadFromFile() 198 | end ) 199 | 200 | concommand.Add( 201 | "gminimap_layers", 202 | function() World:OpenLayers() end, 203 | nil, 204 | "Opens the GMinimap layers editor." 205 | ) 206 | 207 | function World:OpenLayers() 208 | local L = GMinimap.GetLanguageText 209 | local ApplyTheme = StyledTheme.Apply 210 | local ScaleSize = StyledTheme.ScaleSize 211 | 212 | local frame = vgui.Create( "DFrame" ) 213 | frame:SetTitle( L"layers" ) 214 | frame:SetIcon( "icon16/shape_move_forwards.png" ) 215 | frame:SetSize( ScaleSize( 1200 ), ScaleSize( 700 ) ) 216 | frame:SetSizable( true ) 217 | frame:SetDraggable( true ) 218 | frame:SetDeleteOnClose( true ) 219 | frame:SetMinWidth( ScaleSize( 1000 ) ) 220 | frame:SetMinHeight( ScaleSize( 600 ) ) 221 | frame:Center() 222 | frame:MakePopup() 223 | 224 | ApplyTheme( frame ) 225 | 226 | frame.OnClose = function() 227 | self.activeLayerIndex = 0 228 | end 229 | 230 | local menuBar = vgui.Create( "DMenuBar", frame ) 231 | ApplyTheme( menuBar ) 232 | 233 | local container = vgui.Create( "DPanel", frame ) 234 | container:SetPaintBackground( false ) 235 | container:Dock( FILL ) 236 | 237 | local radar = vgui.Create( "GMinimap_Radar", container ) 238 | radar:SetRatio( GMinimap.World.baseZoomRatio ) 239 | radar:AddZoomSlider() 240 | radar:Dock( FILL ) 241 | 242 | local rightPanel = vgui.Create( "DPanel", frame ) 243 | rightPanel:SetWide( ScaleSize( 340 ) ) 244 | rightPanel:SetPaintBackground( false ) 245 | rightPanel:Dock( RIGHT ) 246 | rightPanel:DockMargin( ScaleSize( 4 ), 0, 0, 0 ) 247 | 248 | local layerList = vgui.Create( "DScrollPanel", rightPanel ) 249 | layerList:Dock( FILL ) 250 | ApplyTheme( layerList ) 251 | 252 | local selectedItem 253 | 254 | local function GetItems() 255 | return layerList.pnlCanvas:GetChildren() 256 | end 257 | 258 | local function SelectItem( item ) 259 | selectedItem = nil 260 | 261 | for _, v in ipairs( GetItems() ) do 262 | if v == item then 263 | selectedItem = v 264 | v._isSelected = true 265 | else 266 | v._isSelected = false 267 | end 268 | end 269 | 270 | if not selectedItem then return end 271 | 272 | timer.Simple( 0.1, function() 273 | if IsValid( layerList ) then 274 | layerList:ScrollToChild( selectedItem ) 275 | end 276 | end ) 277 | 278 | radar:SetHeights( selectedItem._layer.bottom, selectedItem._layer.top ) 279 | self:SetActiveLayer( selectedItem._index ) 280 | end 281 | 282 | local function OnItemChanged( item ) 283 | local layer = item._layer 284 | 285 | if layer.isDefault then 286 | self.top = layer.top 287 | self.bottom = layer.bottom 288 | end 289 | 290 | SelectItem( item ) 291 | end 292 | 293 | -- Item panel functions 294 | local colors = StyledTheme.colors 295 | 296 | local function PaintItem( s, w, h ) 297 | local bgColor = s._isSelected and colors.buttonPress or colors.panelBackground 298 | 299 | surface.SetDrawColor( bgColor:Unpack() ) 300 | surface.DrawRect( 0, 0, w, h ) 301 | end 302 | 303 | local function SliderPerformLayout( s ) 304 | s.Label:SetWide( ScaleSize( 80 ) ) 305 | end 306 | 307 | -- Item panel creation 308 | local spacing = ScaleSize( 4 ) 309 | local padding = ScaleSize( 6 ) 310 | 311 | local function AddItem( layer, label, index ) 312 | local item = vgui.Create( "DPanel", layerList ) 313 | item:Dock( TOP ) 314 | item:DockMargin( 0, spacing, 0, 0 ) 315 | item:DockPadding( padding, 0, padding, padding ) 316 | 317 | layer.top = layer.top or 5000 318 | layer.bottom = layer.bottom or -5000 319 | 320 | item._index = index 321 | item._layer = layer 322 | item.Paint = PaintItem 323 | item.OnMousePressed = SelectItem 324 | 325 | local header = StyledTheme.CreateFormHeader( item, L( "layer_boundaries" ):format( label ), 0 ) 326 | header:SetMouseInputEnabled( false ) 327 | 328 | local sliderTop = StyledTheme.CreateFormSlider( item, L"layer_top", layer.top, -20000, 20000, 0, function( value ) 329 | layer.top = value 330 | OnItemChanged( item ) 331 | end ) 332 | 333 | item._sliderTop = sliderTop 334 | sliderTop.PerformLayout = SliderPerformLayout 335 | 336 | local sliderBottom = StyledTheme.CreateFormSlider( item, L"layer_bottom", layer.bottom, -20000, 20000, 0, function( value ) 337 | layer.bottom = value 338 | OnItemChanged( item ) 339 | end ) 340 | 341 | item._sliderBottom = sliderBottom 342 | sliderBottom.PerformLayout = SliderPerformLayout 343 | 344 | item:InvalidateLayout( true ) 345 | item:SizeToChildren( false, true ) 346 | 347 | return item 348 | end 349 | 350 | -- Update the list of items 351 | local function UpdateList() 352 | layerList:Clear() 353 | 354 | local bottom, top = World:GetHeightsNoLayer() 355 | 356 | local defaultItem = AddItem( { 357 | isDefault = true, 358 | bottom = bottom, 359 | top = top 360 | }, L"layer_default", 0 ) 361 | 362 | SelectItem( defaultItem ) 363 | 364 | for i, layer in ipairs( self.layers ) do 365 | AddItem( layer, L( "layer_number" ):format( i ), i ) 366 | end 367 | end 368 | 369 | UpdateList() 370 | 371 | -- Menu functions 372 | local fileMenu = menuBar:AddMenu( L"file" ) 373 | 374 | fileMenu:AddOption( L"import", function() 375 | local path = "data_static/gminimap/" .. game.GetMap() .. ".json" 376 | 377 | if not file.Exists( path, "GAME" ) then 378 | Derma_Message( L( "map_settings_not_found" ):format( path ), L"import", L"ok" ) 379 | return 380 | end 381 | 382 | World:LoadFromFile() 383 | UpdateList() 384 | hook.Run( "OnGMinimapConfigChange" ) 385 | 386 | Derma_Message( L( "map_settings_found" ):format( path ), L"import", L"ok" ) 387 | end ):SetIcon( "icon16/page_white_get.png" ) 388 | 389 | fileMenu:AddOption( L"export", function() 390 | local bottom, top = World:GetHeightsNoLayer() 391 | 392 | local data = { 393 | top = top, 394 | bottom = bottom 395 | } 396 | 397 | if #self.layers > 0 then 398 | data.layers = self.layers 399 | end 400 | 401 | if #self.triggers > 0 then 402 | data.triggers = self.triggers 403 | end 404 | 405 | data = util.TableToJSON( data, true ) 406 | 407 | local frameExport = vgui.Create( "DFrame" ) 408 | frameExport:SetTitle( L"export" ) 409 | frameExport:SetIcon( "icon16/page_white_go.png" ) 410 | frameExport:SetSize( ScaleSize( 700 ), ScaleSize( 600 ) ) 411 | frameExport:SetSizable( false ) 412 | frameExport:SetDraggable( true ) 413 | frameExport:SetDeleteOnClose( true ) 414 | frameExport:Center() 415 | frameExport:MakePopup() 416 | 417 | ApplyTheme( frameExport ) 418 | 419 | local helpLabel1 = vgui.Create( "DLabel", frameExport ) 420 | helpLabel1:SetText( L"export_tip1" ) 421 | helpLabel1:SetContentAlignment( 5 ) 422 | helpLabel1:Dock( TOP ) 423 | ApplyTheme( helpLabel1 ) 424 | 425 | local path = string.format( "%s/data_static/gminimap/%s.json", L"export_tip2", game.GetMap() ) 426 | padding = ScaleSize( 12 ) 427 | 428 | local pathEntry = vgui.Create( "DTextEntry", frameExport ) 429 | pathEntry:SetEnabled( false ) 430 | pathEntry:SetValue( path ) 431 | pathEntry:Dock( TOP ) 432 | pathEntry:DockMargin( padding, padding, padding, padding ) 433 | ApplyTheme( pathEntry ) 434 | 435 | local helpLabel2 = vgui.Create( "DLabel", frameExport ) 436 | helpLabel2:SetText( L"export_tip3" ) 437 | helpLabel2:SetContentAlignment( 5 ) 438 | helpLabel2:Dock( TOP ) 439 | ApplyTheme( helpLabel2 ) 440 | 441 | local codeEntry = vgui.Create( "DTextEntry", frameExport ) 442 | codeEntry:SetEnabled( false ) 443 | codeEntry:SetMultiline( true ) 444 | codeEntry:SetValue( data ) 445 | codeEntry:Dock( FILL ) 446 | codeEntry:DockMargin( padding, padding, padding, padding ) 447 | 448 | ApplyTheme( codeEntry ) 449 | 450 | local buttonCopy = vgui.Create( "DButton", frameExport ) 451 | buttonCopy:SetText( L"copy_code" ) 452 | buttonCopy:Dock( BOTTOM ) 453 | 454 | ApplyTheme( buttonCopy ) 455 | 456 | buttonCopy.DoClick = function() 457 | SetClipboardText( data ) 458 | buttonCopy:SetText( L"code_copied" ) 459 | end 460 | end ):SetIcon( "icon16/page_white_go.png" ) 461 | 462 | local layersMenu = menuBar:AddMenu( L"layers" ) 463 | 464 | layersMenu:AddOption( L"add_layer", function() 465 | local index = #self.layers + 1 466 | 467 | self.layers[index] = {} 468 | UpdateList() 469 | 470 | local items = GetItems() 471 | SelectItem( items[#items] ) 472 | end ):SetIcon( "icon16/shape_square_add.png" ) 473 | 474 | layersMenu:AddOption( L"remove_layer", function() 475 | if not selectedItem then return end 476 | 477 | if selectedItem._layer.isDefault then 478 | Derma_Message( L"remove_layer_blocked", L"remove_layer", L"ok" ) 479 | return 480 | end 481 | 482 | table.remove( self.layers, selectedItem._index ) 483 | selectedItem:Remove() 484 | UpdateList() 485 | end ):SetIcon( "icon16/shape_square_delete.png" ) 486 | 487 | layersMenu:AddOption( L"layer_user_position", function() 488 | if not selectedItem then return end 489 | 490 | local top, bottom = GMinimap.GetHeightsAround( LocalPlayer():EyePos(), 50000 ) 491 | 492 | top = top + 1000 493 | bottom = bottom - 1000 494 | 495 | selectedItem._layer.top = top 496 | selectedItem._layer.bottom = bottom 497 | 498 | selectedItem._sliderTop:SetValue( top ) 499 | selectedItem._sliderBottom:SetValue( bottom ) 500 | 501 | SelectItem( selectedItem ) 502 | end ):SetIcon( "icon16/shape_square_go.png" ) 503 | end 504 | -------------------------------------------------------------------------------- /lua/gminimap/server/main.lua: -------------------------------------------------------------------------------- 1 | resource.AddWorkshop( "3024317004" ) 2 | 3 | util.AddNetworkString( "gminimap.world_heights" ) 4 | util.AddNetworkString( "gminimap.force_cvar_changed" ) 5 | 6 | local worldTop, worldBottom 7 | 8 | hook.Add( "InitPostEntity", "GMinimap.CalculateWorldSize", function() 9 | worldTop, worldBottom = 0, 0 10 | 11 | local GetHeightsAround = GMinimap.GetHeightsAround 12 | 13 | -- Try to find the world heights by looking at spawnpoints 14 | local spawnpoints = ents.FindByClass( "info_player_start" ) 15 | 16 | for _, ent in ipairs( spawnpoints ) do 17 | local top, bottom = GetHeightsAround( ent:GetPos(), 50000 ) 18 | 19 | if top > worldTop then worldTop = top end 20 | if bottom < worldBottom then worldBottom = bottom end 21 | end 22 | 23 | worldTop = worldTop + 1000 24 | worldBottom = worldBottom - 1000 25 | 26 | -- Try to to exclude the skybox from the world heights. 27 | local skyCam = ents.FindByClass( "sky_camera" )[1] 28 | 29 | if IsValid( skyCam ) then 30 | local skyCamPos = skyCam:GetPos() 31 | local skyTop, skyBottom = GetHeightsAround( skyCamPos, 5000 ) 32 | local skyCenter = ( skyTop + skyBottom ) * 0.5 33 | 34 | -- If the skybox is above the map... 35 | if skyCenter > worldTop then 36 | -- Make sure the world top doesn't go into the skybox 37 | worldTop = math.min( worldTop, skyBottom ) 38 | GMinimap.Print( "Looks like this map's skybox is above the rest of the map." ) 39 | end 40 | 41 | -- If the skybox is below the map... 42 | if skyCenter < worldBottom then 43 | -- Make sure the world bottom ends at the top of the skybox 44 | worldBottom = skyTop + 100 45 | GMinimap.Print( "Looks like this map's skybox is below the rest of the map." ) 46 | end 47 | end 48 | end ) 49 | 50 | -- Since `PlayerInitialSpawn` is called before the player is ready to 51 | -- receive net events, we have to use `ClientSignOnStateChanged` instead. 52 | hook.Add( "ClientSignOnStateChanged", "GMinimap.SendWorldHeights", function( user, _, new ) 53 | if new == SIGNONSTATE_FULL then 54 | -- Since we can only retrieve the player entity 55 | -- after this hook runs, lets use a timer. 56 | timer.Simple( 3, function() 57 | local ply = Player( user ) 58 | if not IsValid( ply ) then return end 59 | if ply:IsBot() then return end 60 | 61 | net.Start( "gminimap.world_heights", false ) 62 | net.WriteFloat( worldBottom ) 63 | net.WriteFloat( worldTop ) 64 | net.Send( ply ) 65 | end ) 66 | end 67 | end ) 68 | 69 | -- Callbacks on FCVAR_REPLICATED cvars dont work clientside so we need them here 70 | local function NotifyForceCvarChanged() 71 | net.Start( "gminimap.force_cvar_changed", false ) 72 | net.Broadcast() 73 | end 74 | 75 | cvars.AddChangeCallback( "gminimap_force_x", NotifyForceCvarChanged, "changed_force_x" ) 76 | cvars.AddChangeCallback( "gminimap_force_y", NotifyForceCvarChanged, "changed_force_y" ) 77 | cvars.AddChangeCallback( "gminimap_force_w", NotifyForceCvarChanged, "changed_force_w" ) 78 | cvars.AddChangeCallback( "gminimap_force_h", NotifyForceCvarChanged, "changed_force_h" ) 79 | 80 | -- Workaround for key hooks that only run serverside on single-player 81 | if game.SinglePlayer() then 82 | util.AddNetworkString( "gminimap.key" ) 83 | 84 | hook.Add( "PlayerButtonDown", "GMinimap.ButtonDownWorkaround", function( ply, button ) 85 | net.Start( "gminimap.key", true ) 86 | net.WriteUInt( button, 8 ) 87 | net.Send( ply ) 88 | end ) 89 | end 90 | -------------------------------------------------------------------------------- /lua/includes/modules/styled_draw_utils.lua: -------------------------------------------------------------------------------- 1 | SDrawUtils = {} 2 | 3 | local function LogF( str, ... ) 4 | MsgC( Color( 113, 54, 250 ), "[SDrawUtils] ", color_white, string.format( str, ... ), "\n" ) 5 | end 6 | 7 | function SDrawUtils.ModifyColorBrightness( color, brightness ) 8 | local h, s, _ = ColorToHSV( color ) 9 | 10 | -- does not have color metatable 11 | local c = HSVToColor( h, s, brightness ) 12 | 13 | return Color( c.r, c.g, c.b ) 14 | end 15 | 16 | do 17 | -- render targets cannot be destroyed, 18 | -- therefore we should recycle them 19 | local rtCache = {} 20 | 21 | function SDrawUtils.AllocateRT() 22 | -- look for free render targets 23 | for id, rt in ipairs( rtCache ) do 24 | if rt.isFree then 25 | rt.isFree = false 26 | 27 | LogF( "RT #%d was recycled", id ) 28 | return id, rt.texture 29 | end 30 | end 31 | 32 | --[[ 33 | Texture flags used here, in order: 34 | - trilinear texture filtering 35 | - clamp S coordinates 36 | - clamp T coordinates 37 | - no mipmaps 38 | - no LODs (not affected by texture quality settings) 39 | - is a depth render target (duh) 40 | ]] 41 | local flags = bit.bor( 2, 4, 8, 256, 512, 65536 ) 42 | local rt = { isFree = false } 43 | local id = #rtCache + 1 44 | 45 | rtCache[id] = rt 46 | 47 | rt.texture = GetRenderTargetEx( 48 | "sdrawutils_rt_" .. id, 49 | 1024, 1024, 50 | RT_SIZE_LITERAL, 51 | MATERIAL_RT_DEPTH_SEPARATE, 52 | flags, 0, 53 | IMAGE_FORMAT_RGB888 54 | ) 55 | 56 | LogF( "RT #%d was created.", id ) 57 | 58 | return id, rt.texture 59 | end 60 | 61 | function SDrawUtils.FreeRT( id ) 62 | local rt = rtCache[id] 63 | 64 | if rt then 65 | rt.isFree = true 66 | LogF( "RT #%d is free for reuse.", id ) 67 | else 68 | LogF( "Tried to free a inexistent render target #%d", id ) 69 | end 70 | end 71 | end 72 | 73 | do 74 | -- Original implementation from Starfall can be found at: 75 | -- https://github.com/thegrb93/StarfallEx/blob/master/lua/starfall/libs_cl/render.lua 76 | 77 | local segments = 32 78 | local circleMesh = Mesh() 79 | local meshMatrix = Matrix() 80 | local meshVector = Vector() 81 | 82 | mesh.Begin( circleMesh, MATERIAL_POLYGON, segments + 2 ) 83 | 84 | mesh.Position( meshVector ) 85 | mesh.TexCoord( 0, 0.5, 0.5 ) 86 | mesh.Color( 255, 255, 255, 255 ) 87 | mesh.AdvanceVertex() 88 | 89 | for i = 0, segments do 90 | local a = math.rad( ( i / segments ) * -360 ) 91 | local s, c = math.sin( a ), math.cos( a ) 92 | 93 | meshVector:SetUnpacked( s, c, 0 ) 94 | 95 | mesh.Position( meshVector ) 96 | mesh.TexCoord( 0, s / 2 + 0.5, c / 2 + 0.5 ) 97 | mesh.Color( 255, 255, 255, 255 ) 98 | mesh.AdvanceVertex() 99 | end 100 | 101 | mesh.End() 102 | 103 | local meshMaterial = CreateMaterial( "SDrawUtilsMesh", "UnlitGeneric", { 104 | ["$basetexture"] = "color/white", 105 | ["$model"] = 1, 106 | ["$vertexalpha"] = 1, 107 | ["$vertexcolor"] = 1 108 | } ) 109 | 110 | local PushModelMatrix = cam.PushModelMatrix 111 | local PopModelMatrix = cam.PopModelMatrix 112 | local SetMaterial = render.SetMaterial 113 | 114 | function SDrawUtils.DrawFilledCircle( r, x, y, color ) 115 | meshVector:SetUnpacked( color.r / 255, color.g / 255, color.b / 255 ) 116 | 117 | meshMaterial:SetVector( "$color", meshVector ) 118 | meshMaterial:SetFloat( "$alpha", color.a / 255 ) 119 | 120 | SetMaterial( meshMaterial ) 121 | 122 | meshVector:SetUnpacked( x, y, 0 ) 123 | meshMatrix:SetTranslation( meshVector ) 124 | 125 | meshVector:SetUnpacked( r, r, r ) 126 | meshMatrix:SetScale( meshVector ) 127 | 128 | PushModelMatrix( meshMatrix, true ) 129 | circleMesh:Draw() 130 | PopModelMatrix() 131 | end 132 | end 133 | 134 | do 135 | -- our own implementation of DrawTexturedRectRotated 136 | -- that allows us to use floating-point coordinates. 137 | -- Original implementation from Starfall can be found at: 138 | -- https://github.com/thegrb93/StarfallEx/blob/master/lua/starfall/libs_cl/render.lua 139 | 140 | local v1, v2, v3, v4 = Vector(), Vector(), Vector(), Vector() 141 | local m_rad, m_sin, m_cos = math.rad, math.sin, math.cos 142 | local DrawQuad = render.DrawQuad 143 | 144 | local function MakeQuad( x, y, w, h ) 145 | v1.x, v1.y = x, y 146 | v2.x, v2.y = x + w, y 147 | v3.x, v3.y = x + w, y + h 148 | v4.x, v4.y = x, y + h 149 | end 150 | 151 | local function RotateVector( v, x, y, c, s ) 152 | x = v.x * c - v.y * s + x 153 | y = v.x * s + v.y * c + y 154 | v.x = x 155 | v.y = y 156 | end 157 | 158 | function SDrawUtils.DrawTexturedRectRotated( x, y, w, h, angle, color ) 159 | MakeQuad( w * -0.5, h * -0.5, w, h ) 160 | 161 | local r = m_rad( -angle ) 162 | local c, s = m_cos( r ), m_sin( r ) 163 | 164 | RotateVector( v1, x, y, c, s ) 165 | RotateVector( v2, x, y, c, s ) 166 | RotateVector( v3, x, y, c, s ) 167 | RotateVector( v4, x, y, c, s ) 168 | 169 | DrawQuad( v1, v2, v3, v4, color ) 170 | end 171 | end 172 | 173 | --[[ 174 | Utilities to draw images from the web. 175 | 176 | - Download & cache images 177 | - Lazy-loading (in a "dont-spam-http-requests" kind of way) 178 | ]] 179 | local MAT_BUSY = Material( "icon16/hourglass.png", "smooth" ) 180 | local MAT_ERROR = Material( "error" ) 181 | 182 | local CRC = util.CRC 183 | local CACHE_DIR = "cache/drawutils/" 184 | 185 | local loaded = {} 186 | local fetching = 0 187 | 188 | if not file.Exists( CACHE_DIR, "DATA" ) then 189 | file.CreateDir( CACHE_DIR ) 190 | end 191 | 192 | local function GetMaterialFromURL( url ) 193 | if loaded[url] then 194 | return loaded[url] 195 | end 196 | 197 | if string.sub( url, 1, 4 ) ~= "http" then 198 | loaded[url] = Material( url, "smooth ignorez" ) 199 | loaded[url]:GetTexture( "$basetexture" ):Download() 200 | 201 | return loaded[url] 202 | end 203 | 204 | local checksum = CRC( url ) 205 | local path = CACHE_DIR .. checksum .. ".png" 206 | 207 | -- if we already have this image downloaded, create a material from the file 208 | if file.Exists( path, "DATA" ) then 209 | loaded[url] = Material( "data/" .. path, "smooth ignorez" ) 210 | loaded[url]:GetTexture( "$basetexture" ):Download() 211 | 212 | return loaded[url] 213 | end 214 | 215 | -- if we are waiting for 3 images to download already, return a placeholder 216 | if fetching > 3 then return MAT_BUSY end 217 | 218 | -- put a placeholder on the cache for now (to prevent 219 | -- everything in this function from running every frame) 220 | loaded[url] = MAT_BUSY 221 | 222 | -- start the download! 223 | LogF( "Retrieving image: " .. url ) 224 | fetching = fetching + 1 225 | 226 | http.Fetch( url, function( data, _, _, code ) 227 | if tostring( code ):sub( 1, 1 ) ~= "2" then 228 | -- failed, not a success code! 229 | loaded[url] = MAT_ERROR 230 | fetching = fetching - 1 231 | LogF( string.format( "Failed to retrieve image (%d): %s", code, url ) ) 232 | 233 | return 234 | end 235 | 236 | -- success! save it... 237 | file.Write( path, data ) 238 | 239 | -- and create a material from it 240 | loaded[url] = Material( "data/" .. path, "smooth ignorez" ) 241 | loaded[url]:GetTexture( "$basetexture" ):Download() 242 | fetching = fetching - 1 243 | 244 | end, function() 245 | -- failed! 246 | loaded[url] = MAT_ERROR 247 | fetching = fetching - 1 248 | LogF( "Failed to retrieve image: " .. url ) 249 | end ) 250 | 251 | return MAT_BUSY 252 | end 253 | 254 | local RealTime = RealTime 255 | 256 | --[[ 257 | Draw a rotated rectangle using a image from the web. 258 | x and y positions represent the center of the rectangle. 259 | ]] 260 | local SetMaterial = render.SetMaterial 261 | local DrawTexturedRectRotated = SDrawUtils.DrawTexturedRectRotated 262 | 263 | function SDrawUtils.URLTexturedRectRotated( url, x, y, w, h, angle, color ) 264 | local mat = GetMaterialFromURL( url ) 265 | 266 | if mat == MAT_BUSY then 267 | angle = RealTime() * 100 268 | end 269 | 270 | SetMaterial( mat ) 271 | DrawTexturedRectRotated( x, y, w, h, angle, color ) 272 | end 273 | -------------------------------------------------------------------------------- /lua/includes/modules/styled_theme.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | StyledStrike's VGUI theme utilities 3 | 4 | A collection of functions to create common 5 | UI panels and to apply a custom theme to them. 6 | ]] 7 | 8 | StyledTheme = StyledTheme or {} 9 | 10 | --[[ 11 | Setup color constants 12 | ]] 13 | do 14 | StyledTheme.colors = StyledTheme.colors or {} 15 | 16 | local colors = StyledTheme.colors or {} 17 | 18 | colors.accent = Color( 56, 113, 179 ) 19 | colors.panelBackground = Color( 46, 46, 46, 240 ) 20 | colors.panelDisabledBackground = Color( 90, 90, 90, 255 ) 21 | colors.scrollBackground = Color( 0, 0, 0, 200 ) 22 | 23 | colors.labelText = Color( 255, 255, 255, 255 ) 24 | colors.labelTextDisabled = Color( 180, 180, 180, 255 ) 25 | 26 | colors.buttonHover = Color( 150, 150, 150, 50 ) 27 | colors.buttonPress = colors.accent 28 | colors.buttonBorder = Color( 32, 32, 32, 255 ) 29 | colors.buttonText = Color( 255, 255, 255, 255 ) 30 | colors.buttonTextDisabled = Color( 180, 180, 180, 255 ) 31 | 32 | colors.entryBackground = Color( 20, 20, 20, 255 ) 33 | colors.entryBorder = Color( 80, 80, 80, 255 ) 34 | colors.entryHighlight = colors.accent 35 | colors.entryPlaceholder = Color( 150, 150, 150, 255 ) 36 | colors.entryText = Color( 255, 255, 255, 255 ) 37 | end 38 | 39 | --[[ 40 | Setup dimensions 41 | ]] 42 | StyledTheme.dimensions = StyledTheme.dimensions or {} 43 | 44 | hook.Add( "StyledTheme_OnResolutionChange", "StyledTheme.UpdateDimensions", function() 45 | local dimensions = StyledTheme.dimensions 46 | local ScaleSize = StyledTheme.ScaleSize 47 | 48 | dimensions.framePadding = ScaleSize( 10 ) 49 | dimensions.frameButtonSize = ScaleSize( 36 ) 50 | 51 | dimensions.buttonHeight = ScaleSize( 40 ) 52 | dimensions.headerHeight = ScaleSize( 32 ) 53 | 54 | dimensions.scrollBarWidth = ScaleSize( 16 ) 55 | dimensions.scrollPadding = ScaleSize( 8 ) 56 | 57 | dimensions.formPadding = ScaleSize( 20 ) 58 | dimensions.formSeparator = ScaleSize( 6 ) 59 | dimensions.formLabelWidth = ScaleSize( 300 ) 60 | 61 | dimensions.menuPadding = ScaleSize( 6 ) 62 | dimensions.indicatorSize = ScaleSize( 20 ) 63 | end ) 64 | 65 | --[[ 66 | Setup fonts 67 | ]] 68 | StyledTheme.BASE_FONT_NAME = "Roboto" 69 | StyledTheme.fonts = StyledTheme.fonts or {} 70 | 71 | function StyledTheme.RegisterFont( name, screenSize, data ) 72 | data = data or {} 73 | 74 | data.screenSize = screenSize 75 | data.font = data.font or StyledTheme.BASE_FONT_NAME 76 | data.extended = true 77 | 78 | StyledTheme.fonts[name] = data 79 | StyledTheme.forceUpdateResolution = true 80 | end 81 | 82 | StyledTheme.RegisterFont( "StyledTheme_Small", 0.018, { 83 | weight = 500, 84 | } ) 85 | 86 | StyledTheme.RegisterFont( "StyledTheme_Tiny", 0.013, { 87 | weight = 500, 88 | } ) 89 | 90 | hook.Add( "StyledTheme_OnResolutionChange", "StyledTheme.UpdateFonts", function( _, screenH ) 91 | for name, data in pairs( StyledTheme.fonts ) do 92 | data.size = math.floor( screenH * data.screenSize ) 93 | surface.CreateFont( name, data ) 94 | end 95 | end ) 96 | 97 | --[[ 98 | Watch for changes in screen resolution 99 | ]] 100 | do 101 | local screenW, screenH = ScrW(), ScrH() 102 | local Floor = math.floor 103 | 104 | --- Scales the given size (in pixels) from a 1080p resolution to 105 | --- the resolution currently being used by the game. 106 | function StyledTheme.ScaleSize( size ) 107 | return Floor( ( size / 1080 ) * screenH ) 108 | end 109 | 110 | local function UpdateResolution() 111 | screenW, screenH = ScrW(), ScrH() 112 | StyledTheme.forceUpdateResolution = false 113 | hook.Run( "StyledTheme_OnResolutionChange", screenW, screenH ) 114 | end 115 | 116 | -- Only update resolution on gamemode initialization. 117 | hook.Add( "Initialize", "StyledTheme.UpdateResolution", UpdateResolution ) 118 | 119 | local ScrW, ScrH = ScrW, ScrH 120 | 121 | timer.Create( "StyledTheme.CheckResolution", 2, 0, function() 122 | if ScrW() ~= screenW or ScrH() ~= screenH or StyledTheme.forceUpdateResolution then 123 | UpdateResolution() 124 | end 125 | end ) 126 | end 127 | 128 | --[[ 129 | Misc. utility functions 130 | ]] 131 | do 132 | --- Gets a localized language string, with the first character being in uppercase. 133 | function StyledTheme.GetUpperLanguagePhrase( text ) 134 | text = language.GetPhrase( text ) 135 | return text:sub( 1, 1 ):upper() .. text:sub( 2 ) 136 | end 137 | 138 | local SetDrawColor = surface.SetDrawColor 139 | local DrawRect = surface.DrawRect 140 | 141 | --- Draw box, using the specified background color. 142 | --- It allows overriding the alpha while keeping the supplied color table intact. 143 | function StyledTheme.DrawRect( x, y, w, h, color, alpha ) 144 | alpha = alpha or 1 145 | 146 | SetDrawColor( color.r, color.g, color.b, color.a * alpha ) 147 | DrawRect( x, y, w, h ) 148 | end 149 | 150 | local SetMaterial = surface.SetMaterial 151 | local MAT_BLUR = Material( "pp/blurscreen" ) 152 | 153 | --- Blur the background of a panel. 154 | function StyledTheme.BlurPanel( panel, alpha, density ) 155 | SetDrawColor( 255, 255, 255, alpha or panel:GetAlpha() ) 156 | SetMaterial( MAT_BLUR ) 157 | 158 | MAT_BLUR:SetFloat( "$blur", density or 4 ) 159 | MAT_BLUR:Recompute() 160 | 161 | render.UpdateScreenEffectTexture() 162 | 163 | local x, y = panel:LocalToScreen( 0, 0 ) 164 | surface.DrawTexturedRect( -x, -y, ScrW(), ScrH() ) 165 | end 166 | 167 | local cache = {} 168 | 169 | -- Get a material given a path to a material or .png file. 170 | function StyledTheme.GetMaterial( path ) 171 | if cache[path] then 172 | return cache[path] 173 | end 174 | 175 | cache[path] = Material( path, "smooth ignorez" ) 176 | 177 | return cache[path] 178 | end 179 | 180 | local GetMaterial = StyledTheme.GetMaterial 181 | local DrawTexturedRect = surface.DrawTexturedRect 182 | local COLOR_WHITE = Color( 255, 255, 255, 255 ) 183 | 184 | --- Draw a icon, using the specified image file path and color. 185 | --- It allows overriding the alpha while keeping the supplied color table intact. 186 | function StyledTheme.DrawIcon( path, x, y, w, h, alpha, color ) 187 | color = color or COLOR_WHITE 188 | alpha = alpha or 1 189 | 190 | SetMaterial( GetMaterial( path ) ) 191 | SetDrawColor( color.r, color.g, color.b, 255 * alpha ) 192 | DrawTexturedRect( x, y, w, h ) 193 | end 194 | end 195 | 196 | --[[ 197 | Utility function to apply the theme to existing VGUI panels 198 | ]] 199 | do 200 | local ClassFunctions = {} 201 | 202 | function StyledTheme.Apply( panel, classOverride ) 203 | local funcs = ClassFunctions[classOverride or panel.ClassName] 204 | if not funcs then return end 205 | 206 | if funcs.Prepare then 207 | funcs.Prepare( panel ) 208 | end 209 | 210 | if funcs.Paint then 211 | panel.Paint = funcs.Paint 212 | end 213 | 214 | if funcs.UpdateColours then 215 | panel.UpdateColours = funcs.UpdateColours 216 | end 217 | 218 | if funcs.Close then 219 | panel.Close = funcs.Close 220 | end 221 | end 222 | 223 | local colors = StyledTheme.colors 224 | local dimensions = StyledTheme.dimensions 225 | local DrawRect = StyledTheme.DrawRect 226 | 227 | ClassFunctions["DLabel"] = { 228 | Prepare = function( self ) 229 | self:SetColor( colors.labelText ) 230 | self:SetFont( "StyledTheme_Small" ) 231 | end 232 | } 233 | 234 | ClassFunctions["DPanel"] = { 235 | Paint = function( self, w, h ) 236 | DrawRect( 0, 0, w, h, self:GetBackgroundColor() or colors.panelBackground ) 237 | end 238 | } 239 | 240 | local function CustomMenuAdd( self, class ) 241 | local pnl = self:OriginalAdd( class ) 242 | 243 | if class == "DButton" then 244 | StyledTheme.Apply( pnl ) 245 | 246 | timer.Simple( 0, function() 247 | if not IsValid( pnl ) then return end 248 | 249 | pnl:SetPaintBackground( true ) 250 | pnl:SizeToContentsX( StyledTheme.ScaleSize( 20 ) ) 251 | pnl:DockMargin( 0, 0, dimensions.menuPadding, 0 ) 252 | end ) 253 | end 254 | 255 | return pnl 256 | end 257 | 258 | ClassFunctions["DMenuBar"] = { 259 | Prepare = function( self ) 260 | self:SetTall( dimensions.buttonHeight ) 261 | self:DockMargin( 0, 0, 0, 0 ) 262 | self:DockPadding( dimensions.menuPadding, dimensions.menuPadding, dimensions.menuPadding, dimensions.menuPadding ) 263 | 264 | self.OriginalAdd = self.Add 265 | self.Add = CustomMenuAdd 266 | end, 267 | Paint = function( self, w, h ) 268 | DrawRect( 0, 0, w, h, self:GetBackgroundColor() or colors.accent ) 269 | end 270 | } 271 | 272 | local Lerp = Lerp 273 | local FrameTime = FrameTime 274 | 275 | ClassFunctions["DButton"] = { 276 | Prepare = function( self ) 277 | self:SetFont( "StyledTheme_Small" ) 278 | self:SetTall( dimensions.buttonHeight ) 279 | self.animHover = 0 280 | self.animPress = 0 281 | end, 282 | 283 | Paint = function( self, w, h ) 284 | local dt = FrameTime() * 10 285 | local enabled = self:IsEnabled() 286 | 287 | self.animHover = Lerp( dt, self.animHover, ( enabled and self.Hovered ) and 1 or 0 ) 288 | self.animPress = Lerp( dt, self.animPress, ( enabled and ( self:IsDown() or self.m_bSelected ) ) and 1 or 0 ) 289 | 290 | DrawRect( 0, 0, w, h, ( self.isToggle and self.isChecked ) and colors.buttonPress or colors.buttonBorder ) 291 | DrawRect( 1, 1, w - 2, h - 2, enabled and colors.panelBackground or colors.panelDisabledBackground ) 292 | DrawRect( 1, 1, w - 2, h - 2, colors.buttonHover, self.animHover ) 293 | DrawRect( 1, 1, w - 2, h - 2, colors.buttonPress, self.animPress ) 294 | end, 295 | 296 | UpdateColours = function( self ) 297 | if self:IsEnabled() then 298 | self:SetTextStyleColor( colors.buttonText ) 299 | else 300 | self:SetTextStyleColor( colors.buttonTextDisabled ) 301 | end 302 | end 303 | } 304 | 305 | ClassFunctions["DBinder"] = ClassFunctions["DButton"] 306 | 307 | ClassFunctions["DTextEntry"] = { 308 | Prepare = function( self ) 309 | self:SetFont( "StyledTheme_Small" ) 310 | self:SetTall( dimensions.buttonHeight ) 311 | self:SetDrawBorder( false ) 312 | self:SetPaintBackground( false ) 313 | 314 | self:SetTextColor( colors.entryText ) 315 | self:SetCursorColor( colors.entryText ) 316 | self:SetHighlightColor( colors.entryHighlight ) 317 | self:SetPlaceholderColor( colors.entryPlaceholder ) 318 | end, 319 | 320 | Paint = function( self, w, h ) 321 | local enabled = self:IsEnabled() 322 | 323 | DrawRect( 0, 0, w, h, ( self:IsEditing() and enabled ) and colors.entryHighlight or colors.entryBorder ) 324 | DrawRect( 1, 1, w - 2, h - 2, enabled and colors.entryBackground or colors.panelDisabledBackground ) 325 | 326 | derma.SkinHook( "Paint", "TextEntry", self, w, h ) 327 | end 328 | } 329 | 330 | ClassFunctions["DComboBox"] = { 331 | Prepare = function( self ) 332 | self:SetFont( "StyledTheme_Small" ) 333 | self:SetTall( dimensions.buttonHeight ) 334 | self:SetTextColor( colors.entryText ) 335 | self.animHover = 0 336 | end, 337 | 338 | Paint = function( self, w, h ) 339 | local dt = FrameTime() * 10 340 | local enabled = self:IsEnabled() 341 | 342 | self.animHover = Lerp( dt, self.animHover, ( enabled and self.Hovered ) and 1 or 0 ) 343 | 344 | DrawRect( 0, 0, w, h, ( self:IsMenuOpen() and enabled ) and colors.entryHighlight or colors.buttonBorder ) 345 | DrawRect( 1, 1, w - 2, h - 2, enabled and colors.panelBackground or colors.panelDisabledBackground ) 346 | DrawRect( 1, 1, w - 2, h - 2, colors.buttonHover, self.animHover ) 347 | end 348 | } 349 | 350 | ClassFunctions["DNumSlider"] = { 351 | Prepare = function( self ) 352 | StyledTheme.Apply( self.TextArea ) 353 | StyledTheme.Apply( self.Label ) 354 | end 355 | } 356 | 357 | ClassFunctions["DScrollPanel"] = { 358 | Prepare = function( self ) 359 | StyledTheme.Apply( self.VBar ) 360 | 361 | local padding = dimensions.scrollPadding 362 | self.pnlCanvas:DockPadding( padding, padding, padding, padding ) 363 | self:SetPaintBackground( true ) 364 | end, 365 | 366 | Paint = function( self, w, h ) 367 | if self:GetPaintBackground() then 368 | DrawRect( 0, 0, w, h, colors.scrollBackground ) 369 | end 370 | end 371 | } 372 | 373 | local Clamp = math.Clamp 374 | 375 | local function AddScroll( self, delta ) 376 | local oldScroll = self.animTargetScroll or self:GetScroll() 377 | local newScroll = Clamp( oldScroll + delta * 40, 0, self.CanvasSize ) 378 | 379 | if oldScroll == newScroll then 380 | return false 381 | end 382 | 383 | self:Stop() 384 | self.animTargetScroll = newScroll 385 | 386 | local anim = self:NewAnimation( 0.4, 0, 0.25, function( _, pnl ) 387 | pnl.animTargetScroll = nil 388 | end ) 389 | 390 | anim.StartPos = oldScroll 391 | anim.TargetPos = newScroll 392 | 393 | anim.Think = function( a, pnl, fraction ) 394 | pnl:SetScroll( Lerp( fraction, a.StartPos, a.TargetPos ) ) 395 | end 396 | 397 | return true 398 | end 399 | 400 | local function DrawGrip( self, w, h ) 401 | local dt = FrameTime() * 10 402 | 403 | self.animHover = Lerp( dt, self.animHover, self.Hovered and 1 or 0 ) 404 | self.animPress = Lerp( dt, self.animPress, self.Depressed and 1 or 0 ) 405 | 406 | DrawRect( 0, 0, w, h, colors.buttonBorder ) 407 | DrawRect( 1, 1, w - 2, h - 2, colors.panelBackground ) 408 | DrawRect( 1, 1, w - 2, h - 2, colors.buttonHover, self.animHover ) 409 | DrawRect( 1, 1, w - 2, h - 2, colors.buttonPress, self.animPress ) 410 | end 411 | 412 | ClassFunctions["DVScrollBar"] = { 413 | Prepare = function( self ) 414 | self:SetWide( dimensions.scrollBarWidth ) 415 | self:SetHideButtons( true ) 416 | self.AddScroll = AddScroll 417 | 418 | self.btnGrip.animHover = 0 419 | self.btnGrip.animPress = 0 420 | self.btnGrip.Paint = DrawGrip 421 | end, 422 | 423 | Paint = function( _, w, h ) 424 | DrawRect( 0, 0, w, h, colors.scrollBackground ) 425 | end 426 | } 427 | 428 | local function FrameSlideAnim( anim, panel, fraction ) 429 | if not anim.StartPos then 430 | anim.StartPos = Vector( panel.x, panel.y + anim.StartOffset, 0 ) 431 | anim.TargetPos = Vector( panel.x, panel.y + anim.EndOffset, 0 ) 432 | end 433 | 434 | local pos = LerpVector( fraction, anim.StartPos, anim.TargetPos ) 435 | panel:SetPos( pos.x, pos.y ) 436 | panel:SetAlpha( 255 * Lerp( fraction, anim.StartAlpha, anim.EndAlpha ) ) 437 | end 438 | 439 | local function FramePerformLayout( self, w ) 440 | local padding = dimensions.framePadding 441 | local buttonSize = dimensions.frameButtonSize 442 | 443 | self.btnClose:SetSize( buttonSize, buttonSize ) 444 | self.btnClose:SetPos( w - self.btnClose:GetWide() - padding, padding ) 445 | 446 | local iconMargin = 0 447 | 448 | if IsValid( self.imgIcon ) then 449 | local iconSize = buttonSize * 0.6 450 | 451 | self.imgIcon:SetPos( padding, padding + ( buttonSize * 0.5 ) - ( iconSize * 0.5 ) ) 452 | self.imgIcon:SetSize( iconSize, iconSize ) 453 | 454 | iconMargin = iconSize + padding * 0.5 455 | end 456 | 457 | self.lblTitle:SetPos( padding + iconMargin, padding ) 458 | self.lblTitle:SetSize( w - ( padding * 2 ) - iconMargin, buttonSize ) 459 | end 460 | 461 | ClassFunctions["DFrame"] = { 462 | Prepare = function( self ) 463 | self._OriginalClose = self.Close 464 | self.PerformLayout = FramePerformLayout 465 | 466 | StyledTheme.Apply( self.btnClose ) 467 | StyledTheme.Apply( self.lblTitle ) 468 | 469 | local padding = dimensions.framePadding 470 | local buttonSize = dimensions.frameButtonSize 471 | 472 | self:DockPadding( padding, buttonSize + padding * 2, padding, padding ) 473 | self.btnClose:SetText( "X" ) 474 | 475 | if IsValid( self.btnMaxim ) then 476 | self.btnMaxim:Remove() 477 | end 478 | 479 | if IsValid( self.btnMinim ) then 480 | self.btnMinim:Remove() 481 | end 482 | 483 | local anim = self:NewAnimation( 0.4, 0, 0.25 ) 484 | anim.StartOffset = -80 485 | anim.EndOffset = 0 486 | anim.StartAlpha = 0 487 | anim.EndAlpha = 1 488 | anim.Think = FrameSlideAnim 489 | end, 490 | 491 | Close = function( self ) 492 | self:SetMouseInputEnabled( false ) 493 | self:SetKeyboardInputEnabled( false ) 494 | 495 | if self.OnStartClosing then 496 | self.OnStartClosing() 497 | end 498 | 499 | local anim = self:NewAnimation( 0.2, 0, 0.5, function() 500 | self:_OriginalClose() 501 | end ) 502 | 503 | anim.StartOffset = 0 504 | anim.EndOffset = -80 505 | anim.StartAlpha = 1 506 | anim.EndAlpha = 0 507 | anim.Think = FrameSlideAnim 508 | end, 509 | 510 | Paint = function( self, w, h ) 511 | if self.m_bBackgroundBlur then 512 | Derma_DrawBackgroundBlur( self, self.m_fCreateTime ) 513 | else 514 | StyledTheme.BlurPanel( self ) 515 | end 516 | 517 | DrawRect( 0, 0, w, h, colors.panelBackground, self:GetAlpha() / 255 ) 518 | end 519 | } 520 | end 521 | 522 | --[[ 523 | Utility functions to create "form" panels. 524 | ]] 525 | do 526 | local colors = StyledTheme.colors 527 | local dimensions = StyledTheme.dimensions 528 | 529 | function StyledTheme.CreateFormHeader( parent, text, mtop, mbottom ) 530 | mtop = mtop or dimensions.formSeparator 531 | mbottom = mbottom or dimensions.formSeparator 532 | 533 | local panel = vgui.Create( "DPanel", parent ) 534 | panel:SetTall( dimensions.headerHeight ) 535 | panel:Dock( TOP ) 536 | panel:DockMargin( -dimensions.formPadding, mtop, -dimensions.formPadding, mbottom ) 537 | panel:SetBackgroundColor( colors.scrollBackground ) 538 | 539 | StyledTheme.Apply( panel ) 540 | 541 | local label = vgui.Create( "DLabel", panel ) 542 | label:SetText( text ) 543 | label:SetContentAlignment( 5 ) 544 | label:SizeToContents() 545 | label:Dock( FILL ) 546 | 547 | StyledTheme.Apply( label ) 548 | 549 | return panel 550 | end 551 | 552 | function StyledTheme.CreateFormLabel( parent, text ) 553 | local label = vgui.Create( "DLabel", parent ) 554 | label:Dock( TOP ) 555 | label:DockMargin( 0, 0, 0, dimensions.formSeparator ) 556 | label:SetText( text ) 557 | label:SetTall( dimensions.buttonHeight ) 558 | 559 | StyledTheme.Apply( label ) 560 | 561 | return label 562 | end 563 | 564 | function StyledTheme.CreateFormButton( parent, label, callback ) 565 | local button = vgui.Create( "DButton", parent ) 566 | button:SetText( label ) 567 | button:Dock( TOP ) 568 | button:DockMargin( 0, 0, 0, dimensions.formSeparator ) 569 | button.DoClick = callback 570 | 571 | StyledTheme.Apply( button ) 572 | 573 | return button 574 | end 575 | 576 | function StyledTheme.CreateFormToggle( parent, label, isChecked, callback ) 577 | local button = vgui.Create( "DButton", parent ) 578 | button:SetIcon( isChecked and "icon16/accept.png" or "icon16/cancel.png" ) 579 | button:SetText( label ) 580 | button:Dock( TOP ) 581 | button:DockMargin( 0, 0, 0, dimensions.formSeparator ) 582 | button.isToggle = true 583 | button.isChecked = isChecked 584 | 585 | StyledTheme.Apply( button ) 586 | 587 | button.SetChecked = function( s, value ) 588 | value = value == true 589 | s.isChecked = value 590 | button:SetIcon( value and "icon16/accept.png" or "icon16/cancel.png" ) 591 | callback( value ) 592 | end 593 | 594 | button.DoClick = function( s ) 595 | s:SetChecked( not s.isChecked ) 596 | end 597 | 598 | return button 599 | end 600 | 601 | function StyledTheme.CreateFormSlider( parent, label, default, min, max, decimals, callback ) 602 | local slider = vgui.Create( "DNumSlider", parent ) 603 | slider:SetText( label ) 604 | slider:SetMin( min ) 605 | slider:SetMax( max ) 606 | slider:SetValue( default ) 607 | slider:SetDecimals( decimals ) 608 | slider:Dock( TOP ) 609 | slider:DockMargin( 0, 0, 0, dimensions.formSeparator ) 610 | 611 | slider.PerformLayout = function( s ) 612 | s.Label:SetWide( dimensions.formLabelWidth ) 613 | end 614 | 615 | StyledTheme.Apply( slider ) 616 | 617 | slider.OnValueChanged = function( _, value ) 618 | callback( decimals == 0 and math.floor( value ) or math.Round( value, decimals ) ) 619 | end 620 | 621 | return slider 622 | end 623 | 624 | function StyledTheme.CreateFormCombo( parent, text, options, defaultIndex, callback ) 625 | local panel = vgui.Create( "DPanel", parent ) 626 | panel:SetTall( dimensions.buttonHeight ) 627 | panel:SetPaintBackground( false ) 628 | panel:Dock( TOP ) 629 | panel:DockMargin( 0, 0, 0, dimensions.formSeparator ) 630 | 631 | local label = vgui.Create( "DLabel", panel ) 632 | label:Dock( LEFT ) 633 | label:DockMargin( 0, 0, 0, 0 ) 634 | label:SetText( text ) 635 | label:SetWide( dimensions.formLabelWidth ) 636 | 637 | StyledTheme.Apply( label ) 638 | 639 | local combo = vgui.Create( "DComboBox", panel ) 640 | combo:Dock( FILL ) 641 | combo:SetSortItems( false ) 642 | 643 | for _, v in ipairs( options ) do 644 | combo:AddChoice( v ) 645 | end 646 | 647 | if defaultIndex then 648 | combo:ChooseOptionID( defaultIndex ) 649 | end 650 | 651 | StyledTheme.Apply( combo ) 652 | 653 | combo.OnSelect = function( _, index ) 654 | callback( index ) 655 | end 656 | end 657 | 658 | function StyledTheme.CreateFormBinder( parent, text, defaultKey ) 659 | local panel = vgui.Create( "DPanel", parent ) 660 | panel:SetTall( dimensions.buttonHeight ) 661 | panel:SetPaintBackground( false ) 662 | panel:Dock( TOP ) 663 | panel:DockMargin( 0, 0, 0, dimensions.formSeparator ) 664 | 665 | local label = vgui.Create( "DLabel", panel ) 666 | label:Dock( LEFT ) 667 | label:DockMargin( 0, 0, 0, 0 ) 668 | label:SetText( text ) 669 | label:SetWide( dimensions.formLabelWidth ) 670 | 671 | StyledTheme.Apply( label ) 672 | 673 | local binder = vgui.Create( "DBinder", panel ) 674 | binder:SetValue( defaultKey or KEY_NONE ) 675 | binder:Dock( FILL ) 676 | 677 | StyledTheme.Apply( binder ) 678 | 679 | return binder 680 | end 681 | end 682 | -------------------------------------------------------------------------------- /lua/includes/modules/styled_theme_tabbed_frame.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | StyledStrike's VGUI theme utilities 3 | 4 | This file adds a new panel class: the tabbed frame 5 | ]] 6 | 7 | if not StyledTheme then 8 | error( "styled_theme.lua must be included first!" ) 9 | end 10 | 11 | local colors = StyledTheme.colors 12 | local dimensions = StyledTheme.dimensions 13 | 14 | local TAB_BUTTON = {} 15 | 16 | AccessorFunc( TAB_BUTTON, "iconPath", "Icon", FORCE_STRING ) 17 | 18 | function TAB_BUTTON:Init() 19 | self:SetCursor( "hand" ) 20 | self:SetIcon( "icon16/bricks.png" ) 21 | 22 | self.isSelected = false 23 | self.notificationCount = 0 24 | self.animHover = 0 25 | end 26 | 27 | function TAB_BUTTON:OnMousePressed( keyCode ) 28 | if keyCode == MOUSE_LEFT then 29 | self:GetParent():GetParent():SetActiveTab( self.tab ) 30 | end 31 | end 32 | 33 | local Lerp = Lerp 34 | local FrameTime = FrameTime 35 | local DrawRect = StyledTheme.DrawRect 36 | local DrawIcon = StyledTheme.DrawIcon 37 | 38 | local COLOR_INDICATOR = Color( 200, 0, 0, 255 ) 39 | 40 | function TAB_BUTTON:Paint( w, h ) 41 | self.animHover = Lerp( FrameTime() * 10, self.animHover, self:IsHovered() and 1 or 0 ) 42 | 43 | DrawRect( 0, 0, w, h, colors.buttonBorder ) 44 | DrawRect( 1, 1, w - 2, h - 2, colors.panelBackground ) 45 | DrawRect( 1, 1, w - 2, h - 2, colors.buttonHover, self.animHover ) 46 | 47 | if self.isSelected then 48 | DrawRect( 1, 1, w - 2, h - 2, colors.buttonPress ) 49 | end 50 | 51 | local iconSize = math.floor( math.max( w, h ) * 0.5 ) 52 | DrawIcon( self.iconPath, ( w * 0.5 ) - ( iconSize * 0.5 ), ( h * 0.5 ) - ( iconSize * 0.5 ), iconSize, iconSize ) 53 | 54 | if self.notificationCount > 0 then 55 | local size = dimensions.indicatorSize 56 | local margin = math.floor( h * 0.05 ) 57 | local x = w - size - margin 58 | local y = h - size - margin 59 | 60 | draw.RoundedBox( size * 0.5, x, y, size, size, COLOR_INDICATOR ) 61 | draw.SimpleText( self.notificationCount, "StyledTheme_Tiny", x + size * 0.5, y + size * 0.5, colors.buttonText, 1, 1 ) 62 | end 63 | end 64 | 65 | vgui.Register( "Styled_TabButton", TAB_BUTTON, "DPanel" ) 66 | 67 | local TABBED_FRAME = {} 68 | local ScaleSize = StyledTheme.ScaleSize 69 | 70 | function TABBED_FRAME:Init() 71 | StyledTheme.Apply( self, "DFrame" ) 72 | 73 | local w = ScaleSize( 850 ) 74 | local h = ScaleSize( 600 ) 75 | 76 | self:SetSize( w, h ) 77 | self:SetSizable( true ) 78 | self:SetDraggable( true ) 79 | self:SetDeleteOnClose( true ) 80 | self:SetScreenLock( true ) 81 | self:SetMinWidth( w ) 82 | self:SetMinHeight( h ) 83 | 84 | self.tabList = vgui.Create( "DPanel", self ) 85 | self.tabList:SetWide( ScaleSize( 64 ) ) 86 | self.tabList:Dock( LEFT ) 87 | self.tabList:DockPadding( 0, 0, 0, 0 ) 88 | self.tabList:SetPaintBackground( false ) 89 | --StyledTheme.Apply( self.tabList ) 90 | 91 | self.contentContainer = vgui.Create( "DPanel", self ) 92 | self.contentContainer:Dock( FILL ) 93 | self.contentContainer:DockMargin( ScaleSize( 4 ), 0, 0, 0 ) 94 | self.contentContainer:DockPadding( 0, 0, 0, 0 ) 95 | self.contentContainer:SetPaintBackground( false ) 96 | 97 | self.tabs = {} 98 | end 99 | 100 | function TABBED_FRAME:AddTab( icon, tooltip, panelClass ) 101 | panelClass = panelClass or "DScrollPanel" 102 | 103 | local tab = {} 104 | 105 | tab.button = vgui.Create( "Styled_TabButton", self.tabList ) 106 | tab.button:SetIcon( icon ) 107 | tab.button:SetTall( ScaleSize( 64 ) ) 108 | tab.button:SetTooltip( tooltip ) 109 | tab.button:Dock( TOP ) 110 | tab.button:DockMargin( 0, 0, 0, 2 ) 111 | tab.button.tab = tab 112 | 113 | tab.panel = vgui.Create( panelClass, self.contentContainer ) 114 | tab.panel:Dock( FILL ) 115 | tab.panel:DockMargin( 0, 0, 0, 0 ) 116 | tab.panel:DockPadding( 0, 0, 0, 0 ) 117 | tab.panel:SetVisible( false ) 118 | 119 | StyledTheme.Apply( tab.panel ) 120 | 121 | if panelClass == "DScrollPanel" then 122 | local padding = dimensions.formPadding 123 | tab.panel.pnlCanvas:DockPadding( padding, 0, padding, padding ) 124 | end 125 | 126 | self.tabs[#self.tabs + 1] = tab 127 | 128 | if #self.tabs == 1 then 129 | self:SetActiveTab( tab ) 130 | end 131 | 132 | return tab.panel 133 | end 134 | 135 | function TABBED_FRAME:SetActiveTab( tab ) 136 | for i, t in ipairs( self.tabs ) do 137 | local isThisOne = t == tab 138 | 139 | t.button.isSelected = isThisOne 140 | t.panel:SetVisible( isThisOne ) 141 | 142 | if isThisOne then 143 | self.lastTabIndex = i 144 | end 145 | end 146 | end 147 | 148 | function TABBED_FRAME:SetActiveTabByIndex( index ) 149 | if self.tabs[index] then 150 | self:SetActiveTab( self.tabs[index] ) 151 | end 152 | end 153 | 154 | function TABBED_FRAME:SetTabNotificationCountByIndex( index, count ) 155 | if self.tabs[index] then 156 | self.tabs[index].button.notificationCount = count 157 | end 158 | end 159 | 160 | vgui.Register( "Styled_TabbedFrame", TABBED_FRAME, "DFrame" ) 161 | -------------------------------------------------------------------------------- /lua/lambdaplayers/extaddon/client/gminimap_blips.lua: -------------------------------------------------------------------------------- 1 | hook.Add( "OnEntityCreated", "GMinimap.TrackLambdaPlayers", function( ent ) 2 | if not IsValid( ent ) then return end 3 | if ent:GetClass() ~= "npc_lambdaplayer" then return end 4 | 5 | GMinimap:AddBlip( { 6 | id = "lambda_player_" .. ent:EntIndex(), 7 | icon = "gminimap/blips/npc_default.png", 8 | parent = ent, 9 | indicateAng = true, 10 | scale = 0.8 11 | } ) 12 | end ) 13 | -------------------------------------------------------------------------------- /materials/gminimap/blips/anchor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/anchor.png -------------------------------------------------------------------------------- /materials/gminimap/blips/boost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/boost.png -------------------------------------------------------------------------------- /materials/gminimap/blips/bullets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/bullets.png -------------------------------------------------------------------------------- /materials/gminimap/blips/car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/car.png -------------------------------------------------------------------------------- /materials/gminimap/blips/chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/chat.png -------------------------------------------------------------------------------- /materials/gminimap/blips/chip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/chip.png -------------------------------------------------------------------------------- /materials/gminimap/blips/computer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/computer.png -------------------------------------------------------------------------------- /materials/gminimap/blips/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/cross.png -------------------------------------------------------------------------------- /materials/gminimap/blips/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/default.png -------------------------------------------------------------------------------- /materials/gminimap/blips/diamond.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/diamond.png -------------------------------------------------------------------------------- /materials/gminimap/blips/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/download.png -------------------------------------------------------------------------------- /materials/gminimap/blips/drone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/drone.png -------------------------------------------------------------------------------- /materials/gminimap/blips/fast_forward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/fast_forward.png -------------------------------------------------------------------------------- /materials/gminimap/blips/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/file.png -------------------------------------------------------------------------------- /materials/gminimap/blips/fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/fire.png -------------------------------------------------------------------------------- /materials/gminimap/blips/globe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/globe.png -------------------------------------------------------------------------------- /materials/gminimap/blips/gun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/gun.png -------------------------------------------------------------------------------- /materials/gminimap/blips/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/heart.png -------------------------------------------------------------------------------- /materials/gminimap/blips/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/home.png -------------------------------------------------------------------------------- /materials/gminimap/blips/jerry_can.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/jerry_can.png -------------------------------------------------------------------------------- /materials/gminimap/blips/jet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/jet.png -------------------------------------------------------------------------------- /materials/gminimap/blips/joystick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/joystick.png -------------------------------------------------------------------------------- /materials/gminimap/blips/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/key.png -------------------------------------------------------------------------------- /materials/gminimap/blips/lab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/lab.png -------------------------------------------------------------------------------- /materials/gminimap/blips/npc_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/npc_default.png -------------------------------------------------------------------------------- /materials/gminimap/blips/office.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/office.png -------------------------------------------------------------------------------- /materials/gminimap/blips/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/pause.png -------------------------------------------------------------------------------- /materials/gminimap/blips/phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/phone.png -------------------------------------------------------------------------------- /materials/gminimap/blips/plane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/plane.png -------------------------------------------------------------------------------- /materials/gminimap/blips/play_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/play_next.png -------------------------------------------------------------------------------- /materials/gminimap/blips/question.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/question.png -------------------------------------------------------------------------------- /materials/gminimap/blips/return.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/return.png -------------------------------------------------------------------------------- /materials/gminimap/blips/rocket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/rocket.png -------------------------------------------------------------------------------- /materials/gminimap/blips/satellite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/satellite.png -------------------------------------------------------------------------------- /materials/gminimap/blips/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/search.png -------------------------------------------------------------------------------- /materials/gminimap/blips/sports_car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/sports_car.png -------------------------------------------------------------------------------- /materials/gminimap/blips/stairs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/stairs.png -------------------------------------------------------------------------------- /materials/gminimap/blips/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/star.png -------------------------------------------------------------------------------- /materials/gminimap/blips/sword.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/sword.png -------------------------------------------------------------------------------- /materials/gminimap/blips/tank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/tank.png -------------------------------------------------------------------------------- /materials/gminimap/blips/tweak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/tweak.png -------------------------------------------------------------------------------- /materials/gminimap/blips/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/blips/user.png -------------------------------------------------------------------------------- /materials/gminimap/dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/dead.png -------------------------------------------------------------------------------- /materials/gminimap/gminimap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/gminimap.png -------------------------------------------------------------------------------- /materials/gminimap/heading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/heading.png -------------------------------------------------------------------------------- /materials/gminimap/player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StyledStrike/gmod-gminimap/eaf5ba89928c8b2b5d0621f2ab13085f1f06595c/materials/gminimap/player.png -------------------------------------------------------------------------------- /resource/localization/en/gminimap.properties: -------------------------------------------------------------------------------- 1 | 2 | gminimap.title=GMinimap 3 | gminimap.configure=Configure 4 | gminimap.configure_minimap=Configure minimap 5 | gminimap.enable=Enable Minimap 6 | gminimap.reset=Reset Config 7 | gminimap.reset_query=Reset all settings back to default? 8 | gminimap.yes=Yes 9 | gminimap.no=No 10 | gminimap.ok=OK 11 | gminimap.invalid_bind=Cannot use %s! 12 | gminimap.toggle_key=Toggle Key 13 | gminimap.expand_key=Expand Key 14 | gminimap.radar=Radar 15 | gminimap.zoom=Zoom 16 | gminimap.lock_rotation=Lock rotation 17 | gminimap.pivot_offset=Pivot offset 18 | gminimap.forced_config=(Some of these are locked on this server) 19 | gminimap.width=Width 20 | gminimap.height=Height 21 | gminimap.border_thickness=Border Thickness 22 | gminimap.border_color=Border Color 23 | gminimap.terrain=Terrain 24 | gminimap.terrain_color=Color/Tint 25 | gminimap.terrain_brightness=Brightness 26 | gminimap.terrain_saturation=Saturation 27 | gminimap.terrain_color_inv=Invert Colors 28 | gminimap.terrain_lighting=Show Lighting 29 | gminimap.health_armor=Custom Health/Armor 30 | gminimap.health_show_custom=Show Custom Health/Armor 31 | gminimap.health_hide_default=Hide Default Health/Armor 32 | gminimap.health_height=Health/Armor Bar Height 33 | gminimap.health_color=Health Color 34 | gminimap.low_health_color=Low Health Color 35 | gminimap.armor_color=Armor Color 36 | gminimap.landmarks=Landmarks 37 | gminimap.landmarks_help1=Right-click on the map to add a landmark 38 | gminimap.landmarks_help2=Click and drag to move the map, use the mouse wheel for zooming 39 | gminimap.scale=Scale 40 | gminimap.builtin_icons=GMinimap Icons 41 | gminimap.gmod_icons=Silkicons 42 | gminimap.landmark_remove=Remove landmark 43 | gminimap.landmark_remove_query=Are you sure you want to remove this landmark? 44 | gminimap.landmark_label=Label 45 | gminimap.landmark_icon=Icon 46 | gminimap.landmark_icon_color=Icon color 47 | gminimap.layers=Layers 48 | gminimap.layer_top=Top 49 | gminimap.layer_bottom=Bottom 50 | gminimap.layer_default=Default layer 51 | gminimap.layer_number=Layer %d 52 | gminimap.layer_boundaries=%s boundaries 53 | gminimap.file=File 54 | gminimap.import=Import... 55 | gminimap.map_settings_not_found=Map settings file '%s' does not exist! 56 | gminimap.map_settings_found=Map settings file '%s' has been imported! 57 | gminimap.export=Export... 58 | gminimap.export_tip1=To make the current map use these layers, create a .json file on: 59 | gminimap.export_tip2= 60 | gminimap.export_tip3=Then, paste this code: 61 | gminimap.copy_code=Copy code 62 | gminimap.code_copied=Code has been copied to your clipboard! 63 | gminimap.add_layer=Add layer 64 | gminimap.remove_layer=Remove selected layer 65 | gminimap.remove_layer_blocked=You cannot remove the default layer. 66 | gminimap.layer_user_position=Use ceiling/floor from where you're standing now -------------------------------------------------------------------------------- /resource/localization/es-ES/gminimap.properties: -------------------------------------------------------------------------------- 1 | 2 | gminimap.title=GMinimap 3 | gminimap.configure=Configurar 4 | gminimap.configure_minimap=Configurar Minimapa 5 | gminimap.enable=Activar Minimapa 6 | gminimap.reset=Restablecer Configuración 7 | gminimap.reset_query=¿Restablecer todos los ajustes a su estado por defecto? 8 | gminimap.yes=Si 9 | gminimap.no=No 10 | gminimap.ok=Confirmar 11 | gminimap.invalid_bind=¡No puedo usar %s! 12 | gminimap.toggle_key=Tecla para Alternar 13 | gminimap.expand_key=Tecla para Expandir 14 | gminimap.radar=Radar 15 | gminimap.zoom=Zoom 16 | gminimap.lock_rotation=Bloquear la rotación 17 | gminimap.pivot_offset=Desplazamiento de pivote 18 | gminimap.forced_config=(Algunos de estos están bloqueados en este servidor) 19 | gminimap.width=Ancho 20 | gminimap.height=Alto 21 | gminimap.border_thickness=Grosor del Borde 22 | gminimap.border_color=Color del Borde 23 | gminimap.terrain=Terreno 24 | gminimap.terrain_color=Color/Tinte 25 | gminimap.terrain_brightness=Brillo 26 | gminimap.terrain_saturation=Saturación 27 | gminimap.terrain_color_inv=Invertir Colores 28 | gminimap.terrain_lighting=Mostrar Iluminación 29 | gminimap.health_armor=Vida/Armadura Personalizado 30 | gminimap.health_show_custom=Mostrar Vida/Armadura Personalizado 31 | gminimap.health_hide_default=Ocultar Vida/Armadura por Defecto 32 | gminimap.health_height=Altura de la barra de Vida/Armadura 33 | gminimap.health_color=Color de Vida 34 | gminimap.low_health_color=Color de Vida Baja 35 | gminimap.armor_color=Color de Armadura 36 | gminimap.landmarks=Puntos de Referencia 37 | gminimap.landmarks_help1=Pulsa Click Derecho en el mapa para añadir un punto de referencia 38 | gminimap.landmarks_help2=Pulsa Click y arrastra para mover el mapa, usa la rueda del ratón para hacer zoom 39 | gminimap.scale=Escala 40 | gminimap.builtin_icons=Iconos de GMinimap 41 | gminimap.gmod_icons=Silkicons 42 | gminimap.landmark_remove=Eliminar Punto de Referencia 43 | gminimap.landmark_remove_query=¿Estás seguro de eliminar este punto de referencia? 44 | gminimap.landmark_label=Etiqueta 45 | gminimap.landmark_icon=Icono 46 | gminimap.landmark_icon_color=Color del icono 47 | gminimap.layers=Capas 48 | gminimap.layer_top=Más alto 49 | gminimap.layer_bottom=Abajo 50 | gminimap.layer_default=Capa predeterminada 51 | gminimap.layer_number=Capa %d 52 | gminimap.layer_boundaries=Límites de %s 53 | gminimap.file=Archivo 54 | gminimap.import=Importar... 55 | gminimap.map_settings_not_found=¡El archivo de configuración del mapa '%s' no existe! 56 | gminimap.map_settings_found=¡El archivo de configuración del mapa '%s' ha sido importado! 57 | gminimap.export=Exportar... 58 | gminimap.export_tip1=Para que el mapa actual utilice estas capas, cree un archivo .json en: 59 | gminimap.export_tip2= 60 | gminimap.export_tip3=Luego, pega este código: 61 | gminimap.copy_code=Copiar código 62 | gminimap.code_copied=¡El código ha sido copiado a tu portapapeles! 63 | gminimap.add_layer=Agregar capa 64 | gminimap.remove_layer=Eliminar capa seleccionada 65 | gminimap.remove_layer_blocked=No puede eliminar la capa predeterminada. 66 | gminimap.layer_user_position=Usa el techo/suelo desde donde estás parado ahora -------------------------------------------------------------------------------- /resource/localization/pt-br/gminimap.properties: -------------------------------------------------------------------------------- 1 | 2 | gminimap.title=GMinimap 3 | gminimap.configure=Configurar 4 | gminimap.configure_minimap=Configurar Minimapa 5 | gminimap.enable=Habilitar Minimapa 6 | gminimap.reset=Restaurar Config. 7 | gminimap.reset_query=Restaurar todas as opções de volta ao padrão? 8 | gminimap.yes=Sim 9 | gminimap.no=Não 10 | gminimap.ok=OK 11 | gminimap.invalid_bind=Não pode usar %s! 12 | gminimap.toggle_key=Tecla para Mostrar/Ocultar 13 | gminimap.expand_key=Tecla para Expandir 14 | gminimap.radar=Radar 15 | gminimap.zoom=Zoom 16 | gminimap.lock_rotation=Travar rotação 17 | gminimap.pivot_offset=Posição do pivote 18 | gminimap.forced_config=(Alguns destes valores estão bloqueados neste servidor) 19 | gminimap.width=Largura 20 | gminimap.height=Altura 21 | gminimap.border_thickness=Espessura da Borda 22 | gminimap.border_color=Cor da Borda 23 | gminimap.terrain=Terreno 24 | gminimap.terrain_color=Coloração 25 | gminimap.terrain_brightness=Brilho 26 | gminimap.terrain_saturation=Saturação 27 | gminimap.terrain_color_inv=Inverter Cores 28 | gminimap.terrain_lighting=Mostrar Iluminação 29 | gminimap.health_armor=Vida/Colete Customizado 30 | gminimap.health_show_custom=Mostrar Vida/Colete Customizado 31 | gminimap.health_hide_default=Ocultar Vida/Colete Padrão 32 | gminimap.health_height=Altura da Vida/Colete 33 | gminimap.health_color=Cor da Vida 34 | gminimap.low_health_color=Cor da Vida Baixa 35 | gminimap.armor_color=Cor do Colete 36 | gminimap.landmarks=Pontos de Interesse 37 | gminimap.landmarks_help1=Clique com o botão direito para criar um ponto de interesse 38 | gminimap.landmarks_help2=Clique e segure para mover o mapa, use a roda do mouse para o zoom 39 | gminimap.scale=Escala 40 | gminimap.builtin_icons=Ícones GMinimap 41 | gminimap.gmod_icons=Silkicons 42 | gminimap.landmark_remove=Remover Ponto 43 | gminimap.landmark_remove_query=Tem certeza que quer remover este ponto de interesse? 44 | gminimap.landmark_label=Etiqueta 45 | gminimap.landmark_icon=Ícone 46 | gminimap.landmark_icon_color=Cor do ícone 47 | gminimap.layers=Camadas 48 | gminimap.layer_top=Superior 49 | gminimap.layer_bottom=Inferior 50 | gminimap.layer_default=Camada padrão 51 | gminimap.layer_number=Camada %d 52 | gminimap.layer_boundaries=Limites de %s 53 | gminimap.file=Arquivo 54 | gminimap.import=Importar... 55 | gminimap.map_settings_not_found=O arquivo de configurações do mapa '%s' não existe! 56 | gminimap.map_settings_found=O arquivo de configurações do mapa '%s' foi importado! 57 | gminimap.export=Exportar... 58 | gminimap.export_tip1=Para fazer com que o mapa atual use essas camadas, crie um arquivo .json em: 59 | gminimap.export_tip2= 60 | gminimap.export_tip3=Em seguida, cole este código: 61 | gminimap.copy_code=Copiar código 62 | gminimap.code_copied=O código foi copiado para sua área de transferência! 63 | gminimap.add_layer=Adicionar camada 64 | gminimap.remove_layer=Remover camada selecionada 65 | gminimap.remove_layer_blocked=Você não pode remover a camada padrão. 66 | gminimap.layer_user_position=Use o teto/chão de onde você está agora -------------------------------------------------------------------------------- /resource/localization/ru/gminimap.properties: -------------------------------------------------------------------------------- 1 | 2 | gminimap.title=GMinimap 3 | gminimap.configure=Настройки 4 | gminimap.configure_minimap=Настроить мини-карту 5 | gminimap.enable=Включить мини-карту 6 | gminimap.reset=Сбросить настройки 7 | gminimap.reset_query=Сбросить настройки по умолчанию? 8 | gminimap.yes=Да 9 | gminimap.no=Нет 10 | gminimap.ok=OK 11 | gminimap.invalid_bind=Невозможно использовать %s! 12 | gminimap.expand_key=Переключить расширение карты 13 | gminimap.toggle_key=Переключить отображение карты 14 | gminimap.radar=Радар 15 | gminimap.zoom=Масштаб 16 | gminimap.lock_rotation=Заблокировать вращение 17 | gminimap.pivot_offset=Смещение 18 | gminimap.forced_config=(Некоторые настройки принудительно установлены сервером) 19 | gminimap.width=Ширина 20 | gminimap.height=Высота 21 | gminimap.border_thickness=Толщина обводки 22 | gminimap.border_color=Цвет обводки 23 | gminimap.terrain=Ландшафт 24 | gminimap.terrain_color=Цвет/Оттенок 25 | gminimap.terrain_brightness=Яркость 26 | gminimap.terrain_saturation=Насыщенность 27 | gminimap.terrain_color_inv=Инвертирование 28 | gminimap.terrain_lighting=Отображать освещение 29 | gminimap.health_armor=Здоровье/Броня 30 | gminimap.health_show_custom=Показывать здоровье/броню 31 | gminimap.health_hide_default=Отключить движковые здоровье/броню 32 | gminimap.health_height=Высота полоски здоровья/брони 33 | gminimap.health_color=Цвет здоровья 34 | gminimap.low_health_color=Цвет здоровья (низкое) 35 | gminimap.armor_color=Цвет брони 36 | gminimap.landmarks=Точки 37 | gminimap.landmarks_help1=Нажмите ПКМ, чтобы добавить точку на карту 38 | gminimap.landmarks_help2=Удерживайте ЛКМ, чтобы двигать карту. Используйте колёсико мыши для приближения 39 | gminimap.scale=Масштаб 40 | gminimap.builtin_icons=Иконки GMinimap 41 | gminimap.gmod_icons=Silkicons 42 | gminimap.landmark_remove=Удалить точку 43 | gminimap.landmark_remove_query=Вы уверены, что хотите удалить эту точку? 44 | gminimap.landmark_label=Точка 45 | gminimap.landmark_icon=Иконка 46 | gminimap.landmark_icon_color=Цвет иконки 47 | gminimap.layers=Слои 48 | gminimap.layer_top=Верх 49 | gminimap.layer_bottom=Низ 50 | gminimap.layer_default=стандартного слоя 51 | gminimap.layer_number=слоя %d 52 | gminimap.layer_boundaries=Границы %s 53 | gminimap.file=Файл 54 | gminimap.import=Импорт... 55 | gminimap.map_settings_not_found=Файл настроек карты «%s» не существует! 56 | gminimap.map_settings_found=Файл настроек карты «%s» импортирован! 57 | gminimap.export=Экспорт... 58 | gminimap.export_tip1=Чтобы карта использовала эти слои, создайте файл .json в: 59 | gminimap.export_tip2=<папка аддона вашей карты> 60 | gminimap.export_tip3=Затем вставьте этот код: 61 | gminimap.copy_code=Скопировать код 62 | gminimap.code_copied=Код скопирован в буфер обмена! 63 | gminimap.add_layer=Добавить слой 64 | gminimap.remove_layer=Удалить выбранный слой 65 | gminimap.remove_layer_blocked=Вы не можете удалить стандартный слой. 66 | gminimap.layer_user_position=Задать низ/верх с координат вашего текущего положения. -------------------------------------------------------------------------------- /resource/localization/tr/gminimap.properties: -------------------------------------------------------------------------------- 1 | 2 | gminimap.title=GMini Harita 3 | gminimap.configure=Yapılandır 4 | gminimap.configure_minimap=Mini Haritayı Yapılandır 5 | gminimap.enable=Mini Haritayı Etkinleştir 6 | gminimap.reset=Yapılandırmayı Sıfırla 7 | gminimap.reset_query=Tüm ayarları varsayılana sıfırla? 8 | gminimap.yes=Evet 9 | gminimap.no=Hayır 10 | gminimap.ok=Onayla 11 | gminimap.invalid_bind=%s kullanılamıyor! 12 | gminimap.toggle_key=Açma Tuşu 13 | gminimap.expand_key=Büyütme Tuşu 14 | gminimap.radar=Radar 15 | gminimap.zoom=Yakınlaştırma 16 | gminimap.lock_rotation=Döndürmeyi kilitle 17 | gminimap.pivot_offset=Pivot ofseti 18 | gminimap.forced_config=(Bunlardan bazıları bu sunucuda kilitli) 19 | gminimap.width=Genişlik 20 | gminimap.height=Yükseklik 21 | gminimap.border_thickness=Çerçeve Kalınlığı 22 | gminimap.border_color=Çerçeve Rengi 23 | gminimap.terrain=Arazi 24 | gminimap.terrain_color=Renk/Tonlama 25 | gminimap.terrain_brightness=Parlaklık 26 | gminimap.terrain_saturation=Doygunluk 27 | gminimap.terrain_color_inv=Renkleri Ters Çevir 28 | gminimap.terrain_lighting=Işıklandırmayı Göster 29 | gminimap.health_armor=Özel Sağlık/Zırh 30 | gminimap.health_show_custom=Özel Sağlık/Zırhı Göster 31 | gminimap.health_hide_default=Varsayılan Sağlık/Zırhı Gizle 32 | gminimap.health_height=Sağlık/Zırh Çubuğu Yüksekliği 33 | gminimap.health_color=Sağlık Rengi 34 | gminimap.low_health_color=Zayıf Sağlık Rengi 35 | gminimap.armor_color=Zırh Rengi 36 | gminimap.landmarks=Yer İşaretleri 37 | gminimap.landmarks_help1=Yer işareti eklemek için haritaya sağ tıkla 38 | gminimap.landmarks_help2=Haritayı hareket ettirmek için tıkla ve sürükle, yakınlaştırmak için fare tekerleğini kullan 39 | gminimap.scale=Ölçek 40 | gminimap.builtin_icons=GMini Harita Simgeleri 41 | gminimap.gmod_icons=GMOD Simgeleri 42 | gminimap.landmark_remove=İşareti Sil 43 | gminimap.landmark_remove_query=Bu işareti kaldırmak istediğinizden emin misiniz? 44 | gminimap.landmark_label=Etiket 45 | gminimap.landmark_icon=Simge 46 | gminimap.landmark_icon_color=Simge rengi 47 | gminimap.layers=Katmanlar 48 | gminimap.layer_top=Tepe 49 | gminimap.layer_bottom=Alt 50 | gminimap.layer_default=Varsayılan katman 51 | gminimap.layer_number=Katman %d 52 | gminimap.layer_boundaries=%s sınırları 53 | gminimap.file=Dosya 54 | gminimap.import=İçe aktar... 55 | gminimap.map_settings_not_found=Harita ayarları dosyası '%s' mevcut değil! 56 | gminimap.map_settings_found=Harita ayarları dosyası '%s' içe aktarıldı! 57 | gminimap.export=Dışa aktar... 58 | gminimap.export_tip1=Geçerli haritanın bu katmanları kullanmasını sağlamak için şu konumda bir .json dosyası oluşturun: 59 | gminimap.export_tip2= 60 | gminimap.export_tip3=Daha sonra şu kodu yapıştırın: 61 | gminimap.copy_code=Kodu kopyala 62 | gminimap.code_copied=Kod panonuza kopyalandı! 63 | gminimap.add_layer=Katman ekle 64 | gminimap.remove_layer=Seçilen katmanı kaldır 65 | gminimap.remove_layer_blocked=Varsayılan katmanı kaldıramazsınız. 66 | gminimap.layer_user_position=Şu anda durduğunuz yerden tavanı/zemini kullanın 67 | -------------------------------------------------------------------------------- /resource/localization/uk/gminimap.properties: -------------------------------------------------------------------------------- 1 | 2 | gminimap.title=GMinimap 3 | gminimap.configure=Конфігурація 4 | gminimap.configure_minimap=Налаштувати Мінімапу 5 | gminimap.enable=Увімкнути мінікарту 6 | gminimap.reset=Скинути конфігурацію 7 | gminimap.reset_query=Скинути все до початкових параметрів? 8 | gminimap.yes=Так 9 | gminimap.no=Ні 10 | gminimap.ok=Підтвердити 11 | gminimap.invalid_bind=Не можна використовувати %s! 12 | gminimap.expand_key=Клавіша розгорнутої мапи 13 | gminimap.radar=Радар 14 | gminimap.lock_rotation=Блокування повороту 15 | gminimap.pivot_offset=Зсув опори 16 | gminimap.forced_config=(Деякі з них заблоковані на цьому сервері) 17 | gminimap.width=Ширина 18 | gminimap.height=Висота 19 | gminimap.border_thickness=Товщина рамки 20 | gminimap.border_color=Колір рамки 21 | gminimap.terrain=Рельєф 22 | gminimap.terrain_color=Колір/Відтінок 23 | gminimap.terrain_brightness=Яскравість 24 | gminimap.terrain_saturation=Насиченість 25 | gminimap.terrain_color_inv=Інверсія кольорів 26 | gminimap.terrain_lighting=Увімкнути світло 27 | gminimap.health_armor=Налаштування Здоров'я/Броні 28 | gminimap.health_show_custom=Транслювати Кастомне Здоров'я/Броня 29 | gminimap.health_hide_default=Прибрати за замовчуванням Здоров'я/Броня 30 | gminimap.health_height=Здоров'я/Броня Висота показників Здоров'я 31 | gminimap.health_color=Колір Здоров'я 32 | gminimap.low_health_color=Колір низького Здоров'я 33 | gminimap.armor_color=Колір Броні 34 | gminimap.landmarks=Орієнтири 35 | gminimap.landmarks_help1=Правоюклавішею миші-Клікніть на мапу аби додати Орієнтир 36 | gminimap.landmarks_help2=Натисніть і тягніть, щоб перемістити карту,та використовуйте колесо миші для масштабування 37 | gminimap.scale=Масштаб 38 | gminimap.builtin_icons=Gмінімап іконки 39 | gminimap.gmod_icons=Силкіконки 40 | gminimap.landmark_remove=Прибрати Орієнтир 41 | gminimap.landmark_remove_query=Ви впевнені що хочете прибрати цей Орієнтир? 42 | gminimap.landmark_label=Мітка 43 | gminimap.landmark_icon=значок 44 | gminimap.landmark_icon_color=Колір значка 45 | gminimap.layers=Шари 46 | gminimap.layer_top=Топ 47 | gminimap.layer_bottom=Дно 48 | gminimap.layer_default=Стандартний шар 49 | gminimap.layer_number=Шар %d 50 | gminimap.layer_boundaries=межі %s 51 | gminimap.file=Файл 52 | gminimap.import=Імпорт... 53 | gminimap.map_settings_not_found=Файл налаштувань карти '%s' не існує! 54 | gminimap.map_settings_found=Файл налаштувань карти '%s' імпортовано! 55 | gminimap.export=Експорт... 56 | gminimap.export_tip1=Щоб поточна карта використовувала ці шари, створіть файл .json на: 57 | gminimap.export_tip2=<ваша папка надбудови карти> 58 | gminimap.export_tip3=Потім вставте цей код: 59 | gminimap.copy_code=Скопіюйте код 60 | gminimap.code_copied=Код скопійовано в буфер обміну! 61 | gminimap.add_layer=Додайте шар 62 | gminimap.remove_layer=Видалити виділений шар 63 | gminimap.remove_layer_blocked=Ви не можете видалити стандартний шар. 64 | gminimap.layer_user_position=Використовуйте стелю/підлогу з того місця, де ви зараз стоїте --------------------------------------------------------------------------------