├── Core.lua ├── OptionMenus.lua ├── WolfHUDTweakData.lua ├── assets ├── assets.xml ├── drivinghud │ ├── bike.texture │ ├── blackhawk.texture │ ├── boat.texture │ ├── falcogini.texture │ ├── forklift.texture │ ├── golfcart.texture │ ├── legend.texture │ ├── longfellow.texture │ └── truck.texture └── hudlist │ ├── mask_up.texture │ └── weapon_charge.texture ├── docs ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── README.md ├── loc ├── RealWeaponNames.json ├── chinese.json ├── dutch.json ├── english.json ├── french.json ├── german.json ├── italian.json ├── korean.json ├── portuguese.json ├── russian.json └── spanish.json ├── lua ├── AdvAssault.lua ├── BurstFire.lua ├── BuyAllAsset.lua ├── ContractHeat.lua ├── CustomHUD.lua ├── CustomWaypoints.lua ├── DamageIndicator.lua ├── DamagePopup.lua ├── DownCounter.lua ├── DrivingHUD.lua ├── EnemyHealthbar.lua ├── EnhancedCrewLoadout.lua ├── EnhancedObjective.lua ├── EquipmentTweaks.lua ├── GameInfoManager.lua ├── HUDChat.lua ├── HUDList.lua ├── Interaction.lua ├── KillCounter.lua ├── MenuTweaks.lua ├── NetworkHandler.lua ├── NumbericSuspicion.lua ├── PacifiedCivs.lua ├── PrePlanManager.lua ├── ProfileMenu.lua ├── RichPresence.lua ├── Scripts.lua ├── TabStats.lua ├── Utils │ ├── InputDialog.lua │ ├── LoadoutPanel.lua │ ├── OutlinedText.lua │ └── QuickInputMenu.lua ├── VanillaHUD.lua ├── WaypointsManager.lua └── WeaponGadgets.lua ├── mod.txt ├── supermod.xml └── wolfhud.png /assets/assets.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /assets/drivinghud/bike.texture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamikaze94/WolfHUD/1b2736f9290062fe73ef3f2f10b913255def4905/assets/drivinghud/bike.texture -------------------------------------------------------------------------------- /assets/drivinghud/blackhawk.texture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamikaze94/WolfHUD/1b2736f9290062fe73ef3f2f10b913255def4905/assets/drivinghud/blackhawk.texture -------------------------------------------------------------------------------- /assets/drivinghud/boat.texture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamikaze94/WolfHUD/1b2736f9290062fe73ef3f2f10b913255def4905/assets/drivinghud/boat.texture -------------------------------------------------------------------------------- /assets/drivinghud/falcogini.texture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamikaze94/WolfHUD/1b2736f9290062fe73ef3f2f10b913255def4905/assets/drivinghud/falcogini.texture -------------------------------------------------------------------------------- /assets/drivinghud/forklift.texture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamikaze94/WolfHUD/1b2736f9290062fe73ef3f2f10b913255def4905/assets/drivinghud/forklift.texture -------------------------------------------------------------------------------- /assets/drivinghud/golfcart.texture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamikaze94/WolfHUD/1b2736f9290062fe73ef3f2f10b913255def4905/assets/drivinghud/golfcart.texture -------------------------------------------------------------------------------- /assets/drivinghud/legend.texture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamikaze94/WolfHUD/1b2736f9290062fe73ef3f2f10b913255def4905/assets/drivinghud/legend.texture -------------------------------------------------------------------------------- /assets/drivinghud/longfellow.texture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamikaze94/WolfHUD/1b2736f9290062fe73ef3f2f10b913255def4905/assets/drivinghud/longfellow.texture -------------------------------------------------------------------------------- /assets/drivinghud/truck.texture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamikaze94/WolfHUD/1b2736f9290062fe73ef3f2f10b913255def4905/assets/drivinghud/truck.texture -------------------------------------------------------------------------------- /assets/hudlist/mask_up.texture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamikaze94/WolfHUD/1b2736f9290062fe73ef3f2f10b913255def4905/assets/hudlist/mask_up.texture -------------------------------------------------------------------------------- /assets/hudlist/weapon_charge.texture: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamikaze94/WolfHUD/1b2736f9290062fe73ef3f2f10b913255def4905/assets/hudlist/weapon_charge.texture -------------------------------------------------------------------------------- /docs/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | 3 | Please answer the following questions for yourself before submitting an issue. 4 | **YOU MAY DELETE THE PREREQUISITES SECTION.** 5 | 6 | - [ ] I am running the latest version 7 | - [ ] I made sure this issue doesn't persist without this mod 8 | - [ ] I checked to make sure that this issue has not already been filed 9 | 10 | # Expected Behavior 11 | 12 | Please describe the behavior you are expecting 13 | 14 | # Current Behavior 15 | 16 | What is the current behavior? 17 | 18 | # Failure Information (for bugs) 19 | #### Please help provide information about the failure if this is a bug. If it is not a bug, please remove the rest of this template. 20 | 21 | ## Steps to Reproduce 22 | 23 | Please provide detailed steps for reproducing the issue. 24 | 25 | 1. step 1 26 | 2. step 2 27 | 3. you get it... 28 | 29 | ## Context 30 | 31 | Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions. 32 | 33 | * Operating System: 34 | * BLT version: 35 | * Other mods installed: 36 | * ... 37 | 38 | ## BLT log 39 | 40 | Please include the contents of your BLT log around the time the bug appeared. 41 | You can find the BLT log inside your games "/mods/logs/" folder. 42 | 43 |
44 | BLT Log 45 | 46 | [Please paste your BLT log here.] 47 | 48 |
49 | 50 | ## Crash log(s) 51 | 52 | If the bug is causing a crash, please provide the contents of your crashlog. 53 | You can find your crashlog in "%localappdata%/Payday 2/crash.txt" 54 | 55 |
56 | Crash.txt 57 | 58 | [Please paste your crash log here.] 59 | 60 |
61 | -------------------------------------------------------------------------------- /docs/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | # Prerequisites 8 | 9 | Please answer the following questions for yourself before submitting an issue. 10 | **YOU MAY DELETE THE PREREQUISITES SECTION.** 11 | 12 | - [ ] I am running the latest version 13 | - [ ] I made sure this issue doesn't persist without this mod 14 | - [ ] I checked to make sure that this issue has not already been filed 15 | 16 | # Expected Behavior 17 | 18 | Please describe the behavior you are expecting 19 | 20 | # Current Behavior 21 | 22 | What is the current behavior? 23 | 24 | # Failure Information 25 | 26 | ## Steps to Reproduce 27 | 28 | Please provide detailed steps for reproducing the issue. 29 | 30 | 1. step 1 31 | 2. step 2 32 | 3. you get it... 33 | 34 | ## Context 35 | 36 | Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions. 37 | 38 | * Operating System: 39 | * BLT version: 40 | * Other mods installed: 41 | * ... 42 | 43 | ## BLT log 44 | 45 | Please include the contents of your BLT log around the time the bug appeared. 46 | You can find the BLT log inside your games "/mods/logs/" folder. 47 | 48 |
49 | BLT Log 50 | 51 | [Please paste your BLT log here.] 52 | 53 |
54 | 55 | ## Crash log(s) 56 | 57 | If the bug is causing a crash, please provide the contents of your crashlog. 58 | You can find your crashlog in "%localappdata%/Payday 2/crash.txt" 59 | 60 |
61 | Crash.txt 62 | 63 | [Please paste your crash log here.] 64 | 65 |
66 | -------------------------------------------------------------------------------- /docs/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | # Prerequisites 8 | 9 | Please answer the following questions for yourself before submitting an issue. 10 | **YOU MAY DELETE THE PREREQUISITES SECTION.** 11 | 12 | - [ ] I am running the latest version 13 | - [ ] I made sure this issue doesn't persist without this mod 14 | - [ ] I checked to make sure that this issue has not already been filed 15 | 16 | 17 | # Current Behavior 18 | 19 | What is the current behavior? 20 | 21 | # Expected Behavior 22 | 23 | Please describe the behavior you are expecting 24 | -------------------------------------------------------------------------------- /docs/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | [Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.] 4 | 5 | Fixes # [issue] 6 | 7 | ## Type of change 8 | 9 | Please delete options that are not relevant. 10 | 11 | - [ ] Bug fix (non-breaking change which fixes an issue) 12 | - [ ] New feature (non-breaking change which adds functionality) 13 | - [ ] This change requires a documentation update 14 | 15 | # How Has This Been Tested? 16 | 17 | Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration 18 | 19 | - [ ] Test A 20 | - [ ] Test B 21 | 22 | 23 | # Checklist: 24 | 25 | - [ ] My changes generate no new warnings 26 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # WolfHUD 2 | 3 | ## Description 4 | 5 | This is a Mod collection of several useful HUD altering mods. 6 | I've modified them and added features which I felt that were either needed or useful, or both. 7 | It was originally a recreated and updated version of GageHUD, since it was really painful to maintain with all the mixed up files. 8 | Over time I added more and more useful scripts and currently it becomes kind of an All-in-One solution... 9 | Not sure if I want it to become that, but at least I added ingame Options to turn off functions you don't like. ;) 10 | 11 | I got the permission from **Seven, ViciousWalrus, Undeadsewer, FishTaco, friendIyfire and Terminator01** for the use of their scripts and from N**ervatel Hanging Closet Monster** for using his icons. 12 | 13 | **Huge thanks to [Kampfhörnchen](http://modworkshop.net/member.php?action=profile&uid=19364) for creating a better logo and a banner.** 14 | 15 | ## Preview 16 | 17 |

18 | 19 |
20 | (Click to view bigger version) 21 |

22 | 23 | ## Requirements 24 | 25 | To make this mod work you will need to install __[SuperBLT](https://superblt.znix.xyz)__. 26 | 27 | *Note: Regular BLT is, with Version 3.00 forward, no longer supported.* 28 | 29 | ## Installation (Direct Download) 30 | 31 | 1. [Download WolfHUD from here](https://github.com/Kamikaze94/WolfHUD/archive/master.zip) or clone the master branch. 32 | 2. Open the downloaded archive, using WinRAR or 7Zip 33 | 3. Extract the **WolfHUD-master** folder into your 'PAYDAY 2/mods' folder 34 | 4. Start the game once. It will prompt you to update Federal Inventory. 35 | 36 | ## AutoUpdates 37 | 38 | This mod uses SuperBLTs custom update URL support. 39 | When an update is available, you'll be notified about it in the game. 40 | The update can be downloaded and installed through SuperBLTs download manager. 41 | *(The installation-folder needs to be named **WolfHUD-master** for autoupdates to work!)* 42 | 43 | ## If you don't want/like Inventory Icons 44 | 45 | To remove "Federal Inventory" go to your **PAYDAY 2/assets/mod_overrides/Federal Inventory** folder and delete it. 46 | Don't download the "update" for it, obviously. 47 | 48 | ## Bug reports 49 | 50 | If you encounter any bugs, feel free to post about it on the issues tab! 51 | Please try to provide information from your crashlog and the actions you and your teammates were doing, when you crashed. ;) 52 | The Crashlog can be found at **%localappdata%/PAYDAY 2/crashlog.txt**. 53 | The BLT log can be found at **PAYDAY 2/mods/logs/**. 54 | 55 | ## Included Mods 56 | 57 | * [CustomHUD](https://bitbucket.org/pjal3urb/customhud/src) made by **Seven** modified by **me** 58 | * [HudPanelList](https://bitbucket.org/pjal3urb/hudlist/src/) made by **Seven**, modified by **me** 59 | * [KillCounter](https://bitbucket.org/pjal3urb/customhud/src) made by **Seven**, modified by **me** 60 | * [Accuracy Plugin](https://bitbucket.org/pjal3urb/customhud/src) made by **Seven**, modified by **me** 61 | * [ToggleInteract](https://bitbucket.org/pjal3urb/toggleinteract/src) made by **Seven**, modified by **Iron Predator** and **me** 62 | * [DoubleTap Granades](https://bitbucket.org/pjal3urb/doubletapgrenades/src) made by **Seven** 63 | * [AutoPickup](https://bitbucket.org/pjal3urb/autopickup/src) made by **Seven** 64 | * [WeaponGadgets](https://bitbucket.org/pjal3urb/gadgets) made by **Seven** 65 | * [Remember Gadget State](https://bitbucket.org/pjal3urb/persistentgadgets/src) made by **Seven** 66 | * [TabStats](https://steamcommunity.com/app/218620/discussions/15/618463738399320805/) made by **friendIyfire** 67 | * [Numeric Suspicion](https://github.com/cjur3/GageHud) made by **friendIyfire**, updated by **me** 68 | * Angeled Sight Visualisation, made by **me** (based on HoxHUD P4.1) 69 | * [EnemyHealthbar](https://modworkshop.net/mydownloads.php?action=view_down&did=15157) made by **Undeadsewer**, modified by **me** 70 | * AdvAssault made by **me** (based on HoxHUD P4.1) 71 | * Dynamic Damage Indicator made by **me** 72 | * DamagePopups made by **me** (Uses [WaypointsManager](https://bitbucket.org/pjal3urb/waypoints) made by **Seven**) 73 | * [BurstFire](https://bitbucket.org/pjal3urb/burstfire/src) made by **Seven** 74 | * [Real Ammo](https://modworkshop.net/mydownloads.php?action=view_down&did=15108) made by **FishTaco**, modified by **me** 75 | * [No Spam](http://steamcommunity.com/app/218620/discussions/15/618457398976607330/) made by **Ahab**, **Seven**, **AmEyeBlind** & **money123451**, updated by **me** 76 | * [DrivingHUD](https://modworkshop.net/mydownloads.php?action=view_down&did=12982) made by **ViciousWalrus** and **Great Big Bushy Beard**, rewritten by **me** 77 | * [Real Weapon Names](http://modworkshop.net/mydownloads.php?action=view_down&did=15433) made by **(AD) Jaqueto**, updated by **Terminator01** 78 | * [Buy all Assets](http://steamcommunity.com/app/218620/discussions/15/618458030689719683/) made by **=TBM= BangL**, rewritten by **me** 79 | * PrePlanManager made by **me** 80 | * ProfileMenu made by **me** 81 | * Equipment Tweaks made by **me** 82 | * Smaller Menu Tweaks 83 | * [Always show Mod Icons](http://modworkshop.net/mydownloads.php?action=view_down&did=13975) made by **Slippai**, updated by **me** 84 | * [Slider Values](http://modworkshop.net/mydownloads.php?action=view_down&did=14800) made by **Snh20**, modified by **me** 85 | * Show Weapon Names made by **me** 86 | * Show Skill Names made by **me** 87 | * [Skillset Info](http://modworkshop.net/mydownloads.php?action=view_down&did=15294) made by **Fooksie** 88 | * Increased the maximum chars of custom Weapon/Mask/Skillset names 89 | * Federal Inventory (Mod Override), made by **Nervatel Hanging Closet Monster** and **Luffy**, updated by **me** 90 | * [Weapon Icons](https://modworkshop.net/mydownloads.php?action=view_down&did=14240), [Melee Icons](http://modworkshop.net/mydownloads.php?action=view_down&did=13910) and [Mask Icons](http://modworkshop.net/mydownloads.php?action=view_down&did=13911) made by **Nervatel Hanging Closet Monster** 91 | * [Throwables, Equipment, Armor and Heister Portraits](http://modworkshop.net/mydownloads.php?action=view_down&did=13916) made by **Luffy** 92 | 93 | ## Localizations 94 | 95 | * English made by **me** 96 | * German made by **me** 97 | * Russian made by **chrom[K]a**, **Magic3000** & **MEXAHOTABOP** 98 | * Korean made by **Я!zu** 99 | * Spanish made by **papydeath95** & **ElReyZero1201** 100 | * Chinese made by **zhongfly** & **CoolLKK** 101 | * French made by **Elise MRX (La Mule)** 102 | * Portuguese made by **Kazenin (Aldo Raine)** 103 | * Italian made by **LeecanIt** 104 | * Dutch made by **Azoraqua** 105 | 106 | Big credit goes to all of you! 107 | Without your awesome mods, I would have quitted this game a long time ago! 108 | **If I forgot to mention you, I'm really sorry. 109 | Please feel free to contact me, so I can credit you, for the awesome stuff you have made :)** 110 | -------------------------------------------------------------------------------- /loc/RealWeaponNames.json: -------------------------------------------------------------------------------- 1 | { 2 | "bm_w_akm_gold" : "Golden AKMS", 3 | "bm_w_amcar" : "Colt Model 733", 4 | "bm_w_ak74" : "Izhmash AKS-74", 5 | "bm_w_m4" : "Colt M4A1", 6 | "bm_w_aug" : "Steyr AUG A2", 7 | "bm_w_akm" : "Izhmash AKMS", 8 | "bm_w_g36" : "H&K G36KV", 9 | "bm_w_m14" : "Springfield Armory M1A SOCOM 16", 10 | "bm_w_ak5" : "Bofors Ak 5", 11 | "bm_w_m16" : "Colt M16A4", 12 | "bm_w_s552" : "Swiss Arms SG 552-2", 13 | "bm_w_scar" : "FN SCAR-H STD", 14 | "bm_w_fal" : "FN FAL", 15 | "bm_w_famas" : "FAMAS F1", 16 | "bm_w_galil" : "IMI Galil ARM", 17 | "bm_w_g3" : "H&K G3A3", 18 | "bm_w_l85a2" : "Enfield L85A2", 19 | "bm_w_vhs" : "VHS-D2", 20 | "bm_w_asval" : "AS VAL", 21 | "bm_w_sub2000" : "Kel-tec SUB-2000", 22 | "bm_w_tecci" : "H&K 416C", 23 | "bm_w_contraband" : "H&K 417 w/M203 GL", 24 | "bm_w_ak12" : "Izhmash AK-12", 25 | "bm_w_spas12" : "Franchi SPAS-12", 26 | "bm_w_b682" : "CZ Redhead Deluxe", 27 | "bm_w_r870" : "Remington M870", 28 | "bm_w_saiga" : "Izhmash Saiga-12K", 29 | "bm_w_huntsman" : "Stoeger/IGA Coach", 30 | "bm_w_benelli" : "Benelli M4 Super 90", 31 | "bm_w_ksg" : "Kel-tec KSG", 32 | "bm_w_aa12" : "MPS AA-12 CQB", 33 | "bm_w_boot" : "Winchester Model 1887", 34 | "bm_w_rota" : "Crye Precision SIX12", 35 | "bm_w_ching" : "Springfield Armory M1 Garand", 36 | "bm_w_corgi" : "FN F2000", 37 | "bm_w_komodo" : "IWI X95", 38 | "bm_w_groza" : "OTs-14 Groza", 39 | "bm_w_shak12" : "Izhmash ShAK-12", 40 | 41 | "bm_w_jowi" : "Akimbo Glock 26", 42 | "bm_w_x_1911" : "Akimbo M1911 Lightweight Operator", 43 | "bm_w_x_b92fs" : "Akimbo Beretta 92FS Centurion", 44 | "bm_w_x_deagle" : "Akimbo IMI Desert Eagle Mark XIX", 45 | "bm_w_x_g17" : "Akimbo Glock 17", 46 | "bm_w_x_g22c" : "Akimbo Glock 22C", 47 | "bm_w_x_usp" : "Akimbo USP Tactical .45", 48 | "bm_w_x_packrat" : "Akimbo H&K P30L", 49 | "bm_w_x_chinchilla" : "Akimbo S&W Model 29", 50 | "bm_w_x_basset" : "Akimbo CBRPS Saiga Spike X1S", 51 | "bm_w_x_shrew" : "Akimbo Colt Defender", 52 | "bm_w_x_baka" : "Akimbo IMI Micro Uzi", 53 | "bm_w_x_2006m" : "Akimbo Mateba 2006M", 54 | 55 | "bm_w_gre_m79" : "SA M79 Grenade Launcher", 56 | "bm_w_saw" : "OVE9000", 57 | "bm_w_m134" : "M134 Minigun", 58 | "bm_w_m32" : "MGL Mk 1S", 59 | "bm_w_flamethrower_mk2" : "Flamethrower", 60 | "bm_w_plainsrider" : "Plainsrider Bow", 61 | "bm_w_elastic" : "Compound Bow", 62 | 63 | "bm_w_msr" : "Remington MSR", 64 | "bm_w_r93" : "Blaser R93 LRS2", 65 | "bm_w_m95" : "Barrett M95", 66 | "bm_w_mosin" : "Mosin-Nagant M1907", 67 | "bm_w_winchester1874" : "Winchester Model 1873", 68 | "bm_w_wa2000" : "Walther WA 2000", 69 | "bm_w_model70" : "Winchester Model 70", 70 | "bm_w_desertfox" : "Desert Tech SRSA1", 71 | "bm_w_tti" : "KAC SR-25", 72 | "bm_w_siltstone" : "SVD Dragunov", 73 | "bm_w_sbl" : "Marlin Model 1895", 74 | "bm_w_r700" : "Remington Model 700P", 75 | "bm_w_qbu88" : "Norinco QBU-88", 76 | "bm_w_scout" : "Steyr Scout", 77 | 78 | "bm_w_rpk" : "Izhmash RPK", 79 | "bm_w_m249" : "FN M249 Para", 80 | "bm_w_hk21" : "H&K 21E", 81 | "bm_w_mg42" : "MG-42", 82 | "bm_w_par" : "FN M240B", 83 | "bm_w_hk51b" : "H&K 51 Bravo", 84 | 85 | "bm_w_usp" : "H&K USP Tactical .45", 86 | "bm_w_g22c" : "Glock 22C", 87 | "bm_wp_pis_g26" : "Glock 26", 88 | "bm_w_glock_17" : "Glock 17", 89 | "bm_w_colt_1911" : "M1911 Lightweight Operator", 90 | "bm_w_b92fs" : "Beretta 92FS Centurion", 91 | "bm_w_raging_bull" : "Taurus Raging Bull .44 Magnum", 92 | "bm_w_glock_18c" : "Glock 18C", 93 | "bm_w_deagle" : "IMI Desert Eagle Mark XIX", 94 | "bm_w_ppk" : "Walther PPK", 95 | "bm_w_p226" : "SIG-Sauer P226", 96 | "bm_w_c96" : "Mauser C96", 97 | "bm_w_hs2000" : "Springfield Armory XDM", 98 | "bm_w_peacemaker" : "Single Action Army Cavalry", 99 | "bm_w_mateba" : "Mateba 2006M", 100 | "bm_w_sparrow" : "Jericho 941 RPL", 101 | "bm_w_pl14" : "PL-14 Lebedev", 102 | "bm_w_packrat" : "H&K P30L", 103 | "bm_w_lemming" : "FN Five-Seven", 104 | "bm_w_chinchilla" : "S&W Model 29", 105 | "bm_w_breech" : "Luger P08", 106 | "bm_w_shrew" : "Colt Defender", 107 | "bm_w_legacy" : "H&K P7M13", 108 | "bm_w_beer" : "Beretta 93R", 109 | "bm_w_czech" : "CZ AccuShadow 2", 110 | "bm_w_stech" : "Stechkin Automatic Pistol", 111 | "bm_w_holt" : "Hudson H9", 112 | "bm_w_model3" : "S&W No. 3 Russian Model", 113 | "bm_w_m1911" : "Colt M1911", 114 | "bm_w_type54" : "Tokarev TT-33", 115 | "bm_w_rsh12" : "KBP RSh-12", 116 | "bm_w_maxim9" : "Maxim 9", 117 | 118 | 119 | "bm_w_m1928" : "Thompson M1928A1", 120 | "bm_w_mac10" : "Ingram MAC-10", 121 | "bm_w_mp5" : "H&K MP5A4", 122 | "bm_w_x_mp5" : "Akimbo H&K MP5A4", 123 | "bm_w_mp9" : "B&T MP9", 124 | "bm_w_olympic" : "Olympic Arms K23B Tactical", 125 | "bm_w_akmsu" : "Izhmash AKMSU", 126 | "bm_w_p90" : "FN P90 TR", 127 | "bm_w_m45" : "Carl Gustav M/45", 128 | "bm_w_mp7" : "H&K MP7A2", 129 | "bm_w_tec9" : "Intratec TEC-9", 130 | "bm_w_scorpion" : "Scorpion vz.61", 131 | "bm_w_uzi" : "IMI Uzi", 132 | "bm_w_sterling" : "Sterling L2A1", 133 | "bm_w_cobray" : "Cobray M11/9", 134 | "bm_w_polymer" : "KRISS Vector", 135 | "bm_w_baka" : "IMI Micro Uzi", 136 | "bm_w_x_sr2" : "Akimbo SR-2M Veresk", 137 | "bm_w_sr2" : "SR-2M Veresk", 138 | "bm_w_hajk" : "CZ 805 BREN A1", 139 | "bm_w_schakal" : "H&K UMP-45", 140 | "bm_w_coal" : "PP-19 Bizon", 141 | "bm_w_erma" : "MP40", 142 | "bm_w_shepheard" : "SIG-Sauer MPX", 143 | "bm_w_vityaz" : "PP-19 Vityaz-SN", 144 | "bm_w_pm9" : "Minebea PM-9", 145 | "bm_w_fmg9" : "Magpul FMG-9", 146 | 147 | "bm_w_judge" : "Taurus Judge 4510PLYFS", 148 | "bm_w_serbu" : "Remington M870 Short-Barreled", 149 | "bm_w_striker" : "Sentinel Armsel Striker", 150 | "bm_w_m37" : "Ithaca 37", 151 | "bm_w_basset" : "CBRPS Saiga Spike X1S", 152 | "bm_w_coach" : "Remington Exposed Hammer SxS", 153 | "bm_w_m1897" : "Winchester Model 1897", 154 | "bm_w_m590" : "Mossberg 590 Compact Cruiser", 155 | "bm_w_ultima" : "Kalashnikov MP-155 Ultima", 156 | 157 | "bm_w_rpg7" : "RPG-7", 158 | "bm_w_hunter" : "Avalanche Pistol Crossbow", 159 | "bm_w_china" : "China Lake Grenade Launcher", 160 | "bm_w_arbiter" : "H&K XM-25", 161 | "bm_w_ray" : "M202 FLASH", 162 | "bm_w_slap" : "H&K M320", 163 | "bm_w_ms3gl" : "Metal Storm 3GL", 164 | 165 | "bm_w_x_coal" : "Akimbo PP-19 Bizon", 166 | "bm_w_x_uzi" : "Akimbo IMI Uzi", 167 | "bm_w_x_sterling" : "Akimbo Sterling L2A1", 168 | "bm_w_x_mp7" : "Akimbo H&K MP7A2", 169 | "bm_w_x_schakal" : "Akimbo H&K UMP-45", 170 | "bm_w_x_m45" : "Akimbo Carl Gustav M/45", 171 | "bm_w_x_judge" : "Akimbo Taurus Judge", 172 | "bm_w_x_tec9" : "Akimbo Intratec TEC-9", 173 | "bm_w_x_rota" : "Akimbo Crye Precision SIX12", 174 | "bm_w_x_mp9" : "Akimbo B&T MP9", 175 | "bm_w_x_m1928" : "Akimbo Thompson M1928A1", 176 | "bm_w_x_cobray" : "Akimbo Cobray M11/9", 177 | "bm_w_x_olympic" : "Akimbo K23B Tactical", 178 | "bm_w_x_scorpion" : "Akimbo Scorpion vz.61", 179 | "bm_w_x_mac10" : "Akimbo Ingram MAC-10", 180 | "bm_w_x_polymer" : "Akimbo KRISS Vector", 181 | "bm_w_x_akmsu" : "Akimbo Izhmash AKMSU", 182 | "bm_w_x_p90" : "Akimbo FN P90 TR", 183 | "bm_w_x_hajk" : "Akimbo CZ 805 BREN A1", 184 | "bm_w_x_g18c" : "Akimbo Glock 18C", 185 | "bm_w_x_breech" : "Akimbo Luger P08", 186 | "bm_w_x_ppk" : "Akimbo Walther PPK", 187 | "bm_w_x_p226" : "Akimbo Sig-Sauer P226", 188 | "bm_w_x_c96" : "Akimbo Mauser C96", 189 | "bm_w_x_hs2000" : "Akimbo SA XDM", 190 | "bm_w_x_rage" : "Akimbo Taurus Raging Bull.44", 191 | "bm_w_x_sparrow" : "Akimbo Jericho 941 RPL", 192 | "bm_w_x_erma" : "Akimbo MP40", 193 | "bm_w_x_shepheard" : "Akimbo SIG-Sauer MPX", 194 | "bm_w_x_pl14" : "Akimbo PL-14 Lebedev", 195 | "bm_w_x_beer" : "Akimbo Beretta 93R", 196 | "bm_w_x_czech" : "Akimbo CZ AccuShadow 2", 197 | "bm_w_x_stech" : "Akimbo Stechkin Automatic Pistol", 198 | "bm_w_x_model3" : "Akimbo S&W No. 3 Russian Model", 199 | "bm_w_x_m1911" : "Akimbo Colt M1911", 200 | "bm_w_x_vityaz" : "Akimbo PP-19 Vityaz-SN", 201 | "bm_w_x_pm9" : "Akimbo Minebea PM-9", 202 | "bm_w_x_type54" : "Akimbo Tokarev TT-33", 203 | "bm_w_x_maxim9" : "Akimbo Maxim 9" 204 | } -------------------------------------------------------------------------------- /lua/AdvAssault.lua: -------------------------------------------------------------------------------- 1 | if string.lower(RequiredScript) == "lib/managers/hud/hudassaultcorner" then 2 | local init_original = HUDAssaultCorner.init 3 | local _start_assault_original = HUDAssaultCorner._start_assault 4 | local _set_hostage_offseted_original = HUDAssaultCorner._set_hostage_offseted 5 | local set_buff_enabled_original = HUDAssaultCorner.set_buff_enabled 6 | local show_point_of_no_return_timer_original = HUDAssaultCorner.show_point_of_no_return_timer 7 | local hide_point_of_no_return_timer_original = HUDAssaultCorner.hide_point_of_no_return_timer 8 | local show_casing_original = HUDAssaultCorner.show_casing 9 | local hide_casing_original = HUDAssaultCorner.hide_casing 10 | local set_assault_wave_number_original = HUDAssaultCorner.set_assault_wave_number 11 | local _animate_wave_started_original = HUDAssaultCorner._animate_wave_started 12 | local _animate_wave_completed_original = HUDAssaultCorner._animate_wave_completed 13 | 14 | function HUDAssaultCorner:init(...) 15 | init_original(self, ...) 16 | 17 | -- Waves completed are visible in Objective and overlapping with HUDList. 18 | if self:should_display_waves() then 19 | local wave_panel = self._hud_panel:child("wave_panel") 20 | if alive(wave_panel) then 21 | wave_panel:set_alpha(0) 22 | end 23 | local assault_panel = self._hud_panel:child("assault_panel") 24 | if alive(assault_panel) then 25 | self._wave_text = assault_panel:text({ 26 | name = "num_waves", 27 | text = self:get_completed_waves_string(), 28 | valign = "center", 29 | vertical = "center", 30 | align = "center", 31 | halign = "right", 32 | w = self._bg_box and self._bg_box:w() or 100, 33 | h = tweak_data.hud.active_objective_title_font_size, 34 | layer = 1, 35 | x = 0, 36 | y = 0, 37 | color = Color.white, 38 | alpha = 0.8, 39 | font = "fonts/font_medium_shadow_mf", 40 | font_size = tweak_data.hud.active_objective_title_font_size * 0.9, 41 | }) 42 | self._wave_text:set_top(self._bg_box and self._bg_box:bottom() or 40) 43 | self._wave_text:set_right(self._bg_box and self._bg_box:right() or 575) 44 | end 45 | end 46 | 47 | self:update_banner_pos() 48 | end 49 | 50 | function HUDAssaultCorner:update_banner_pos() 51 | if not alive(self._hud_panel) then return end 52 | local hud_w = self._hud_panel:w() 53 | local banner_pos = math.clamp(WolfHUD:getSetting({"AssaultBanner", "POSITION"}, 2), 1, 3) 54 | local assault_panel = self._hud_panel:child("assault_panel") 55 | local buffs_panel = self._hud_panel:child("buffs_panel") 56 | local point_of_no_return_panel = self._hud_panel:child("point_of_no_return_panel") 57 | local casing_panel = self._hud_panel:child("casing_panel") 58 | if alive(assault_panel) and alive(buffs_panel) and alive(point_of_no_return_panel) and alive(casing_panel) then 59 | if banner_pos < 2 then --Quite messy, but all the panels in this class are far wider than they would need to be, giving "false information" on their w() function... 60 | buffs_panel:set_right(self._vip_bg_box:w()) 61 | assault_panel:set_right((buffs_panel:visible() and buffs_panel:right() or 80) + self._bg_box:w() + 6 + assault_panel:child("icon_assaultbox"):w()) 62 | point_of_no_return_panel:set_right(80 + self._bg_box:w() + 3 + point_of_no_return_panel:child("icon_noreturnbox"):w()) 63 | casing_panel:set_right(80 + self._bg_box:w() + 3 + casing_panel:child("icon_casingbox"):w()) 64 | elseif banner_pos == 2 then 65 | assault_panel:set_right(hud_w / 2 + self._bg_box:w() / 2 + assault_panel:child("icon_assaultbox"):w() + 3) 66 | buffs_panel:set_x(assault_panel:left() + self._bg_box:left() - 3 - buffs_panel:w()) 67 | point_of_no_return_panel:set_right(hud_w / 2 + (self._bg_box:w() + point_of_no_return_panel:child("icon_noreturnbox"):w()) / 2) 68 | casing_panel:set_right(hud_w / 2 + (self._bg_box:w() + casing_panel:child("icon_casingbox"):w()) / 2) 69 | else 70 | assault_panel:set_right(hud_w) 71 | buffs_panel:set_x(assault_panel:left() + self._bg_box:left() - 3 - buffs_panel:w()) 72 | point_of_no_return_panel:set_right(hud_w) 73 | casing_panel:set_right(hud_w) 74 | end 75 | end 76 | 77 | self:update_hudlist_offset() 78 | end 79 | 80 | function HUDAssaultCorner:set_buff_enabled(...) 81 | self:update_banner_pos() 82 | return set_buff_enabled_original(self, ...) 83 | end 84 | 85 | function HUDAssaultCorner:update_hudlist_offset(banner_visible) 86 | banner_visible = banner_visible or banner_visible == nil and (self._assault or self._point_of_no_return or self._casing) 87 | local banner_pos = math.clamp(WolfHUD:getSetting({"AssaultBanner", "POSITION"}, 2), 1, 3) 88 | if managers.hud and banner_pos ~= 2 then 89 | local offset = banner_visible and ((self._bg_box and self._bg_box:bottom() or 0) + (self:should_display_waves() and self._wave_text:h() or 0)+ 12) or 0 90 | if banner_pos > 2 and HUDListManager then 91 | managers.hud:change_list_setting("right_list_height_offset", offset) 92 | elseif banner_pos < 2 then 93 | if managers.hud._hud_objectives and managers.hud._hud_objectives.apply_offset then 94 | managers.hud._hud_objectives:apply_offset(offset) 95 | end 96 | end 97 | end 98 | end 99 | 100 | function HUDAssaultCorner:_set_hostage_offseted(is_offseted, ...) 101 | _set_hostage_offseted_original(self, is_offseted, ...) 102 | self:update_hudlist_offset(is_offseted) 103 | end 104 | 105 | function HUDAssaultCorner:show_point_of_no_return_timer(...) 106 | show_point_of_no_return_timer_original(self, ...) 107 | self:update_hudlist_offset(true) 108 | end 109 | 110 | function HUDAssaultCorner:hide_point_of_no_return_timer(...) 111 | hide_point_of_no_return_timer_original(self, ...) 112 | self:update_hudlist_offset(false) 113 | end 114 | 115 | function HUDAssaultCorner:show_casing(...) 116 | --show_casing_original(self, ...) 117 | --self:update_hudlist_offset(true) 118 | end 119 | function HUDAssaultCorner:hide_casing(...) 120 | --hide_casing_original(self, ...) 121 | --self:update_hudlist_offset(false) 122 | end 123 | 124 | function HUDAssaultCorner:_start_assault(text_list, ...) 125 | for i, string_id in ipairs(text_list) do 126 | if string_id == "hud_assault_assault" then 127 | text_list[i] = "hud_adv_assault" 128 | end 129 | end 130 | return _start_assault_original(self, text_list, ...) 131 | end 132 | 133 | function HUDAssaultCorner:_animate_wave_started(...) 134 | self._wave_text:set_text(self:get_completed_waves_string()) 135 | 136 | return _animate_wave_started_original(self, ...) 137 | end 138 | function HUDAssaultCorner:_animate_wave_completed(...) 139 | self._wave_text:set_text(self:get_completed_waves_string()) 140 | 141 | return _animate_wave_completed_original(self, ...) 142 | end 143 | 144 | function HUDAssaultCorner:set_assault_wave_number(...) 145 | if alive(self._wave_text) then 146 | self._wave_text:set_text(self:get_completed_waves_string()) 147 | self._wave_text:animate(callback(self, self, "_animate_wave_text")) 148 | end 149 | 150 | return set_assault_wave_number_original(self, ...) 151 | end 152 | 153 | function HUDAssaultCorner:_animate_wave_text(object) 154 | local TOTAL_T = 2 155 | local t = TOTAL_T 156 | object:set_alpha(0.8) 157 | while t > 0 do 158 | local dt = coroutine.yield() 159 | t = t - dt 160 | object:set_alpha(0.5 + 0.5 * (0.5 * math.sin(t * 360 * 2) + 0.5)) 161 | end 162 | object:set_alpha(0.8) 163 | end 164 | 165 | function HUDAssaultCorner:locked_assault(status) 166 | local assault_panel = self._hud_panel:child("assault_panel") 167 | local icon_assaultbox = assault_panel and assault_panel:child("icon_assaultbox") 168 | local image 169 | if status then 170 | image = "guis/textures/pd2/hud_icon_padlockbox" 171 | else 172 | image = "guis/textures/pd2/hud_icon_assaultbox" 173 | end 174 | if icon_assaultbox and image then 175 | icon_assaultbox:set_image(image) 176 | end 177 | end 178 | elseif string.lower(RequiredScript) == "lib/managers/hudmanagerpd2" then 179 | local sync_start_assault_original = HUDManager.sync_start_assault 180 | local sync_end_assault_original = HUDManager.sync_end_assault 181 | local _create_downed_hud_original = HUDManager._create_downed_hud 182 | local _create_custody_hud_original = HUDManager._create_custody_hud 183 | 184 | function HUDManager:_locked_assault(status) 185 | if Network:is_server() then 186 | status = managers.groupai:state():get_hunt_mode() or false 187 | end 188 | if self._assault_locked ~= status then 189 | if self._hud_assault_corner then 190 | self._hud_assault_corner:locked_assault(status) 191 | end 192 | self._assault_locked = status 193 | if Network:is_server() and WolfHUD.Sync then 194 | WolfHUD.Sync:endless_assault_status(self._assault_locked) 195 | end 196 | end 197 | end 198 | 199 | function HUDManager:is_assault_locked() 200 | return self._assault_locked or false 201 | end 202 | 203 | function HUDManager:change_assaultbanner_setting(setting, value) 204 | if self._hud_assault_corner then 205 | if setting == "POSITION" then 206 | self._hud_assault_corner:update_banner_pos() 207 | end 208 | end 209 | end 210 | 211 | function HUDManager:sync_start_assault(...) 212 | sync_start_assault_original(self, ...) 213 | 214 | if Network:is_server() then 215 | self:_locked_assault() 216 | end 217 | end 218 | 219 | function HUDManager:sync_end_assault(...) 220 | sync_end_assault_original(self, ...) 221 | 222 | if Network:is_server() then 223 | self:_locked_assault() 224 | end 225 | end 226 | 227 | function HUDManager:_create_downed_hud(...) 228 | _create_downed_hud_original(self, ...) 229 | local banner_pos = math.clamp(WolfHUD:getSetting({"AssaultBanner", "POSITION"}, 2), 1, 3) 230 | if banner_pos == 2 and self._hud_player_downed then 231 | local downed_panel = self._hud_player_downed._hud_panel 232 | local downed_hud = self._hud_player_downed._hud 233 | local timer_msg = downed_panel and downed_panel:child("downed_panel"):child("timer_msg") 234 | local timer = downed_hud and downed_hud.timer 235 | if timer_msg and timer then 236 | timer_msg:set_y(65) 237 | timer:set_y(math.round(timer_msg:bottom() - 6)) 238 | end 239 | end 240 | end 241 | 242 | function HUDManager:_create_custody_hud(...) 243 | _create_custody_hud_original(self, ...) 244 | local banner_pos = math.clamp(WolfHUD:getSetting({"AssaultBanner", "POSITION"}, 2), 1, 3) 245 | if banner_pos == 2 and self._hud_player_custody then 246 | local custody_panel = self._hud_player_custody._hud_panel 247 | local timer_msg = custody_panel and custody_panel:child("custody_panel") and custody_panel:child("custody_panel"):child("timer_msg") 248 | local timer = self._hud_player_custody._timer 249 | if timer_msg and timer then 250 | timer_msg:set_y(65) 251 | timer:set_y(math.round(timer_msg:bottom() - 6)) 252 | end 253 | end 254 | end 255 | elseif string.lower(RequiredScript) == "lib/managers/localizationmanager" then 256 | local text_original = LocalizationManager.text 257 | 258 | function LocalizationManager:text(string_id, ...) 259 | if string_id == "hud_adv_assault" then 260 | return self:hud_adv_assault() 261 | end 262 | return text_original(self, string_id, ...) 263 | end 264 | 265 | function LocalizationManager:hud_adv_assault() 266 | if WolfHUD:getSetting({"AssaultBanner", "USE_ADV_ASSAULT"}, true) then 267 | if managers.hud and managers.hud:is_assault_locked() then 268 | return self:text("wolfhud_locked_assault") 269 | else 270 | local tweak = tweak_data.group_ai.besiege.assault 271 | local gai_state = managers.groupai:state() 272 | local assault_data = Network:is_server() and gai_state and gai_state._task_data.assault 273 | if tweak and gai_state and assault_data and assault_data.active then 274 | local get_value = gai_state._get_difficulty_dependent_value or function() return 0 end 275 | local get_mult = gai_state._get_balancing_multiplier or function() return 0 end 276 | local phase = self:text("wolfhud_advassault_phase_title") .. " " .. self:text("wolfhud_advassault_phase_" .. assault_data.phase) 277 | 278 | local spawns = get_value(gai_state, tweak.force_pool) * get_mult(gai_state, tweak.force_pool_balance_mul) 279 | local spawns_left = self:text("wolfhud_advassault_spawns_title") .. " " .. math.round(math.max(spawns - assault_data.force_spawned, 0)) 280 | 281 | local time_left = assault_data.phase_end_t - gai_state._t + 350 282 | if assault_data.phase == "build" then 283 | local sustain_duration = math.lerp(get_value(gai_state, tweak.sustain_duration_min), get_value(gai_state, tweak.sustain_duration_max), 0.5) * get_mult(gai_state, tweak.sustain_duration_balance_mul) 284 | time_left = time_left + sustain_duration + tweak.fade_duration 285 | elseif assault_data.phase == "sustain" then 286 | time_left = time_left + tweak.fade_duration 287 | end 288 | --if gai_state:_count_police_force("assault") > 7 then -- 350 = additional duration, if more than 7 assault groups are active (hardcoded values in gai_state). 289 | -- time_left = time_left + 350 290 | --end 291 | if time_left < 0 then 292 | time_left = self:text("wolfhud_advassault_time_overdue") 293 | else 294 | time_left = self:text("wolfhud_advassault_time_title") .. " " .. string.format("%.2f", time_left) 295 | end 296 | 297 | local spacer = string.rep(" ", 10) 298 | local sep = string.format("%s%s%s", spacer, self:text("hud_assault_end_line"), spacer) 299 | return string.format("%s%s%s%s%s", phase, sep, spawns_left, sep, time_left) 300 | end 301 | end 302 | end 303 | return self:text("hud_assault_assault") 304 | end 305 | end 306 | -------------------------------------------------------------------------------- /lua/BurstFire.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Weapon tweak data attributes: 3 | BURST_FIRE: 4 | - Force enable burst fire using the specified number as the burst size (if weapon does not have a fire mode mod installed) 5 | nil /undef - If the weapon can toggle fire mode and does not have a fire mode mod installed, enable burst fire with default burst size (3) 6 | false - Force disable burst fire even if weapon can toggle fire mode 7 | 8 | ADAPTIVE_BURST_SIZE: 9 | nil/true - Allow abortion of ongoing burst if trigger is released 10 | false - Force entire burst to be fired before resetting 11 | 12 | BURST_FIRE_RATE_MULTIPLIER: 13 | - Apply specified multiplier to fire rate when firing in burst mode 14 | 15 | DELAYED_BURST_RECOIL: 16 | true/false - Build up and delay recoil until last shot in burst is fired 17 | ]] 18 | if not WolfHUD:getSetting({"EQUIPMENT", "ENABLE_BURSTMODE"}, true) then 19 | return 20 | end 21 | 22 | if string.lower(RequiredScript) == "lib/units/weapons/newraycastweaponbase" then 23 | 24 | local _update_stats_values_original = NewRaycastWeaponBase._update_stats_values 25 | local fire_rate_multiplier_original = NewRaycastWeaponBase.fire_rate_multiplier 26 | local recoil_multiplier_original = NewRaycastWeaponBase.recoil_multiplier 27 | local on_enabled_original = NewRaycastWeaponBase.on_enabled 28 | local on_disabled_original = NewRaycastWeaponBase.on_disabled 29 | local start_reload_original = NewRaycastWeaponBase.start_reload 30 | local fire_original = NewRaycastWeaponBase.fire 31 | local toggle_firemode_original = NewRaycastWeaponBase.toggle_firemode 32 | 33 | NewRaycastWeaponBase.DEFAULT_BURST_SIZE = 3 34 | NewRaycastWeaponBase.IDSTRING_SINGLE = Idstring("single") 35 | NewRaycastWeaponBase.IDSTRING_AUTO = Idstring("auto") 36 | 37 | function NewRaycastWeaponBase:_update_stats_values(...) 38 | _update_stats_values_original(self, ...) 39 | 40 | if not self:is_npc() then 41 | self._burst_rounds_remaining = 0 42 | self._has_auto = not self._locked_fire_mode and (self:can_toggle_firemode() or self:weapon_tweak_data().FIRE_MODE == "auto") 43 | self._has_burst_fire = (self:can_toggle_firemode() or self:weapon_tweak_data().BURST_FIRE) and self:weapon_tweak_data().BURST_FIRE ~= false 44 | --self._has_burst_fire = (not self._locked_fire_mode or managers.weapon_factor:has_perk("fire_mode_burst", self._factory_id, self._blueprint) or (self:can_toggle_firemode() or self:weapon_tweak_data().BURST_FIRE) and self:weapon_tweak_data().BURST_FIRE ~= false 45 | --self._locked_fire_mode = self._locked_fire_mode or managers.weapon_factor:has_perk("fire_mode_burst", self._factory_id, self._blueprint) and Idstring("burst") 46 | self._burst_size = self:weapon_tweak_data().BURST_FIRE or NewRaycastWeaponBase.DEFAULT_BURST_SIZE 47 | self._adaptive_burst_size = self:weapon_tweak_data().ADAPTIVE_BURST_SIZE ~= false 48 | self._burst_fire_rate_multiplier = self:weapon_tweak_data().BURST_FIRE_RATE_MULTIPLIER or 1 49 | self._delayed_burst_recoil = self:weapon_tweak_data().DELAYED_BURST_RECOIL 50 | 51 | self._burst_rounds_fired = 0 52 | end 53 | end 54 | 55 | function NewRaycastWeaponBase:fire_rate_multiplier(...) 56 | local mult = 1 57 | if self:in_burst_mode() then 58 | mult = mult * (self._burst_fire_rate_multiplier or 1) 59 | end 60 | 61 | return fire_rate_multiplier_original(self, ...) * mult 62 | end 63 | 64 | function NewRaycastWeaponBase:recoil_multiplier(...) 65 | local mult = 1 66 | if self._delayed_burst_recoil and self:in_burst_mode() and self:burst_rounds_remaining() then 67 | mult = 0 68 | end 69 | 70 | return recoil_multiplier_original(self, ...) * mult 71 | end 72 | 73 | function NewRaycastWeaponBase:on_enabled(...) 74 | self:cancel_burst() 75 | return on_enabled_original(self, ...) 76 | end 77 | 78 | function NewRaycastWeaponBase:on_disabled(...) 79 | self:cancel_burst() 80 | return on_disabled_original(self, ...) 81 | end 82 | 83 | function NewRaycastWeaponBase:start_reload(...) 84 | self:cancel_burst() 85 | return start_reload_original(self, ...) 86 | end 87 | 88 | function NewRaycastWeaponBase:fire(...) 89 | local result = fire_original(self, ...) 90 | 91 | if result and not self.AKIMBO and self:in_burst_mode() then 92 | if self:clip_empty() then 93 | self:cancel_burst() 94 | else 95 | self._burst_rounds_fired = self._burst_rounds_fired + 1 96 | self._burst_rounds_remaining = (self._burst_rounds_remaining <= 0 and self._burst_size or self._burst_rounds_remaining) - 1 97 | if self._burst_rounds_remaining <= 0 then 98 | self:cancel_burst() 99 | end 100 | end 101 | end 102 | 103 | return result 104 | end 105 | 106 | --Semi-override 107 | function NewRaycastWeaponBase:toggle_firemode(...) 108 | return self:can_use_burst_mode() and not self._locked_fire_mode and not self:gadget_overrides_weapon_functions() and self:_check_toggle_burst() or toggle_firemode_original(self, ...) 109 | end 110 | 111 | function NewRaycastWeaponBase:_check_toggle_burst() 112 | if self:in_burst_mode() then 113 | self:_set_burst_mode(false, self.AKIMBO and not self._has_auto) 114 | return true 115 | elseif (self._fire_mode == Idstring("single")) or (self._fire_mode == Idstring("auto") and not self:can_toggle_firemode()) then 116 | self:_set_burst_mode(true, self.AKIMBO) 117 | return true 118 | end 119 | end 120 | 121 | function NewRaycastWeaponBase:_set_burst_mode(status, skip_sound) 122 | self._in_burst_mode = status 123 | self._fire_mode = NewRaycastWeaponBase["IDSTRING_" .. (status and "SINGLE" or self._has_auto and "AUTO" or "SINGLE")] 124 | 125 | if not skip_sound then 126 | self._sound_fire:post_event((status or self._has_auto) and "wp_auto_switch_on" or "wp_auto_switch_off") 127 | end 128 | 129 | self:cancel_burst() 130 | end 131 | 132 | function NewRaycastWeaponBase:can_use_burst_mode() 133 | return self._has_burst_fire 134 | end 135 | 136 | function NewRaycastWeaponBase:in_burst_mode() 137 | return self._fire_mode == NewRaycastWeaponBase.IDSTRING_SINGLE and self._in_burst_mode and not self:gadget_overrides_weapon_functions() 138 | end 139 | 140 | function NewRaycastWeaponBase:burst_rounds_remaining() 141 | return self._burst_rounds_remaining > 0 and self._burst_rounds_remaining or false 142 | end 143 | 144 | function NewRaycastWeaponBase:cancel_burst(soft_cancel) 145 | if self._adaptive_burst_size or not soft_cancel then 146 | self._burst_rounds_remaining = 0 147 | 148 | if self._delayed_burst_recoil and self._burst_rounds_fired > 0 then 149 | self._setup.user_unit:movement():current_state():force_recoil_kick(self, self._burst_rounds_fired) 150 | end 151 | self._burst_rounds_fired = 0 152 | end 153 | end 154 | 155 | elseif string.lower(RequiredScript) == "lib/units/weapons/akimboweaponbase" then 156 | 157 | local _update_stats_values_original = AkimboWeaponBase._update_stats_values 158 | local fire_original = AkimboWeaponBase.fire 159 | local fire_rate_multiplier_original = AkimboWeaponBase.fire_rate_multiplier 160 | local toggle_firemode_original = AkimboWeaponBase.toggle_firemode 161 | 162 | function AkimboWeaponBase:_update_stats_values(...) 163 | _update_stats_values_original(self, ...) 164 | 165 | if not self:is_npc() then 166 | self._has_burst_fire = self._has_burst_fire or ((self:weapon_tweak_data().BURST_FIRE ~= false) and (self._fire_mode == NewRaycastWeaponBase.IDSTRING_SINGLE)) 167 | 168 | if self:can_use_burst_mode() then 169 | self:_set_burst_mode(not self._manual_fire_second_gun, true) 170 | end 171 | end 172 | end 173 | 174 | function AkimboWeaponBase:fire(...) 175 | local results = fire_original(self, ...) 176 | 177 | if not self:_in_burst_or_auto_mode() then 178 | self._fire_callbacks = {} 179 | end 180 | 181 | return results 182 | end 183 | 184 | function AkimboWeaponBase:fire_rate_multiplier(...) 185 | return fire_rate_multiplier_original(self, ...) * (self:_in_burst_or_auto_mode() and 2 or 1) 186 | end 187 | 188 | --Override 189 | function AkimboWeaponBase:toggle_firemode(...) 190 | return self:can_use_burst_mode() and self:_check_toggle_burst() or toggle_firemode_original(self, ...) 191 | end 192 | 193 | function AkimboWeaponBase:_set_burst_mode(status, skip_sound) 194 | if alive(self._second_gun) then 195 | self._second_gun:base():_set_burst_mode(status, skip_sound) 196 | end 197 | 198 | return AkimboWeaponBase.super._set_burst_mode(self, status, skip_sound) 199 | end 200 | 201 | function AkimboWeaponBase:_in_burst_or_auto_mode() 202 | return self._fire_mode == NewRaycastWeaponBase.IDSTRING_AUTO or self:in_burst_mode() 203 | end 204 | 205 | elseif string.lower(RequiredScript) == "lib/units/weapons/akimboshotgunbase" then 206 | 207 | local _update_stats_values_original = AkimboShotgunBase._update_stats_values 208 | 209 | function AkimboShotgunBase:_update_stats_values(...) 210 | _update_stats_values_original(self, ...) 211 | 212 | if not self:is_npc() then 213 | self._has_burst_fire = self._has_burst_fire or ((self:weapon_tweak_data().BURST_FIRE ~= false) and (self._fire_mode == NewRaycastWeaponBase.IDSTRING_SINGLE)) 214 | 215 | if self._has_burst_fire then 216 | self:_set_burst_mode(not self._manual_fire_second_gun, true) 217 | end 218 | end 219 | end 220 | 221 | elseif string.lower(RequiredScript) == "lib/units/beings/player/states/playerstandard" then 222 | 223 | local update_original = PlayerStandard.update 224 | local _check_action_primary_attack_original = PlayerStandard._check_action_primary_attack 225 | local _check_action_deploy_underbarrel_original = PlayerStandard._check_action_deploy_underbarrel 226 | 227 | function PlayerStandard:update(t, ...) 228 | update_original(self, t, ...) 229 | self:_update_burst_fire(t) 230 | end 231 | 232 | function PlayerStandard:_check_action_primary_attack(t, input, ...) 233 | if self._trigger_down and not input.btn_primary_attack_state then 234 | self._equipped_unit:base():cancel_burst(true) 235 | end 236 | self._trigger_down = input.btn_primary_attack_state 237 | 238 | return _check_action_primary_attack_original(self, t, input, ...) 239 | end 240 | 241 | function PlayerStandard:_check_action_deploy_underbarrel(...) 242 | local new_action = _check_action_deploy_underbarrel_original(self, ...) 243 | 244 | if new_action and alive(self._equipped_unit) and self._equipped_unit:base() and self._equipped_unit:base():in_burst_mode() then 245 | managers.hud:set_teammate_weapon_firemode_burst(self._equipped_unit:base():selection_index()) 246 | end 247 | 248 | return new_action 249 | end 250 | 251 | --Override 252 | function PlayerStandard:_check_action_weapon_firemode(t, input) 253 | local wbase = self._equipped_unit:base() 254 | if input.btn_weapon_firemode_press and wbase.toggle_firemode then 255 | self:_check_stop_shooting() 256 | if wbase:toggle_firemode() then 257 | if wbase:in_burst_mode() then 258 | managers.hud:set_teammate_weapon_firemode_burst(self._unit:inventory():equipped_selection()) 259 | else 260 | managers.hud:set_teammate_weapon_firemode(HUDManager.PLAYER_PANEL, self._unit:inventory():equipped_selection(), wbase:fire_mode()) 261 | end 262 | end 263 | end 264 | end 265 | 266 | function PlayerStandard:_update_burst_fire(t) 267 | if alive(self._equipped_unit) and self._equipped_unit:base():burst_rounds_remaining() then 268 | self:_check_action_primary_attack(t, { btn_primary_attack_state = true, btn_primary_attack_press = true }) 269 | end 270 | end 271 | 272 | function PlayerStandard:force_recoil_kick(weap_base, manual_multiplier) 273 | local recoil_multiplier = (weap_base:recoil() + weap_base:recoil_addend()) * weap_base:recoil_multiplier() * (manual_multiplier or 1) 274 | local up, down, left, right = unpack(weap_base:weapon_tweak_data().kick[self._state_data.in_steelsight and "steelsight" or self._state_data.ducking and "crouching" or "standing"]) 275 | self._camera_unit:base():recoil_kick(up * recoil_multiplier, down * recoil_multiplier, left * recoil_multiplier, right * recoil_multiplier) 276 | end 277 | 278 | elseif string.lower(RequiredScript) == "lib/managers/playermanager" then 279 | 280 | local spawned_player_original = PlayerManager.spawned_player 281 | 282 | function PlayerManager:spawned_player(id, unit, ...) 283 | spawned_player_original(self, id, unit, ...) 284 | 285 | if unit:key() == self:player_unit():key() then 286 | for i = 1, 2, 1 do 287 | local wbase = unit:inventory():unit_by_selection(i):base() 288 | if wbase:in_burst_mode() then 289 | managers.hud:set_teammate_weapon_firemode_burst(i) 290 | end 291 | end 292 | end 293 | end 294 | 295 | elseif string.lower(RequiredScript) == "lib/managers/hudmanagerpd2" then 296 | 297 | HUDManager._USE_BURST_MODE = true --Custom HUD compatibility 298 | 299 | HUDManager.set_teammate_weapon_firemode_burst = HUDManager.set_teammate_weapon_firemode_burst or function(self, id) 300 | self._teammate_panels[HUDManager.PLAYER_PANEL]:set_weapon_firemode_burst(id) 301 | end 302 | 303 | elseif string.lower(RequiredScript) == "lib/managers/hud/hudteammate" then 304 | 305 | --Default function for vanilla HUD. If using a custom HUD that alters fire mode HUD components, make sure to implement this function in it 306 | HUDTeammate.set_weapon_firemode_burst = HUDTeammate.set_weapon_firemode_burst or function(self, id) 307 | local is_secondary = id == 1 308 | local secondary_weapon_panel = self._player_panel:child("weapons_panel"):child("secondary_weapon_panel") 309 | local primary_weapon_panel = self._player_panel:child("weapons_panel"):child("primary_weapon_panel") 310 | local weapon_selection = is_secondary and secondary_weapon_panel:child("weapon_selection") or primary_weapon_panel:child("weapon_selection") 311 | if alive(weapon_selection) then 312 | local firemode_single = weapon_selection:child("firemode_single") 313 | local firemode_auto = weapon_selection:child("firemode_auto") 314 | if alive(firemode_single) and alive(firemode_auto) then 315 | firemode_single:show() 316 | firemode_auto:show() 317 | end 318 | end 319 | end 320 | 321 | end 322 | -------------------------------------------------------------------------------- /lua/BuyAllAsset.lua: -------------------------------------------------------------------------------- 1 | --do return end 2 | 3 | if string.lower(RequiredScript) == "lib/managers/missionassetsmanager" then 4 | function MissionAssetsManager:unlock_all_buyable_assets() 5 | for _, asset in ipairs(self._global.assets) do 6 | if self:asset_is_buyable(asset) then 7 | self:unlock_asset(asset.id) 8 | end 9 | end 10 | end 11 | 12 | function MissionAssetsManager:asset_is_buyable(asset) 13 | return self:asset_is_locked(asset) and (Network:is_server() and asset.can_unlock or Network:is_client() and self:get_asset_can_unlock_by_id(asset.id)) 14 | end 15 | 16 | function MissionAssetsManager:asset_is_locked(asset) 17 | return asset.show and not asset.unlocked 18 | end 19 | 20 | function MissionAssetsManager:has_locked_assets() 21 | local level_id = managers.job:current_level_id() 22 | if not tweak_data.preplanning or not tweak_data.preplanning.locations or not tweak_data.preplanning.locations[level_id] then 23 | for _, asset in ipairs(self._global.assets) do 24 | if self:asset_is_locked(asset) then 25 | return true 26 | end 27 | end 28 | end 29 | return false 30 | end 31 | 32 | function MissionAssetsManager:has_buyable_assets() 33 | local level_id = managers.job:current_level_id() 34 | if self:is_unlock_asset_allowed() and not tweak_data.preplanning or not tweak_data.preplanning.locations or not tweak_data.preplanning.locations[level_id] then 35 | local asset_costs = self:get_total_assets_costs() 36 | if asset_costs > 0 then 37 | return true 38 | end 39 | end 40 | return false 41 | end 42 | 43 | function MissionAssetsManager:get_total_assets_costs() 44 | local total_costs = 0 45 | for _, asset in ipairs(self._global.assets) do 46 | if self:asset_is_buyable(asset) then 47 | total_costs = total_costs + (asset.id and managers.money:get_mission_asset_cost_by_id(asset.id) or 0) 48 | end 49 | end 50 | return total_costs 51 | end 52 | elseif string.lower(RequiredScript) == "lib/managers/menu/missionbriefinggui" then 53 | local create_assets_original = AssetsItem.create_assets 54 | local unlock_asset_by_id_original = AssetsItem.unlock_asset_by_id 55 | local move_up_original = AssetsItem.move_up 56 | local move_down_original = AssetsItem.move_down 57 | local move_left_original = AssetsItem.move_left 58 | local move_right_original = AssetsItem.move_right 59 | local confirm_pressed_original = AssetsItem.confirm_pressed 60 | local mouse_moved_original = AssetsItem.mouse_moved 61 | local mouse_pressed_original = AssetsItem.mouse_pressed 62 | 63 | function AssetsItem:create_assets(...) 64 | create_assets_original(self, ...) 65 | 66 | if self.buy_all_button then 67 | self.buy_all_button:hide() 68 | self._is_buy_all_dialog_open = true 69 | end 70 | 71 | self._buy_all_btn = self._panel:text({ 72 | name = "buy_all_btn", 73 | text = "", 74 | h = tweak_data.menu.pd2_medium_font_size * 0.95, 75 | font_size = tweak_data.menu.pd2_medium_font_size * 0.9, 76 | font = tweak_data.menu.pd2_medium_font, 77 | color = tweak_data.screen_colors.button_stage_3, 78 | align = "right", 79 | blend_mode = "add", 80 | visible = managers.assets:has_locked_assets(), 81 | }) 82 | 83 | self:update_buy_all_btn() 84 | end 85 | 86 | function AssetsItem:unlock_asset_by_id(...) 87 | unlock_asset_by_id_original(self, ...) 88 | 89 | self:update_buy_all_btn() 90 | end 91 | 92 | function AssetsItem:move_up(...) 93 | if self._asset_selected and (self._asset_selected % 2 > 0) and managers.assets:has_buyable_assets() and self:can_afford_all_assets() then 94 | self._buy_all_highlighted = true 95 | self._last_selected_asset = self._asset_selected 96 | self:check_deselect_item() 97 | self:update_buy_all_btn(true) 98 | managers.menu_component:post_event("highlight") 99 | else 100 | move_up_original(self, ...) 101 | end 102 | end 103 | 104 | function AssetsItem:move_down(...) 105 | if self._buy_all_highlighted then 106 | self._buy_all_highlighted = nil 107 | self:select_asset(self._last_selected_asset) 108 | self:update_buy_all_btn(true) 109 | self._last_selected_asset = nil 110 | else 111 | move_down_original(self, ...) 112 | end 113 | end 114 | 115 | function AssetsItem:move_left(...) 116 | if not self._buy_all_highlighted then 117 | move_left_original(self, ...) 118 | end 119 | end 120 | 121 | function AssetsItem:move_right(...) 122 | if not self._buy_all_highlighted then 123 | move_right_original(self, ...) 124 | end 125 | end 126 | 127 | function AssetsItem:confirm_pressed(...) 128 | if self._buy_all_highlighted then 129 | if self:can_afford_all_assets() then 130 | managers.assets:unlock_all_buyable_assets() 131 | self:update_buy_all_btn() 132 | self:move_down() 133 | end 134 | else 135 | return confirm_pressed_original(self, ...) 136 | end 137 | end 138 | 139 | function AssetsItem:mouse_moved(x, y, ...) 140 | if alive(self._buy_all_btn) and managers.assets:has_buyable_assets() then 141 | if self._buy_all_btn:inside(x, y) then 142 | if not self._buy_all_highlighted then 143 | self._buy_all_highlighted = true 144 | self:update_buy_all_btn(true) 145 | self:check_deselect_item() 146 | if self:can_afford_all_assets() then 147 | managers.menu_component:post_event("highlight") 148 | end 149 | end 150 | return true, "link" 151 | elseif self._buy_all_highlighted then 152 | self._buy_all_highlighted = nil 153 | self:update_buy_all_btn(true) 154 | end 155 | end 156 | 157 | return mouse_moved_original(self, x, y, ...) 158 | end 159 | 160 | function AssetsItem:mouse_pressed(button, x, y, ...) 161 | if alive(self._buy_all_btn) and self:can_afford_all_assets() and button == Idstring("0") and self._buy_all_btn:inside(x, y) then 162 | managers.assets:unlock_all_buyable_assets() 163 | self:update_buy_all_btn() 164 | end 165 | 166 | return mouse_pressed_original(self, button, x, y, ...) 167 | end 168 | 169 | function AssetsItem:update_buy_all_btn(colors_only) 170 | if alive(self._buy_all_btn) then 171 | local asset_costs = managers.assets:get_total_assets_costs() 172 | if managers.assets:has_buyable_assets() then 173 | if self:can_afford_all_assets() then 174 | self._buy_all_btn:set_color(self._buy_all_highlighted and tweak_data.screen_colors.button_stage_2 or tweak_data.screen_colors.button_stage_3) 175 | else 176 | self._buy_all_btn:set_color(tweak_data.screen_colors.pro_color) 177 | end 178 | else 179 | self._buy_all_btn:set_color(tweak_data.screen_color_grey) 180 | end 181 | if not colors_only then 182 | local text = string.format("%s (%s)", managers.localization:to_upper_text("wolfhud_buy_all_assets"), managers.experience:cash_string(asset_costs)) 183 | self._buy_all_btn:set_text(text) 184 | local _, _, w, _ = self._buy_all_btn:text_rect() 185 | self._buy_all_btn:set_w(math.ceil(w)) 186 | self._buy_all_btn:set_top(15) 187 | if managers.menu:is_pc_controller() then 188 | self._buy_all_btn:set_right(self._panel:w() - 5) 189 | else 190 | self._buy_all_btn:set_left(5) 191 | end 192 | end 193 | end 194 | end 195 | 196 | function AssetsItem:can_afford_all_assets() 197 | return (managers.assets:get_total_assets_costs() <= managers.money:total()) 198 | end 199 | end -------------------------------------------------------------------------------- /lua/ContractHeat.lua: -------------------------------------------------------------------------------- 1 | -- CONFIG ********************************************************************** 2 | 3 | -- OVERRIDES ******************************************************************* 4 | local init_original = ContractBrokerHeistItem.init 5 | function ContractBrokerHeistItem:init(...) -- parent_panel, job_data, idx 6 | 7 | init_original(self, ...) 8 | 9 | local job_tweak = tweak_data.narrative:job_data(self._job_data.job_id) 10 | 11 | if WolfHUD:getSetting({"INVENTORY", "SHOW_CONTRACTOR_JOB_HEAT"}, true) and job_tweak and job_tweak.contact ~= "skirmish" then 12 | local heat_text, heat_color = self:get_job_heat_text(self._job_data.job_id) 13 | 14 | local heat = self._panel:text({ 15 | alpha = 1, 16 | vertical = "top", 17 | layer = 1, 18 | align = "right", 19 | halign = "right", 20 | valign = "top", 21 | text = heat_text, 22 | font = tweak_data.menu.pd2_large_font, 23 | font_size = tweak_data.menu.pd2_medium_font_size * 0.8, 24 | color = heat_color 25 | }) 26 | self:make_fine_text(heat) 27 | heat:set_right(self._panel:right() - 10) 28 | heat:set_top(13) 29 | end 30 | end 31 | 32 | -- FUNCTION LIB **************************************************************** 33 | function ContractBrokerHeistItem:make_fine_text(text) 34 | local x, y, w, h = text:text_rect() 35 | 36 | text:set_size(w, h) 37 | text:set_position(math.round(text:x()), math.round(text:y())) 38 | end 39 | 40 | function ContractBrokerHeistItem:get_job_heat_text(job_id) 41 | local heat_text = "" 42 | local heat_color = Color(1,0,1) 43 | local exp_multiplier = managers.job:heat_to_experience_multiplier(managers.job:get_job_heat(job_id)) 44 | local exp_percent = ((1 - exp_multiplier)*-1)*100 45 | 46 | if exp_percent ~= 0 then 47 | local prefix = exp_percent > 0 and "+" or "" 48 | heat_text = "("..prefix..exp_percent.."%)" 49 | heat_color = exp_percent > 0 and tweak_data.screen_colors.heat_warm_color or tweak_data.screen_colors.heat_cold_color 50 | end 51 | 52 | return heat_text, heat_color 53 | end -------------------------------------------------------------------------------- /lua/DamageIndicator.lua: -------------------------------------------------------------------------------- 1 | if not WolfHUD:getSetting({"DamageIndicator", "ENABLED"}, true) then return end 2 | 3 | if string.lower(RequiredScript) == "lib/managers/hud/hudhitdirection" then 4 | HUDHitDirection.indicator_count = 0 5 | HUDHitDirection.DAMAGE_TYPES = {} 6 | HUDHitDirection.DAMAGE_TYPES.HEALTH = 1 7 | HUDHitDirection.DAMAGE_TYPES.ARMOUR = 2 8 | HUDHitDirection.DAMAGE_TYPES.VEHICLE = 3 9 | HUDHitDirection.DAMAGE_TYPES.CRIT = 4 10 | HUDHitDirection.DAMAGE_TYPES.FRIENDLY_FIRE = 5 11 | 12 | local init_original = HUDHitDirection.init 13 | local _add_hit_indicator_original = HUDHitDirection._add_hit_indicator 14 | local _remove_original = HUDHitDirection._remove 15 | 16 | function HUDHitDirection:init(...) 17 | init_original(self, ...) 18 | if alive(self._hud_panel) and alive(self._hit_direction_panel) then 19 | self._hit_direction_panel:set_w(self._hud_panel:w()) 20 | self._hit_direction_panel:set_h(self._hud_panel:h()) 21 | self._hit_direction_panel:set_center(self._hit_direction_panel:parent():w() * 0.5, self._hit_direction_panel:parent():h() * 0.5) 22 | end 23 | end 24 | 25 | function HUDHitDirection:_add_hit_indicator(...) 26 | HUDHitDirection.PANEL_SIZE = WolfHUD:getSetting({"DamageIndicator", "SIZE"}, 150) 27 | if self.indicator_count < WolfHUD:getSetting({"DamageIndicator", "MAX_AMOUNT"}, 10) then 28 | self.indicator_count = self.indicator_count + 1 29 | _add_hit_indicator_original(self, ...) 30 | end 31 | end 32 | 33 | function HUDHitDirection:_animate(indicator, data, remove_func) 34 | data.duration = WolfHUD:getSetting({"DamageIndicator", "DURATION"}, 2) 35 | data.t = 0 36 | while data.t < data.duration do 37 | data.t = data.t + coroutine.yield() 38 | if alive(indicator) then 39 | local o = data.t / data.duration 40 | indicator:set_color(self:_get_indicator_color(data.damage_type, o)) 41 | indicator:set_alpha( math.clamp(math.sin(o * 180), 0, 1) ) 42 | if managers.player:player_unit() then 43 | local ply_camera = managers.player:player_unit():camera() 44 | if ply_camera then 45 | local target_vec = ply_camera:position() - data.origin 46 | local angle = target_vec:to_polar_with_reference(ply_camera:forward(), math.UP).spin 47 | local r = HUDHitDirection.PANEL_SIZE + (1-math.pow(o,0.5)) * (100) 48 | if data.fixed_angle ~= nil then 49 | angle = data.fixed_angle 50 | end 51 | indicator:set_rotation(90 - angle) 52 | indicator:set_center(self._hit_direction_panel:w() * 0.5 - math.sin(angle + 180) * r, self._hit_direction_panel:h() * 0.5 - math.cos(angle + 180) * r) 53 | end 54 | end 55 | end 56 | end 57 | remove_func(indicator, data) 58 | end 59 | 60 | function HUDHitDirection:_remove(...) 61 | _remove_original(self, ...) 62 | self.indicator_count = self.indicator_count - 1 63 | end 64 | 65 | function HUDHitDirection:_get_indicator_color(damage_type, t) 66 | if damage_type == HUDHitDirection.DAMAGE_TYPES.HEALTH then 67 | return WolfHUD:getColorSetting({"DamageIndicator", "HEALTH_COLOR"}, "red") 68 | elseif damage_type == HUDHitDirection.DAMAGE_TYPES.ARMOUR then 69 | return WolfHUD:getColorSetting({"DamageIndicator", "SHIELD_COLOR"}, "white") 70 | elseif damage_type == HUDHitDirection.DAMAGE_TYPES.VEHICLE then 71 | return WolfHUD:getColorSetting({"DamageIndicator", "VEHICLE_COLOR"}, "yellow") 72 | elseif damage_type == HUDHitDirection.DAMAGE_TYPES.CRIT then 73 | return WolfHUD:getColorSetting({"DamageIndicator", "CRIT_COLOR"}, "purple") 74 | elseif damage_type == HUDHitDirection.DAMAGE_TYPES.FRIENDLY_FIRE then 75 | return WolfHUD:getColorSetting({"DamageIndicator", "FRIENDLY_FIRE_COLOR"}, "orange") 76 | else 77 | return Color(1, t, t) 78 | end 79 | end 80 | elseif string.lower(RequiredScript) == "lib/units/beings/player/playerdamage" then 81 | local PlayerDamage_damage_explosion = PlayerDamage.damage_explosion 82 | local PlayerDamage_damage_fire = PlayerDamage.damage_fire 83 | 84 | function PlayerDamage:damage_explosion(attack_data, ...) 85 | local value = PlayerDamage_damage_explosion(self, attack_data, ...) 86 | if alive(self._unit) and (attack_data.position or attack_data.col_ray.position) then 87 | local distance = mvector3.distance(attack_data.position or attack_data.col_ray.position, self._unit:position()) 88 | if self:_chk_can_take_dmg() and distance <= attack_data.range and not (self._god_mode or self._invulnerable or self._mission_damage_blockers.invulnerable or self:incapacitated() or self._bleed_out) then 89 | self:_hit_direction(attack_data.position, HUDHitDirection.DAMAGE_TYPES.FRIENDLY_FIRE) 90 | end 91 | end 92 | return value 93 | end 94 | 95 | function PlayerDamage:damage_fire(attack_data, ...) 96 | local value = PlayerDamage_damage_fire(self, attack_data, ...) 97 | if alive(self._unit) and (attack_data.position or attack_data.col_ray.position) then 98 | local distance = mvector3.distance(attack_data.position or attack_data.col_ray.position, self._unit:position()) 99 | if self:_chk_can_take_dmg() and (attack_data.range == nil or distance <= attack_data.range) and not (self._god_mode or self._invulnerable or self._mission_damage_blockers.invulnerable or self:incapacitated() or self._bleed_out) then 100 | self:_hit_direction(attack_data.position, HUDHitDirection.DAMAGE_TYPES.FRIENDLY_FIRE) 101 | end 102 | end 103 | return value 104 | end 105 | 106 | function PlayerDamage:_hit_direction(position_vector, damage_type) 107 | if position_vector then 108 | local armor_left, low_health = (self:get_real_armor() > 0), ((self:get_real_health() / self:_max_health()) <= 0.20) 109 | local dmg_type = damage_type or armor_left and HUDHitDirection.DAMAGE_TYPES.ARMOUR or low_health and HUDHitDirection.DAMAGE_TYPES.CRIT or HUDHitDirection.DAMAGE_TYPES.HEALTH 110 | managers.hud:on_hit_direction(position_vector, dmg_type) 111 | end 112 | end 113 | elseif string.lower(RequiredScript) == "lib/units/vehicles/vehicledamage" then 114 | --[[ -- Causes Access violation: Something with the angle calculation of the animation... 115 | function VehicleDamage:_hit_direction(position_vector, damage_type) 116 | if position_vector then 117 | local dmg_type = damage_type or HUDHitDirection.DAMAGE_TYPES.VEHICLE 118 | managers.hud:on_hit_direction(position_vector, dmg_type) 119 | end 120 | end 121 | --]] 122 | end -------------------------------------------------------------------------------- /lua/DamagePopup.lua: -------------------------------------------------------------------------------- 1 | if RequiredScript == "lib/units/enemies/cop/copdamage" and not CopDamage._damage_popup_loaded then 2 | CopDamage._damage_popup_loaded = true 3 | local _on_damage_received_original = CopDamage._on_damage_received 4 | --Workaround for Teammate Headshots, since col_ray doesn't get forwarded... (self._sync_ibody_popup) 5 | local sync_damage_bullet_original = CopDamage.sync_damage_bullet 6 | local sync_damage_melee_original = CopDamage.sync_damage_melee 7 | 8 | function CopDamage:_on_damage_received(data, ...) 9 | self:_process_popup_damage(data) 10 | self._sync_ibody_popup = nil 11 | return _on_damage_received_original(self, data, ...) 12 | end 13 | 14 | function CopDamage:sync_damage_bullet(attacker_unit, damage_percent, i_body, ...) 15 | if i_body then 16 | self._sync_ibody_popup = i_body 17 | end 18 | 19 | return sync_damage_bullet_original(self, attacker_unit, damage_percent, i_body, ...) 20 | end 21 | 22 | function CopDamage:sync_damage_melee(attacker_unit, damage_percent, damage_effect_percent, i_body, ...) 23 | if i_body then 24 | self._sync_ibody_popup = i_body 25 | end 26 | 27 | return sync_damage_melee_original(self, attacker_unit, damage_percent, damage_effect_percent, i_body, ...) 28 | 29 | end 30 | 31 | function CopDamage:_process_popup_damage(data) 32 | CopDamage.DMG_POPUP_SETTING = WolfHUD:getSetting({"DamagePopup", "DISPLAY_MODE"}, 2) 33 | 34 | local attacker = alive(data.attacker_unit) and data.attacker_unit 35 | local damage = tonumber(data.damage) or 0 36 | 37 | if attacker and damage >= 0.1 and CopDamage.DMG_POPUP_SETTING > 1 then 38 | local killer 39 | 40 | if attacker:in_slot(3) or attacker:in_slot(5) then 41 | --Human team mate 42 | killer = attacker 43 | elseif attacker:in_slot(2) then 44 | --Player 45 | killer = attacker 46 | elseif attacker:in_slot(16) then 47 | --Bot/joker 48 | local key = tostring(attacker:key()) 49 | local minion_data = managers.gameinfo and managers.gameinfo:get_minions(key) 50 | if minion_data then 51 | -- Joker 52 | killer = minion_data.owner and managers.criminals:character_unit_by_peer_id(minion_data.owner) 53 | else 54 | -- Bot 55 | killer = attacker 56 | end 57 | elseif attacker:in_slot(12) then 58 | --Enemy 59 | elseif attacker:in_slot(25) then 60 | --Turret 61 | local owner = attacker:base():get_owner_id() 62 | if owner then 63 | killer = managers.criminals:character_unit_by_peer_id(owner) 64 | end 65 | elseif attacker:base().thrower_unit then 66 | killer = attacker:base():thrower_unit() 67 | end 68 | 69 | if alive(killer) and alive(self._unit) then 70 | local body = data.col_ray and data.col_ray.body or self._sync_ibody_popup and self._unit:body(self._sync_ibody_popup) 71 | local headshot = body and self.is_head and self:is_head(body) or false 72 | if CopDamage.DMG_POPUP_SETTING == 2 then 73 | if killer:in_slot(2) then 74 | self:show_popup(damage, self._dead, headshot, data.critical_hit) 75 | end 76 | else 77 | local color_id = managers.criminals:character_color_id_by_unit(killer) 78 | if color_id then 79 | self:show_popup(damage, self._dead, headshot, false, color_id) 80 | end 81 | end 82 | end 83 | end 84 | end 85 | 86 | function CopDamage:show_popup(damage, dead, headshot, critical, color_id) 87 | if managers.waypoints then 88 | local id = "damage_wp_" .. tostring(self._unit:key()) 89 | local waypoint = managers.waypoints:get_waypoint(id) 90 | local waypoint_color = color_id and ((color_id == 5 and WolfHUD:getSetting({"CustomHUD", "TEAMMATE", "AI_COLOR", "USE"}, false)) and WolfHUD:getColorSetting({"CustomHUD", "TEAMMATE", "AI_COLOR", "COLOR"}, Color.white) or tweak_data.chat_colors[color_id]) or WolfHUD:getColorSetting({"DamagePopup", critical and "CRITICAL_COLOR" or headshot and "HEADSHOT_COLOR" or "COLOR"}, "yellow") 91 | waypoint_color = waypoint_color:with_alpha(WolfHUD:getSetting({"DamagePopup", "ALPHA"}, 1)) 92 | local waypoint_duration = WolfHUD:getSetting({"DamagePopup", "DURATION"}, 3) 93 | if waypoint and not waypoint:is_deleted() then 94 | managers.waypoints:set_waypoint_duration(id, "duration", waypoint_duration) 95 | managers.waypoints:set_waypoint_label(id, "label", self:build_popup_text(damage, headshot)) 96 | managers.waypoints:set_waypoint_setting(id, "color", waypoint_color) 97 | managers.waypoints:set_waypoint_component_setting(id, "icon", "show", dead) 98 | else 99 | local params = { 100 | unit = self._unit, 101 | offset = Vector3(10, 10, WolfHUD:getSetting({"DamagePopup", "HEIGHT"}, 20)), 102 | scale = 2 * WolfHUD:getSetting({"DamagePopup", "SCALE"}, 1), 103 | color = waypoint_color, 104 | visible_distance = { 105 | min = 30, 106 | max = 10000 107 | }, 108 | rescale_distance = { 109 | start_distance = 500, 110 | end_distance = 3000, 111 | final_scale = 0.5 112 | }, 113 | fade_duration = { 114 | start = 0.5, 115 | stop = 1, 116 | alpha = true, 117 | }, 118 | icon = { 119 | type = "icon", 120 | show = dead, 121 | scale = WolfHUD:getSetting({"DamagePopup", "SKULL_SCALE"}, 1.2), 122 | texture = "guis/textures/pd2/risklevel_blackscreen", 123 | texture_rect = {0, 0, 64, 64}, 124 | blend_mode = "normal", 125 | on_minimap = false 126 | }, 127 | label = { 128 | type = "label", 129 | show = true, 130 | text = self:build_popup_text(damage, headshot, true) 131 | }, 132 | duration = { 133 | type = "duration", 134 | show = false, 135 | initial_value = waypoint_duration, 136 | fade_duration = { 137 | start = 0, 138 | stop = 1, 139 | position = Vector3(0, 0, 30), 140 | }, 141 | }, 142 | component_order = { WolfHUD:getSetting({"DamagePopup", "SKULL_ALIGN"}, 1) == 1 and { "icon", "label" } or { "label", "icon" } , { "duration" } } 143 | } 144 | managers.waypoints:add_waypoint(id, "CustomWaypoint", params) 145 | end 146 | end 147 | end 148 | 149 | function CopDamage:build_popup_text(damage, headshot, is_new) 150 | self._dmg_value = (not is_new and self._dmg_value or 0) + (damage * 10) 151 | return math.floor(self._dmg_value) .. ((CopDamage.DMG_POPUP_SETTING == 3 and headshot) and "!" or "") 152 | end 153 | 154 | elseif RequiredScript == "lib/units/civilians/civiliandamage" then 155 | local _on_damage_received_original = CivilianDamage._on_damage_received 156 | function CivilianDamage:_on_damage_received(data, ...) 157 | CivilianDamage.super._process_popup_damage(self, data) 158 | return _on_damage_received_original(self, data, ...) 159 | end 160 | end 161 | -------------------------------------------------------------------------------- /lua/DownCounter.lua: -------------------------------------------------------------------------------- 1 | --TODO: Add bots? Add player support for standalone version? 2 | 3 | if string.lower(RequiredScript) == "lib/units/beings/player/huskplayermovement" then 4 | 5 | local _perform_movement_action_enter_bleedout_original = HuskPlayerMovement._perform_movement_action_enter_bleedout 6 | 7 | function HuskPlayerMovement:_perform_movement_action_enter_bleedout(...) 8 | if not self._bleedout then 9 | local crim_data = managers.criminals:character_data_by_unit(self._unit) 10 | if crim_data and crim_data.panel_id then 11 | managers.hud:increment_teammate_downs(crim_data.panel_id) 12 | end 13 | end 14 | 15 | return _perform_movement_action_enter_bleedout_original(self, ...) 16 | end 17 | elseif string.lower(RequiredScript) == "lib/network/handlers/unitnetworkhandler" then 18 | 19 | local sync_doctor_bag_taken_original = UnitNetworkHandler.sync_doctor_bag_taken 20 | 21 | function UnitNetworkHandler:sync_doctor_bag_taken(unit, amount, sender, ...) 22 | local peer = self._verify_sender(sender) 23 | if peer then 24 | local crim_data = managers.criminals:character_data_by_peer_id(peer:id()) 25 | if crim_data and crim_data.panel_id then 26 | managers.hud:reset_teammate_downs(crim_data.panel_id) 27 | end 28 | end 29 | 30 | return sync_doctor_bag_taken_original(self, unit, amount, sender, ...) 31 | end 32 | elseif string.lower(RequiredScript) == "lib/managers/hudmanagerpd2" then 33 | 34 | HUDManager.DOWNS_COUNTER_PLUGIN = true 35 | 36 | local set_player_health_original = HUDManager.set_player_health 37 | local set_mugshot_custody_original = HUDManager.set_mugshot_custody 38 | 39 | function HUDManager:set_player_health(data, ...) 40 | if data.revives then 41 | self:set_player_revives(HUDManager.PLAYER_PANEL, data.revives - 1) 42 | end 43 | return set_player_health_original(self, data, ...) 44 | end 45 | 46 | function HUDManager:set_mugshot_custody(id, ...) 47 | local data = self:_get_mugshot_data(id) 48 | if data then 49 | local i = managers.criminals:character_data_by_name(data.character_name_id).panel_id 50 | managers.hud:reset_teammate_downs(i) 51 | end 52 | 53 | return set_mugshot_custody_original(self, id, ...) 54 | end 55 | 56 | HUDManager.set_player_revives = HUDManager.set_player_revives or function(self, i, value) 57 | self._teammate_panels[i]:set_revives(value) 58 | end 59 | 60 | HUDManager.increment_teammate_downs = HUDManager.increment_teammate_downs or function(self, i) 61 | self._teammate_panels[i]:increment_downs() 62 | end 63 | 64 | HUDManager.reset_teammate_downs = HUDManager.reset_teammate_downs or function(self, i) 65 | self._teammate_panels[i]:reset_downs() 66 | end 67 | 68 | elseif string.lower(RequiredScript) == "lib/managers/hud/hudteammate" and not HUDManager.CUSTOM_TEAMMATE_PANELS then 69 | 70 | Hooks:PostHook( HUDTeammate, "init", "WolfHUD_DownCounter_HUDTeammate_init", function(self, ...) 71 | self._health_panel = self._health_panel or self._player_panel:child("radial_health_panel") 72 | self._condition_icon = self._condition_icon or self._panel:child("condition_icon") 73 | 74 | self._max_downs = (Global.game_settings.one_down and 2 or tweak_data.player.damage.LIVES_INIT) - 1 75 | if managers.modifiers and managers.modifiers.modify_value then 76 | self._max_downs = managers.modifiers:modify_value("PlayerDamage:GetMaximumLives", self._max_downs) 77 | end 78 | self._max_downs = self._max_downs + (self._main_player and managers.player:upgrade_value("player", "additional_lives", 0) or 0) 79 | self._downs = self._main_player and self._max_downs or 0 80 | 81 | self._setting_prefix = self._main_player and "PLAYER" or "TEAMMATE" 82 | 83 | self._health_panel:bitmap({ 84 | name = "risk_indicator_bg", 85 | texture = "guis/textures/pd2/crimenet_marker_glow", 86 | texture_rect = { 0, 0, 64, 64 }, 87 | blend_mode = "normal", 88 | color = Color.black, 89 | alpha = 0.6, 90 | w = self._health_panel:w(), 91 | h = self._health_panel:h(), 92 | layer = 1, 93 | }) 94 | 95 | self._downs_counter = self._health_panel:text({ 96 | name = "downs", 97 | text = tostring(self._downs), 98 | color = Color.white, 99 | align = "center", 100 | vertical = "center", 101 | w = self._health_panel:w(), 102 | h = self._health_panel:h(), 103 | font_size = self._main_player and 15 or 12, 104 | font = tweak_data.menu.pd2_medium_font, 105 | layer = 2, 106 | visible = HUDManager.DOWNS_COUNTER_PLUGIN and WolfHUD:getSetting({"CustomHUD", self._setting_prefix, "DOWNCOUNTER"}, true) and not self._ai or false, 107 | }) 108 | 109 | self._detection_counter = self._health_panel:text({ 110 | name = "detection", 111 | text = utf8.char(57363), 112 | color = Color.red, 113 | align = "center", 114 | vertical = "center", 115 | w = self._health_panel:w(), 116 | h = self._health_panel:h(), 117 | font_size = self._main_player and 15 or 12, 118 | font = tweak_data.menu.pd2_medium_font, 119 | layer = 2, 120 | visible = HUDManager.DOWNS_COUNTER_PLUGIN and WolfHUD:getSetting({"CustomHUD", self._setting_prefix, "DOWNCOUNTER"}, true) and not self._ai or false, 121 | }) 122 | 123 | self:set_detection() 124 | 125 | if managers.gameinfo then 126 | managers.gameinfo:register_listener("HealthRadial_whisper_mode_listener" .. tostring(self._id), "whisper_mode", "change", callback(self, self, "_whisper_mode_change"), nil, true) 127 | end 128 | end) 129 | 130 | Hooks:PostHook( HUDTeammate, "remove_panel", "WolfHUD_DownCounter_HUDTeammate_remove_panel", function(self, ...) 131 | managers.gameinfo:unregister_listener("HealthRadial_whisper_mode_listener" .. tostring(self._id), "whisper_mode", "change") 132 | end) 133 | 134 | Hooks:PostHook( HUDTeammate, "set_peer_id", "WolfHUD_DownCounter_HUDTeammate_set_peer_id", function(self, ...) 135 | self:set_detection() 136 | end) 137 | 138 | Hooks:PostHook( HUDTeammate, "set_callsign", "WolfHUD_DownCounter_HUDTeammate_set_callsign", function(self, ...) 139 | if self._main_player then 140 | self:set_detection() 141 | end 142 | end) 143 | 144 | Hooks:PreHook( HUDTeammate, "set_name", "WolfHUD_DownCounter_HUDTeammate_set_name", function(self, teammate_name, ...) 145 | if teammate_name ~= self._name then 146 | self._name = teammate_name 147 | self:reset_downs() 148 | end 149 | end) 150 | 151 | function HUDTeammate:_whisper_mode_change(status) 152 | local disabled = self._condition_icon and self._condition_icon:visible() or not (HUDManager.DOWNS_COUNTER_PLUGIN and WolfHUD:getSetting({"CustomHUD", self._setting_prefix, "DOWNCOUNTER"}, true)) or self._ai 153 | self._downs_counter:set_visible(not disabled and (not status or self:down_amount() > 0)) 154 | self._detection_counter:set_visible(not disabled and not self._downs_counter:visible()) 155 | end 156 | 157 | HUDTeammate.set_downs = HUDTeammate.set_downs or function(self, amount) 158 | if amount and self._downs ~= amount then 159 | self._downs = amount 160 | self._downs_counter:set_text(tostring(self._downs)) 161 | local progress = math.clamp(self:down_amount() / self._max_downs, 0, 1) 162 | self._downs_counter:set_color(math.lerp(Color.white, Color(1, 1, 0.2, 0), progress)) 163 | local disabled = self._condition_icon and self._condition_icon:visible() or not (HUDManager.DOWNS_COUNTER_PLUGIN and WolfHUD:getSetting({"CustomHUD", self._setting_prefix, "DOWNCOUNTER"}, true)) or self._ai 164 | self._downs_counter:set_visible(not disabled and (not managers.groupai:state():whisper_mode() or self:down_amount() > 0)) 165 | self._detection_counter:set_visible(not disabled and not self._downs_counter:visible()) 166 | end 167 | end 168 | 169 | HUDTeammate.set_revives = HUDTeammate.set_revives or function(self, value) 170 | self:set_downs(value) 171 | end 172 | 173 | HUDTeammate.increment_downs = HUDTeammate.increment_downs or function(self) 174 | self:set_downs(self._downs + 1) 175 | end 176 | 177 | HUDTeammate.reset_downs = HUDTeammate.reset_downs or function(self) 178 | self:set_downs((self._main_player and self._max_downs) or 0) 179 | end 180 | 181 | HUDTeammate.down_amount = HUDTeammate.down_amount or function(self) 182 | return self._main_player and self._max_downs - self._downs or self._downs 183 | end 184 | 185 | HUDTeammate.set_detection = HUDTeammate.set_detection or function(self, risk) 186 | if not risk then 187 | if self._main_player then 188 | risk = tonumber(string.format("%.0f", managers.blackmarket:get_suspicion_offset_of_local(tweak_data.player.SUSPICION_OFFSET_LERP or 0.75) * 100)) 189 | elseif self:peer_id() then 190 | risk = tonumber(string.format("%.0f", managers.blackmarket:get_suspicion_offset_of_peer(managers.network:session():peer(self:peer_id()), tweak_data.player.SUSPICION_OFFSET_LERP or 0.75) * 100)) 191 | end 192 | end 193 | if not self._risk or risk and risk ~= self._risk then 194 | self._risk = risk 195 | if self._risk then 196 | local color = self._risk < 50 and Color(1, 0, 0.8, 1) or Color(1, 1, 0.2, 0) 197 | self._detection_counter:set_text(utf8.char(57363) .. tostring(self._risk)) 198 | self._detection_counter:set_color(color) 199 | end 200 | local disabled = self._condition_icon and self._condition_icon:visible() or not (HUDManager.DOWNS_COUNTER_PLUGIN and WolfHUD:getSetting({"CustomHUD", self._setting_prefix, "DOWNCOUNTER"}, true)) or self._ai 201 | self._downs_counter:set_visible(not disabled and (not managers.groupai:state():whisper_mode() or self:down_amount() > 0)) 202 | self._detection_counter:set_visible(not disabled and not self._downs_counter:visible()) 203 | end 204 | end 205 | end 206 | -------------------------------------------------------------------------------- /lua/EnemyHealthbar.lua: -------------------------------------------------------------------------------- 1 | if string.lower(RequiredScript) == "lib/managers/hudmanager" then 2 | 3 | Hooks:PostHook( HUDManager , "_player_hud_layout" , "WolfHUDPostHUDManagerPlayerInfoHUDLayout" , function( self ) 4 | self._health_text_rect = { 2 , 18 , 232 , 11 } --Green Bar 5 | self._shield_text_rect = { 2 , 34 , 232 , 11 } --Blue Bar 6 | self._bar_text_rect = self._health_text_rect 7 | self._shield = false 8 | 9 | local unit_health_main = managers.hud:script(PlayerBase.PLAYER_INFO_HUD_PD2).panel:panel({ 10 | name = "unit_health_main", 11 | halign = "grow", 12 | valign = "grow" 13 | }) 14 | 15 | self._unit_health_panel = unit_health_main:panel({ 16 | name = "unit_health_panel", 17 | visible = false 18 | }) 19 | 20 | self._unit_bar = self._unit_health_panel:bitmap({ 21 | name = "unit_health", 22 | texture = "guis/textures/pd2/healthshield", 23 | texture_rect = self._bar_text_rect, 24 | blend_mode = "normal" 25 | }) 26 | 27 | self._unit_bar_bg = self._unit_health_panel:bitmap({ 28 | name = "unit_shield", 29 | texture = "guis/textures/pd2/healthshield", 30 | texture_rect = { 1, 1, 234, 13 }, 31 | blend_mode = "normal" 32 | }) 33 | 34 | self._unit_health_text = self._unit_health_panel:text({ 35 | name = "unit_health_text", 36 | text = "250000/250000", 37 | blend_mode = "normal", 38 | alpha = 1, 39 | halign = "right", 40 | font = "fonts/font_medium_shadow_mf", 41 | font_size = 20, 42 | color = Color.white, 43 | align = "center", 44 | layer = 1 45 | }) 46 | 47 | self._unit_health_enemy_text = self._unit_health_panel:text({ 48 | name = "unit_health_enemy_text", 49 | text = "SWAT VAN TURRET", 50 | blend_mode = "normal", 51 | alpha = 1, 52 | halign = "left", 53 | font = "fonts/font_medium_mf", 54 | font_size = 22, 55 | color = Color.white, 56 | align = "center", 57 | layer = 1 58 | }) 59 | 60 | self._unit_health_enemy_location = self._unit_health_panel:text({ 61 | name = "unit_health_enemy_location", 62 | text = "^", 63 | blend_mode = "normal", 64 | visible = WolfHUD:getSetting({"EnemyHealthbar", "SHOW_POINTER"}, false), 65 | alpha = 0.75, 66 | halign = "center", 67 | font = "fonts/font_medium_shadow_mf", 68 | font_size = 20, 69 | color = Color.white, 70 | align = "center", 71 | layer = 1 72 | }) 73 | 74 | local _ ,_ ,hw ,hh = self._unit_health_text:text_rect() 75 | local _ ,_ ,ew ,eh = self._unit_health_enemy_text:text_rect() 76 | local _ ,_ ,lw ,lh = self._unit_health_enemy_location:text_rect() 77 | 78 | self._unit_health_text:set_size( hw , hh ) 79 | self._unit_health_enemy_text:set_size( ew , eh ) 80 | self._unit_health_enemy_location:set_size( lw , lh ) 81 | 82 | self._unit_bar:set_w( self._unit_bar:w() - 2 ) 83 | 84 | self._unit_bar:set_center( self._unit_health_panel:center_x() , self._unit_health_panel:center_y() - 190 ) 85 | self._unit_bar_bg:set_center( self._unit_health_panel:center_x() , self._unit_health_panel:center_y() - 190 ) 86 | 87 | self._unit_health_text:set_right( self._unit_bar_bg:right() ) 88 | self._unit_health_text:set_bottom( self._unit_bar_bg:top() ) 89 | 90 | self._unit_health_enemy_text:set_left( self._unit_bar_bg:left() ) 91 | self._unit_health_enemy_text:set_bottom( self._unit_bar_bg:top() ) 92 | 93 | self._unit_health_enemy_location:set_center_x( self._unit_bar_bg:center_x() ) 94 | self._unit_health_enemy_location:set_top( self._unit_bar_bg:bottom() ) 95 | 96 | end ) 97 | 98 | function HUDManager:set_unit_health_visible( visible, shield ) 99 | if visible and self._shield ~= shield then 100 | self._shield = shield or false 101 | self._bar_text_rect = self._shield and self._shield_text_rect or self._health_text_rect 102 | end 103 | 104 | if visible == true and not self._unit_health_visible and WolfHUD:getSetting({"EnemyHealthbar", "ENABLED"}, true) then 105 | 106 | self._unit_health_visible = true 107 | self._unit_health_enemy_location:set_visible(WolfHUD:getSetting({"EnemyHealthbar", "SHOW_POINTER"}, false)) 108 | self._unit_health_panel:stop() 109 | self._unit_health_panel:animate( function( p ) 110 | self._unit_health_panel:set_visible( true ) 111 | 112 | over( 0.25 , function( o ) 113 | self._unit_health_panel:set_alpha( math.lerp( self._unit_health_panel:alpha() , 1 , o ) ) 114 | end ) 115 | end ) 116 | 117 | elseif visible == false and self._unit_health_visible then 118 | 119 | self._unit_health_visible = nil 120 | self._unit_health_panel:stop() 121 | 122 | self._unit_health_panel:animate( function( p ) 123 | if self._unit_health_panel:alpha() >= 0.9 then 124 | over( 0.5 , function( o ) end ) 125 | end 126 | 127 | over( 1.5 , function( o ) 128 | self._unit_health_panel:set_alpha( math.lerp( self._unit_health_panel:alpha() , 0 , o ) ) 129 | end ) 130 | 131 | self._unit_health_panel:set_visible( false ) 132 | end ) 133 | end 134 | end 135 | 136 | function HUDManager:set_unit_health( current , total , tweak_table ) 137 | 138 | if not current or not total then return end 139 | 140 | local enemy = WolfHUD:getCharacterName(tweak_table, true) 141 | if enemy == tweak_table and managers.localization:exists(tweak_table) then 142 | enemy = managers.localization:to_upper_text(tweak_table) 143 | end 144 | 145 | total = math.min(total, 999999999) 146 | current = math.clamp(current, 0, total) 147 | local _r = current / total 148 | 149 | local r = self._unit_bar:width() 150 | local rn = ( self._unit_bar_bg:w() - 4 ) * _r 151 | 152 | self._unit_health_enemy_text:set_text( enemy ) 153 | if total > 0 then 154 | self._unit_health_text:set_text( string.format( "%s/%s" , managers.money:add_decimal_marks_to_string(tostring(current)) , managers.money:add_decimal_marks_to_string(tostring(total)) ) ) 155 | else 156 | self._unit_health_text:set_text( string.format( "%s" , managers.money:add_decimal_marks_to_string(tostring(current)) ) ) 157 | end 158 | 159 | local _ ,_ ,hw ,hh = self._unit_health_text:text_rect() 160 | local _ ,_ ,ew ,eh = self._unit_health_enemy_text:text_rect() 161 | 162 | self._unit_health_text:set_size( hw , hh ) 163 | self._unit_health_enemy_text:set_size( ew , eh ) 164 | 165 | self._unit_health_text:set_right( self._unit_bar_bg:right() ) 166 | self._unit_health_text:set_bottom( self._unit_bar_bg:top() ) 167 | self._unit_health_enemy_text:set_left( self._unit_bar_bg:left() ) 168 | self._unit_health_enemy_text:set_bottom( self._unit_bar_bg:top() ) 169 | 170 | self._unit_health_text:set_color( _r <= 0.1 and Color.red or _r <= 0.25 and Color.yellow or Color.white ) 171 | 172 | self._unit_bar:stop() 173 | 174 | self._bar_text_rect = self._shield and self._shield_text_rect or self._health_text_rect 175 | 176 | self._unit_bar:animate( function( p ) 177 | if rn < r then 178 | over( 0.2 , function( o ) 179 | self._unit_bar:set_w( math.lerp( r , rn , o ) ) 180 | self._unit_bar:set_texture_rect( self._bar_text_rect[1] , self._bar_text_rect[2] , math.lerp( r , rn , o ) , self._bar_text_rect[4] ) 181 | end ) 182 | end 183 | 184 | self._unit_bar:set_w( _r * ( self._bar_text_rect[3] - 2 ) ) 185 | self._unit_bar:set_texture_rect( self._bar_text_rect[1] , self._bar_text_rect[2] , self._bar_text_rect[3] * _r , self._bar_text_rect[4] ) 186 | end ) 187 | end 188 | 189 | function HUDManager:set_unit_health_rotation( angle ) 190 | self._unit_health_enemy_location:set_rotation( angle ) 191 | end 192 | 193 | elseif string.lower(RequiredScript) == "lib/units/beings/player/states/playerstandard" then 194 | Hooks:PostHook( PlayerStandard , "_update_fwd_ray" , "WolfHUDPostPlayerStandardUpdate" , function( self , t , dt ) 195 | if self._fwd_ray and self._fwd_ray.unit and type(self._fwd_ray.unit) == "userdata" then 196 | local unit = self._fwd_ray.unit 197 | if unit:in_slot( 8 ) and alive(unit:parent()) then -- Fix when aiming at shields shield. 198 | unit = unit:parent() 199 | end 200 | 201 | local visible, name, name_id, health, max_health, shield 202 | if alive( unit ) and unit:character_damage() then 203 | if unit:in_slot( 25 ) and not unit:character_damage():dead() and (table.contains(managers.groupai:state():turrets() or {}, unit) or WolfHUD:getSetting({"EnemyHealthbar", "SHOW_CIVILIAN"}, true) and Network:is_server()) then 204 | self._last_unit = nil 205 | visible = true 206 | name_id = unit:base() and unit:base():get_name_id() or "TURRET" 207 | if not unit:character_damage():needs_repair() then 208 | shield = true 209 | health = unit:character_damage()._shield_health * 10 or 0 210 | max_health = unit:character_damage()._SHIELD_HEALTH_INIT * 10 or 0 211 | else 212 | health = unit:character_damage()._health * 10 or 0 213 | max_health = unit:character_damage()._HEALTH_INIT * 10 or 0 214 | end 215 | elseif alive( unit ) and ( unit:in_slot( 12 ) or WolfHUD:getSetting({"EnemyHealthbar", "SHOW_CIVILIAN"}, false) and ( unit:in_slot( 21 ) or unit:in_slot( 22 ) ) or unit:in_slot( 16 ) and Network:is_server()) and not unit:character_damage():dead() then 216 | self._last_unit = unit 217 | visible = true 218 | health = unit:character_damage()._health * 10 or 0 219 | max_health = unit:character_damage()._HEALTH_INIT * 10 or 0 220 | name_id = unit:base() and unit:base()._tweak_table or "ENEMY" 221 | 222 | if name_id == "robbers_safehouse" and unit:interaction() then 223 | name_id = CriminalsManager.convert_new_to_old_character_workname(unit:interaction().character or name_id) 224 | end 225 | elseif alive( unit ) and unit:in_slot( 39 ) and WolfHUD:getSetting({"EnemyHealthbar", "SHOW_VEHICLE"}, true) and unit:vehicle_driving() and not self._seat then 226 | self._last_unit = nil 227 | visible = true 228 | health = unit:character_damage()._health or 0 229 | max_health = unit:character_damage()._current_max_health or 0 230 | name = unit:vehicle_driving()._tweak_data.name_id and managers.localization:text(unit:vehicle_driving()._tweak_data.name_id) or "VEHICLE" 231 | else 232 | visible = false 233 | end 234 | end 235 | 236 | if not visible and self._last_unit and alive( self._last_unit ) then 237 | health = self._last_unit:character_damage()._health * 10 or 0 238 | max_health = self._last_unit:character_damage()._HEALTH_INIT * 10 or 0 239 | name_id = self._last_unit:base() and self._last_unit:base()._tweak_table or "ENEMY" 240 | 241 | if name_id == "robbers_safehouse" and self._last_unit:interaction() then 242 | name_id = CriminalsManager.convert_new_to_old_character_workname(self._last_unit:interaction().character or name_id) 243 | end 244 | 245 | local angle = (self:getUnitRotation(self._last_unit) + 360) % 360 246 | if self._last_unit:character_damage():dead() or (angle < 350 and angle > 10) then 247 | visible = false 248 | self._last_unit = nil 249 | else 250 | visible = true 251 | end 252 | 253 | managers.hud:set_unit_health_rotation( 360 - angle ) 254 | else 255 | managers.hud:set_unit_health_rotation(0) 256 | end 257 | 258 | managers.hud:set_unit_health_visible( visible, shield ) 259 | if health and name_id then 260 | managers.hud:set_unit_health( math.floor(health or 0) , math.floor(max_health or 0) , name_id or string.upper(name or "UNKNOWN")) 261 | end 262 | else 263 | managers.hud:set_unit_health_visible( false ) 264 | end 265 | 266 | end ) 267 | 268 | function PlayerStandard:getUnitRotation( unit ) 269 | 270 | if not unit or not alive( unit ) then return 360 end 271 | 272 | local unit_position = unit:position() 273 | local vector = unit_position - self._camera_unit:position() 274 | local forward = self._camera_unit:rotation():y() 275 | local rotation = math.floor( vector:to_polar_with_reference( forward , math.UP ).spin ) 276 | 277 | return rotation 278 | 279 | end 280 | elseif string.lower(RequiredScript) == "lib/states/ingamearrested" then 281 | Hooks:PostHook( IngameArrestedState , "at_enter" , "WolfHUDPostIngameArrestedAtEnter" , function( self ) 282 | if managers.hud then 283 | managers.hud:set_unit_health_visible( false, false ) 284 | end 285 | end ) 286 | end 287 | -------------------------------------------------------------------------------- /lua/EnhancedObjective.lua: -------------------------------------------------------------------------------- 1 | if string.lower(RequiredScript) == "lib/managers/hud/hudobjectives" then 2 | 3 | HUDObjectives._TEXT_MARGIN = 8 4 | HUDObjectives._MAX_WIDTH = 300 5 | HUDObjectives._FONT_SIZE = tweak_data.hud.active_objective_title_font_size 6 | HUDObjectives._BOUNCE = 12 7 | 8 | function HUDObjectives:init(hud) 9 | if alive(self._panel) then 10 | hud.panel:remove(self._panel) 11 | end 12 | 13 | self._panel = hud.panel:panel({ 14 | visible = false, 15 | name = "objectives_panel", 16 | h = 130, 17 | w = 400, 18 | x = (WolfHUD:getSetting({"TabStats", "CLOCK_MODE"}, 3) == 4) and 0 or 80, 19 | valign = "top" 20 | }) 21 | 22 | self._bg_box = HUDBGBox_create(self._panel, { 23 | w = 400, 24 | h = 38, 25 | }) 26 | 27 | self._objective_text = self._bg_box:text({ 28 | name = "objective_text", 29 | visible = false, 30 | layer = 2, 31 | color = Color.white, 32 | text = "", 33 | font_size = HUDObjectives._FONT_SIZE, 34 | font = tweak_data.hud.medium_font_noshadow, 35 | align = "left", 36 | vertical = "center", 37 | w = self._bg_box:w(), 38 | x = HUDObjectives._TEXT_MARGIN, 39 | y = HUDObjectives._TEXT_MARGIN, 40 | wrap = false, 41 | word_wrap = false 42 | }) 43 | 44 | self._amount_text = self._bg_box:text({ 45 | name = "amount_text", 46 | visible = false, 47 | layer = 2, 48 | color = Color.white, 49 | text = "", 50 | font_size = HUDObjectives._FONT_SIZE, 51 | font = tweak_data.hud.medium_font_noshadow, 52 | align = "left", 53 | vertical = "center", 54 | w = self._bg_box:w(), 55 | h = HUDObjectives._FONT_SIZE, 56 | x = HUDObjectives._TEXT_MARGIN, 57 | y = HUDObjectives._TEXT_MARGIN 58 | }) 59 | 60 | self:apply_offset(0) 61 | end 62 | 63 | function HUDObjectives:activate_objective(data) 64 | self._active_objective_id = data.id 65 | self._panel:set_visible(true) 66 | self._objective_text:set_visible(true) 67 | self._amount_text:set_visible(false) 68 | 69 | local width, height, wrapped_text = self:_get_wrapped_text_dimensions(utf8.to_upper(data.text)) 70 | 71 | self._objective_text:set_text(wrapped_text) 72 | self._objective_text:set_w(width) 73 | self._objective_text:set_h(height) 74 | self._bg_box:set_h(HUDObjectives._TEXT_MARGIN * 2 + height) 75 | 76 | if data.amount then 77 | self:update_amount_objective(data, true) 78 | else 79 | self._amount_text:set_text("") 80 | self._bg_box:set_w(HUDObjectives._TEXT_MARGIN * 2 + width) 81 | end 82 | if not self._active_move then 83 | self._bg_box:stop() 84 | self._bg_box:animate(callback(self, self, "_animate_update_objective")) 85 | end 86 | 87 | self:apply_offset(self._offset_y) 88 | end 89 | 90 | function HUDObjectives:update_amount_objective(data, hide_animation) 91 | if data.id ~= self._active_objective_id then 92 | return 93 | end 94 | local amount = (data.current_amount or 0) 95 | self._amount_text:set_text(amount .. "/" .. data.amount) 96 | self._amount_text:set_left(self._objective_text:right() + HUDObjectives._TEXT_MARGIN) 97 | self._amount_text:set_bottom(self._objective_text:h() + HUDObjectives._TEXT_MARGIN) 98 | self._bg_box:set_w(HUDObjectives._TEXT_MARGIN * 3 + self._objective_text:w() + self:_get_text_dimensions(self._amount_text:text()).w) 99 | self._amount_text:set_visible(true) 100 | self._amount_text:stop() 101 | if not hide_animation and amount > 0 then 102 | self._amount_text:animate(callback(self, self, "_animate_new_amount")) 103 | else 104 | self._amount_text:set_color(Color(1, 1, 1, 1)) 105 | end 106 | end 107 | 108 | function HUDObjectives:remind_objective(id) 109 | if id ~= self._active_objective_id then 110 | return 111 | end 112 | if not self._active_move then 113 | self._bg_box:stop() 114 | self._bg_box:animate(callback(self, self, "_animate_update_objective")) 115 | end 116 | end 117 | 118 | function HUDObjectives:complete_objective(data) 119 | if data.id ~= self._active_objective_id then 120 | return 121 | end 122 | 123 | self._active_objective_id = "" 124 | self._amount_text:set_visible(false) 125 | self._objective_text:set_visible(false) 126 | self._panel:set_visible(false) 127 | self._bg_box:set_w(0) 128 | 129 | self:apply_offset(self._offset_y) 130 | end 131 | 132 | function HUDObjectives:_animate_new_amount(object) 133 | local TOTAL_T = 2 134 | local t = TOTAL_T 135 | object:set_color(Color(1, 1, 1, 1)) 136 | while t > 0 do 137 | local dt = coroutine.yield() 138 | t = t - dt 139 | object:set_color(Color(1, 1 , 1, 1 - (0.5 * math.sin(t * 360 * 2) + 0.5))) 140 | end 141 | object:set_color(Color(1, 1, 1, 1)) 142 | end 143 | 144 | function HUDObjectives:_animate_update_objective(object) 145 | local TOTAL_T = 2 146 | local t = TOTAL_T 147 | object:set_y(self._offset_y or 0) 148 | while t > 0 do 149 | local dt = coroutine.yield() 150 | t = t - dt 151 | object:set_y((self._offset_y or 0) + math.round((1 + math.sin((TOTAL_T - t) * 450 * 2)) * (HUDObjectives._BOUNCE * (t / TOTAL_T)))) 152 | end 153 | object:set_y(self._offset_y or 0) 154 | end 155 | 156 | function HUDObjectives:_get_text_dimensions(text_string) 157 | local string_width_measure_text_field = self._panel:child("string_dimensions") or self._panel:text({ 158 | name = "string_dimensions", 159 | visible = false, 160 | font_size = HUDObjectives._FONT_SIZE, 161 | font = tweak_data.hud.medium_font_noshadow, 162 | align = "left", 163 | vertical = "center", 164 | wrap = false 165 | }) 166 | string_width_measure_text_field:set_text(text_string) 167 | local x, y, w, h = string_width_measure_text_field:text_rect() 168 | return {x = x, y = y, w = w, h = h} 169 | end 170 | 171 | function HUDObjectives:_get_wrapped_text_dimensions(text_string) 172 | local layout_text_field = self._panel:child("layout") or self._panel:text({ 173 | name = "layout", 174 | width = self._MAX_WIDTH, 175 | visible = false, 176 | font_size = HUDObjectives._FONT_SIZE, 177 | font = tweak_data.hud.medium_font_noshadow, 178 | align = "left", 179 | vertical = "center", 180 | wrap = true, 181 | word_wrap = true 182 | }) 183 | layout_text_field:set_text(text_string) 184 | local line_breaks = table.collect(layout_text_field:line_breaks(), function(index) 185 | return index + 1 186 | end) 187 | local wrapped_lines = {} 188 | for line = 1, #line_breaks do 189 | local range_start = line_breaks[line] 190 | local range_end = line_breaks[line + 1] 191 | local string_range = utf8.sub(text_string, range_start, (range_end or 0) - 1) 192 | table.insert(wrapped_lines, string.trim(string_range)) 193 | end 194 | local wrapped_text = "" 195 | local w, h = 0, layout_text_field:font_size() * math.max(#wrapped_lines, 1) 196 | for _, line in ipairs(wrapped_lines) do 197 | w = math.max(w, self:_get_text_dimensions(line).w) 198 | wrapped_text = string.format("%s%s\n", wrapped_text, line) 199 | end 200 | return math.ceil(w), math.ceil(h), wrapped_text 201 | end 202 | 203 | function HUDObjectives:apply_offset(offset) 204 | if offset and offset ~= self._offset_y then 205 | self._offset_y = offset 206 | if alive(self._bg_box) then 207 | self._bg_box:stop() 208 | self._bg_box:animate(callback(self, self, "_animate_move"), self._bg_box:x(), self._offset_y, true) 209 | self._panel:animate(callback(self, self, "_animate_move"), (self._offset_y > 40 or WolfHUD:getSetting({"TabStats", "CLOCK_MODE"}, 3) == 4) and 0 or 80, self._panel:y(), true) 210 | end 211 | end 212 | if managers.hud and managers.hud.change_list_setting then 213 | managers.hud:change_list_setting("left_list_height_offset", self._offset_y + (self._bg_box:w() > 0 and (self._bg_box:h() + HUDObjectives._BOUNCE) or 40) + HUDObjectives._TEXT_MARGIN) 214 | end 215 | end 216 | 217 | function HUDObjectives:_animate_move(panel, x, y, instant) 218 | self._active_move = true 219 | if not instant then 220 | local move_speed = 150 221 | local init_x = panel:x() 222 | local init_y = panel:y() 223 | local x_change = x > init_x and 1 or x < init_x and -1 224 | local y_change = y > init_y and 1 or y < init_y and -1 225 | local T = math.max(math.abs(x - init_x) / move_speed, math.abs(y - init_y) / move_speed) 226 | local t = 0 227 | 228 | while alive(panel) and t < T do 229 | if x_change then 230 | panel:set_x(init_x + t * x_change * move_speed) 231 | end 232 | if y_change then 233 | panel:set_y(init_y + t * y_change * move_speed) 234 | end 235 | t = t + coroutine.yield() 236 | end 237 | end 238 | 239 | if alive(panel) then 240 | panel:set_x(x) 241 | panel:set_y(y) 242 | end 243 | self._active_move = nil 244 | end 245 | 246 | elseif string.lower(RequiredScript) == "lib/managers/hud/hudheisttimer" then 247 | 248 | function HUDHeistTimer:init(hud, tweak_hud) 249 | self._hud_panel = hud.panel 250 | self._enabled = (WolfHUD:getSetting({"TabStats", "CLOCK_MODE"}, 3) ~= 4) and not (tweak_hud and tweak_hud.no_timer) 251 | if self._hud_panel:child("heist_timer_panel") then 252 | self._hud_panel:remove(self._hud_panel:child("heist_timer_panel")) 253 | end 254 | 255 | self._heist_timer_panel = self._hud_panel:panel({ 256 | visible = self._enabled, 257 | name = "heist_timer_panel", 258 | h = 40, 259 | w = 80, 260 | valign = "top", 261 | layer = 0 262 | }) 263 | self._timer_text = self._heist_timer_panel:text({ 264 | name = "timer_text", 265 | text = "00:00:00", 266 | font_size = tweak_data.hud.medium_deafult_font_size, 267 | font = tweak_data.hud.medium_font_noshadow, 268 | color = Color.white, 269 | align = "center", 270 | vertical = "center", 271 | layer = 1, 272 | wrap = false, 273 | word_wrap = false 274 | }) 275 | 276 | self._last_time = 0 277 | end 278 | elseif string.lower(RequiredScript) == "core/lib/managers/subtitle/coresubtitlepresenter" then 279 | core:module("CoreSubtitlePresenter") 280 | local _on_resolution_changed_original = OverlayPresenter._on_resolution_changed 281 | function OverlayPresenter:_on_resolution_changed(...) 282 | _on_resolution_changed_original(self, ...) 283 | self:apply_bottom_offset() 284 | end 285 | 286 | function OverlayPresenter:set_bottom(offset) 287 | if self._bottom_off ~= offset then 288 | self._bottom_off = offset 289 | self:apply_bottom_offset() 290 | end 291 | end 292 | 293 | function OverlayPresenter:apply_bottom_offset() 294 | if self.__subtitle_panel and self._bottom_off then 295 | self.__subtitle_panel:set_height(self._bottom_off or self.__subtitle_panel:h()) 296 | local label = self.__subtitle_panel:child("label") 297 | if label then 298 | label:set_h(self.__subtitle_panel:h()) 299 | label:set_w(self.__subtitle_panel:w()) 300 | end 301 | local shadow = self.__subtitle_panel:child("shadow") 302 | if shadow then 303 | shadow:set_h(self.__subtitle_panel:h()) 304 | shadow:set_w(self.__subtitle_panel:w()) 305 | end 306 | end 307 | end 308 | end 309 | -------------------------------------------------------------------------------- /lua/EquipmentTweaks.lua: -------------------------------------------------------------------------------- 1 | if string.lower(RequiredScript) == "lib/units/weapons/sentrygunweapon" then 2 | local old_setup = SentryGunWeapon.init 3 | local old_destroy = SentryGunWeapon.destroy 4 | 5 | function SentryGunWeapon:init(...) 6 | old_setup(self, ...) 7 | if tweak_data.blackmarket.deployables[self._unit:base():get_type()] then 8 | managers.enemy:add_delayed_clbk("Sentry_post_init_" .. tostring(self._unit:key()), callback(self, self, "post_init"), Application:time() + 0.1) 9 | end 10 | end 11 | 12 | function SentryGunWeapon:post_init() 13 | local enable_ap = false 14 | local laser_theme = "team_sentry" 15 | if self._unit:base():is_owner() then 16 | laser_theme = "player_sentry" 17 | enable_ap = managers.player:has_category_upgrade("sentry_gun", "ap_bullets") 18 | end 19 | self._laser_align = self._unit:get_object(Idstring("fire")) 20 | self:set_laser_enabled(laser_theme) 21 | 22 | if WolfHUD:getSetting({"EQUIPMENT", "SENTRY_AUTO_AP"}, true) and enable_ap then 23 | if alive(self._fire_mode_unit) and alive(self._unit) then 24 | local firemode_interaction = self._fire_mode_unit:interaction() 25 | if firemode_interaction and firemode_interaction:can_interact(managers.player:player_unit()) then 26 | self:_set_fire_mode(true) 27 | self._unit:network():send("sentrygun_sync_armor_piercing", self._use_armor_piercing) 28 | self._unit:event_listener():call("on_switch_fire_mode", self._use_armor_piercing) 29 | end 30 | end 31 | end 32 | end 33 | 34 | function SentryGunWeapon:destroy(...) 35 | managers.enemy:remove_delayed_clbk("Sentry_post_init_" .. tostring(self._unit:key())) 36 | old_destroy(self, ...) 37 | end 38 | elseif string.lower(RequiredScript) == "lib/units/equipment/ecm_jammer/ecmjammerbase" then 39 | local setup_original = ECMJammerBase.setup 40 | local contour_interaction_original = ECMJammerBase.contour_interaction 41 | local destroy_original = ECMJammerBase.destroy 42 | function ECMJammerBase:setup(...) 43 | setup_original(self, ...) 44 | if WolfHUD:getSetting({"EQUIPMENT", "ECM_FEEDBACK_STEALTH_DISABLED"}, true) and managers.groupai:state():whisper_mode() then 45 | local owner_unit = self:owner() 46 | local player_unit = managers.player:player_unit() 47 | if alive(owner_unit) and alive(player_unit) and owner_unit:key() == player_unit:key() then 48 | managers.gameinfo:register_listener("ECMContour_whisper_mode_listener" .. tostring(self._unit:key()), "whisper_mode", "change", callback(self, self, "_whisper_mode_change")) 49 | end 50 | end 51 | end 52 | 53 | function ECMJammerBase:contour_interaction(...) 54 | if not (managers.groupai:state():whisper_mode() and WolfHUD:getSetting({"EQUIPMENT", "ECM_FEEDBACK_STEALTH_DISABLED"}, true)) then 55 | contour_interaction_original(self, ...) 56 | end 57 | end 58 | 59 | function ECMJammerBase:destroy(...) 60 | managers.gameinfo:unregister_listener("ECMContour_whisper_mode_listener" .. tostring(self._unit:key()), "whisper_mode", "change") 61 | destroy_original(self, ...) 62 | end 63 | 64 | function ECMJammerBase:_whisper_mode_change(event, key, status) 65 | if not status then 66 | contour_interaction_original(self) 67 | end 68 | end 69 | elseif string.lower(RequiredScript) == "lib/units/interactions/interactionext" then 70 | BaseInteractionExt.SHAPED_CHARGE_TIMEOUT = WolfHUD:getTweakEntry("STEALTH_SHAPED_CHARGE_TIMEOUT", "number", 0.25) --Timeout for 2 InteractKey pushes, to prevent accidents in stealth 71 | BaseInteractionExt.KEYCARD_DOORS_TIMEOUT = WolfHUD:getTweakEntry("KEYCARD_DOORS_TIMEOUT", "number", 0.25) --Timeout for 2 InteractKey pushes, to prevent accidents in hoxton breakout day 2 72 | 73 | local BaseInteraction_interact_start_original = BaseInteractionExt.interact_start 74 | local ECMJammerInteaction_can_interact_original = ECMJammerInteractionExt.can_interact 75 | local ECMJammerInteraction_can_select_original = ECMJammerInteractionExt.can_select 76 | 77 | function BaseInteractionExt:interact_start(player, data, ...) 78 | local t = Application:time() 79 | if WolfHUD:getSetting({"EQUIPMENT", "SHAPED_CHARGE_STEALTH_DISABLED"}, true) and managers.groupai:state():whisper_mode() 80 | and self._tweak_data.required_deployable and self._tweak_data.required_deployable == "trip_mine" 81 | and (t - (self._last_shaped_charge_t or 0) >= BaseInteractionExt.SHAPED_CHARGE_TIMEOUT) then 82 | self._last_shaped_charge_t = t 83 | return false 84 | end 85 | if WolfHUD:getSetting({"EQUIPMENT", "KEYCARD_DOORS_DISABLED"}, true) 86 | and self.tweak_data and self.tweak_data == "hold_close_keycard" 87 | and (t - (self._last_hold_close_keycard_t or 0) >= BaseInteractionExt.KEYCARD_DOORS_TIMEOUT) then 88 | self._last_hold_close_keycard_t = t 89 | return false 90 | end 91 | return BaseInteraction_interact_start_original(self, player, data, ...) 92 | end 93 | 94 | function ECMJammerInteractionExt:can_interact(...) 95 | if WolfHUD:getSetting({"EQUIPMENT", "ECM_FEEDBACK_STEALTH_DISABLED"}, true) and managers.groupai:state():whisper_mode() then 96 | return false 97 | end 98 | return ECMJammerInteaction_can_interact_original(self, ...) 99 | end 100 | 101 | function ECMJammerInteractionExt:can_select(...) 102 | if WolfHUD:getSetting({"EQUIPMENT", "ECM_FEEDBACK_STEALTH_DISABLED"}, true) and managers.groupai:state():whisper_mode() then 103 | return false 104 | end 105 | return ECMJammerInteraction_can_select_original(self, ...) 106 | end 107 | end -------------------------------------------------------------------------------- /lua/KillCounter.lua: -------------------------------------------------------------------------------- 1 | if RequiredScript == "lib/units/enemies/cop/copdamage" and not CopDamage._kill_counter_loaded then 2 | CopDamage._kill_counter_loaded = true 3 | 4 | --This needs fixing for DoT kills (then again, so does the games own kill counter) as client somehow and a lot of testing 5 | 6 | --[[ 7 | local chk_killshot_original = CopDamage.chk_killshot 8 | 9 | function CopDamage:chk_killshot(attacker_unit, variant) 10 | --printf("chk_killshot: %s\n", tostring(attacker_unit and attacker_unit:slot())) 11 | 12 | if alive(attacker_unit) then 13 | local source = "direct/unknown" 14 | local killer = attacker_unit 15 | 16 | if attacker_unit:in_slot(14) then 17 | if attacker_unit:base().thrower_unit then 18 | source = "throwable" 19 | killer = attacker_unit:base():thrower_unit() 20 | end 21 | elseif attacker_unit:in_slot(25) then 22 | if attacker_unit:base().sentry_gun then 23 | local owner = attacker_unit:base()._owner_id 24 | if owner then 25 | source = "sentry" 26 | killer = managers.criminals:character_unit_by_peer_id(owner) 27 | end 28 | end 29 | end 30 | 31 | if killer then 32 | if killer:in_slot(3) then 33 | --printf("Teammate kill (%s)\n", source) 34 | local crim_data = managers.criminals:character_data_by_unit(killer) 35 | if crim_data and crim_data.panel_id then 36 | managers.hud:increment_teammate_kill_count(crim_data.panel_id, managers.groupai:state():is_enemy_special(self._unit)) 37 | end 38 | elseif killer:in_slot(2) then 39 | --printf("Player kill (%s)\n", source) 40 | managers.hud:increment_teammate_kill_count(HUDManager.PLAYER_PANEL, managers.groupai:state():is_enemy_special(self._unit)) 41 | elseif killer:in_slot(16) then 42 | printf("Bot/joker kill (%s)\n", source) 43 | local crim_data = managers.criminals:character_data_by_unit(killer) 44 | if crim_data and crim_data.panel_id then 45 | managers.hud:increment_teammate_kill_count(crim_data.panel_id, managers.groupai:state():is_enemy_special(self._unit)) 46 | end 47 | elseif killer:in_slot(12) then 48 | --printf("Enemy kill (%s)\n", source) 49 | else 50 | printf("UNKNOWN KILL (%d / %s)\n", killer:slot(), source) 51 | end 52 | else 53 | printf("UNKNOWN KILL (no killer, attacker unit: %d)\n", attacker_unit:slot()) 54 | end 55 | end 56 | 57 | return chk_killshot_original(self, attacker_unit, variant) 58 | end 59 | ]] 60 | 61 | local _on_damage_received_original = CopDamage._on_damage_received 62 | --Workaround for Teammate Headshots, since col_ray doesn't get forwarded... (self._sync_ibody_killcount) 63 | local sync_damage_bullet_original = CopDamage.sync_damage_bullet 64 | local sync_damage_melee_original = CopDamage.sync_damage_melee 65 | 66 | function CopDamage:_process_kill(data) 67 | local killer 68 | 69 | local attacker = alive(data.attacker_unit) and data.attacker_unit 70 | 71 | if attacker then 72 | if attacker:in_slot(3) or attacker:in_slot(5) then 73 | --Human team mate 74 | killer = attacker 75 | elseif attacker:in_slot(2) then 76 | --Player 77 | killer = attacker 78 | elseif attacker:in_slot(16) then 79 | --Bot/joker 80 | killer = attacker 81 | elseif attacker:in_slot(12) then 82 | --Enemy 83 | elseif attacker:in_slot(25) then 84 | --Turret 85 | local owner = attacker:base():get_owner_id() 86 | if owner then 87 | killer = managers.criminals:character_unit_by_peer_id(owner) 88 | end 89 | elseif attacker:base().thrower_unit then 90 | killer = attacker:base():thrower_unit() 91 | end 92 | 93 | if alive(killer) and alive(self._unit) then 94 | local tweak_id = self._unit:base()._tweak_table 95 | local special_unit_ids = managers.statistics and managers.statistics.special_unit_ids or {} 96 | local is_special = managers.groupai:state():is_enemy_special(self._unit) or table.contains(special_unit_ids, tweak_id) 97 | local body = data.col_ray and data.col_ray.body or self._sync_ibody_killcount and self._unit:body(self._sync_ibody_killcount) 98 | local headshot = body and self.is_head and self:is_head(body) or false 99 | 100 | if killer:in_slot(2) then 101 | managers.hud:increment_teammate_kill_count(HUDManager.PLAYER_PANEL, is_special, headshot) 102 | 103 | local current_player_state = managers.player and managers.player:get_current_state() 104 | local weapon_base = current_player_state and current_player_state._equipped_unit:base() 105 | local projectile_name = "bullet" 106 | if weapon_base._projectile_type_index then 107 | projectile_name = tweak_data and tweak_data:get_raw_value("blackmarket", "projectiles", "_projectiles_index", weapon_base._projectile_type_index) 108 | end 109 | if projectile_name == (data.variant or "") then 110 | local weapon_id = weapon_base:get_name_id() 111 | local weapon_tweak = weapon_base and weapon_base:weapon_tweak_data() 112 | local weapon_type = weapon_tweak.category 113 | local slot = weapon_tweak and weapon_tweak.use_data and weapon_tweak.use_data.selection_index 114 | managers.hud:increment_teammate_kill_count_detailed(HUDManager.PLAYER_PANEL, self._unit, weapon_id, weapon_type, slot) 115 | end 116 | else 117 | local crim_data = managers.criminals:character_data_by_unit(killer) 118 | if crim_data and crim_data.panel_id then 119 | managers.hud:increment_teammate_kill_count(crim_data.panel_id, is_special, headshot) 120 | end 121 | end 122 | end 123 | end 124 | end 125 | 126 | function CopDamage:_on_damage_received(data, ...) 127 | if self._dead then 128 | self:_process_kill(data) 129 | end 130 | self._sync_ibody_killcount = nil 131 | return _on_damage_received_original(self, data, ...) 132 | end 133 | 134 | function CopDamage:sync_damage_bullet(attacker_unit, damage_percent, i_body, ...) 135 | if i_body then 136 | self._sync_ibody_killcount = i_body 137 | end 138 | 139 | return sync_damage_bullet_original(self, attacker_unit, damage_percent, i_body, ...) 140 | end 141 | 142 | function CopDamage:sync_damage_melee(attacker_unit, damage_percent, damage_effect_percent, i_body, ...) 143 | if i_body then 144 | self._sync_ibody_killcount = i_body 145 | end 146 | 147 | return sync_damage_melee_original(self, attacker_unit, damage_percent, damage_effect_percent, i_body, ...) 148 | 149 | end 150 | 151 | --TODO: Add sync damage checks for non-local bots and players 152 | 153 | elseif RequiredScript == "lib/units/equipment/sentry_gun/sentrygunbase" then 154 | 155 | local sync_setup_original = SentryGunBase.sync_setup 156 | 157 | function SentryGunBase:sync_setup(upgrade_lvl, peer_id, ...) 158 | sync_setup_original(self, upgrade_lvl, peer_id, ...) 159 | self._owner_id = self._owner_id or peer_id 160 | end 161 | 162 | elseif RequiredScript == "lib/managers/statisticsmanager" then 163 | 164 | local shot_fired_original = StatisticsManager.shot_fired 165 | 166 | function StatisticsManager:shot_fired(data, ...) 167 | shot_fired_original(self, data, ...) 168 | 169 | --[[ 170 | This does not work well for HE rounds. It would be almost correct if you halved number of shots, 171 | but would not take into account shots that goes into the void or compensate for direct hits 172 | ]] 173 | 174 | local name_id = data.name_id or data.weapon_unit:base():get_name_id() 175 | local weapon_tweak = tweak_data.weapon[name_id] 176 | local slot = weapon_tweak and weapon_tweak.use_data and weapon_tweak.use_data.selection_index 177 | if slot then --Exclude throwables like exploding cards mod... 178 | local weapon_data = name_id and self._global.session.shots_by_weapon[name_id] 179 | local weapon_accuracy = 0 180 | if weapon_data and weapon_data.total > 0 then 181 | weapon_accuracy = math.floor(100 * weapon_data.hits / weapon_data.total) 182 | end 183 | managers.hud:set_teammate_weapon_accuracy(HUDManager.PLAYER_PANEL, slot, weapon_accuracy) 184 | end 185 | 186 | managers.hud:set_teammate_accuracy(HUDManager.PLAYER_PANEL, self:session_hit_accuracy()) 187 | end 188 | 189 | elseif RequiredScript == "lib/managers/hudmanagerpd2" then 190 | 191 | HUDManager.KILL_COUNTER_PLUGIN = true 192 | HUDManager.ACCURACY_PLUGIN = true 193 | 194 | HUDManager.increment_teammate_kill_count = HUDManager.increment_teammate_kill_count or function (self, i, is_special, headshot) 195 | self._teammate_panels[i]:increment_kill_count(is_special, headshot) 196 | end 197 | 198 | HUDManager.reset_teammate_kill_count = HUDManager.reset_teammate_kill_count or function(self, i) 199 | self._teammate_panels[i]:reset_kill_count() 200 | end 201 | 202 | HUDManager.increment_teammate_kill_count_detailed = HUDManager.increment_teammate_kill_count_detailed or function(self, i, unit, weapon_id, weapon_type, weapon_slot) 203 | --TODO: Add call for default HUD | No need for that, really... 204 | end 205 | 206 | HUDManager.set_teammate_accuracy = HUDManager.set_teammate_accuracy or function(self, i, value) 207 | self._teammate_panels[i]:set_accuracy(value) 208 | end 209 | 210 | HUDManager.set_teammate_weapon_accuracy = HUDManager.set_teammate_weapon_accuracy or function(self, i, slot, value) 211 | --TODO 212 | end 213 | 214 | function HUDManager:teampanels_height() 215 | return (WolfHUD:getSetting({"CustomHUD", "PLAYER", "SHOW_ACCURACY"}, true) and not WolfHUD:getSetting({"CustomHUD", "PLAYER", "KILLCOUNTER", "HIDE"}, false)) and 140 or 120 216 | end 217 | 218 | elseif string.lower(RequiredScript) == "lib/managers/hud/hudteammate" then 219 | 220 | if not HUDManager.CUSTOM_TEAMMATE_PANELS then --Custom HUD compatibility 221 | local init_original = HUDTeammate.init 222 | local set_name_original = HUDTeammate.set_name 223 | local set_state_original = HUDTeammate.set_state 224 | 225 | function HUDTeammate:init(...) 226 | init_original(self, ...) 227 | self._setting_prefix = self._main_player and "PLAYER" or "TEAMMATE" 228 | self:_init_killcount() 229 | self:init_accuracy() 230 | end 231 | 232 | function HUDTeammate:_init_killcount() 233 | self._kills_panel = self._panel:panel({ 234 | name = "kills_panel", 235 | visible = not WolfHUD:getSetting({"CustomHUD", self._setting_prefix, "KILLCOUNTER", "HIDE"}, false), 236 | w = 150, 237 | h = 20, 238 | x = 0, 239 | halign = "right" 240 | }) 241 | 242 | local player_panel = self._panel:child("player") 243 | local name_label = self._panel:child("name") 244 | self._kills_panel:set_rightbottom(player_panel:right(), (self._main_player or WolfHUD:getSetting({"CustomHUD", "TEAMMATE", "INTERACTION", "TEXT"}, true)) and name_label:bottom() or name_label:top()) 245 | local killcount_color = WolfHUD:getColorSetting({"CustomHUD", self._setting_prefix, "KILLCOUNTER", "COLOR"}, "yellow") 246 | 247 | self._kill_icon = self._kills_panel:bitmap({ 248 | texture = "guis/textures/pd2/cn_miniskull", 249 | w = self._kills_panel:h() * 0.75, 250 | h = self._kills_panel:h(), 251 | texture_rect = { 0, 0, 12, 16 }, 252 | alpha = 1, 253 | blend_mode = "normal", 254 | layer = 0, 255 | color = killcount_color 256 | }) 257 | 258 | self._kills_text = self._kills_panel:text({ 259 | name = "kills_text", 260 | text = "-", 261 | layer = 1, 262 | color = killcount_color, 263 | w = self._kills_panel:w() - self._kill_icon:w(), 264 | h = self._kills_panel:h(), 265 | vertical = "center", 266 | align = "right", 267 | font_size = self._kills_panel:h(), 268 | font = tweak_data.hud_players.name_font 269 | }) 270 | self._kills_text:set_right(self._kills_panel:w()) 271 | 272 | self:reset_kill_count() 273 | end 274 | 275 | function HUDTeammate:init_accuracy() 276 | if not self._main_player then return end 277 | self._accuracy_panel = self._panel:panel({ 278 | name = "accuracy_panel", 279 | visible = WolfHUD:getSetting({"CustomHUD", "PLAYER", "SHOW_ACCURACY"}, true), 280 | w = 100, 281 | h = 20, 282 | x = 0, 283 | halign = "right" 284 | }) 285 | 286 | local player_panel = self._panel:child("player") 287 | local name_label = self._panel:child("name") 288 | self._accuracy_panel:set_rightbottom(player_panel:right(), self._kills_panel and self._kills_panel:visible() and self._kills_panel:top() or name_label:bottom()) 289 | 290 | self._accuracy_icon = self._accuracy_panel:bitmap({ 291 | texture = "guis/textures/pd2/pd2_waypoints", 292 | w = self._accuracy_panel:h() * 0.75, 293 | h = self._accuracy_panel:h(), 294 | texture_rect = { 96, 0, 32, 32 }, 295 | alpha = 1, 296 | blend_mode = "normal", 297 | layer = 0, 298 | color = Color.white 299 | }) 300 | 301 | self._accuracy_text = self._accuracy_panel:text({ 302 | name = "accuracy_text", 303 | text = "0%", 304 | layer = 1, 305 | color = Color.white, 306 | w = self._accuracy_panel:w(), 307 | h = self._accuracy_panel:h(), 308 | vertical = "center", 309 | align = "right", 310 | font_size = self._accuracy_panel:h(), 311 | font = tweak_data.hud_players.name_font 312 | }) 313 | self:set_accuracy(0) 314 | end 315 | 316 | function HUDTeammate:increment_kill_count(is_special, headshot) 317 | self._kill_count = self._kill_count + 1 318 | self._kill_count_special = self._kill_count_special + (is_special and 1 or 0) 319 | self._headshot_kills = self._headshot_kills + (headshot and 1 or 0) 320 | self:_update_kill_count_text() 321 | end 322 | 323 | function HUDTeammate:_update_kill_count_text() 324 | local kill_string = tostring(self._kill_count) 325 | if WolfHUD:getSetting({"CustomHUD", self._setting_prefix, "KILLCOUNTER", "SHOW_SPECIAL_KILLS"}, true) then 326 | kill_string = kill_string .. "/" .. tostring(self._kill_count_special) 327 | end 328 | if WolfHUD:getSetting({"CustomHUD", self._setting_prefix, "KILLCOUNTER", "SHOW_HEADSHOT_KILLS"}, true) then 329 | kill_string = kill_string .. " (" .. tostring(self._headshot_kills) .. ")" 330 | end 331 | self._kills_text:set_text(kill_string) 332 | local _, _, w, _ = self._kills_text:text_rect() 333 | self._kill_icon:set_right(self._kills_panel:w() - w - self._kill_icon:w() * 0.15) 334 | 335 | if (self._main_player or WolfHUD:getSetting({"CustomHUD", "TEAMMATE", "INTERACTION", "TEXT"}, true)) and not WolfHUD:getSetting({"CustomHUD", self._setting_prefix, "KILLCOUNTER", "HIDE"}, false) then 336 | self._max_name_panel_width = (self._kills_panel:x() + self._kill_icon:x() - 4) 337 | self:_truncate_name() 338 | end 339 | 340 | local color = WolfHUD:getColorSetting({"CustomHUD", self._setting_prefix, "KILLCOUNTER", "COLOR"}, "yellow") 341 | self._kill_icon:set_color(color) 342 | self._kills_text:set_color(color) 343 | end 344 | 345 | function HUDTeammate:reset_kill_count() 346 | self._kill_count = 0 347 | self._kill_count_special = 0 348 | self._headshot_kills = 0 349 | self:_update_kill_count_text() 350 | end 351 | 352 | function HUDTeammate:set_name(teammate_name, ...) 353 | if teammate_name ~= self._name then 354 | self._name = teammate_name 355 | self:reset_kill_count() 356 | end 357 | 358 | return set_name_original(self, teammate_name, ...) 359 | end 360 | 361 | function HUDTeammate:set_state(...) 362 | set_state_original(self, ...) 363 | 364 | local visible = not WolfHUD:getSetting({"CustomHUD", self._setting_prefix, "KILLCOUNTER", "HIDE"}, false) and (not self._ai or WolfHUD:getSetting({"CustomHUD", "TEAMMATE", "KILLCOUNTER", "SHOW_BOT_KILLS"}, true)) 365 | self._kills_panel:set_visible(visible) 366 | 367 | if self._ai then 368 | self._kills_panel:set_bottom(self._panel:child("player"):bottom()) 369 | else 370 | local name_label = self._panel:child("name") 371 | self._kills_panel:set_bottom((self._main_player or WolfHUD:getSetting({"CustomHUD", "TEAMMATE", "INTERACTION", "TEXT"}, true)) and name_label:bottom() or name_label:top()) 372 | end 373 | end 374 | 375 | function HUDTeammate:set_accuracy(value) 376 | self._accuracy_text:set_text(tostring(value) .. "%") 377 | local _, _, w, _ = self._accuracy_text:text_rect() 378 | self._accuracy_icon:set_right(self._accuracy_panel:w() - w - self._accuracy_icon:w() * 0.15) 379 | if WolfHUD:getSetting({"CustomHUD", "PLAYER", "KILLCOUNTER", "HIDE"}, false) and WolfHUD:getSetting({"CustomHUD", "PLAYER", "SHOW_ACCURACY"}, true) then 380 | self._max_name_panel_width = (self._accuracy_panel:x() + self._accuracy_icon:x() - 4) 381 | self:_truncate_name() 382 | end 383 | end 384 | 385 | HUDTeammate._truncate_name = HUDTeammate._truncate_name or function() end 386 | end 387 | end 388 | -------------------------------------------------------------------------------- /lua/NetworkHandler.lua: -------------------------------------------------------------------------------- 1 | --do return end -- Disabled cause: WiP 2 | if WolfHUD and not WolfHUD.Sync then 3 | WolfHUD.Sync = { 4 | msg_id = "WolfHUD_Sync", 5 | peers = { false, false, false, false }, 6 | events = { 7 | discover_wolfhud = "Using_WolfHUD?", 8 | confirm_wolfhud = "Using_WolfHUD!", 9 | peer_disconnect = "Leaving_Game", 10 | locked_assault_status = "locked_assault_status", 11 | }, 12 | } 13 | 14 | local Net = _G.LuaNetworking 15 | 16 | function WolfHUD.Sync.table_to_string(tbl) 17 | return Net:TableToString(tbl) or "" 18 | end 19 | 20 | function WolfHUD.Sync.string_to_table(str) 21 | local tbl = Net:StringToTable(str) or "" 22 | 23 | for k, v in pairs(tbl) do 24 | tbl[k] = self.to_original_type(v) 25 | end 26 | 27 | return tbl 28 | end 29 | 30 | function WolfHUD.Sync.to_original_type(s) 31 | local v = s 32 | if type(s) == "string" then 33 | if s == "nil" then 34 | v = nil 35 | elseif s == "true" or s == "false" then 36 | v = (s == "true") 37 | else 38 | v = tonumber(s) or s 39 | end 40 | end 41 | return v 42 | end 43 | 44 | function WolfHUD.Sync:send_to_peer(peer_id, messageType, data) 45 | if peer_id and peer_id ~= Net:LocalPeerID() and messageType then 46 | local tags = { 47 | id = self.msg_id, 48 | event = messageType 49 | } 50 | 51 | if type(data) == "table" then 52 | data = self.table_to_string(data) 53 | tags["table"] = true 54 | end 55 | 56 | Net:SendToPeer(peer_id, self.table_to_string(tags), data or "") 57 | end 58 | end 59 | 60 | function WolfHUD.Sync:send_to_host(messageType, data) 61 | self:send_to_peer(managers.network:session():server_peer():id(), messageType, data) 62 | end 63 | 64 | function WolfHUD.Sync:send_to_all_peers( messageType, data) 65 | for peer_id, enabled in ipairs(self.peers) do 66 | self:send_to_peer(peer_id, messageType, data) 67 | end 68 | end 69 | 70 | function WolfHUD.Sync:send_to_all_discovered_peers( messageType, data) 71 | for peer_id, enabled in ipairs(self.peers) do 72 | if enabled then 73 | self:send_to_peer(peer_id, messageType, data) 74 | end 75 | end 76 | end 77 | 78 | function WolfHUD.Sync:send_to_all_undiscovered_peers( messageType, data) 79 | for peer_id, enabled in ipairs(self.peers) do 80 | if not enabled then 81 | self:send_to_peer(peer_id, messageType, data) 82 | end 83 | end 84 | end 85 | 86 | function WolfHUD.Sync:receive_message(peer_id, event, data) 87 | if peer_id and event then 88 | local events = WolfHUD.Sync.events 89 | 90 | if event == events.discover_wolfhud then 91 | WolfHUD.Sync:send_to_peer(peer_id, events.confirm_wolfhud) 92 | WolfHUD.Sync.peers[peer_id] = true 93 | managers.chat:feed_system_message(ChatManager.GAME, "Client " .. tostring(peer_id) .. " is using WolfHUD ;)") --TEST 94 | elseif event == events.confirm_wolfhud then 95 | WolfHUD.Sync.peers[peer_id] = true 96 | managers.chat:feed_system_message(ChatManager.GAME, "The Host is using WolfHUD ;)") --TEST 97 | elseif event == events.peer_disconnect then 98 | WolfHUD.Sync.peers[peer_id] = false 99 | elseif event == events.locked_assault_status then 100 | managers.hud:_locked_assault(data) 101 | end 102 | end 103 | end 104 | 105 | -- Manage Networking and list of peers to sync to... 106 | Hooks:Add("NetworkReceivedData", "NetworkReceivedData_WolfHUDSync", function(sender, messageType, data) 107 | sender = tonumber(sender) 108 | if sender then 109 | local tags = WolfHUD.Sync.string_to_table(messageType) 110 | if WolfHUD.Sync and tags.id and tags.id == WolfHUD.Sync.msg_id and not string.is_nil_or_empty(tags.event) then 111 | if tags.table then 112 | data = WolfHUD.Sync.string_to_table(data) 113 | else 114 | data = WolfHUD.Sync.to_original_type(data) 115 | end 116 | 117 | WolfHUD.Sync:receive_message(sender, tags.event, data) 118 | end 119 | end 120 | end) 121 | 122 | Hooks:Add("BaseNetworkSessionOnPeerRemoved", "BaseNetworkSessionOnPeerRemoved_WolfHUDSync", function(self, peer, peer_id, reason) 123 | WolfHUD.Sync:receive_message(peer_id, WolfHUD.Sync.events.peer_disconnect, "") 124 | end) 125 | 126 | Hooks:Add("BaseNetworkSessionOnLoadComplete", "BaseNetworkSessionOnLoadComplete_WolfHUDSync", function(local_peer, id) 127 | if WolfHUD.Sync and Net:IsMultiplayer() and Network:is_client() then 128 | WolfHUD.Sync:send_to_host(WolfHUD.Sync.events.discover_wolfhud) 129 | end 130 | end) 131 | 132 | 133 | -- Data Sync functions: 134 | 135 | function WolfHUD.Sync:endless_assault_status(status) 136 | if Network:is_server() then 137 | self:send_to_all_discovered_peers(WolfHUD.Sync.events.locked_assault_status, tostring(status)) 138 | end 139 | end 140 | end -------------------------------------------------------------------------------- /lua/NumbericSuspicion.lua: -------------------------------------------------------------------------------- 1 | local hudsuspicion_init_original = HUDSuspicion.init 2 | local hudsuspicions_animate_eye_original = HUDSuspicion.animate_eye 3 | local hudsuspicion_hide_original = HUDSuspicion.hide 4 | 5 | function HUDSuspicion:init(...) 6 | hudsuspicion_init_original(self, ...) 7 | self._scale = 1 8 | self._suspicion_text_panel = self._suspicion_panel:panel({ 9 | name = "suspicion_text_panel", 10 | visible = true, 11 | x = 0, 12 | y = 0, 13 | h = self._suspicion_panel:h(), 14 | w = self._suspicion_panel:w(), 15 | layer = 1 16 | }) 17 | 18 | self._suspicion_text = OutlinedText:new(self._suspicion_text_panel, { 19 | name = "suspicion_text", 20 | visible = true, 21 | text = "", 22 | valign = "center", 23 | align = "center", 24 | layer = 2, 25 | color = Color.white, 26 | font = tweak_data.menu.pd2_large_font, 27 | font_size = 28, 28 | h = 64 29 | }) 30 | self._suspicion_text:set_y((math.round(self._suspicion_text_panel:h() / 4))) 31 | end 32 | 33 | function HUDSuspicion:_is_detected() 34 | local detected_text = self._suspicion_panel and self._suspicion_panel:child("suspicion_detected") 35 | return self._discovered or self._suspicion_value and self._suspicion_value >= 1 or detected_text and detected_text:visible() and detected_text:alpha() > 0 36 | end 37 | 38 | function HUDSuspicion:set_detection(text_item, detection) 39 | detection = math.clamp(detection, 0, 1) 40 | local color = math.lerp(Color(0, 0.71, 1), Color(0.99, 0.08, 0), detection) 41 | text_item:set_color(color) 42 | text_item:set_text(string.format("%d%%", detection*100)) 43 | end 44 | 45 | function HUDSuspicion:_animate_text(suspicion_panel, suspicion_text) 46 | while true do 47 | local detection = self:_is_detected() and 1 or self._suspicion_value or 0 48 | self:set_detection(suspicion_text, detection) 49 | coroutine.yield() 50 | end 51 | end 52 | 53 | function HUDSuspicion:animate_eye(...) 54 | local was_animating = self._eye_animation and true or false 55 | hudsuspicions_animate_eye_original(self, ...) 56 | 57 | if not was_animating and self._eye_animation then 58 | self:rescale() 59 | 60 | local visible = WolfHUD:getSetting({"HUDSuspicion", "SHOW_BARS"}, true) 61 | self._suspicion_panel:child("suspicion_left"):set_visible(visible) 62 | self._suspicion_panel:child("suspicion_right"):set_visible(visible) 63 | self._misc_panel:child("hud_stealthmeter_bg"):set_visible(visible) 64 | self._misc_panel:child("hud_stealth_eye"):set_visible(visible) 65 | self._misc_panel:child("hud_stealth_exclam"):set_visible(visible) 66 | 67 | if WolfHUD:getSetting({"HUDSuspicion", "SHOW_PERCENTAGE"}, true) and not self._text_animation then 68 | self._suspicion_text:set_outlines_visible(WolfHUD:getSetting({"HUDSuspicion", "SHOW_PERCENTAGE_OUTLINE"}, true)) 69 | self._text_animation = self._suspicion_text_panel:animate(callback(self, self, "_animate_text"), self._suspicion_text) 70 | end 71 | end 72 | end 73 | 74 | function HUDSuspicion:hide(...) 75 | if self._text_animation then 76 | self._suspicion_text_panel:stop() 77 | self._text_animation = nil 78 | end 79 | 80 | if self._suspicion_panel then 81 | self._suspicion_panel:set_visible(false) 82 | end 83 | 84 | return hudsuspicion_hide_original(self, ...) 85 | end 86 | 87 | function HUDSuspicion:rescale() 88 | local scale = WolfHUD:getSetting({"HUDSuspicion", "SCALE"}, 0.8) 89 | if self._scale ~= scale then 90 | local suspicion_left = self._suspicion_panel:child("suspicion_left") 91 | local suspicion_right = self._suspicion_panel:child("suspicion_right") 92 | local hud_stealthmeter_bg = self._misc_panel:child("hud_stealthmeter_bg") 93 | local suspicion_detected = self._suspicion_panel:child("suspicion_detected") 94 | local hud_stealth_eye = self._misc_panel:child("hud_stealth_eye") 95 | local hud_stealth_exclam = self._misc_panel:child("hud_stealth_exclam") 96 | suspicion_left:set_size((suspicion_left:w() / self._scale) * scale, (suspicion_left:h() / self._scale) * scale) 97 | suspicion_right:set_size((suspicion_right:w() / self._scale) * scale, (suspicion_right:h() / self._scale) * scale) 98 | hud_stealthmeter_bg:set_size((hud_stealthmeter_bg:w() / self._scale) * scale, (hud_stealthmeter_bg:h() / self._scale) * scale) 99 | suspicion_detected:set_font_size((suspicion_detected:font_size() / self._scale) * scale) 100 | local fontSize = (self._suspicion_text:font_size() / self._scale) * scale 101 | self._suspicion_text:set_font_size(fontSize) 102 | hud_stealth_eye:set_size((hud_stealth_eye:w() / self._scale) * scale, (hud_stealth_eye:h() / self._scale) * scale) 103 | hud_stealth_exclam:set_size((hud_stealth_exclam:w() / self._scale) * scale, (hud_stealth_exclam:h() / self._scale) * scale) 104 | suspicion_left:set_center_x(self._suspicion_panel:w() / 2) 105 | suspicion_left:set_center_y(self._suspicion_panel:h() / 2) 106 | suspicion_right:set_center(suspicion_left:center()) 107 | hud_stealthmeter_bg:set_center(suspicion_left:center()) 108 | hud_stealth_eye:set_center(suspicion_left:center_x(), suspicion_left:bottom() - 4) 109 | hud_stealth_exclam:set_center(suspicion_left:center_x(), suspicion_left:top() - 4) 110 | self._suspicion_text:set_y(suspicion_left:top() + (suspicion_left:center_y() - suspicion_left:top()) / 2 - self._suspicion_text:font_size() / 2) 111 | self._scale = scale 112 | end 113 | end 114 | -------------------------------------------------------------------------------- /lua/PacifiedCivs.lua: -------------------------------------------------------------------------------- 1 | local _upd_criminal_suspicion_progress_original = GroupAIStateBase._upd_criminal_suspicion_progress 2 | function GroupAIStateBase:_upd_criminal_suspicion_progress(...) 3 | if self._ai_enabled then 4 | for obs_key, obs_susp_data in pairs(self._suspicion_hud_data or {}) do 5 | local unit = obs_susp_data.u_observer 6 | if managers.enemy:is_civilian(unit) then 7 | local waypoint_id = "susp1" .. tostring(obs_key) 8 | local waypoint = managers.hud and managers.hud._hud.waypoints[waypoint_id] 9 | if waypoint then 10 | local color, arrow_color 11 | if unit:anim_data().drop and WolfHUD:getSetting({"HUDSuspicion", "SHOW_PACIFIED_CIVILIANS"}, true) then 12 | if not obs_susp_data._subdued_civ then 13 | obs_susp_data._alerted_civ = nil 14 | obs_susp_data._subdued_civ = true 15 | color = Color(0, 0.71, 1) 16 | arrow_color = Color(0, 0.35, 0.5) 17 | waypoint.bitmap:set_image("guis/textures/menu_singletick") 18 | end 19 | elseif obs_susp_data.alerted then 20 | if not obs_susp_data._alerted_civ then 21 | obs_susp_data._subdued_civ = nil 22 | obs_susp_data._alerted_civ = true 23 | color = Color.white 24 | arrow_color = tweak_data.hud.detected_color 25 | waypoint.bitmap:set_image("guis/textures/hud_icons") 26 | waypoint.bitmap:set_texture_rect(479, 433, 32, 32) 27 | end 28 | end 29 | if color and arrow_color then 30 | waypoint.bitmap:set_color(color) 31 | waypoint.arrow:set_color(arrow_color:with_alpha(0.75)) 32 | end 33 | end 34 | end 35 | end 36 | end 37 | return _upd_criminal_suspicion_progress_original(self, ...) 38 | end -------------------------------------------------------------------------------- /lua/ProfileMenu.lua: -------------------------------------------------------------------------------- 1 | local PROFILE_MENU_ID = "wolfhud_profile_switch_node_menu" 2 | 3 | if RequiredScript == "lib/managers/menumanager" then 4 | local function create_profile_menu_node(nodes, menu_id) 5 | local arugements = { 6 | gui_class = "MenuNodeProfileSwitchGui", 7 | help_id = "menu_skill_switch_help", 8 | menu_components = nodes.kit and "mission_briefing" or managers.menu:is_pc_controller() and "inventory" or "", 9 | modifier = "ProfileSwitchInitiator", 10 | name = PROFILE_MENU_ID, 11 | no_item_parent = false, 12 | no_menu_wrapper = true, 13 | refresh = "ProfileSwitchInitiator", 14 | scene_state = "inventory", --"standard", 15 | sync_state = "inventory", 16 | topic_id = "menu_inventory", 17 | --back_callback = "profile_menu_back", 18 | { 19 | ["_meta"] = "legend", 20 | ["name"] = "menu_legend_select" 21 | }, 22 | { 23 | ["_meta"] = "legend", 24 | ["name"] = "menu_legend_back" 25 | }, 26 | } 27 | 28 | local node_class = CoreSerialize.string_to_classtable("MenuNodeTable") 29 | if node_class then 30 | nodes[menu_id] = node_class:new(arugements) 31 | 32 | local callback_handler = CoreSerialize.string_to_classtable("MenuCallbackHandler") 33 | if callback_handler then 34 | nodes[menu_id]:set_callback_handler(callback_handler:new()) 35 | end 36 | end 37 | end 38 | 39 | Hooks:Add("MenuManagerBuildCustomMenus", "WolfHUD_MenuManager_BuildProfileMenu", function( menu_manager, nodes ) 40 | if nodes.main and not nodes[PROFILE_MENU_ID]then 41 | create_profile_menu_node(nodes, PROFILE_MENU_ID) 42 | end 43 | end) 44 | 45 | local LobbyOptionInitiator_modify_node_orig = LobbyOptionInitiator.modify_node 46 | function LobbyOptionInitiator:modify_node(node, ...) 47 | local active_menu = managers.menu:active_menu() 48 | local briefing_nodes = active_menu and active_menu.logic and active_menu.logic._data._nodes 49 | if briefing_nodes and briefing_nodes.kit and not briefing_nodes[PROFILE_MENU_ID] then 50 | create_profile_menu_node(briefing_nodes, PROFILE_MENU_ID, true) 51 | end 52 | return LobbyOptionInitiator_modify_node_orig(self, node, ...) 53 | end 54 | 55 | ProfileSwitchInitiator = ProfileSwitchInitiator or class(SkillSwitchInitiator) 56 | function ProfileSwitchInitiator:modify_node(node, data) 57 | node:clean_items() 58 | self:create_divider(node, "title", "wolfhud_profile_switch_title_profiles", nil, tweak_data.screen_colors.text) 59 | local mpm = managers.multi_profile 60 | for i = 1, mpm:profile_count() do 61 | local profile = mpm:profile(i) 62 | if profile then 63 | local hightlight_color, row_item_color, callback 64 | if (i == mpm:current_profile_id()) then 65 | hightlight_color = tweak_data.screen_colors.text 66 | row_item_color = tweak_data.screen_colors.text 67 | callback = "menu_back" 68 | else 69 | hightlight_color = tweak_data.screen_colors.button_stage_2 70 | row_item_color = tweak_data.screen_colors.button_stage_3 71 | callback = "profile_menu_switch" 72 | end 73 | 74 | self:create_item(node, { 75 | name = i, 76 | text_id = profile.name or string.format("Profile %d", i), 77 | enabled = true, 78 | localize = false, 79 | callback = callback, 80 | hightlight_color = hightlight_color, 81 | row_item_color = row_item_color, 82 | disabled_color = row_item_color, 83 | }) 84 | end 85 | end 86 | self:create_divider(node, "back_div") 87 | self:add_back_button(node) 88 | node:set_default_item_name(1) 89 | return node 90 | end 91 | 92 | function MenuCallbackHandler:profile_menu_switch(item) 93 | local mpm = managers.multi_profile 94 | local profile_id = item:parameters().name 95 | if mpm and mpm:is_valid_id(profile_id) then 96 | mpm:set_current_profile(profile_id) 97 | end 98 | 99 | self:refresh_node() 100 | end 101 | elseif RequiredScript == "lib/managers/menu/renderers/menunodeskillswitchgui" then 102 | MenuNodeProfileSwitchGui = MenuNodeProfileSwitchGui or class(MenuNodeSkillSwitchGui) 103 | MenuNodeProfileSwitchGui.SKILL_POINTS_X = 0.24 104 | MenuNodeProfileSwitchGui.STATUS_X = 0.52 105 | MenuNodeProfileSwitchGui.PROFILE_PREVIEW_W = 195 106 | 107 | function MenuNodeProfileSwitchGui:_setup_item_panel(...) 108 | MenuNodeProfileSwitchGui.super._setup_item_panel(self, ...) 109 | 110 | if alive(self.title_text) then 111 | self.title_text:set_text(managers.localization:to_upper_text("wolfhud_profile_switch_title")) 112 | end 113 | 114 | local ws_panel = self.item_panel:parent() 115 | if alive(ws_panel) then 116 | ws_panel:bitmap({ 117 | texture = "guis/textures/test_blur_df", 118 | w = ws_panel:w(), 119 | h = ws_panel:h(), 120 | render_template = "VertexColorTexturedBlur3D", 121 | halign = "scale", 122 | valign = "scale", 123 | layer = 50, 124 | alpha = 1 125 | }) 126 | end 127 | 128 | if alive(self.item_panel) and LoadoutPanel then 129 | local offset_h = managers.menu:is_pc_controller() and 25 or 0 130 | self.profile_preview = LoadoutPanel:new(self.item_panel:parent(), self, 0, self.PROFILE_PREVIEW_W, math.floor(self.item_panel:h()) - offset_h, { 131 | component_layout = { 132 | { "skills" }, 133 | { "perk" }, 134 | { "primary", "secondary" }, 135 | { "grenade", "armor" }, 136 | { "deployable", "secondary_deployable" } 137 | }, 138 | default = { alpha = 0.9 }, 139 | margin = 5, 140 | borders = { 1, 1, 1, 1 }, 141 | layer = 53, 142 | }) 143 | local outfit = managers.multi_profile:get_profile_outfit(managers.multi_profile:current_profile_id()) 144 | self.profile_preview:set_outfit(outfit) 145 | end 146 | end 147 | 148 | function MenuNodeProfileSwitchGui:_create_menu_item(row_item, ...) 149 | MenuNodeProfileSwitchGui.super._create_menu_item(self, row_item, ...) 150 | if row_item.type ~= "divider" and row_item.name ~= "back" then 151 | local mpm = managers.multi_profile 152 | local profile_id = row_item.name 153 | local profile = mpm:profile(profile_id) 154 | local skill_name, perk_name = "", "" 155 | 156 | skill_name = profile.skillset and managers.skilltree:get_skill_switch_name(profile.skillset, false) or managers.localization:to_upper_text("menu_st_locked_skill_switch") 157 | if profile.perk_deck then 158 | local data = tweak_data.skilltree.specializations[tonumber(profile.perk_deck)] 159 | local name_id = data and data.name_id 160 | perk_name = managers.localization:to_upper_text(name_id) 161 | end 162 | 163 | if alive(row_item.skill_points_gui) then 164 | row_item.skill_points_gui:set_text(utf8.to_upper(skill_name)) 165 | row_item.skill_points_gui:set_alpha(1) 166 | end 167 | if alive(row_item.status_gui) then 168 | row_item.status_gui:set_text(perk_name) 169 | end 170 | row_item.distribution_after_text = false 171 | elseif row_item.type == "divider" and row_item.name == "divider_title" then 172 | 173 | if alive(row_item.skill_points_gui) then 174 | row_item.skill_points_gui:set_text(string.format("%s-%s", managers.localization:to_upper_text("menu_st_skilltree", {}), managers.localization:to_upper_text("menu_st_skill_switch_title_name", {}))) 175 | row_item.skill_points_gui:show() 176 | end 177 | if alive(row_item.status_gui) then 178 | row_item.status_gui:set_text(managers.localization:to_upper_text("menu_specialization", {}) .. ":") 179 | end 180 | end 181 | end 182 | 183 | function MenuNodeProfileSwitchGui:arrange_loadout_panels() 184 | if self.profile_preview then 185 | self.profile_preview:set_top(self.item_panel:top()) 186 | self.profile_preview:set_right(self.item_panel:right()) 187 | end 188 | end 189 | 190 | function MenuNodeProfileSwitchGui:_highlight_row_item(row_item, mouse_over, ...) 191 | MenuNodeProfileSwitchGui.super._highlight_row_item(self, row_item, mouse_over, ...) 192 | 193 | local mpm = managers.multi_profile 194 | local profile_id = row_item.name and mpm:is_valid_id(row_item.name) and row_item.name or mpm:current_profile_id() 195 | self:change_outfit_preview(profile_id) 196 | end 197 | 198 | function MenuNodeProfileSwitchGui:_align_marker(row_item, ...) 199 | MenuNodeProfileSwitchGui.super._align_marker(self, row_item, ...) 200 | 201 | if self.profile_preview then 202 | if row_item.name ~= "back" then 203 | self._marker_data.marker:set_w(self.item_panel:w() - self.profile_preview:w() - 10) 204 | else 205 | self._marker_data.marker:set_w(self.profile_preview:w()) 206 | self._marker_data.marker:set_right(self.item_panel:w()) 207 | end 208 | end 209 | end 210 | 211 | function MenuNodeProfileSwitchGui:_clear_gui(...) 212 | if self.profile_preview then 213 | self.profile_preview:destroy() 214 | end 215 | 216 | MenuNodeProfileSwitchGui.super._clear_gui(self, ...) 217 | end 218 | 219 | function MenuNodeProfileSwitchGui:mouse_moved(o, x, y, ...) 220 | local used, icon = MenuNodeProfileSwitchGui.super.mouse_moved(self, o, x, y, ...) 221 | return true, icon 222 | end 223 | 224 | function MenuNodeProfileSwitchGui:change_outfit_preview(profile_id) 225 | local mpm = managers.multi_profile 226 | if self.profile_preview and mpm:is_valid_id(profile_id) and (self._previewing_outfit or 0) ~= profile_id then 227 | local outfit = mpm:get_profile_outfit(profile_id) 228 | self.profile_preview:set_outfit(outfit) 229 | self._previewing_outfit = profile_id 230 | end 231 | end 232 | elseif RequiredScript == "lib/managers/multiprofilemanager" then 233 | local open_quick_select_original = MultiProfileManager.open_quick_select 234 | function MultiProfileManager:open_quick_select(...) 235 | if WolfHUD:getSetting({"CrewLoadout", "REPLACE_PROFILE_MENU"}, true) then 236 | managers.menu:open_node(PROFILE_MENU_ID, {}) 237 | else 238 | open_quick_select_original(self, ...) 239 | end 240 | end 241 | 242 | 243 | function MultiProfileManager:current_profile_id() 244 | return self._global._current_profile or 1 245 | end 246 | 247 | function MultiProfileManager:is_valid_id(profile_id) 248 | return profile_id and type(profile_id) == "number" and profile_id > 0 and profile_id <= self:profile_count() and true or false 249 | end 250 | 251 | function MultiProfileManager:get_profile_outfit(profile_id) 252 | if profile_id ~= self:current_profile_id() then 253 | local profile = self:profile(profile_id) 254 | local gd = Global.skilltree_manager.skill_switches[profile.skillset] 255 | local skills = {} 256 | if gd.trees then 257 | local pts = 0 258 | for i=1, #gd.trees do 259 | pts=Application:digest_value(gd.trees[i].points_spent, false) 260 | table.insert(skills, pts) 261 | end 262 | end 263 | local outfit = { 264 | skills = { 265 | skills = skills, 266 | specializations = { profile.perk_deck, 9 }, 267 | }, 268 | primary = self:get_item_data("primaries", profile.primary), 269 | secondary = self:get_item_data("secondaries", profile.secondary), 270 | melee_weapon = profile.melee, 271 | grenade = profile.throwable, 272 | armor = profile.armor, 273 | armor_skin = profile.armor_skin, 274 | mask = self:get_item_data("mask", profile.mask), 275 | player_style = profile.player_style or managers.blackmarket:get_default_player_style(), 276 | suit_variations = (profile.suit_variations or {}), 277 | deployable = profile.deployable, 278 | secondary_deployable = profile.deployable_secondary, 279 | deployable_amount = self:get_deployable_amount(profile.deployable, gd.skills), 280 | secondary_deployable_amount = self:get_deployable_amount(profile.deployable_secondary, gd.skills), 281 | } 282 | return outfit 283 | else 284 | local outfit = managers.blackmarket:unpack_outfit_from_string(managers.blackmarket:outfit_string()) 285 | return outfit 286 | end 287 | end 288 | 289 | function MultiProfileManager:get_item_data(category, slot) 290 | if not Global.blackmarket_manager.crafted_items[category] then 291 | return nil 292 | end 293 | if not Global.blackmarket_manager.crafted_items[category][slot] then 294 | slot = 1 295 | end 296 | local item_data = Global.blackmarket_manager.crafted_items[category][slot] 297 | return item_data or {} 298 | end 299 | 300 | function MultiProfileManager:get_deployable_amount(deployable, skills) 301 | local amount = 0 302 | if deployable then 303 | if deployable == "sentry_gun_silent" then 304 | deployable = "sentry_gun" 305 | end 306 | amount = tweak_data.equipments[deployable] and tweak_data.equipments[deployable].quantity[1] or 0 307 | local upgrade_def, upgrade_values, skill_tweak = tweak_data.upgrades.definitions, tweak_data.upgrades.values, tweak_data.skilltree.skills 308 | if skills and upgrade_values[deployable] and upgrade_values[deployable]["quantity"] then 309 | local value_index = 0 310 | for skill, data in pairs(skills) do 311 | local skill_data = skill_tweak[skill] 312 | for i = 1, data.unlocked do 313 | local upgrade_ids = skill_data and skill_data[i].upgrades 314 | for _, upgrade_id in ipairs(upgrade_ids) do 315 | local u_data = upgrade_def[upgrade_id] and upgrade_def[upgrade_id].upgrade 316 | if u_data and u_data.category == deployable and u_data.upgrade == "quantity" then 317 | if upgrade_def[upgrade_id].incremental then 318 | value_index = value_index + (u_data.value or 0) 319 | else 320 | value_index = u_data.value 321 | end 322 | end 323 | end 324 | end 325 | end 326 | if value_index > 0 then 327 | amount = amount + (upgrade_values[deployable]["quantity"][math.floor(value_index)] or 0) 328 | end 329 | end 330 | end 331 | return amount 332 | end 333 | elseif RequiredScript == "lib/managers/menu/multiprofileitemgui" then 334 | local init_orig = MultiProfileItemGui.init 335 | 336 | function MultiProfileItemGui:init(...) 337 | init_orig(self, ...) 338 | 339 | self._max_length = WolfHUD:getTweakEntry("MAX_PROFILE_NAME_LENGTH", "number", 20) 340 | end 341 | elseif RequiredScript == "lib/managers/menu/missionbriefinggui" then 342 | local special_btn_pressed_orig = MissionBriefingGui.special_btn_pressed 343 | local input_focus_orig = MissionBriefingGui.input_focus 344 | local reload_loadout_orig = MissionBriefingGui.reload_loadout 345 | function MissionBriefingGui:special_btn_pressed(button, ...) 346 | if self._enabled and button == Idstring("menu_change_profile_right") and managers.menu:get_controller():get_input_bool("menu_change_profile_left") then 347 | managers.menu:open_node(PROFILE_MENU_ID, {}) 348 | return 349 | end 350 | 351 | return special_btn_pressed_orig(self, button, ...) 352 | end 353 | 354 | function MissionBriefingGui:input_focus(...) 355 | local focus = input_focus_orig(self, ...) 356 | if focus then 357 | local active_menu = managers.menu:active_menu() 358 | local selected_node = active_menu and active_menu.logic:selected_node() 359 | if selected_node and selected_node:parameters().name == PROFILE_MENU_ID then 360 | focus = false 361 | end 362 | end 363 | return focus 364 | end 365 | 366 | JukeboxItemNew = JukeboxItemNew or class(JukeboxItem) 367 | function JukeboxItemNew:select(...) 368 | local active_menu = managers.menu:active_menu() 369 | local selected_node = active_menu and active_menu.logic:selected_node() 370 | if not selected_node or selected_node:parameters().name == "kit" then 371 | JukeboxItemNew.super.select(self, ...) 372 | else 373 | self.displayed = true 374 | JukeboxItemNew.super.super.select(self, ...) 375 | end 376 | end 377 | function JukeboxItemNew:deselect(...) 378 | local active_menu = managers.menu:active_menu() 379 | local selected_node = active_menu and active_menu.logic:selected_node() 380 | if not selected_node or selected_node:parameters().name == "jukebox" then 381 | JukeboxItemNew.super.deselect(self, ...) 382 | else 383 | self.closing = true 384 | self.displayed = nil 385 | JukeboxItemNew.super.super.deselect(self, ...) 386 | end 387 | end 388 | 389 | if CoreClass then 390 | CoreClass.override_class(JukeboxItem, JukeboxItemNew) 391 | end 392 | elseif RequiredScript == "lib/managers/menu/playerinventorygui" then 393 | local special_btn_pressed_orig = PlayerInventoryGui.special_btn_pressed 394 | function PlayerInventoryGui:special_btn_pressed(button, ...) 395 | if button == Idstring("menu_change_profile_right") and managers.menu:get_controller():get_input_bool("menu_change_profile_left") then 396 | managers.menu:open_node(PROFILE_MENU_ID, {}) 397 | return 398 | end 399 | 400 | return special_btn_pressed_orig(self, button, ...) 401 | end 402 | end 403 | -------------------------------------------------------------------------------- /lua/Scripts.lua: -------------------------------------------------------------------------------- 1 | if string.lower(RequiredScript) == "lib/managers/hudmanagerpd2" then 2 | local set_teammate_ammo_amount_orig = HUDManager.set_teammate_ammo_amount 3 | local set_slot_ready_orig = HUDManager.set_slot_ready 4 | 5 | function HUDManager:set_teammate_ammo_amount(id, selection_index, max_clip, current_clip, current_left, max, ...) 6 | if WolfHUD:getSetting({"CustomHUD", "USE_REAL_AMMO"}, true) then 7 | local total_left = current_left - current_clip 8 | if total_left >= 0 then 9 | current_left = total_left 10 | max = max - current_clip 11 | end 12 | end 13 | return set_teammate_ammo_amount_orig(self, id, selection_index, max_clip, current_clip, current_left, max, ...) 14 | end 15 | 16 | local FORCE_READY_CLICKS = 3 17 | local FORCE_READY_TIME = 2 18 | local FORCE_READY_ACTIVE_T = 90 19 | 20 | local force_ready_start_t = 0 21 | local force_ready_clicked = 0 22 | 23 | function HUDManager:set_slot_ready(peer, peer_id, ...) 24 | set_slot_ready_orig(self, peer, peer_id, ...) 25 | 26 | if Network:is_server() and not Global.game_settings.single_player then 27 | local session = managers.network and managers.network:session() 28 | local local_peer = session and session:local_peer() 29 | local time_elapsed = managers.game_play_central and managers.game_play_central:get_heist_timer() or 0 30 | if local_peer and local_peer:id() == peer_id then 31 | local t = Application:time() 32 | if (force_ready_start_t + FORCE_READY_TIME) > t then 33 | force_ready_clicked = force_ready_clicked + 1 34 | if force_ready_clicked >= FORCE_READY_CLICKS then 35 | local enough_wait_time = (time_elapsed > FORCE_READY_ACTIVE_T) 36 | local friends_list = not enough_wait_time and Steam:logged_on() and Steam:friends() or {} 37 | local abort = false 38 | for _, peer in ipairs(session:peers()) do 39 | local is_friend = false 40 | for _, friend in ipairs(friends_list) do 41 | if friend:id() == peer:user_id() then 42 | is_friend = true 43 | break 44 | end 45 | end 46 | if not (enough_wait_time or is_friend) or not (peer:synced() or peer:id() == local_peer:id()) then 47 | abort = true 48 | break 49 | end 50 | end 51 | if game_state_machine and not abort then 52 | local menu_options = { 53 | [1] = { 54 | text = managers.localization:text("dialog_yes"), 55 | callback = function(self, item) 56 | managers.chat:send_message(ChatManager.GAME, local_peer, "The Game was forced to start.") 57 | game_state_machine:current_state():start_game_intro() 58 | end, 59 | }, 60 | [2] = { 61 | text = managers.localization:text("dialog_no"), 62 | is_cancel_button = true, 63 | } 64 | } 65 | QuickMenu:new( managers.localization:text("wolfhud_dialog_force_start_title"), managers.localization:text("wolfhud_dialog_force_start_desc"), menu_options, true ) 66 | end 67 | end 68 | else 69 | force_ready_clicked = 1 70 | force_ready_start_t = t 71 | end 72 | end 73 | end 74 | end 75 | elseif string.lower(RequiredScript) == "lib/tweak_data/timespeedeffecttweakdata" then 76 | local init_original = TimeSpeedEffectTweakData.init 77 | local FORCE_ENABLE = { 78 | mission_effects = true, 79 | } 80 | function TimeSpeedEffectTweakData:init(...) 81 | init_original(self, ...) 82 | if WolfHUD:getSetting({"SkipIt", "NO_SLOWMOTION"}, true) then 83 | local function disable_effect(table) 84 | for name, data in pairs(table) do 85 | if not FORCE_ENABLE[name] then 86 | if data.speed and data.sustain then 87 | data.speed = 1 88 | data.fade_in_delay = 0 89 | data.fade_in = 0 90 | data.sustain = 0 91 | data.fade_out = 0 92 | elseif type(data) == "table" then 93 | disable_effect(data) 94 | end 95 | end 96 | end 97 | end 98 | 99 | disable_effect(self) 100 | end 101 | end 102 | elseif string.lower(RequiredScript) == "lib/tweak_data/economytweakdata" then 103 | if EconomyTweakData then 104 | -- Fix community market links for Real Weapon Names 105 | Hooks:PostHook(EconomyTweakData, "create_weapon_skin_market_search_url" ,"WolfHUD_EconomyTweakDataPostCreateWeaponSkinMarketSearchUrl", function(self, weapon_id, cosmetic_id) 106 | local cosmetic_name = tweak_data.blackmarket.weapon_skins[cosmetic_id] and managers.localization:text(tweak_data.blackmarket.weapon_skins[cosmetic_id].name_id) 107 | local weapon_name = managers.localization.orig.text(managers.localization, tweak_data.weapon[weapon_id].name_id) -- bypass custom localizations 108 | if cosmetic_name and weapon_name then 109 | cosmetic_name = string.gsub(cosmetic_name, " ", "+") 110 | weapon_name = string.gsub(weapon_name, " ", "+") 111 | return string.gsub("http://steamcommunity.com/market/search?appid=218620&q=" .. cosmetic_name .. "+" .. weapon_name, "++", "+") 112 | end 113 | return nil 114 | end) 115 | end 116 | elseif string.lower(RequiredScript) == "lib/managers/menu/items/menuitemmultichoice" then 117 | if MenuItemMultiChoice then 118 | Hooks:PostHook( MenuItemMultiChoice , "setup_gui" , "MenuItemMultiChoicePostSetupGui_WolfHUD" , function( self, node, row_item ) 119 | if self:selected_option() and self:selected_option():parameters().color and row_item.choice_text then 120 | row_item.choice_text:set_blend_mode("normal") 121 | end 122 | end) 123 | end 124 | elseif string.lower(RequiredScript) == "lib/managers/menu/menunodegui" then 125 | if MenuNodeMainGui then 126 | Hooks:PostHook( MenuNodeMainGui , "_add_version_string" , "MenuNodeMainGuiPostAddVersionString_WolfHUD" , function( self ) 127 | if alive(self._version_string) then 128 | self._version_string:set_text("Payday 2 v" .. Application:version() .. " | WolfHUD v" .. WolfHUD:getVersion()) 129 | end 130 | end) 131 | end 132 | elseif string.lower(RequiredScript) == "lib/managers/experiencemanager" then 133 | local cash_string_original = ExperienceManager.cash_string 134 | 135 | function ExperienceManager:cash_string(...) 136 | local val = cash_string_original(self, ...) 137 | if self._cash_sign ~= "$" and val:find(self._cash_sign) then 138 | val = val:gsub(self._cash_sign, "") .. self._cash_sign 139 | end 140 | return val 141 | end 142 | elseif string.lower(RequiredScript) == "lib/managers/moneymanager" then 143 | function MoneyManager:total_string() 144 | local total = math.round(self:total()) 145 | return managers.experience:cash_string(total) 146 | end 147 | function MoneyManager:total_collected_string() 148 | local total = math.round(self:total_collected()) 149 | return managers.experience:cash_string(total) 150 | end 151 | end 152 | -------------------------------------------------------------------------------- /lua/Utils/InputDialog.lua: -------------------------------------------------------------------------------- 1 | --Dialog working fine, GUI not present...? 2 | 3 | local requiresScript = RequiredScript:lower() 4 | if requiresScript == "lib/managers/systemmenumanager" then 5 | 6 | core:module("SystemMenuManager") 7 | require("lib/managers/dialogs/SpecializationDialog") 8 | 9 | GenericSystemMenuManager.GENERIC_INPUT_DIALOG_CLASS = TextInputDialog 10 | GenericSystemMenuManager.INPUT_DIALOG_CLASS = TextInputDialog 11 | 12 | function GenericSystemMenuManager:show_input( data ) 13 | local success = self:_show_class(data, self.GENERIC_INPUT_DIALOG_CLASS, self.INPUT_DIALOG_CLASS, data.force) 14 | self:_show_result(success, data) 15 | end 16 | elseif requiresScript == "lib/managers/dialogs/specializationdialog" then 17 | 18 | core:module("SystemMenuManager") 19 | require("lib/managers/dialogs/GenericDialog") 20 | 21 | TextInputDialog = TextInputDialog or class(GenericDialog) 22 | TextInputDialog.KEY_INIT_DELAY = 0.6 23 | TextInputDialog.KEY_DELAY = 0.03 24 | 25 | function TextInputDialog:init(manager, data, ...) 26 | Dialog.init(self, manager, data) 27 | 28 | if not self._data.focus_button then 29 | if #self._button_text_list > 0 then 30 | self._data.focus_button = #self._button_text_list 31 | else 32 | self._data.focus_button = 1 33 | end 34 | end 35 | self._ws = self._data.ws or manager:_get_ws() 36 | local text_config = { 37 | title_font = data.title_font, 38 | title_font_size = data.title_font_size, 39 | font = data.font or _G.tweak_data.menu.pd2_medium_font, 40 | font_size = data.font_size or _G.tweak_data.menu.pd2_medium_font_size, 41 | w = data.w or 500, 42 | h = data.h or 400, 43 | no_close_legend = true, 44 | no_scroll_legend = true, 45 | use_indicator = data.indicator or data.no_buttons or false, 46 | is_title_outside = is_title_outside or false, 47 | use_text_formating = data.use_text_formating, 48 | text_formating_color = data.text_formating_color, 49 | text_formating_color_table = data.text_formating_color_table, 50 | text_blend_mode = data.text_blend_mode or "add" 51 | } 52 | self._panel_script = _G.TextInputBoxGui:new(self._ws, self._data.title or "", self._data.text or "", self._data.user_text or "", self._data, text_config) 53 | self._panel_script:add_background() 54 | self._panel_script:set_layer(_G.tweak_data.gui.DIALOG_LAYER) 55 | self._panel_script:set_centered() 56 | self._panel_script:set_fade(0) 57 | self._controller = self._data.controller or manager:_get_controller() 58 | self._confirm_func = callback(self, self, "button_pressed_callback") 59 | self._cancel_func = callback(self, self, "dialog_cancel_callback") 60 | self._resolution_changed_callback = callback(self, self, "resolution_changed_callback") 61 | managers.viewport:add_resolution_changed_func(self._resolution_changed_callback) 62 | if data.counter then 63 | self._counter = data.counter 64 | self._counter_time = self._counter[1] 65 | end 66 | self._sound_event = data.sound_event 67 | 68 | self._last_key_pressed = nil 69 | self._next_key_send_t = 0 70 | end 71 | 72 | function TextInputDialog:set_input_enabled(enabled) 73 | TextInputDialog.super.set_input_enabled(self, enabled) 74 | 75 | if managers.controller:get_default_wrapper_type() == "pc" or managers.controller:get_default_wrapper_type() == "steam" then 76 | self._controller:remove_trigger("confirm", self._confirm_func) 77 | self._controller:remove_trigger("toggle_menu", self._cancel_func) 78 | self._controller:remove_trigger("cancel", self._cancel_func) 79 | end 80 | 81 | if enabled then 82 | local dialog_panel = self._panel_script and self._panel_script._panel 83 | if not self._kb_connected and self._ws and dialog_panel then 84 | local kb = Input:keyboard() 85 | self._ws:connect_keyboard(kb) 86 | 87 | dialog_panel:enter_text(function(that, char) self:on_enter_text(char) end) 88 | dialog_panel:key_press(function(that, key) self:on_key_press(key) end) 89 | dialog_panel:key_release(function(that, key) self:on_key_release(key) end) 90 | 91 | self._kb_connected = true 92 | end 93 | else 94 | local dialog_panel = self._panel_script and self._panel_script._panel 95 | if self._kb_connected and self._ws and dialog_panel then 96 | self._ws:disconnect_keyboard() 97 | dialog_panel:enter_text(nil) 98 | dialog_panel:key_press(nil) 99 | dialog_panel:key_release(nil) 100 | self._kb_connected = nil 101 | end 102 | end 103 | end 104 | 105 | function TextInputDialog:mouse_moved(o, x, y) 106 | if self._panel_script and alive(self._panel_script._text_input_panel) then 107 | local x, y = managers.mouse_pointer:convert_1280_mouse_pos(x, y) 108 | local status = self._panel_script._text_input_panel:inside(x, y) 109 | self._panel_script:set_textinput_highlight(status) 110 | self._data.text_input_highlight = status 111 | if status then 112 | return true, "link" 113 | end 114 | end 115 | return TextInputDialog.super.mouse_moved(self, o, x, y) 116 | end 117 | 118 | function TextInputDialog:mouse_pressed(o, button, x, y) 119 | if button == Idstring("0") then 120 | if self._panel_script and alive(self._panel_script._text_input_panel) then 121 | local x, y = managers.mouse_pointer:convert_1280_mouse_pos(x, y) 122 | local status = self._panel_script._text_input_panel:inside(x, y) 123 | self._panel_script:set_textinput_selected(status) 124 | self._data.text_input_focus = status 125 | if status then 126 | self:chk_mouse_pointer_status() 127 | return true, "link" 128 | end 129 | end 130 | end 131 | return TextInputDialog.super.mouse_pressed(self, o, button, x, y) 132 | end 133 | 134 | function TextInputDialog:on_enter_text(char) 135 | if self._data.text_input_focus then 136 | local text = self._data.user_text 137 | local n = utf8.len(text) 138 | local m = self._data.max_len or n + 1 139 | if n < m then 140 | local key = self._data.to_upper and utf8.to_upper(char) or char 141 | text = string.format("%s%s", text, key) 142 | self._panel_script:update_user_text(text) 143 | self._data.user_text = text 144 | end 145 | end 146 | end 147 | 148 | function TextInputDialog:on_key_press(key) 149 | local text = self._data.user_text 150 | local n = utf8.len(text) 151 | if key == Idstring("backspace") then 152 | if self._data.text_input_focus then 153 | text = utf8.sub(text, 0, math.max(n - 1, 0)) 154 | self._panel_script:update_user_text(text) 155 | self._last_key_pressed = key 156 | self._next_key_send_t = Application:time() + TextInputDialog.KEY_INIT_DELAY 157 | end 158 | elseif key == Idstring("enter") then 159 | if self._data.text_input_focus then 160 | self._panel_script:set_textinput_selected(false) 161 | self._data.text_input_focus = false 162 | self._data.text_input_highlight = false 163 | self:chk_mouse_pointer_status() 164 | elseif self._data.text_input_highlight then 165 | self._panel_script:set_textinput_selected(true) 166 | self._data.text_input_focus = true 167 | self:chk_mouse_pointer_status() 168 | else 169 | self:button_pressed_callback() 170 | end 171 | elseif key == Idstring("esc") then 172 | if self._data.text_input_focus then 173 | self._panel_script:set_textinput_selected(false) 174 | self._data.text_input_focus = false 175 | self:chk_mouse_pointer_status() 176 | else 177 | self:dialog_cancel_callback() 178 | end 179 | end 180 | self._data.user_text = text 181 | end 182 | 183 | function TextInputDialog:on_key_release(key) 184 | self._last_key_pressed = nil 185 | end 186 | 187 | function TextInputDialog:update_input(t, dt) 188 | if self._data.text_input_focus then 189 | if self._last_key_pressed and self._next_key_send_t < t then 190 | local text = self._data.user_text 191 | local n = utf8.len(text) 192 | 193 | if self._last_key_pressed == Idstring("backspace") then 194 | text = utf8.sub(text, 0, math.max(n - 1, 0)) 195 | else 196 | text = string.format("%s%s", text, self._last_key_pressed) 197 | end 198 | self._panel_script:update_user_text(text) 199 | self._next_key_send_t = self._next_key_send_t + TextInputDialog.KEY_DELAY 200 | 201 | self._data.user_text = text 202 | end 203 | 204 | local scroll = self._controller:get_input_axis("menu_scroll") 205 | if scroll.y > self.MOVE_AXIS_LIMIT or scroll.y < -self.MOVE_AXIS_LIMIT then 206 | self._panel_script:set_textinput_selected(false) 207 | self._data.text_input_focus = false 208 | end 209 | end 210 | return TextInputDialog.super.update_input(self, t, dt) 211 | end 212 | 213 | function TextInputDialog:chk_mouse_pointer_status() 214 | if self._text_box_focus then 215 | managers.mouse_pointer:disable() 216 | elseif managers.controller:get_default_wrapper_type() == "pc" or managers.controller:get_default_wrapper_type() == "steam" then 217 | managers.mouse_pointer:enable() 218 | end 219 | end 220 | 221 | function TextInputDialog:button_pressed(button_index) 222 | local button_list = self._data.button_list 223 | self:fade_out_close() 224 | if button_list then 225 | local button = button_list[button_index] 226 | if button and button.callback_func then 227 | button.callback_func(button_index, button, self._data.user_text) 228 | end 229 | end 230 | local callback_func = self._data.callback_func 231 | if callback_func then 232 | callback_func(button_index, self._data) 233 | end 234 | end 235 | elseif requiresScript == "lib/managers/menu/specializationboxgui" then 236 | TextInputBoxGui = TextInputBoxGui or class(TextBoxGui) 237 | TextInputBoxGui.TEXT = "" 238 | 239 | function TextInputBoxGui:init(...) 240 | 241 | TextInputBoxGui.super.init(self, ...) 242 | 243 | self._text_box_highlight = false 244 | self._text_box_focus = false 245 | self._cursor_animation = false 246 | end 247 | 248 | function TextInputBoxGui:_create_text_box(ws, title, text, user_text, content_data, config, ...) 249 | text = text .. "\n\n." 250 | 251 | local panel = TextInputBoxGui.super._create_text_box(self, ws, title, text, content_data, config, ...) 252 | 253 | local text_input_panel = self._scroll_panel:panel({ 254 | name = "text_input_panel", 255 | x = 10, 256 | h = tweak_data.menu.pd2_medium_font_size * 1.1, 257 | w = panel:w() - 20, 258 | layer = self._scroll_panel:layer() + 1, 259 | }) 260 | 261 | local scroll_text = self._scroll_panel:child("text") 262 | if alive(scroll_text) then 263 | scroll_text:set_h(scroll_text:h() - 10) 264 | text_input_panel:set_bottom(scroll_text:bottom() - 5) 265 | end 266 | 267 | local input_text = text_input_panel:text({ 268 | x = 5, 269 | y = 0, 270 | w = text_input_panel:w(), 271 | h = text_input_panel:h(), 272 | name = "input_text", 273 | text = user_text or "", 274 | color = tweak_data.screen_colors.button_stage_3, 275 | font = config.font or tweak_data.menu.pd2_medium_font, 276 | font_size = config.font_size or tweak_data.menu.pd2_medium_font_size, 277 | align = "left", 278 | vertical = "center", 279 | blend_mode = config.text_blend_mode or "add", 280 | layer = 2, 281 | }) 282 | local _, _, w, _ = input_text:text_rect() 283 | input_text:set_w(w) 284 | 285 | local input_text_bg = text_input_panel:rect({ 286 | name = "text_input_bg", 287 | h = text_input_panel:h(), 288 | w = text_input_panel:w(), 289 | color = tweak_data.screen_colors.button_stage_1, 290 | alpha = 0.4, 291 | layer = 1 292 | }) 293 | 294 | local cursor = text_input_panel:rect({ 295 | name = "cursor", 296 | x = input_text:right(), 297 | w = 4, 298 | h = text_input_panel:h(), 299 | color = Color.white, 300 | alpha = 0.8, 301 | layer = 2, 302 | blend_mode = config.text_blend_mode or "add", 303 | visible = false, 304 | }) 305 | 306 | self._text_input_panel = text_input_panel 307 | end 308 | 309 | function TextInputBoxGui:scroll_up(y) 310 | 311 | TextInputBoxGui.super.scroll_up(self, y) 312 | 313 | local scroll_text = self._scroll_panel:child("text") 314 | if alive(self._text_input_panel) and alive(scroll_text) then 315 | self._text_input_panel:set_bottom(scroll_text:bottom() - 5) 316 | end 317 | end 318 | 319 | function TextInputBoxGui:scroll_down(y) 320 | 321 | TextInputBoxGui.super.scroll_down(self, y) 322 | 323 | local scroll_text = self._scroll_panel:child("text") 324 | if alive(self._text_input_panel) and alive(scroll_text) then 325 | self._text_input_panel:set_bottom(scroll_text:bottom() - 5) 326 | end 327 | end 328 | 329 | function TextInputBoxGui:update_user_text(text) 330 | local text_box = self._text_input_panel:child("input_text") 331 | if text_box then 332 | text_box:set_text(text) 333 | local _, _, w, _ = text_box:text_rect() 334 | text_box:set_w(w) 335 | 336 | local cursor = self._text_input_panel:child("cursor") 337 | if cursor then 338 | cursor:set_x(text_box:right()) 339 | end 340 | end 341 | end 342 | 343 | function TextInputBoxGui:set_textinput_highlight(status) 344 | if status ~= self._text_box_highlight and alive(self._text_input_panel) then 345 | self._text_box_highlight = status 346 | 347 | local text_input_bg = self._text_input_panel:child("text_input_bg") 348 | local text_input = self._text_input_panel:child("input_text") 349 | if not self._text_box_focus then 350 | local color, color_bg 351 | if status then 352 | color = tweak_data.screen_colors.button_stage_2 353 | color_bg = tweak_data.screen_colors.button_stage_3 354 | else 355 | color = tweak_data.screen_colors.button_stage_3 356 | color_bg = tweak_data.screen_colors.button_stage_1 357 | self._cursor_animation = false 358 | end 359 | if alive(text_input) then 360 | text_input:set_color(color) 361 | end 362 | if alive(text_input_bg) then 363 | text_input_bg:set_color(color_bg) 364 | end 365 | managers.menu_component:post_event("highlight") 366 | end 367 | end 368 | end 369 | 370 | function TextInputBoxGui:set_textinput_selected(status) 371 | if status ~= self._text_box_focus and alive(self._text_input_panel) then 372 | self._text_box_focus = status 373 | 374 | local text_input_bg = self._text_input_panel:child("text_input_bg") 375 | local text_input = self._text_input_panel:child("input_text") 376 | if alive(text_input_bg) and alive(text_input) then 377 | local color, color_bg 378 | if status then 379 | color = tweak_data.screen_colors.button_stage_2 380 | color_bg = tweak_data.screen_colors.button_stage_2 381 | else 382 | if self._text_box_highlight then 383 | color = tweak_data.screen_colors.button_stage_2 384 | color_bg = tweak_data.screen_colors.button_stage_3 385 | else 386 | color = tweak_data.screen_colors.button_stage_3 387 | color_bg = tweak_data.screen_colors.button_stage_1 388 | end 389 | end 390 | text_input:set_color(color) 391 | text_input_bg:set_color(color_bg) 392 | end 393 | 394 | local cursor = self._text_input_panel:child("cursor") 395 | if alive(cursor) and self._cursor_animation ~= status then 396 | self._cursor_animation = status 397 | if status then 398 | cursor:animate(callback(self, self, "_animate_cursor")) 399 | end 400 | end 401 | end 402 | end 403 | 404 | function TextInputBoxGui:_animate_cursor(o) 405 | local t = Application:time() 406 | o:set_visible(true) 407 | while self._cursor_animation do 408 | t = t + coroutine.yield() 409 | o:set_alpha(math.sin(t * 540) * 0.5 + 0.5) 410 | end 411 | o:set_visible(false) 412 | end 413 | end -------------------------------------------------------------------------------- /lua/Utils/OutlinedText.lua: -------------------------------------------------------------------------------- 1 | OutlinedText = OutlinedText or class() 2 | 3 | function OutlinedText:init(panel, params) 4 | self._name = params.name 5 | self._parent = panel 6 | self._outlines_disabled = false 7 | 8 | self._text = panel:text(params) 9 | self._bgs = {} 10 | 11 | local bg_params = deep_clone(params) 12 | bg_params.name = nil 13 | bg_params.color = Color.black:with_alpha(0.5) 14 | bg_params.layer = self._text:layer() - 1 15 | for i = 1, 4 do 16 | bg_params.name = string.format("bg_%d", i) 17 | self._bgs[i] = panel:text(bg_params) 18 | end 19 | 20 | self:_update_positions() 21 | end 22 | 23 | function OutlinedText:set_outlines_visible(status) 24 | if self._outlines_disabled ~= not status then 25 | self._outlines_disabled = not status 26 | self:_update_visibility() 27 | end 28 | end 29 | 30 | function OutlinedText:set_outlines_color(color) 31 | if color then 32 | self:_call_func_bgs("set_color", color:with_alpha(0.5)) 33 | end 34 | end 35 | 36 | function OutlinedText:remove() 37 | self._parent:remove(self._text) 38 | for _, bg in pairs(self._bgs) do 39 | self._parent:remove(bg) 40 | end 41 | end 42 | 43 | function OutlinedText:x() return self._text:x() end 44 | function OutlinedText:y() return self._text:y() end 45 | function OutlinedText:w() return self._text:w() end 46 | function OutlinedText:h() return self._text:h() end 47 | function OutlinedText:center() return self._text:center() end 48 | function OutlinedText:center_x() return self._text:center_x() end 49 | function OutlinedText:center_y() return self._text:center_y() end 50 | function OutlinedText:text() return self._text:text() end 51 | function OutlinedText:font() return self._text:font() end 52 | function OutlinedText:font_size() return self._text:font_size() end 53 | function OutlinedText:visible() return self._text:visible() end 54 | function OutlinedText:color() return self._text:color() end 55 | function OutlinedText:alpha() return self._text:alpha() end 56 | 57 | function OutlinedText:set_x(...) return self:_call_func_text("set_x", ...) end 58 | function OutlinedText:set_y(...) return self:_call_func_text("set_y", ...) end 59 | function OutlinedText:set_w(...) return self:_call_func_text("set_w", ...) end 60 | function OutlinedText:set_h(...) return self:_call_func_text("set_h", ...) end 61 | function OutlinedText:set_center(...) return self:_call_func_text("set_center", ...) end 62 | function OutlinedText:set_center_x(...) return self:_call_func_text("set_center_x", ...) end 63 | function OutlinedText:set_center_y(...) return self:_call_func_text("set_center_y", ...) end 64 | function OutlinedText:set_text(...) return self:_call_func_all("set_text", ...) end 65 | function OutlinedText:set_font(...) return self:_call_func_all("set_font", ...) end 66 | function OutlinedText:set_font_size(...) return self:_call_func_all("set_font_size", ...) end 67 | function OutlinedText:set_visible(...) return self:_call_func_text("set_visible", ...) end 68 | function OutlinedText:show(...) return self:_call_func_text("show", ...) end 69 | function OutlinedText:hide(...) return self:_call_func_text("hide", ...) end 70 | function OutlinedText:set_color(...) return self:_call_func_text("set_color", ...) end 71 | function OutlinedText:set_alpha(...) return self:_call_func_all("set_alpha", ...) end 72 | function OutlinedText:animate(...) return self:_call_func_text("animate", ...) end 73 | function OutlinedText:stop(...) return self:_call_func_text("stop", ...) end 74 | 75 | function OutlinedText:_call_func_all(func, ...) 76 | local results 77 | if self._text[func] then 78 | results = { self._text[func](self._text, ...) } 79 | end 80 | 81 | self:_call_func_bgs(func, ...) 82 | 83 | return unpack(results or {}) 84 | end 85 | 86 | function OutlinedText:_call_func_text(func, ...) 87 | local results 88 | if self._text[func] then 89 | results = { self._text[func](self._text, ...) } 90 | end 91 | 92 | self:_update_visibility() 93 | self:_update_positions() 94 | 95 | return unpack(results or {}) 96 | end 97 | 98 | function OutlinedText:_call_func_bgs(func, ...) 99 | for _, bg in ipairs(self._bgs) do 100 | if bg[func] then 101 | bg[func](bg, ...) 102 | end 103 | end 104 | 105 | self:_update_visibility() 106 | self:_update_positions() 107 | end 108 | 109 | function OutlinedText:_update_positions() 110 | local x, y = self._text:x(), self._text:y() 111 | self._bgs[1]:set_x(x - 1) 112 | self._bgs[1]:set_y(y - 1) 113 | self._bgs[2]:set_x(x + 1) 114 | self._bgs[2]:set_y(y - 1) 115 | self._bgs[3]:set_x(x - 1) 116 | self._bgs[3]:set_y(y + 1) 117 | self._bgs[4]:set_x(x + 1) 118 | self._bgs[4]:set_y(y + 1) 119 | end 120 | 121 | function OutlinedText:_update_visibility() 122 | for _, bg in pairs(self._bgs) do 123 | bg:set_visible(not self._outlines_disabled and self._text:visible()) 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /lua/Utils/QuickInputMenu.lua: -------------------------------------------------------------------------------- 1 | QuickInputMenu = QuickInputMenu or class() 2 | QuickInputMenu._menu_id_key = "quick_input_menu_id_" 3 | QuickInputMenu._menu_id_index = 0 4 | 5 | function QuickInputMenu:new( ... ) 6 | return self:init( ... ) 7 | end 8 | 9 | function QuickInputMenu:init( title, text, user_text, options, show_immediately, config ) 10 | 11 | QuickInputMenu._menu_id_index = QuickInputMenu._menu_id_index + 1 12 | self.dialog_data = { 13 | id = QuickInputMenu._menu_id_key .. tostring( QuickInputMenu._menu_id_index ), 14 | title = title, 15 | text = text, 16 | user_text = user_text or "", 17 | button_list = {}, 18 | } 19 | 20 | if config then 21 | for k, v in pairs(config) do 22 | self.dialog_data[k] = self.dialog_data[k] or v 23 | end 24 | end 25 | 26 | self.visible = false 27 | 28 | local add_default = false 29 | if (not options) or (options ~= nil and type(options) ~= "table") or (options ~= nil and type(options) == "table" and #options == 0) then 30 | add_default = true 31 | end 32 | if add_default then 33 | local tbl = { 34 | text = "OK", 35 | is_cancel_button = true, 36 | } 37 | table.insert( options, tbl ) 38 | end 39 | 40 | for _, option in ipairs( options ) do 41 | 42 | option.data = option.data 43 | option.callback = option.callback 44 | 45 | local button = {} 46 | local callback_data = { 47 | data = option.data, 48 | callback = option.callback 49 | } 50 | button.text = option.text 51 | button.callback_func = callback( self, self, "_callback", callback_data ) 52 | button.cancel_button = option.is_cancel_button or false 53 | 54 | if option.is_focused_button then 55 | self.dialog_data.focus_button = #self.dialog_data.button_list + 1 56 | end 57 | 58 | table.insert( self.dialog_data.button_list, button ) 59 | 60 | end 61 | 62 | if show_immediately then 63 | self:show() 64 | end 65 | 66 | return self 67 | 68 | end 69 | 70 | function QuickInputMenu:_callback( callback_data, ... ) 71 | 72 | if callback_data.callback then 73 | callback_data.callback( callback_data.data, ... ) 74 | end 75 | 76 | self.visible = false 77 | 78 | end 79 | 80 | function QuickInputMenu:Show() 81 | 82 | if not self.visible then 83 | self.visible = true 84 | managers.system_menu:show_input( self.dialog_data ) 85 | end 86 | 87 | end 88 | 89 | function QuickInputMenu:show() 90 | self:Show() 91 | end 92 | 93 | function QuickInputMenu:Hide() 94 | 95 | if self.visible then 96 | managers.system_menu:close( self.dialog_data.id ) 97 | self.visible = false 98 | end 99 | 100 | end 101 | 102 | function QuickInputMenu:hide() 103 | self:Hide() 104 | end 105 | -------------------------------------------------------------------------------- /lua/VanillaHUD.lua: -------------------------------------------------------------------------------- 1 | 2 | if WolfHUD:getSetting({"CustomHUD", "ENABLED"}, true) then 3 | return 4 | end 5 | 6 | if RequiredScript == "lib/managers/hudmanagerpd2" then 7 | 8 | local set_stamina_value_original = HUDManager.set_stamina_value 9 | local set_max_stamina_original = HUDManager.set_max_stamina 10 | local teammate_progress_original = HUDManager.teammate_progress 11 | 12 | function HUDManager:set_stamina_value(value, ...) 13 | self._teammate_panels[HUDManager.PLAYER_PANEL]:set_current_stamina(value) 14 | return set_stamina_value_original(self, value, ...) 15 | end 16 | 17 | function HUDManager:set_max_stamina(value, ...) 18 | self._teammate_panels[HUDManager.PLAYER_PANEL]:set_max_stamina(value) 19 | return set_max_stamina_original(self, value, ...) 20 | end 21 | 22 | function HUDManager:teammate_progress(peer_id, type_index, enabled, tweak_data_id, timer, success, ...) 23 | teammate_progress_original(self, peer_id, type_index, enabled, tweak_data_id, timer, success, ...) 24 | local character_data = managers.criminals:character_data_by_peer_id(peer_id) 25 | if character_data then 26 | local teammate_panel = self._teammate_panels[character_data.panel_id] 27 | local name_label = self:_name_label_by_peer_id(peer_id) 28 | if name_label then 29 | teammate_panel:set_interact_text(name_label.panel:child("action"):text()) 30 | end 31 | teammate_panel:set_interact_visibility(enabled and timer and WolfHUD:getSetting({"CustomHUD", "TEAMMATE", "INTERACTION", "MIN_DURATION"}, 1) <= timer and not WolfHUD:getSetting({"CustomHUD", "TEAMMATE", "INTERACTION", "HIDE"}, false) and WolfHUD:getSetting({"CustomHUD", "TEAMMATE", "INTERACTION", "TEXT"}, true)) 32 | end 33 | end 34 | 35 | elseif RequiredScript == "lib/managers/hud/hudteammate" then 36 | 37 | local init_original = HUDTeammate.init 38 | local set_name_original = HUDTeammate.set_name 39 | local set_condition_original = HUDTeammate.set_condition 40 | local teammate_progress_original = HUDTeammate.teammate_progress 41 | local update_original = HUDManager.update 42 | 43 | function HUDTeammate:init(...) 44 | init_original(self, ...) 45 | 46 | self._setting_prefix = self._main_player and "PLAYER" or "TEAMMATE" 47 | self._max_name_panel_width = self._panel:w() 48 | 49 | self._condition_icon = self._panel:child("condition_icon") 50 | self._condition_icon:set_color(WolfHUD:getColorSetting({"CustomHUD", self._setting_prefix, "CONDITION_ICON_COLOR"}, "white")) 51 | 52 | self._next_latency_update_t = 0 53 | 54 | if self._main_player and not HUDManager.CUSTOM_TEAMMATE_PANELS then 55 | self:_create_stamina_circle() 56 | else 57 | self:_init_interact_info() 58 | end 59 | 60 | if WolfHUD:getSetting({"CustomHUD","TEAMMATE","LATENCY"}, true) then 61 | self:_create_ping_info() 62 | end 63 | end 64 | 65 | function HUDTeammate:set_name(name, ...) 66 | if not self._ai then 67 | if WolfHUD:getSetting({"CustomHUD", self._setting_prefix, "TRUNCATE_TAGS"}, true) then 68 | name = WolfHUD:truncateNameTag(name) 69 | end 70 | if WolfHUD:getSetting({"CustomHUD", self._setting_prefix, "RANK"}, true) then 71 | local peer = self:peer_id() and managers.network:session():peer(self:peer_id()) 72 | local infamy, level = peer and peer:rank() or managers.experience:current_rank(), peer and peer:level() or managers.experience:current_level() 73 | local level_str = string.format("%s%s ", 74 | (infamy or 0) > 0 and string.format("%s-", managers.experience:rank_string(infamy)) or "", 75 | tostring(level) 76 | ) 77 | name = level_str .. name 78 | self._color_pos = level_str:len() 79 | end 80 | end 81 | set_name_original(self, name,...) 82 | self:_truncate_name() 83 | end 84 | 85 | function HUDTeammate:_truncate_name() 86 | local name_panel = self._panel:child("name") 87 | local name_bg_panel = self._panel:child("name_bg") 88 | local teammate_name = name_panel:text() 89 | name_panel:set_vertical("center") 90 | name_panel:set_font_size(tweak_data.hud_players.name_size) 91 | name_panel:set_w(self._panel:w() - name_panel:x()) 92 | local _,_,w,h = name_panel:text_rect() 93 | while (name_panel:x() + w) > self._max_name_panel_width do 94 | if name_panel:font_size() > 15.1 then 95 | name_panel:set_font_size(name_panel:font_size() - 0.1) 96 | else 97 | name_panel:set_text(teammate_name:sub(1, teammate_name:len() - 1)) 98 | end 99 | teammate_name = name_panel:text() 100 | _,_,w,h = name_panel:text_rect() 101 | end 102 | if not self._ai then 103 | name_panel:set_range_color((self._color_pos or 0) + 1, name_panel:text():len() + 1, self._panel:child("callsign"):color():with_alpha(1)) 104 | else 105 | name_panel:set_color(WolfHUD:getSetting({"CustomHUD", "TEAMMATE", "AI_COLOR", "USE"}, false) and WolfHUD:getColorSetting({"CustomHUD", "TEAMMATE", "AI_COLOR", "COLOR"}, "white") or tweak_data.chat_colors[5]) 106 | end 107 | name_bg_panel:set_w(w + 4) 108 | name_bg_panel:set_h(h + 2) 109 | name_bg_panel:set_y(name_panel:y() + name_panel:h() / 2 - h / 2 - 1) 110 | end 111 | 112 | function HUDTeammate:update(t,dt) 113 | self:update_latency(t,dt) 114 | end 115 | 116 | function HUDTeammate:update_latency(t,dt) 117 | local ping_panel = self._panel:child("latency") 118 | if ping_panel and self:peer_id() and t > self._next_latency_update_t then 119 | local net_session = managers.network:session() 120 | local peer = net_session and net_session:peer(self:peer_id()) 121 | local latency = peer and Network:qos(peer:rpc()).ping or "n/a" 122 | 123 | if type(latency) == "number" then 124 | ping_panel:set_text(string.format("%.0fms", latency)) 125 | ping_panel:set_color(latency < 75 and Color('C2FC97') or latency < 150 and Color('CEA168') or Color('E24E4E')) 126 | else 127 | ping_panel:set_text(latency) 128 | ping_panel:set_color(Color('E24E4E')) 129 | end 130 | 131 | self._next_latency_update_t = t + 1 132 | elseif not self:peer_id() and ping_panel then 133 | ping_panel:set_text("") 134 | end 135 | end 136 | 137 | function HUDManager:update(...) 138 | for i, panel in ipairs(self._teammate_panels) do 139 | panel:update(...) 140 | end 141 | 142 | return update_original(self, ...) 143 | end 144 | 145 | function HUDTeammate:_create_ping_info() 146 | local name_panel = self._panel:child("name") 147 | local ping_info = self._panel:text({ 148 | name = "latency", 149 | vertical = "right", 150 | font_size = tweak_data.hud.small_font_size, 151 | align = "right", 152 | halign = "right", 153 | text = "", 154 | font = "fonts/font_small_mf", 155 | layer = 1, 156 | visible = true, 157 | color = Color.white, 158 | x = -12, 159 | y = name_panel:y() - tweak_data.hud.small_font_size, 160 | h = 50 161 | }) 162 | end 163 | 164 | function HUDTeammate:_create_stamina_circle() 165 | local radial_health_panel = self._panel:child("player"):child("radial_health_panel") 166 | self._stamina_bar = radial_health_panel:bitmap({ 167 | name = "radial_stamina", 168 | texture = "guis/textures/pd2/hud_radial_rim", 169 | render_template = "VertexColorTexturedRadial", 170 | blend_mode = "add", 171 | alpha = 1, 172 | w = radial_health_panel:w() * 0.37,--53, 173 | h = radial_health_panel:h() * 0.37,--53, 174 | layer = 2, 175 | visible = WolfHUD:getSetting({"CustomHUD", "PLAYER", "STAMINA"}, true) 176 | }) 177 | self._stamina_bar:set_color(Color(1, 1, 0, 0)) 178 | self._stamina_bar:set_center(radial_health_panel:child("radial_health"):center()) 179 | 180 | self._stamina_line = radial_health_panel:rect({ 181 | color = Color.red:with_alpha(0.4), 182 | w = radial_health_panel:w() * 0.05, 183 | h = 2, 184 | layer = 10, 185 | visible = WolfHUD:getSetting({"CustomHUD", "PLAYER", "STAMINA"}, true) 186 | }) 187 | self._stamina_line:set_center(radial_health_panel:child("radial_health"):center()) 188 | end 189 | 190 | function HUDTeammate:set_max_stamina(value) 191 | if not self._max_stamina or self._max_stamina ~= value then 192 | self._max_stamina = value 193 | local w = self._stamina_bar:w() 194 | local threshold = tweak_data.player.movement_state.stamina.MIN_STAMINA_THRESHOLD 195 | local angle = 360 * (threshold/self._max_stamina) - 90 196 | local x = 0.48 * w * math.cos(angle) + w * 0.5 + self._stamina_bar:x() 197 | local y = 0.48 * w * math.sin(angle) + w * 0.5 + self._stamina_bar:y() 198 | self._stamina_line:set_x(x) 199 | self._stamina_line:set_y(y) 200 | self._stamina_line:set_rotation(angle) 201 | end 202 | end 203 | 204 | function HUDTeammate:set_current_stamina(value) 205 | self._stamina_bar:set_color(Color(1, value/self._max_stamina, 0, 0)) 206 | self:set_stamina_meter_visibility(WolfHUD:getSetting({"CustomHUD", "PLAYER", "STAMINA"}, true) and not self._condition_icon:visible()) 207 | end 208 | 209 | function HUDTeammate:set_stamina_meter_visibility(value) 210 | if self._stamina_bar and self._stamina_bar:visible() ~= value then 211 | self._stamina_bar:set_visible(value) 212 | self._stamina_line:set_visible(value) 213 | end 214 | end 215 | 216 | function HUDTeammate:_init_interact_info() 217 | self._interact_info_panel = self._panel:panel({ 218 | name = "interact_info_panel", 219 | x = 0, 220 | y = 0, 221 | visible = false 222 | }) 223 | self._interact_info = self._interact_info_panel:text({ 224 | name = "interact_info", 225 | text = "|", 226 | layer = 3, 227 | color = Color.white, 228 | x = 0, 229 | y = 1, 230 | align = "right", 231 | vertical = "top", 232 | font_size = tweak_data.hud_players.name_size, 233 | font = tweak_data.hud_players.name_font 234 | }) 235 | local _, _, text_w, text_h = self._interact_info:text_rect() 236 | self._interact_info:set_right(self._interact_info_panel:w() - 4) 237 | self._interact_info_bg = self._interact_info_panel:bitmap({ 238 | name = "interact_info_bg", 239 | texture = "guis/textures/pd2/hud_tabs", 240 | texture_rect = { 241 | 84, 242 | 0, 243 | 44, 244 | 32 245 | }, 246 | layer = 2, 247 | color = Color.white / 3, 248 | x = 0, 249 | y = 0, 250 | align = "left", 251 | vertical = "bottom", 252 | w = text_w + 4, 253 | h = text_h 254 | }) 255 | end 256 | 257 | function HUDTeammate:set_interact_text(text) 258 | if alive(self._interact_info) then 259 | self._interact_info:set_text(text) 260 | local _, _, w, _ = self._interact_info:text_rect() 261 | self._interact_info_bg:set_w(w + 8) 262 | self._interact_info_bg:set_right(self._interact_info:right() + 4) 263 | end 264 | end 265 | 266 | function HUDTeammate:set_interact_visibility(visible) 267 | if self._interact_info_panel then 268 | self._interact_info_panel:set_visible(visible) 269 | end 270 | end 271 | 272 | function HUDTeammate:teammate_progress(enabled, tweak_data_id, timer, success, ...) 273 | local show = timer and WolfHUD:getSetting({"CustomHUD", "TEAMMATE", "INTERACTION", "MIN_DURATION"}, 1) <= timer and not WolfHUD:getSetting({"CustomHUD", "TEAMMATE", "INTERACTION", "HIDE"}, false) 274 | teammate_progress_original(self, enabled and show, tweak_data_id, timer, success and show, ...) 275 | if enabled and show and WolfHUD:getSetting({"CustomHUD", "TEAMMATE", "INTERACTION", "NUMBER"}, true) then 276 | self:_start_interact_timer(timer) 277 | else 278 | self:_stop_interact_timer() 279 | end 280 | end 281 | 282 | function HUDTeammate:_start_interact_timer(interaction_time) 283 | local condition_timer = self._panel:child("condition_timer") 284 | condition_timer:stop() 285 | condition_timer:animate(callback(self, self, "_animate_interact_timer"), interaction_time) 286 | end 287 | 288 | function HUDTeammate:_animate_interact_timer(condition_timer, total_time) 289 | condition_timer:set_font_size(tweak_data.hud_players.timer_size) 290 | condition_timer:set_color(Color.white) 291 | condition_timer:set_visible(true) 292 | 293 | local t = total_time 294 | while t >= 0 do 295 | t = t - coroutine.yield() 296 | condition_timer:set_text(string.format("%.1fs", t)) 297 | condition_timer:set_color(math.lerp(Color('00FF00'), Color.white, t / total_time)) 298 | end 299 | condition_timer:set_text(string.format("%.1fs", 0)) 300 | condition_timer:set_color(Color('00FF00')) 301 | end 302 | 303 | function HUDTeammate:_stop_interact_timer() 304 | if alive(self._panel) then 305 | local condition_timer = self._panel:child("condition_timer") 306 | condition_timer:stop() 307 | condition_timer:set_visible(false) 308 | end 309 | end 310 | 311 | function HUDTeammate:set_condition(icon_data, ...) 312 | local visible = icon_data ~= "mugshot_normal" 313 | self:set_stamina_meter_visibility(not visible and WolfHUD:getSetting({"CustomHUD", "PLAYER", "STAMINA"}, true)) 314 | if HUDManager.DOWNS_COUNTER_PLUGIN and self._downs_counter and self._detection_counter then 315 | local disabled = visible or not WolfHUD:getSetting({"CustomHUD", self._setting_prefix, "DOWNCOUNTER"}, true) or self._ai 316 | self._downs_counter:set_visible(not disabled and (not managers.groupai:state():whisper_mode() or self:down_amount() > 0)) 317 | self._detection_counter:set_visible(not disabled and not self._downs_counter:visible()) 318 | end 319 | set_condition_original(self, icon_data, ...) 320 | end 321 | 322 | end 323 | -------------------------------------------------------------------------------- /mod.txt: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "WolfHUD", 3 | "description" : "This is a Mod Collection of HUD altering scripts, as well as quality of life changes to the game and its menus. \n\nSome of the included scripts are: \n- CustomHUD, HUDList, KillCounter, Press2Hold & WeaponGadgets,\n made by Seven\n- TabStats + NumericSuspicion, by friendlyfire\n- Enemy Healthbar, by Undeadsewer\n- DrivingHUD, by ViciousWalrus + Great Big Bushy Beard, rewritten by me\n- PrePlanManager, ProfileMenu & Equipment Tweaks, made by me\n- many many more...\n\nFor a full list of included mods, go to the GitHUB repository, linked as contact.\n\nLocalizations: \n- English made by me\n- German made by me\n- Russian made by chrom[K]a, Magic3000 & MEXAHOTABOP\n- Korean made by Я!zu\n- Spanish made by papydeath95\n- Chinese made by zhongfly\n- French made by EliseMRX (La Mule).", 4 | "author" : "Kamikaze94", 5 | "contact" : "https://github.com/Kamikaze94/WolfHUD", 6 | "version" : "3.4.0", 7 | "priority" : 1, 8 | "blt_version" : 2, 9 | "supermod_definition" : "supermod.xml", 10 | "image" : "wolfhud.png", 11 | "color" : "0 255 128", 12 | "pre_hooks" : [ 13 | { "hook_id" : "lib/entry", "script_path" : "Core.lua" } 14 | ], 15 | "updates" : [ 16 | { 17 | "identifier" : "wolfhud", 18 | "host" : { 19 | "meta" : "https://github.com/Kamikaze94/WolfHUD/raw/autoupdate/meta.json" 20 | } 21 | }, 22 | { 23 | "identifier" : "fed_inv", 24 | "host" : { 25 | "meta" : "https://github.com/Kamikaze94/WolfHUD/raw/autoupdate/meta.json" 26 | }, 27 | "install_dir" : "assets/mod_overrides/", 28 | "install_folder" : "Federal Inventory", 29 | "display_name" : "Federal Inventory" 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /supermod.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
  5 | 
  6 | 		
  7 | 			
  8 | 				
  9 | 				
 10 | 				
 11 | 			
 12 | 
 13 | 			
 14 | 				
 15 | 				
 16 | 			
 17 | 
 18 | 			
 19 | 			
 20 | 
 21 | 			
 22 | 				
 23 | 				
 24 | 				
 25 | 				
 26 | 				
 27 | 				
 28 | 				
 29 | 				
 30 | 				
 31 | 				
 32 | 				
 33 | 				
 34 | 				
 35 | 				
 36 | 				
 37 | 				
 38 | 
 39 | 				
 40 | 					
 41 | 					
 42 | 					
 43 | 				
 44 | 
 45 | 				
 46 | 					
 47 | 					
 48 | 				
 49 | 
 50 | 				
 51 | 					
 52 | 					
 53 | 					
 54 | 					
 55 | 					
 56 | 					
 57 | 					
 58 | 					
 59 | 					
 60 | 					
 61 | 					
 62 | 					
 63 | 					
 64 | 					
 65 | 				
 66 | 
 67 | 				
 68 | 					
 69 | 					
 70 | 					
 71 | 				
 72 | 
 73 | 				
 74 | 					
 75 | 					
 76 | 				
 77 | 
 78 | 				
 79 | 					
 80 | 					
 81 | 				
 82 | 
 83 | 				
 84 | 					
 85 | 					
 86 | 				
 87 | 
 88 | 				
 89 | 					
 90 | 					
 91 | 					
 92 | 					
 93 | 					
 94 | 					
 95 | 					
 96 | 					
 97 | 					
 98 | 
 99 | 					
100 | 						
101 | 						
102 | 						
103 | 						
104 | 						
105 | 					
106 | 
107 | 					
108 | 						
109 | 						
110 | 					
111 | 
112 | 					
113 | 						
114 | 						
115 | 					
116 | 				
117 | 
118 | 				
119 | 					
120 | 					
121 | 					
122 | 					
123 | 					
124 | 					
125 | 					
126 | 					
127 | 					
128 | 					
129 | 					
130 | 					
131 | 
132 | 					
133 | 						
134 | 						
135 | 					
136 | 
137 | 					
138 | 						
139 | 						
140 | 						
141 | 					
142 | 
143 | 					
144 | 						
145 | 						
146 | 					
147 | 
148 | 					
149 | 						
150 | 						
151 | 					
152 | 				
153 | 			
154 | 
155 | 			
156 | 				
157 | 				
158 | 			
159 | 
160 | 			
161 | 				
162 | 				
163 | 				
164 | 				
165 | 			
166 | 
167 | 			
168 | 				
169 | 
170 | 				
171 | 					
172 | 					
173 | 				
174 | 
175 | 				
176 | 					
177 | 					
178 | 					
179 | 					
180 | 				
181 | 
182 | 				
183 | 					
184 | 					
185 | 					
186 | 				
187 | 
188 | 				
189 | 					
190 | 					
191 | 					
192 | 
193 | 					
194 | 						
195 | 						
196 | 					
197 | 
198 | 					
199 | 						
200 | 						
201 | 						
202 | 						
203 | 
204 | 						
205 | 							
206 | 							
207 | 							
208 | 							
209 | 							
210 | 						
211 | 					
212 | 				
213 | 
214 | 				
215 | 					
216 | 					
217 | 					
218 | 					
219 | 					
220 | 				
221 | 
222 | 				
223 | 					
224 | 					
225 | 					
226 | 					
227 | 					
228 | 
229 | 					
230 | 						
231 | 						
232 | 					
233 | 
234 | 					
235 | 						
236 | 						
237 | 					
238 | 
239 | 					
240 | 						
241 | 						
242 | 					
243 | 				
244 | 
245 | 				
246 | 					
247 | 					
248 | 					
249 | 					
250 | 					
251 | 					
252 | 					
253 | 
254 | 					
255 | 						
256 | 						
257 | 						
258 | 					
259 | 
260 | 					
261 | 						
262 | 						
263 | 					
264 | 
265 | 					
266 | 						
267 | 						
268 | 					
269 | 				
270 | 
271 | 				
272 | 					
273 | 					
274 | 				
275 | 			
276 | 
277 | 			
278 | 				
279 | 				
280 | 				
281 | 				
282 | 				
283 | 				
284 | 				
285 | 			
286 | 
287 | 			
288 | 				
289 | 				
290 | 				
291 | 			
292 | 
293 | 			
294 | 				
295 | 				
296 | 				
297 | 				
298 | 			
299 | 		
300 | 
301 | 		
302 | 			
303 | 			
304 | 		
305 | 	
306 | 
307 | 	
308 | 	<:include src="assets/assets.xml"/>
309 | 
310 | 


--------------------------------------------------------------------------------
/wolfhud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kamikaze94/WolfHUD/1b2736f9290062fe73ef3f2f10b913255def4905/wolfhud.png


--------------------------------------------------------------------------------